diff --git a/ASSET-LICENSE.md b/ASSET-LICENSE.md
new file mode 100644
index 0000000..9519836
--- /dev/null
+++ b/ASSET-LICENSE.md
@@ -0,0 +1,189 @@
+# straywild Asset License 1.1
+
+Copyright © 2026 Woofmeow. All rights reserved except for the permissions
+expressly granted below.
+
+## 1. Scope
+
+This license applies to original straywild artwork, graphics, textures,
+models, animations, music, sound recordings, narrative content, visual
+designs, characters, and other creative assets owned by the straywild owners
+or licensed to them with authority to sublicense, publishing as Woofmeow (the
+**“straywild 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 straywild 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 straywild Software** means:
+
+- an unmodified binary release published by the straywild owners through an
+ official straywild distribution channel; or
+- a build made from an unmodified official straywild source release or tagged
+ revision.
+
+**Mod** means a non-standalone add-on created to operate with Official
+straywild Software. A Mod may add, replace, or alter content loaded by the
+Official straywild Software, but it may not redistribute a replacement copy
+of the game or represent a fork, clone, sequel, or separate game.
+
+**Contribution Fork** means a source-control fork or clone of an official
+straywild source repository that is created and maintained in good faith
+primarily to prepare, test, review, or submit changes for possible inclusion
+in Official straywild Software. It does not include a fork presented or
+developed as an independent game, community edition, replacement distribution,
+or ongoing product separate from the official straywild project.
+
+## 3. Permission to use the official game
+
+You may reproduce straywild Assets as technically necessary to install, run,
+back up, and privately modify a lawfully obtained copy of Official straywild
+Software.
+
+You may capture, publish, and monetize screenshots, gameplay videos,
+livestreams, reviews, tutorials, and similar commentary showing a lawfully
+obtained copy of straywild. 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 straywild
+Assets solely as part of a Mod for Official straywild 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
+straywild 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 straywild Software;
+2. be presented as unofficial community content;
+3. include the following notice in its documentation or distribution page:
+
+ > Contains or adapts straywild assets © 2026 Woofmeow, used under the
+ > straywild Asset License 1.1. This Mod is unofficial and is not endorsed
+ > by Woofmeow.
+
+4. identify material created by the Mod author separately from straywild
+ 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 straywild
+Assets in paid products requires separate written permission from both
+straywild owners.
+
+## 5. Contribution Fork license
+
+Subject to this license, you may reproduce, modify, and distribute straywild
+Assets within a Contribution Fork. You may also make the nonpublic builds
+reasonably necessary to develop, test, and review its proposed contribution.
+
+This permission is worldwide, nonexclusive, royalty-free, and available for
+as long as you comply with this license. Public source-control hosting of the
+Contribution Fork and the repository history technically necessary for that
+fork are permitted distributions.
+
+A Contribution Fork must:
+
+1. remain primarily directed toward preparing changes for possible inclusion
+ in the official straywild project;
+2. preserve this license and the official straywild project's applicable
+ attribution, third-party license, and copyright notices;
+3. remain visibly linked to or identified as a fork of the official source
+ repository and not be presented as official or endorsed;
+4. limit test-build access to the owners, maintainers, contributors, and
+ testers directly participating in development or review of the proposed
+ contribution;
+5. not publish general-audience binaries, releases, packages, or downloads,
+ operate a persistent public alternative service, or monetize access;
+6. not distribute the straywild Assets as a standalone extraction, archive,
+ or general-purpose asset pack; and
+7. comply with `TRADEMARKS.md`.
+
+Temporary access-controlled servers may be operated when reasonably necessary
+to test a proposed contribution. They may not be promoted or maintained as a
+public alternative to Official straywild Software or its services.
+
+Submitting work from a Contribution Fork is governed separately by
+`CONTRIBUTOR-TERMS.md` and does not require the straywild owners to accept the
+work. Declining or closing a proposed contribution does not retroactively
+invalidate good-faith use under this section. A compliant Contribution Fork
+may remain publicly accessible as an archival development record, but it may
+not be repurposed for continued development or distribution of a separate
+product containing straywild Assets.
+
+## 6. Uses not permitted
+
+Except as expressly permitted by Sections 3, 4, and 5 or by separate written
+permission from both straywild owners, you may not use or distribute
+straywild Assets:
+
+- in a fork, clone, remake, sequel, or standalone game that is not a qualifying
+ Contribution Fork;
+- with a modified distribution of the GPL-covered straywild code that is not
+ Official straywild Software, except for nonpublic contribution-testing
+ builds permitted by Section 5;
+- 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 straywild Assets themselves.
+
+The GPL permits forks and redistribution of the GPL-covered code. Except for a
+qualifying Contribution Fork under Section 5, this asset license does not
+permit those forks to carry straywild Assets. A code fork that is not a
+Contribution Fork must remove or replace the straywild Assets and reserved
+branding unless both owners grant separate written permission.
+
+## 7. Ownership of Mods and contribution work
+
+You retain ownership of your original Mod material and original material
+created in a Contribution Fork. The straywild owners retain ownership of the
+straywild Assets and adaptations of those assets to the extent provided by
+applicable law.
+
+This license does not require you to assign or submit your original material.
+It also does not grant you ownership of the underlying straywild Assets. If
+you intentionally submit material for inclusion in straywild,
+`CONTRIBUTOR-TERMS.md` applies separately.
+
+## 8. 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 straywild 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.
+
+## 9. No warranty
+
+THE straywild 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 straywild ASSETS.
+
+## 10. Additional permission
+
+Uses not granted here require written permission from both straywild 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
index 8406c0a..2dc5724 100644
--- a/CONTRIBUTING.md
+++ b/CONTRIBUTING.md
@@ -1,8 +1,32 @@
-# Contributing
+# Contribution policy
-NETfishing is currently developed through focused changes on the repository's
-active development branch. Coordinate scope with the project owner before
-starting substantial work.
+straywild is an owner-directed Woofmeow project and is not accepting
+unsolicited contributions. Please do not submit pull requests, patches, code,
+artwork, models, audio, writing, documentation, translations, designs, or
+other implementation work. Pull requests are disabled for the official
+repositories.
+
+Issues remain available for reproducible bug reports and player feedback. An
+Issue is not a request for contributed implementation, and attaching or
+linking unsolicited work does not make it an accepted Project contribution.
+
+The owners may occasionally invite specific work in writing before it is
+created or submitted. [`CONTRIBUTOR-TERMS.md`](CONTRIBUTOR-TERMS.md) is retained
+to document previously accepted contributions and govern expressly invited
+work; it is not a standing invitation to contribute.
+
+## Contribution forks
+
+The [straywild Asset License](ASSET-LICENSE.md) contains limited permissions
+for qualifying Contribution Forks. Those permissions do not promise review or
+acceptance and do not override the policy above. Keep the hosting platform's
+fork relationship visible, preserve all license and attribution notices, and
+do not publish general-audience builds or use the fork as an independent game
+or distribution.
+
+Private test builds and temporary access-controlled test servers may be shared
+only as allowed by that license. Permission to maintain a Contribution Fork
+does not authorize unsolicited submission or obligate the owners to review it.
## Change discipline
@@ -15,10 +39,15 @@ starting substantial work.
- 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.
+- Retain source, permission, and license evidence for newly imported assets.
+ Update [`docs/ATTRIBUTION.md`](docs/ATTRIBUTION.md) when the accepted work
+ adds or changes a distributed credit or third-party notice; do not add a
+ file-by-file inventory of ordinary project-owned assets.
+- 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
+## Before committing a maintainer change
```sh
git diff --check
@@ -26,7 +55,8 @@ scripts/run_validations.sh quick
git status --short
```
-Choose additional focused tests from [`docs/TESTING.md`](docs/TESTING.md).
+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.
@@ -38,7 +68,7 @@ tests are supporting evidence, not a replacement.
- 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.
+ the internal review or release record.
## Style
@@ -46,3 +76,7 @@ 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 straywild Asset License.
diff --git a/CONTRIBUTOR-TERMS.md b/CONTRIBUTOR-TERMS.md
new file mode 100644
index 0000000..261de60
--- /dev/null
+++ b/CONTRIBUTOR-TERMS.md
@@ -0,0 +1,100 @@
+# straywild contributor terms
+
+straywild does not accept unsolicited contributions. These terms are retained
+as the governing record for contributions previously accepted after their
+publication and for specific future work that both Owners expressly invite in
+writing before submission. They are not a request or standing invitation for
+patches, pull requests, assets, or other material.
+
+These terms do not retroactively change ownership of earlier work or replace a
+separately signed agreement.
+
+“Owners” means the two straywild co-owners who publish the Project as
+Woofmeow and are publicly credited as Voyager and Endeavour. “Project” means
+the official straywild 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 straywild, its promotion, and official successor or
+derived straywild 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 straywild, 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, linked,
+emailed, or discussed. Unsolicited material will not be reviewed or accepted.
+The Owners or their authorized maintainers may modify, decline, or later
+remove previously accepted or expressly invited work.
+
+For work expressly invited in writing, intentionally delivering the requested
+material for Project inclusion after receiving notice of these terms records
+agreement to the applicable grants above. Maintainers must retain both the
+written invitation and the contributor's affirmative agreement in a durable
+Project record.
+
+Do not send unsolicited work. An owner invitation may instead specify a
+separate written arrangement signed by the appropriate parties.
diff --git a/CREDITS.md b/CREDITS.md
deleted file mode 100644
index ea0a6f5..0000000
--- a/CREDITS.md
+++ /dev/null
@@ -1,56 +0,0 @@
-# Credits
-
-## Project direction
-
-NETfishing is designed, directed, reviewed, and released by its project
-creator. A public-facing creator or studio credit has not yet been recorded in
-this repository.
-
-## Development assistance
-
-Development has used automated coding assistance for implementation,
-investigation, testing, documentation, and review. Design choices, supplied
-artwork, acceptance decisions, repository operations, and releases remain
-human-controlled.
-
-This disclosure is intentionally about process. Commit style, code style, or
-repository metadata cannot reliably prove whether a particular change was
-written with or without assistance.
-
-## Fonts
-
-- Tuffy: Thatcher Ulrich, Karoly Barta, and Michael Evans. The bundled license
- dedicates the work to the public domain; see `ui/fonts/Tuffy-LICENSE.txt`.
-- Seattle Avenue: bundled as `ui/fonts/seattle_avenue.otf`. Author and
- redistribution terms still need to be recorded before public distribution.
-
-## Music
-
-- `audio/music/title/as_in_four_wolves.ogg`: original title/ambient music by
- the project owner.
-
-## Sound effects
-
-- “Spinning reel.wav” by tosha73, used as the source for the fishing fight and
- manual-reeling loops. Freesound sound 509902, Creative Commons Zero (CC0):
- https://freesound.org/s/509902/
-- “Gentle Ocean Waves Loop” by kkenny101, used for the saltwater shoreline
- ambience. Freesound sound 852826, Creative Commons Zero (CC0):
- https://freesound.org/s/852826/
-- Rain Loop Ontario by Ayton -- https://freesound.org/s/212799/ -- License:
- Attribution 3.0
-- Dog_Bark.wav by ivolipa -- https://freesound.org/s/328729/ -- License:
- Creative Commons 0
-- Cat meow.m4a by Christyboy100 -- https://freesound.org/s/495694/ -- License:
- Attribution 3.0
-- “Quick Water Droplet” by qubodup, used for the bobber water-impact sound.
- Freesound sound 792931, Creative Commons Zero (CC0):
- https://freesound.org/s/792931/
-
-## Artwork
-
-- 2D artwork: Rheannon Eisworth, contributor.
-- 3D models: original work by the project owner.
-
-Asset categories and release-record requirements are listed in
-[`docs/ASSET-PROVENANCE.md`](docs/ASSET-PROVENANCE.md).
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
index 7c865fc..e228e7b 100644
--- a/README.md
+++ b/README.md
@@ -1,6 +1,6 @@
-# NETfishing
+# straywild
-NETfishing is a multiplayer-first fishing game built with Godot 4.7. The
+straywild 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.
@@ -8,6 +8,41 @@ 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.
+straywild is developed and published by **Woofmeow**, the independent team of
+co-owners publicly credited as Voyager and Endeavour.
+
+## Links
+Patreon: https://www.patreon.com/cw/woofmeowgames
+
+Steam: https://store.steampowered.com/app/5068950/straywild/
+
+Itch.io: https://sol6-vi.itch.io/straywild
+
+Discord: https://discord.gg/5gP22447kc
+
+Matrix: https://matrix.to/#/#straywild:matrix.makearmy.io
+
+Repo: https://forge.makearmy.io/woofmeow/straywild
+
+Releases: https://forge.makearmy.io/woofmeow/straywild/releases
+
+
+## Installing release builds
+
+Download and extract the archive for your platform from the official release
+page, then run straywild from the extracted platform folder.
+
+### macOS
+
+Current macOS builds are not signed or notarized. macOS may block the first
+launch because it cannot verify the developer. Control-click the straywild
+application, choose **Open**, then confirm **Open**. If that option is not
+available, attempt to open the application once, then use **System Settings →
+Privacy & Security → Open Anyway**.
+
+Only bypass this warning for a straywild build downloaded from an official
+project release.
+
## Requirements
- Godot 4.7.x with the GL Compatibility renderer
@@ -29,14 +64,14 @@ godot --path .
## Validation
The validation scripts isolate Godot's settings and application-data roots.
-Run the fast, deterministic suite before submitting a change:
+Run the fast, deterministic suite before committing a change:
```sh
scripts/run_validations.sh quick
```
-Additional suites and individual commands are documented in
-[`docs/TESTING.md`](docs/TESTING.md).
+Additional suites and individual commands are documented in the
+[`docs/DEVELOPMENT.md`](docs/DEVELOPMENT.md#validation) validation section.
## Building
@@ -65,6 +100,28 @@ scripts do not create or publish tags.
- `tests/`: focused Godot validation scripts
- `scripts/`: build, bake, and validation entry points
-See [`docs/ARCHITECTURE.md`](docs/ARCHITECTURE.md) and
-[`world/README-WORLD-AUTHORING.md`](world/README-WORLD-AUTHORING.md) for more
-detail.
+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 [straywild Asset License](ASSET-LICENSE.md). It permits community
+ Mods for official straywild software and narrowly scoped contribution forks,
+ but not independent forks, clones, or unrelated products.
+- The straywild 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).
+- Unsolicited contributions are not accepted. See
+ [CONTRIBUTING.md](CONTRIBUTING.md). Historical and expressly invited work is
+ governed by [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..fbf49d5
--- /dev/null
+++ b/TRADEMARKS.md
@@ -0,0 +1,56 @@
+# straywild and Woofmeow branding
+
+The straywild and Woofmeow names, logos, icons, trade dress, and other source
+identifiers are reserved by the straywild 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 straywild and Woofmeow truthfully to:
+
+- refer to the official game or development team;
+- state that software interoperates with or is a Mod for straywild;
+- 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 straywild” if its
+name and presentation remain clearly distinct and it displays an unofficial
+notice.
+
+## Contribution forks
+
+A qualifying Contribution Fork under `ASSET-LICENSE.md` may retain the
+straywild and Woofmeow names, logos, icons, and trade dress already present in
+the official source repository solely as necessary to reproduce, modify, and
+test the Project while preparing a proposed contribution. The source-control
+host's visible fork relationship or an equivalent repository notice must make
+its unofficial contribution purpose clear.
+
+This permission does not allow a Contribution Fork to use straywild or
+Woofmeow branding to advertise a public build, release, service, community
+edition, or independent product. It does not imply acceptance, sponsorship,
+endorsement, or official status.
+
+## Other forks and redistributed code
+
+The GNU GPL permits forks and redistribution of covered code. A fork or
+modified distribution that is not a qualifying Contribution Fork must:
+
+- use a distinct product name;
+- remove straywild 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
+straywild owners.
diff --git a/addons/straywild_shoreline_baker/plugin.cfg b/addons/straywild_shoreline_baker/plugin.cfg
new file mode 100644
index 0000000..c3f4e47
--- /dev/null
+++ b/addons/straywild_shoreline_baker/plugin.cfg
@@ -0,0 +1,7 @@
+[plugin]
+
+name="straywild 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/straywild_shoreline_baker/shoreline_auto_baker.gd b/addons/straywild_shoreline_baker/shoreline_auto_baker.gd
new file mode 100644
index 0000000..18da4af
--- /dev/null
+++ b/addons/straywild_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/straywild_shoreline_baker/shoreline_auto_baker.gd.uid b/addons/straywild_shoreline_baker/shoreline_auto_baker.gd.uid
new file mode 100644
index 0000000..a143fcb
--- /dev/null
+++ b/addons/straywild_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
deleted file mode 100644
index 15135f4..0000000
Binary files a/art/exported/characters/base/netfishing_base_character.glb and /dev/null differ
diff --git a/art/exported/characters/base/straywild_base_character.glb b/art/exported/characters/base/straywild_base_character.glb
new file mode 100644
index 0000000..58eb7cd
Binary files /dev/null and b/art/exported/characters/base/straywild_base_character.glb differ
diff --git a/art/exported/characters/base/netfishing_base_character.glb.import b/art/exported/characters/base/straywild_base_character.glb.import
similarity index 77%
rename from art/exported/characters/base/netfishing_base_character.glb.import
rename to art/exported/characters/base/straywild_base_character.glb.import
index b1409e1..96925de 100644
--- a/art/exported/characters/base/netfishing_base_character.glb.import
+++ b/art/exported/characters/base/straywild_base_character.glb.import
@@ -4,12 +4,12 @@ importer="scene"
importer_version=1
type="PackedScene"
uid="uid://w7n4gjqq1juc"
-path="res://.godot/imported/netfishing_base_character.glb-0167c087f16181d405afb962dc877d2c.scn"
+path="res://.godot/imported/straywild_base_character.glb-65021653e51a906d3b4a3494f170317f.scn"
[deps]
-source_file="res://art/exported/characters/base/netfishing_base_character.glb"
-dest_files=["res://.godot/imported/netfishing_base_character.glb-0167c087f16181d405afb962dc877d2c.scn"]
+source_file="res://art/exported/characters/base/straywild_base_character.glb"
+dest_files=["res://.godot/imported/straywild_base_character.glb-65021653e51a906d3b4a3494f170317f.scn"]
[params]
diff --git a/art/exported/characters/base/netfishing_base_character_eyes_simple_shine.png b/art/exported/characters/base/straywild_base_character_eyes_simple_shine.png
similarity index 100%
rename from art/exported/characters/base/netfishing_base_character_eyes_simple_shine.png
rename to art/exported/characters/base/straywild_base_character_eyes_simple_shine.png
diff --git a/art/exported/characters/base/netfishing_base_character_eyes_simple_shine.png.import b/art/exported/characters/base/straywild_base_character_eyes_simple_shine.png.import
similarity index 57%
rename from art/exported/characters/base/netfishing_base_character_eyes_simple_shine.png.import
rename to art/exported/characters/base/straywild_base_character_eyes_simple_shine.png.import
index 42a1604..ddf8bb7 100644
--- a/art/exported/characters/base/netfishing_base_character_eyes_simple_shine.png.import
+++ b/art/exported/characters/base/straywild_base_character_eyes_simple_shine.png.import
@@ -3,8 +3,8 @@
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"
+path.s3tc="res://.godot/imported/straywild_base_character_eyes_simple_shine.png-200a946ddace2078bbe44addb4e4524c.s3tc.ctex"
+path.etc2="res://.godot/imported/straywild_base_character_eyes_simple_shine.png-200a946ddace2078bbe44addb4e4524c.etc2.ctex"
metadata={
"imported_formats": ["s3tc_bptc", "etc2_astc"],
"vram_texture": true
@@ -15,8 +15,8 @@ generator_parameters={
[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"]
+source_file="res://art/exported/characters/base/straywild_base_character_eyes_simple_shine.png"
+dest_files=["res://.godot/imported/straywild_base_character_eyes_simple_shine.png-200a946ddace2078bbe44addb4e4524c.s3tc.ctex", "res://.godot/imported/straywild_base_character_eyes_simple_shine.png-200a946ddace2078bbe44addb4e4524c.etc2.ctex"]
[params]
@@ -28,7 +28,7 @@ compress/rdo_quality_loss=0.0
compress/hdr_compression=1
compress/normal_map=0
compress/channel_pack=0
-mipmaps/generate=true
+mipmaps/generate=false
mipmaps/limit=-1
roughness/mode=0
roughness/src_normal=""
diff --git a/art/exported/characters/base/netfishing_base_character_mouth_three.png b/art/exported/characters/base/straywild_base_character_mouth_three.png
similarity index 100%
rename from art/exported/characters/base/netfishing_base_character_mouth_three.png
rename to art/exported/characters/base/straywild_base_character_mouth_three.png
diff --git a/art/exported/characters/base/netfishing_base_character_mouth_three.png.import b/art/exported/characters/base/straywild_base_character_mouth_three.png.import
similarity index 58%
rename from art/exported/characters/base/netfishing_base_character_mouth_three.png.import
rename to art/exported/characters/base/straywild_base_character_mouth_three.png.import
index 5c94ab7..ba0ec2c 100644
--- a/art/exported/characters/base/netfishing_base_character_mouth_three.png.import
+++ b/art/exported/characters/base/straywild_base_character_mouth_three.png.import
@@ -3,8 +3,8 @@
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"
+path.s3tc="res://.godot/imported/straywild_base_character_mouth_three.png-5bcf329b31a11eff496760a63e62d0e2.s3tc.ctex"
+path.etc2="res://.godot/imported/straywild_base_character_mouth_three.png-5bcf329b31a11eff496760a63e62d0e2.etc2.ctex"
metadata={
"imported_formats": ["s3tc_bptc", "etc2_astc"],
"vram_texture": true
@@ -15,8 +15,8 @@ generator_parameters={
[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"]
+source_file="res://art/exported/characters/base/straywild_base_character_mouth_three.png"
+dest_files=["res://.godot/imported/straywild_base_character_mouth_three.png-5bcf329b31a11eff496760a63e62d0e2.s3tc.ctex", "res://.godot/imported/straywild_base_character_mouth_three.png-5bcf329b31a11eff496760a63e62d0e2.etc2.ctex"]
[params]
@@ -28,7 +28,7 @@ compress/rdo_quality_loss=0.0
compress/hdr_compression=1
compress/normal_map=0
compress/channel_pack=0
-mipmaps/generate=true
+mipmaps/generate=false
mipmaps/limit=-1
roughness/mode=0
roughness/src_normal=""
diff --git a/art/exported/characters/base/netfishing_base_character_nose_dog_round.png b/art/exported/characters/base/straywild_base_character_nose_dog_round.png
similarity index 100%
rename from art/exported/characters/base/netfishing_base_character_nose_dog_round.png
rename to art/exported/characters/base/straywild_base_character_nose_dog_round.png
diff --git a/art/exported/characters/base/netfishing_base_character_nose_dog_round.png.import b/art/exported/characters/base/straywild_base_character_nose_dog_round.png.import
similarity index 57%
rename from art/exported/characters/base/netfishing_base_character_nose_dog_round.png.import
rename to art/exported/characters/base/straywild_base_character_nose_dog_round.png.import
index 68c6e25..cb2c573 100644
--- a/art/exported/characters/base/netfishing_base_character_nose_dog_round.png.import
+++ b/art/exported/characters/base/straywild_base_character_nose_dog_round.png.import
@@ -3,8 +3,8 @@
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"
+path.s3tc="res://.godot/imported/straywild_base_character_nose_dog_round.png-e6909a2c139cf4a2748c2b79d1e12d92.s3tc.ctex"
+path.etc2="res://.godot/imported/straywild_base_character_nose_dog_round.png-e6909a2c139cf4a2748c2b79d1e12d92.etc2.ctex"
metadata={
"imported_formats": ["s3tc_bptc", "etc2_astc"],
"vram_texture": true
@@ -15,8 +15,8 @@ generator_parameters={
[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"]
+source_file="res://art/exported/characters/base/straywild_base_character_nose_dog_round.png"
+dest_files=["res://.godot/imported/straywild_base_character_nose_dog_round.png-e6909a2c139cf4a2748c2b79d1e12d92.s3tc.ctex", "res://.godot/imported/straywild_base_character_nose_dog_round.png-e6909a2c139cf4a2748c2b79d1e12d92.etc2.ctex"]
[params]
@@ -28,7 +28,7 @@ compress/rdo_quality_loss=0.0
compress/hdr_compression=1
compress/normal_map=0
compress/channel_pack=0
-mipmaps/generate=true
+mipmaps/generate=false
mipmaps/limit=-1
roughness/mode=0
roughness/src_normal=""
diff --git a/art/exported/characters/faces/README.md b/art/exported/characters/faces/README.md
deleted file mode 100644
index 06f3960..0000000
--- a/art/exported/characters/faces/README.md
+++ /dev/null
@@ -1,24 +0,0 @@
-# Drop-in facial features
-
-Facial feature textures are discovered when the game starts. Put new PNGs in
-the matching directory, restart the game, and the option will appear in the
-Profile customization page.
-
-```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` become the option ID `sleepy`.
-Filenames are normalized to lowercase snake_case. Use stable names because
-the resulting IDs are stored in appearance snapshots and sent to peers.
-
-Keep each PNG on the established facial-feature canvas with an RGBA
-transparent background. Godot imports the PNG automatically in the editor;
-the runtime registry ignores `.import` files and other non-PNG files.
-
-The current game bundles its facial assets under `res://`. Adding a file to a
-development checkout is picked up on the next startup. An exported build must
-be rebuilt to include newly added `res://` files in its package.
diff --git a/art/exported/characters/faces/eyes/eyes_alligator_extra.png.import b/art/exported/characters/faces/eyes/eyes_alligator_extra.png.import
index c9348c7..6be8860 100644
--- a/art/exported/characters/faces/eyes/eyes_alligator_extra.png.import
+++ b/art/exported/characters/faces/eyes/eyes_alligator_extra.png.import
@@ -3,19 +3,21 @@
importer="texture"
type="CompressedTexture2D"
uid="uid://c4rj1r1doawb4"
-path="res://.godot/imported/eyes_alligator_extra.png-30730001eeef37c24fac0158f6613bd7.ctex"
+path.s3tc="res://.godot/imported/eyes_alligator_extra.png-30730001eeef37c24fac0158f6613bd7.s3tc.ctex"
+path.etc2="res://.godot/imported/eyes_alligator_extra.png-30730001eeef37c24fac0158f6613bd7.etc2.ctex"
metadata={
-"vram_texture": false
+"imported_formats": ["s3tc_bptc", "etc2_astc"],
+"vram_texture": true
}
[deps]
source_file="res://art/exported/characters/faces/eyes/eyes_alligator_extra.png"
-dest_files=["res://.godot/imported/eyes_alligator_extra.png-30730001eeef37c24fac0158f6613bd7.ctex"]
+dest_files=["res://.godot/imported/eyes_alligator_extra.png-30730001eeef37c24fac0158f6613bd7.s3tc.ctex", "res://.godot/imported/eyes_alligator_extra.png-30730001eeef37c24fac0158f6613bd7.etc2.ctex"]
[params]
-compress/mode=0
+compress/mode=2
compress/high_quality=false
compress/lossy_quality=0.7
compress/uastc_level=0
@@ -36,5 +38,5 @@ 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
+process/size_limit=512
+detect_3d/compress_to=0
diff --git a/art/exported/characters/faces/eyes/eyes_alligator_eyes.png.import b/art/exported/characters/faces/eyes/eyes_alligator_eyes.png.import
index 7f2eb8d..410a5ef 100644
--- a/art/exported/characters/faces/eyes/eyes_alligator_eyes.png.import
+++ b/art/exported/characters/faces/eyes/eyes_alligator_eyes.png.import
@@ -3,19 +3,21 @@
importer="texture"
type="CompressedTexture2D"
uid="uid://br2h5sfqmool8"
-path="res://.godot/imported/eyes_alligator_eyes.png-843d36964858cdf7ffafe1972ffed681.ctex"
+path.s3tc="res://.godot/imported/eyes_alligator_eyes.png-843d36964858cdf7ffafe1972ffed681.s3tc.ctex"
+path.etc2="res://.godot/imported/eyes_alligator_eyes.png-843d36964858cdf7ffafe1972ffed681.etc2.ctex"
metadata={
-"vram_texture": false
+"imported_formats": ["s3tc_bptc", "etc2_astc"],
+"vram_texture": true
}
[deps]
source_file="res://art/exported/characters/faces/eyes/eyes_alligator_eyes.png"
-dest_files=["res://.godot/imported/eyes_alligator_eyes.png-843d36964858cdf7ffafe1972ffed681.ctex"]
+dest_files=["res://.godot/imported/eyes_alligator_eyes.png-843d36964858cdf7ffafe1972ffed681.s3tc.ctex", "res://.godot/imported/eyes_alligator_eyes.png-843d36964858cdf7ffafe1972ffed681.etc2.ctex"]
[params]
-compress/mode=0
+compress/mode=2
compress/high_quality=false
compress/lossy_quality=0.7
compress/uastc_level=0
@@ -36,5 +38,5 @@ 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
+process/size_limit=512
+detect_3d/compress_to=0
diff --git a/art/exported/characters/faces/eyes/eyes_angry.png.import b/art/exported/characters/faces/eyes/eyes_angry.png.import
index 3be46d5..ee3d3df 100644
--- a/art/exported/characters/faces/eyes/eyes_angry.png.import
+++ b/art/exported/characters/faces/eyes/eyes_angry.png.import
@@ -3,19 +3,21 @@
importer="texture"
type="CompressedTexture2D"
uid="uid://b1ieqa8qos1vk"
-path="res://.godot/imported/eyes_angry.png-a114dbeb3a8a14b8d1d86dd5a614b228.ctex"
+path.s3tc="res://.godot/imported/eyes_angry.png-a114dbeb3a8a14b8d1d86dd5a614b228.s3tc.ctex"
+path.etc2="res://.godot/imported/eyes_angry.png-a114dbeb3a8a14b8d1d86dd5a614b228.etc2.ctex"
metadata={
-"vram_texture": false
+"imported_formats": ["s3tc_bptc", "etc2_astc"],
+"vram_texture": true
}
[deps]
source_file="res://art/exported/characters/faces/eyes/eyes_angry.png"
-dest_files=["res://.godot/imported/eyes_angry.png-a114dbeb3a8a14b8d1d86dd5a614b228.ctex"]
+dest_files=["res://.godot/imported/eyes_angry.png-a114dbeb3a8a14b8d1d86dd5a614b228.s3tc.ctex", "res://.godot/imported/eyes_angry.png-a114dbeb3a8a14b8d1d86dd5a614b228.etc2.ctex"]
[params]
-compress/mode=0
+compress/mode=2
compress/high_quality=false
compress/lossy_quality=0.7
compress/uastc_level=0
@@ -36,5 +38,5 @@ 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
+process/size_limit=512
+detect_3d/compress_to=0
diff --git a/art/exported/characters/faces/eyes/eyes_anime_blue.png.import b/art/exported/characters/faces/eyes/eyes_anime_blue.png.import
index 443c8d0..6baa979 100644
--- a/art/exported/characters/faces/eyes/eyes_anime_blue.png.import
+++ b/art/exported/characters/faces/eyes/eyes_anime_blue.png.import
@@ -3,19 +3,21 @@
importer="texture"
type="CompressedTexture2D"
uid="uid://2ecbidhb33vy"
-path="res://.godot/imported/eyes_anime_blue.png-a86f72e375fc30cac2cd8320813c7c6c.ctex"
+path.s3tc="res://.godot/imported/eyes_anime_blue.png-a86f72e375fc30cac2cd8320813c7c6c.s3tc.ctex"
+path.etc2="res://.godot/imported/eyes_anime_blue.png-a86f72e375fc30cac2cd8320813c7c6c.etc2.ctex"
metadata={
-"vram_texture": false
+"imported_formats": ["s3tc_bptc", "etc2_astc"],
+"vram_texture": true
}
[deps]
source_file="res://art/exported/characters/faces/eyes/eyes_anime_blue.png"
-dest_files=["res://.godot/imported/eyes_anime_blue.png-a86f72e375fc30cac2cd8320813c7c6c.ctex"]
+dest_files=["res://.godot/imported/eyes_anime_blue.png-a86f72e375fc30cac2cd8320813c7c6c.s3tc.ctex", "res://.godot/imported/eyes_anime_blue.png-a86f72e375fc30cac2cd8320813c7c6c.etc2.ctex"]
[params]
-compress/mode=0
+compress/mode=2
compress/high_quality=false
compress/lossy_quality=0.7
compress/uastc_level=0
@@ -36,5 +38,5 @@ 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
+process/size_limit=512
+detect_3d/compress_to=0
diff --git a/art/exported/characters/faces/eyes/eyes_anime_green.png.import b/art/exported/characters/faces/eyes/eyes_anime_green.png.import
index a1be2b0..9c2a60c 100644
--- a/art/exported/characters/faces/eyes/eyes_anime_green.png.import
+++ b/art/exported/characters/faces/eyes/eyes_anime_green.png.import
@@ -3,19 +3,21 @@
importer="texture"
type="CompressedTexture2D"
uid="uid://bwrxrtiv375ne"
-path="res://.godot/imported/eyes_anime_green.png-a789a8f21c23e3085bca8992b3a3fc25.ctex"
+path.s3tc="res://.godot/imported/eyes_anime_green.png-a789a8f21c23e3085bca8992b3a3fc25.s3tc.ctex"
+path.etc2="res://.godot/imported/eyes_anime_green.png-a789a8f21c23e3085bca8992b3a3fc25.etc2.ctex"
metadata={
-"vram_texture": false
+"imported_formats": ["s3tc_bptc", "etc2_astc"],
+"vram_texture": true
}
[deps]
source_file="res://art/exported/characters/faces/eyes/eyes_anime_green.png"
-dest_files=["res://.godot/imported/eyes_anime_green.png-a789a8f21c23e3085bca8992b3a3fc25.ctex"]
+dest_files=["res://.godot/imported/eyes_anime_green.png-a789a8f21c23e3085bca8992b3a3fc25.s3tc.ctex", "res://.godot/imported/eyes_anime_green.png-a789a8f21c23e3085bca8992b3a3fc25.etc2.ctex"]
[params]
-compress/mode=0
+compress/mode=2
compress/high_quality=false
compress/lossy_quality=0.7
compress/uastc_level=0
@@ -36,5 +38,5 @@ 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
+process/size_limit=512
+detect_3d/compress_to=0
diff --git a/art/exported/characters/faces/eyes/eyes_beady.png.import b/art/exported/characters/faces/eyes/eyes_beady.png.import
index 829845c..ce4ddea 100644
--- a/art/exported/characters/faces/eyes/eyes_beady.png.import
+++ b/art/exported/characters/faces/eyes/eyes_beady.png.import
@@ -3,19 +3,21 @@
importer="texture"
type="CompressedTexture2D"
uid="uid://bahypju2dturm"
-path="res://.godot/imported/eyes_beady.png-18d3b3ff0818d80f773d4f72d0b0061e.ctex"
+path.s3tc="res://.godot/imported/eyes_beady.png-18d3b3ff0818d80f773d4f72d0b0061e.s3tc.ctex"
+path.etc2="res://.godot/imported/eyes_beady.png-18d3b3ff0818d80f773d4f72d0b0061e.etc2.ctex"
metadata={
-"vram_texture": false
+"imported_formats": ["s3tc_bptc", "etc2_astc"],
+"vram_texture": true
}
[deps]
source_file="res://art/exported/characters/faces/eyes/eyes_beady.png"
-dest_files=["res://.godot/imported/eyes_beady.png-18d3b3ff0818d80f773d4f72d0b0061e.ctex"]
+dest_files=["res://.godot/imported/eyes_beady.png-18d3b3ff0818d80f773d4f72d0b0061e.s3tc.ctex", "res://.godot/imported/eyes_beady.png-18d3b3ff0818d80f773d4f72d0b0061e.etc2.ctex"]
[params]
-compress/mode=0
+compress/mode=2
compress/high_quality=false
compress/lossy_quality=0.7
compress/uastc_level=0
@@ -36,5 +38,5 @@ 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
+process/size_limit=512
+detect_3d/compress_to=0
diff --git a/art/exported/characters/faces/eyes/eyes_beady_extra.png.import b/art/exported/characters/faces/eyes/eyes_beady_extra.png.import
index 2951456..29babb1 100644
--- a/art/exported/characters/faces/eyes/eyes_beady_extra.png.import
+++ b/art/exported/characters/faces/eyes/eyes_beady_extra.png.import
@@ -3,19 +3,21 @@
importer="texture"
type="CompressedTexture2D"
uid="uid://bqgdrdc0hpi7s"
-path="res://.godot/imported/eyes_beady_extra.png-d07d79345b1d22e55a1cf140f7682248.ctex"
+path.s3tc="res://.godot/imported/eyes_beady_extra.png-d07d79345b1d22e55a1cf140f7682248.s3tc.ctex"
+path.etc2="res://.godot/imported/eyes_beady_extra.png-d07d79345b1d22e55a1cf140f7682248.etc2.ctex"
metadata={
-"vram_texture": false
+"imported_formats": ["s3tc_bptc", "etc2_astc"],
+"vram_texture": true
}
[deps]
source_file="res://art/exported/characters/faces/eyes/eyes_beady_extra.png"
-dest_files=["res://.godot/imported/eyes_beady_extra.png-d07d79345b1d22e55a1cf140f7682248.ctex"]
+dest_files=["res://.godot/imported/eyes_beady_extra.png-d07d79345b1d22e55a1cf140f7682248.s3tc.ctex", "res://.godot/imported/eyes_beady_extra.png-d07d79345b1d22e55a1cf140f7682248.etc2.ctex"]
[params]
-compress/mode=0
+compress/mode=2
compress/high_quality=false
compress/lossy_quality=0.7
compress/uastc_level=0
@@ -36,5 +38,5 @@ 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
+process/size_limit=512
+detect_3d/compress_to=0
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
index fe258f0..047869d 100644
--- a/art/exported/characters/faces/eyes/eyes_beady_extra_shine.png.import
+++ b/art/exported/characters/faces/eyes/eyes_beady_extra_shine.png.import
@@ -3,19 +3,21 @@
importer="texture"
type="CompressedTexture2D"
uid="uid://mvjcdk84ko6f"
-path="res://.godot/imported/eyes_beady_extra_shine.png-c72a35f216a8c1a06122f7bd743ca6a4.ctex"
+path.s3tc="res://.godot/imported/eyes_beady_extra_shine.png-c72a35f216a8c1a06122f7bd743ca6a4.s3tc.ctex"
+path.etc2="res://.godot/imported/eyes_beady_extra_shine.png-c72a35f216a8c1a06122f7bd743ca6a4.etc2.ctex"
metadata={
-"vram_texture": false
+"imported_formats": ["s3tc_bptc", "etc2_astc"],
+"vram_texture": true
}
[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"]
+dest_files=["res://.godot/imported/eyes_beady_extra_shine.png-c72a35f216a8c1a06122f7bd743ca6a4.s3tc.ctex", "res://.godot/imported/eyes_beady_extra_shine.png-c72a35f216a8c1a06122f7bd743ca6a4.etc2.ctex"]
[params]
-compress/mode=0
+compress/mode=2
compress/high_quality=false
compress/lossy_quality=0.7
compress/uastc_level=0
@@ -36,5 +38,5 @@ 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
+process/size_limit=512
+detect_3d/compress_to=0
diff --git a/art/exported/characters/faces/eyes/eyes_beady_eyebrows.png.import b/art/exported/characters/faces/eyes/eyes_beady_eyebrows.png.import
index 4368bec..63a4b2f 100644
--- a/art/exported/characters/faces/eyes/eyes_beady_eyebrows.png.import
+++ b/art/exported/characters/faces/eyes/eyes_beady_eyebrows.png.import
@@ -3,19 +3,21 @@
importer="texture"
type="CompressedTexture2D"
uid="uid://dp4sm67vn52um"
-path="res://.godot/imported/eyes_beady_eyebrows.png-4f63e1a7ca603e9daf2d37a1d182e93f.ctex"
+path.s3tc="res://.godot/imported/eyes_beady_eyebrows.png-4f63e1a7ca603e9daf2d37a1d182e93f.s3tc.ctex"
+path.etc2="res://.godot/imported/eyes_beady_eyebrows.png-4f63e1a7ca603e9daf2d37a1d182e93f.etc2.ctex"
metadata={
-"vram_texture": false
+"imported_formats": ["s3tc_bptc", "etc2_astc"],
+"vram_texture": true
}
[deps]
source_file="res://art/exported/characters/faces/eyes/eyes_beady_eyebrows.png"
-dest_files=["res://.godot/imported/eyes_beady_eyebrows.png-4f63e1a7ca603e9daf2d37a1d182e93f.ctex"]
+dest_files=["res://.godot/imported/eyes_beady_eyebrows.png-4f63e1a7ca603e9daf2d37a1d182e93f.s3tc.ctex", "res://.godot/imported/eyes_beady_eyebrows.png-4f63e1a7ca603e9daf2d37a1d182e93f.etc2.ctex"]
[params]
-compress/mode=0
+compress/mode=2
compress/high_quality=false
compress/lossy_quality=0.7
compress/uastc_level=0
@@ -36,5 +38,5 @@ 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
+process/size_limit=512
+detect_3d/compress_to=0
diff --git a/art/exported/characters/faces/eyes/eyes_beady_whites.png.import b/art/exported/characters/faces/eyes/eyes_beady_whites.png.import
index 2b6680e..8503f0e 100644
--- a/art/exported/characters/faces/eyes/eyes_beady_whites.png.import
+++ b/art/exported/characters/faces/eyes/eyes_beady_whites.png.import
@@ -3,19 +3,21 @@
importer="texture"
type="CompressedTexture2D"
uid="uid://cw34wh51s5sxi"
-path="res://.godot/imported/eyes_beady_whites.png-87ce5dcbdfca9ca43a6f7c5ddca77bfa.ctex"
+path.s3tc="res://.godot/imported/eyes_beady_whites.png-87ce5dcbdfca9ca43a6f7c5ddca77bfa.s3tc.ctex"
+path.etc2="res://.godot/imported/eyes_beady_whites.png-87ce5dcbdfca9ca43a6f7c5ddca77bfa.etc2.ctex"
metadata={
-"vram_texture": false
+"imported_formats": ["s3tc_bptc", "etc2_astc"],
+"vram_texture": true
}
[deps]
source_file="res://art/exported/characters/faces/eyes/eyes_beady_whites.png"
-dest_files=["res://.godot/imported/eyes_beady_whites.png-87ce5dcbdfca9ca43a6f7c5ddca77bfa.ctex"]
+dest_files=["res://.godot/imported/eyes_beady_whites.png-87ce5dcbdfca9ca43a6f7c5ddca77bfa.s3tc.ctex", "res://.godot/imported/eyes_beady_whites.png-87ce5dcbdfca9ca43a6f7c5ddca77bfa.etc2.ctex"]
[params]
-compress/mode=0
+compress/mode=2
compress/high_quality=false
compress/lossy_quality=0.7
compress/uastc_level=0
@@ -36,5 +38,5 @@ 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
+process/size_limit=512
+detect_3d/compress_to=0
diff --git a/art/exported/characters/faces/eyes/eyes_carrot_eyes.png.import b/art/exported/characters/faces/eyes/eyes_carrot_eyes.png.import
index 80ef07a..6444d02 100644
--- a/art/exported/characters/faces/eyes/eyes_carrot_eyes.png.import
+++ b/art/exported/characters/faces/eyes/eyes_carrot_eyes.png.import
@@ -3,19 +3,21 @@
importer="texture"
type="CompressedTexture2D"
uid="uid://vn7mkjkx80ix"
-path="res://.godot/imported/eyes_carrot_eyes.png-4d4af86c340a0dd0633449a9c6cc1bae.ctex"
+path.s3tc="res://.godot/imported/eyes_carrot_eyes.png-4d4af86c340a0dd0633449a9c6cc1bae.s3tc.ctex"
+path.etc2="res://.godot/imported/eyes_carrot_eyes.png-4d4af86c340a0dd0633449a9c6cc1bae.etc2.ctex"
metadata={
-"vram_texture": false
+"imported_formats": ["s3tc_bptc", "etc2_astc"],
+"vram_texture": true
}
[deps]
source_file="res://art/exported/characters/faces/eyes/eyes_carrot_eyes.png"
-dest_files=["res://.godot/imported/eyes_carrot_eyes.png-4d4af86c340a0dd0633449a9c6cc1bae.ctex"]
+dest_files=["res://.godot/imported/eyes_carrot_eyes.png-4d4af86c340a0dd0633449a9c6cc1bae.s3tc.ctex", "res://.godot/imported/eyes_carrot_eyes.png-4d4af86c340a0dd0633449a9c6cc1bae.etc2.ctex"]
[params]
-compress/mode=0
+compress/mode=2
compress/high_quality=false
compress/lossy_quality=0.7
compress/uastc_level=0
@@ -36,5 +38,5 @@ 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
+process/size_limit=512
+detect_3d/compress_to=0
diff --git a/art/exported/characters/faces/eyes/eyes_cat_real.png.import b/art/exported/characters/faces/eyes/eyes_cat_real.png.import
index 2a4cbd3..25675c9 100644
--- a/art/exported/characters/faces/eyes/eyes_cat_real.png.import
+++ b/art/exported/characters/faces/eyes/eyes_cat_real.png.import
@@ -3,19 +3,21 @@
importer="texture"
type="CompressedTexture2D"
uid="uid://sbxopn55femq"
-path="res://.godot/imported/eyes_cat_real.png-13c9b6d280ac1000d177e74cbd33f07f.ctex"
+path.s3tc="res://.godot/imported/eyes_cat_real.png-13c9b6d280ac1000d177e74cbd33f07f.s3tc.ctex"
+path.etc2="res://.godot/imported/eyes_cat_real.png-13c9b6d280ac1000d177e74cbd33f07f.etc2.ctex"
metadata={
-"vram_texture": false
+"imported_formats": ["s3tc_bptc", "etc2_astc"],
+"vram_texture": true
}
[deps]
source_file="res://art/exported/characters/faces/eyes/eyes_cat_real.png"
-dest_files=["res://.godot/imported/eyes_cat_real.png-13c9b6d280ac1000d177e74cbd33f07f.ctex"]
+dest_files=["res://.godot/imported/eyes_cat_real.png-13c9b6d280ac1000d177e74cbd33f07f.s3tc.ctex", "res://.godot/imported/eyes_cat_real.png-13c9b6d280ac1000d177e74cbd33f07f.etc2.ctex"]
[params]
-compress/mode=0
+compress/mode=2
compress/high_quality=false
compress/lossy_quality=0.7
compress/uastc_level=0
@@ -36,5 +38,5 @@ 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
+process/size_limit=512
+detect_3d/compress_to=0
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
index 9d9b065..ab1c3f5 100644
--- a/art/exported/characters/faces/eyes/eyes_cat_real_hunter.png.import
+++ b/art/exported/characters/faces/eyes/eyes_cat_real_hunter.png.import
@@ -3,19 +3,21 @@
importer="texture"
type="CompressedTexture2D"
uid="uid://cg1l01m0ae2oa"
-path="res://.godot/imported/eyes_cat_real_hunter.png-6eb486fc423d7e3fa84f71aa30ec04f3.ctex"
+path.s3tc="res://.godot/imported/eyes_cat_real_hunter.png-6eb486fc423d7e3fa84f71aa30ec04f3.s3tc.ctex"
+path.etc2="res://.godot/imported/eyes_cat_real_hunter.png-6eb486fc423d7e3fa84f71aa30ec04f3.etc2.ctex"
metadata={
-"vram_texture": false
+"imported_formats": ["s3tc_bptc", "etc2_astc"],
+"vram_texture": true
}
[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"]
+dest_files=["res://.godot/imported/eyes_cat_real_hunter.png-6eb486fc423d7e3fa84f71aa30ec04f3.s3tc.ctex", "res://.godot/imported/eyes_cat_real_hunter.png-6eb486fc423d7e3fa84f71aa30ec04f3.etc2.ctex"]
[params]
-compress/mode=0
+compress/mode=2
compress/high_quality=false
compress/lossy_quality=0.7
compress/uastc_level=0
@@ -36,5 +38,5 @@ 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
+process/size_limit=512
+detect_3d/compress_to=0
diff --git a/art/exported/characters/faces/eyes/eyes_close.png.import b/art/exported/characters/faces/eyes/eyes_close.png.import
index 25986e6..e40b902 100644
--- a/art/exported/characters/faces/eyes/eyes_close.png.import
+++ b/art/exported/characters/faces/eyes/eyes_close.png.import
@@ -3,19 +3,21 @@
importer="texture"
type="CompressedTexture2D"
uid="uid://kbigjk76ejx8"
-path="res://.godot/imported/eyes_close.png-13a7da90295acf57ea489edcf26fc555.ctex"
+path.s3tc="res://.godot/imported/eyes_close.png-13a7da90295acf57ea489edcf26fc555.s3tc.ctex"
+path.etc2="res://.godot/imported/eyes_close.png-13a7da90295acf57ea489edcf26fc555.etc2.ctex"
metadata={
-"vram_texture": false
+"imported_formats": ["s3tc_bptc", "etc2_astc"],
+"vram_texture": true
}
[deps]
source_file="res://art/exported/characters/faces/eyes/eyes_close.png"
-dest_files=["res://.godot/imported/eyes_close.png-13a7da90295acf57ea489edcf26fc555.ctex"]
+dest_files=["res://.godot/imported/eyes_close.png-13a7da90295acf57ea489edcf26fc555.s3tc.ctex", "res://.godot/imported/eyes_close.png-13a7da90295acf57ea489edcf26fc555.etc2.ctex"]
[params]
-compress/mode=0
+compress/mode=2
compress/high_quality=false
compress/lossy_quality=0.7
compress/uastc_level=0
@@ -36,5 +38,5 @@ 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
+process/size_limit=512
+detect_3d/compress_to=0
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..325eb74
--- /dev/null
+++ b/art/exported/characters/faces/eyes/eyes_closed.png.import
@@ -0,0 +1,42 @@
+[remap]
+
+importer="texture"
+type="CompressedTexture2D"
+uid="uid://b2ufsni8mnpmq"
+path.s3tc="res://.godot/imported/eyes_closed.png-80789498475c2cfbcb6e3ac14e067a76.s3tc.ctex"
+path.etc2="res://.godot/imported/eyes_closed.png-80789498475c2cfbcb6e3ac14e067a76.etc2.ctex"
+metadata={
+"imported_formats": ["s3tc_bptc", "etc2_astc"],
+"vram_texture": true
+}
+
+[deps]
+
+source_file="res://art/exported/characters/faces/eyes/eyes_closed.png"
+dest_files=["res://.godot/imported/eyes_closed.png-80789498475c2cfbcb6e3ac14e067a76.s3tc.ctex", "res://.godot/imported/eyes_closed.png-80789498475c2cfbcb6e3ac14e067a76.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=512
+detect_3d/compress_to=0
diff --git a/art/exported/characters/faces/eyes/eyes_cute.png.import b/art/exported/characters/faces/eyes/eyes_cute.png.import
index c8d9f09..b19f9ef 100644
--- a/art/exported/characters/faces/eyes/eyes_cute.png.import
+++ b/art/exported/characters/faces/eyes/eyes_cute.png.import
@@ -3,19 +3,21 @@
importer="texture"
type="CompressedTexture2D"
uid="uid://b5455dbwse02f"
-path="res://.godot/imported/eyes_cute.png-e2eabbdf5281c15927e85f1e2f22b127.ctex"
+path.s3tc="res://.godot/imported/eyes_cute.png-e2eabbdf5281c15927e85f1e2f22b127.s3tc.ctex"
+path.etc2="res://.godot/imported/eyes_cute.png-e2eabbdf5281c15927e85f1e2f22b127.etc2.ctex"
metadata={
-"vram_texture": false
+"imported_formats": ["s3tc_bptc", "etc2_astc"],
+"vram_texture": true
}
[deps]
source_file="res://art/exported/characters/faces/eyes/eyes_cute.png"
-dest_files=["res://.godot/imported/eyes_cute.png-e2eabbdf5281c15927e85f1e2f22b127.ctex"]
+dest_files=["res://.godot/imported/eyes_cute.png-e2eabbdf5281c15927e85f1e2f22b127.s3tc.ctex", "res://.godot/imported/eyes_cute.png-e2eabbdf5281c15927e85f1e2f22b127.etc2.ctex"]
[params]
-compress/mode=0
+compress/mode=2
compress/high_quality=false
compress/lossy_quality=0.7
compress/uastc_level=0
@@ -36,5 +38,5 @@ 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
+process/size_limit=512
+detect_3d/compress_to=0
diff --git a/art/exported/characters/faces/eyes/eyes_cute_close.png.import b/art/exported/characters/faces/eyes/eyes_cute_close.png.import
index 5b51dc2..9a3c9b2 100644
--- a/art/exported/characters/faces/eyes/eyes_cute_close.png.import
+++ b/art/exported/characters/faces/eyes/eyes_cute_close.png.import
@@ -3,19 +3,21 @@
importer="texture"
type="CompressedTexture2D"
uid="uid://lp21yuhfqbc1"
-path="res://.godot/imported/eyes_cute_close.png-7a8a1b4e10864cfef604b9ced31a3f0c.ctex"
+path.s3tc="res://.godot/imported/eyes_cute_close.png-7a8a1b4e10864cfef604b9ced31a3f0c.s3tc.ctex"
+path.etc2="res://.godot/imported/eyes_cute_close.png-7a8a1b4e10864cfef604b9ced31a3f0c.etc2.ctex"
metadata={
-"vram_texture": false
+"imported_formats": ["s3tc_bptc", "etc2_astc"],
+"vram_texture": true
}
[deps]
source_file="res://art/exported/characters/faces/eyes/eyes_cute_close.png"
-dest_files=["res://.godot/imported/eyes_cute_close.png-7a8a1b4e10864cfef604b9ced31a3f0c.ctex"]
+dest_files=["res://.godot/imported/eyes_cute_close.png-7a8a1b4e10864cfef604b9ced31a3f0c.s3tc.ctex", "res://.godot/imported/eyes_cute_close.png-7a8a1b4e10864cfef604b9ced31a3f0c.etc2.ctex"]
[params]
-compress/mode=0
+compress/mode=2
compress/high_quality=false
compress/lossy_quality=0.7
compress/uastc_level=0
@@ -36,5 +38,5 @@ 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
+process/size_limit=512
+detect_3d/compress_to=0
diff --git a/art/exported/characters/faces/eyes/eyes_huge.png.import b/art/exported/characters/faces/eyes/eyes_huge.png.import
index 2c449bd..7b55837 100644
--- a/art/exported/characters/faces/eyes/eyes_huge.png.import
+++ b/art/exported/characters/faces/eyes/eyes_huge.png.import
@@ -3,19 +3,21 @@
importer="texture"
type="CompressedTexture2D"
uid="uid://vb1eaupu8fpm"
-path="res://.godot/imported/eyes_huge.png-9747926f369b1134a94639907057a08d.ctex"
+path.s3tc="res://.godot/imported/eyes_huge.png-9747926f369b1134a94639907057a08d.s3tc.ctex"
+path.etc2="res://.godot/imported/eyes_huge.png-9747926f369b1134a94639907057a08d.etc2.ctex"
metadata={
-"vram_texture": false
+"imported_formats": ["s3tc_bptc", "etc2_astc"],
+"vram_texture": true
}
[deps]
source_file="res://art/exported/characters/faces/eyes/eyes_huge.png"
-dest_files=["res://.godot/imported/eyes_huge.png-9747926f369b1134a94639907057a08d.ctex"]
+dest_files=["res://.godot/imported/eyes_huge.png-9747926f369b1134a94639907057a08d.s3tc.ctex", "res://.godot/imported/eyes_huge.png-9747926f369b1134a94639907057a08d.etc2.ctex"]
[params]
-compress/mode=0
+compress/mode=2
compress/high_quality=false
compress/lossy_quality=0.7
compress/uastc_level=0
@@ -36,5 +38,5 @@ 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
+process/size_limit=512
+detect_3d/compress_to=0
diff --git a/art/exported/characters/faces/eyes/eyes_interested.png.import b/art/exported/characters/faces/eyes/eyes_interested.png.import
index 061fe34..7f3a996 100644
--- a/art/exported/characters/faces/eyes/eyes_interested.png.import
+++ b/art/exported/characters/faces/eyes/eyes_interested.png.import
@@ -3,19 +3,21 @@
importer="texture"
type="CompressedTexture2D"
uid="uid://b3481384do6m4"
-path="res://.godot/imported/eyes_interested.png-5e8a34c0afaf7b4543f1b892af2227c7.ctex"
+path.s3tc="res://.godot/imported/eyes_interested.png-5e8a34c0afaf7b4543f1b892af2227c7.s3tc.ctex"
+path.etc2="res://.godot/imported/eyes_interested.png-5e8a34c0afaf7b4543f1b892af2227c7.etc2.ctex"
metadata={
-"vram_texture": false
+"imported_formats": ["s3tc_bptc", "etc2_astc"],
+"vram_texture": true
}
[deps]
source_file="res://art/exported/characters/faces/eyes/eyes_interested.png"
-dest_files=["res://.godot/imported/eyes_interested.png-5e8a34c0afaf7b4543f1b892af2227c7.ctex"]
+dest_files=["res://.godot/imported/eyes_interested.png-5e8a34c0afaf7b4543f1b892af2227c7.s3tc.ctex", "res://.godot/imported/eyes_interested.png-5e8a34c0afaf7b4543f1b892af2227c7.etc2.ctex"]
[params]
-compress/mode=0
+compress/mode=2
compress/high_quality=false
compress/lossy_quality=0.7
compress/uastc_level=0
@@ -36,5 +38,5 @@ 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
+process/size_limit=512
+detect_3d/compress_to=0
diff --git a/art/exported/characters/faces/eyes/eyes_jackolantern.png.import b/art/exported/characters/faces/eyes/eyes_jackolantern.png.import
index c447cdd..0515c3f 100644
--- a/art/exported/characters/faces/eyes/eyes_jackolantern.png.import
+++ b/art/exported/characters/faces/eyes/eyes_jackolantern.png.import
@@ -3,19 +3,21 @@
importer="texture"
type="CompressedTexture2D"
uid="uid://cdqqfoss11muq"
-path="res://.godot/imported/eyes_jackolantern.png-16dc8591f3bd827988f8fcb90bbced59.ctex"
+path.s3tc="res://.godot/imported/eyes_jackolantern.png-16dc8591f3bd827988f8fcb90bbced59.s3tc.ctex"
+path.etc2="res://.godot/imported/eyes_jackolantern.png-16dc8591f3bd827988f8fcb90bbced59.etc2.ctex"
metadata={
-"vram_texture": false
+"imported_formats": ["s3tc_bptc", "etc2_astc"],
+"vram_texture": true
}
[deps]
source_file="res://art/exported/characters/faces/eyes/eyes_jackolantern.png"
-dest_files=["res://.godot/imported/eyes_jackolantern.png-16dc8591f3bd827988f8fcb90bbced59.ctex"]
+dest_files=["res://.godot/imported/eyes_jackolantern.png-16dc8591f3bd827988f8fcb90bbced59.s3tc.ctex", "res://.godot/imported/eyes_jackolantern.png-16dc8591f3bd827988f8fcb90bbced59.etc2.ctex"]
[params]
-compress/mode=0
+compress/mode=2
compress/high_quality=false
compress/lossy_quality=0.7
compress/uastc_level=0
@@ -36,5 +38,5 @@ 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
+process/size_limit=512
+detect_3d/compress_to=0
diff --git a/art/exported/characters/faces/eyes/eyes_jackolantern_small.png.import b/art/exported/characters/faces/eyes/eyes_jackolantern_small.png.import
index 8de58a2..3c8fb9b 100644
--- a/art/exported/characters/faces/eyes/eyes_jackolantern_small.png.import
+++ b/art/exported/characters/faces/eyes/eyes_jackolantern_small.png.import
@@ -3,19 +3,21 @@
importer="texture"
type="CompressedTexture2D"
uid="uid://6n6riqe60s2f"
-path="res://.godot/imported/eyes_jackolantern_small.png-7608ab74472a3edb79fcddb1cafbcc2f.ctex"
+path.s3tc="res://.godot/imported/eyes_jackolantern_small.png-7608ab74472a3edb79fcddb1cafbcc2f.s3tc.ctex"
+path.etc2="res://.godot/imported/eyes_jackolantern_small.png-7608ab74472a3edb79fcddb1cafbcc2f.etc2.ctex"
metadata={
-"vram_texture": false
+"imported_formats": ["s3tc_bptc", "etc2_astc"],
+"vram_texture": true
}
[deps]
source_file="res://art/exported/characters/faces/eyes/eyes_jackolantern_small.png"
-dest_files=["res://.godot/imported/eyes_jackolantern_small.png-7608ab74472a3edb79fcddb1cafbcc2f.ctex"]
+dest_files=["res://.godot/imported/eyes_jackolantern_small.png-7608ab74472a3edb79fcddb1cafbcc2f.s3tc.ctex", "res://.godot/imported/eyes_jackolantern_small.png-7608ab74472a3edb79fcddb1cafbcc2f.etc2.ctex"]
[params]
-compress/mode=0
+compress/mode=2
compress/high_quality=false
compress/lossy_quality=0.7
compress/uastc_level=0
@@ -36,5 +38,5 @@ 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
+process/size_limit=512
+detect_3d/compress_to=0
diff --git a/art/exported/characters/faces/eyes/eyes_punk_purple.png.import b/art/exported/characters/faces/eyes/eyes_punk_purple.png.import
index 547a560..5e58771 100644
--- a/art/exported/characters/faces/eyes/eyes_punk_purple.png.import
+++ b/art/exported/characters/faces/eyes/eyes_punk_purple.png.import
@@ -3,19 +3,21 @@
importer="texture"
type="CompressedTexture2D"
uid="uid://b2c784my766ag"
-path="res://.godot/imported/eyes_punk_purple.png-fc0ccb3a5f1d2c23f2f367bb306d1ada.ctex"
+path.s3tc="res://.godot/imported/eyes_punk_purple.png-fc0ccb3a5f1d2c23f2f367bb306d1ada.s3tc.ctex"
+path.etc2="res://.godot/imported/eyes_punk_purple.png-fc0ccb3a5f1d2c23f2f367bb306d1ada.etc2.ctex"
metadata={
-"vram_texture": false
+"imported_formats": ["s3tc_bptc", "etc2_astc"],
+"vram_texture": true
}
[deps]
source_file="res://art/exported/characters/faces/eyes/eyes_punk_purple.png"
-dest_files=["res://.godot/imported/eyes_punk_purple.png-fc0ccb3a5f1d2c23f2f367bb306d1ada.ctex"]
+dest_files=["res://.godot/imported/eyes_punk_purple.png-fc0ccb3a5f1d2c23f2f367bb306d1ada.s3tc.ctex", "res://.godot/imported/eyes_punk_purple.png-fc0ccb3a5f1d2c23f2f367bb306d1ada.etc2.ctex"]
[params]
-compress/mode=0
+compress/mode=2
compress/high_quality=false
compress/lossy_quality=0.7
compress/uastc_level=0
@@ -36,5 +38,5 @@ 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
+process/size_limit=512
+detect_3d/compress_to=0
diff --git a/art/exported/characters/faces/eyes/eyes_sexy_blue.png.import b/art/exported/characters/faces/eyes/eyes_sexy_blue.png.import
index 7d2046d..dc29314 100644
--- a/art/exported/characters/faces/eyes/eyes_sexy_blue.png.import
+++ b/art/exported/characters/faces/eyes/eyes_sexy_blue.png.import
@@ -3,19 +3,21 @@
importer="texture"
type="CompressedTexture2D"
uid="uid://drajn1tk8gq4a"
-path="res://.godot/imported/eyes_sexy_blue.png-f99ecb6d89cb6c453161f6ad9fd5f802.ctex"
+path.s3tc="res://.godot/imported/eyes_sexy_blue.png-f99ecb6d89cb6c453161f6ad9fd5f802.s3tc.ctex"
+path.etc2="res://.godot/imported/eyes_sexy_blue.png-f99ecb6d89cb6c453161f6ad9fd5f802.etc2.ctex"
metadata={
-"vram_texture": false
+"imported_formats": ["s3tc_bptc", "etc2_astc"],
+"vram_texture": true
}
[deps]
source_file="res://art/exported/characters/faces/eyes/eyes_sexy_blue.png"
-dest_files=["res://.godot/imported/eyes_sexy_blue.png-f99ecb6d89cb6c453161f6ad9fd5f802.ctex"]
+dest_files=["res://.godot/imported/eyes_sexy_blue.png-f99ecb6d89cb6c453161f6ad9fd5f802.s3tc.ctex", "res://.godot/imported/eyes_sexy_blue.png-f99ecb6d89cb6c453161f6ad9fd5f802.etc2.ctex"]
[params]
-compress/mode=0
+compress/mode=2
compress/high_quality=false
compress/lossy_quality=0.7
compress/uastc_level=0
@@ -36,5 +38,5 @@ 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
+process/size_limit=512
+detect_3d/compress_to=0
diff --git a/art/exported/characters/faces/eyes/eyes_sexy_brown.png.import b/art/exported/characters/faces/eyes/eyes_sexy_brown.png.import
index a2b30f1..3abd188 100644
--- a/art/exported/characters/faces/eyes/eyes_sexy_brown.png.import
+++ b/art/exported/characters/faces/eyes/eyes_sexy_brown.png.import
@@ -3,19 +3,21 @@
importer="texture"
type="CompressedTexture2D"
uid="uid://c38u5tdksowpw"
-path="res://.godot/imported/eyes_sexy_brown.png-e77cd183e70234d3ee7772ed2837cf34.ctex"
+path.s3tc="res://.godot/imported/eyes_sexy_brown.png-e77cd183e70234d3ee7772ed2837cf34.s3tc.ctex"
+path.etc2="res://.godot/imported/eyes_sexy_brown.png-e77cd183e70234d3ee7772ed2837cf34.etc2.ctex"
metadata={
-"vram_texture": false
+"imported_formats": ["s3tc_bptc", "etc2_astc"],
+"vram_texture": true
}
[deps]
source_file="res://art/exported/characters/faces/eyes/eyes_sexy_brown.png"
-dest_files=["res://.godot/imported/eyes_sexy_brown.png-e77cd183e70234d3ee7772ed2837cf34.ctex"]
+dest_files=["res://.godot/imported/eyes_sexy_brown.png-e77cd183e70234d3ee7772ed2837cf34.s3tc.ctex", "res://.godot/imported/eyes_sexy_brown.png-e77cd183e70234d3ee7772ed2837cf34.etc2.ctex"]
[params]
-compress/mode=0
+compress/mode=2
compress/high_quality=false
compress/lossy_quality=0.7
compress/uastc_level=0
@@ -36,5 +38,5 @@ 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
+process/size_limit=512
+detect_3d/compress_to=0
diff --git a/art/exported/characters/faces/eyes/eyes_spider.png.import b/art/exported/characters/faces/eyes/eyes_spider.png.import
index 3b137b9..b274e66 100644
--- a/art/exported/characters/faces/eyes/eyes_spider.png.import
+++ b/art/exported/characters/faces/eyes/eyes_spider.png.import
@@ -3,19 +3,21 @@
importer="texture"
type="CompressedTexture2D"
uid="uid://bevrdbag3eixb"
-path="res://.godot/imported/eyes_spider.png-4cbe85913f69207b18d4b23095aca596.ctex"
+path.s3tc="res://.godot/imported/eyes_spider.png-4cbe85913f69207b18d4b23095aca596.s3tc.ctex"
+path.etc2="res://.godot/imported/eyes_spider.png-4cbe85913f69207b18d4b23095aca596.etc2.ctex"
metadata={
-"vram_texture": false
+"imported_formats": ["s3tc_bptc", "etc2_astc"],
+"vram_texture": true
}
[deps]
source_file="res://art/exported/characters/faces/eyes/eyes_spider.png"
-dest_files=["res://.godot/imported/eyes_spider.png-4cbe85913f69207b18d4b23095aca596.ctex"]
+dest_files=["res://.godot/imported/eyes_spider.png-4cbe85913f69207b18d4b23095aca596.s3tc.ctex", "res://.godot/imported/eyes_spider.png-4cbe85913f69207b18d4b23095aca596.etc2.ctex"]
[params]
-compress/mode=0
+compress/mode=2
compress/high_quality=false
compress/lossy_quality=0.7
compress/uastc_level=0
@@ -36,5 +38,5 @@ 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
+process/size_limit=512
+detect_3d/compress_to=0
diff --git a/art/exported/characters/faces/eyes/eyes_surprised.png.import b/art/exported/characters/faces/eyes/eyes_surprised.png.import
index 0d7907b..d83d6c5 100644
--- a/art/exported/characters/faces/eyes/eyes_surprised.png.import
+++ b/art/exported/characters/faces/eyes/eyes_surprised.png.import
@@ -3,19 +3,21 @@
importer="texture"
type="CompressedTexture2D"
uid="uid://chvod4aqeqn10"
-path="res://.godot/imported/eyes_surprised.png-21858dc3f7520b1423e3d0aff59b6896.ctex"
+path.s3tc="res://.godot/imported/eyes_surprised.png-21858dc3f7520b1423e3d0aff59b6896.s3tc.ctex"
+path.etc2="res://.godot/imported/eyes_surprised.png-21858dc3f7520b1423e3d0aff59b6896.etc2.ctex"
metadata={
-"vram_texture": false
+"imported_formats": ["s3tc_bptc", "etc2_astc"],
+"vram_texture": true
}
[deps]
source_file="res://art/exported/characters/faces/eyes/eyes_surprised.png"
-dest_files=["res://.godot/imported/eyes_surprised.png-21858dc3f7520b1423e3d0aff59b6896.ctex"]
+dest_files=["res://.godot/imported/eyes_surprised.png-21858dc3f7520b1423e3d0aff59b6896.s3tc.ctex", "res://.godot/imported/eyes_surprised.png-21858dc3f7520b1423e3d0aff59b6896.etc2.ctex"]
[params]
-compress/mode=0
+compress/mode=2
compress/high_quality=false
compress/lossy_quality=0.7
compress/uastc_level=0
@@ -36,5 +38,5 @@ 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
+process/size_limit=512
+detect_3d/compress_to=0
diff --git a/art/exported/characters/faces/eyes/eyes_tired.png.import b/art/exported/characters/faces/eyes/eyes_tired.png.import
index 9de4b0f..70a45ab 100644
--- a/art/exported/characters/faces/eyes/eyes_tired.png.import
+++ b/art/exported/characters/faces/eyes/eyes_tired.png.import
@@ -3,19 +3,21 @@
importer="texture"
type="CompressedTexture2D"
uid="uid://bimnlu17t6u6v"
-path="res://.godot/imported/eyes_tired.png-752efd150fd50cc078ee25c8bbbeeb36.ctex"
+path.s3tc="res://.godot/imported/eyes_tired.png-752efd150fd50cc078ee25c8bbbeeb36.s3tc.ctex"
+path.etc2="res://.godot/imported/eyes_tired.png-752efd150fd50cc078ee25c8bbbeeb36.etc2.ctex"
metadata={
-"vram_texture": false
+"imported_formats": ["s3tc_bptc", "etc2_astc"],
+"vram_texture": true
}
[deps]
source_file="res://art/exported/characters/faces/eyes/eyes_tired.png"
-dest_files=["res://.godot/imported/eyes_tired.png-752efd150fd50cc078ee25c8bbbeeb36.ctex"]
+dest_files=["res://.godot/imported/eyes_tired.png-752efd150fd50cc078ee25c8bbbeeb36.s3tc.ctex", "res://.godot/imported/eyes_tired.png-752efd150fd50cc078ee25c8bbbeeb36.etc2.ctex"]
[params]
-compress/mode=0
+compress/mode=2
compress/high_quality=false
compress/lossy_quality=0.7
compress/uastc_level=0
@@ -36,5 +38,5 @@ 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
+process/size_limit=512
+detect_3d/compress_to=0
diff --git a/art/exported/characters/faces/eyes/eyes_tired_flat.png.import b/art/exported/characters/faces/eyes/eyes_tired_flat.png.import
index 5a2ec55..831ff6d 100644
--- a/art/exported/characters/faces/eyes/eyes_tired_flat.png.import
+++ b/art/exported/characters/faces/eyes/eyes_tired_flat.png.import
@@ -3,19 +3,21 @@
importer="texture"
type="CompressedTexture2D"
uid="uid://b4hmrvoblu3cl"
-path="res://.godot/imported/eyes_tired_flat.png-c441cfd39c6147e6a13989a4dd86be05.ctex"
+path.s3tc="res://.godot/imported/eyes_tired_flat.png-c441cfd39c6147e6a13989a4dd86be05.s3tc.ctex"
+path.etc2="res://.godot/imported/eyes_tired_flat.png-c441cfd39c6147e6a13989a4dd86be05.etc2.ctex"
metadata={
-"vram_texture": false
+"imported_formats": ["s3tc_bptc", "etc2_astc"],
+"vram_texture": true
}
[deps]
source_file="res://art/exported/characters/faces/eyes/eyes_tired_flat.png"
-dest_files=["res://.godot/imported/eyes_tired_flat.png-c441cfd39c6147e6a13989a4dd86be05.ctex"]
+dest_files=["res://.godot/imported/eyes_tired_flat.png-c441cfd39c6147e6a13989a4dd86be05.s3tc.ctex", "res://.godot/imported/eyes_tired_flat.png-c441cfd39c6147e6a13989a4dd86be05.etc2.ctex"]
[params]
-compress/mode=0
+compress/mode=2
compress/high_quality=false
compress/lossy_quality=0.7
compress/uastc_level=0
@@ -36,5 +38,5 @@ 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
+process/size_limit=512
+detect_3d/compress_to=0
diff --git a/art/exported/characters/faces/eyes/eyes_upset.png.import b/art/exported/characters/faces/eyes/eyes_upset.png.import
index 0c455d4..a0b51d5 100644
--- a/art/exported/characters/faces/eyes/eyes_upset.png.import
+++ b/art/exported/characters/faces/eyes/eyes_upset.png.import
@@ -3,19 +3,21 @@
importer="texture"
type="CompressedTexture2D"
uid="uid://blm1i6xyjclp5"
-path="res://.godot/imported/eyes_upset.png-a8509f19cb25f577e84ebe86d28a31b1.ctex"
+path.s3tc="res://.godot/imported/eyes_upset.png-a8509f19cb25f577e84ebe86d28a31b1.s3tc.ctex"
+path.etc2="res://.godot/imported/eyes_upset.png-a8509f19cb25f577e84ebe86d28a31b1.etc2.ctex"
metadata={
-"vram_texture": false
+"imported_formats": ["s3tc_bptc", "etc2_astc"],
+"vram_texture": true
}
[deps]
source_file="res://art/exported/characters/faces/eyes/eyes_upset.png"
-dest_files=["res://.godot/imported/eyes_upset.png-a8509f19cb25f577e84ebe86d28a31b1.ctex"]
+dest_files=["res://.godot/imported/eyes_upset.png-a8509f19cb25f577e84ebe86d28a31b1.s3tc.ctex", "res://.godot/imported/eyes_upset.png-a8509f19cb25f577e84ebe86d28a31b1.etc2.ctex"]
[params]
-compress/mode=0
+compress/mode=2
compress/high_quality=false
compress/lossy_quality=0.7
compress/uastc_level=0
@@ -36,5 +38,5 @@ 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
+process/size_limit=512
+detect_3d/compress_to=0
diff --git a/art/exported/characters/faces/mouth/mouth_3.png.import b/art/exported/characters/faces/mouth/mouth_3.png.import
index eab9f48..3da8cbf 100644
--- a/art/exported/characters/faces/mouth/mouth_3.png.import
+++ b/art/exported/characters/faces/mouth/mouth_3.png.import
@@ -3,19 +3,21 @@
importer="texture"
type="CompressedTexture2D"
uid="uid://b5wnxcm61q4ou"
-path="res://.godot/imported/mouth_3.png-9fd9edc312c3b2c33df59da33d62bb30.ctex"
+path.s3tc="res://.godot/imported/mouth_3.png-9fd9edc312c3b2c33df59da33d62bb30.s3tc.ctex"
+path.etc2="res://.godot/imported/mouth_3.png-9fd9edc312c3b2c33df59da33d62bb30.etc2.ctex"
metadata={
-"vram_texture": false
+"imported_formats": ["s3tc_bptc", "etc2_astc"],
+"vram_texture": true
}
[deps]
source_file="res://art/exported/characters/faces/mouth/mouth_3.png"
-dest_files=["res://.godot/imported/mouth_3.png-9fd9edc312c3b2c33df59da33d62bb30.ctex"]
+dest_files=["res://.godot/imported/mouth_3.png-9fd9edc312c3b2c33df59da33d62bb30.s3tc.ctex", "res://.godot/imported/mouth_3.png-9fd9edc312c3b2c33df59da33d62bb30.etc2.ctex"]
[params]
-compress/mode=0
+compress/mode=2
compress/high_quality=false
compress/lossy_quality=0.7
compress/uastc_level=0
@@ -36,5 +38,5 @@ 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
+process/size_limit=512
+detect_3d/compress_to=0
diff --git a/art/exported/characters/faces/mouth/mouth_3_cheeky.png.import b/art/exported/characters/faces/mouth/mouth_3_cheeky.png.import
index a53b280..f212e57 100644
--- a/art/exported/characters/faces/mouth/mouth_3_cheeky.png.import
+++ b/art/exported/characters/faces/mouth/mouth_3_cheeky.png.import
@@ -3,19 +3,21 @@
importer="texture"
type="CompressedTexture2D"
uid="uid://dhaec7illng60"
-path="res://.godot/imported/mouth_3_cheeky.png-1c9ae1bcb18ff29fe44e13b55b692f57.ctex"
+path.s3tc="res://.godot/imported/mouth_3_cheeky.png-1c9ae1bcb18ff29fe44e13b55b692f57.s3tc.ctex"
+path.etc2="res://.godot/imported/mouth_3_cheeky.png-1c9ae1bcb18ff29fe44e13b55b692f57.etc2.ctex"
metadata={
-"vram_texture": false
+"imported_formats": ["s3tc_bptc", "etc2_astc"],
+"vram_texture": true
}
[deps]
source_file="res://art/exported/characters/faces/mouth/mouth_3_cheeky.png"
-dest_files=["res://.godot/imported/mouth_3_cheeky.png-1c9ae1bcb18ff29fe44e13b55b692f57.ctex"]
+dest_files=["res://.godot/imported/mouth_3_cheeky.png-1c9ae1bcb18ff29fe44e13b55b692f57.s3tc.ctex", "res://.godot/imported/mouth_3_cheeky.png-1c9ae1bcb18ff29fe44e13b55b692f57.etc2.ctex"]
[params]
-compress/mode=0
+compress/mode=2
compress/high_quality=false
compress/lossy_quality=0.7
compress/uastc_level=0
@@ -36,5 +38,5 @@ 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
+process/size_limit=512
+detect_3d/compress_to=0
diff --git a/art/exported/characters/faces/mouth/mouth_buck teeth.png.import b/art/exported/characters/faces/mouth/mouth_buck teeth.png.import
index 8909223..6e837c8 100644
--- a/art/exported/characters/faces/mouth/mouth_buck teeth.png.import
+++ b/art/exported/characters/faces/mouth/mouth_buck teeth.png.import
@@ -3,19 +3,21 @@
importer="texture"
type="CompressedTexture2D"
uid="uid://dmvxpg0jbiyqw"
-path="res://.godot/imported/mouth_buck teeth.png-84be0f368b98b8640d5cc51d8a9b956f.ctex"
+path.s3tc="res://.godot/imported/mouth_buck teeth.png-84be0f368b98b8640d5cc51d8a9b956f.s3tc.ctex"
+path.etc2="res://.godot/imported/mouth_buck teeth.png-84be0f368b98b8640d5cc51d8a9b956f.etc2.ctex"
metadata={
-"vram_texture": false
+"imported_formats": ["s3tc_bptc", "etc2_astc"],
+"vram_texture": true
}
[deps]
source_file="res://art/exported/characters/faces/mouth/mouth_buck teeth.png"
-dest_files=["res://.godot/imported/mouth_buck teeth.png-84be0f368b98b8640d5cc51d8a9b956f.ctex"]
+dest_files=["res://.godot/imported/mouth_buck teeth.png-84be0f368b98b8640d5cc51d8a9b956f.s3tc.ctex", "res://.godot/imported/mouth_buck teeth.png-84be0f368b98b8640d5cc51d8a9b956f.etc2.ctex"]
[params]
-compress/mode=0
+compress/mode=2
compress/high_quality=false
compress/lossy_quality=0.7
compress/uastc_level=0
@@ -36,5 +38,5 @@ 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
+process/size_limit=512
+detect_3d/compress_to=0
diff --git a/art/exported/characters/faces/mouth/mouth_cheeky_smile.png.import b/art/exported/characters/faces/mouth/mouth_cheeky_smile.png.import
index 926ef6c..dce960a 100644
--- a/art/exported/characters/faces/mouth/mouth_cheeky_smile.png.import
+++ b/art/exported/characters/faces/mouth/mouth_cheeky_smile.png.import
@@ -3,19 +3,21 @@
importer="texture"
type="CompressedTexture2D"
uid="uid://8tdtfnv3bgft"
-path="res://.godot/imported/mouth_cheeky_smile.png-d55ec6f5fd7e73fd8793d524475f9f1f.ctex"
+path.s3tc="res://.godot/imported/mouth_cheeky_smile.png-d55ec6f5fd7e73fd8793d524475f9f1f.s3tc.ctex"
+path.etc2="res://.godot/imported/mouth_cheeky_smile.png-d55ec6f5fd7e73fd8793d524475f9f1f.etc2.ctex"
metadata={
-"vram_texture": false
+"imported_formats": ["s3tc_bptc", "etc2_astc"],
+"vram_texture": true
}
[deps]
source_file="res://art/exported/characters/faces/mouth/mouth_cheeky_smile.png"
-dest_files=["res://.godot/imported/mouth_cheeky_smile.png-d55ec6f5fd7e73fd8793d524475f9f1f.ctex"]
+dest_files=["res://.godot/imported/mouth_cheeky_smile.png-d55ec6f5fd7e73fd8793d524475f9f1f.s3tc.ctex", "res://.godot/imported/mouth_cheeky_smile.png-d55ec6f5fd7e73fd8793d524475f9f1f.etc2.ctex"]
[params]
-compress/mode=0
+compress/mode=2
compress/high_quality=false
compress/lossy_quality=0.7
compress/uastc_level=0
@@ -36,5 +38,5 @@ 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
+process/size_limit=512
+detect_3d/compress_to=0
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
index 0b1450a..f500e42 100644
--- a/art/exported/characters/faces/mouth/mouth_cheeky_smile_onesided.png.import
+++ b/art/exported/characters/faces/mouth/mouth_cheeky_smile_onesided.png.import
@@ -3,19 +3,21 @@
importer="texture"
type="CompressedTexture2D"
uid="uid://8xyawmp5gru8"
-path="res://.godot/imported/mouth_cheeky_smile_onesided.png-366c346f7be47b1521677b84666a672b.ctex"
+path.s3tc="res://.godot/imported/mouth_cheeky_smile_onesided.png-366c346f7be47b1521677b84666a672b.s3tc.ctex"
+path.etc2="res://.godot/imported/mouth_cheeky_smile_onesided.png-366c346f7be47b1521677b84666a672b.etc2.ctex"
metadata={
-"vram_texture": false
+"imported_formats": ["s3tc_bptc", "etc2_astc"],
+"vram_texture": true
}
[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"]
+dest_files=["res://.godot/imported/mouth_cheeky_smile_onesided.png-366c346f7be47b1521677b84666a672b.s3tc.ctex", "res://.godot/imported/mouth_cheeky_smile_onesided.png-366c346f7be47b1521677b84666a672b.etc2.ctex"]
[params]
-compress/mode=0
+compress/mode=2
compress/high_quality=false
compress/lossy_quality=0.7
compress/uastc_level=0
@@ -36,5 +38,5 @@ 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
+process/size_limit=512
+detect_3d/compress_to=0
diff --git a/art/exported/characters/faces/mouth/mouth_jackolantern.png.import b/art/exported/characters/faces/mouth/mouth_jackolantern.png.import
index 1756a04..6c0861a 100644
--- a/art/exported/characters/faces/mouth/mouth_jackolantern.png.import
+++ b/art/exported/characters/faces/mouth/mouth_jackolantern.png.import
@@ -3,19 +3,21 @@
importer="texture"
type="CompressedTexture2D"
uid="uid://ck4bbdnpf6ekd"
-path="res://.godot/imported/mouth_jackolantern.png-7f9373a2b59e617edd25d92ef63041f0.ctex"
+path.s3tc="res://.godot/imported/mouth_jackolantern.png-7f9373a2b59e617edd25d92ef63041f0.s3tc.ctex"
+path.etc2="res://.godot/imported/mouth_jackolantern.png-7f9373a2b59e617edd25d92ef63041f0.etc2.ctex"
metadata={
-"vram_texture": false
+"imported_formats": ["s3tc_bptc", "etc2_astc"],
+"vram_texture": true
}
[deps]
source_file="res://art/exported/characters/faces/mouth/mouth_jackolantern.png"
-dest_files=["res://.godot/imported/mouth_jackolantern.png-7f9373a2b59e617edd25d92ef63041f0.ctex"]
+dest_files=["res://.godot/imported/mouth_jackolantern.png-7f9373a2b59e617edd25d92ef63041f0.s3tc.ctex", "res://.godot/imported/mouth_jackolantern.png-7f9373a2b59e617edd25d92ef63041f0.etc2.ctex"]
[params]
-compress/mode=0
+compress/mode=2
compress/high_quality=false
compress/lossy_quality=0.7
compress/uastc_level=0
@@ -36,5 +38,5 @@ 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
+process/size_limit=512
+detect_3d/compress_to=0
diff --git a/art/exported/characters/faces/mouth/mouth_jowls.png.import b/art/exported/characters/faces/mouth/mouth_jowls.png.import
index 1af65fe..7f5823b 100644
--- a/art/exported/characters/faces/mouth/mouth_jowls.png.import
+++ b/art/exported/characters/faces/mouth/mouth_jowls.png.import
@@ -3,19 +3,21 @@
importer="texture"
type="CompressedTexture2D"
uid="uid://dyynmci0hf3vq"
-path="res://.godot/imported/mouth_jowls.png-88cd6159c02a5b4eee74b98dc8248dcd.ctex"
+path.s3tc="res://.godot/imported/mouth_jowls.png-88cd6159c02a5b4eee74b98dc8248dcd.s3tc.ctex"
+path.etc2="res://.godot/imported/mouth_jowls.png-88cd6159c02a5b4eee74b98dc8248dcd.etc2.ctex"
metadata={
-"vram_texture": false
+"imported_formats": ["s3tc_bptc", "etc2_astc"],
+"vram_texture": true
}
[deps]
source_file="res://art/exported/characters/faces/mouth/mouth_jowls.png"
-dest_files=["res://.godot/imported/mouth_jowls.png-88cd6159c02a5b4eee74b98dc8248dcd.ctex"]
+dest_files=["res://.godot/imported/mouth_jowls.png-88cd6159c02a5b4eee74b98dc8248dcd.s3tc.ctex", "res://.godot/imported/mouth_jowls.png-88cd6159c02a5b4eee74b98dc8248dcd.etc2.ctex"]
[params]
-compress/mode=0
+compress/mode=2
compress/high_quality=false
compress/lossy_quality=0.7
compress/uastc_level=0
@@ -36,5 +38,5 @@ 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
+process/size_limit=512
+detect_3d/compress_to=0
diff --git a/art/exported/characters/faces/mouth/mouth_jowls_brock.png.import b/art/exported/characters/faces/mouth/mouth_jowls_brock.png.import
index d2ec5c4..0461534 100644
--- a/art/exported/characters/faces/mouth/mouth_jowls_brock.png.import
+++ b/art/exported/characters/faces/mouth/mouth_jowls_brock.png.import
@@ -3,19 +3,21 @@
importer="texture"
type="CompressedTexture2D"
uid="uid://duajvlqrieray"
-path="res://.godot/imported/mouth_jowls_brock.png-de76cffdce72eb30289c44bcb2e68ce6.ctex"
+path.s3tc="res://.godot/imported/mouth_jowls_brock.png-de76cffdce72eb30289c44bcb2e68ce6.s3tc.ctex"
+path.etc2="res://.godot/imported/mouth_jowls_brock.png-de76cffdce72eb30289c44bcb2e68ce6.etc2.ctex"
metadata={
-"vram_texture": false
+"imported_formats": ["s3tc_bptc", "etc2_astc"],
+"vram_texture": true
}
[deps]
source_file="res://art/exported/characters/faces/mouth/mouth_jowls_brock.png"
-dest_files=["res://.godot/imported/mouth_jowls_brock.png-de76cffdce72eb30289c44bcb2e68ce6.ctex"]
+dest_files=["res://.godot/imported/mouth_jowls_brock.png-de76cffdce72eb30289c44bcb2e68ce6.s3tc.ctex", "res://.godot/imported/mouth_jowls_brock.png-de76cffdce72eb30289c44bcb2e68ce6.etc2.ctex"]
[params]
-compress/mode=0
+compress/mode=2
compress/high_quality=false
compress/lossy_quality=0.7
compress/uastc_level=0
@@ -36,5 +38,5 @@ 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
+process/size_limit=512
+detect_3d/compress_to=0
diff --git a/art/exported/characters/faces/mouth/mouth_jowls_simple.png.import b/art/exported/characters/faces/mouth/mouth_jowls_simple.png.import
index f6cf4a0..fc1ad41 100644
--- a/art/exported/characters/faces/mouth/mouth_jowls_simple.png.import
+++ b/art/exported/characters/faces/mouth/mouth_jowls_simple.png.import
@@ -3,19 +3,21 @@
importer="texture"
type="CompressedTexture2D"
uid="uid://c37gty8itl5oe"
-path="res://.godot/imported/mouth_jowls_simple.png-0586400ebfd9ef98c9067f0ebbe730be.ctex"
+path.s3tc="res://.godot/imported/mouth_jowls_simple.png-0586400ebfd9ef98c9067f0ebbe730be.s3tc.ctex"
+path.etc2="res://.godot/imported/mouth_jowls_simple.png-0586400ebfd9ef98c9067f0ebbe730be.etc2.ctex"
metadata={
-"vram_texture": false
+"imported_formats": ["s3tc_bptc", "etc2_astc"],
+"vram_texture": true
}
[deps]
source_file="res://art/exported/characters/faces/mouth/mouth_jowls_simple.png"
-dest_files=["res://.godot/imported/mouth_jowls_simple.png-0586400ebfd9ef98c9067f0ebbe730be.ctex"]
+dest_files=["res://.godot/imported/mouth_jowls_simple.png-0586400ebfd9ef98c9067f0ebbe730be.s3tc.ctex", "res://.godot/imported/mouth_jowls_simple.png-0586400ebfd9ef98c9067f0ebbe730be.etc2.ctex"]
[params]
-compress/mode=0
+compress/mode=2
compress/high_quality=false
compress/lossy_quality=0.7
compress/uastc_level=0
@@ -36,5 +38,5 @@ 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
+process/size_limit=512
+detect_3d/compress_to=0
diff --git a/art/exported/characters/faces/mouth/mouth_kissy.png.import b/art/exported/characters/faces/mouth/mouth_kissy.png.import
index ff0e1c1..1281fa1 100644
--- a/art/exported/characters/faces/mouth/mouth_kissy.png.import
+++ b/art/exported/characters/faces/mouth/mouth_kissy.png.import
@@ -3,19 +3,21 @@
importer="texture"
type="CompressedTexture2D"
uid="uid://druncaq416lvu"
-path="res://.godot/imported/mouth_kissy.png-52ec7a06301d8506497442f0ea0054a0.ctex"
+path.s3tc="res://.godot/imported/mouth_kissy.png-52ec7a06301d8506497442f0ea0054a0.s3tc.ctex"
+path.etc2="res://.godot/imported/mouth_kissy.png-52ec7a06301d8506497442f0ea0054a0.etc2.ctex"
metadata={
-"vram_texture": false
+"imported_formats": ["s3tc_bptc", "etc2_astc"],
+"vram_texture": true
}
[deps]
source_file="res://art/exported/characters/faces/mouth/mouth_kissy.png"
-dest_files=["res://.godot/imported/mouth_kissy.png-52ec7a06301d8506497442f0ea0054a0.ctex"]
+dest_files=["res://.godot/imported/mouth_kissy.png-52ec7a06301d8506497442f0ea0054a0.s3tc.ctex", "res://.godot/imported/mouth_kissy.png-52ec7a06301d8506497442f0ea0054a0.etc2.ctex"]
[params]
-compress/mode=0
+compress/mode=2
compress/high_quality=false
compress/lossy_quality=0.7
compress/uastc_level=0
@@ -36,5 +38,5 @@ 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
+process/size_limit=512
+detect_3d/compress_to=0
diff --git a/art/exported/characters/faces/mouth/mouth_kissy_lips.png.import b/art/exported/characters/faces/mouth/mouth_kissy_lips.png.import
index 2141904..3778c31 100644
--- a/art/exported/characters/faces/mouth/mouth_kissy_lips.png.import
+++ b/art/exported/characters/faces/mouth/mouth_kissy_lips.png.import
@@ -3,19 +3,21 @@
importer="texture"
type="CompressedTexture2D"
uid="uid://babmei7rq8uaf"
-path="res://.godot/imported/mouth_kissy_lips.png-a90d7b9649dbba8cc17232f59291a2c8.ctex"
+path.s3tc="res://.godot/imported/mouth_kissy_lips.png-a90d7b9649dbba8cc17232f59291a2c8.s3tc.ctex"
+path.etc2="res://.godot/imported/mouth_kissy_lips.png-a90d7b9649dbba8cc17232f59291a2c8.etc2.ctex"
metadata={
-"vram_texture": false
+"imported_formats": ["s3tc_bptc", "etc2_astc"],
+"vram_texture": true
}
[deps]
source_file="res://art/exported/characters/faces/mouth/mouth_kissy_lips.png"
-dest_files=["res://.godot/imported/mouth_kissy_lips.png-a90d7b9649dbba8cc17232f59291a2c8.ctex"]
+dest_files=["res://.godot/imported/mouth_kissy_lips.png-a90d7b9649dbba8cc17232f59291a2c8.s3tc.ctex", "res://.godot/imported/mouth_kissy_lips.png-a90d7b9649dbba8cc17232f59291a2c8.etc2.ctex"]
[params]
-compress/mode=0
+compress/mode=2
compress/high_quality=false
compress/lossy_quality=0.7
compress/uastc_level=0
@@ -36,5 +38,5 @@ 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
+process/size_limit=512
+detect_3d/compress_to=0
diff --git a/art/exported/characters/faces/mouth/mouth_lil_c.png.import b/art/exported/characters/faces/mouth/mouth_lil_c.png.import
index 1a7c28d..06518f1 100644
--- a/art/exported/characters/faces/mouth/mouth_lil_c.png.import
+++ b/art/exported/characters/faces/mouth/mouth_lil_c.png.import
@@ -3,19 +3,21 @@
importer="texture"
type="CompressedTexture2D"
uid="uid://bfafrjmy6liy6"
-path="res://.godot/imported/mouth_lil_c.png-e65a240e587f99537a3a9f1d3f241323.ctex"
+path.s3tc="res://.godot/imported/mouth_lil_c.png-e65a240e587f99537a3a9f1d3f241323.s3tc.ctex"
+path.etc2="res://.godot/imported/mouth_lil_c.png-e65a240e587f99537a3a9f1d3f241323.etc2.ctex"
metadata={
-"vram_texture": false
+"imported_formats": ["s3tc_bptc", "etc2_astc"],
+"vram_texture": true
}
[deps]
source_file="res://art/exported/characters/faces/mouth/mouth_lil_c.png"
-dest_files=["res://.godot/imported/mouth_lil_c.png-e65a240e587f99537a3a9f1d3f241323.ctex"]
+dest_files=["res://.godot/imported/mouth_lil_c.png-e65a240e587f99537a3a9f1d3f241323.s3tc.ctex", "res://.godot/imported/mouth_lil_c.png-e65a240e587f99537a3a9f1d3f241323.etc2.ctex"]
[params]
-compress/mode=0
+compress/mode=2
compress/high_quality=false
compress/lossy_quality=0.7
compress/uastc_level=0
@@ -36,5 +38,5 @@ 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
+process/size_limit=512
+detect_3d/compress_to=0
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..f37ae3a
--- /dev/null
+++ b/art/exported/characters/faces/mouth/mouth_open_ah.png.import
@@ -0,0 +1,42 @@
+[remap]
+
+importer="texture"
+type="CompressedTexture2D"
+uid="uid://44cqu18imydc"
+path.s3tc="res://.godot/imported/mouth_open_ah.png-2710d397264f57dbe8f9bac82dd4b0de.s3tc.ctex"
+path.etc2="res://.godot/imported/mouth_open_ah.png-2710d397264f57dbe8f9bac82dd4b0de.etc2.ctex"
+metadata={
+"imported_formats": ["s3tc_bptc", "etc2_astc"],
+"vram_texture": true
+}
+
+[deps]
+
+source_file="res://art/exported/characters/faces/mouth/mouth_open_ah.png"
+dest_files=["res://.godot/imported/mouth_open_ah.png-2710d397264f57dbe8f9bac82dd4b0de.s3tc.ctex", "res://.godot/imported/mouth_open_ah.png-2710d397264f57dbe8f9bac82dd4b0de.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=512
+detect_3d/compress_to=0
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..b5e7a1d
--- /dev/null
+++ b/art/exported/characters/faces/mouth/mouth_open_oh.png.import
@@ -0,0 +1,42 @@
+[remap]
+
+importer="texture"
+type="CompressedTexture2D"
+uid="uid://c78hwv0t3ph47"
+path.s3tc="res://.godot/imported/mouth_open_oh.png-10fc59160b782879c415074831421088.s3tc.ctex"
+path.etc2="res://.godot/imported/mouth_open_oh.png-10fc59160b782879c415074831421088.etc2.ctex"
+metadata={
+"imported_formats": ["s3tc_bptc", "etc2_astc"],
+"vram_texture": true
+}
+
+[deps]
+
+source_file="res://art/exported/characters/faces/mouth/mouth_open_oh.png"
+dest_files=["res://.godot/imported/mouth_open_oh.png-10fc59160b782879c415074831421088.s3tc.ctex", "res://.godot/imported/mouth_open_oh.png-10fc59160b782879c415074831421088.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=512
+detect_3d/compress_to=0
diff --git a/art/exported/characters/faces/mouth/mouth_open_small.png.import b/art/exported/characters/faces/mouth/mouth_open_small.png.import
index 9b15e51..5a83a6e 100644
--- a/art/exported/characters/faces/mouth/mouth_open_small.png.import
+++ b/art/exported/characters/faces/mouth/mouth_open_small.png.import
@@ -3,19 +3,21 @@
importer="texture"
type="CompressedTexture2D"
uid="uid://bn68lfabdo7ga"
-path="res://.godot/imported/mouth_open_small.png-30e9081c075d21159d6563a981b15378.ctex"
+path.s3tc="res://.godot/imported/mouth_open_small.png-30e9081c075d21159d6563a981b15378.s3tc.ctex"
+path.etc2="res://.godot/imported/mouth_open_small.png-30e9081c075d21159d6563a981b15378.etc2.ctex"
metadata={
-"vram_texture": false
+"imported_formats": ["s3tc_bptc", "etc2_astc"],
+"vram_texture": true
}
[deps]
source_file="res://art/exported/characters/faces/mouth/mouth_open_small.png"
-dest_files=["res://.godot/imported/mouth_open_small.png-30e9081c075d21159d6563a981b15378.ctex"]
+dest_files=["res://.godot/imported/mouth_open_small.png-30e9081c075d21159d6563a981b15378.s3tc.ctex", "res://.godot/imported/mouth_open_small.png-30e9081c075d21159d6563a981b15378.etc2.ctex"]
[params]
-compress/mode=0
+compress/mode=2
compress/high_quality=false
compress/lossy_quality=0.7
compress/uastc_level=0
@@ -36,5 +38,5 @@ 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
+process/size_limit=512
+detect_3d/compress_to=0
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..46b3100
--- /dev/null
+++ b/art/exported/characters/faces/mouth/mouth_open_smile.png.import
@@ -0,0 +1,42 @@
+[remap]
+
+importer="texture"
+type="CompressedTexture2D"
+uid="uid://b12jrc4ao3r16"
+path.s3tc="res://.godot/imported/mouth_open_smile.png-2561ffea934645e2eac40ab3a0a6395a.s3tc.ctex"
+path.etc2="res://.godot/imported/mouth_open_smile.png-2561ffea934645e2eac40ab3a0a6395a.etc2.ctex"
+metadata={
+"imported_formats": ["s3tc_bptc", "etc2_astc"],
+"vram_texture": true
+}
+
+[deps]
+
+source_file="res://art/exported/characters/faces/mouth/mouth_open_smile.png"
+dest_files=["res://.godot/imported/mouth_open_smile.png-2561ffea934645e2eac40ab3a0a6395a.s3tc.ctex", "res://.godot/imported/mouth_open_smile.png-2561ffea934645e2eac40ab3a0a6395a.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=512
+detect_3d/compress_to=0
diff --git a/art/exported/characters/faces/mouth/mouth_p_tounge.png.import b/art/exported/characters/faces/mouth/mouth_p_tounge.png.import
index 1a791ce..a49c3ae 100644
--- a/art/exported/characters/faces/mouth/mouth_p_tounge.png.import
+++ b/art/exported/characters/faces/mouth/mouth_p_tounge.png.import
@@ -3,19 +3,21 @@
importer="texture"
type="CompressedTexture2D"
uid="uid://nt1q6gpij6y3"
-path="res://.godot/imported/mouth_p_tounge.png-f2e1b9018a58fa3c500b05c19941a1f3.ctex"
+path.s3tc="res://.godot/imported/mouth_p_tounge.png-f2e1b9018a58fa3c500b05c19941a1f3.s3tc.ctex"
+path.etc2="res://.godot/imported/mouth_p_tounge.png-f2e1b9018a58fa3c500b05c19941a1f3.etc2.ctex"
metadata={
-"vram_texture": false
+"imported_formats": ["s3tc_bptc", "etc2_astc"],
+"vram_texture": true
}
[deps]
source_file="res://art/exported/characters/faces/mouth/mouth_p_tounge.png"
-dest_files=["res://.godot/imported/mouth_p_tounge.png-f2e1b9018a58fa3c500b05c19941a1f3.ctex"]
+dest_files=["res://.godot/imported/mouth_p_tounge.png-f2e1b9018a58fa3c500b05c19941a1f3.s3tc.ctex", "res://.godot/imported/mouth_p_tounge.png-f2e1b9018a58fa3c500b05c19941a1f3.etc2.ctex"]
[params]
-compress/mode=0
+compress/mode=2
compress/high_quality=false
compress/lossy_quality=0.7
compress/uastc_level=0
@@ -36,5 +38,5 @@ 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
+process/size_limit=512
+detect_3d/compress_to=0
diff --git a/art/exported/characters/faces/mouth/mouth_sad.png.import b/art/exported/characters/faces/mouth/mouth_sad.png.import
index bfe50b3..a0cce86 100644
--- a/art/exported/characters/faces/mouth/mouth_sad.png.import
+++ b/art/exported/characters/faces/mouth/mouth_sad.png.import
@@ -3,19 +3,21 @@
importer="texture"
type="CompressedTexture2D"
uid="uid://camwvowsgxnlw"
-path="res://.godot/imported/mouth_sad.png-ced7361b391677e61acbb1877a8e7156.ctex"
+path.s3tc="res://.godot/imported/mouth_sad.png-ced7361b391677e61acbb1877a8e7156.s3tc.ctex"
+path.etc2="res://.godot/imported/mouth_sad.png-ced7361b391677e61acbb1877a8e7156.etc2.ctex"
metadata={
-"vram_texture": false
+"imported_formats": ["s3tc_bptc", "etc2_astc"],
+"vram_texture": true
}
[deps]
source_file="res://art/exported/characters/faces/mouth/mouth_sad.png"
-dest_files=["res://.godot/imported/mouth_sad.png-ced7361b391677e61acbb1877a8e7156.ctex"]
+dest_files=["res://.godot/imported/mouth_sad.png-ced7361b391677e61acbb1877a8e7156.s3tc.ctex", "res://.godot/imported/mouth_sad.png-ced7361b391677e61acbb1877a8e7156.etc2.ctex"]
[params]
-compress/mode=0
+compress/mode=2
compress/high_quality=false
compress/lossy_quality=0.7
compress/uastc_level=0
@@ -36,5 +38,5 @@ 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
+process/size_limit=512
+detect_3d/compress_to=0
diff --git a/art/exported/characters/faces/mouth/mouth_sigh.png.import b/art/exported/characters/faces/mouth/mouth_sigh.png.import
index 928a5d2..3199031 100644
--- a/art/exported/characters/faces/mouth/mouth_sigh.png.import
+++ b/art/exported/characters/faces/mouth/mouth_sigh.png.import
@@ -3,19 +3,21 @@
importer="texture"
type="CompressedTexture2D"
uid="uid://b0842jxg3m1ca"
-path="res://.godot/imported/mouth_sigh.png-f58b2992f2709483fde720834a5e8820.ctex"
+path.s3tc="res://.godot/imported/mouth_sigh.png-f58b2992f2709483fde720834a5e8820.s3tc.ctex"
+path.etc2="res://.godot/imported/mouth_sigh.png-f58b2992f2709483fde720834a5e8820.etc2.ctex"
metadata={
-"vram_texture": false
+"imported_formats": ["s3tc_bptc", "etc2_astc"],
+"vram_texture": true
}
[deps]
source_file="res://art/exported/characters/faces/mouth/mouth_sigh.png"
-dest_files=["res://.godot/imported/mouth_sigh.png-f58b2992f2709483fde720834a5e8820.ctex"]
+dest_files=["res://.godot/imported/mouth_sigh.png-f58b2992f2709483fde720834a5e8820.s3tc.ctex", "res://.godot/imported/mouth_sigh.png-f58b2992f2709483fde720834a5e8820.etc2.ctex"]
[params]
-compress/mode=0
+compress/mode=2
compress/high_quality=false
compress/lossy_quality=0.7
compress/uastc_level=0
@@ -36,5 +38,5 @@ 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
+process/size_limit=512
+detect_3d/compress_to=0
diff --git a/art/exported/characters/faces/mouth/mouth_simple_smile.png.import b/art/exported/characters/faces/mouth/mouth_simple_smile.png.import
index 82bd51a..c71a778 100644
--- a/art/exported/characters/faces/mouth/mouth_simple_smile.png.import
+++ b/art/exported/characters/faces/mouth/mouth_simple_smile.png.import
@@ -3,19 +3,21 @@
importer="texture"
type="CompressedTexture2D"
uid="uid://nafybekpya31"
-path="res://.godot/imported/mouth_simple_smile.png-029050835f365c4976c1b4e62edbc7c8.ctex"
+path.s3tc="res://.godot/imported/mouth_simple_smile.png-029050835f365c4976c1b4e62edbc7c8.s3tc.ctex"
+path.etc2="res://.godot/imported/mouth_simple_smile.png-029050835f365c4976c1b4e62edbc7c8.etc2.ctex"
metadata={
-"vram_texture": false
+"imported_formats": ["s3tc_bptc", "etc2_astc"],
+"vram_texture": true
}
[deps]
source_file="res://art/exported/characters/faces/mouth/mouth_simple_smile.png"
-dest_files=["res://.godot/imported/mouth_simple_smile.png-029050835f365c4976c1b4e62edbc7c8.ctex"]
+dest_files=["res://.godot/imported/mouth_simple_smile.png-029050835f365c4976c1b4e62edbc7c8.s3tc.ctex", "res://.godot/imported/mouth_simple_smile.png-029050835f365c4976c1b4e62edbc7c8.etc2.ctex"]
[params]
-compress/mode=0
+compress/mode=2
compress/high_quality=false
compress/lossy_quality=0.7
compress/uastc_level=0
@@ -36,5 +38,5 @@ 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
+process/size_limit=512
+detect_3d/compress_to=0
diff --git a/art/exported/characters/faces/mouth/mouth_slanted.png.import b/art/exported/characters/faces/mouth/mouth_slanted.png.import
index 01efb87..80611ac 100644
--- a/art/exported/characters/faces/mouth/mouth_slanted.png.import
+++ b/art/exported/characters/faces/mouth/mouth_slanted.png.import
@@ -3,19 +3,21 @@
importer="texture"
type="CompressedTexture2D"
uid="uid://nuj6ednwjawn"
-path="res://.godot/imported/mouth_slanted.png-cb0a217a5bb11b80e53b1a6e3e94f43d.ctex"
+path.s3tc="res://.godot/imported/mouth_slanted.png-cb0a217a5bb11b80e53b1a6e3e94f43d.s3tc.ctex"
+path.etc2="res://.godot/imported/mouth_slanted.png-cb0a217a5bb11b80e53b1a6e3e94f43d.etc2.ctex"
metadata={
-"vram_texture": false
+"imported_formats": ["s3tc_bptc", "etc2_astc"],
+"vram_texture": true
}
[deps]
source_file="res://art/exported/characters/faces/mouth/mouth_slanted.png"
-dest_files=["res://.godot/imported/mouth_slanted.png-cb0a217a5bb11b80e53b1a6e3e94f43d.ctex"]
+dest_files=["res://.godot/imported/mouth_slanted.png-cb0a217a5bb11b80e53b1a6e3e94f43d.s3tc.ctex", "res://.godot/imported/mouth_slanted.png-cb0a217a5bb11b80e53b1a6e3e94f43d.etc2.ctex"]
[params]
-compress/mode=0
+compress/mode=2
compress/high_quality=false
compress/lossy_quality=0.7
compress/uastc_level=0
@@ -36,5 +38,5 @@ 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
+process/size_limit=512
+detect_3d/compress_to=0
diff --git a/art/exported/characters/faces/mouth/mouth_squigle.png.import b/art/exported/characters/faces/mouth/mouth_squigle.png.import
index 980021f..22c7de7 100644
--- a/art/exported/characters/faces/mouth/mouth_squigle.png.import
+++ b/art/exported/characters/faces/mouth/mouth_squigle.png.import
@@ -3,19 +3,21 @@
importer="texture"
type="CompressedTexture2D"
uid="uid://calw27urbe3hk"
-path="res://.godot/imported/mouth_squigle.png-263a4acb09e9f29aff38899ef1aac53f.ctex"
+path.s3tc="res://.godot/imported/mouth_squigle.png-263a4acb09e9f29aff38899ef1aac53f.s3tc.ctex"
+path.etc2="res://.godot/imported/mouth_squigle.png-263a4acb09e9f29aff38899ef1aac53f.etc2.ctex"
metadata={
-"vram_texture": false
+"imported_formats": ["s3tc_bptc", "etc2_astc"],
+"vram_texture": true
}
[deps]
source_file="res://art/exported/characters/faces/mouth/mouth_squigle.png"
-dest_files=["res://.godot/imported/mouth_squigle.png-263a4acb09e9f29aff38899ef1aac53f.ctex"]
+dest_files=["res://.godot/imported/mouth_squigle.png-263a4acb09e9f29aff38899ef1aac53f.s3tc.ctex", "res://.godot/imported/mouth_squigle.png-263a4acb09e9f29aff38899ef1aac53f.etc2.ctex"]
[params]
-compress/mode=0
+compress/mode=2
compress/high_quality=false
compress/lossy_quality=0.7
compress/uastc_level=0
@@ -36,5 +38,5 @@ 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
+process/size_limit=512
+detect_3d/compress_to=0
diff --git a/art/exported/characters/faces/mouth/mouth_vampy.png.import b/art/exported/characters/faces/mouth/mouth_vampy.png.import
index d191c95..cd3d5c0 100644
--- a/art/exported/characters/faces/mouth/mouth_vampy.png.import
+++ b/art/exported/characters/faces/mouth/mouth_vampy.png.import
@@ -3,19 +3,21 @@
importer="texture"
type="CompressedTexture2D"
uid="uid://bdpe0lrftqjkq"
-path="res://.godot/imported/mouth_vampy.png-0f16a1970484a70927f4063fa1bab422.ctex"
+path.s3tc="res://.godot/imported/mouth_vampy.png-0f16a1970484a70927f4063fa1bab422.s3tc.ctex"
+path.etc2="res://.godot/imported/mouth_vampy.png-0f16a1970484a70927f4063fa1bab422.etc2.ctex"
metadata={
-"vram_texture": false
+"imported_formats": ["s3tc_bptc", "etc2_astc"],
+"vram_texture": true
}
[deps]
source_file="res://art/exported/characters/faces/mouth/mouth_vampy.png"
-dest_files=["res://.godot/imported/mouth_vampy.png-0f16a1970484a70927f4063fa1bab422.ctex"]
+dest_files=["res://.godot/imported/mouth_vampy.png-0f16a1970484a70927f4063fa1bab422.s3tc.ctex", "res://.godot/imported/mouth_vampy.png-0f16a1970484a70927f4063fa1bab422.etc2.ctex"]
[params]
-compress/mode=0
+compress/mode=2
compress/high_quality=false
compress/lossy_quality=0.7
compress/uastc_level=0
@@ -36,5 +38,5 @@ 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
+process/size_limit=512
+detect_3d/compress_to=0
diff --git a/art/exported/characters/faces/mouth/mouth_whiskers.png.import b/art/exported/characters/faces/mouth/mouth_whiskers.png.import
index 7fe6438..799b0f1 100644
--- a/art/exported/characters/faces/mouth/mouth_whiskers.png.import
+++ b/art/exported/characters/faces/mouth/mouth_whiskers.png.import
@@ -3,19 +3,21 @@
importer="texture"
type="CompressedTexture2D"
uid="uid://cpxwladf5vobh"
-path="res://.godot/imported/mouth_whiskers.png-27b3d6495cd4a57de394a2ad8eb67d39.ctex"
+path.s3tc="res://.godot/imported/mouth_whiskers.png-27b3d6495cd4a57de394a2ad8eb67d39.s3tc.ctex"
+path.etc2="res://.godot/imported/mouth_whiskers.png-27b3d6495cd4a57de394a2ad8eb67d39.etc2.ctex"
metadata={
-"vram_texture": false
+"imported_formats": ["s3tc_bptc", "etc2_astc"],
+"vram_texture": true
}
[deps]
source_file="res://art/exported/characters/faces/mouth/mouth_whiskers.png"
-dest_files=["res://.godot/imported/mouth_whiskers.png-27b3d6495cd4a57de394a2ad8eb67d39.ctex"]
+dest_files=["res://.godot/imported/mouth_whiskers.png-27b3d6495cd4a57de394a2ad8eb67d39.s3tc.ctex", "res://.godot/imported/mouth_whiskers.png-27b3d6495cd4a57de394a2ad8eb67d39.etc2.ctex"]
[params]
-compress/mode=0
+compress/mode=2
compress/high_quality=false
compress/lossy_quality=0.7
compress/uastc_level=0
@@ -36,5 +38,5 @@ 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
+process/size_limit=512
+detect_3d/compress_to=0
diff --git a/art/exported/characters/faces/mouth/mouth_y.png.import b/art/exported/characters/faces/mouth/mouth_y.png.import
index 989fa6b..916f91f 100644
--- a/art/exported/characters/faces/mouth/mouth_y.png.import
+++ b/art/exported/characters/faces/mouth/mouth_y.png.import
@@ -3,19 +3,21 @@
importer="texture"
type="CompressedTexture2D"
uid="uid://6w65l4rjbgq"
-path="res://.godot/imported/mouth_y.png-08338d9ca7fd2d139dd64add4385096d.ctex"
+path.s3tc="res://.godot/imported/mouth_y.png-08338d9ca7fd2d139dd64add4385096d.s3tc.ctex"
+path.etc2="res://.godot/imported/mouth_y.png-08338d9ca7fd2d139dd64add4385096d.etc2.ctex"
metadata={
-"vram_texture": false
+"imported_formats": ["s3tc_bptc", "etc2_astc"],
+"vram_texture": true
}
[deps]
source_file="res://art/exported/characters/faces/mouth/mouth_y.png"
-dest_files=["res://.godot/imported/mouth_y.png-08338d9ca7fd2d139dd64add4385096d.ctex"]
+dest_files=["res://.godot/imported/mouth_y.png-08338d9ca7fd2d139dd64add4385096d.s3tc.ctex", "res://.godot/imported/mouth_y.png-08338d9ca7fd2d139dd64add4385096d.etc2.ctex"]
[params]
-compress/mode=0
+compress/mode=2
compress/high_quality=false
compress/lossy_quality=0.7
compress/uastc_level=0
@@ -36,5 +38,5 @@ 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
+process/size_limit=512
+detect_3d/compress_to=0
diff --git a/art/exported/characters/faces/mouth/mouth_y_small.png.import b/art/exported/characters/faces/mouth/mouth_y_small.png.import
index 205579e..80dedf9 100644
--- a/art/exported/characters/faces/mouth/mouth_y_small.png.import
+++ b/art/exported/characters/faces/mouth/mouth_y_small.png.import
@@ -3,19 +3,21 @@
importer="texture"
type="CompressedTexture2D"
uid="uid://cchv8fvnqbtm2"
-path="res://.godot/imported/mouth_y_small.png-493215adfb2e980f01f69152d24a8ef4.ctex"
+path.s3tc="res://.godot/imported/mouth_y_small.png-493215adfb2e980f01f69152d24a8ef4.s3tc.ctex"
+path.etc2="res://.godot/imported/mouth_y_small.png-493215adfb2e980f01f69152d24a8ef4.etc2.ctex"
metadata={
-"vram_texture": false
+"imported_formats": ["s3tc_bptc", "etc2_astc"],
+"vram_texture": true
}
[deps]
source_file="res://art/exported/characters/faces/mouth/mouth_y_small.png"
-dest_files=["res://.godot/imported/mouth_y_small.png-493215adfb2e980f01f69152d24a8ef4.ctex"]
+dest_files=["res://.godot/imported/mouth_y_small.png-493215adfb2e980f01f69152d24a8ef4.s3tc.ctex", "res://.godot/imported/mouth_y_small.png-493215adfb2e980f01f69152d24a8ef4.etc2.ctex"]
[params]
-compress/mode=0
+compress/mode=2
compress/high_quality=false
compress/lossy_quality=0.7
compress/uastc_level=0
@@ -36,5 +38,5 @@ 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
+process/size_limit=512
+detect_3d/compress_to=0
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
index f8237f1..efe6ac8 100644
--- a/art/exported/characters/faces/mouth/mouth_y_small_pink.png.import
+++ b/art/exported/characters/faces/mouth/mouth_y_small_pink.png.import
@@ -3,19 +3,21 @@
importer="texture"
type="CompressedTexture2D"
uid="uid://dvqdmhhiet5ix"
-path="res://.godot/imported/mouth_y_small_pink.png-febc93e05453be68458eff8bab3d21c1.ctex"
+path.s3tc="res://.godot/imported/mouth_y_small_pink.png-febc93e05453be68458eff8bab3d21c1.s3tc.ctex"
+path.etc2="res://.godot/imported/mouth_y_small_pink.png-febc93e05453be68458eff8bab3d21c1.etc2.ctex"
metadata={
-"vram_texture": false
+"imported_formats": ["s3tc_bptc", "etc2_astc"],
+"vram_texture": true
}
[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"]
+dest_files=["res://.godot/imported/mouth_y_small_pink.png-febc93e05453be68458eff8bab3d21c1.s3tc.ctex", "res://.godot/imported/mouth_y_small_pink.png-febc93e05453be68458eff8bab3d21c1.etc2.ctex"]
[params]
-compress/mode=0
+compress/mode=2
compress/high_quality=false
compress/lossy_quality=0.7
compress/uastc_level=0
@@ -36,5 +38,5 @@ 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
+process/size_limit=512
+detect_3d/compress_to=0
diff --git a/art/exported/characters/faces/noses/nose_cat_real.png.import b/art/exported/characters/faces/noses/nose_cat_real.png.import
index aaedb17..8e1a496 100644
--- a/art/exported/characters/faces/noses/nose_cat_real.png.import
+++ b/art/exported/characters/faces/noses/nose_cat_real.png.import
@@ -3,19 +3,21 @@
importer="texture"
type="CompressedTexture2D"
uid="uid://5n0kihgof38n"
-path="res://.godot/imported/nose_cat_real.png-25c7e8464e384043f0969f191e2c2950.ctex"
+path.s3tc="res://.godot/imported/nose_cat_real.png-25c7e8464e384043f0969f191e2c2950.s3tc.ctex"
+path.etc2="res://.godot/imported/nose_cat_real.png-25c7e8464e384043f0969f191e2c2950.etc2.ctex"
metadata={
-"vram_texture": false
+"imported_formats": ["s3tc_bptc", "etc2_astc"],
+"vram_texture": true
}
[deps]
source_file="res://art/exported/characters/faces/noses/nose_cat_real.png"
-dest_files=["res://.godot/imported/nose_cat_real.png-25c7e8464e384043f0969f191e2c2950.ctex"]
+dest_files=["res://.godot/imported/nose_cat_real.png-25c7e8464e384043f0969f191e2c2950.s3tc.ctex", "res://.godot/imported/nose_cat_real.png-25c7e8464e384043f0969f191e2c2950.etc2.ctex"]
[params]
-compress/mode=0
+compress/mode=2
compress/high_quality=false
compress/lossy_quality=0.7
compress/uastc_level=0
@@ -36,5 +38,5 @@ 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
+process/size_limit=512
+detect_3d/compress_to=0
diff --git a/art/exported/characters/faces/noses/nose_clown.png.import b/art/exported/characters/faces/noses/nose_clown.png.import
index a9afce1..1a642e3 100644
--- a/art/exported/characters/faces/noses/nose_clown.png.import
+++ b/art/exported/characters/faces/noses/nose_clown.png.import
@@ -3,19 +3,21 @@
importer="texture"
type="CompressedTexture2D"
uid="uid://cc0nb34t7helb"
-path="res://.godot/imported/nose_clown.png-bfa33a86cf06540d45ba440868a39bb3.ctex"
+path.s3tc="res://.godot/imported/nose_clown.png-bfa33a86cf06540d45ba440868a39bb3.s3tc.ctex"
+path.etc2="res://.godot/imported/nose_clown.png-bfa33a86cf06540d45ba440868a39bb3.etc2.ctex"
metadata={
-"vram_texture": false
+"imported_formats": ["s3tc_bptc", "etc2_astc"],
+"vram_texture": true
}
[deps]
source_file="res://art/exported/characters/faces/noses/nose_clown.png"
-dest_files=["res://.godot/imported/nose_clown.png-bfa33a86cf06540d45ba440868a39bb3.ctex"]
+dest_files=["res://.godot/imported/nose_clown.png-bfa33a86cf06540d45ba440868a39bb3.s3tc.ctex", "res://.godot/imported/nose_clown.png-bfa33a86cf06540d45ba440868a39bb3.etc2.ctex"]
[params]
-compress/mode=0
+compress/mode=2
compress/high_quality=false
compress/lossy_quality=0.7
compress/uastc_level=0
@@ -36,5 +38,5 @@ 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
+process/size_limit=512
+detect_3d/compress_to=0
diff --git a/art/exported/characters/faces/noses/nose_dog_real.png.import b/art/exported/characters/faces/noses/nose_dog_real.png.import
index 9fe82cc..316c80e 100644
--- a/art/exported/characters/faces/noses/nose_dog_real.png.import
+++ b/art/exported/characters/faces/noses/nose_dog_real.png.import
@@ -3,19 +3,21 @@
importer="texture"
type="CompressedTexture2D"
uid="uid://b8jwky86fl3ky"
-path="res://.godot/imported/nose_dog_real.png-f065da766aead62076c01fa08fbffb2a.ctex"
+path.s3tc="res://.godot/imported/nose_dog_real.png-f065da766aead62076c01fa08fbffb2a.s3tc.ctex"
+path.etc2="res://.godot/imported/nose_dog_real.png-f065da766aead62076c01fa08fbffb2a.etc2.ctex"
metadata={
-"vram_texture": false
+"imported_formats": ["s3tc_bptc", "etc2_astc"],
+"vram_texture": true
}
[deps]
source_file="res://art/exported/characters/faces/noses/nose_dog_real.png"
-dest_files=["res://.godot/imported/nose_dog_real.png-f065da766aead62076c01fa08fbffb2a.ctex"]
+dest_files=["res://.godot/imported/nose_dog_real.png-f065da766aead62076c01fa08fbffb2a.s3tc.ctex", "res://.godot/imported/nose_dog_real.png-f065da766aead62076c01fa08fbffb2a.etc2.ctex"]
[params]
-compress/mode=0
+compress/mode=2
compress/high_quality=false
compress/lossy_quality=0.7
compress/uastc_level=0
@@ -36,5 +38,5 @@ 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
+process/size_limit=512
+detect_3d/compress_to=0
diff --git a/art/exported/characters/faces/noses/nose_dog_simple.png.import b/art/exported/characters/faces/noses/nose_dog_simple.png.import
index f61b74a..344af2e 100644
--- a/art/exported/characters/faces/noses/nose_dog_simple.png.import
+++ b/art/exported/characters/faces/noses/nose_dog_simple.png.import
@@ -3,19 +3,21 @@
importer="texture"
type="CompressedTexture2D"
uid="uid://b6d4ubqjijjux"
-path="res://.godot/imported/nose_dog_simple.png-fdc0a92e3e2c82271db9ea87256058a7.ctex"
+path.s3tc="res://.godot/imported/nose_dog_simple.png-fdc0a92e3e2c82271db9ea87256058a7.s3tc.ctex"
+path.etc2="res://.godot/imported/nose_dog_simple.png-fdc0a92e3e2c82271db9ea87256058a7.etc2.ctex"
metadata={
-"vram_texture": false
+"imported_formats": ["s3tc_bptc", "etc2_astc"],
+"vram_texture": true
}
[deps]
source_file="res://art/exported/characters/faces/noses/nose_dog_simple.png"
-dest_files=["res://.godot/imported/nose_dog_simple.png-fdc0a92e3e2c82271db9ea87256058a7.ctex"]
+dest_files=["res://.godot/imported/nose_dog_simple.png-fdc0a92e3e2c82271db9ea87256058a7.s3tc.ctex", "res://.godot/imported/nose_dog_simple.png-fdc0a92e3e2c82271db9ea87256058a7.etc2.ctex"]
[params]
-compress/mode=0
+compress/mode=2
compress/high_quality=false
compress/lossy_quality=0.7
compress/uastc_level=0
@@ -36,5 +38,5 @@ 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
+process/size_limit=512
+detect_3d/compress_to=0
diff --git a/art/exported/characters/faces/noses/nose_dragon.png.import b/art/exported/characters/faces/noses/nose_dragon.png.import
index 0ba2695..92d98eb 100644
--- a/art/exported/characters/faces/noses/nose_dragon.png.import
+++ b/art/exported/characters/faces/noses/nose_dragon.png.import
@@ -3,19 +3,21 @@
importer="texture"
type="CompressedTexture2D"
uid="uid://8n1bhbyt5id6"
-path="res://.godot/imported/nose_dragon.png-6c98cf6f5692ec43ffe43650115cd010.ctex"
+path.s3tc="res://.godot/imported/nose_dragon.png-6c98cf6f5692ec43ffe43650115cd010.s3tc.ctex"
+path.etc2="res://.godot/imported/nose_dragon.png-6c98cf6f5692ec43ffe43650115cd010.etc2.ctex"
metadata={
-"vram_texture": false
+"imported_formats": ["s3tc_bptc", "etc2_astc"],
+"vram_texture": true
}
[deps]
source_file="res://art/exported/characters/faces/noses/nose_dragon.png"
-dest_files=["res://.godot/imported/nose_dragon.png-6c98cf6f5692ec43ffe43650115cd010.ctex"]
+dest_files=["res://.godot/imported/nose_dragon.png-6c98cf6f5692ec43ffe43650115cd010.s3tc.ctex", "res://.godot/imported/nose_dragon.png-6c98cf6f5692ec43ffe43650115cd010.etc2.ctex"]
[params]
-compress/mode=0
+compress/mode=2
compress/high_quality=false
compress/lossy_quality=0.7
compress/uastc_level=0
@@ -36,5 +38,5 @@ 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
+process/size_limit=512
+detect_3d/compress_to=0
diff --git a/art/exported/characters/faces/noses/nose_hippo.png.import b/art/exported/characters/faces/noses/nose_hippo.png.import
index 0206809..3e5736f 100644
--- a/art/exported/characters/faces/noses/nose_hippo.png.import
+++ b/art/exported/characters/faces/noses/nose_hippo.png.import
@@ -3,19 +3,21 @@
importer="texture"
type="CompressedTexture2D"
uid="uid://bmfa44uf6u4or"
-path="res://.godot/imported/nose_hippo.png-059f63ab00597454d369b0d51b5712a6.ctex"
+path.s3tc="res://.godot/imported/nose_hippo.png-059f63ab00597454d369b0d51b5712a6.s3tc.ctex"
+path.etc2="res://.godot/imported/nose_hippo.png-059f63ab00597454d369b0d51b5712a6.etc2.ctex"
metadata={
-"vram_texture": false
+"imported_formats": ["s3tc_bptc", "etc2_astc"],
+"vram_texture": true
}
[deps]
source_file="res://art/exported/characters/faces/noses/nose_hippo.png"
-dest_files=["res://.godot/imported/nose_hippo.png-059f63ab00597454d369b0d51b5712a6.ctex"]
+dest_files=["res://.godot/imported/nose_hippo.png-059f63ab00597454d369b0d51b5712a6.s3tc.ctex", "res://.godot/imported/nose_hippo.png-059f63ab00597454d369b0d51b5712a6.etc2.ctex"]
[params]
-compress/mode=0
+compress/mode=2
compress/high_quality=false
compress/lossy_quality=0.7
compress/uastc_level=0
@@ -36,5 +38,5 @@ 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
+process/size_limit=512
+detect_3d/compress_to=0
diff --git a/art/exported/characters/faces/noses/nose_human.png.import b/art/exported/characters/faces/noses/nose_human.png.import
index 2381527..486fa00 100644
--- a/art/exported/characters/faces/noses/nose_human.png.import
+++ b/art/exported/characters/faces/noses/nose_human.png.import
@@ -3,19 +3,21 @@
importer="texture"
type="CompressedTexture2D"
uid="uid://cuthoacwggro3"
-path="res://.godot/imported/nose_human.png-337d9ba4f724d6241a6fa9a307a30008.ctex"
+path.s3tc="res://.godot/imported/nose_human.png-337d9ba4f724d6241a6fa9a307a30008.s3tc.ctex"
+path.etc2="res://.godot/imported/nose_human.png-337d9ba4f724d6241a6fa9a307a30008.etc2.ctex"
metadata={
-"vram_texture": false
+"imported_formats": ["s3tc_bptc", "etc2_astc"],
+"vram_texture": true
}
[deps]
source_file="res://art/exported/characters/faces/noses/nose_human.png"
-dest_files=["res://.godot/imported/nose_human.png-337d9ba4f724d6241a6fa9a307a30008.ctex"]
+dest_files=["res://.godot/imported/nose_human.png-337d9ba4f724d6241a6fa9a307a30008.s3tc.ctex", "res://.godot/imported/nose_human.png-337d9ba4f724d6241a6fa9a307a30008.etc2.ctex"]
[params]
-compress/mode=0
+compress/mode=2
compress/high_quality=false
compress/lossy_quality=0.7
compress/uastc_level=0
@@ -36,5 +38,5 @@ 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
+process/size_limit=512
+detect_3d/compress_to=0
diff --git a/art/exported/characters/faces/noses/nose_nostrils.png.import b/art/exported/characters/faces/noses/nose_nostrils.png.import
index 8e9c447..68ae74a 100644
--- a/art/exported/characters/faces/noses/nose_nostrils.png.import
+++ b/art/exported/characters/faces/noses/nose_nostrils.png.import
@@ -3,19 +3,21 @@
importer="texture"
type="CompressedTexture2D"
uid="uid://bvu2lox5px3ab"
-path="res://.godot/imported/nose_nostrils.png-9e1c2b5c8e7adb019d72a39b1e805781.ctex"
+path.s3tc="res://.godot/imported/nose_nostrils.png-9e1c2b5c8e7adb019d72a39b1e805781.s3tc.ctex"
+path.etc2="res://.godot/imported/nose_nostrils.png-9e1c2b5c8e7adb019d72a39b1e805781.etc2.ctex"
metadata={
-"vram_texture": false
+"imported_formats": ["s3tc_bptc", "etc2_astc"],
+"vram_texture": true
}
[deps]
source_file="res://art/exported/characters/faces/noses/nose_nostrils.png"
-dest_files=["res://.godot/imported/nose_nostrils.png-9e1c2b5c8e7adb019d72a39b1e805781.ctex"]
+dest_files=["res://.godot/imported/nose_nostrils.png-9e1c2b5c8e7adb019d72a39b1e805781.s3tc.ctex", "res://.godot/imported/nose_nostrils.png-9e1c2b5c8e7adb019d72a39b1e805781.etc2.ctex"]
[params]
-compress/mode=0
+compress/mode=2
compress/high_quality=false
compress/lossy_quality=0.7
compress/uastc_level=0
@@ -36,5 +38,5 @@ 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
+process/size_limit=512
+detect_3d/compress_to=0
diff --git a/art/exported/characters/faces/noses/nose_piercing.png.import b/art/exported/characters/faces/noses/nose_piercing.png.import
index 3584e75..d50d3e0 100644
--- a/art/exported/characters/faces/noses/nose_piercing.png.import
+++ b/art/exported/characters/faces/noses/nose_piercing.png.import
@@ -3,19 +3,21 @@
importer="texture"
type="CompressedTexture2D"
uid="uid://3w37sblwbd8b"
-path="res://.godot/imported/nose_piercing.png-0c3316905c9f379988cc52353c88569c.ctex"
+path.s3tc="res://.godot/imported/nose_piercing.png-0c3316905c9f379988cc52353c88569c.s3tc.ctex"
+path.etc2="res://.godot/imported/nose_piercing.png-0c3316905c9f379988cc52353c88569c.etc2.ctex"
metadata={
-"vram_texture": false
+"imported_formats": ["s3tc_bptc", "etc2_astc"],
+"vram_texture": true
}
[deps]
source_file="res://art/exported/characters/faces/noses/nose_piercing.png"
-dest_files=["res://.godot/imported/nose_piercing.png-0c3316905c9f379988cc52353c88569c.ctex"]
+dest_files=["res://.godot/imported/nose_piercing.png-0c3316905c9f379988cc52353c88569c.s3tc.ctex", "res://.godot/imported/nose_piercing.png-0c3316905c9f379988cc52353c88569c.etc2.ctex"]
[params]
-compress/mode=0
+compress/mode=2
compress/high_quality=false
compress/lossy_quality=0.7
compress/uastc_level=0
@@ -36,5 +38,5 @@ 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
+process/size_limit=512
+detect_3d/compress_to=0
diff --git a/art/exported/characters/faces/noses/nose_pig_snout.png.import b/art/exported/characters/faces/noses/nose_pig_snout.png.import
index 5e34829..9ead70a 100644
--- a/art/exported/characters/faces/noses/nose_pig_snout.png.import
+++ b/art/exported/characters/faces/noses/nose_pig_snout.png.import
@@ -3,19 +3,21 @@
importer="texture"
type="CompressedTexture2D"
uid="uid://bvran6dr0u22l"
-path="res://.godot/imported/nose_pig_snout.png-40e08884db61f222d4371c6c6a798bca.ctex"
+path.s3tc="res://.godot/imported/nose_pig_snout.png-40e08884db61f222d4371c6c6a798bca.s3tc.ctex"
+path.etc2="res://.godot/imported/nose_pig_snout.png-40e08884db61f222d4371c6c6a798bca.etc2.ctex"
metadata={
-"vram_texture": false
+"imported_formats": ["s3tc_bptc", "etc2_astc"],
+"vram_texture": true
}
[deps]
source_file="res://art/exported/characters/faces/noses/nose_pig_snout.png"
-dest_files=["res://.godot/imported/nose_pig_snout.png-40e08884db61f222d4371c6c6a798bca.ctex"]
+dest_files=["res://.godot/imported/nose_pig_snout.png-40e08884db61f222d4371c6c6a798bca.s3tc.ctex", "res://.godot/imported/nose_pig_snout.png-40e08884db61f222d4371c6c6a798bca.etc2.ctex"]
[params]
-compress/mode=0
+compress/mode=2
compress/high_quality=false
compress/lossy_quality=0.7
compress/uastc_level=0
@@ -36,5 +38,5 @@ 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
+process/size_limit=512
+detect_3d/compress_to=0
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
index 52ab860..c66209b 100644
--- a/art/exported/characters/faces/noses/nose_pig_snout_pink.png.import
+++ b/art/exported/characters/faces/noses/nose_pig_snout_pink.png.import
@@ -3,19 +3,21 @@
importer="texture"
type="CompressedTexture2D"
uid="uid://cyigqco0dp8ph"
-path="res://.godot/imported/nose_pig_snout_pink.png-da6fcfc06a8aa02d6ad664e2ed669558.ctex"
+path.s3tc="res://.godot/imported/nose_pig_snout_pink.png-da6fcfc06a8aa02d6ad664e2ed669558.s3tc.ctex"
+path.etc2="res://.godot/imported/nose_pig_snout_pink.png-da6fcfc06a8aa02d6ad664e2ed669558.etc2.ctex"
metadata={
-"vram_texture": false
+"imported_formats": ["s3tc_bptc", "etc2_astc"],
+"vram_texture": true
}
[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"]
+dest_files=["res://.godot/imported/nose_pig_snout_pink.png-da6fcfc06a8aa02d6ad664e2ed669558.s3tc.ctex", "res://.godot/imported/nose_pig_snout_pink.png-da6fcfc06a8aa02d6ad664e2ed669558.etc2.ctex"]
[params]
-compress/mode=0
+compress/mode=2
compress/high_quality=false
compress/lossy_quality=0.7
compress/uastc_level=0
@@ -36,5 +38,5 @@ 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
+process/size_limit=512
+detect_3d/compress_to=0
diff --git a/art/exported/characters/faces/noses/nose_squidward.png.import b/art/exported/characters/faces/noses/nose_squidward.png.import
index dbba070..ab07b93 100644
--- a/art/exported/characters/faces/noses/nose_squidward.png.import
+++ b/art/exported/characters/faces/noses/nose_squidward.png.import
@@ -3,19 +3,21 @@
importer="texture"
type="CompressedTexture2D"
uid="uid://ilvb5vre4kv7"
-path="res://.godot/imported/nose_squidward.png-579e15cf6acfb402abda29a8a555d504.ctex"
+path.s3tc="res://.godot/imported/nose_squidward.png-579e15cf6acfb402abda29a8a555d504.s3tc.ctex"
+path.etc2="res://.godot/imported/nose_squidward.png-579e15cf6acfb402abda29a8a555d504.etc2.ctex"
metadata={
-"vram_texture": false
+"imported_formats": ["s3tc_bptc", "etc2_astc"],
+"vram_texture": true
}
[deps]
source_file="res://art/exported/characters/faces/noses/nose_squidward.png"
-dest_files=["res://.godot/imported/nose_squidward.png-579e15cf6acfb402abda29a8a555d504.ctex"]
+dest_files=["res://.godot/imported/nose_squidward.png-579e15cf6acfb402abda29a8a555d504.s3tc.ctex", "res://.godot/imported/nose_squidward.png-579e15cf6acfb402abda29a8a555d504.etc2.ctex"]
[params]
-compress/mode=0
+compress/mode=2
compress/high_quality=false
compress/lossy_quality=0.7
compress/uastc_level=0
@@ -36,5 +38,5 @@ 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
+process/size_limit=512
+detect_3d/compress_to=0
diff --git a/art/exported/characters/faces/noses/nose_squidward_wide.png.import b/art/exported/characters/faces/noses/nose_squidward_wide.png.import
index 92d6c8a..cf307ce 100644
--- a/art/exported/characters/faces/noses/nose_squidward_wide.png.import
+++ b/art/exported/characters/faces/noses/nose_squidward_wide.png.import
@@ -3,19 +3,21 @@
importer="texture"
type="CompressedTexture2D"
uid="uid://dtisqywq2rv14"
-path="res://.godot/imported/nose_squidward_wide.png-fca231f913f03b97c273c870770f7948.ctex"
+path.s3tc="res://.godot/imported/nose_squidward_wide.png-fca231f913f03b97c273c870770f7948.s3tc.ctex"
+path.etc2="res://.godot/imported/nose_squidward_wide.png-fca231f913f03b97c273c870770f7948.etc2.ctex"
metadata={
-"vram_texture": false
+"imported_formats": ["s3tc_bptc", "etc2_astc"],
+"vram_texture": true
}
[deps]
source_file="res://art/exported/characters/faces/noses/nose_squidward_wide.png"
-dest_files=["res://.godot/imported/nose_squidward_wide.png-fca231f913f03b97c273c870770f7948.ctex"]
+dest_files=["res://.godot/imported/nose_squidward_wide.png-fca231f913f03b97c273c870770f7948.s3tc.ctex", "res://.godot/imported/nose_squidward_wide.png-fca231f913f03b97c273c870770f7948.etc2.ctex"]
[params]
-compress/mode=0
+compress/mode=2
compress/high_quality=false
compress/lossy_quality=0.7
compress/uastc_level=0
@@ -36,5 +38,5 @@ 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
+process/size_limit=512
+detect_3d/compress_to=0
diff --git a/art/exported/characters/faces/noses/nose_triangle.png.import b/art/exported/characters/faces/noses/nose_triangle.png.import
index bc7aa21..ba494a2 100644
--- a/art/exported/characters/faces/noses/nose_triangle.png.import
+++ b/art/exported/characters/faces/noses/nose_triangle.png.import
@@ -3,19 +3,21 @@
importer="texture"
type="CompressedTexture2D"
uid="uid://bitins3gtewe"
-path="res://.godot/imported/nose_triangle.png-018974a6bbf68652b8c5533c2675cc2f.ctex"
+path.s3tc="res://.godot/imported/nose_triangle.png-018974a6bbf68652b8c5533c2675cc2f.s3tc.ctex"
+path.etc2="res://.godot/imported/nose_triangle.png-018974a6bbf68652b8c5533c2675cc2f.etc2.ctex"
metadata={
-"vram_texture": false
+"imported_formats": ["s3tc_bptc", "etc2_astc"],
+"vram_texture": true
}
[deps]
source_file="res://art/exported/characters/faces/noses/nose_triangle.png"
-dest_files=["res://.godot/imported/nose_triangle.png-018974a6bbf68652b8c5533c2675cc2f.ctex"]
+dest_files=["res://.godot/imported/nose_triangle.png-018974a6bbf68652b8c5533c2675cc2f.s3tc.ctex", "res://.godot/imported/nose_triangle.png-018974a6bbf68652b8c5533c2675cc2f.etc2.ctex"]
[params]
-compress/mode=0
+compress/mode=2
compress/high_quality=false
compress/lossy_quality=0.7
compress/uastc_level=0
@@ -36,5 +38,5 @@ 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
+process/size_limit=512
+detect_3d/compress_to=0
diff --git a/art/exported/characters/faces/noses/nose_triangle_pink.png.import b/art/exported/characters/faces/noses/nose_triangle_pink.png.import
index 56712ab..eff0bb1 100644
--- a/art/exported/characters/faces/noses/nose_triangle_pink.png.import
+++ b/art/exported/characters/faces/noses/nose_triangle_pink.png.import
@@ -3,19 +3,21 @@
importer="texture"
type="CompressedTexture2D"
uid="uid://oi8qhxbtijmk"
-path="res://.godot/imported/nose_triangle_pink.png-051ce6df5e7fd5932f19fd353123d7d1.ctex"
+path.s3tc="res://.godot/imported/nose_triangle_pink.png-051ce6df5e7fd5932f19fd353123d7d1.s3tc.ctex"
+path.etc2="res://.godot/imported/nose_triangle_pink.png-051ce6df5e7fd5932f19fd353123d7d1.etc2.ctex"
metadata={
-"vram_texture": false
+"imported_formats": ["s3tc_bptc", "etc2_astc"],
+"vram_texture": true
}
[deps]
source_file="res://art/exported/characters/faces/noses/nose_triangle_pink.png"
-dest_files=["res://.godot/imported/nose_triangle_pink.png-051ce6df5e7fd5932f19fd353123d7d1.ctex"]
+dest_files=["res://.godot/imported/nose_triangle_pink.png-051ce6df5e7fd5932f19fd353123d7d1.s3tc.ctex", "res://.godot/imported/nose_triangle_pink.png-051ce6df5e7fd5932f19fd353123d7d1.etc2.ctex"]
[params]
-compress/mode=0
+compress/mode=2
compress/high_quality=false
compress/lossy_quality=0.7
compress/uastc_level=0
@@ -36,5 +38,5 @@ 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
+process/size_limit=512
+detect_3d/compress_to=0
diff --git a/art/exported/characters/faces/noses/nose_triangle_small.png.import b/art/exported/characters/faces/noses/nose_triangle_small.png.import
index a659077..e704938 100644
--- a/art/exported/characters/faces/noses/nose_triangle_small.png.import
+++ b/art/exported/characters/faces/noses/nose_triangle_small.png.import
@@ -3,19 +3,21 @@
importer="texture"
type="CompressedTexture2D"
uid="uid://cedgrca26ponk"
-path="res://.godot/imported/nose_triangle_small.png-64129c5fc69bf7095f684c2b7de2855b.ctex"
+path.s3tc="res://.godot/imported/nose_triangle_small.png-64129c5fc69bf7095f684c2b7de2855b.s3tc.ctex"
+path.etc2="res://.godot/imported/nose_triangle_small.png-64129c5fc69bf7095f684c2b7de2855b.etc2.ctex"
metadata={
-"vram_texture": false
+"imported_formats": ["s3tc_bptc", "etc2_astc"],
+"vram_texture": true
}
[deps]
source_file="res://art/exported/characters/faces/noses/nose_triangle_small.png"
-dest_files=["res://.godot/imported/nose_triangle_small.png-64129c5fc69bf7095f684c2b7de2855b.ctex"]
+dest_files=["res://.godot/imported/nose_triangle_small.png-64129c5fc69bf7095f684c2b7de2855b.s3tc.ctex", "res://.godot/imported/nose_triangle_small.png-64129c5fc69bf7095f684c2b7de2855b.etc2.ctex"]
[params]
-compress/mode=0
+compress/mode=2
compress/high_quality=false
compress/lossy_quality=0.7
compress/uastc_level=0
@@ -36,5 +38,5 @@ 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
+process/size_limit=512
+detect_3d/compress_to=0
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
index c875ba4..b3060e6 100644
--- a/art/exported/characters/faces/noses/nose_triangle_small_pink.png.import
+++ b/art/exported/characters/faces/noses/nose_triangle_small_pink.png.import
@@ -3,19 +3,21 @@
importer="texture"
type="CompressedTexture2D"
uid="uid://0kird82ka1ug"
-path="res://.godot/imported/nose_triangle_small_pink.png-fba736a5f1806c1286f1d3040592933b.ctex"
+path.s3tc="res://.godot/imported/nose_triangle_small_pink.png-fba736a5f1806c1286f1d3040592933b.s3tc.ctex"
+path.etc2="res://.godot/imported/nose_triangle_small_pink.png-fba736a5f1806c1286f1d3040592933b.etc2.ctex"
metadata={
-"vram_texture": false
+"imported_formats": ["s3tc_bptc", "etc2_astc"],
+"vram_texture": true
}
[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"]
+dest_files=["res://.godot/imported/nose_triangle_small_pink.png-fba736a5f1806c1286f1d3040592933b.s3tc.ctex", "res://.godot/imported/nose_triangle_small_pink.png-fba736a5f1806c1286f1d3040592933b.etc2.ctex"]
[params]
-compress/mode=0
+compress/mode=2
compress/high_quality=false
compress/lossy_quality=0.7
compress/uastc_level=0
@@ -36,5 +38,5 @@ 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
+process/size_limit=512
+detect_3d/compress_to=0
diff --git a/art/exported/characters/faces/noses/nose_under.png.import b/art/exported/characters/faces/noses/nose_under.png.import
index 5bf860f..d0c87ea 100644
--- a/art/exported/characters/faces/noses/nose_under.png.import
+++ b/art/exported/characters/faces/noses/nose_under.png.import
@@ -3,19 +3,21 @@
importer="texture"
type="CompressedTexture2D"
uid="uid://u0ih35v73j5h"
-path="res://.godot/imported/nose_under.png-269bae96966b3c6367d43ac9bd5b2a3f.ctex"
+path.s3tc="res://.godot/imported/nose_under.png-269bae96966b3c6367d43ac9bd5b2a3f.s3tc.ctex"
+path.etc2="res://.godot/imported/nose_under.png-269bae96966b3c6367d43ac9bd5b2a3f.etc2.ctex"
metadata={
-"vram_texture": false
+"imported_formats": ["s3tc_bptc", "etc2_astc"],
+"vram_texture": true
}
[deps]
source_file="res://art/exported/characters/faces/noses/nose_under.png"
-dest_files=["res://.godot/imported/nose_under.png-269bae96966b3c6367d43ac9bd5b2a3f.ctex"]
+dest_files=["res://.godot/imported/nose_under.png-269bae96966b3c6367d43ac9bd5b2a3f.s3tc.ctex", "res://.godot/imported/nose_under.png-269bae96966b3c6367d43ac9bd5b2a3f.etc2.ctex"]
[params]
-compress/mode=0
+compress/mode=2
compress/high_quality=false
compress/lossy_quality=0.7
compress/uastc_level=0
@@ -36,5 +38,5 @@ 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
+process/size_limit=512
+detect_3d/compress_to=0
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/calico/body_main_calico.png b/art/exported/characters/patterns/calico/body_main_calico.png
new file mode 100644
index 0000000..ef2acad
Binary files /dev/null and b/art/exported/characters/patterns/calico/body_main_calico.png differ
diff --git a/art/exported/characters/patterns/calico/body_main_calico.png.import b/art/exported/characters/patterns/calico/body_main_calico.png.import
new file mode 100644
index 0000000..25f60c0
--- /dev/null
+++ b/art/exported/characters/patterns/calico/body_main_calico.png.import
@@ -0,0 +1,40 @@
+[remap]
+
+importer="texture"
+type="CompressedTexture2D"
+uid="uid://m1ujlrpjghq2"
+path="res://.godot/imported/body_main_calico.png-c5644e883b1aaedc3c3114ce40eaae02.ctex"
+metadata={
+"vram_texture": false
+}
+
+[deps]
+
+source_file="res://art/exported/characters/patterns/calico/body_main_calico.png"
+dest_files=["res://.godot/imported/body_main_calico.png-c5644e883b1aaedc3c3114ce40eaae02.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/ears_pointy_short_fox.png b/art/exported/characters/patterns/fox/ears_pointy_short_fox.png
new file mode 100644
index 0000000..03720dd
Binary files /dev/null and b/art/exported/characters/patterns/fox/ears_pointy_short_fox.png differ
diff --git a/art/exported/characters/patterns/fox/ears_pointy_short_fox.png.import b/art/exported/characters/patterns/fox/ears_pointy_short_fox.png.import
new file mode 100644
index 0000000..70910ef
--- /dev/null
+++ b/art/exported/characters/patterns/fox/ears_pointy_short_fox.png.import
@@ -0,0 +1,40 @@
+[remap]
+
+importer="texture"
+type="CompressedTexture2D"
+uid="uid://bawjitj68qe5s"
+path="res://.godot/imported/ears_pointy_short_fox.png-a42bd3e51180136284f946a738c44a1d.ctex"
+metadata={
+"vram_texture": false
+}
+
+[deps]
+
+source_file="res://art/exported/characters/patterns/fox/ears_pointy_short_fox.png"
+dest_files=["res://.godot/imported/ears_pointy_short_fox.png-a42bd3e51180136284f946a738c44a1d.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..427650f
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/characters/patterns/paws/body_arms_paws.png b/art/exported/characters/patterns/paws/body_arms_paws.png
new file mode 100644
index 0000000..4b3c2d7
Binary files /dev/null and b/art/exported/characters/patterns/paws/body_arms_paws.png differ
diff --git a/art/exported/characters/patterns/paws/body_arms_paws.png.import b/art/exported/characters/patterns/paws/body_arms_paws.png.import
new file mode 100644
index 0000000..877788b
--- /dev/null
+++ b/art/exported/characters/patterns/paws/body_arms_paws.png.import
@@ -0,0 +1,40 @@
+[remap]
+
+importer="texture"
+type="CompressedTexture2D"
+uid="uid://dmqpy81ves1g8"
+path="res://.godot/imported/body_arms_paws.png-06ae28eef9d8a116856aac8b0c511466.ctex"
+metadata={
+"vram_texture": false
+}
+
+[deps]
+
+source_file="res://art/exported/characters/patterns/paws/body_arms_paws.png"
+dest_files=["res://.godot/imported/body_arms_paws.png-06ae28eef9d8a116856aac8b0c511466.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/paws/body_main_paws.png b/art/exported/characters/patterns/paws/body_main_paws.png
new file mode 100644
index 0000000..bd738b7
Binary files /dev/null and b/art/exported/characters/patterns/paws/body_main_paws.png differ
diff --git a/art/exported/characters/patterns/paws/body_main_paws.png.import b/art/exported/characters/patterns/paws/body_main_paws.png.import
new file mode 100644
index 0000000..4db3aa8
--- /dev/null
+++ b/art/exported/characters/patterns/paws/body_main_paws.png.import
@@ -0,0 +1,40 @@
+[remap]
+
+importer="texture"
+type="CompressedTexture2D"
+uid="uid://nl50my4whybb"
+path="res://.godot/imported/body_main_paws.png-7262be81b419a66af0dc6fa26e01a7f8.ctex"
+metadata={
+"vram_texture": false
+}
+
+[deps]
+
+source_file="res://art/exported/characters/patterns/paws/body_main_paws.png"
+dest_files=["res://.godot/imported/body_main_paws.png-7262be81b419a66af0dc6fa26e01a7f8.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/stripes/body_main_stripes.png b/art/exported/characters/patterns/stripes/body_main_stripes.png
new file mode 100644
index 0000000..1eac4f2
Binary files /dev/null and b/art/exported/characters/patterns/stripes/body_main_stripes.png differ
diff --git a/art/exported/characters/patterns/stripes/body_main_stripes.png.import b/art/exported/characters/patterns/stripes/body_main_stripes.png.import
new file mode 100644
index 0000000..28192d5
--- /dev/null
+++ b/art/exported/characters/patterns/stripes/body_main_stripes.png.import
@@ -0,0 +1,40 @@
+[remap]
+
+importer="texture"
+type="CompressedTexture2D"
+uid="uid://cmmemafdvgw5y"
+path="res://.godot/imported/body_main_stripes.png-2ff942bebaea90b5e13ff6f122820689.ctex"
+metadata={
+"vram_texture": false
+}
+
+[deps]
+
+source_file="res://art/exported/characters/patterns/stripes/body_main_stripes.png"
+dest_files=["res://.godot/imported/body_main_stripes.png-2ff942bebaea90b5e13ff6f122820689.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/tiger/body_main_tiger.png b/art/exported/characters/patterns/tiger/body_main_tiger.png
new file mode 100644
index 0000000..e61d7ba
Binary files /dev/null and b/art/exported/characters/patterns/tiger/body_main_tiger.png differ
diff --git a/art/exported/characters/patterns/tiger/body_main_tiger.png.import b/art/exported/characters/patterns/tiger/body_main_tiger.png.import
new file mode 100644
index 0000000..28f3b3d
--- /dev/null
+++ b/art/exported/characters/patterns/tiger/body_main_tiger.png.import
@@ -0,0 +1,40 @@
+[remap]
+
+importer="texture"
+type="CompressedTexture2D"
+uid="uid://dbakek50irjdp"
+path="res://.godot/imported/body_main_tiger.png-30e81e0d87ce0d9af9d678c10fd2e5bd.ctex"
+metadata={
+"vram_texture": false
+}
+
+[deps]
+
+source_file="res://art/exported/characters/patterns/tiger/body_main_tiger.png"
+dest_files=["res://.godot/imported/body_main_tiger.png-30e81e0d87ce0d9af9d678c10fd2e5bd.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/tiger/head_round_tiger.png b/art/exported/characters/patterns/tiger/head_round_tiger.png
new file mode 100644
index 0000000..e89fb56
Binary files /dev/null and b/art/exported/characters/patterns/tiger/head_round_tiger.png differ
diff --git a/art/exported/characters/patterns/tiger/head_round_tiger.png.import b/art/exported/characters/patterns/tiger/head_round_tiger.png.import
new file mode 100644
index 0000000..1b0c5e1
--- /dev/null
+++ b/art/exported/characters/patterns/tiger/head_round_tiger.png.import
@@ -0,0 +1,40 @@
+[remap]
+
+importer="texture"
+type="CompressedTexture2D"
+uid="uid://cnpjuifjm0h4i"
+path="res://.godot/imported/head_round_tiger.png-fe9ec3d30703aa477bff0311b12fc252.ctex"
+metadata={
+"vram_texture": false
+}
+
+[deps]
+
+source_file="res://art/exported/characters/patterns/tiger/head_round_tiger.png"
+dest_files=["res://.godot/imported/head_round_tiger.png-fe9ec3d30703aa477bff0311b12fc252.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/tummy/body_main_tummy.png b/art/exported/characters/patterns/tummy/body_main_tummy.png
new file mode 100644
index 0000000..241c2a8
Binary files /dev/null and b/art/exported/characters/patterns/tummy/body_main_tummy.png differ
diff --git a/art/exported/characters/patterns/tummy/body_main_tummy.png.import b/art/exported/characters/patterns/tummy/body_main_tummy.png.import
new file mode 100644
index 0000000..044bee3
--- /dev/null
+++ b/art/exported/characters/patterns/tummy/body_main_tummy.png.import
@@ -0,0 +1,40 @@
+[remap]
+
+importer="texture"
+type="CompressedTexture2D"
+uid="uid://dv1bran6gqbd5"
+path="res://.godot/imported/body_main_tummy.png-74bd92fbb462e4dbe1c1bf841db71eb0.ctex"
+metadata={
+"vram_texture": false
+}
+
+[deps]
+
+source_file="res://art/exported/characters/patterns/tummy/body_main_tummy.png"
+dest_files=["res://.godot/imported/body_main_tummy.png-74bd92fbb462e4dbe1c1bf841db71eb0.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/creatures/101.png b/art/exported/creatures/101.png
new file mode 100644
index 0000000..e6e431c
Binary files /dev/null and b/art/exported/creatures/101.png differ
diff --git a/art/exported/creatures/101.png.import b/art/exported/creatures/101.png.import
new file mode 100644
index 0000000..3dc8865
--- /dev/null
+++ b/art/exported/creatures/101.png.import
@@ -0,0 +1,42 @@
+[remap]
+
+importer="texture"
+type="CompressedTexture2D"
+uid="uid://x5e744k11k70"
+path.s3tc="res://.godot/imported/101.png-81f7f90e29292575c1a9727aa6060fa0.s3tc.ctex"
+path.etc2="res://.godot/imported/101.png-81f7f90e29292575c1a9727aa6060fa0.etc2.ctex"
+metadata={
+"imported_formats": ["s3tc_bptc", "etc2_astc"],
+"vram_texture": true
+}
+
+[deps]
+
+source_file="res://art/exported/creatures/101.png"
+dest_files=["res://.godot/imported/101.png-81f7f90e29292575c1a9727aa6060fa0.s3tc.ctex", "res://.godot/imported/101.png-81f7f90e29292575c1a9727aa6060fa0.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/creatures/102.png b/art/exported/creatures/102.png
new file mode 100644
index 0000000..b1b66a6
Binary files /dev/null and b/art/exported/creatures/102.png differ
diff --git a/art/exported/creatures/102.png.import b/art/exported/creatures/102.png.import
new file mode 100644
index 0000000..c958e5f
--- /dev/null
+++ b/art/exported/creatures/102.png.import
@@ -0,0 +1,42 @@
+[remap]
+
+importer="texture"
+type="CompressedTexture2D"
+uid="uid://1kla4jewitot"
+path.s3tc="res://.godot/imported/102.png-2ab8ac9408a482b1042b6ba3cd3bd718.s3tc.ctex"
+path.etc2="res://.godot/imported/102.png-2ab8ac9408a482b1042b6ba3cd3bd718.etc2.ctex"
+metadata={
+"imported_formats": ["s3tc_bptc", "etc2_astc"],
+"vram_texture": true
+}
+
+[deps]
+
+source_file="res://art/exported/creatures/102.png"
+dest_files=["res://.godot/imported/102.png-2ab8ac9408a482b1042b6ba3cd3bd718.s3tc.ctex", "res://.godot/imported/102.png-2ab8ac9408a482b1042b6ba3cd3bd718.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/creatures/103.png b/art/exported/creatures/103.png
new file mode 100644
index 0000000..98dd618
Binary files /dev/null and b/art/exported/creatures/103.png differ
diff --git a/art/exported/creatures/103.png.import b/art/exported/creatures/103.png.import
new file mode 100644
index 0000000..2972cde
--- /dev/null
+++ b/art/exported/creatures/103.png.import
@@ -0,0 +1,42 @@
+[remap]
+
+importer="texture"
+type="CompressedTexture2D"
+uid="uid://cfdsg4tevv0jm"
+path.s3tc="res://.godot/imported/103.png-f8985f10e04b19499cd6f5ffbadc83c9.s3tc.ctex"
+path.etc2="res://.godot/imported/103.png-f8985f10e04b19499cd6f5ffbadc83c9.etc2.ctex"
+metadata={
+"imported_formats": ["s3tc_bptc", "etc2_astc"],
+"vram_texture": true
+}
+
+[deps]
+
+source_file="res://art/exported/creatures/103.png"
+dest_files=["res://.godot/imported/103.png-f8985f10e04b19499cd6f5ffbadc83c9.s3tc.ctex", "res://.godot/imported/103.png-f8985f10e04b19499cd6f5ffbadc83c9.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/creatures/104.png b/art/exported/creatures/104.png
new file mode 100644
index 0000000..09eec3c
Binary files /dev/null and b/art/exported/creatures/104.png differ
diff --git a/art/exported/creatures/104.png.import b/art/exported/creatures/104.png.import
new file mode 100644
index 0000000..01a9393
--- /dev/null
+++ b/art/exported/creatures/104.png.import
@@ -0,0 +1,42 @@
+[remap]
+
+importer="texture"
+type="CompressedTexture2D"
+uid="uid://pgm364g5qlgs"
+path.s3tc="res://.godot/imported/104.png-e164f67032648af2cc0a545d2a861c7d.s3tc.ctex"
+path.etc2="res://.godot/imported/104.png-e164f67032648af2cc0a545d2a861c7d.etc2.ctex"
+metadata={
+"imported_formats": ["s3tc_bptc", "etc2_astc"],
+"vram_texture": true
+}
+
+[deps]
+
+source_file="res://art/exported/creatures/104.png"
+dest_files=["res://.godot/imported/104.png-e164f67032648af2cc0a545d2a861c7d.s3tc.ctex", "res://.godot/imported/104.png-e164f67032648af2cc0a545d2a861c7d.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/creatures/105.png b/art/exported/creatures/105.png
new file mode 100644
index 0000000..3ba1000
Binary files /dev/null and b/art/exported/creatures/105.png differ
diff --git a/art/exported/creatures/105.png.import b/art/exported/creatures/105.png.import
new file mode 100644
index 0000000..de797a7
--- /dev/null
+++ b/art/exported/creatures/105.png.import
@@ -0,0 +1,42 @@
+[remap]
+
+importer="texture"
+type="CompressedTexture2D"
+uid="uid://dwanip2j561n7"
+path.s3tc="res://.godot/imported/105.png-a2b440912e59b3375c6e61e17d08287b.s3tc.ctex"
+path.etc2="res://.godot/imported/105.png-a2b440912e59b3375c6e61e17d08287b.etc2.ctex"
+metadata={
+"imported_formats": ["s3tc_bptc", "etc2_astc"],
+"vram_texture": true
+}
+
+[deps]
+
+source_file="res://art/exported/creatures/105.png"
+dest_files=["res://.godot/imported/105.png-a2b440912e59b3375c6e61e17d08287b.s3tc.ctex", "res://.godot/imported/105.png-a2b440912e59b3375c6e61e17d08287b.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/creatures/106.png b/art/exported/creatures/106.png
new file mode 100644
index 0000000..4227b44
Binary files /dev/null and b/art/exported/creatures/106.png differ
diff --git a/art/exported/creatures/106.png.import b/art/exported/creatures/106.png.import
new file mode 100644
index 0000000..3032c38
--- /dev/null
+++ b/art/exported/creatures/106.png.import
@@ -0,0 +1,42 @@
+[remap]
+
+importer="texture"
+type="CompressedTexture2D"
+uid="uid://cr7tc11ievso5"
+path.s3tc="res://.godot/imported/106.png-306ce449448588714e5a0e883ae441ac.s3tc.ctex"
+path.etc2="res://.godot/imported/106.png-306ce449448588714e5a0e883ae441ac.etc2.ctex"
+metadata={
+"imported_formats": ["s3tc_bptc", "etc2_astc"],
+"vram_texture": true
+}
+
+[deps]
+
+source_file="res://art/exported/creatures/106.png"
+dest_files=["res://.godot/imported/106.png-306ce449448588714e5a0e883ae441ac.s3tc.ctex", "res://.godot/imported/106.png-306ce449448588714e5a0e883ae441ac.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/creatures/107.png b/art/exported/creatures/107.png
new file mode 100644
index 0000000..c843bbe
Binary files /dev/null and b/art/exported/creatures/107.png differ
diff --git a/art/exported/creatures/107.png.import b/art/exported/creatures/107.png.import
new file mode 100644
index 0000000..c4d28e8
--- /dev/null
+++ b/art/exported/creatures/107.png.import
@@ -0,0 +1,42 @@
+[remap]
+
+importer="texture"
+type="CompressedTexture2D"
+uid="uid://c8rf4tnxx8mov"
+path.s3tc="res://.godot/imported/107.png-062740d0bdad6e3ac4ecc1cf92ee6b9d.s3tc.ctex"
+path.etc2="res://.godot/imported/107.png-062740d0bdad6e3ac4ecc1cf92ee6b9d.etc2.ctex"
+metadata={
+"imported_formats": ["s3tc_bptc", "etc2_astc"],
+"vram_texture": true
+}
+
+[deps]
+
+source_file="res://art/exported/creatures/107.png"
+dest_files=["res://.godot/imported/107.png-062740d0bdad6e3ac4ecc1cf92ee6b9d.s3tc.ctex", "res://.godot/imported/107.png-062740d0bdad6e3ac4ecc1cf92ee6b9d.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/creatures/201.png b/art/exported/creatures/201.png
new file mode 100644
index 0000000..3994912
Binary files /dev/null and b/art/exported/creatures/201.png differ
diff --git a/art/exported/creatures/201.png.import b/art/exported/creatures/201.png.import
new file mode 100644
index 0000000..9d5decf
--- /dev/null
+++ b/art/exported/creatures/201.png.import
@@ -0,0 +1,42 @@
+[remap]
+
+importer="texture"
+type="CompressedTexture2D"
+uid="uid://c0q8o1sc8hlv0"
+path.s3tc="res://.godot/imported/201.png-1b2063c63e42271e8f3910da8e3f1277.s3tc.ctex"
+path.etc2="res://.godot/imported/201.png-1b2063c63e42271e8f3910da8e3f1277.etc2.ctex"
+metadata={
+"imported_formats": ["s3tc_bptc", "etc2_astc"],
+"vram_texture": true
+}
+
+[deps]
+
+source_file="res://art/exported/creatures/201.png"
+dest_files=["res://.godot/imported/201.png-1b2063c63e42271e8f3910da8e3f1277.s3tc.ctex", "res://.godot/imported/201.png-1b2063c63e42271e8f3910da8e3f1277.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/creatures/2502.png b/art/exported/creatures/2502.png
new file mode 100644
index 0000000..467431b
Binary files /dev/null and b/art/exported/creatures/2502.png differ
diff --git a/art/exported/creatures/2502.png.import b/art/exported/creatures/2502.png.import
new file mode 100644
index 0000000..17bcad9
--- /dev/null
+++ b/art/exported/creatures/2502.png.import
@@ -0,0 +1,42 @@
+[remap]
+
+importer="texture"
+type="CompressedTexture2D"
+uid="uid://busj0feq2dncu"
+path.s3tc="res://.godot/imported/2502.png-3922e306611b91541a86520cf32cbdbd.s3tc.ctex"
+path.etc2="res://.godot/imported/2502.png-3922e306611b91541a86520cf32cbdbd.etc2.ctex"
+metadata={
+"imported_formats": ["s3tc_bptc", "etc2_astc"],
+"vram_texture": true
+}
+
+[deps]
+
+source_file="res://art/exported/creatures/2502.png"
+dest_files=["res://.godot/imported/2502.png-3922e306611b91541a86520cf32cbdbd.s3tc.ctex", "res://.godot/imported/2502.png-3922e306611b91541a86520cf32cbdbd.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/creatures/2504.png b/art/exported/creatures/2504.png
new file mode 100644
index 0000000..d0279d8
Binary files /dev/null and b/art/exported/creatures/2504.png differ
diff --git a/art/exported/creatures/2504.png.import b/art/exported/creatures/2504.png.import
new file mode 100644
index 0000000..73fa801
--- /dev/null
+++ b/art/exported/creatures/2504.png.import
@@ -0,0 +1,42 @@
+[remap]
+
+importer="texture"
+type="CompressedTexture2D"
+uid="uid://4a7c1aat4777"
+path.s3tc="res://.godot/imported/2504.png-c9f47ab152b933d8158b8c058ec5fcc2.s3tc.ctex"
+path.etc2="res://.godot/imported/2504.png-c9f47ab152b933d8158b8c058ec5fcc2.etc2.ctex"
+metadata={
+"imported_formats": ["s3tc_bptc", "etc2_astc"],
+"vram_texture": true
+}
+
+[deps]
+
+source_file="res://art/exported/creatures/2504.png"
+dest_files=["res://.godot/imported/2504.png-c9f47ab152b933d8158b8c058ec5fcc2.s3tc.ctex", "res://.godot/imported/2504.png-c9f47ab152b933d8158b8c058ec5fcc2.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/creatures/2505.png b/art/exported/creatures/2505.png
new file mode 100644
index 0000000..7463c25
Binary files /dev/null and b/art/exported/creatures/2505.png differ
diff --git a/art/exported/creatures/2505.png.import b/art/exported/creatures/2505.png.import
new file mode 100644
index 0000000..130cedb
--- /dev/null
+++ b/art/exported/creatures/2505.png.import
@@ -0,0 +1,42 @@
+[remap]
+
+importer="texture"
+type="CompressedTexture2D"
+uid="uid://b8u3lw242yr3c"
+path.s3tc="res://.godot/imported/2505.png-fe14d70a6372dbe0cc4a8275d0816cad.s3tc.ctex"
+path.etc2="res://.godot/imported/2505.png-fe14d70a6372dbe0cc4a8275d0816cad.etc2.ctex"
+metadata={
+"imported_formats": ["s3tc_bptc", "etc2_astc"],
+"vram_texture": true
+}
+
+[deps]
+
+source_file="res://art/exported/creatures/2505.png"
+dest_files=["res://.godot/imported/2505.png-fe14d70a6372dbe0cc4a8275d0816cad.s3tc.ctex", "res://.godot/imported/2505.png-fe14d70a6372dbe0cc4a8275d0816cad.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/creatures/2508.png b/art/exported/creatures/2508.png
new file mode 100644
index 0000000..4cefefd
Binary files /dev/null and b/art/exported/creatures/2508.png differ
diff --git a/art/exported/creatures/2508.png.import b/art/exported/creatures/2508.png.import
new file mode 100644
index 0000000..0fdbbe4
--- /dev/null
+++ b/art/exported/creatures/2508.png.import
@@ -0,0 +1,42 @@
+[remap]
+
+importer="texture"
+type="CompressedTexture2D"
+uid="uid://c2k6ghkng6081"
+path.s3tc="res://.godot/imported/2508.png-0478df4a4cb2af8e08ada8086efda0c3.s3tc.ctex"
+path.etc2="res://.godot/imported/2508.png-0478df4a4cb2af8e08ada8086efda0c3.etc2.ctex"
+metadata={
+"imported_formats": ["s3tc_bptc", "etc2_astc"],
+"vram_texture": true
+}
+
+[deps]
+
+source_file="res://art/exported/creatures/2508.png"
+dest_files=["res://.godot/imported/2508.png-0478df4a4cb2af8e08ada8086efda0c3.s3tc.ctex", "res://.godot/imported/2508.png-0478df4a4cb2af8e08ada8086efda0c3.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/creatures/2601.png b/art/exported/creatures/2601.png
new file mode 100644
index 0000000..f7565d8
Binary files /dev/null and b/art/exported/creatures/2601.png differ
diff --git a/art/exported/creatures/2601.png.import b/art/exported/creatures/2601.png.import
new file mode 100644
index 0000000..7444643
--- /dev/null
+++ b/art/exported/creatures/2601.png.import
@@ -0,0 +1,42 @@
+[remap]
+
+importer="texture"
+type="CompressedTexture2D"
+uid="uid://d14jvhurcfryb"
+path.s3tc="res://.godot/imported/2601.png-912942ea4c6b2c66982e089ee46fde12.s3tc.ctex"
+path.etc2="res://.godot/imported/2601.png-912942ea4c6b2c66982e089ee46fde12.etc2.ctex"
+metadata={
+"imported_formats": ["s3tc_bptc", "etc2_astc"],
+"vram_texture": true
+}
+
+[deps]
+
+source_file="res://art/exported/creatures/2601.png"
+dest_files=["res://.godot/imported/2601.png-912942ea4c6b2c66982e089ee46fde12.s3tc.ctex", "res://.godot/imported/2601.png-912942ea4c6b2c66982e089ee46fde12.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/creatures/2801.png b/art/exported/creatures/2801.png
new file mode 100644
index 0000000..ad8294d
Binary files /dev/null and b/art/exported/creatures/2801.png differ
diff --git a/art/exported/creatures/2801.png.import b/art/exported/creatures/2801.png.import
new file mode 100644
index 0000000..512f812
--- /dev/null
+++ b/art/exported/creatures/2801.png.import
@@ -0,0 +1,42 @@
+[remap]
+
+importer="texture"
+type="CompressedTexture2D"
+uid="uid://cc7el34sfad78"
+path.s3tc="res://.godot/imported/2801.png-d1c5df9faa2d1eb1cc3453c8e18d4049.s3tc.ctex"
+path.etc2="res://.godot/imported/2801.png-d1c5df9faa2d1eb1cc3453c8e18d4049.etc2.ctex"
+metadata={
+"imported_formats": ["s3tc_bptc", "etc2_astc"],
+"vram_texture": true
+}
+
+[deps]
+
+source_file="res://art/exported/creatures/2801.png"
+dest_files=["res://.godot/imported/2801.png-d1c5df9faa2d1eb1cc3453c8e18d4049.s3tc.ctex", "res://.godot/imported/2801.png-d1c5df9faa2d1eb1cc3453c8e18d4049.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/creatures/2802.png b/art/exported/creatures/2802.png
new file mode 100644
index 0000000..4190610
Binary files /dev/null and b/art/exported/creatures/2802.png differ
diff --git a/art/exported/creatures/2802.png.import b/art/exported/creatures/2802.png.import
new file mode 100644
index 0000000..4d014d1
--- /dev/null
+++ b/art/exported/creatures/2802.png.import
@@ -0,0 +1,42 @@
+[remap]
+
+importer="texture"
+type="CompressedTexture2D"
+uid="uid://bawecr4tv52p1"
+path.s3tc="res://.godot/imported/2802.png-6319ce264fbd950e232fd0b428319265.s3tc.ctex"
+path.etc2="res://.godot/imported/2802.png-6319ce264fbd950e232fd0b428319265.etc2.ctex"
+metadata={
+"imported_formats": ["s3tc_bptc", "etc2_astc"],
+"vram_texture": true
+}
+
+[deps]
+
+source_file="res://art/exported/creatures/2802.png"
+dest_files=["res://.godot/imported/2802.png-6319ce264fbd950e232fd0b428319265.s3tc.ctex", "res://.godot/imported/2802.png-6319ce264fbd950e232fd0b428319265.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/creatures/2803.png b/art/exported/creatures/2803.png
new file mode 100644
index 0000000..6360f26
Binary files /dev/null and b/art/exported/creatures/2803.png differ
diff --git a/art/exported/creatures/2803.png.import b/art/exported/creatures/2803.png.import
new file mode 100644
index 0000000..5096057
--- /dev/null
+++ b/art/exported/creatures/2803.png.import
@@ -0,0 +1,42 @@
+[remap]
+
+importer="texture"
+type="CompressedTexture2D"
+uid="uid://cjwm07240ehr2"
+path.s3tc="res://.godot/imported/2803.png-c2e31a8adbab3bf9899bc74b3feb4abb.s3tc.ctex"
+path.etc2="res://.godot/imported/2803.png-c2e31a8adbab3bf9899bc74b3feb4abb.etc2.ctex"
+metadata={
+"imported_formats": ["s3tc_bptc", "etc2_astc"],
+"vram_texture": true
+}
+
+[deps]
+
+source_file="res://art/exported/creatures/2803.png"
+dest_files=["res://.godot/imported/2803.png-c2e31a8adbab3bf9899bc74b3feb4abb.s3tc.ctex", "res://.godot/imported/2803.png-c2e31a8adbab3bf9899bc74b3feb4abb.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/creatures/2804.png b/art/exported/creatures/2804.png
new file mode 100644
index 0000000..90952fd
Binary files /dev/null and b/art/exported/creatures/2804.png differ
diff --git a/art/exported/creatures/2804.png.import b/art/exported/creatures/2804.png.import
new file mode 100644
index 0000000..e6147c2
--- /dev/null
+++ b/art/exported/creatures/2804.png.import
@@ -0,0 +1,42 @@
+[remap]
+
+importer="texture"
+type="CompressedTexture2D"
+uid="uid://dgcrfxo1k4juj"
+path.s3tc="res://.godot/imported/2804.png-f86c52a01f04302b057cf526a498e87a.s3tc.ctex"
+path.etc2="res://.godot/imported/2804.png-f86c52a01f04302b057cf526a498e87a.etc2.ctex"
+metadata={
+"imported_formats": ["s3tc_bptc", "etc2_astc"],
+"vram_texture": true
+}
+
+[deps]
+
+source_file="res://art/exported/creatures/2804.png"
+dest_files=["res://.godot/imported/2804.png-f86c52a01f04302b057cf526a498e87a.s3tc.ctex", "res://.godot/imported/2804.png-f86c52a01f04302b057cf526a498e87a.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/creatures/2806.png b/art/exported/creatures/2806.png
new file mode 100644
index 0000000..acb930e
Binary files /dev/null and b/art/exported/creatures/2806.png differ
diff --git a/art/exported/creatures/2806.png.import b/art/exported/creatures/2806.png.import
new file mode 100644
index 0000000..9d444cd
--- /dev/null
+++ b/art/exported/creatures/2806.png.import
@@ -0,0 +1,42 @@
+[remap]
+
+importer="texture"
+type="CompressedTexture2D"
+uid="uid://c5be8db5ppkq4"
+path.s3tc="res://.godot/imported/2806.png-ed0484fec72605166ddb723c1a1bc7d0.s3tc.ctex"
+path.etc2="res://.godot/imported/2806.png-ed0484fec72605166ddb723c1a1bc7d0.etc2.ctex"
+metadata={
+"imported_formats": ["s3tc_bptc", "etc2_astc"],
+"vram_texture": true
+}
+
+[deps]
+
+source_file="res://art/exported/creatures/2806.png"
+dest_files=["res://.godot/imported/2806.png-ed0484fec72605166ddb723c1a1bc7d0.s3tc.ctex", "res://.godot/imported/2806.png-ed0484fec72605166ddb723c1a1bc7d0.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/creatures/2807.png b/art/exported/creatures/2807.png
new file mode 100644
index 0000000..5c2a6da
Binary files /dev/null and b/art/exported/creatures/2807.png differ
diff --git a/art/exported/creatures/2807.png.import b/art/exported/creatures/2807.png.import
new file mode 100644
index 0000000..af51ff5
--- /dev/null
+++ b/art/exported/creatures/2807.png.import
@@ -0,0 +1,42 @@
+[remap]
+
+importer="texture"
+type="CompressedTexture2D"
+uid="uid://bdus4qo0ebs3e"
+path.s3tc="res://.godot/imported/2807.png-aceaf0f586f975d29e318a9513fc3125.s3tc.ctex"
+path.etc2="res://.godot/imported/2807.png-aceaf0f586f975d29e318a9513fc3125.etc2.ctex"
+metadata={
+"imported_formats": ["s3tc_bptc", "etc2_astc"],
+"vram_texture": true
+}
+
+[deps]
+
+source_file="res://art/exported/creatures/2807.png"
+dest_files=["res://.godot/imported/2807.png-aceaf0f586f975d29e318a9513fc3125.s3tc.ctex", "res://.godot/imported/2807.png-aceaf0f586f975d29e318a9513fc3125.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/creatures/3002.png b/art/exported/creatures/3002.png
new file mode 100644
index 0000000..2ae7e73
Binary files /dev/null and b/art/exported/creatures/3002.png differ
diff --git a/art/exported/creatures/3002.png.import b/art/exported/creatures/3002.png.import
new file mode 100644
index 0000000..e193cfd
--- /dev/null
+++ b/art/exported/creatures/3002.png.import
@@ -0,0 +1,42 @@
+[remap]
+
+importer="texture"
+type="CompressedTexture2D"
+uid="uid://b68vlgtclvkfg"
+path.s3tc="res://.godot/imported/3002.png-a87d71158798b43b6bf702a6e1664b4d.s3tc.ctex"
+path.etc2="res://.godot/imported/3002.png-a87d71158798b43b6bf702a6e1664b4d.etc2.ctex"
+metadata={
+"imported_formats": ["s3tc_bptc", "etc2_astc"],
+"vram_texture": true
+}
+
+[deps]
+
+source_file="res://art/exported/creatures/3002.png"
+dest_files=["res://.godot/imported/3002.png-a87d71158798b43b6bf702a6e1664b4d.s3tc.ctex", "res://.godot/imported/3002.png-a87d71158798b43b6bf702a6e1664b4d.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/creatures/3102.png b/art/exported/creatures/3102.png
new file mode 100644
index 0000000..fe4d8fb
Binary files /dev/null and b/art/exported/creatures/3102.png differ
diff --git a/art/exported/creatures/3102.png.import b/art/exported/creatures/3102.png.import
new file mode 100644
index 0000000..47e9c80
--- /dev/null
+++ b/art/exported/creatures/3102.png.import
@@ -0,0 +1,42 @@
+[remap]
+
+importer="texture"
+type="CompressedTexture2D"
+uid="uid://c4d3f28rf86bb"
+path.s3tc="res://.godot/imported/3102.png-951ca90331737e3057749e1bdbd88c44.s3tc.ctex"
+path.etc2="res://.godot/imported/3102.png-951ca90331737e3057749e1bdbd88c44.etc2.ctex"
+metadata={
+"imported_formats": ["s3tc_bptc", "etc2_astc"],
+"vram_texture": true
+}
+
+[deps]
+
+source_file="res://art/exported/creatures/3102.png"
+dest_files=["res://.godot/imported/3102.png-951ca90331737e3057749e1bdbd88c44.s3tc.ctex", "res://.godot/imported/3102.png-951ca90331737e3057749e1bdbd88c44.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/creatures/3103.png b/art/exported/creatures/3103.png
new file mode 100644
index 0000000..3d3d615
Binary files /dev/null and b/art/exported/creatures/3103.png differ
diff --git a/art/exported/creatures/3103.png.import b/art/exported/creatures/3103.png.import
new file mode 100644
index 0000000..9743e89
--- /dev/null
+++ b/art/exported/creatures/3103.png.import
@@ -0,0 +1,42 @@
+[remap]
+
+importer="texture"
+type="CompressedTexture2D"
+uid="uid://cjtncgnb0hcsj"
+path.s3tc="res://.godot/imported/3103.png-f3b4b5bb43b7611f7c448dbdfff36375.s3tc.ctex"
+path.etc2="res://.godot/imported/3103.png-f3b4b5bb43b7611f7c448dbdfff36375.etc2.ctex"
+metadata={
+"imported_formats": ["s3tc_bptc", "etc2_astc"],
+"vram_texture": true
+}
+
+[deps]
+
+source_file="res://art/exported/creatures/3103.png"
+dest_files=["res://.godot/imported/3103.png-f3b4b5bb43b7611f7c448dbdfff36375.s3tc.ctex", "res://.godot/imported/3103.png-f3b4b5bb43b7611f7c448dbdfff36375.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/creatures/3104.png b/art/exported/creatures/3104.png
new file mode 100644
index 0000000..425932e
Binary files /dev/null and b/art/exported/creatures/3104.png differ
diff --git a/art/exported/creatures/3104.png.import b/art/exported/creatures/3104.png.import
new file mode 100644
index 0000000..fbc70d7
--- /dev/null
+++ b/art/exported/creatures/3104.png.import
@@ -0,0 +1,42 @@
+[remap]
+
+importer="texture"
+type="CompressedTexture2D"
+uid="uid://dlme86lrrhyg8"
+path.s3tc="res://.godot/imported/3104.png-fa31dd69bf22b484cf3ac0d99197b886.s3tc.ctex"
+path.etc2="res://.godot/imported/3104.png-fa31dd69bf22b484cf3ac0d99197b886.etc2.ctex"
+metadata={
+"imported_formats": ["s3tc_bptc", "etc2_astc"],
+"vram_texture": true
+}
+
+[deps]
+
+source_file="res://art/exported/creatures/3104.png"
+dest_files=["res://.godot/imported/3104.png-fa31dd69bf22b484cf3ac0d99197b886.s3tc.ctex", "res://.godot/imported/3104.png-fa31dd69bf22b484cf3ac0d99197b886.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/creatures/3107.png b/art/exported/creatures/3107.png
new file mode 100644
index 0000000..7dee611
Binary files /dev/null and b/art/exported/creatures/3107.png differ
diff --git a/art/exported/creatures/3107.png.import b/art/exported/creatures/3107.png.import
new file mode 100644
index 0000000..241bac9
--- /dev/null
+++ b/art/exported/creatures/3107.png.import
@@ -0,0 +1,42 @@
+[remap]
+
+importer="texture"
+type="CompressedTexture2D"
+uid="uid://csv1dsgvifh4l"
+path.s3tc="res://.godot/imported/3107.png-5b981d56f7094a29a92b2f5035a47b11.s3tc.ctex"
+path.etc2="res://.godot/imported/3107.png-5b981d56f7094a29a92b2f5035a47b11.etc2.ctex"
+metadata={
+"imported_formats": ["s3tc_bptc", "etc2_astc"],
+"vram_texture": true
+}
+
+[deps]
+
+source_file="res://art/exported/creatures/3107.png"
+dest_files=["res://.godot/imported/3107.png-5b981d56f7094a29a92b2f5035a47b11.s3tc.ctex", "res://.godot/imported/3107.png-5b981d56f7094a29a92b2f5035a47b11.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/creatures/3201.png b/art/exported/creatures/3201.png
new file mode 100644
index 0000000..bcc639b
Binary files /dev/null and b/art/exported/creatures/3201.png differ
diff --git a/art/exported/creatures/3201.png.import b/art/exported/creatures/3201.png.import
new file mode 100644
index 0000000..a8d6a43
--- /dev/null
+++ b/art/exported/creatures/3201.png.import
@@ -0,0 +1,42 @@
+[remap]
+
+importer="texture"
+type="CompressedTexture2D"
+uid="uid://cd8u0ow8cqsbh"
+path.s3tc="res://.godot/imported/3201.png-846cb0fbb88950bb70d5b03c50a88457.s3tc.ctex"
+path.etc2="res://.godot/imported/3201.png-846cb0fbb88950bb70d5b03c50a88457.etc2.ctex"
+metadata={
+"imported_formats": ["s3tc_bptc", "etc2_astc"],
+"vram_texture": true
+}
+
+[deps]
+
+source_file="res://art/exported/creatures/3201.png"
+dest_files=["res://.godot/imported/3201.png-846cb0fbb88950bb70d5b03c50a88457.s3tc.ctex", "res://.godot/imported/3201.png-846cb0fbb88950bb70d5b03c50a88457.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/creatures/3401.png b/art/exported/creatures/3401.png
new file mode 100644
index 0000000..c14c2ae
Binary files /dev/null and b/art/exported/creatures/3401.png differ
diff --git a/art/exported/creatures/3401.png.import b/art/exported/creatures/3401.png.import
new file mode 100644
index 0000000..096d0fb
--- /dev/null
+++ b/art/exported/creatures/3401.png.import
@@ -0,0 +1,42 @@
+[remap]
+
+importer="texture"
+type="CompressedTexture2D"
+uid="uid://begsj5tj035e1"
+path.s3tc="res://.godot/imported/3401.png-cd8c7ec154f8248110ec396b3f8a76c9.s3tc.ctex"
+path.etc2="res://.godot/imported/3401.png-cd8c7ec154f8248110ec396b3f8a76c9.etc2.ctex"
+metadata={
+"imported_formats": ["s3tc_bptc", "etc2_astc"],
+"vram_texture": true
+}
+
+[deps]
+
+source_file="res://art/exported/creatures/3401.png"
+dest_files=["res://.godot/imported/3401.png-cd8c7ec154f8248110ec396b3f8a76c9.s3tc.ctex", "res://.godot/imported/3401.png-cd8c7ec154f8248110ec396b3f8a76c9.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/creatures/3902.png b/art/exported/creatures/3902.png
new file mode 100644
index 0000000..d231239
Binary files /dev/null and b/art/exported/creatures/3902.png differ
diff --git a/art/exported/creatures/3902.png.import b/art/exported/creatures/3902.png.import
new file mode 100644
index 0000000..a8e5440
--- /dev/null
+++ b/art/exported/creatures/3902.png.import
@@ -0,0 +1,42 @@
+[remap]
+
+importer="texture"
+type="CompressedTexture2D"
+uid="uid://ddc556gcby50v"
+path.s3tc="res://.godot/imported/3902.png-485bc4f9f1a505225bdc037111e32499.s3tc.ctex"
+path.etc2="res://.godot/imported/3902.png-485bc4f9f1a505225bdc037111e32499.etc2.ctex"
+metadata={
+"imported_formats": ["s3tc_bptc", "etc2_astc"],
+"vram_texture": true
+}
+
+[deps]
+
+source_file="res://art/exported/creatures/3902.png"
+dest_files=["res://.godot/imported/3902.png-485bc4f9f1a505225bdc037111e32499.s3tc.ctex", "res://.godot/imported/3902.png-485bc4f9f1a505225bdc037111e32499.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/creatures/3906.png b/art/exported/creatures/3906.png
new file mode 100644
index 0000000..6cc1d1b
Binary files /dev/null and b/art/exported/creatures/3906.png differ
diff --git a/art/exported/creatures/3906.png.import b/art/exported/creatures/3906.png.import
new file mode 100644
index 0000000..9bce5e4
--- /dev/null
+++ b/art/exported/creatures/3906.png.import
@@ -0,0 +1,42 @@
+[remap]
+
+importer="texture"
+type="CompressedTexture2D"
+uid="uid://bdmm1jfod7gq0"
+path.s3tc="res://.godot/imported/3906.png-39cc349a6d3e716822bc9e12bd0ac7a2.s3tc.ctex"
+path.etc2="res://.godot/imported/3906.png-39cc349a6d3e716822bc9e12bd0ac7a2.etc2.ctex"
+metadata={
+"imported_formats": ["s3tc_bptc", "etc2_astc"],
+"vram_texture": true
+}
+
+[deps]
+
+source_file="res://art/exported/creatures/3906.png"
+dest_files=["res://.godot/imported/3906.png-39cc349a6d3e716822bc9e12bd0ac7a2.s3tc.ctex", "res://.godot/imported/3906.png-39cc349a6d3e716822bc9e12bd0ac7a2.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/creatures/403.png b/art/exported/creatures/403.png
new file mode 100644
index 0000000..a1eb283
Binary files /dev/null and b/art/exported/creatures/403.png differ
diff --git a/art/exported/creatures/403.png.import b/art/exported/creatures/403.png.import
new file mode 100644
index 0000000..da790e8
--- /dev/null
+++ b/art/exported/creatures/403.png.import
@@ -0,0 +1,42 @@
+[remap]
+
+importer="texture"
+type="CompressedTexture2D"
+uid="uid://bypagdb0ollh0"
+path.s3tc="res://.godot/imported/403.png-57b9dd64545d97bbd63b86e7f3d61562.s3tc.ctex"
+path.etc2="res://.godot/imported/403.png-57b9dd64545d97bbd63b86e7f3d61562.etc2.ctex"
+metadata={
+"imported_formats": ["s3tc_bptc", "etc2_astc"],
+"vram_texture": true
+}
+
+[deps]
+
+source_file="res://art/exported/creatures/403.png"
+dest_files=["res://.godot/imported/403.png-57b9dd64545d97bbd63b86e7f3d61562.s3tc.ctex", "res://.godot/imported/403.png-57b9dd64545d97bbd63b86e7f3d61562.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/creatures/4101.png b/art/exported/creatures/4101.png
new file mode 100644
index 0000000..74a068b
Binary files /dev/null and b/art/exported/creatures/4101.png differ
diff --git a/art/exported/creatures/4101.png.import b/art/exported/creatures/4101.png.import
new file mode 100644
index 0000000..d906da5
--- /dev/null
+++ b/art/exported/creatures/4101.png.import
@@ -0,0 +1,42 @@
+[remap]
+
+importer="texture"
+type="CompressedTexture2D"
+uid="uid://bahd6nks2g7ik"
+path.s3tc="res://.godot/imported/4101.png-163a8880f3559279d2acc129509430bc.s3tc.ctex"
+path.etc2="res://.godot/imported/4101.png-163a8880f3559279d2acc129509430bc.etc2.ctex"
+metadata={
+"imported_formats": ["s3tc_bptc", "etc2_astc"],
+"vram_texture": true
+}
+
+[deps]
+
+source_file="res://art/exported/creatures/4101.png"
+dest_files=["res://.godot/imported/4101.png-163a8880f3559279d2acc129509430bc.s3tc.ctex", "res://.godot/imported/4101.png-163a8880f3559279d2acc129509430bc.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/creatures/4401.png b/art/exported/creatures/4401.png
new file mode 100644
index 0000000..d043f4c
Binary files /dev/null and b/art/exported/creatures/4401.png differ
diff --git a/art/exported/creatures/4401.png.import b/art/exported/creatures/4401.png.import
new file mode 100644
index 0000000..809a146
--- /dev/null
+++ b/art/exported/creatures/4401.png.import
@@ -0,0 +1,42 @@
+[remap]
+
+importer="texture"
+type="CompressedTexture2D"
+uid="uid://7dbcbicxwvlo"
+path.s3tc="res://.godot/imported/4401.png-0fd242cb090b49614af9b4d07503b2db.s3tc.ctex"
+path.etc2="res://.godot/imported/4401.png-0fd242cb090b49614af9b4d07503b2db.etc2.ctex"
+metadata={
+"imported_formats": ["s3tc_bptc", "etc2_astc"],
+"vram_texture": true
+}
+
+[deps]
+
+source_file="res://art/exported/creatures/4401.png"
+dest_files=["res://.godot/imported/4401.png-0fd242cb090b49614af9b4d07503b2db.s3tc.ctex", "res://.godot/imported/4401.png-0fd242cb090b49614af9b4d07503b2db.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/creatures/4701.png b/art/exported/creatures/4701.png
new file mode 100644
index 0000000..afd6a85
Binary files /dev/null and b/art/exported/creatures/4701.png differ
diff --git a/art/exported/creatures/4701.png.import b/art/exported/creatures/4701.png.import
new file mode 100644
index 0000000..5f4ed32
--- /dev/null
+++ b/art/exported/creatures/4701.png.import
@@ -0,0 +1,42 @@
+[remap]
+
+importer="texture"
+type="CompressedTexture2D"
+uid="uid://do6hcv3omivv5"
+path.s3tc="res://.godot/imported/4701.png-dcb44d4ce6dfdc812a2e185e7f9511be.s3tc.ctex"
+path.etc2="res://.godot/imported/4701.png-dcb44d4ce6dfdc812a2e185e7f9511be.etc2.ctex"
+metadata={
+"imported_formats": ["s3tc_bptc", "etc2_astc"],
+"vram_texture": true
+}
+
+[deps]
+
+source_file="res://art/exported/creatures/4701.png"
+dest_files=["res://.godot/imported/4701.png-dcb44d4ce6dfdc812a2e185e7f9511be.s3tc.ctex", "res://.godot/imported/4701.png-dcb44d4ce6dfdc812a2e185e7f9511be.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/creatures/5001.png b/art/exported/creatures/5001.png
new file mode 100644
index 0000000..cf39422
Binary files /dev/null and b/art/exported/creatures/5001.png differ
diff --git a/art/exported/creatures/5001.png.import b/art/exported/creatures/5001.png.import
new file mode 100644
index 0000000..c87c852
--- /dev/null
+++ b/art/exported/creatures/5001.png.import
@@ -0,0 +1,42 @@
+[remap]
+
+importer="texture"
+type="CompressedTexture2D"
+uid="uid://byil5mr6w3j1q"
+path.s3tc="res://.godot/imported/5001.png-ebe37fb8bd4263bb83e36bb5890b5641.s3tc.ctex"
+path.etc2="res://.godot/imported/5001.png-ebe37fb8bd4263bb83e36bb5890b5641.etc2.ctex"
+metadata={
+"imported_formats": ["s3tc_bptc", "etc2_astc"],
+"vram_texture": true
+}
+
+[deps]
+
+source_file="res://art/exported/creatures/5001.png"
+dest_files=["res://.godot/imported/5001.png-ebe37fb8bd4263bb83e36bb5890b5641.s3tc.ctex", "res://.godot/imported/5001.png-ebe37fb8bd4263bb83e36bb5890b5641.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/creatures/5702.png b/art/exported/creatures/5702.png
new file mode 100644
index 0000000..aba6b80
Binary files /dev/null and b/art/exported/creatures/5702.png differ
diff --git a/art/exported/creatures/5702.png.import b/art/exported/creatures/5702.png.import
new file mode 100644
index 0000000..527c367
--- /dev/null
+++ b/art/exported/creatures/5702.png.import
@@ -0,0 +1,42 @@
+[remap]
+
+importer="texture"
+type="CompressedTexture2D"
+uid="uid://dxwr7yv5h30s"
+path.s3tc="res://.godot/imported/5702.png-b0cead42bd9a5851c3f6775a7d0e469e.s3tc.ctex"
+path.etc2="res://.godot/imported/5702.png-b0cead42bd9a5851c3f6775a7d0e469e.etc2.ctex"
+metadata={
+"imported_formats": ["s3tc_bptc", "etc2_astc"],
+"vram_texture": true
+}
+
+[deps]
+
+source_file="res://art/exported/creatures/5702.png"
+dest_files=["res://.godot/imported/5702.png-b0cead42bd9a5851c3f6775a7d0e469e.s3tc.ctex", "res://.godot/imported/5702.png-b0cead42bd9a5851c3f6775a7d0e469e.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/creatures/5901.png b/art/exported/creatures/5901.png
new file mode 100644
index 0000000..a75039f
Binary files /dev/null and b/art/exported/creatures/5901.png differ
diff --git a/art/exported/creatures/5901.png.import b/art/exported/creatures/5901.png.import
new file mode 100644
index 0000000..e52ad8c
--- /dev/null
+++ b/art/exported/creatures/5901.png.import
@@ -0,0 +1,42 @@
+[remap]
+
+importer="texture"
+type="CompressedTexture2D"
+uid="uid://dovitajskavmi"
+path.s3tc="res://.godot/imported/5901.png-10c9e77992ad31ce54d34b95a6db6f61.s3tc.ctex"
+path.etc2="res://.godot/imported/5901.png-10c9e77992ad31ce54d34b95a6db6f61.etc2.ctex"
+metadata={
+"imported_formats": ["s3tc_bptc", "etc2_astc"],
+"vram_texture": true
+}
+
+[deps]
+
+source_file="res://art/exported/creatures/5901.png"
+dest_files=["res://.godot/imported/5901.png-10c9e77992ad31ce54d34b95a6db6f61.s3tc.ctex", "res://.godot/imported/5901.png-10c9e77992ad31ce54d34b95a6db6f61.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/creatures/5902.png b/art/exported/creatures/5902.png
new file mode 100644
index 0000000..809057d
Binary files /dev/null and b/art/exported/creatures/5902.png differ
diff --git a/art/exported/creatures/5902.png.import b/art/exported/creatures/5902.png.import
new file mode 100644
index 0000000..2c8a5fb
--- /dev/null
+++ b/art/exported/creatures/5902.png.import
@@ -0,0 +1,42 @@
+[remap]
+
+importer="texture"
+type="CompressedTexture2D"
+uid="uid://0k4qnt7wusam"
+path.s3tc="res://.godot/imported/5902.png-e3a13a1e827488fa86f34c2aad883127.s3tc.ctex"
+path.etc2="res://.godot/imported/5902.png-e3a13a1e827488fa86f34c2aad883127.etc2.ctex"
+metadata={
+"imported_formats": ["s3tc_bptc", "etc2_astc"],
+"vram_texture": true
+}
+
+[deps]
+
+source_file="res://art/exported/creatures/5902.png"
+dest_files=["res://.godot/imported/5902.png-e3a13a1e827488fa86f34c2aad883127.s3tc.ctex", "res://.godot/imported/5902.png-e3a13a1e827488fa86f34c2aad883127.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/creatures/5903.png b/art/exported/creatures/5903.png
new file mode 100644
index 0000000..74a8af6
Binary files /dev/null and b/art/exported/creatures/5903.png differ
diff --git a/art/exported/creatures/5903.png.import b/art/exported/creatures/5903.png.import
new file mode 100644
index 0000000..22946e8
--- /dev/null
+++ b/art/exported/creatures/5903.png.import
@@ -0,0 +1,42 @@
+[remap]
+
+importer="texture"
+type="CompressedTexture2D"
+uid="uid://bjb8gqbta2oxw"
+path.s3tc="res://.godot/imported/5903.png-44743c2e322f48eefaeedcd3defec27d.s3tc.ctex"
+path.etc2="res://.godot/imported/5903.png-44743c2e322f48eefaeedcd3defec27d.etc2.ctex"
+metadata={
+"imported_formats": ["s3tc_bptc", "etc2_astc"],
+"vram_texture": true
+}
+
+[deps]
+
+source_file="res://art/exported/creatures/5903.png"
+dest_files=["res://.godot/imported/5903.png-44743c2e322f48eefaeedcd3defec27d.s3tc.ctex", "res://.godot/imported/5903.png-44743c2e322f48eefaeedcd3defec27d.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/creatures/5904.png b/art/exported/creatures/5904.png
new file mode 100644
index 0000000..924ba4b
Binary files /dev/null and b/art/exported/creatures/5904.png differ
diff --git a/art/exported/creatures/5904.png.import b/art/exported/creatures/5904.png.import
new file mode 100644
index 0000000..23458ae
--- /dev/null
+++ b/art/exported/creatures/5904.png.import
@@ -0,0 +1,42 @@
+[remap]
+
+importer="texture"
+type="CompressedTexture2D"
+uid="uid://dtapwjgdbcgr7"
+path.s3tc="res://.godot/imported/5904.png-e43ba3245eadac7081a6a23e64ccc940.s3tc.ctex"
+path.etc2="res://.godot/imported/5904.png-e43ba3245eadac7081a6a23e64ccc940.etc2.ctex"
+metadata={
+"imported_formats": ["s3tc_bptc", "etc2_astc"],
+"vram_texture": true
+}
+
+[deps]
+
+source_file="res://art/exported/creatures/5904.png"
+dest_files=["res://.godot/imported/5904.png-e43ba3245eadac7081a6a23e64ccc940.s3tc.ctex", "res://.godot/imported/5904.png-e43ba3245eadac7081a6a23e64ccc940.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/creatures/6001.png b/art/exported/creatures/6001.png
new file mode 100644
index 0000000..9219597
Binary files /dev/null and b/art/exported/creatures/6001.png differ
diff --git a/art/exported/creatures/6001.png.import b/art/exported/creatures/6001.png.import
new file mode 100644
index 0000000..a63cf85
--- /dev/null
+++ b/art/exported/creatures/6001.png.import
@@ -0,0 +1,42 @@
+[remap]
+
+importer="texture"
+type="CompressedTexture2D"
+uid="uid://c0164i40101lq"
+path.s3tc="res://.godot/imported/6001.png-6fd8c3ef0885c466baece687cd24d38d.s3tc.ctex"
+path.etc2="res://.godot/imported/6001.png-6fd8c3ef0885c466baece687cd24d38d.etc2.ctex"
+metadata={
+"imported_formats": ["s3tc_bptc", "etc2_astc"],
+"vram_texture": true
+}
+
+[deps]
+
+source_file="res://art/exported/creatures/6001.png"
+dest_files=["res://.godot/imported/6001.png-6fd8c3ef0885c466baece687cd24d38d.s3tc.ctex", "res://.godot/imported/6001.png-6fd8c3ef0885c466baece687cd24d38d.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/creatures/6103.png b/art/exported/creatures/6103.png
new file mode 100644
index 0000000..f45e607
Binary files /dev/null and b/art/exported/creatures/6103.png differ
diff --git a/art/exported/creatures/6103.png.import b/art/exported/creatures/6103.png.import
new file mode 100644
index 0000000..ac97dac
--- /dev/null
+++ b/art/exported/creatures/6103.png.import
@@ -0,0 +1,42 @@
+[remap]
+
+importer="texture"
+type="CompressedTexture2D"
+uid="uid://k3ser2dy6wj7"
+path.s3tc="res://.godot/imported/6103.png-5fc95bf908f801d5397114bde5081f55.s3tc.ctex"
+path.etc2="res://.godot/imported/6103.png-5fc95bf908f801d5397114bde5081f55.etc2.ctex"
+metadata={
+"imported_formats": ["s3tc_bptc", "etc2_astc"],
+"vram_texture": true
+}
+
+[deps]
+
+source_file="res://art/exported/creatures/6103.png"
+dest_files=["res://.godot/imported/6103.png-5fc95bf908f801d5397114bde5081f55.s3tc.ctex", "res://.godot/imported/6103.png-5fc95bf908f801d5397114bde5081f55.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/creatures/6406.png b/art/exported/creatures/6406.png
new file mode 100644
index 0000000..15c3b83
Binary files /dev/null and b/art/exported/creatures/6406.png differ
diff --git a/art/exported/creatures/6406.png.import b/art/exported/creatures/6406.png.import
new file mode 100644
index 0000000..ac060ee
--- /dev/null
+++ b/art/exported/creatures/6406.png.import
@@ -0,0 +1,42 @@
+[remap]
+
+importer="texture"
+type="CompressedTexture2D"
+uid="uid://gnlehenln3kp"
+path.s3tc="res://.godot/imported/6406.png-67ebebdd8d13cd88e0a209b3ed00024c.s3tc.ctex"
+path.etc2="res://.godot/imported/6406.png-67ebebdd8d13cd88e0a209b3ed00024c.etc2.ctex"
+metadata={
+"imported_formats": ["s3tc_bptc", "etc2_astc"],
+"vram_texture": true
+}
+
+[deps]
+
+source_file="res://art/exported/creatures/6406.png"
+dest_files=["res://.godot/imported/6406.png-67ebebdd8d13cd88e0a209b3ed00024c.s3tc.ctex", "res://.godot/imported/6406.png-67ebebdd8d13cd88e0a209b3ed00024c.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/creatures/6601.png b/art/exported/creatures/6601.png
new file mode 100644
index 0000000..93c05c0
Binary files /dev/null and b/art/exported/creatures/6601.png differ
diff --git a/art/exported/creatures/6601.png.import b/art/exported/creatures/6601.png.import
new file mode 100644
index 0000000..f5facf7
--- /dev/null
+++ b/art/exported/creatures/6601.png.import
@@ -0,0 +1,42 @@
+[remap]
+
+importer="texture"
+type="CompressedTexture2D"
+uid="uid://jauudro8rgay"
+path.s3tc="res://.godot/imported/6601.png-7d5f9459e449f3aa7ae72a9e069e6a21.s3tc.ctex"
+path.etc2="res://.godot/imported/6601.png-7d5f9459e449f3aa7ae72a9e069e6a21.etc2.ctex"
+metadata={
+"imported_formats": ["s3tc_bptc", "etc2_astc"],
+"vram_texture": true
+}
+
+[deps]
+
+source_file="res://art/exported/creatures/6601.png"
+dest_files=["res://.godot/imported/6601.png-7d5f9459e449f3aa7ae72a9e069e6a21.s3tc.ctex", "res://.godot/imported/6601.png-7d5f9459e449f3aa7ae72a9e069e6a21.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/creatures/6801.png b/art/exported/creatures/6801.png
new file mode 100644
index 0000000..bcdf70b
Binary files /dev/null and b/art/exported/creatures/6801.png differ
diff --git a/art/exported/creatures/6801.png.import b/art/exported/creatures/6801.png.import
new file mode 100644
index 0000000..c257a50
--- /dev/null
+++ b/art/exported/creatures/6801.png.import
@@ -0,0 +1,42 @@
+[remap]
+
+importer="texture"
+type="CompressedTexture2D"
+uid="uid://cuik8y4tqqetk"
+path.s3tc="res://.godot/imported/6801.png-f771f612f1db1d1b99cb446dfebc4992.s3tc.ctex"
+path.etc2="res://.godot/imported/6801.png-f771f612f1db1d1b99cb446dfebc4992.etc2.ctex"
+metadata={
+"imported_formats": ["s3tc_bptc", "etc2_astc"],
+"vram_texture": true
+}
+
+[deps]
+
+source_file="res://art/exported/creatures/6801.png"
+dest_files=["res://.godot/imported/6801.png-f771f612f1db1d1b99cb446dfebc4992.s3tc.ctex", "res://.godot/imported/6801.png-f771f612f1db1d1b99cb446dfebc4992.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/creatures/6804.png b/art/exported/creatures/6804.png
new file mode 100644
index 0000000..14cc738
Binary files /dev/null and b/art/exported/creatures/6804.png differ
diff --git a/art/exported/creatures/6804.png.import b/art/exported/creatures/6804.png.import
new file mode 100644
index 0000000..12be7af
--- /dev/null
+++ b/art/exported/creatures/6804.png.import
@@ -0,0 +1,42 @@
+[remap]
+
+importer="texture"
+type="CompressedTexture2D"
+uid="uid://b5ejex5wtfoun"
+path.s3tc="res://.godot/imported/6804.png-28bcc10060d656f6d7f080ea81e2d1d3.s3tc.ctex"
+path.etc2="res://.godot/imported/6804.png-28bcc10060d656f6d7f080ea81e2d1d3.etc2.ctex"
+metadata={
+"imported_formats": ["s3tc_bptc", "etc2_astc"],
+"vram_texture": true
+}
+
+[deps]
+
+source_file="res://art/exported/creatures/6804.png"
+dest_files=["res://.godot/imported/6804.png-28bcc10060d656f6d7f080ea81e2d1d3.s3tc.ctex", "res://.godot/imported/6804.png-28bcc10060d656f6d7f080ea81e2d1d3.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/creatures/6807.png b/art/exported/creatures/6807.png
new file mode 100644
index 0000000..270004f
Binary files /dev/null and b/art/exported/creatures/6807.png differ
diff --git a/art/exported/creatures/6807.png.import b/art/exported/creatures/6807.png.import
new file mode 100644
index 0000000..026d3d9
--- /dev/null
+++ b/art/exported/creatures/6807.png.import
@@ -0,0 +1,42 @@
+[remap]
+
+importer="texture"
+type="CompressedTexture2D"
+uid="uid://cu5s2rxrc0dh5"
+path.s3tc="res://.godot/imported/6807.png-35d499aa8b3f10cc974cc7df8b70f89e.s3tc.ctex"
+path.etc2="res://.godot/imported/6807.png-35d499aa8b3f10cc974cc7df8b70f89e.etc2.ctex"
+metadata={
+"imported_formats": ["s3tc_bptc", "etc2_astc"],
+"vram_texture": true
+}
+
+[deps]
+
+source_file="res://art/exported/creatures/6807.png"
+dest_files=["res://.godot/imported/6807.png-35d499aa8b3f10cc974cc7df8b70f89e.s3tc.ctex", "res://.godot/imported/6807.png-35d499aa8b3f10cc974cc7df8b70f89e.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/creatures/6901.png b/art/exported/creatures/6901.png
new file mode 100644
index 0000000..0c34e37
Binary files /dev/null and b/art/exported/creatures/6901.png differ
diff --git a/art/exported/creatures/6901.png.import b/art/exported/creatures/6901.png.import
new file mode 100644
index 0000000..651f116
--- /dev/null
+++ b/art/exported/creatures/6901.png.import
@@ -0,0 +1,42 @@
+[remap]
+
+importer="texture"
+type="CompressedTexture2D"
+uid="uid://umjy47x208xb"
+path.s3tc="res://.godot/imported/6901.png-f9ff2d1fc8e00eabd73822b5031f91d8.s3tc.ctex"
+path.etc2="res://.godot/imported/6901.png-f9ff2d1fc8e00eabd73822b5031f91d8.etc2.ctex"
+metadata={
+"imported_formats": ["s3tc_bptc", "etc2_astc"],
+"vram_texture": true
+}
+
+[deps]
+
+source_file="res://art/exported/creatures/6901.png"
+dest_files=["res://.godot/imported/6901.png-f9ff2d1fc8e00eabd73822b5031f91d8.s3tc.ctex", "res://.godot/imported/6901.png-f9ff2d1fc8e00eabd73822b5031f91d8.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/creatures/6902.png b/art/exported/creatures/6902.png
new file mode 100644
index 0000000..3d4517e
Binary files /dev/null and b/art/exported/creatures/6902.png differ
diff --git a/art/exported/creatures/6902.png.import b/art/exported/creatures/6902.png.import
new file mode 100644
index 0000000..35f84df
--- /dev/null
+++ b/art/exported/creatures/6902.png.import
@@ -0,0 +1,42 @@
+[remap]
+
+importer="texture"
+type="CompressedTexture2D"
+uid="uid://csdl6u3uvft45"
+path.s3tc="res://.godot/imported/6902.png-f8899611759244b42c6c786ff118dcba.s3tc.ctex"
+path.etc2="res://.godot/imported/6902.png-f8899611759244b42c6c786ff118dcba.etc2.ctex"
+metadata={
+"imported_formats": ["s3tc_bptc", "etc2_astc"],
+"vram_texture": true
+}
+
+[deps]
+
+source_file="res://art/exported/creatures/6902.png"
+dest_files=["res://.godot/imported/6902.png-f8899611759244b42c6c786ff118dcba.s3tc.ctex", "res://.godot/imported/6902.png-f8899611759244b42c6c786ff118dcba.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/creatures/6904.png b/art/exported/creatures/6904.png
new file mode 100644
index 0000000..f2e8a7b
Binary files /dev/null and b/art/exported/creatures/6904.png differ
diff --git a/art/exported/creatures/6904.png.import b/art/exported/creatures/6904.png.import
new file mode 100644
index 0000000..75051e3
--- /dev/null
+++ b/art/exported/creatures/6904.png.import
@@ -0,0 +1,42 @@
+[remap]
+
+importer="texture"
+type="CompressedTexture2D"
+uid="uid://bqh7d4v8jrbpr"
+path.s3tc="res://.godot/imported/6904.png-38739e4e48fbb83e3f65074a93d681bb.s3tc.ctex"
+path.etc2="res://.godot/imported/6904.png-38739e4e48fbb83e3f65074a93d681bb.etc2.ctex"
+metadata={
+"imported_formats": ["s3tc_bptc", "etc2_astc"],
+"vram_texture": true
+}
+
+[deps]
+
+source_file="res://art/exported/creatures/6904.png"
+dest_files=["res://.godot/imported/6904.png-38739e4e48fbb83e3f65074a93d681bb.s3tc.ctex", "res://.godot/imported/6904.png-38739e4e48fbb83e3f65074a93d681bb.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/creatures/6905.png b/art/exported/creatures/6905.png
new file mode 100644
index 0000000..296848e
Binary files /dev/null and b/art/exported/creatures/6905.png differ
diff --git a/art/exported/creatures/6905.png.import b/art/exported/creatures/6905.png.import
new file mode 100644
index 0000000..f267dc2
--- /dev/null
+++ b/art/exported/creatures/6905.png.import
@@ -0,0 +1,42 @@
+[remap]
+
+importer="texture"
+type="CompressedTexture2D"
+uid="uid://c4d8x6qyhahvn"
+path.s3tc="res://.godot/imported/6905.png-a52c1647421059618698e17d7a3f8f98.s3tc.ctex"
+path.etc2="res://.godot/imported/6905.png-a52c1647421059618698e17d7a3f8f98.etc2.ctex"
+metadata={
+"imported_formats": ["s3tc_bptc", "etc2_astc"],
+"vram_texture": true
+}
+
+[deps]
+
+source_file="res://art/exported/creatures/6905.png"
+dest_files=["res://.godot/imported/6905.png-a52c1647421059618698e17d7a3f8f98.s3tc.ctex", "res://.godot/imported/6905.png-a52c1647421059618698e17d7a3f8f98.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/creatures/701.png b/art/exported/creatures/701.png
new file mode 100644
index 0000000..4f7d8f8
Binary files /dev/null and b/art/exported/creatures/701.png differ
diff --git a/art/exported/creatures/701.png.import b/art/exported/creatures/701.png.import
new file mode 100644
index 0000000..7598ee8
--- /dev/null
+++ b/art/exported/creatures/701.png.import
@@ -0,0 +1,42 @@
+[remap]
+
+importer="texture"
+type="CompressedTexture2D"
+uid="uid://jw5xt0cfjn1d"
+path.s3tc="res://.godot/imported/701.png-8a66c28a9f49fa465ce3b5342fa08dbc.s3tc.ctex"
+path.etc2="res://.godot/imported/701.png-8a66c28a9f49fa465ce3b5342fa08dbc.etc2.ctex"
+metadata={
+"imported_formats": ["s3tc_bptc", "etc2_astc"],
+"vram_texture": true
+}
+
+[deps]
+
+source_file="res://art/exported/creatures/701.png"
+dest_files=["res://.godot/imported/701.png-8a66c28a9f49fa465ce3b5342fa08dbc.s3tc.ctex", "res://.godot/imported/701.png-8a66c28a9f49fa465ce3b5342fa08dbc.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/creatures/704.png b/art/exported/creatures/704.png
new file mode 100644
index 0000000..526d090
Binary files /dev/null and b/art/exported/creatures/704.png differ
diff --git a/art/exported/creatures/704.png.import b/art/exported/creatures/704.png.import
new file mode 100644
index 0000000..e80e19e
--- /dev/null
+++ b/art/exported/creatures/704.png.import
@@ -0,0 +1,42 @@
+[remap]
+
+importer="texture"
+type="CompressedTexture2D"
+uid="uid://bonpxkp6uwxq6"
+path.s3tc="res://.godot/imported/704.png-649669246217a14c9609a7e929841827.s3tc.ctex"
+path.etc2="res://.godot/imported/704.png-649669246217a14c9609a7e929841827.etc2.ctex"
+metadata={
+"imported_formats": ["s3tc_bptc", "etc2_astc"],
+"vram_texture": true
+}
+
+[deps]
+
+source_file="res://art/exported/creatures/704.png"
+dest_files=["res://.godot/imported/704.png-649669246217a14c9609a7e929841827.s3tc.ctex", "res://.godot/imported/704.png-649669246217a14c9609a7e929841827.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/creatures/7101.png b/art/exported/creatures/7101.png
new file mode 100644
index 0000000..93a13af
Binary files /dev/null and b/art/exported/creatures/7101.png differ
diff --git a/art/exported/creatures/7101.png.import b/art/exported/creatures/7101.png.import
new file mode 100644
index 0000000..2d312b3
--- /dev/null
+++ b/art/exported/creatures/7101.png.import
@@ -0,0 +1,42 @@
+[remap]
+
+importer="texture"
+type="CompressedTexture2D"
+uid="uid://dd5lpjei8ix4d"
+path.s3tc="res://.godot/imported/7101.png-9be787a05b48075222e923e556de87ac.s3tc.ctex"
+path.etc2="res://.godot/imported/7101.png-9be787a05b48075222e923e556de87ac.etc2.ctex"
+metadata={
+"imported_formats": ["s3tc_bptc", "etc2_astc"],
+"vram_texture": true
+}
+
+[deps]
+
+source_file="res://art/exported/creatures/7101.png"
+dest_files=["res://.godot/imported/7101.png-9be787a05b48075222e923e556de87ac.s3tc.ctex", "res://.godot/imported/7101.png-9be787a05b48075222e923e556de87ac.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/creatures/7403.png b/art/exported/creatures/7403.png
new file mode 100644
index 0000000..ad73c17
Binary files /dev/null and b/art/exported/creatures/7403.png differ
diff --git a/art/exported/creatures/7403.png.import b/art/exported/creatures/7403.png.import
new file mode 100644
index 0000000..bef32c4
--- /dev/null
+++ b/art/exported/creatures/7403.png.import
@@ -0,0 +1,42 @@
+[remap]
+
+importer="texture"
+type="CompressedTexture2D"
+uid="uid://bl7ru6gm5xnpb"
+path.s3tc="res://.godot/imported/7403.png-54b51089a6ff5c2091372d659ba4bdc7.s3tc.ctex"
+path.etc2="res://.godot/imported/7403.png-54b51089a6ff5c2091372d659ba4bdc7.etc2.ctex"
+metadata={
+"imported_formats": ["s3tc_bptc", "etc2_astc"],
+"vram_texture": true
+}
+
+[deps]
+
+source_file="res://art/exported/creatures/7403.png"
+dest_files=["res://.godot/imported/7403.png-54b51089a6ff5c2091372d659ba4bdc7.s3tc.ctex", "res://.godot/imported/7403.png-54b51089a6ff5c2091372d659ba4bdc7.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/creatures/7506.png b/art/exported/creatures/7506.png
new file mode 100644
index 0000000..7e8c5b2
Binary files /dev/null and b/art/exported/creatures/7506.png differ
diff --git a/art/exported/creatures/7506.png.import b/art/exported/creatures/7506.png.import
new file mode 100644
index 0000000..c2c99a0
--- /dev/null
+++ b/art/exported/creatures/7506.png.import
@@ -0,0 +1,42 @@
+[remap]
+
+importer="texture"
+type="CompressedTexture2D"
+uid="uid://c6qm3sujp8xvg"
+path.s3tc="res://.godot/imported/7506.png-5c0f871c623e61a890015626ea315de9.s3tc.ctex"
+path.etc2="res://.godot/imported/7506.png-5c0f871c623e61a890015626ea315de9.etc2.ctex"
+metadata={
+"imported_formats": ["s3tc_bptc", "etc2_astc"],
+"vram_texture": true
+}
+
+[deps]
+
+source_file="res://art/exported/creatures/7506.png"
+dest_files=["res://.godot/imported/7506.png-5c0f871c623e61a890015626ea315de9.s3tc.ctex", "res://.godot/imported/7506.png-5c0f871c623e61a890015626ea315de9.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/creatures/7508.png b/art/exported/creatures/7508.png
new file mode 100644
index 0000000..27f1a59
Binary files /dev/null and b/art/exported/creatures/7508.png differ
diff --git a/art/exported/creatures/7508.png.import b/art/exported/creatures/7508.png.import
new file mode 100644
index 0000000..a14d42f
--- /dev/null
+++ b/art/exported/creatures/7508.png.import
@@ -0,0 +1,42 @@
+[remap]
+
+importer="texture"
+type="CompressedTexture2D"
+uid="uid://bnw0dw14iyxbh"
+path.s3tc="res://.godot/imported/7508.png-555eff33a2cb66622ccca9d7f063522e.s3tc.ctex"
+path.etc2="res://.godot/imported/7508.png-555eff33a2cb66622ccca9d7f063522e.etc2.ctex"
+metadata={
+"imported_formats": ["s3tc_bptc", "etc2_astc"],
+"vram_texture": true
+}
+
+[deps]
+
+source_file="res://art/exported/creatures/7508.png"
+dest_files=["res://.godot/imported/7508.png-555eff33a2cb66622ccca9d7f063522e.s3tc.ctex", "res://.godot/imported/7508.png-555eff33a2cb66622ccca9d7f063522e.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/creatures/801.png b/art/exported/creatures/801.png
new file mode 100644
index 0000000..f2e6390
Binary files /dev/null and b/art/exported/creatures/801.png differ
diff --git a/art/exported/creatures/801.png.import b/art/exported/creatures/801.png.import
new file mode 100644
index 0000000..3721440
--- /dev/null
+++ b/art/exported/creatures/801.png.import
@@ -0,0 +1,42 @@
+[remap]
+
+importer="texture"
+type="CompressedTexture2D"
+uid="uid://dkocdkoyy2udh"
+path.s3tc="res://.godot/imported/801.png-b0324ef8c10934464b8be69cb9eca981.s3tc.ctex"
+path.etc2="res://.godot/imported/801.png-b0324ef8c10934464b8be69cb9eca981.etc2.ctex"
+metadata={
+"imported_formats": ["s3tc_bptc", "etc2_astc"],
+"vram_texture": true
+}
+
+[deps]
+
+source_file="res://art/exported/creatures/801.png"
+dest_files=["res://.godot/imported/801.png-b0324ef8c10934464b8be69cb9eca981.s3tc.ctex", "res://.godot/imported/801.png-b0324ef8c10934464b8be69cb9eca981.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/creatures/802.png b/art/exported/creatures/802.png
new file mode 100644
index 0000000..f786031
Binary files /dev/null and b/art/exported/creatures/802.png differ
diff --git a/art/exported/creatures/802.png.import b/art/exported/creatures/802.png.import
new file mode 100644
index 0000000..f7e4cc6
--- /dev/null
+++ b/art/exported/creatures/802.png.import
@@ -0,0 +1,42 @@
+[remap]
+
+importer="texture"
+type="CompressedTexture2D"
+uid="uid://b6pkx1koopl7p"
+path.s3tc="res://.godot/imported/802.png-1e79f95b56c444a9c7183dada8a14dc3.s3tc.ctex"
+path.etc2="res://.godot/imported/802.png-1e79f95b56c444a9c7183dada8a14dc3.etc2.ctex"
+metadata={
+"imported_formats": ["s3tc_bptc", "etc2_astc"],
+"vram_texture": true
+}
+
+[deps]
+
+source_file="res://art/exported/creatures/802.png"
+dest_files=["res://.godot/imported/802.png-1e79f95b56c444a9c7183dada8a14dc3.s3tc.ctex", "res://.godot/imported/802.png-1e79f95b56c444a9c7183dada8a14dc3.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/creatures/805.png b/art/exported/creatures/805.png
new file mode 100644
index 0000000..727793c
Binary files /dev/null and b/art/exported/creatures/805.png differ
diff --git a/art/exported/creatures/805.png.import b/art/exported/creatures/805.png.import
new file mode 100644
index 0000000..db359af
--- /dev/null
+++ b/art/exported/creatures/805.png.import
@@ -0,0 +1,42 @@
+[remap]
+
+importer="texture"
+type="CompressedTexture2D"
+uid="uid://cyo0ihvopej4"
+path.s3tc="res://.godot/imported/805.png-f508211dd3f15d8b9090675eef9939f7.s3tc.ctex"
+path.etc2="res://.godot/imported/805.png-f508211dd3f15d8b9090675eef9939f7.etc2.ctex"
+metadata={
+"imported_formats": ["s3tc_bptc", "etc2_astc"],
+"vram_texture": true
+}
+
+[deps]
+
+source_file="res://art/exported/creatures/805.png"
+dest_files=["res://.godot/imported/805.png-f508211dd3f15d8b9090675eef9939f7.s3tc.ctex", "res://.godot/imported/805.png-f508211dd3f15d8b9090675eef9939f7.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/creatures/806.png b/art/exported/creatures/806.png
new file mode 100644
index 0000000..a8acdb1
Binary files /dev/null and b/art/exported/creatures/806.png differ
diff --git a/art/exported/creatures/806.png.import b/art/exported/creatures/806.png.import
new file mode 100644
index 0000000..8078cb6
--- /dev/null
+++ b/art/exported/creatures/806.png.import
@@ -0,0 +1,42 @@
+[remap]
+
+importer="texture"
+type="CompressedTexture2D"
+uid="uid://c31dw2wqjat40"
+path.s3tc="res://.godot/imported/806.png-640099ab90a2d2daf3c4e7229634452c.s3tc.ctex"
+path.etc2="res://.godot/imported/806.png-640099ab90a2d2daf3c4e7229634452c.etc2.ctex"
+metadata={
+"imported_formats": ["s3tc_bptc", "etc2_astc"],
+"vram_texture": true
+}
+
+[deps]
+
+source_file="res://art/exported/creatures/806.png"
+dest_files=["res://.godot/imported/806.png-640099ab90a2d2daf3c4e7229634452c.s3tc.ctex", "res://.godot/imported/806.png-640099ab90a2d2daf3c4e7229634452c.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/creatures/808.png b/art/exported/creatures/808.png
new file mode 100644
index 0000000..e218b8e
Binary files /dev/null and b/art/exported/creatures/808.png differ
diff --git a/art/exported/creatures/808.png.import b/art/exported/creatures/808.png.import
new file mode 100644
index 0000000..1126876
--- /dev/null
+++ b/art/exported/creatures/808.png.import
@@ -0,0 +1,42 @@
+[remap]
+
+importer="texture"
+type="CompressedTexture2D"
+uid="uid://x2d4nglo0rlk"
+path.s3tc="res://.godot/imported/808.png-bc683347c2e3b627379b7eb047e12a89.s3tc.ctex"
+path.etc2="res://.godot/imported/808.png-bc683347c2e3b627379b7eb047e12a89.etc2.ctex"
+metadata={
+"imported_formats": ["s3tc_bptc", "etc2_astc"],
+"vram_texture": true
+}
+
+[deps]
+
+source_file="res://art/exported/creatures/808.png"
+dest_files=["res://.godot/imported/808.png-bc683347c2e3b627379b7eb047e12a89.s3tc.ctex", "res://.godot/imported/808.png-bc683347c2e3b627379b7eb047e12a89.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/creatures/fish_placeholder.png b/art/exported/creatures/fish_placeholder.png
new file mode 100644
index 0000000..de69afb
Binary files /dev/null and b/art/exported/creatures/fish_placeholder.png differ
diff --git a/art/exported/creatures/fish_placeholder.png.import b/art/exported/creatures/fish_placeholder.png.import
new file mode 100644
index 0000000..87111f4
--- /dev/null
+++ b/art/exported/creatures/fish_placeholder.png.import
@@ -0,0 +1,42 @@
+[remap]
+
+importer="texture"
+type="CompressedTexture2D"
+uid="uid://d08ce7q8vmlfs"
+path.s3tc="res://.godot/imported/fish_placeholder.png-41ecfc07b87417338ce888ac1e2c2362.s3tc.ctex"
+path.etc2="res://.godot/imported/fish_placeholder.png-41ecfc07b87417338ce888ac1e2c2362.etc2.ctex"
+metadata={
+"imported_formats": ["s3tc_bptc", "etc2_astc"],
+"vram_texture": true
+}
+
+[deps]
+
+source_file="res://art/exported/creatures/fish_placeholder.png"
+dest_files=["res://.godot/imported/fish_placeholder.png-41ecfc07b87417338ce888ac1e2c2362.s3tc.ctex", "res://.godot/imported/fish_placeholder.png-41ecfc07b87417338ce888ac1e2c2362.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/environment/terrain/starter_island.glb b/art/exported/environment/terrain/starter_island.glb
index 09210a7..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/items/equipment/net.glb b/art/exported/items/equipment/net.glb
new file mode 100644
index 0000000..db4fa34
Binary files /dev/null and b/art/exported/items/equipment/net.glb differ
diff --git a/art/exported/items/equipment/net.glb.import b/art/exported/items/equipment/net.glb.import
new file mode 100644
index 0000000..28667d6
--- /dev/null
+++ b/art/exported/items/equipment/net.glb.import
@@ -0,0 +1,45 @@
+[remap]
+
+importer="scene"
+importer_version=1
+type="PackedScene"
+uid="uid://dsa6yss1sxfcy"
+path="res://.godot/imported/net.glb-cbbc53905aec6a50c4a3322ca7a93b1e.scn"
+
+[deps]
+
+source_file="res://art/exported/items/equipment/net.glb"
+dest_files=["res://.godot/imported/net.glb-cbbc53905aec6a50c4a3322ca7a93b1e.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/exported/items/equipment/net_Material.png b/art/exported/items/equipment/net_Material.png
new file mode 100644
index 0000000..bd5eff2
Binary files /dev/null and b/art/exported/items/equipment/net_Material.png differ
diff --git a/art/exported/items/equipment/net_Material.png.import b/art/exported/items/equipment/net_Material.png.import
new file mode 100644
index 0000000..0723973
--- /dev/null
+++ b/art/exported/items/equipment/net_Material.png.import
@@ -0,0 +1,45 @@
+[remap]
+
+importer="texture"
+type="CompressedTexture2D"
+uid="uid://binoqhg45vgbs"
+path.s3tc="res://.godot/imported/net_Material.png-f4c4b0400561bc2751fe719b6b1a1e1a.s3tc.ctex"
+path.etc2="res://.godot/imported/net_Material.png-f4c4b0400561bc2751fe719b6b1a1e1a.etc2.ctex"
+metadata={
+"imported_formats": ["s3tc_bptc", "etc2_astc"],
+"vram_texture": true
+}
+generator_parameters={
+"md5": "ef2c2ae657c9460c90eb808ba5217f39"
+}
+
+[deps]
+
+source_file="res://art/exported/items/equipment/net_Material.png"
+dest_files=["res://.godot/imported/net_Material.png-f4c4b0400561bc2751fe719b6b1a1e1a.s3tc.ctex", "res://.godot/imported/net_Material.png-f4c4b0400561bc2751fe719b6b1a1e1a.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/items/equipment/net_Plane Base Color.png b/art/exported/items/equipment/net_Plane Base Color.png
new file mode 100644
index 0000000..795d0a3
Binary files /dev/null and b/art/exported/items/equipment/net_Plane Base Color.png differ
diff --git a/art/exported/items/equipment/net_Plane Base Color.png.import b/art/exported/items/equipment/net_Plane Base Color.png.import
new file mode 100644
index 0000000..61077be
--- /dev/null
+++ b/art/exported/items/equipment/net_Plane Base Color.png.import
@@ -0,0 +1,45 @@
+[remap]
+
+importer="texture"
+type="CompressedTexture2D"
+uid="uid://cdsabucjy5rpk"
+path.s3tc="res://.godot/imported/net_Plane Base Color.png-3875ae3dd186ddf319c55aabb13e663f.s3tc.ctex"
+path.etc2="res://.godot/imported/net_Plane Base Color.png-3875ae3dd186ddf319c55aabb13e663f.etc2.ctex"
+metadata={
+"imported_formats": ["s3tc_bptc", "etc2_astc"],
+"vram_texture": true
+}
+generator_parameters={
+"md5": "a6001c381d4c8e16bd2d64ea74ccff82"
+}
+
+[deps]
+
+source_file="res://art/exported/items/equipment/net_Plane Base Color.png"
+dest_files=["res://.godot/imported/net_Plane Base Color.png-3875ae3dd186ddf319c55aabb13e663f.s3tc.ctex", "res://.godot/imported/net_Plane Base Color.png-3875ae3dd186ddf319c55aabb13e663f.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/system_icons/netfishing.ico b/art/exported/system_icons/netfishing.ico
deleted file mode 100644
index dc5bdae..0000000
Binary files a/art/exported/system_icons/netfishing.ico and /dev/null differ
diff --git a/art/exported/system_icons/netfishing_1024.png b/art/exported/system_icons/netfishing_1024.png
deleted file mode 100644
index a433e80..0000000
Binary files a/art/exported/system_icons/netfishing_1024.png and /dev/null differ
diff --git a/art/exported/system_icons/netfishing_256.png b/art/exported/system_icons/netfishing_256.png
deleted file mode 100644
index 1e7be72..0000000
Binary files a/art/exported/system_icons/netfishing_256.png and /dev/null differ
diff --git a/art/exported/system_icons/straywild.ico b/art/exported/system_icons/straywild.ico
new file mode 100644
index 0000000..c03de8e
Binary files /dev/null and b/art/exported/system_icons/straywild.ico differ
diff --git a/art/exported/system_icons/straywild_1024.png b/art/exported/system_icons/straywild_1024.png
new file mode 100644
index 0000000..2a3b204
Binary files /dev/null and b/art/exported/system_icons/straywild_1024.png differ
diff --git a/art/exported/system_icons/netfishing_1024.png.import b/art/exported/system_icons/straywild_1024.png.import
similarity index 74%
rename from art/exported/system_icons/netfishing_1024.png.import
rename to art/exported/system_icons/straywild_1024.png.import
index 08aa925..dea156a 100644
--- a/art/exported/system_icons/netfishing_1024.png.import
+++ b/art/exported/system_icons/straywild_1024.png.import
@@ -3,15 +3,15 @@
importer="texture"
type="CompressedTexture2D"
uid="uid://bkh5hw8wwu8pc"
-path="res://.godot/imported/netfishing_1024.png-7c53a099ed2743b4a623c258641b1d29.ctex"
+path="res://.godot/imported/straywild_1024.png-b9b5c159c9fcecc21b04324370d4522f.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"]
+source_file="res://art/exported/system_icons/straywild_1024.png"
+dest_files=["res://.godot/imported/straywild_1024.png-b9b5c159c9fcecc21b04324370d4522f.ctex"]
[params]
diff --git a/art/exported/system_icons/straywild_256.png b/art/exported/system_icons/straywild_256.png
new file mode 100644
index 0000000..98275eb
Binary files /dev/null and b/art/exported/system_icons/straywild_256.png differ
diff --git a/art/exported/system_icons/netfishing_256.png.import b/art/exported/system_icons/straywild_256.png.import
similarity index 74%
rename from art/exported/system_icons/netfishing_256.png.import
rename to art/exported/system_icons/straywild_256.png.import
index 34d5d27..5726347 100644
--- a/art/exported/system_icons/netfishing_256.png.import
+++ b/art/exported/system_icons/straywild_256.png.import
@@ -3,15 +3,15 @@
importer="texture"
type="CompressedTexture2D"
uid="uid://cty16m8ydla06"
-path="res://.godot/imported/netfishing_256.png-d9dfc9c7871ac38bc73d09d00df679be.ctex"
+path="res://.godot/imported/straywild_256.png-87b7e62e710eae4d3e3ed24a8454ca6d.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"]
+source_file="res://art/exported/system_icons/straywild_256.png"
+dest_files=["res://.godot/imported/straywild_256.png-87b7e62e710eae4d3e3ed24a8454ca6d.ctex"]
[params]
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_character_source.blend b/art/source/characters/base/netfishing_character_source.blend
deleted file mode 100644
index 3d513a5..0000000
Binary files a/art/source/characters/base/netfishing_character_source.blend and /dev/null differ
diff --git a/art/source/characters/base/netfishing_base_character.blend b/art/source/characters/base/straywild_base_character.blend
similarity index 100%
rename from art/source/characters/base/netfishing_base_character.blend
rename to art/source/characters/base/straywild_base_character.blend
diff --git a/art/source/characters/base/netfishing_base_character.blend.import b/art/source/characters/base/straywild_base_character.blend.import
similarity index 84%
rename from art/source/characters/base/netfishing_base_character.blend.import
rename to art/source/characters/base/straywild_base_character.blend.import
index f6df489..2cab53c 100644
--- a/art/source/characters/base/netfishing_base_character.blend.import
+++ b/art/source/characters/base/straywild_base_character.blend.import
@@ -4,12 +4,12 @@ importer="scene"
importer_version=1
type="PackedScene"
uid="uid://cb4bcakhoprm8"
-path="res://.godot/imported/netfishing_base_character.blend-65bf3bc5b77cc1a37fa3f81ebd808d37.scn"
+path="res://.godot/imported/straywild_base_character.blend-02abbfdb9b1d393f1bd3cfface615e27.scn"
[deps]
-source_file="res://art/source/characters/base/netfishing_base_character.blend"
-dest_files=["res://.godot/imported/netfishing_base_character.blend-65bf3bc5b77cc1a37fa3f81ebd808d37.scn"]
+source_file="res://art/source/characters/base/straywild_base_character.blend"
+dest_files=["res://.godot/imported/straywild_base_character.blend-02abbfdb9b1d393f1bd3cfface615e27.scn"]
[params]
diff --git a/art/source/characters/base/netfishing_base_character.glb.import b/art/source/characters/base/straywild_base_character.glb.import
similarity index 77%
rename from art/source/characters/base/netfishing_base_character.glb.import
rename to art/source/characters/base/straywild_base_character.glb.import
index 5a38b76..bda93c0 100644
--- a/art/source/characters/base/netfishing_base_character.glb.import
+++ b/art/source/characters/base/straywild_base_character.glb.import
@@ -4,12 +4,12 @@ importer="scene"
importer_version=1
type="PackedScene"
uid="uid://bfhnup14638r7"
-path="res://.godot/imported/netfishing_base_character.glb-cbdc33eaa4fcd1d2d2a8f4a463b228d6.scn"
+path="res://.godot/imported/straywild_base_character.glb-c9650c715a1b5b9ae0d8541191f7d7a9.scn"
[deps]
-source_file="res://art/source/characters/base/netfishing_base_character.glb"
-dest_files=["res://.godot/imported/netfishing_base_character.glb-cbdc33eaa4fcd1d2d2a8f4a463b228d6.scn"]
+source_file="res://art/source/characters/base/straywild_base_character.glb"
+dest_files=["res://.godot/imported/straywild_base_character.glb-c9650c715a1b5b9ae0d8541191f7d7a9.scn"]
[params]
diff --git a/art/source/characters/base/straywild_character_source.blend b/art/source/characters/base/straywild_character_source.blend
new file mode 100644
index 0000000..17a1b4d
Binary files /dev/null and b/art/source/characters/base/straywild_character_source.blend differ
diff --git a/art/source/characters/base/netfishing_character_source.blend.import b/art/source/characters/base/straywild_character_source.blend.import
similarity index 84%
rename from art/source/characters/base/netfishing_character_source.blend.import
rename to art/source/characters/base/straywild_character_source.blend.import
index 8288280..741c389 100644
--- a/art/source/characters/base/netfishing_character_source.blend.import
+++ b/art/source/characters/base/straywild_character_source.blend.import
@@ -4,12 +4,12 @@ importer="scene"
importer_version=1
type="PackedScene"
uid="uid://d38y8blk2w5c5"
-path="res://.godot/imported/netfishing_character_source.blend-1d18c95606937d4c8bcb87e334e02ee5.scn"
+path="res://.godot/imported/straywild_character_source.blend-0e7b8a87008f88e309ad73a63561ef5b.scn"
[deps]
-source_file="res://art/source/characters/base/netfishing_character_source.blend"
-dest_files=["res://.godot/imported/netfishing_character_source.blend-1d18c95606937d4c8bcb87e334e02ee5.scn"]
+source_file="res://art/source/characters/base/straywild_character_source.blend"
+dest_files=["res://.godot/imported/straywild_character_source.blend-0e7b8a87008f88e309ad73a63561ef5b.scn"]
[params]
diff --git a/art/source/characters/base/netfishing_character_source.glb.import b/art/source/characters/base/straywild_character_source.glb.import
similarity index 76%
rename from art/source/characters/base/netfishing_character_source.glb.import
rename to art/source/characters/base/straywild_character_source.glb.import
index 61158c5..bbcee6a 100644
--- a/art/source/characters/base/netfishing_character_source.glb.import
+++ b/art/source/characters/base/straywild_character_source.glb.import
@@ -4,12 +4,12 @@ importer="scene"
importer_version=1
type="PackedScene"
uid="uid://3uq4cxiomttt"
-path="res://.godot/imported/netfishing_character_source.glb-9c98ee44b142c5b3d06f0838b7d2c9c4.scn"
+path="res://.godot/imported/straywild_character_source.glb-3a8ccc55dbe60da1b70b62110b206353.scn"
[deps]
-source_file="res://art/source/characters/base/netfishing_character_source.glb"
-dest_files=["res://.godot/imported/netfishing_character_source.glb-9c98ee44b142c5b3d06f0838b7d2c9c4.scn"]
+source_file="res://art/source/characters/base/straywild_character_source.glb"
+dest_files=["res://.godot/imported/straywild_character_source.glb-3a8ccc55dbe60da1b70b62110b206353.scn"]
[params]
diff --git a/art/source/characters/base/netfishing_character_source_eyes1_eyebrows1.png b/art/source/characters/base/straywild_character_source_eyes1_eyebrows1.png
similarity index 100%
rename from art/source/characters/base/netfishing_character_source_eyes1_eyebrows1.png
rename to art/source/characters/base/straywild_character_source_eyes1_eyebrows1.png
diff --git a/art/source/characters/base/netfishing_character_source_eyes1_eyebrows1.png.import b/art/source/characters/base/straywild_character_source_eyes1_eyebrows1.png.import
similarity index 57%
rename from art/source/characters/base/netfishing_character_source_eyes1_eyebrows1.png.import
rename to art/source/characters/base/straywild_character_source_eyes1_eyebrows1.png.import
index 4946a3c..dba5a5f 100644
--- a/art/source/characters/base/netfishing_character_source_eyes1_eyebrows1.png.import
+++ b/art/source/characters/base/straywild_character_source_eyes1_eyebrows1.png.import
@@ -3,8 +3,8 @@
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"
+path.s3tc="res://.godot/imported/straywild_character_source_eyes1_eyebrows1.png-3c638377e631a94b998336d0ca1d8b8e.s3tc.ctex"
+path.etc2="res://.godot/imported/straywild_character_source_eyes1_eyebrows1.png-3c638377e631a94b998336d0ca1d8b8e.etc2.ctex"
metadata={
"imported_formats": ["s3tc_bptc", "etc2_astc"],
"vram_texture": true
@@ -15,8 +15,8 @@ generator_parameters={
[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"]
+source_file="res://art/source/characters/base/straywild_character_source_eyes1_eyebrows1.png"
+dest_files=["res://.godot/imported/straywild_character_source_eyes1_eyebrows1.png-3c638377e631a94b998336d0ca1d8b8e.s3tc.ctex", "res://.godot/imported/straywild_character_source_eyes1_eyebrows1.png-3c638377e631a94b998336d0ca1d8b8e.etc2.ctex"]
[params]
@@ -28,7 +28,7 @@ compress/rdo_quality_loss=0.0
compress/hdr_compression=1
compress/normal_map=0
compress/channel_pack=0
-mipmaps/generate=true
+mipmaps/generate=false
mipmaps/limit=-1
roughness/mode=0
roughness/src_normal=""
diff --git a/art/source/characters/base/netfishing_character_source_eyes_simple_shine.png b/art/source/characters/base/straywild_character_source_eyes_simple_shine.png
similarity index 100%
rename from art/source/characters/base/netfishing_character_source_eyes_simple_shine.png
rename to art/source/characters/base/straywild_character_source_eyes_simple_shine.png
diff --git a/art/source/characters/base/netfishing_character_source_eyes_simple_shine.png.import b/art/source/characters/base/straywild_character_source_eyes_simple_shine.png.import
similarity index 56%
rename from art/source/characters/base/netfishing_character_source_eyes_simple_shine.png.import
rename to art/source/characters/base/straywild_character_source_eyes_simple_shine.png.import
index e3894c9..8b99f72 100644
--- a/art/source/characters/base/netfishing_character_source_eyes_simple_shine.png.import
+++ b/art/source/characters/base/straywild_character_source_eyes_simple_shine.png.import
@@ -3,8 +3,8 @@
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"
+path.s3tc="res://.godot/imported/straywild_character_source_eyes_simple_shine.png-97500ad873e0da3aaf0e9e3c3198e38a.s3tc.ctex"
+path.etc2="res://.godot/imported/straywild_character_source_eyes_simple_shine.png-97500ad873e0da3aaf0e9e3c3198e38a.etc2.ctex"
metadata={
"imported_formats": ["s3tc_bptc", "etc2_astc"],
"vram_texture": true
@@ -15,8 +15,8 @@ generator_parameters={
[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"]
+source_file="res://art/source/characters/base/straywild_character_source_eyes_simple_shine.png"
+dest_files=["res://.godot/imported/straywild_character_source_eyes_simple_shine.png-97500ad873e0da3aaf0e9e3c3198e38a.s3tc.ctex", "res://.godot/imported/straywild_character_source_eyes_simple_shine.png-97500ad873e0da3aaf0e9e3c3198e38a.etc2.ctex"]
[params]
@@ -28,7 +28,7 @@ compress/rdo_quality_loss=0.0
compress/hdr_compression=1
compress/normal_map=0
compress/channel_pack=0
-mipmaps/generate=true
+mipmaps/generate=false
mipmaps/limit=-1
roughness/mode=0
roughness/src_normal=""
diff --git a/art/source/characters/base/netfishing_character_source_mouth1.png b/art/source/characters/base/straywild_character_source_mouth1.png
similarity index 100%
rename from art/source/characters/base/netfishing_character_source_mouth1.png
rename to art/source/characters/base/straywild_character_source_mouth1.png
diff --git a/art/source/characters/base/netfishing_character_source_mouth1.png.import b/art/source/characters/base/straywild_character_source_mouth1.png.import
similarity index 59%
rename from art/source/characters/base/netfishing_character_source_mouth1.png.import
rename to art/source/characters/base/straywild_character_source_mouth1.png.import
index e5394e7..5dd84f4 100644
--- a/art/source/characters/base/netfishing_character_source_mouth1.png.import
+++ b/art/source/characters/base/straywild_character_source_mouth1.png.import
@@ -3,8 +3,8 @@
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"
+path.s3tc="res://.godot/imported/straywild_character_source_mouth1.png-ff2ed280ca7e84bdd0749ed5b5b16f54.s3tc.ctex"
+path.etc2="res://.godot/imported/straywild_character_source_mouth1.png-ff2ed280ca7e84bdd0749ed5b5b16f54.etc2.ctex"
metadata={
"imported_formats": ["s3tc_bptc", "etc2_astc"],
"vram_texture": true
@@ -15,8 +15,8 @@ generator_parameters={
[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"]
+source_file="res://art/source/characters/base/straywild_character_source_mouth1.png"
+dest_files=["res://.godot/imported/straywild_character_source_mouth1.png-ff2ed280ca7e84bdd0749ed5b5b16f54.s3tc.ctex", "res://.godot/imported/straywild_character_source_mouth1.png-ff2ed280ca7e84bdd0749ed5b5b16f54.etc2.ctex"]
[params]
@@ -28,7 +28,7 @@ compress/rdo_quality_loss=0.0
compress/hdr_compression=1
compress/normal_map=0
compress/channel_pack=0
-mipmaps/generate=true
+mipmaps/generate=false
mipmaps/limit=-1
roughness/mode=0
roughness/src_normal=""
diff --git a/art/source/characters/base/netfishing_character_source_mouth_three.png b/art/source/characters/base/straywild_character_source_mouth_three.png
similarity index 100%
rename from art/source/characters/base/netfishing_character_source_mouth_three.png
rename to art/source/characters/base/straywild_character_source_mouth_three.png
diff --git a/art/source/characters/base/netfishing_character_source_mouth_three.png.import b/art/source/characters/base/straywild_character_source_mouth_three.png.import
similarity index 58%
rename from art/source/characters/base/netfishing_character_source_mouth_three.png.import
rename to art/source/characters/base/straywild_character_source_mouth_three.png.import
index 4445b17..4b2413d 100644
--- a/art/source/characters/base/netfishing_character_source_mouth_three.png.import
+++ b/art/source/characters/base/straywild_character_source_mouth_three.png.import
@@ -3,8 +3,8 @@
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"
+path.s3tc="res://.godot/imported/straywild_character_source_mouth_three.png-adf40ee30e725bafadf6a8fa4dbd8abf.s3tc.ctex"
+path.etc2="res://.godot/imported/straywild_character_source_mouth_three.png-adf40ee30e725bafadf6a8fa4dbd8abf.etc2.ctex"
metadata={
"imported_formats": ["s3tc_bptc", "etc2_astc"],
"vram_texture": true
@@ -15,8 +15,8 @@ generator_parameters={
[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"]
+source_file="res://art/source/characters/base/straywild_character_source_mouth_three.png"
+dest_files=["res://.godot/imported/straywild_character_source_mouth_three.png-adf40ee30e725bafadf6a8fa4dbd8abf.s3tc.ctex", "res://.godot/imported/straywild_character_source_mouth_three.png-adf40ee30e725bafadf6a8fa4dbd8abf.etc2.ctex"]
[params]
@@ -28,7 +28,7 @@ compress/rdo_quality_loss=0.0
compress/hdr_compression=1
compress/normal_map=0
compress/channel_pack=0
-mipmaps/generate=true
+mipmaps/generate=false
mipmaps/limit=-1
roughness/mode=0
roughness/src_normal=""
diff --git a/art/source/characters/base/netfishing_character_source_nose1.2.png b/art/source/characters/base/straywild_character_source_nose1.2.png
similarity index 100%
rename from art/source/characters/base/netfishing_character_source_nose1.2.png
rename to art/source/characters/base/straywild_character_source_nose1.2.png
diff --git a/art/source/characters/base/netfishing_character_source_nose1.2.png.import b/art/source/characters/base/straywild_character_source_nose1.2.png.import
similarity index 58%
rename from art/source/characters/base/netfishing_character_source_nose1.2.png.import
rename to art/source/characters/base/straywild_character_source_nose1.2.png.import
index 99bfef3..e0c3667 100644
--- a/art/source/characters/base/netfishing_character_source_nose1.2.png.import
+++ b/art/source/characters/base/straywild_character_source_nose1.2.png.import
@@ -3,8 +3,8 @@
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"
+path.s3tc="res://.godot/imported/straywild_character_source_nose1.2.png-0a8355472731651768eaa5fc2ee168d4.s3tc.ctex"
+path.etc2="res://.godot/imported/straywild_character_source_nose1.2.png-0a8355472731651768eaa5fc2ee168d4.etc2.ctex"
metadata={
"imported_formats": ["s3tc_bptc", "etc2_astc"],
"vram_texture": true
@@ -15,8 +15,8 @@ generator_parameters={
[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"]
+source_file="res://art/source/characters/base/straywild_character_source_nose1.2.png"
+dest_files=["res://.godot/imported/straywild_character_source_nose1.2.png-0a8355472731651768eaa5fc2ee168d4.s3tc.ctex", "res://.godot/imported/straywild_character_source_nose1.2.png-0a8355472731651768eaa5fc2ee168d4.etc2.ctex"]
[params]
@@ -28,7 +28,7 @@ compress/rdo_quality_loss=0.0
compress/hdr_compression=1
compress/normal_map=0
compress/channel_pack=0
-mipmaps/generate=true
+mipmaps/generate=false
mipmaps/limit=-1
roughness/mode=0
roughness/src_normal=""
diff --git a/art/source/characters/base/netfishing_character_source_nose_dog_round.png b/art/source/characters/base/straywild_character_source_nose_dog_round.png
similarity index 100%
rename from art/source/characters/base/netfishing_character_source_nose_dog_round.png
rename to art/source/characters/base/straywild_character_source_nose_dog_round.png
diff --git a/art/source/characters/base/netfishing_character_source_nose_dog_round.png.import b/art/source/characters/base/straywild_character_source_nose_dog_round.png.import
similarity index 57%
rename from art/source/characters/base/netfishing_character_source_nose_dog_round.png.import
rename to art/source/characters/base/straywild_character_source_nose_dog_round.png.import
index 9a8da06..5fe079e 100644
--- a/art/source/characters/base/netfishing_character_source_nose_dog_round.png.import
+++ b/art/source/characters/base/straywild_character_source_nose_dog_round.png.import
@@ -3,8 +3,8 @@
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"
+path.s3tc="res://.godot/imported/straywild_character_source_nose_dog_round.png-d1ea0db61e41dfeeed3b599286ffb82c.s3tc.ctex"
+path.etc2="res://.godot/imported/straywild_character_source_nose_dog_round.png-d1ea0db61e41dfeeed3b599286ffb82c.etc2.ctex"
metadata={
"imported_formats": ["s3tc_bptc", "etc2_astc"],
"vram_texture": true
@@ -15,8 +15,8 @@ generator_parameters={
[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"]
+source_file="res://art/source/characters/base/straywild_character_source_nose_dog_round.png"
+dest_files=["res://.godot/imported/straywild_character_source_nose_dog_round.png-d1ea0db61e41dfeeed3b599286ffb82c.s3tc.ctex", "res://.godot/imported/straywild_character_source_nose_dog_round.png-d1ea0db61e41dfeeed3b599286ffb82c.etc2.ctex"]
[params]
@@ -28,7 +28,7 @@ compress/rdo_quality_loss=0.0
compress/hdr_compression=1
compress/normal_map=0
compress/channel_pack=0
-mipmaps/generate=true
+mipmaps/generate=false
mipmaps/limit=-1
roughness/mode=0
roughness/src_normal=""
diff --git a/art/source/environment/terrain/starter_island.blend b/art/source/environment/terrain/starter_island.blend
index 14f9394..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/source/items/equipment/.gdignore b/art/source/items/equipment/.gdignore
new file mode 100644
index 0000000..e69de29
diff --git a/art/source/items/equipment/net.blend b/art/source/items/equipment/net.blend
new file mode 100644
index 0000000..0a7ba4c
Binary files /dev/null and b/art/source/items/equipment/net.blend differ
diff --git a/audio/music/world/tulips.wav b/audio/music/world/tulips.wav
new file mode 100644
index 0000000..9cd4605
Binary files /dev/null and b/audio/music/world/tulips.wav differ
diff --git a/sound/dialogue/animalese/placeholder/k.wav.import b/audio/music/world/tulips.wav.import
similarity index 50%
rename from sound/dialogue/animalese/placeholder/k.wav.import
rename to audio/music/world/tulips.wav.import
index 4af149c..0f8cce1 100644
--- a/sound/dialogue/animalese/placeholder/k.wav.import
+++ b/audio/music/world/tulips.wav.import
@@ -2,13 +2,13 @@
importer="wav"
type="AudioStreamWAV"
-uid="uid://eypaqf1iaq5"
-path="res://.godot/imported/k.wav-b85d045dc022b9055c25a650b2e3af2a.sample"
+uid="uid://5hgmht4bq1my"
+path="res://.godot/imported/tulips.wav-7c18a300a62f25396913efb28c2ba3f6.sample"
[deps]
-source_file="res://sound/dialogue/animalese/placeholder/k.wav"
-dest_files=["res://.godot/imported/k.wav-b85d045dc022b9055c25a650b2e3af2a.sample"]
+source_file="res://audio/music/world/tulips.wav"
+dest_files=["res://.godot/imported/tulips.wav-7c18a300a62f25396913efb28c2ba3f6.sample"]
[params]
diff --git a/collection/collection_log.gd b/collection/collection_log.gd
index da36eb8..59d33d1 100644
--- a/collection/collection_log.gd
+++ b/collection/collection_log.gd
@@ -6,9 +6,11 @@ signal fish_quality_discovered(fish_id: StringName, quality: int)
signal collection_changed
const FishQualityType = preload("res://fish/fish_quality.gd")
+const MAX_CATCH_COUNT: int = 1000000000
var _discovered: Dictionary[StringName, bool] = {}
var _quality_masks: Dictionary[StringName, int] = {}
+var _catch_counts: Dictionary[StringName, int] = {}
func has_discovered(fish_id: StringName) -> bool:
@@ -41,6 +43,26 @@ func mark_quality_discovered(fish_id: StringName, quality: int) -> void:
collection_changed.emit()
+func record_catch(fish_id: StringName, quality: int) -> void:
+ if fish_id.is_empty() or not FishQualityType.is_valid(quality):
+ return
+ var species_was_discovered: bool = has_discovered(fish_id)
+ if not species_was_discovered:
+ _discovered[fish_id] = true
+ var previous_mask: int = _quality_masks.get(fish_id, 0)
+ var next_mask: int = previous_mask | FishQualityType.bit_for(quality)
+ _quality_masks[fish_id] = next_mask
+ _catch_counts[fish_id] = mini(
+ int(_catch_counts.get(fish_id, 0)) + 1,
+ MAX_CATCH_COUNT,
+ )
+ if not species_was_discovered:
+ fish_discovered.emit(fish_id)
+ if next_mask != previous_mask:
+ fish_quality_discovered.emit(fish_id, quality)
+ collection_changed.emit()
+
+
func get_discovered_ids() -> Array[StringName]:
var discovered_ids: Array[StringName] = []
for fish_id: StringName in _discovered:
@@ -65,6 +87,14 @@ func get_discovered_quality_masks() -> Dictionary[StringName, int]:
return _quality_masks.duplicate()
+func get_catch_count(fish_id: StringName) -> int:
+ return _catch_counts.get(fish_id, 0) if has_discovered(fish_id) else 0
+
+
+func get_catch_counts() -> Dictionary[StringName, int]:
+ return _catch_counts.duplicate()
+
+
func has_mastered(fish_id: StringName) -> bool:
return get_quality_mask(fish_id) == FishQualityType.ALL_TIERS_MASK
@@ -77,6 +107,22 @@ func replace_discovered_ids(fish_ids: Array[StringName]) -> bool:
func replace_discovery_state(
fish_ids: Array[StringName],
quality_masks: Dictionary[StringName, int],
+) -> bool:
+ var preserved_counts: Dictionary[StringName, int] = {}
+ for fish_id: StringName in fish_ids:
+ if _catch_counts.has(fish_id):
+ preserved_counts[fish_id] = _catch_counts[fish_id]
+ return replace_collection_state(
+ fish_ids,
+ quality_masks,
+ preserved_counts,
+ )
+
+
+func replace_collection_state(
+ fish_ids: Array[StringName],
+ quality_masks: Dictionary[StringName, int],
+ catch_counts: Dictionary[StringName, int],
) -> bool:
var replacement: Dictionary[StringName, bool] = {}
for fish_id: StringName in fish_ids:
@@ -95,7 +141,19 @@ func replace_discovery_state(
return false
if mask != 0:
replacement_masks[fish_id] = mask
+ var replacement_counts: Dictionary[StringName, int] = {}
+ for fish_id: StringName in catch_counts:
+ var count: int = catch_counts[fish_id]
+ if (
+ fish_id.is_empty()
+ or not replacement.has(fish_id)
+ or count <= 0
+ or count > MAX_CATCH_COUNT
+ ):
+ return false
+ replacement_counts[fish_id] = count
_discovered = replacement
_quality_masks = replacement_masks
+ _catch_counts = replacement_counts
collection_changed.emit()
return true
diff --git a/docs/ARCHITECTURE.md b/docs/ARCHITECTURE.md
deleted file mode 100644
index 33745bc..0000000
--- a/docs/ARCHITECTURE.md
+++ /dev/null
@@ -1,80 +0,0 @@
-# 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
-
-`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. See [`decisions/0001-host-authority.md`](decisions/0001-host-authority.md).
-
-Protocol compatibility is defined in `network/network_protocol.gd`. A visible
-release version is not a reason to change the protocol number.
-
-## Persistence
-
-`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.
-
-## 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.
-
-## 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; see [`TESTING.md`](TESTING.md).
diff --git a/docs/ASSET-PROVENANCE.md b/docs/ASSET-PROVENANCE.md
deleted file mode 100644
index 3d3907a..0000000
--- a/docs/ASSET-PROVENANCE.md
+++ /dev/null
@@ -1,145 +0,0 @@
-# Asset provenance
-
-This document records where runtime assets came from and what must be verified
-before redistribution. A Git commit proves when bytes entered this repository;
-it does not by itself prove authorship or licensing.
-
-## 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.
-
-Runtime resources must use `res://` paths. Workstation Sync folders are intake
-locations only and must never appear in scenes or resources.
-
-## Current inventory
-
-| Asset family | Repository location | Recorded provenance |
-| --- | --- | --- |
-| Fish art and portraits | `fish/species/`, `art/exported/fish/` | 2D artwork by contributor Rheannon Eisworth. |
-| Item icons | `items/icons/` | 2D artwork by contributor Rheannon Eisworth. |
-| Inventory notepad | `art/ui/ui_notepad.png` | 2D artwork by contributor Rheannon Eisworth; integrated without a runtime external path. |
-| Environment textures | `art/exported/environment/textures/` | 2D artwork by contributor Rheannon Eisworth. |
-| UI patterns | `art/patterns/` | 2D artwork by contributor Rheannon Eisworth. |
-| Character and world models | `art/exported/` | Original 3D models by the project owner. Any embedded 2D artwork is by Rheannon Eisworth. |
-| Tuffy font | `ui/fonts/Tuffy_Bold.otf` | Public-domain dedication in `ui/fonts/Tuffy-LICENSE.txt`. |
-| Seattle Avenue font | `ui/fonts/seattle_avenue.otf` | License and attribution not present; resolve before public distribution. |
-| Title music | `audio/music/title/as_in_four_wolves.ogg` | Original music composed and owned by the project owner. |
-| 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. |
-| 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. |
-
-### 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.
-
-## 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. Resolve the Seattle Avenue font license and attribution gap above.
-2. Record the public owner/studio credit and the contributor's redistribution
- permission in the chosen project license or release records.
-3. Confirm the project-wide source/content license selected by the owner.
-4. Search exported resources for external filesystem paths.
-5. Preserve required third-party notices with the package.
diff --git a/docs/ATTRIBUTION.md b/docs/ATTRIBUTION.md
new file mode 100644
index 0000000..d8676f0
--- /dev/null
+++ b/docs/ATTRIBUTION.md
@@ -0,0 +1,334 @@
+# 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
+
+straywild 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
+ provisional fish placeholder artwork and original 3D work except where
+ another creator is credited, and composer of the original title track
+ `audio/music/title/as_in_four_wolves.ogg`, dusk/world track
+ `audio/music/world/craft.mp3`, and the synthesized robot animalese tones.
+- Endeavour: co-owner, developer, and 2D artist. Her incorporated work includes
+ character pattern/channel-map art, environment and UI art, item art, and
+ finished fish and creature artwork. Planned work is not described as
+ incorporated until its files enter the project.
+- 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`.
+- kat: supplied the original source voice recording for the kat animalese set.
+ Voyager created the sliced, normalized runtime derivatives.
+- kim: supplied the original source voice recording for the kim animalese set.
+ Voyager created the sliced, normalized runtime derivatives.
+
+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 straywild, 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` |
+| straywild 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 |
+| Previously accepted or expressly invited 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 straywild creative assets or branding. The
+straywild Asset License separately permits qualifying noncommercial add-on
+Mods for official straywild software and narrowly scoped Contribution Forks
+used to prepare changes for possible upstream inclusion. It does not permit
+those assets to accompany independent code forks, clones, standalone games,
+unrelated products, or general-purpose asset packs. A GPL fork that is not a
+qualifying Contribution Fork must replace straywild 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 straywild owners under their private joint
+ownership agreement.
+
+## Godot Engine notice
+
+straywild 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 straywild Asset License.
+
+## Intake policy
+
+For every new external or supplied asset, retain a durable intake record with:
+
+- creator or supplying party;
+- original source location or delivery channel;
+- source filename and, when useful, its initial repository destination;
+- source and destination SHA-256 when copied without modification;
+- license or explicit permission;
+- any permitted transformations;
+- the importing commit or release.
+
+The intake record may live in the accepted pull request, issue, email, or the
+owners' private project records. This public document should contain the
+credits and license notices needed by recipients; it should not duplicate a
+changing file inventory that can be derived from the repository itself.
+
+straywild does not accept unsolicited contributions. For work expressly
+invited by both Owners, maintainers must obtain affirmative agreement to the
+source-root `CONTRIBUTOR-TERMS.md` and retain the written invitation and
+agreement in a durable Project record.
+
+Runtime resources must use `res://` paths. Workstation Sync folders are intake
+locations only and must never appear in scenes or resources.
+
+## Project-created artwork and music
+
+Fish entries without finished replacement artwork use provisional placeholder
+artwork by Voyager. Incorporated finished fish and creature artwork is by
+Endeavour; work that has not entered this repository is not presented here as
+part of the game or attributed as a shipped asset.
+
+Endeavour's incorporated 2D contributions include the inventory notepad,
+environment and UI artwork, item artwork, finished fish and creature artwork,
+and character customization pattern and channel-map artwork. Voyager's
+incorporated original work includes 3D assets, the provisional fish
+placeholder artwork, the title and dusk music identified above, and the robot
+animalese tones. The current starter motorcoach exterior was supplied by a
+contributing artist who released its copyright to the project; the original
+and cleaned source files are retained in the owners' source-work archive.
+Specific contributor exceptions are recorded below and in the in-game credits.
+
+These credits intentionally describe work by creator and contribution family
+instead of maintaining a second file-by-file asset catalog. Runtime resources
+and Git history identify the files present in a particular revision. A path,
+directory name, commit author, or supplying party is not by itself evidence of
+authorship.
+
+## Third-party and contributor runtime material
+
+The following paths identify distributed material whose external license or
+individual contributor credit must remain easy to find. They were checked
+against the repository when this record was revised.
+
+| Material | Runtime location | Credit or terms |
+| --- | --- | --- |
+| Kat animalese alphabet and number sounds | `sound/dialogue/animalese/alphanumeric/` | Project-processed derivatives of the original source voice recording supplied by straywild community member kat. |
+| Kim animalese alphabet and number sounds | `sound/dialogue/animalese/kim/` | Project-processed derivatives of the original source voice recording supplied by straywild community member kim. |
+| 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; notice in `ui/fonts/Seattle-Avenue-LICENSE.txt`. |
+| 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. |
+
+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: these contributions may be incorporated into combined
+ character or environment resources rather than retained as one file per
+ originally supplied model. Git history is the revision-specific file map.
+- 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.
+
+### Endeavour 2D contribution record
+
+Endeavour created the incorporated character customization patterns and
+channel maps as well as additional UI, environment, item, fish, and creature
+artwork. Voyager's provisional fish placeholder is not part of this credit.
+New or replacement art is added to this record only after it is accepted into
+the repository.
+
+The exact current filenames and bytes are intentionally not copied into this
+document. The character appearance resources and Git history are the
+revision-specific inventory.
+
+
+### 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/BALANCE.md b/docs/BALANCE.md
deleted file mode 100644
index 948b882..0000000
--- a/docs/BALANCE.md
+++ /dev/null
@@ -1,41 +0,0 @@
-# Balance and content notes
-
-Balance is authored data and should change deliberately. This document records
-the policy; 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/saltwater habitat is validated by the host in addition to pool
- membership.
-- Value, rarity, quality, and weight ranges 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/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.
-
-## Recording a balance change
-
-Include in the change description:
-
-1. affected stable IDs and resource paths;
-2. old and new values;
-3. intended player-facing outcome;
-4. interactions with quality, availability, and economy;
-5. deterministic validation performed;
-6. whether existing saves remain semantically valid.
-
-Do not bump the save schema or network protocol simply because an authored
-number changed. Bump a format only when its serialized contract changes.
diff --git a/docs/DEVELOPMENT.md b/docs/DEVELOPMENT.md
new file mode 100644
index 0000000..76ed032
--- /dev/null
+++ b/docs/DEVELOPMENT.md
@@ -0,0 +1,368 @@
+# Development reference
+
+This is the authoritative reference for straywild 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 `straywild_DISCOVERY_URL` available as a
+development/deployment override.
+
+Friendships are local identity relationships shared across save slots. A live,
+authenticated gameplay session is required to exchange the directional
+capabilities that establish a friendship. Blocking an identity removes that
+friendship; unblocking does not recreate it. Discovery may publish opt-in,
+short-lived friend presence and deliver invitations only while both games are
+online. It stores hashed capability identifiers rather than identity
+fingerprints or a complete friend graph, keeps no durable social records, and
+provides no offline delivery. Direct joins still use the existing verified
+public-room and ENet connection path.
+
+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` and indexed as named slots by
+`PlayerSaveSlotCatalog`. Existing single-save installations are adopted as the
+first slot in place. Device settings, appearance, and social/identity stores
+remain shared across slots and have separate formats and lifecycles.
+Progression archive import and export belong to the title-screen Play page:
+imports create a new slot, while exports copy only the selected progression.
+
+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 treatment, player blob shadows) do not own gameplay collision or
+network authority.
+
+### 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, inventory and storage 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
+
+`world/test_world.tscn` is the shared gameplay stage. It owns the environment,
+sun, world bounds, and below-world failsafe, then hosts exactly one active
+`WorldRegion`. Two canonical region implementations are supported:
+
+- the generated world, which is the default for new games and is rebuilt from
+ the saved seed; and
+- the starter island, which remains an authored selectable layout and a useful
+ compatibility/reference scene.
+
+The stable layout IDs live in `world/world_layout.gd`. Do not infer a layout
+from a scene filename, display label, or region node name. Both implementations
+must satisfy the shared `WorldRegion` contract for player spawn, safe respawns,
+water regions, recovery, gathering, digging, the shop, and player storage.
+
+A region owns the content that changes with the selected world:
+
+```text
+WorldRegion
+├── Terrain
+├── WaterBodies
+├── PlayerSpawn
+├── SafeRespawns
+├── DiggableAreas
+├── GatherableAnchors
+└── Interactables
+ ├── FishingShopWorld
+ └── PlayerStorageBox
+```
+
+The generated region owns its chunk, biome, and prop catalogs and derives
+terrain collision, water coverage, biome placement, and presentation reference
+geometry from the generated result. The starter-island region owns its imported
+terrain hierarchy and explicitly placed content. Code shared by both layouts
+must depend on `WorldRegion`, not a starter-island child path.
+
+Every fishable water body authors its water type explicitly. 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.
+
+In the generated layout, seed and layout ID are persistent data. Chunk IDs,
+edge rules, biome definitions, and prop definitions are authored resources;
+changing them can change the world produced by an existing seed and therefore
+requires deliberate compatibility review.
+
+For the authored starter island, move a meaningful feature root rather than one
+of its implementation children. Child transforms are local offsets owned by
+that feature. Its imported GLB hierarchy is the starter region's terrain and
+collision authority; this rule does not describe generated-world collision.
+
+The active `Environment` and `Sun` remain Inspector-authored on the shared
+stage and are not recreated by either region.
+
+### 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 presentation
+
+The visible shoreline treatment is currently produced by the water materials.
+The older smooth-ribbon baker and generated starter-island ribbon resource are
+retained as development history/tooling, but the ribbon mesh is disabled and
+is not part of the current rendered shoreline. Do not treat a ribbon rebuild
+as a normal terrain-authoring requirement or re-enable the mesh as incidental
+cleanup.
+
+### 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.
+
+The tracked import policy keeps these facial-feature textures at a maximum
+runtime size of 512×512, uses VRAM compression, and does not generate mipmaps.
+The source PNGs remain unchanged. Run the texture import normalizer shown below
+after adding facial artwork so low-memory builds do not retain full-resolution
+copies of the entire customization catalog.
+
+### Texture sampling
+
+straywild artwork uses nearest-neighbor sampling. Do not enable linear,
+bilinear, trilinear, or anisotropic texture filtering. Shader samplers must
+declare `filter_nearest`. The three canonical repeating terrain textures are
+the sole mipmap exception: their projected shader uses nearest-filtered
+mipmaps to prevent distance aliasing without smoothing nearby pixels. 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
+```
+
+### Importing animalese voice sets
+
+Animalese clips use one deterministic runtime level and format: -18 dBFS RMS
+with a -3 dBFS peak ceiling, mono, 48 kHz, 16-bit PCM. Import a supplied set
+through the normalization script rather than copying its WAV files directly:
+
+```bash
+scripts/import_animalese_voice.sh /path/to/source_clips
+```
+
+The source directory may contain lowercase letter, number, and underscore WAV
+filenames. The script converts every top-level WAV and writes the results under
+`sound/dialogue/animalese//`. Add a new runtime set to
+`player/animalese_voice_profiles.gd` after import, then run the quick validation
+suite. Keep the untouched submission and its original hashes in the private
+intake record; repository files are normalized derivatives.
+
+### 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 `straywild_DATA_DIR` at an arbitrary empty directory; it is an
+explicit portable-data override and must identify a valid data root. The
+PortMaster launcher is the narrow exception: it also sets
+`straywild_CREATE_DATA_DIR=1`, allowing the game to initialize its known empty
+external save directory on first launch.
+
+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-PERFORMANCE.md b/docs/PORTMASTER-PERFORMANCE.md
deleted file mode 100644
index 8c53f34..0000000
--- a/docs/PORTMASTER-PERFORMANCE.md
+++ /dev/null
@@ -1,28 +0,0 @@
-# PortMaster performance profile
-
-The PortMaster 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 the Allwinner H616/H700 XX handhelds:
-
-- `allwinner,h616`
-- `sun50iw9p1`
-- `allwinner,sun50i-h700`
-
-The low-end launch profile passes these options to Godot:
-
-- `--single-window`
-- `--disable-vsync`
-- `--max-fps 30`
-- `--audio-output-latency 40`
-
-It also passes `NETFISHING_LOW_END=1`. The game responds by rendering the 3D
-world at 75 percent linear resolution with nearest-neighbor scaling. This
-reduces 3D pixel work by about 44 percent 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.
-
-When adding another device family, record its exact NUL-separated device-tree
-`compatible` value from the hardware before extending the launcher match. Do
-not infer detection from a retail product name alone.
diff --git a/docs/PORTMASTER-RELEASE.md b/docs/PORTMASTER-RELEASE.md
deleted file mode 100644
index 3bfae87..0000000
--- a/docs/PORTMASTER-RELEASE.md
+++ /dev/null
@@ -1,137 +0,0 @@
-# PortMaster release builds
-
-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
-ff08aacc52bbdc95616320800da3eaee0c0ba5fafdd962bf04f9655859409764 licenses/CREDITS.md
-b84fdd2c3da5db56385cdbb639795e90aa3e035c53bc3591135f18df3331451f licenses/Tuffy-LICENSE.txt
-```
-
-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.
diff --git a/docs/PORTMASTER.md b/docs/PORTMASTER.md
new file mode 100644
index 0000000..803f72f
--- /dev/null
+++ b/docs/PORTMASTER.md
@@ -0,0 +1,270 @@
+# PortMaster reference
+
+straywild's PortMaster release is a canonical PortMaster package, not a
+version-named generic ZIP. The installer-facing archive must be named
+`straywild.zip`, and its root must contain exactly:
+
+```text
+straywild.sh
+straywild/
+```
+
+`port.json` belongs at `straywild/port.json` in the distributable archive.
+The upstream source-submission layout keeps it beside the launcher and port
+directory, but PortMaster's release builder relocates it into the declared
+port directory. Do not copy the source-submission layout directly into a
+release ZIP.
+
+HarbourMaster's installer also relocates recognized non-script files found at
+an archive root into the detected port directory. That compatibility behavior
+masked straywild's former root-level `port.json` during auto-install tests.
+Directly extracting the same archive into `/ports` did not perform the repair.
+Do not rely on installer relocation; build the distributable layout correctly.
+
+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, names `straywild.zip`, and marks the
+ self-contained package ready to run.
+- `straywild.sh` does not contain a `# PORTMASTER:` signature in the source or
+ distributable archive. HarbourMaster adds that installation marker when it
+ installs the port.
+- The executable is `straywild/straywild.aarch64`.
+- The package declares AArch64, two analog sticks, GLIBC 2.28, and
+ `weston_pkg_0.2.squashfs`.
+- The launcher starts GPTOKEYB with the explicit `straywild.gptk` no-op map.
+ Every keyboard and mouse binding is disabled, leaving only GPTOKEYB's
+ device-specific force-quit chord while Godot and straywild's controller
+ mapping manager handle gameplay input. The explicit map also avoids the
+ anomalous controller behavior observed on ROCKNIX without a passthrough map.
+- The launcher calls `pm_platform_helper` for the game executable after
+ starting GPTOKEYB and calls `pm_finish` after the game exits.
+- straywild automatically creates player data for new installations under
+ `ports/saves/straywild/` and does not present the in-game folder picker.
+- Existing installations with a selected data root keep that location when
+ upgraded. Device-local data, configuration, and cache remain under
+ `straywild/conf/data`, `straywild/conf/config`, and
+ `straywild/conf/cache`.
+- A rebrand upgrade copies an existing `netfishing/conf/` tree into the new
+ `straywild/conf/` tree when the latter does not exist. It also reuses an
+ existing `ports/saves/netfishing/` data root rather than creating an empty
+ replacement. New installations use straywild paths exclusively.
+- The archive must not contain `conf/`, saves, identities, logs, source files,
+ `.git`, or `.godot` content.
+- Release downloads must publish only the canonical `straywild.zip` for this
+ platform. Do not also publish a byte-identical versioned PortMaster ZIP;
+ HarbourMaster identifies the port by its canonical archive name and the
+ Forgejo release tag already supplies the version.
+
+## 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/straywild.zip
+```
+
+`--package-only` is reserved for repackaging an already validated ARM64 export.
+It does not rebuild game content.
+
+For a packaging-only correction to an already published game release, commit
+only PortMaster launcher, packaging-script, or PortMaster documentation changes
+after the immutable release tag, then run:
+
+```bash
+bash scripts/build_portmaster.sh --package-only --hotfix
+```
+
+The hotfix keeps the game executable and PCK tied to the release tag while
+recording the newer packaging commit separately in `BUILD-INFO.txt`. Replace
+the release's existing `straywild.zip`; never move the game release tag.
+
+## Local muOS installation
+
+The muOS auto-install directory is:
+
+```text
+/mnt/mmc/MUOS/PortMaster/autoinstall/
+```
+
+For an explicit offline installation test, copy `straywild.zip` to a temporary
+device directory and run:
+
+```bash
+/mnt/mmc/MUOS/PortMaster/harbourmaster \
+ --offline --no-check install ./straywild.zip
+```
+
+Before upgrading an existing installation, preserve either the current
+`/mnt/mmc/ports/straywild/` paths or the former `/mnt/mmc/ports/netfishing/`
+paths on the device. After installation:
+
+1. Confirm the installed executable and PCK hashes match the staged release.
+2. Confirm HarbourMaster added its `# PORTMASTER:` installation signature to
+ the installed launcher, then confirm the launcher starts GPTOKEYB with
+ `straywild.gptk` and calls `pm_platform_helper` for the game executable.
+3. Confirm `conf/` and the external save directory were 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.
+6. Verify GPTOKEYB's device-specific force-quit chord exits the game. This is
+ Start+Select on most devices.
+
+Installing the public PortMaster catalog entry can still install an older
+catalog build until the upstream `straywild.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
+`straywild.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/straywild.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 reconstructs the wrapped game command
+through a shell, so raw mapping values passed in that command can be split into
+unintended commands. Controller names may legally contain spaces, which caused
+this failure with the `GO-Super Gamepad` mapping. The outer Weston environment
+also does not reliably preserve the corrected muOS mapping for the game.
+
+Write the exact selected mapping to `conf/cache/controller_mapping.txt` before
+starting Weston. Weston must launch `launch-straywild.sh`, which reads that
+file, exports `SDL_GAMECONTROLLERCONFIG`, and immediately replaces itself with
+the game process. This applies the mapping at the same point as the working
+0.10.1 launcher without passing its raw text through WestonPack's command
+reconstruction. Do not move the mapping back to either side of
+`westonwrap.sh`.
+
+Always pass `straywild.gptk`, which explicitly unassigns every direct and
+hotkey binding with GPTOKEYB's `\"` value. This keeps GPTOKEYB from injecting
+keyboard or mouse events on top of Godot's native controller input and retains
+the device-specific kill chord. Supplying the explicit passthrough map also
+avoids the anomalous controller behavior observed on ROCKNIX when no map is
+provided. Do not add gameplay mappings to this file.
+
+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.
+
+## PortMaster performance profile
+
+Every PortMaster launch defaults to the light performance profile, regardless
+of device family. This reflects the low-end hardware that makes up most of the
+PortMaster ecosystem and keeps the canonical package conservative by default.
+
+The light profile passes these Godot options:
+
+- `--single-window`
+- `--disable-vsync`
+- `--max-fps 30`
+- `--audio-output-latency 40`
+
+It also passes `straywild_PERFORMANCE_PROFILE=light` and
+`straywild_LOW_END=1`. The game renders the 3D world at 37.5% linear
+resolution with nearest-neighbor scaling, reducing 3D pixel work by about 86%
+while the separately rendered UI retains its canonical resolution. The light
+profile also:
+
+- disables the additional full-screen world-pixelation pass;
+- replaces animated depth-aware water shaders with opaque, per-vertex water;
+- disables ocean surface motion;
+- disables foliage wind;
+- keeps the title water and full-window menu patterns static;
+- replaces the animated cooler water and decorative bubbles with flat water;
+- disables decorative title-screen fish and bubbles;
+- retains title and dusk music while omitting rain and shoreline ambience;
+- reduces rain to 48 particles simulated at 8 FPS; and
+- replaces procedural sky clouds and 81 moving local cloud patches with one
+ flat cloud ceiling.
+
+PortMaster exports are built in an isolated project copy with a dedicated
+micro-texture profile. Every raster texture is capped at 128 pixels on its
+longest side. High-volume gameplay catalogs, including fish, character parts,
+environment art, equipment, gathering art, and item icons, are capped at 64
+pixels. All remaining raster textures use ETC2 VRAM compression. Short sound
+effects, speech, and calls are imported as 22.05 kHz mono QOA samples. The
+authoritative artwork and the desktop, Android, and macOS imports are not
+modified. Source artwork, design documents, tests, platform-specific icons,
+and ambience are omitted from the temporary export project. The original
+compressed title and dusk music remain available in both runtime profiles.
+This build-time profile is always present in the PortMaster package; selecting
+the normal runtime profile restores visual effects, but does not restore
+omitted ambience or replace the micro textures with desktop-resolution assets.
+
+The normal profile otherwise retains the full visual presentation.
+
+To opt a capable device into the normal profile, create the persistent file
+`straywild/conf/performance_profile` containing exactly:
+
+```text
+normal
+```
+
+On a typical muOS installation, the full path is:
+
+```text
+/mnt/mmc/ports/straywild/conf/performance_profile
+```
+
+Set the file to `light`, or remove it, to restore the default. An externally
+provided `straywild_PERFORMANCE_PROFILE=normal` or `light` environment value
+takes precedence over the persistent file. Invalid values safely fall back to
+the light profile and are reported in `straywild/log.txt`.
+
+Do not use Godot's low processor mode for the light profile. It reduces idle
+CPU usage by sleeping between updates and is not a game-performance
+optimization.
diff --git a/docs/README-PLAYTEST.txt b/docs/README-PLAYTEST.txt
new file mode 100644
index 0000000..f187182
--- /dev/null
+++ b/docs/README-PLAYTEST.txt
@@ -0,0 +1,124 @@
+straywild
+v0.20.2-alpha
+Alpha 0.20.2
+
+Thank you for supporting this private testing release.
+
+straywild 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
+
+1. Extract the entire ZIP into a new folder.
+2. Keep the executable and straywild.pck together.
+3. Launch straywild.exe on Windows or straywild on Linux.
+
+Windows may show a warning for an unsigned application. This private build is
+not code signed. Only run a copy received from the playtest organizer.
+
+Linux users may need to allow execution:
+
+ chmod +x straywild
+
+CONTROLS
+
+Move W / A / S / D or left stick
+Jump Space
+Sprint Shift
+Sneak Ctrl or click right stick
+Slow walk Alt
+Rotate camera Hold right mouse, or use right stick
+Cycle active hotbar slot Mouse wheel or D-pad left/right
+Select hotbar slot 1 through 9
+Zoom camera Shift + mouse wheel
+ (swap wheel controls in Settings > Controls)
+Primary tool / fishing Left mouse or right trigger
+Inventory and player pages Tab
+Game Menu / back Escape
+Interact E
+Chat T
+
+Keyboard and controller bindings can be reviewed and changed in Settings.
+
+FISHING
+
+- Hold left mouse to select cast distance, then release to cast.
+- The target preview shows whether the selected landing point is fishable.
+- While waiting, hold left mouse to withdraw the bobber.
+- A bite starts the chase automatically.
+- Hold left mouse to advance the green catch meter.
+- At barriers, use distinct left-click presses to damage the barrier.
+- Do not let the red chase meter catch the green meter.
+- Accessibility auto-click can be enabled in Settings.
+- Approach the Fishing Shop and interact to open it.
+- Move sellable inventory entries into the shop's sell tray, review the total,
+ and confirm the sale.
+- Buy supplies, equipment, and persistent upgrades from their shop tabs.
+- Move world-use items into the hotbar and select their slot to use or hold
+ them.
+- Coffee improves movement; other supplies temporarily improve fishing.
+- Inventory and storage capacity can be expanded permanently at the shop.
+- Reel Speed upgrades increase authoritative green reeling progress.
+- 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
+- Kat, Kim, and robot speech sample sets
+- Movement, jumping, camera rotation, and zoom
+- Short, medium, and maximum-distance casts, including far ocean casts
+- Valid water and invalid land casts
+- Manual withdrawal before a bite
+- Barrier-and-chase catching
+- Accessibility auto-click settings
+- Catch showcase and fish size variation
+- General inventory, separate tackle, hotbar storage, player storage, and the
+ Logbook
+- Fishing, net gathering, and beach digging
+- Equipment and consumable use from the hotbar
+- Fishing Shop sell tray, wallet updates, and persistent upgrades
+- Portable RV homes, multiplayer visits, RV storage, and the early decor
+ purchasing and placement prototype
+- Seeded generated worlds and the authored starter-island layout
+- Generated beaches, ponds, lakes, complete rivers, denser forests, varied
+ elevation, and closed coastal cliffs
+- Collaborative Art Kit drawing, finalized artwork, PNG export, and stamps
+- Save, Continue, New Game, and Delete Save
+- Game Menu and persistent camera settings
+- Water-entry recovery from several shores
+
+SAVES AND SETTINGS
+
+Progression and settings are stored outside the extracted game folder. The
+Data & Identity page shows the active player-data location. Delete Save removes
+progression but preserves device settings.
+
+This is an early save format. Keep expectations modest and report any failure
+to Continue, save, sell, move, favorite, or retain an inventory entry.
+
+FEEDBACK
+
+Please include:
+
+- Your operating system and whether you used mouse/keyboard or controller
+- What you were doing when a problem occurred
+- Whether the issue repeated after restarting
+- Screenshots or a short recording when useful
+- How movement, casting, chase pressure, barrier clicking, menus, and camera
+ controls felt
+
+KNOWN ALPHA LIMITATIONS
+
+- 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.
+- RV exteriors and decor use temporary prototype geometry and presentation.
+- 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..444ebc7
--- /dev/null
+++ b/docs/README.md
@@ -0,0 +1,75 @@
+# straywild 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/straywild-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/straywild-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.
+
+The same rule applies to credits. `ATTRIBUTION.md` records creators, licenses,
+and durable contribution families. It does not attempt to mirror every current
+asset filename or private intake-directory layout. Planned or unincorporated
+work is not described as shipped, and repository paths are included only when
+they help recipients locate a distributed third-party work or license.
+
+## 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`
+
+`CONTRIBUTING.md` records that unsolicited contributions are not accepted.
+`CONTRIBUTOR-TERMS.md` remains a governing legal record for previously
+accepted and expressly invited work, not an invitation for new submissions.
+
+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.
+
+Before adding a literal project path to prose, verify that it exists in the
+same revision and that readers actually need the path. Prefer a stable owner,
+interface, or resource type over a directory listing. A path or Git author is
+never evidence of creative authorship.
diff --git a/docs/TESTING.md b/docs/TESTING.md
deleted file mode 100644
index e70455b..0000000
--- a/docs/TESTING.md
+++ /dev/null
@@ -1,63 +0,0 @@
-# Testing
-
-NETfishing validations are Godot `SceneTree` scripts. They use assertions and
-exit nonzero on failure. Run them with isolated user data so development checks
-cannot touch real saves, identity, trust, relationships, or settings.
-
-## 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 validations.
-- `full` adds socket-free scene/runtime checks suitable for headless execution.
-- `host` runs single-process authoritative-host checks that bind a local UDP
- port.
-- `network` runs the loopback host/client validations in pairs.
-- `all` runs `full`, `host`, and then `network`.
-
-Set `GODOT_BIN` to select another executable. Set `TEST_TIMEOUT_SECONDS` to
-change the per-process timeout. The runner creates one temporary XDG root per
-process and removes it on exit.
-
-## 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 set `NETFISHING_DATA_DIR` to an arbitrary empty folder: that variable is
-an explicit portable-data override and must point at a valid NETfishing data
-root. Isolating XDG paths is sufficient for the validation scripts.
-
-## Graphical checks
-
-Headless tests cannot prove visual alignment, actual mouse routing, shader
-appearance, controller feel, or window-resize behavior. Presentation changes
-need a real graphical startup with isolated XDG roots and inspection at the
-canonical 1280×720 layout plus relevant low/high and ultrawide resolutions.
-
-## Network checks
-
-Host and paired tests bind loopback UDP ports defined in their scripts. Ensure
-no old Godot validation process is holding those ports and that the execution
-environment permits local sockets. The runner starts paired hosts before their
-clients and requires both processes to exit successfully. Network tests do not
-contact external servers.
-
-## Release checks
-
-A release audit additionally includes clean Git/tag verification, editor import,
-export-template checks, platform exports, executable smoke tests, archive
-inspection, and SHA-256 verification. Those checks are intentionally not hidden
-inside the development runner.
diff --git a/docs/decisions/0001-host-authority.md b/docs/decisions/0001-host-authority.md
deleted file mode 100644
index f134d93..0000000
--- a/docs/decisions/0001-host-authority.md
+++ /dev/null
@@ -1,27 +0,0 @@
-# ADR 0001: Host-authoritative shared state
-
-Status: accepted
-
-## Context
-
-NETfishing supports private hosts, open hosts, and joined clients. Catches,
-currency, owned items, jobs, mail, drawings, profiles, and moderation affect
-state that cannot safely trust a client's local presentation.
-
-## Decision
-
-The host validates requests against registered peers and host-owned context,
-then performs the authoritative mutation. Clients receive results and replicated
-presentation state. Client evidence is bounded and reconstructed where
-possible; it is not treated as authority.
-
-Private single-player uses the same host path. Presentation-only state does not
-add RPCs or persistence.
-
-## Consequences
-
-- Shared behavior is consistent across private, open-host, and joined-client
- modes.
-- Tests must cover local-host and paired host/client paths.
-- Stable protocol payloads and rejection cleanup require explicit handling.
-- UI activation cannot be used as proof that a server-side mutation succeeded.
diff --git a/docs/decisions/0002-authored-data-and-stable-ids.md b/docs/decisions/0002-authored-data-and-stable-ids.md
deleted file mode 100644
index 23163cb..0000000
--- a/docs/decisions/0002-authored-data-and-stable-ids.md
+++ /dev/null
@@ -1,27 +0,0 @@
-# ADR 0002: Authored resources and stable identifiers
-
-Status: accepted
-
-## Context
-
-Fish, items, pools, availability, shop stock, and other game content evolve
-independently from saved catches and network messages. Display labels and scene
-node names are expected to change during development.
-
-## Decision
-
-Repository-owned resources are the 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 rather than
-duplicated strings. Comprehensive catalogs remain separate from location-
-specific selection pools.
-
-## Consequences
-
-- Renaming visible UI does not require save migration.
-- Removing or changing a stable ID requires an explicit compatibility plan.
-- Content validation checks uniqueness, catalog completeness, pool membership,
- and typed habitat compatibility.
-- New authored assets need provenance records as well as resource references.
diff --git a/docs/design/.gdignore b/docs/design/.gdignore
new file mode 100644
index 0000000..8b13789
--- /dev/null
+++ b/docs/design/.gdignore
@@ -0,0 +1 @@
+
diff --git a/docs/design/fish_catalog_proposal.csv b/docs/design/fish_catalog_proposal.csv
new file mode 100644
index 0000000..c427dc0
--- /dev/null
+++ b/docs/design/fish_catalog_proposal.csv
@@ -0,0 +1,314 @@
+catalog_number,display_name,collection_group,water_type,spawn_area,seasons,day_rule,rain_rule,fog_rule,rarity,base_catch_weight,weight_min_lb,weight_max_lb,sell_value_min,sell_value_max,sell_value_curve,required_bait_tags,required_event_tags,forbidden_event_tags,logbook_fact,active_status
+101,bowfin,Ancient freshwater fish,Fresh Water,pond,Spring | Summer | Fall,Night,Any,Required,Rare,0.28,1,20,13,30,0.85,,,,bowfin can gulp air at the surface when warm backwaters run low on oxygen. a long dorsal fin ripples as they stalk prey.,inactive
+102,lake sturgeon,Ancient freshwater fish,Fresh Water,lake,Spring | Summer | Fall | Winter,Night,Any,Any,Epic,0.09,10,180,36,82,0.8,,,,lake sturgeon sweep the bottom with four sensitive barbels before extending a tube-like mouth to feed. they can live for decades.,inactive
+103,longnose gar,Ancient freshwater fish,Fresh Water,river,Spring | Summer | Fall,Day,Any,Any,Rare,0.24,2,35,14,34,0.85,,,,longnose gar wait near the surface before snapping sideways at passing fish. their narrow jaws carry rows of fine needle-like teeth.,inactive
+104,paddlefish,Ancient freshwater fish,Fresh Water,river,Spring | Summer,Day,Required,Any,Epic,0.1,10,120,34,76,0.82,,,,"paddlefish cruise open rivers with their mouths wide, filtering tiny plankton from the water. the broad snout senses faint electrical signals.",inactive
+105,shovelnose sturgeon,Ancient freshwater fish,Fresh Water,river,Spring | Summer | Fall | Winter,Night,Any,Any,Rare,0.22,1,8,12,25,0.9,,,,shovelnose sturgeon hug flowing river bottoms and probe for insect larvae. a flattened snout helps them hold position in strong current.,inactive
+106,spotted gar,Ancient freshwater fish,Fresh Water,pond,Spring | Summer | Fall,Day,Any,Required,Rare,0.2,1,15,13,29,0.88,,,,spotted gar linger among vegetation where their mottled bodies break up their outline. they rise to gulp air as well as breathing through gills.,inactive
+107,West African lungfish,Ancient freshwater fish,Fresh Water,pond,Summer | Fall,Night,Required,Any,Rare,0.3,1,10,12,28,0.86,,,,"west african lungfish breathe air with paired lungs. when floodplains dry, they seal themselves in a mud cocoon until water returns.",inactive
+201,atlantic mudskipper,Brackish oddities,Salt Water,ocean,Spring | Summer | Fall | Winter,Day,Required,Any,Uncommon,1,0.03,0.5,5,11,0.95,,,,atlantic mudskippers use sturdy fins to hop and crawl across exposed mud. moist skin and mouth linings let them breathe out of water.,inactive
+202,largescale foureyes,Brackish oddities,Fresh Water | Salt Water,river,Spring | Summer | Fall | Winter,Day,Any,Any,Rare,0.32,0.05,1,10,23,0.95,,,,largescale foureyes cruise with each eye divided across the waterline. one half watches above while the other scans below.,inactive
+301,red mullet,Coastal bottom fish,Salt Water,ocean,Spring | Summer | Fall,Day,Any,Any,Uncommon,0.9,0.1,3,5,11,0.92,,,,red mullet comb sandy bottoms with two chin barbels that taste hidden prey. their foraging leaves winding trails in the sediment.,inactive
+401,European plaice,Coastal flatfish,Salt Water,ocean,Spring | Fall | Winter,Day,Any,Any,Common,1.8,0.2,10,4,10,0.9,,,,european plaice lie flat on the seafloor with both eyes on the same side. orange spots help distinguish them from neighboring flatfish.,inactive
+402,summer flounder,Coastal flatfish,Salt Water,ocean,Spring | Summer | Fall,Day,Any,Any,Uncommon,0.9,0.3,15,6,16,0.88,,,,summer flounder bury themselves in sand with only their eyes exposed. a sudden burst carries them upward to seize passing prey.,inactive
+403,winter flounder,Coastal flatfish,Salt Water,ocean,Spring | Fall | Winter,Day,Forbidden,Any,Common,1.8,0.2,8,4,9,0.9,,,,winter flounder favor cool bays and estuaries where they rest on muddy bottoms. their eyes sit on the right side of the body.,inactive
+501,dugong,Coastal marine mammals,Salt Water,ocean,Spring | Summer | Fall | Winter,Day,Any,Forbidden,Legendary,0.011,500,1100,72,165,0.77,,,,"dugongs graze seagrass meadows with a downturned, bristled snout. they are the only fully marine members of the sirenian family.",inactive
+502,harbor seal,Coastal marine mammals,Salt Water,ocean,Spring | Summer | Fall | Winter,Day,Any,Required,Legendary,0.018,120,370,60,140,0.79,,,,harbor seals rest on rocky shores and sandbars between hunting trips. sensitive whiskers can trace the water trails left by moving fish.,inactive
+503,sea otter,Coastal marine mammals,Salt Water,ocean,Spring | Summer | Fall | Winter,Day,Any,Any,Legendary,0.02,30,100,55,125,0.84,,,,sea otters float on their backs while opening shellfish with stones. dense fur traps insulating air because they lack a thick blubber layer.,inactive
+504,walrus,Coastal marine mammals,Salt Water,ocean,Spring | Winter,Day,Any,Required,Legendary,0.012,800,3700,74,170,0.76,,,,walruses sweep the seafloor for clams and other soft-bodied prey. long tusks help them haul onto ice and settle disputes.,inactive
+505,West Indian manatee,Coastal marine mammals,Salt Water,ocean,Spring | Summer | Fall | Winter,Day,Any,Forbidden,Legendary,0.012,440,1300,70,160,0.77,,,,west indian manatees graze aquatic plants in warm coastal water and rivers. they surface regularly to breathe through nostrils atop the snout.,inactive
+601,bay pipefish,Coastal oddities,Salt Water,ocean,Spring | Summer | Fall | Winter,Day,Any,Any,Uncommon,0.9,0.005,0.04,4,9,1,,,,"bay pipefish hide upright among seagrass, matching the blades with their slender bodies. males carry developing eggs in a brood pouch.",inactive
+602,lined seahorse,Coastal oddities,Salt Water,ocean,Spring | Summer | Fall | Winter,Day,Forbidden,Any,Rare,0.28,0.005,0.05,9,20,1,,,,lined seahorses anchor to grass and coral with curling tails. the male broods eggs in a pouch and releases fully formed young.,inactive
+603,monkfish,Coastal oddities,Salt Water,ocean,Spring | Fall | Winter,Night,Any,Required,Rare,0.25,2,55,14,35,0.84,,,,monkfish hide against the bottom and wave a fleshy lure above a cavernous mouth. a sudden gulp draws fish and crustaceans inward.,inactive
+604,tripletail,Coastal oddities,Salt Water,ocean,Summer | Fall,Day,Any,Required,Rare,0.28,1,30,13,31,0.85,,,,"tripletail often drift sideways near floating debris, resembling a loose leaf. rounded rear fins create the three-tailed silhouette.",inactive
+701,atlantic mackerel,Coastal pelagic fish,Salt Water,ocean,Spring | Summer | Fall,Any,Any,Any,Common,0.5,0.5,5,4,9,0.9,,,,atlantic mackerel travel in fast schools across the north atlantic and feed on plankton and small schooling fish.,active
+702,bluefish,Coastal pelagic fish,Salt Water,ocean,Spring | Summer | Fall,Day,Any,Any,Uncommon,0.85,0.5,25,7,19,0.85,,,,bluefish race in coordinated schools while pursuing smaller fish. sharp teeth and powerful jaws make their feeding bursts especially fierce.,inactive
+703,cero mackerel,Coastal pelagic fish,Salt Water,ocean,Spring | Summer | Fall | Winter,Any,Any,Any,Uncommon,0.35,1,15,7,15,0.9,,,,cero mackerel are streamlined coastal hunters. sharp teeth help them slash through schools of smaller fish.,active
+704,chub mackerel,Coastal pelagic fish,Salt Water,ocean,Spring | Summer | Fall,Any,Any,Any,Common,0.5,0.5,4,4,9,0.9,,,,chub mackerel school near the surface and follow plankton blooms. their dark wavy bars help identify them at a glance.,active
+705,crevalle jack,Coastal pelagic fish,Salt Water,ocean,Summer | Fall,Day,Required,Any,Uncommon,0.8,1,35,7,20,0.84,,,,"crevalle jacks patrol warm coasts in fast-moving schools. their deep bodies and forked tails deliver long, powerful runs.",inactive
+706,king mackerel,Coastal pelagic fish,Salt Water,ocean,Summer | Fall,Any,Any,Any,Uncommon,0.3,3,40,10,24,0.9,,,,king mackerel are swift coastal predators. their pointed teeth and long body are built for sudden bursts of speed.,active
+707,spanish mackerel,Coastal pelagic fish,Salt Water,ocean,Summer | Fall,Any,Any,Any,Uncommon,0.35,1,15,8,18,0.9,,,,spanish mackerel hunt in warm coastal waters and often travel in loose schools while chasing baitfish.,active
+708,talang queenfish,Coastal pelagic fish,Salt Water,ocean,Spring | Summer | Fall | Winter,Day,Any,Any,Uncommon,0.8,0.5,20,7,18,0.86,,,,talang queenfish chase schooling prey near warm coasts and estuaries. hooked teeth and a deeply forked tail suit their rapid attacks.,inactive
+801,American sand lance,Coastal schooling fish,Salt Water,ocean,Spring | Fall | Winter,Day,Any,Any,Common,3,0.02,0.4,1,3,1,,,,american sand lance dive headfirst into loose sand when danger approaches. immense schools carry energy from plankton to larger predators.,inactive
+802,atlantic menhaden,Coastal schooling fish,Salt Water,ocean,Spring | Summer | Fall,Day,Any,Any,Common,3,0.1,1.5,2,5,0.98,,,,"atlantic menhaden strain plankton from coastal water with fine gill rakers. dense schools feed whales, seabirds, and predatory fish.",inactive
+803,black pomfret,Coastal schooling fish,Salt Water,ocean,Spring | Summer | Fall | Winter,Any,Any,Any,Uncommon,0.3,1,15,8,18,0.9,,,,"black pomfret have a deep, laterally compressed body that lets them maneuver neatly through open water.",active
+804,chinese pomfret,Coastal schooling fish,Salt Water,ocean,Spring | Summer | Fall | Winter,Any,Any,Any,Uncommon,0.3,1,20,9,20,0.9,,,,"chinese pomfret are silvery schooling fish with a tall, flattened body and long sickle-shaped fins.",active
+805,european anchovy,Coastal schooling fish,Salt Water,ocean,Spring | Summer | Fall,Any,Any,Any,Common,0.5,0.05,0.25,2,4,0.9,,,,european anchovies form dense schools and feed by filtering tiny plankton from coastal water.,active
+806,European pilchard,Coastal schooling fish,Salt Water,ocean,Spring | Fall | Winter,Any,Any,Any,Common,3.5,0.05,0.6,2,4,1,,,,european pilchards form shimmering schools along atlantic and mediterranean coasts. they feed mainly on plankton near the surface.,inactive
+807,golden pomfret,Coastal schooling fish,Salt Water,ocean,Spring | Summer | Fall | Winter,Any,Any,Any,Rare,0.18,2,25,14,30,0.9,,,,golden pomfret are warm-water swimmers whose golden sheen is most noticeable along the fins and flanks.,active
+808,northern anchovy,Coastal schooling fish,Salt Water,ocean,Spring | Summer | Fall | Winter,Any,Any,Any,Common,0.5,0.05,0.35,2,4,0.9,,,,northern anchovies gather in huge schools along the pacific coast and filter plankton with their fine gill rakers.,active
+809,Pacific sardine,Coastal schooling fish,Salt Water,ocean,Spring | Summer | Fall | Winter,Any,Any,Any,Common,3.5,0.05,0.5,2,4,1,,,,"pacific sardines follow cool, productive water in enormous schools. their movements shift as currents and plankton supplies change.",inactive
+810,white pomfret,Coastal schooling fish,Salt Water,ocean,Spring | Summer | Fall | Winter,Any,Any,Any,Common,0.45,0.5,10,5,12,0.9,,,,"white pomfret gather over coastal grounds and use their compact, deep bodies to turn quickly while feeding.",active
+901,blacktip shark,Coastal sharks,Salt Water,ocean,Summer | Fall,Day,Required,Any,Epic,0.1,15,150,32,75,0.8,,,,blacktip sharks are named for the dark markings on their fins. they hunt schooling fish in warm shallows and sometimes leap clear of the water.,inactive
+902,great hammerhead shark,Coastal sharks,Salt Water,ocean,Summer | Fall,Day,Any,Forbidden,Legendary,0.035,50,1000,62,145,0.78,,,,great hammerheads sweep their wide heads over the bottom while searching for rays. widely spaced senses help pinpoint hidden prey.,inactive
+903,great white shark,Coastal sharks,Salt Water,ocean,Spring | Summer | Fall | Winter,Day,Any,Any,Legendary,0.025,100,1000,70,150,0.76,,,,great white sharks conserve heat in their swimming muscles while crossing cool seas. rows of serrated teeth are replaced throughout life.,inactive
+904,tiger shark,Coastal sharks,Salt Water,ocean,Spring | Summer | Fall | Winter,Night,Required,Any,Epic,0.06,50,1000,50,120,0.8,,,,tiger sharks carry dark vertical bars that fade as they mature. broad jaws let them investigate an unusually varied range of prey.,inactive
+905,whale shark,Coastal sharks,Salt Water,ocean,Summer | Fall,Day,Any,Forbidden,Legendary,0.025,500,45000,70,160,0.76,,,,whale sharks are enormous filter feeders covered in unique spot patterns. they cruise with open mouths to collect plankton and tiny fish.,inactive
+1001,burbot,Cold freshwater fish,Fresh Water,lake,Spring | Fall | Winter,Night,Any,Required,Rare,0.24,0.5,18,12,28,0.86,,,,burbot are the only freshwater members of the cod family. they become most active in cold darkness and may spawn beneath winter ice.,inactive
+1002,cisco,Cold freshwater fish,Fresh Water,lake,Spring | Fall | Winter,Day,Any,Any,Common,2.2,0.1,3,3,7,0.95,,,,cisco school in the cool open water of northern lakes. these slim relatives of salmon feed on plankton and become prey for larger fish.,inactive
+1003,lake whitefish,Cold freshwater fish,Fresh Water,lake,Spring | Fall | Winter,Any,Any,Any,Common,1.5,0.5,12,4,11,0.9,,,,"lake whitefish browse deep, cold bottoms with small downward-pointing mouths. large schools move toward shallows to spawn in autumn.",inactive
+1101,arctic char,Cold freshwater salmonids,Fresh Water | Salt Water,lake,Spring | Fall | Winter,Any,Forbidden,Any,Rare,0.3,1,25,14,32,0.86,,,,arctic char thrive farther north than any other freshwater fish. some remain in lakes while others migrate between rivers and the sea.,inactive
+1102,arctic grayling,Cold freshwater salmonids,Fresh Water,river,Spring | Fall | Winter,Day,Any,Forbidden,Uncommon,0.75,0.3,6,6,14,0.9,,,,"arctic grayling carry a tall, colorful dorsal fin like a sail. they feed on drifting insects in cold, clear rivers and lakes.",inactive
+1103,brown trout,Cold freshwater salmonids,Fresh Water,river,Spring | Summer | Fall | Winter,Night,Any,Any,Uncommon,0.8,0.5,25,7,20,0.86,,,,"brown trout often shelter by day and hunt more freely after dark. adaptable populations occupy streams, lakes, and even coastal water.",inactive
+1104,cutthroat trout,Cold freshwater salmonids,Fresh Water,river,Spring | Fall | Winter,Any,Any,Any,Uncommon,0.4,0.25,10,5,12,0.9,,,,"cutthroat trout take their name from the red slash beneath the jaw. many populations rely on cold, well-oxygenated streams.",active
+1105,golden trout,Cold freshwater salmonids,Fresh Water,lake,Spring | Summer | Fall,Any,Any,Any,Rare,0.2,0.2,3,8,16,0.9,,,,"golden trout evolved in clear, high-elevation california streams. their bright flanks stand out against rocky alpine water.",active
+1106,rainbow trout,Cold freshwater salmonids,Fresh Water,river,Spring | Summer | Fall | Winter,Any,Any,Any,Uncommon,0.45,0.5,20,5,11,0.9,,,,rainbow trout are adaptable stream hunters. the pink lateral stripe is especially vivid when the fish is in breeding condition.,active
+1201,atlantic wolffish,Cold reef fish,Salt Water,ocean,Spring | Fall | Winter,Any,Forbidden,Any,Rare,0.25,2,40,14,34,0.84,,,,atlantic wolffish use heavy jaws and blunt teeth to crush shellfish and urchins. natural antifreeze proteins help them endure icy water.,inactive
+1202,cusk,Cold reef fish,Salt Water,ocean,Spring | Fall | Winter,Night,Any,Any,Uncommon,0.75,0.5,30,7,19,0.85,,,,"cusk move slowly across cold rocky bottoms, sheltering in cracks and caves. a single long fin runs around much of the rear body.",inactive
+1203,spotted wolffish,Cold reef fish,Salt Water,ocean,Spring | Fall | Winter,Any,Any,Required,Epic,0.11,3,50,26,60,0.84,,,,spotted wolffish crush hard-shelled prey with broad molar-like teeth. their pale spots stand out against a dark cold-water body.,inactive
+1301,atlantic herring,Cold schooling fish,Salt Water,ocean,Spring | Fall | Winter,Any,Any,Any,Common,3,0.1,1.5,2,5,0.98,,,,atlantic herring gather in immense schools that move between feeding and spawning grounds. they filter plankton with fine gill rakers.,inactive
+1302,capelin,Cold schooling fish,Salt Water,ocean,Spring | Fall | Winter,Any,Any,Required,Uncommon,1,0.03,0.2,4,8,1,,,,"capelin are small polar fish that feed whales, seabirds, and larger fish. many schools roll onto beaches or shallows to release their eggs.",inactive
+1303,European sprat,Cold schooling fish,Salt Water,ocean,Spring | Fall | Winter,Any,Any,Any,Common,3.2,0.03,0.3,1,3,1,,,,european sprat form compact schools in northern coastal seas. these tiny herring relatives feed on plankton near the surface.,inactive
+1304,pacific herring,Cold schooling fish,Salt Water,ocean,Spring | Fall | Winter,Any,Required,Any,Common,2.8,0.1,1.5,2,5,0.98,,,,pacific herring return to coastal shallows to coat plants and rocks with eggs. their oily schools support entire marine food webs.,inactive
+1401,Alaska pollock,Cold shelf fish,Salt Water,ocean,Spring | Fall | Winter,Any,Any,Any,Common,2.5,0.3,8,3,8,0.92,,,,alaska pollock form vast schools over cold northern shelves. they feed on plankton and fish while supporting many larger predators.,inactive
+1402,atlantic cod,Cold shelf fish,Salt Water,ocean,Spring | Fall | Winter,Any,Any,Any,Common,1.8,1,55,5,18,0.85,,,,"atlantic cod carry a pale lateral line and one chin barbel. they search cold seafloors for crustaceans, mollusks, and smaller fish.",inactive
+1403,Atlantic pollock,Cold shelf fish,Salt Water,ocean,Spring | Fall | Winter,Day,Any,Forbidden,Uncommon,0.9,1,35,7,18,0.86,,,,atlantic pollock are swift relatives of cod with dark backs and pale lateral lines. adults gather around rocky banks and wrecks.,inactive
+1404,Dover sole,Cold shelf fish,Salt Water,ocean,Spring | Fall | Winter,Night,Any,Any,Uncommon,0.8,0.2,8,6,14,0.9,,,,dover sole glide over deep muddy bottoms of the eastern pacific. both eyes migrate to the right side as the young fish becomes flat.,inactive
+1405,haddock,Cold shelf fish,Salt Water,ocean,Spring | Fall | Winter,Day,Any,Any,Common,2,0.5,20,4,11,0.9,,,,haddock are recognized by a dark shoulder mark and black lateral line. schools forage over cold seafloors for small bottom animals.,inactive
+1406,pacific cod,Cold shelf fish,Salt Water,ocean,Spring | Fall | Winter,Any,Any,Any,Common,1.8,1,45,5,17,0.85,,,,pacific cod use a chin barbel to investigate cold shelf bottoms. seasonal schools follow prey between deeper water and coastal spawning grounds.,inactive
+1407,pacific hake,Cold shelf fish,Salt Water,ocean,Spring | Fall | Winter,Night,Any,Any,Common,1.7,0.3,8,3,9,0.92,,,,pacific hake rise from deep water at night to feed on krill and fish. large schools migrate along the west coast with changing seasons.,inactive
+1408,silver hake,Cold shelf fish,Salt Water,ocean,Spring | Fall | Winter,Night,Any,Any,Common,1.7,0.2,5,3,8,0.92,,,,silver hake hunt fish and crustaceans over sandy atlantic shelves. they move upward after dark and return toward the bottom by day.,inactive
+1501,chambered nautilus,Cuttlefish and nautiluses,Salt Water,ocean,Spring | Summer | Fall | Winter,Night,Any,Required,Legendary,0.05,0.5,3,34,76,0.92,,,,chambered nautiluses regulate buoyancy with gas-filled shell chambers. dozens of small tentacles feel for food in dark reef slopes.,inactive
+1502,common cuttlefish,Cuttlefish and nautiluses,Salt Water,ocean,Spring | Summer | Fall | Winter,Day,Any,Any,Uncommon,0.55,0.2,12,7,17,0.9,,,,common cuttlefish flash changing colors and patterns through specialized skin cells. an internal cuttlebone controls buoyancy.,inactive
+1503,flamboyant cuttlefish,Cuttlefish and nautiluses,Salt Water,ocean,Spring | Summer | Fall | Winter,Day,Required,Any,Epic,0.1,0.05,0.4,26,58,0.96,,,,flamboyant cuttlefish walk along the seafloor on their arms and fins. vivid displays warn predators of potent toxins.,inactive
+1601,blueline tilefish,Deep shelf fish,Salt Water,ocean,Spring | Summer | Fall | Winter,Day,Any,Any,Uncommon,0.7,1,20,7,18,0.86,,,,blueline tilefish shelter in burrows and rocky openings along deep shelves. a narrow blue stripe traces the edge of the dorsal fin.,inactive
+1602,golden tilefish,Deep shelf fish,Salt Water,ocean,Spring | Summer | Fall | Winter,Day,Any,Any,Rare,0.22,2,50,15,37,0.84,,,,golden tilefish excavate burrows in soft clay along deep ocean slopes. their bright yellow spots contrast with blue-green backs.,inactive
+1603,Pacific ocean perch,Deep shelf fish,Salt Water,ocean,Spring | Summer | Fall | Winter,Any,Any,Any,Uncommon,0.8,0.3,12,7,17,0.88,,,,pacific ocean perch hover around deep rocky reefs in the north pacific. these slow-growing fish give birth to live young.,inactive
+1604,Patagonian toothfish,Deep shelf fish,Salt Water,ocean,Spring | Summer | Fall | Winter,Night,Any,Any,Epic,0.09,10,220,32,78,0.8,,,,"patagonian toothfish patrol cold, deep southern water with large canine teeth. natural antifreeze proteins protect their blood from freezing.",inactive
+1605,sablefish,Deep shelf fish,Salt Water,ocean,Spring | Summer | Fall | Winter,Night,Any,Any,Rare,0.22,2,40,15,36,0.84,,,,"sablefish roam deep north pacific slopes and can live for many decades. soft dark bodies conceal rich, oil-filled flesh.",inactive
+1606,wreckfish,Deep shelf fish,Salt Water,ocean,Spring | Summer | Fall | Winter,Any,Any,Required,Epic,0.1,10,180,31,76,0.8,,,,"wreckfish gather around caves, ledges, and shipwrecks in deep water. powerful jaws let them ambush fish and large crustaceans.",inactive
+1701,giant oarfish,Deep-sea legends,Salt Water,ocean,Spring | Summer | Fall | Winter,Night,Any,Required,Legendary,0.035,20,600,60,140,0.78,,,,"giant oarfish drift vertically through deep open water, rowing with a crimson dorsal fin. their ribbon-like bodies can reach remarkable lengths.",inactive
+1702,West Indian Ocean coelacanth,Deep-sea legends,Salt Water,ocean,Spring | Summer | Fall | Winter,Night,Any,Required,Legendary,0.03,20,200,65,145,0.8,,,,"west indian ocean coelacanths shelter in deep volcanic caves by day. fleshy lobed fins move in an alternating, limb-like rhythm.",inactive
+1801,black seadevil,Deep-sea oddities,Salt Water,ocean,Spring | Summer | Fall | Winter,Night,Any,Any,Legendary,0.05,0.1,2,48,105,0.92,,,,black seadevils lure prey with a glowing organ suspended above the mouth. tiny males permanently attach to much larger females.,inactive
+1802,common fangtooth,Deep-sea oddities,Salt Water,ocean,Spring | Summer | Fall | Winter,Night,Any,Any,Epic,0.1,0.05,0.5,25,56,0.96,,,,common fangtooths carry outsized teeth that fit into pockets in the roof of the mouth. they migrate upward from deep water after dark.,inactive
+1803,gulper eel,Deep-sea oddities,Salt Water,ocean,Spring | Summer | Fall | Winter,Night,Any,Required,Legendary,0.04,0.5,18,44,92,0.86,,,,gulper eels unfold enormous mouths to engulf prey in the deep sea. a whip-like tail ends in a small light-producing organ.,inactive
+1804,Pacific barreleye,Deep-sea oddities,Salt Water,ocean,Spring | Summer | Fall | Winter,Night,Any,Required,Legendary,0.045,0.2,2.5,42,88,0.9,,,,pacific barreleyes peer upward through transparent shields covering the head. tubular eyes rotate forward when the fish turns to feed.,inactive
+1805,smooth-head blobfish,Deep-sea oddities,Salt Water,ocean,Spring | Summer | Fall | Winter,Night,Any,Required,Legendary,0.04,0.5,5,42,90,0.86,,,,smooth-head blobfish rest on deep slopes where pressure supports their soft bodies. they conserve energy by waiting for edible matter nearby.,inactive
+1806,spotted lanternfish,Deep-sea oddities,Salt Water,ocean,Spring | Summer | Fall | Winter,Night,Any,Any,Rare,0.24,0.05,0.5,14,30,0.95,,,,spotted lanternfish carry rows of light-producing organs along their bodies. immense schools rise toward the surface each night.,inactive
+1901,crown-of-thorns sea star,Echinoderms,Salt Water,ocean,Spring | Summer | Fall | Winter,Night,Required,Any,Epic,0.1,0.5,7,23,52,0.91,,,,crown-of-thorns sea stars crawl over reefs and digest coral tissue outside the body. venomous spines discourage many predators.,inactive
+1902,giant California sea cucumber,Echinoderms,Salt Water,ocean,Spring | Summer | Fall | Winter,Night,Any,Required,Rare,0.24,0.5,5,13,29,0.93,,,,giant california sea cucumbers sweep organic particles from the seafloor with branching tentacles. soft tube feet anchor their long bodies.,inactive
+1903,purple sea urchin,Echinoderms,Salt Water,ocean,Spring | Summer | Fall | Winter,Day,Any,Any,Uncommon,0.75,0.05,1,5,12,0.96,,,,purple sea urchins scrape algae from rocks with a five-toothed feeding structure. dense groups can clear kelp from entire reefs.,inactive
+1904,sand dollar,Echinoderms,Salt Water,ocean,Spring | Summer | Fall | Winter,Day,Any,Forbidden,Uncommon,0.8,0.01,0.2,4,10,0.98,,,,sand dollars burrow just beneath sandy seafloors and filter tiny food particles. the familiar star pattern marks their five-part body plan.,inactive
+1905,sunflower sea star,Echinoderms,Salt Water,ocean,Spring | Summer | Fall | Winter,Day,Any,Required,Rare,0.25,0.5,13,14,32,0.89,,,,"sunflower sea stars move quickly on thousands of tube feet and may carry many arms. they hunt urchins, clams, and other bottom animals.",inactive
+2001,axolotl,Freshwater amphibians,Fresh Water,lake,Spring | Summer | Fall,Night,Any,Required,Legendary,0.04,0.1,0.7,36,78,0.96,,,,axolotls remain aquatic and keep feathery external gills throughout adulthood. lost limbs and parts of several organs can regenerate.,inactive
+2002,hellbender,Freshwater amphibians,Fresh Water,river,Spring | Summer | Fall,Night,Required,Any,Epic,0.1,0.5,5.5,24,54,0.91,,,,"hellbenders hide beneath large rocks in cool, clean rivers. folds of loose skin absorb oxygen while the flattened body grips the current.",inactive
+2101,guppy,Freshwater aquarium fish,Fresh Water,pond,Spring | Summer | Fall | Winter,Day,Any,Any,Common,1.5,0.01,0.15,2,5,0.98,,,,"guppies are livebearers whose young arrive ready to swim. males display bright colors while females often favor larger, less flashy bodies.",inactive
+2102,neon tetra,Freshwater aquarium fish,Fresh Water,pond,Spring | Summer | Fall | Winter,Night,Any,Any,Uncommon,0.9,0.01,0.08,4,8,0.98,,,,"neon tetras school through shaded forest streams. an iridescent blue stripe remains visible in the dim, tannin-stained water.",inactive
+2103,zebrafish,Freshwater aquarium fish,Fresh Water,pond,Spring | Summer | Fall | Winter,Day,Any,Any,Common,1.35,0.01,0.12,2,5,0.98,,,,"zebrafish carry horizontal blue stripes from gill to tail. hardy schools thrive in shallow streams, flooded fields, and quiet pools.",inactive
+2201,largemouth bass,Freshwater bass,Fresh Water,pond,Spring | Summer | Fall,Any,Any,Any,Common,2.5,0.5,18,4,12,0.9,,,,largemouth bass wait among weeds and timber before lunging at prey. the upper jaw extends beyond the eye when the mouth is closed.,inactive
+2202,smallmouth bass,Freshwater bass,Fresh Water,river,Spring | Summer | Fall,Day,Any,Any,Uncommon,1.2,0.5,12,6,15,0.9,,,,smallmouth bass favor clear water with rock and moving current. bronze flanks and dark vertical bars distinguish them from largemouth bass.,inactive
+2301,bigmouth buffalo,Freshwater bottom fish,Fresh Water,river,Spring | Summer | Fall,Any,Any,Required,Epic,0.12,8,80,28,63,0.82,,,,bigmouth buffalo filter plankton with fine gill rakers instead of rooting for prey. some individuals survive for more than a century.,inactive
+2302,golden redhorse,Freshwater bottom fish,Fresh Water,river,Spring | Summer | Fall,Day,Any,Any,Uncommon,0.9,0.5,8,6,14,0.9,,,,"golden redhorses probe flowing river bottoms with fleshy, downward-facing mouths. coppery scales and reddish fins give them a warm glow.",inactive
+2303,quillback,Freshwater bottom fish,Fresh Water,river,Spring | Summer | Fall,Day,Any,Any,Uncommon,0.8,0.5,15,6,16,0.88,,,,quillbacks carry a long first dorsal ray that rises like a quill. small mouths gather insect larvae and other food from river and lake bottoms.,inactive
+2304,smallmouth buffalo,Freshwater bottom fish,Fresh Water,river,Spring | Summer | Fall,Any,Any,Any,Rare,0.3,3,45,14,34,0.84,,,,"smallmouth buffalo use thick lips to graze bottom-dwelling food in warm rivers. their deep, heavy bodies form a pronounced arch behind the head.",inactive
+2305,white sucker,Freshwater bottom fish,Fresh Water,river,Spring | Summer | Fall,Any,Any,Any,Common,2,0.3,8,3,8,0.9,,,,white suckers vacuum insects and organic matter from cool freshwater bottoms. spring runs carry large groups into shallow streams.,inactive
+2401,black bullhead,Freshwater catfish,Fresh Water,pond,Spring | Summer | Fall,Night,Any,Any,Uncommon,1,0.3,6,5,11,0.92,,,,"black bullheads tolerate warm, cloudy water with little oxygen. stout spines protect the dorsal and pectoral fins from predators.",inactive
+2402,blue catfish,Freshwater catfish,Fresh Water,river,Spring | Summer | Fall,Night,Any,Any,Uncommon,1.25,3,12,6,9,0.9,,,,a deep fork in the tail inspired the blue catfish's scientific name. it rests deep by daylight and becomes more active after dark.,active
+2403,brown bullhead,Freshwater catfish,Fresh Water,pond,Spring | Summer | Fall,Night,Any,Any,Common,2.3,0.2,4,3,7,0.95,,,,brown bullheads search muddy bottoms with eight sensitive barbels. males guard nests and herd newly hatched young in a dark moving ball.,inactive
+2404,channel catfish,Freshwater catfish,Fresh Water,pond,Spring | Summer | Fall,Night,Any,Any,Common,2,1.5,7,4,7,1,,,,"a channel catfish can taste through skin all over its body, especially near its gills and whiskers. the male watches the eggs.",active
+2405,flathead catfish,Freshwater catfish,Fresh Water,river,Spring | Summer | Fall,Night,Any,Any,Uncommon,1,3,14,7,10,0.9,,,,"flatheads favor hideouts made by logs, rocks, ledges, and other underwater structure. as they grow, fish dominate their menu.",active
+2406,tadpole madtom,Freshwater catfish,Fresh Water,river,Spring | Summer | Fall,Night,Any,Any,Rare,0.35,0.02,0.15,8,16,0.95,,,,tadpole madtoms are tiny nocturnal catfish that shelter beneath vegetation and debris. venomous fin spines discourage larger predators.,inactive
+2407,white catfish,Freshwater catfish,Fresh Water,pond,Spring | Summer | Fall,Night,Any,Any,Common,2,1,5,4,6,1,,,,white catfish began along the atlantic coast between new york and florida. people later carried them far beyond that home range.,active
+2408,yellow bullhead,Freshwater catfish,Fresh Water,pond,Spring | Summer | Fall,Night,Required,Any,Common,2,0.2,3.5,3,7,0.95,,,,yellow bullheads use pale chin barbels to distinguish food in murky shallows. both parents may guard the nest and schooling young.,inactive
+2501,common barbel,Freshwater coarse fish,Fresh Water,river,Spring | Summer | Fall,Night,Required,Any,Rare,0.28,1,25,13,31,0.85,,,,common barbels hold close to fast river bottoms with streamlined bodies and broad fins. four mouth barbels locate food among stones.,inactive
+2502,common bream,Freshwater coarse fish,Fresh Water,lake,Spring | Fall | Winter,Any,Any,Any,Common,1.8,0.3,15,4,12,0.88,,,,"common bream gather in slow rivers and lakes with silvery, deep-sided bodies. protruding mouths sift insect larvae from soft bottoms.",inactive
+2503,common carp,Freshwater coarse fish,Fresh Water,pond,Spring | Summer | Fall | Winter,Any,Any,Any,Rare,1.5,3,18,10,10,0.85,,,,whisker-like barbels help a common carp investigate the bottom. nosing through sediment can cloud the water and loosen plants.,active
+2504,common roach,Freshwater coarse fish,Fresh Water,pond,Spring | Summer | Fall | Winter,Any,Any,Any,Common,2.8,0.1,4,2,6,0.95,,,,"common roach thrive in many lakes and rivers, often schooling near vegetation. red fins and a red-orange eye brighten their silver bodies.",inactive
+2505,european chub,Freshwater coarse fish,Fresh Water,river,Spring | Summer | Fall | Winter,Any,Any,Any,Common,0.55,0.2,6,2,4,0.9,,,,"european chub are adaptable river fish. adults often patrol shallows and feed on insects, small fish, and fruit.",active
+2506,fallfish,Freshwater coarse fish,Fresh Water,river,Spring | Summer | Fall,Day,Required,Any,Uncommon,0.9,0.2,3.5,5,10,0.92,,,,fallfish are large minnows that build broad stone nests in flowing water. other stream fish often reuse the gravel mounds for spawning.,inactive
+2507,ide,Freshwater coarse fish,Fresh Water,river,Spring | Summer | Fall,Day,Any,Any,Uncommon,0.8,0.3,9,6,14,0.9,,,,"ide move between rivers, lakes, and brackish coastal water in loose schools. adults eat insects, crustaceans, plants, and small fish.",inactive
+2508,rudd,Freshwater coarse fish,Fresh Water,pond,Spring | Summer | Fall,Day,Any,Any,Common,2.4,0.1,4.5,3,7,0.95,,,,"rudd feed near the surface with an upturned mouth and red fins. golden-green bodies disappear easily among warm, weedy shallows.",inactive
+2509,tench,Freshwater coarse fish,Fresh Water,pond,Spring | Summer | Fall,Night,Any,Required,Uncommon,0.75,0.5,12,7,17,0.88,,,,"tench browse quiet, muddy bottoms with two small mouth barbels. a thick coat of slime protects their olive-green scales.",inactive
+2601,red swamp crayfish,Freshwater crustaceans,Fresh Water,pond,Spring | Summer | Fall,Night,Required,Any,Common,1.1,0.03,0.25,3,7,0.98,,,,"red swamp crayfish burrow into wet ground when water recedes. adaptable populations feed on plants, carrion, and small aquatic animals.",inactive
+2701,alligator gar,Freshwater giants,Fresh Water,river,Spring | Summer | Fall,Night,Any,Any,Legendary,0.055,20,300,55,125,0.8,,,,alligator gar combine a broad tooth-filled snout with heavy armor-like scales. a vascular swim bladder lets them breathe air at the surface.,inactive
+2702,arapaima,Freshwater giants,Fresh Water,river,Spring | Summer | Fall | Winter,Day,Any,Any,Legendary,0.04,40,400,60,140,0.78,,,,arapaima must surface frequently to gulp air through a specialized swim bladder. large armored scales protect their long river-going bodies.,inactive
+2703,goliath tigerfish,Freshwater giants,Fresh Water,river,Spring | Summer | Fall | Winter,Day,Any,Any,Legendary,0.035,20,150,58,132,0.8,,,,goliath tigerfish patrol powerful african rivers with interlocking dagger-like teeth. a muscular forked tail drives sudden attacks.,inactive
+2704,muskellunge,Freshwater giants,Fresh Water,lake,Spring | Fall | Winter,Day,Any,Required,Epic,0.11,8,60,32,72,0.82,,,,muskellunge wait motionless among weeds before exploding toward prey. pale bodies carry bars or spots that fade into broken underwater light.,inactive
+2801,common shiner,Freshwater minnows,Fresh Water,river,Spring | Summer | Fall,Day,Any,Any,Common,4,0.03,0.3,1,3,1,,,,common shiners flash silver in clear streams and small rivers. breeding males develop rosy fins and a blue-violet sheen.,inactive
+2802,creek chub,Freshwater minnows,Fresh Water,river,Spring | Summer | Fall,Day,Any,Any,Common,3.2,0.05,0.8,2,4,1,,,,creek chubs gather around pools and undercut banks in small streams. males build gravel ridges where several fish may spawn.,inactive
+2803,emerald shiner,Freshwater minnows,Fresh Water,lake,Spring | Summer | Fall,Any,Any,Any,Common,3.5,0.03,0.25,1,3,1,,,,emerald shiners form bright surface schools in large lakes and rivers. slender bodies feed on plankton and drifting insects.,inactive
+2804,fathead minnow,Freshwater minnows,Fresh Water,pond,Spring | Summer | Fall,Day,Any,Any,Common,4.5,0.02,0.2,1,2,1,,,,"fathead minnows tolerate warm, muddy ponds where many fish struggle. males guard eggs laid beneath rocks, logs, and other cover.",inactive
+2805,flame chub,Freshwater minnows,Fresh Water,river,Spring | Summer | Fall,Any,Any,Any,Uncommon,0.4,0.05,0.15,3,6,0.9,,,,"flame chubs prefer cool, clear streams. their bright breeding colors make a small fish easy to spot in spring water.",active
+2806,golden shiner,Freshwater minnows,Fresh Water,pond,Spring | Summer | Fall,Day,Any,Any,Common,3.8,0.05,1,2,4,1,,,,golden shiners school around dense vegetation in warm water. an upturned mouth takes insects and plankton from near the surface.,inactive
+2807,lake chub,Freshwater minnows,Fresh Water,lake,Spring | Fall | Winter,Any,Any,Any,Common,0.55,0.05,0.5,2,4,0.9,,,,lake chubs school in cold northern water and use their small mouths to pick insects and other tiny prey from the current.,active
+2901,electric eel,Freshwater oddities,Fresh Water,river,Spring | Summer | Fall | Winter,Night,Required,Any,Epic,0.1,1,44,25,60,0.82,,,,electric eels navigate and hunt with electrical pulses produced along most of the body. they must surface regularly to breathe air.,inactive
+2902,freshwater drum,Freshwater oddities,Fresh Water,river,Spring | Summer | Fall,Night,Required,Any,Uncommon,0.7,1,35,7,20,0.85,,,,freshwater drum crush snails and mussels with powerful throat teeth. muscles vibrating against the swim bladder produce a low drumming sound.,inactive
+2903,goldeye,Freshwater oddities,Fresh Water,river,Spring | Fall | Winter,Night,Any,Required,Rare,0.3,0.3,6,12,25,0.9,,,,goldeye patrol turbid rivers with large light-sensitive eyes and a toothed tongue. they rise toward the surface to hunt insects and small fish.,inactive
+2904,mooneye,Freshwater oddities,Fresh Water,river,Spring | Fall | Winter,Night,Any,Any,Uncommon,0.85,0.2,4,5,11,0.92,,,,mooneyes flash silver in clear rivers and lakes while feeding near the surface. their large eyes suit evening hunts for insects and small fish.,inactive
+2905,weather loach,Freshwater oddities,Fresh Water,pond,Spring | Summer | Fall | Winter,Any,Required,Any,Uncommon,0.85,0.05,0.7,5,11,0.95,,,,weather loaches gulp air and pass it through the intestine when oxygen runs low. restless activity often increases as air pressure falls.,inactive
+3001,bubble-eye goldfish,Freshwater ornamental,Fresh Water,pond,Spring | Summer | Fall | Winter,Any,Any,Any,Uncommon,0.35,0.1,0.35,3,7,0.9,,,,"bubble-eye goldfish have delicate fluid-filled sacs beneath their eyes, making careful handling especially important.",active
+3002,goldfish,Freshwater ornamental,Fresh Water,pond,Spring | Summer | Fall | Winter,Any,Any,Any,Common,0.5,0.1,4,1,3,0.9,,,,"goldfish can tolerate a surprisingly wide range of conditions. their wild relatives often live in slow, weedy water.",active
+3003,koi,Freshwater ornamental,Fresh Water,pond,Spring | Summer | Fall | Winter,Day,Any,Forbidden,Rare,0.28,0.5,35,14,34,0.87,,,,koi are colorful domesticated forms of common carp selected over many generations. each fish carries a distinctive arrangement of patches and scales.,inactive
+3101,black crappie,Freshwater panfish,Fresh Water,pond,Spring | Fall | Winter,Any,Any,Required,Uncommon,1,0.2,4,5,11,0.9,,,,black crappie gather around submerged wood and vegetation in clear water. irregular dark speckles scatter across their deep silver bodies.,inactive
+3102,bluegill,Freshwater panfish,Fresh Water,pond,Spring | Summer | Fall,Day,Any,Any,Common,10,0.3,2,3,3,1,,,,"bluegill fathers sweep shallow bowls into the bottom for nests, then stand guard. whole neighborhoods of nests can crowd together.",active
+3103,green sunfish,Freshwater panfish,Fresh Water,pond,Spring | Summer | Fall,Day,Any,Any,Common,3.2,0.1,1.5,2,5,1,,,,"green sunfish tolerate small, warm, cloudy pools better than many relatives. a large mouth lets them tackle insects, crayfish, and fish.",inactive
+3104,pumpkinseed,Freshwater panfish,Fresh Water,pond,Spring | Summer | Fall,Day,Any,Any,Common,3,0.1,1.3,2,5,1,,,,pumpkinseeds use compact teeth to crush snails and other hard prey. blue facial lines and an orange gill spot brighten their round bodies.,inactive
+3105,redear sunfish,Freshwater panfish,Fresh Water,pond,Spring | Summer | Fall,Day,Any,Any,Uncommon,1.1,0.2,3,4,9,0.95,,,,"redear sunfish specialize in snails, crushing shells with strong throat teeth. a red or orange margin marks the dark flap behind the gill.",inactive
+3106,warmouth,Freshwater panfish,Fresh Water,pond,Spring | Summer | Fall,Night,Required,Any,Uncommon,0.8,0.2,2,5,10,0.95,,,,"warmouth hide in weeds, roots, and murky backwaters before ambushing prey. red eyes and dark cheek stripes frame a notably large mouth.",inactive
+3107,white crappie,Freshwater panfish,Fresh Water,lake,Spring | Fall | Winter,Night,Any,Any,Common,1.7,0.2,4.5,4,9,0.9,,,,"white crappie favor warm, cloudy water and gather around structure. vertical bars cross their silver sides instead of scattered speckles.",inactive
+3201,yellow perch,Freshwater perch,Fresh Water,lake,Spring | Fall | Winter,Day,Any,Any,Common,2.2,0.2,4,3,8,0.9,,,,yellow perch travel in schools through cool lakes and slow rivers. dark vertical bars and orange lower fins make them easy to recognize.,inactive
+3301,chain pickerel,Freshwater predators,Fresh Water,pond,Spring | Fall | Winter,Day,Required,Any,Uncommon,0.8,0.5,9,6,15,0.9,,,,chain pickerel hover among vegetation before darting at fish and frogs. a chain-like pattern breaks up their green flanks.,inactive
+3302,grass pickerel,Freshwater predators,Fresh Water,pond,Spring | Fall | Winter,Day,Any,Any,Common,2.1,0.1,1.5,2,6,0.95,,,,"grass pickerel remain small enough to hunt in narrow, weedy creeks and marshes. dark bars camouflage their slim bodies among stems.",inactive
+3303,northern pike,Freshwater predators,Fresh Water,lake,Spring | Fall | Winter,Day,Any,Any,Uncommon,0.75,1,30,8,24,0.85,,,,northern pike are solitary ambush hunters built for sudden acceleration. pale bean-shaped spots cover their long green bodies.,inactive
+3304,sauger,Freshwater predators,Fresh Water,river,Spring | Fall | Winter,Any,Any,Any,Uncommon,0.35,0.5,8,6,12,0.9,,,,"sauger favor turbid rivers and reservoirs. their mottled backs blend into the dim, shifting light near the bottom.",active
+3305,saugeye,Freshwater predators,Fresh Water,lake,Spring | Fall | Winter,Any,Any,Any,Rare,0.2,0.5,12,9,18,0.9,,,,saugeye are a fertile hybrid of sauger and walleye. they combine traits from both parent species and often grow quickly.,active
+3306,walleye,Freshwater predators,Fresh Water,lake,Spring | Fall | Winter,Any,Any,Any,Uncommon,0.4,0.5,15,7,15,0.9,,,,walleye have light-sensitive eyes that help them hunt in murky water and around dusk. their reflective layer gives the eyes a pale glow.,active
+3401,round goby,Freshwater small fish,Fresh Water,lake,Spring | Summer | Fall | Winter,Any,Any,Any,Common,8,0.1,0.6,2,3,1,,,,round gobies use fused pelvic fins like a suction cup to hold onto rocks. they are small fish with a famously large appetite.,active
+3501,crystal jelly,Jellies,Salt Water,ocean,Spring | Winter,Night,Any,Forbidden,Rare,0.24,0.05,4,15,34,0.93,,,,crystal jellies are nearly transparent except for glowing points around the bell. their green fluorescent protein transformed biological research.,inactive
+3502,lion's mane jelly,Jellies,Salt Water,ocean,Spring | Fall | Winter,Night,Any,Required,Epic,0.11,1,200,24,58,0.82,,,,lion's mane jellies trail dense curtains of tentacles through cold seas. the bell darkens from yellow to deep red as the animal grows.,inactive
+3503,moon jelly,Jellies,Salt Water,ocean,Spring | Summer | Fall | Winter,Night,Any,Any,Uncommon,0.7,0.05,5,6,14,0.94,,,,moon jellies pulse gently with four horseshoe-shaped organs visible through the bell. short tentacles sweep plankton toward the mouth.,inactive
+3504,Pacific sea nettle,Jellies,Salt Water,ocean,Summer | Fall,Day,Any,Forbidden,Rare,0.28,0.2,12,14,32,0.9,,,,pacific sea nettles carry rust-colored stripes across pale bells. long tentacles and frilled mouth arms capture plankton and small fish.,inactive
+3505,sea wasp,Jellies,Salt Water,ocean,Spring | Summer | Fall | Winter,Day,Required,Any,Legendary,0.035,0.2,6,42,90,0.9,,,,sea wasps have box-shaped bells and clusters of eyes along the lower edge. their long tentacles deliver exceptionally potent venom.,inactive
+3601,African pompano,Large coastal predators,Salt Water,ocean,Summer | Fall,Any,Any,Any,Rare,0.26,2,50,14,35,0.84,,,,african pompano develop long streaming fin rays while young. adults become deep-bodied silver hunters around reefs and offshore structure.,inactive
+3602,cobia,Large coastal predators,Salt Water,ocean,Summer | Fall,Day,Any,Any,Rare,0.24,5,80,15,40,0.82,,,,"cobia often follow sharks, rays, and floating structures in warm seas. a broad dark stripe runs along their long, powerful bodies.",inactive
+3603,giant trevally,Large coastal predators,Salt Water,ocean,Summer | Fall,Day,Any,Any,Epic,0.1,10,150,32,78,0.8,,,,giant trevally hunt reef edges with bursts of speed and force. large individuals sometimes charge prey from surf and shallow lagoons.,inactive
+3604,greater amberjack,Large coastal predators,Salt Water,ocean,Summer | Fall,Any,Any,Any,Rare,0.25,5,90,15,42,0.82,,,,"greater amberjacks circle reefs, wrecks, and offshore platforms. a dark stripe crosses the eye of their sturdy torpedo-shaped bodies.",inactive
+3605,lesser amberjack,Large coastal predators,Salt Water,ocean,Summer | Fall,Any,Any,Any,Uncommon,0.75,1,20,7,18,0.86,,,,lesser amberjacks school around deep reefs and floating objects. an enlarged eye suits their preference for dimmer water.,inactive
+3701,atlantic halibut,Large flatfish,Salt Water,ocean,Spring | Fall | Winter,Any,Any,Any,Epic,0.1,10,300,32,82,0.8,,,,atlantic halibut are immense right-eyed flatfish of cold northern seas. adults ambush fish and squid from deep rocky or sandy bottoms.,inactive
+3702,pacific halibut,Large flatfish,Salt Water,ocean,Spring | Fall | Winter,Any,Any,Any,Epic,0.1,10,350,34,86,0.8,,,,pacific halibut lie camouflaged on cold continental shelves. powerful flat bodies can travel long distances between feeding and spawning grounds.,inactive
+3703,turbot,Large flatfish,Salt Water,ocean,Spring | Fall | Winter,Any,Any,Required,Rare,0.24,1,35,14,34,0.85,,,,"turbot have broad, nearly circular bodies covered with bony bumps instead of scales. they bury into sand before ambushing fish and crustaceans.",inactive
+3801,American lobster,Lobsters and shrimp,Salt Water,ocean,Spring | Summer | Fall | Winter,Night,Any,Required,Rare,0.28,0.5,44,16,42,0.86,,,,american lobsters shelter in rocky crevices and emerge to forage along the bottom. one claw crushes while the other cuts and grips.,inactive
+3802,Antarctic krill,Lobsters and shrimp,Salt Water,ocean,Spring | Summer | Fall | Winter,Night,Any,Any,Rare,0.3,0.001,0.01,10,22,0.99,,,,"antarctic krill form enormous swarms beneath southern seas and ice. these small crustaceans feed whales, seals, penguins, and fish.",inactive
+3803,Caribbean spiny lobster,Lobsters and shrimp,Salt Water,ocean,Spring | Summer | Fall | Winter,Night,Any,Forbidden,Rare,0.25,0.5,26,15,38,0.87,,,,caribbean spiny lobsters rely on long antennae and a thorny shell instead of large claws. groups march together across the seafloor.,inactive
+3804,peacock mantis shrimp,Lobsters and shrimp,Salt Water,ocean,Spring | Summer | Fall | Winter,Day,Required,Any,Epic,0.1,0.05,0.4,22,50,0.96,,,,peacock mantis shrimp strike hard-shelled prey with club-like limbs. complex eyes move independently and detect colors beyond human vision.,inactive
+3805,skunk cleaner shrimp,Lobsters and shrimp,Salt Water,ocean,Spring | Summer | Fall | Winter,Day,Any,Forbidden,Uncommon,0.75,0.01,0.15,5,11,0.98,,,,skunk cleaner shrimp advertise at reef cleaning stations with bright white antennae. visiting fish pause while parasites are picked away.,inactive
+3901,Atlantic ghost crab,Marine crabs,Salt Water,ocean,Summer | Fall,Night,Any,Forbidden,Rare,0.28,0.02,0.5,11,24,0.97,,,,atlantic ghost crabs dash across beaches on long pale legs and retreat into deep burrows. raised eyes scan nearly every direction.,inactive
+3902,blue crab,Marine crabs,Salt Water,ocean,Summer | Fall,Day,Any,Any,Common,1.15,0.1,1.1,3,8,0.95,,,,"blue crabs swim with flattened rear legs shaped like paddles. bright blue claws and an olive shell conceal a quick, defensive temperament.",inactive
+3903,Dungeness crab,Marine crabs,Salt Water,ocean,Spring | Fall | Winter,Day,Any,Required,Uncommon,0.65,0.5,3,6,14,0.92,,,,dungeness crabs bury beneath sand with only their eyes and antennae exposed. broad toothed shells protect them along cool pacific coasts.,inactive
+3904,Japanese spider crab,Marine crabs,Salt Water,ocean,Spring | Fall | Winter,Night,Any,Any,Legendary,0.045,5,44,38,88,0.84,,,,japanese spider crabs carry the longest leg span of any living arthropod. adults roam deep seafloors around japan.,inactive
+3905,red king crab,Marine crabs,Salt Water,ocean,Spring | Fall | Winter,Night,Any,Required,Epic,0.11,2,24,24,58,0.85,,,,"red king crabs stride over cold northern bottoms on long armored legs. despite the name, they are closer to hermit crabs than true crabs.",inactive
+4001,american eel,Migratory eels,Fresh Water | Salt Water,river,Fall | Winter,Night,Required,Any,Rare,0.25,0.3,8,12,27,0.88,,,,american eels hatch far out in the sargasso sea before entering freshwater rivers. adults later reverse the journey to spawn once.,inactive
+4002,european eel,Migratory eels,Fresh Water | Salt Water,river,Fall | Winter,Night,Any,Required,Rare,0.2,0.3,10,13,30,0.88,,,,european eels cross the atlantic as transparent larvae before entering rivers and lakes. mature silver eels return to sea to reproduce.,inactive
+4101,rainbow smelt,Migratory forage fish,Fresh Water | Salt Water,lake,Spring | Winter,Night,Any,Any,Common,2.5,0.05,0.8,2,5,1,,,,"rainbow smelt move between coastal water and rivers, though some remain landlocked. cucumber-like aromas cling to freshly caught fish.",inactive
+4201,eulachon,Ocean-run forage fish,Fresh Water | Salt Water,river,Spring,Night,Required,Any,Uncommon,0.9,0.05,0.5,4,9,0.98,,,,eulachon return from the pacific to spawn in coastal rivers. their oil-rich bodies once served as candles when dried and fitted with wicks.,inactive
+4301,white sturgeon,Ocean-run giants,Fresh Water | Salt Water,river,Spring | Fall,Night,Any,Any,Legendary,0.045,20,500,58,135,0.78,,,,white sturgeon are the largest freshwater fish in north america. armored plates line bodies built for long lives in rivers and estuaries.,inactive
+4401,atlantic salmon,Ocean-run salmon,Salt Water,ocean,Spring | Fall,Any,Any,Any,Epic,0.65,8,40,20,42,0.85,,,,"atlantic salmon can return from the ocean to the river where they hatched. unlike many salmon, some survive to make the trip again.",active
+4402,chum salmon,Ocean-run salmon,Salt Water,ocean,Fall,Any,Any,Any,Uncommon,2.5,6,25,10,22,0.9,,,,chum salmon travel immense distances and develop striking bars when they return to fresh water to spawn.,active
+4403,coho salmon,Ocean-run salmon,Salt Water,ocean,Fall | Winter,Any,Any,Any,Rare,1.4,5,30,14,28,0.88,,,,coho salmon are agile hunters known for powerful leaps. young coho may spend more than a year growing in streams.,active
+4404,pink salmon,Ocean-run salmon,Salt Water,ocean,Summer | Fall,Any,Any,Any,Uncommon,3,3,12,8,15,0.92,,,,pink salmon usually follow a strict two-year life cycle. spawning males develop the hump that inspired their nickname.,active
+4405,sockeye salmon,Ocean-run salmon,Salt Water,ocean,Summer | Fall,Any,Any,Any,Rare,1.6,4,15,12,24,0.9,,,,sockeye salmon feed heavily on plankton at sea. adults turn vivid red as they return inland to spawn.,active
+4501,steelhead trout,Ocean-run salmonids,Fresh Water,river,Spring | Fall | Winter,Any,Any,Any,Rare,0.2,2,35,10,20,0.9,,,,steelhead are the ocean-going form of rainbow trout. adults can leave fresh water and later return to spawn.,active
+4601,black marlin,Oceanic billfish,Salt Water,ocean,Summer | Fall,Any,Any,Any,Epic,0.08,80,700,45,90,0.9,,,,black marlin are powerful billfish that spend much of their lives in warm open ocean and can make blistering runs.,active
+4602,blue marlin,Oceanic billfish,Salt Water,ocean,Summer | Fall,Any,Any,Any,Legendary,0.05,100,1000,60,120,0.9,,,,blue marlin are highly migratory open-ocean hunters. their long bill helps them stun prey before turning back to feed.,active
+4603,sailfish,Oceanic billfish,Salt Water,ocean,Spring | Summer | Fall | Winter,Any,Any,Any,Epic,0.1,30,200,38,78,0.9,,,,sailfish raise their enormous dorsal fin while herding baitfish. they are among the fastest fish in the sea.,active
+4604,swordfish,Oceanic billfish,Salt Water,ocean,Spring | Summer | Fall | Winter,Any,Any,Any,Legendary,0.06,50,1000,55,110,0.9,,,,"swordfish are fast, wide-ranging predators with a flattened bill. they can cross deep water and warm surface layers in one day.",active
+4605,white marlin,Oceanic billfish,Salt Water,ocean,Summer | Fall,Any,Any,Any,Epic,0.1,30,180,35,70,0.9,,,,white marlin favor warm offshore water and use their rounded dorsal fin and bill to weave through schools of prey.,active
+4701,mahi-mahi,Oceanic pelagic fish,Salt Water,ocean,Summer | Fall,Day,Any,Forbidden,Rare,0.28,2,50,15,38,0.84,,,,"mahi-mahi flash brilliant blue, green, and gold while chasing prey near the surface. they grow quickly and gather around floating objects.",inactive
+4702,opah,Oceanic pelagic fish,Salt Water,ocean,Summer | Fall,Day,Any,Forbidden,Epic,0.09,20,600,36,88,0.8,,,,"opah circulate warm blood through much of the body, an unusual trait among fish. round silver flanks glow with red fins and pale spots.",inactive
+4703,wahoo,Oceanic pelagic fish,Salt Water,ocean,Summer | Fall,Day,Any,Any,Epic,0.11,10,100,30,70,0.82,,,,wahoo are streamlined open-ocean hunters with rows of sharp teeth. blue vertical bars flicker across their sides during fast pursuits.,inactive
+4801,albacore tuna,Oceanic tuna,Salt Water,ocean,Spring | Summer | Fall,Any,Any,Any,Rare,1.5,10,45,12,24,0.85,,,,albacore have unusually long pectoral fins. their warm muscles help them cruise across wide stretches of open ocean.,active
+4802,bigeye tuna,Oceanic tuna,Salt Water,ocean,Spring | Summer | Fall | Winter,Any,Any,Any,Epic,0.55,20,120,22,48,0.8,,,,"bigeye tuna make daily trips between deep, cool water and warmer surface layers. their large eyes suit those dim depths.",active
+4803,bluefin tuna,Oceanic tuna,Salt Water,ocean,Spring | Fall,Any,Any,Any,Legendary,0.18,30,250,35,90,0.78,,,,bluefin tuna are powerful ocean travelers. heat retained in their swimming muscles lets them thrive in surprisingly cold seas.,active
+4804,skipjack tuna,Oceanic tuna,Salt Water,ocean,Spring | Summer | Fall | Winter,Any,Any,Any,Uncommon,3,5,25,8,16,0.9,,,,skipjack travel in fast-moving schools and have dark stripes along their lower sides. they rarely stop swimming.,active
+4805,yellowfin tuna,Oceanic tuna,Salt Water,ocean,Spring | Summer | Fall | Winter,Any,Any,Any,Rare,1,15,80,16,36,0.82,,,,yellowfin are named for their bright yellow fins and finlets. large schools often gather with other open-ocean hunters.,active
+4901,common octopus,Octopuses,Salt Water,ocean,Spring | Summer | Fall | Winter,Night,Any,Any,Uncommon,0.65,0.2,22,7,18,0.87,,,,common octopuses squeeze through narrow cracks and solve problems while hunting reefs. skin cells rapidly change color and texture.,inactive
+4902,day octopus,Octopuses,Salt Water,ocean,Spring | Summer | Fall | Winter,Day,Any,Forbidden,Rare,0.24,0.2,15,15,36,0.88,,,,"day octopuses forage across tropical reefs while the sun is high. shifting bars and spots communicate alarm, camouflage, and threat.",inactive
+4903,giant Pacific octopus,Octopuses,Salt Water,ocean,Spring | Summer | Fall | Winter,Night,Any,Required,Epic,0.1,5,200,28,70,0.81,,,,giant pacific octopuses explore cold rocky dens with thousands of arm suckers. females guard their eggs without feeding until they hatch.,inactive
+4904,greater blue-ringed octopus,Octopuses,Salt Water,ocean,Spring | Summer | Fall | Winter,Day,Required,Any,Legendary,0.04,0.05,0.3,38,82,0.96,,,,greater blue-ringed octopuses reveal vivid rings when threatened. a tiny beak can deliver venom strong enough to stop breathing.,inactive
+5001,sunfish,Open-ocean giants,Salt Water,ocean,Spring | Summer | Fall,Day,Any,Any,Common,7,0.4,2.5,3,3,1,,,,ocean sunfish are built more like enormous swimming heads than ordinary fish. they visit the surface and turn sideways to warm up.,active
+5101,barndoor skate,Rays and skates,Salt Water,ocean,Spring | Fall | Winter,Any,Any,Required,Epic,0.1,5,150,28,68,0.8,,,,barndoor skates glide over cold atlantic bottoms with broad wing-like fins. rows of thorny spines protect the back and tail.,inactive
+5102,giant manta ray,Rays and skates,Salt Water,ocean,Spring | Summer | Fall | Winter,Day,Any,Forbidden,Legendary,0.025,100,1000,65,150,0.76,,,,giant manta rays funnel plankton into wide mouths with two flexible head fins. each belly carries a unique pattern of dark spots.,inactive
+5103,largetooth sawfish,Rays and skates,Salt Water,ocean,Spring | Summer | Fall | Winter,Night,Required,Any,Legendary,0.035,25,1300,58,135,0.78,,,,largetooth sawfish detect hidden prey with a long sensory rostrum. a swift sideways sweep stuns fish and exposes animals buried in mud.,inactive
+5104,ocellate river stingray,Rays and skates,Fresh Water,river,Spring | Summer | Fall | Winter,Day,Required,Any,Epic,0.09,2,35,28,65,0.82,,,,ocellate river stingrays carry bright eye-like spots across a round disc. they hide beneath sand in south american rivers.,inactive
+5105,southern stingray,Rays and skates,Salt Water,ocean,Spring | Summer | Fall | Winter,Day,Any,Any,Rare,0.23,2,80,15,40,0.82,,,,southern stingrays bury in warm sand with only their eyes and spiracles visible. crushing plates break open clams and crustaceans.,inactive
+5201,red lionfish,Reef ambush fish,Salt Water,ocean,Spring | Summer | Fall | Winter,Night,Any,Any,Rare,0.3,0.2,3,11,25,0.92,,,,red lionfish spread ornate venomous spines when threatened. broad fins herd smaller fish into corners before a rapid gulp.,inactive
+5202,red scorpionfish,Reef ambush fish,Salt Water,ocean,Spring | Summer | Fall | Winter,Night,Any,Any,Rare,0.3,0.2,7,11,25,0.9,,,,red scorpionfish vanish against rocky reefs through mottled color and skin flaps. venomous dorsal spines punish careless predators.,inactive
+5301,green moray,Reef eels,Salt Water,ocean,Spring | Summer | Fall | Winter,Night,Any,Any,Rare,0.28,1,30,13,32,0.86,,,,green morays appear green because yellow mucus coats blue-gray skin. a second set of jaws pulls captured prey deeper into the throat.,inactive
+5401,peacock flounder,Reef flatfish,Salt Water,ocean,Spring | Summer | Fall | Winter,Day,Any,Any,Rare,0.3,0.2,5,11,24,0.9,,,,peacock flounders change their spots and colors to match tropical sand. both eyes scan above while the flat body remains concealed.,inactive
+5501,blue tang,Reef grazers,Salt Water,ocean,Spring | Summer | Fall | Winter,Day,Any,Any,Uncommon,0.9,0.05,1.5,5,11,0.95,,,,blue tangs graze algae from coral reefs with small close-set teeth. sharp folding spines near the tail give surgeonfish their name.,inactive
+5502,rainbow parrotfish,Reef grazers,Salt Water,ocean,Spring | Summer | Fall | Winter,Day,Any,Any,Epic,0.12,2,40,25,58,0.84,,,,rainbow parrotfish scrape algae from reefs with fused beak-like teeth. crushed coral passes through them and returns to the reef as sand.,inactive
+5503,stoplight parrotfish,Reef grazers,Salt Water,ocean,Spring | Summer | Fall | Winter,Day,Any,Any,Rare,0.28,0.5,15,12,29,0.88,,,,stoplight parrotfish change color dramatically as they mature. powerful dental plates bite algae from hard reef surfaces.,inactive
+5504,yellow tang,Reef grazers,Salt Water,ocean,Spring | Summer | Fall | Winter,Day,Forbidden,Any,Rare,0.35,0.05,0.8,9,20,0.96,,,,yellow tangs browse algae across hawaiian reefs during daylight. at night their bright bodies darken and show a pale horizontal stripe.,inactive
+5601,atlantic trumpetfish,Reef oddities,Salt Water,ocean,Spring | Summer | Fall | Winter,Day,Any,Any,Rare,0.3,0.1,2,10,24,0.94,,,,atlantic trumpetfish hover head-down among corals and sea fans. long tubular snouts expand suddenly to draw in small fish.,inactive
+5602,gray triggerfish,Reef oddities,Salt Water,ocean,Spring | Summer | Fall | Winter,Day,Any,Any,Uncommon,0.8,0.3,12,7,17,0.88,,,,"gray triggerfish lock a stout dorsal spine upright when wedged into shelter. strong teeth crack barnacles, crabs, and shellfish.",inactive
+5603,guineafowl puffer,Reef oddities,Salt Water,ocean,Spring | Summer | Fall | Winter,Day,Any,Any,Rare,0.3,0.2,5,11,26,0.9,,,,guineafowl puffers swallow water to inflate when threatened. adults may shift from a spotted dark pattern to a vivid yellow form.,inactive
+5604,longhorn cowfish,Reef oddities,Salt Water,ocean,Spring | Summer | Fall | Winter,Day,Any,Required,Epic,0.13,0.1,2,23,52,0.94,,,,longhorn cowfish wear a rigid box of fused scales with horns above the eyes. small fins steer the angular body with surprising precision.,inactive
+5605,queen triggerfish,Reef oddities,Salt Water,ocean,Spring | Summer | Fall | Winter,Day,Any,Forbidden,Rare,0.25,0.5,15,14,32,0.88,,,,queen triggerfish blow jets of water to uncover buried prey and flip sea urchins. vivid blue lines radiate across the face.,inactive
+5606,red cornetfish,Reef oddities,Salt Water,ocean,Spring | Summer | Fall | Winter,Day,Any,Required,Epic,0.11,0.2,4,24,55,0.92,,,,red cornetfish stalk reefs with extremely long bodies and tubular snouts. a trailing filament extends from the center of the tail.,inactive
+5607,spotted porcupinefish,Reef oddities,Salt Water,ocean,Spring | Summer | Fall | Winter,Night,Any,Required,Epic,0.12,0.5,7,24,54,0.9,,,,spotted porcupinefish inflate until long spines stand outward. fused teeth form a strong beak for crushing shelled prey.,inactive
+5608,yellow boxfish,Reef oddities,Salt Water,ocean,Spring | Summer | Fall | Winter,Day,Any,Any,Rare,0.32,0.03,0.8,10,22,0.96,,,,yellow boxfish juveniles are bright cubes covered in black spots. rigid body armor leaves small fins to provide careful steering.,inactive
+5701,copperband butterflyfish,Reef ornamentals,Salt Water,ocean,Spring | Summer | Fall | Winter,Day,Any,Forbidden,Rare,0.32,0.03,0.4,9,21,0.98,,,,copperband butterflyfish probe reef cracks with long narrow snouts. bold orange bands and a false eyespot confuse predators.,inactive
+5702,ocellaris clownfish,Reef ornamentals,Salt Water,ocean,Spring | Summer | Fall | Winter,Day,Any,Any,Uncommon,1,0.01,0.15,5,10,1,,,,ocellaris clownfish shelter safely among stinging anemone tentacles. each group is led by a female that develops from the largest male.,inactive
+5703,queen angelfish,Reef ornamentals,Salt Water,ocean,Spring | Summer | Fall | Winter,Day,Any,Any,Rare,0.3,0.1,2,11,25,0.94,,,,queen angelfish graze sponges from warm atlantic reefs. a blue-ringed spot on the forehead resembles a tiny crown.,inactive
+5801,Atlantic goliath grouper,Reef predators,Salt Water,ocean,Spring | Summer | Fall | Winter,Any,Any,Required,Legendary,0.05,20,800,50,125,0.78,,,,"atlantic goliath groupers occupy reefs, wrecks, and mangrove roots. enormous mouths pull prey inward with a sudden pressure drop.",inactive
+5802,bluefin trevally,Reef predators,Salt Water,ocean,Spring | Summer | Fall | Winter,Day,Any,Forbidden,Rare,0.3,1,25,14,32,0.86,,,,bluefin trevally hunt reef fish in coordinated groups. electric blue fins and scattered blue spots contrast with a golden body.,inactive
+5803,giant grouper,Reef predators,Salt Water,ocean,Spring | Summer | Fall | Winter,Any,Any,Required,Legendary,0.045,20,880,52,128,0.78,,,,giant groupers become massive ambush predators around indo-pacific reefs. juveniles carry bold black-and-yellow markings.,inactive
+5804,great barracuda,Reef predators,Salt Water,ocean,Spring | Summer | Fall | Winter,Day,Any,Required,Epic,0.12,5,80,28,65,0.82,,,,great barracudas hover motionless before accelerating toward prey. long jaws carry two distinct rows of sharp teeth.,inactive
+5805,gulf grouper,Reef predators,Salt Water,ocean,Spring | Summer | Fall | Winter,Any,Any,Any,Uncommon,0.3,2,40,10,24,0.9,,,,gulf grouper are ambush predators that use a sudden gulp to pull prey into their cavernous mouths.,active
+5806,Nassau grouper,Reef predators,Salt Water,ocean,Spring | Summer | Fall | Winter,Any,Any,Forbidden,Epic,0.12,2,55,25,62,0.82,,,,nassau groupers shift their bars and colors with mood and surroundings. adults gather at traditional reef sites to spawn.,inactive
+5807,red grouper,Reef predators,Salt Water,ocean,Spring | Summer | Fall | Winter,Any,Any,Any,Rare,0.2,2,30,16,36,0.9,,,,red grouper excavate shelters in reef rubble. the hollows they make can become hiding places for many smaller animals.,active
+5901,blue-green chromis,Reef schooling fish,Salt Water,ocean,Spring | Summer | Fall | Winter,Day,Any,Any,Common,2.8,0.01,0.25,2,4,1,,,,blue-green chromis form loose clouds above branching coral. the school dives into narrow shelter whenever a predator approaches.,inactive
+5902,bluestriped grunt,Reef schooling fish,Salt Water,ocean,Spring | Summer | Fall | Winter,Day,Any,Any,Common,2,0.1,3,3,7,0.94,,,,bluestriped grunts shelter on reefs by day and spread over seagrass to feed at night. grinding throat teeth produce audible grunts.,inactive
+5903,French grunt,Reef schooling fish,Salt Water,ocean,Spring | Summer | Fall | Winter,Day,Any,Any,Common,2,0.1,3.5,3,7,0.94,,,,french grunts gather in yellow-striped schools around reefs and wrecks. they travel into nearby sand and grass beds after dark.,inactive
+5904,sergeant major,Reef schooling fish,Salt Water,ocean,Spring | Summer | Fall | Winter,Day,Any,Any,Common,2.5,0.02,0.5,2,5,0.98,,,,sergeant majors guard purple egg patches on hard reef surfaces. five dark bars cross their yellow and silver bodies.,inactive
+6001,lane snapper,Reef snapper,Salt Water,ocean,Spring | Summer | Fall | Winter,Any,Any,Any,Common,0.45,0.5,5,5,11,0.9,,,,lane snapper use reef structure for cover and hunt small crustaceans and fish when the light begins to fade.,active
+6002,mangrove snapper,Reef snapper,Salt Water,ocean,Spring | Summer | Fall | Winter,Any,Any,Any,Uncommon,0.35,0.5,12,7,16,0.9,,,,"mangrove snapper shelter among roots and docks when young, then move toward reefs as they grow.",active
+6003,mutton snapper,Reef snapper,Salt Water,ocean,Spring | Summer | Fall | Winter,Any,Any,Any,Rare,0.2,2,30,12,28,0.9,,,,"mutton snapper have canine teeth that help them pick crabs, shrimp, and other hard-shelled prey from the reef.",active
+6004,red snapper,Reef snapper,Salt Water,ocean,Spring | Summer | Fall | Winter,Any,Any,Any,Uncommon,0.35,1,25,8,19,0.9,,,,"red snapper gather around reefs, wrecks, and ledges. their diet includes small fish, shrimp, and squid.",active
+6101,ballan wrasse,Reef wrasses,Salt Water,ocean,Spring | Summer | Fall | Winter,Day,Any,Any,Uncommon,0.8,0.2,10,6,15,0.9,,,,ballan wrasse crush shellfish and sea urchins with strong throat teeth. thick lips and mottled colors suit rocky european reefs.,inactive
+6102,cleaner wrasse,Reef wrasses,Salt Water,ocean,Spring | Summer | Fall | Winter,Day,Any,Forbidden,Rare,0.35,0.01,0.2,9,20,1,,,,cleaner wrasses advertise with a bright stripe and dance at reef stations. larger fish wait while parasites are picked from skin and gills.,inactive
+6103,hogfish,Reef wrasses,Salt Water,ocean,Spring | Summer | Fall | Winter,Day,Any,Any,Rare,0.3,0.5,20,12,30,0.86,,,,hogfish root through sand with long pig-like snouts in search of shellfish. adults can change from female to male as social groups shift.,inactive
+6201,green sea turtle,Sea turtles,Salt Water,ocean,Summer | Fall,Day,Any,Forbidden,Legendary,0.018,65,500,62,145,0.78,,,,green sea turtles graze seagrass meadows with finely serrated jaws. adults migrate long distances between feeding and nesting grounds.,inactive
+6202,hawksbill sea turtle,Sea turtles,Salt Water,ocean,Summer | Fall,Day,Required,Any,Legendary,0.014,75,250,66,150,0.8,,,,hawksbill sea turtles reach into reef crevices with narrow hooked beaks. a diet rich in sponges helps shape coral communities.,inactive
+6203,leatherback sea turtle,Sea turtles,Salt Water,ocean,Spring | Summer | Fall | Winter,Night,Any,Required,Legendary,0.012,500,2000,75,175,0.76,,,,leatherback sea turtles cross entire oceans while hunting jellies. a flexible ridged shell and internal heat help them enter cold water.,inactive
+6204,loggerhead sea turtle,Sea turtles,Salt Water,ocean,Summer | Fall,Day,Any,Any,Legendary,0.016,150,1200,68,155,0.77,,,,loggerhead sea turtles crush crabs and mollusks with massive jaws. females often return near their birth beaches to nest.,inactive
+6301,leafy seadragon,Seahorses and seadragons,Salt Water,ocean,Spring | Summer | Fall | Winter,Day,Any,Forbidden,Epic,0.1,0.1,0.5,24,55,0.94,,,,leafy seadragons drift among seaweed with elaborate skin lobes that resemble leaves. males carry eggs beneath the tail.,inactive
+6302,weedy seadragon,Seahorses and seadragons,Salt Water,ocean,Spring | Summer | Fall | Winter,Day,Any,Required,Rare,0.22,0.1,0.6,16,38,0.93,,,,weedy seadragons rely on slender bodies and small leaf-like lobes for camouflage. males brood rows of eggs on the underside of the tail.,inactive
+6401,black-lip pearl oyster,Shellfish,Salt Water,ocean,Spring | Summer | Fall | Winter,Day,Any,Any,Rare,0.22,0.1,5,14,40,0.91,,,,black-lip pearl oysters line their shells with dark-edged nacre. a trapped irritant can become coated in the same pearly material.,inactive
+6402,giant clam,Shellfish,Salt Water,ocean,Spring | Summer | Fall | Winter,Day,Any,Forbidden,Epic,0.1,5,500,28,72,0.8,,,,giant clams host photosynthetic algae inside brightly colored mantles. heavy shells remain anchored to one reef spot throughout adulthood.,inactive
+6403,Pacific geoduck,Shellfish,Salt Water,ocean,Spring | Summer | Fall | Winter,Day,Any,Required,Rare,0.25,0.5,15,14,34,0.88,,,,pacific geoducks bury their shells deep while a long siphon reaches the seafloor. these clams can survive for well over a century.,inactive
+6404,queen conch,Shellfish,Salt Water,ocean,Spring | Summer | Fall | Winter,Day,Any,Forbidden,Rare,0.24,1,5,15,36,0.91,,,,queen conchs graze algae with a long flexible snout. adults develop a broad flared shell lip and move by vaulting on one muscular foot.,inactive
+6405,red abalone,Shellfish,Salt Water,ocean,Spring | Summer | Fall | Winter,Day,Any,Required,Epic,0.09,0.2,12,24,56,0.88,,,,red abalones cling to pacific rocks with broad muscular feet. rows of shell holes release water that has passed over the gills.,inactive
+6501,bigfin reef squid,Squid,Salt Water,ocean,Spring | Summer | Fall | Winter,Day,Any,Forbidden,Rare,0.3,0.2,5,13,30,0.91,,,,bigfin reef squid hover in coordinated groups around warm reefs. shifting colors and body patterns signal to nearby squid.,inactive
+6502,colossal squid,Squid,Salt Water,ocean,Spring | Summer | Fall | Winter,Night,Required,Any,Legendary,0.015,400,1100,82,190,0.76,,,,colossal squid carry swiveling hooks on their arms and tentacles. enormous eyes gather faint light in the deep southern ocean.,inactive
+6503,giant squid,Squid,Salt Water,ocean,Spring | Summer | Fall | Winter,Night,Any,Required,Legendary,0.02,100,600,70,165,0.77,,,,giant squid hunt deep ocean prey with two long feeding tentacles. the largest eyes in the animal world watch for movement in darkness.,inactive
+6504,Humboldt squid,Squid,Salt Water,ocean,Spring | Summer | Fall | Winter,Night,Any,Any,Epic,0.09,5,110,26,64,0.82,,,,humboldt squid flash red and white while hunting in large groups. powerful beaks and grasping suckers handle fish and other squid.,inactive
+6601,ballyhoo,Surface ocean fish,Salt Water,ocean,Spring | Summer | Fall | Winter,Day,Any,Any,Common,2.5,0.05,1,2,5,0.98,,,,ballyhoo skim warm ocean surfaces with an extended lower jaw. tight schools scatter into quick leaps when predators strike.,inactive
+6602,houndfish,Surface ocean fish,Salt Water,ocean,Spring | Summer | Fall | Winter,Day,Any,Any,Rare,0.3,0.5,10,11,25,0.88,,,,houndfish hunt near the surface with long jaws filled with fine teeth. powerful leaps can carry their narrow bodies far above the water.,inactive
+6603,tropical flyingfish,Surface ocean fish,Salt Water,ocean,Spring | Summer | Fall | Winter,Day,Forbidden,Any,Rare,0.35,0.05,1.5,10,22,0.94,,,,tropical flyingfish accelerate underwater before spreading enlarged fins above the surface. glides help them escape pursuing predators.,inactive
+6701,striped bass,Temperate coastal,Salt Water,ocean,Spring | Fall,Any,Any,Any,Uncommon,4,1.5,8,7,7,0.9,,,,"striped bass split their lives between salt and fresh water, returning upriver to spawn. a long-lived striper may see three decades.",active
+6801,atlantic croaker,Temperate estuary,Salt Water,ocean,Spring | Summer | Fall,Night,Required,Any,Common,2.1,0.1,5,3,8,0.92,,,,atlantic croakers vibrate muscles against the swim bladder to make a croaking sound. chin barbels locate worms in sandy bays.,inactive
+6802,black drum,Temperate estuary,Salt Water,ocean,Spring | Summer | Fall,Night,Any,Any,Uncommon,0.7,1,70,8,27,0.82,,,,black drum crush oysters and crabs with rounded throat teeth. large adults develop dark vertical bars that fade with age.,inactive
+6803,red drum,Temperate estuary,Salt Water,ocean,Spring | Summer | Fall,Any,Required,Any,Uncommon,0.75,1,45,8,23,0.84,,,,red drum use powerful tails to patrol surf and estuaries. one or more black spots near the tail may confuse predators about which end is the head.,inactive
+6804,striped mullet,Temperate estuary,Fresh Water | Salt Water,ocean,Spring | Summer | Fall,Day,Any,Any,Common,2.5,0.3,8,3,8,0.92,,,,striped mullet leap clear of the water and travel in coastal schools. they graze algae and detritus with specialized digestive systems.,inactive
+6805,weakfish,Temperate estuary,Salt Water,ocean,Spring | Summer | Fall,Night,Any,Required,Rare,0.32,0.5,18,12,29,0.86,,,,weakfish are named for delicate mouth tissue that tears easily around a hook. iridescent spots scatter across their bronze backs.,inactive
+6806,white perch,Temperate estuary,Fresh Water | Salt Water,river,Spring | Summer | Fall,Any,Any,Any,Uncommon,1.1,0.2,3.5,5,11,0.9,,,,white perch tolerate fresh and brackish water along the atlantic coast. deep silver bodies gather in schools around river mouths.,inactive
+6807,yellowfin croaker,Temperate estuary,Salt Water,ocean,Spring | Summer | Fall,Any,Any,Any,Common,2,0.1,4,3,8,0.92,,,,yellowfin croakers search sandy surf zones with a small chin barbel. internal muscles produce croaks and drumming calls.,inactive
+6901,black rockfish,Temperate reef fish,Salt Water,ocean,Spring | Fall | Winter,Any,Any,Any,Common,1.7,0.3,10,4,11,0.9,,,,"black rockfish gather above reefs and kelp along the north pacific coast. like other rockfish, they give birth to live young.",inactive
+6902,black sea bass,Temperate reef fish,Salt Water,ocean,Spring | Fall | Winter,Day,Any,Any,Common,1.7,0.3,8,4,10,0.9,,,,"black sea bass shelter around reefs, piers, and wrecks. many begin life as female and later become male.",inactive
+6903,canary rockfish,Temperate reef fish,Salt Water,ocean,Spring | Fall | Winter,Day,Any,Any,Uncommon,0.7,0.5,12,7,17,0.88,,,,canary rockfish glow orange-yellow over deep pacific reefs. these long-lived fish mature slowly and give birth to live young.,inactive
+6904,scup,Temperate reef fish,Salt Water,ocean,Spring | Fall | Winter,Day,Any,Any,Common,2.2,0.1,5,3,8,0.92,,,,"scup use small strong teeth to crush shellfish and other bottom prey. deep silver bodies gather around piers, reefs, and eelgrass.",inactive
+6905,sheepshead,Temperate reef fish,Salt Water,ocean,Spring | Fall | Winter,Day,Any,Any,Uncommon,0.75,0.5,20,7,18,0.86,,,,sheepshead carry flat human-like front teeth for biting barnacles and crabs. dark bars cross their high silver bodies.,inactive
+6906,tautog,Temperate reef fish,Salt Water,ocean,Spring | Fall | Winter,Day,Any,Any,Uncommon,0.8,0.5,20,7,18,0.86,,,,tautog pry mussels and crabs from rocky structure with thick lips and strong teeth. adults return to familiar shelters year after year.,inactive
+6907,yelloweye rockfish,Temperate reef fish,Salt Water,ocean,Spring | Fall | Winter,Any,Any,Required,Epic,0.09,2,35,28,64,0.84,,,,yelloweye rockfish can live for more than a century on deep pacific reefs. bright golden eyes stand out against orange-red bodies.,inactive
+7001,lingcod,Temperate reef predators,Salt Water,ocean,Spring | Fall | Winter,Any,Any,Any,Rare,0.25,2,60,14,38,0.83,,,,"lingcod guard rocky dens and ambush fish with a huge tooth-lined mouth. despite the name, they are greenling relatives rather than cod.",inactive
+7101,milkfish,Tropical coastal fish,Salt Water,ocean,Spring | Summer | Fall | Winter,Day,Any,Any,Common,1.8,0.5,25,4,12,0.88,,,,milkfish graze algae and tiny organisms in warm coastal shallows. deeply forked tails drive large schools through lagoons and estuaries.,inactive
+7201,roosterfish,Tropical coastal predators,Salt Water,ocean,Spring | Summer | Fall | Winter,Day,Any,Any,Epic,0.11,5,100,30,70,0.82,,,,roosterfish raise seven long dorsal spines while chasing prey through tropical surf. dark bands cross their sleek silver bodies.,inactive
+7301,atlantic tarpon,Tropical estuary,Fresh Water | Salt Water,ocean,Spring | Summer | Fall | Winter,Any,Required,Any,Epic,0.09,10,180,34,82,0.8,,,,atlantic tarpon gulp air with a lung-like swim bladder when coastal water runs low on oxygen. giant silver scales flash during leaps.,inactive
+7302,barramundi,Tropical estuary,Fresh Water | Salt Water,river,Spring | Summer | Fall | Winter,Night,Required,Any,Rare,0.24,2,45,15,38,0.84,,,,barramundi move between rivers and coasts and can breathe in low-oxygen water. many mature first as males and later become females.,inactive
+7303,common snook,Tropical estuary,Salt Water,ocean,Spring | Summer | Fall | Winter,Night,Required,Any,Rare,0.3,1,30,13,32,0.85,,,,common snook wait around mangrove roots and current seams to ambush prey. a bold black lateral line divides their silver flanks.,inactive
+7401,bonefish,Tropical flats fish,Salt Water,ocean,Spring | Summer | Fall | Winter,Day,Any,Forbidden,Rare,0.25,0.5,15,13,31,0.86,,,,bonefish sweep tropical flats in silvery schools and root for buried prey. their speed and pale bodies make them difficult to follow in shallow water.,inactive
+7402,Florida pompano,Tropical flats fish,Salt Water,ocean,Spring | Summer | Fall | Winter,Day,Any,Forbidden,Uncommon,0.85,0.5,9,7,16,0.88,,,,"florida pompano search warm surf for crabs, clams, and sand fleas. short blunt snouts lead into deep, silvery bodies.",inactive
+7403,ladyfish,Tropical flats fish,Salt Water,ocean,Spring | Summer | Fall | Winter,Day,Required,Any,Common,1.7,0.2,8,4,10,0.9,,,,ladyfish race through warm shallows after schooling prey and often leap when pursued. a deeply forked tail powers their slender bodies.,inactive
+7404,permit,Tropical flats fish,Salt Water,ocean,Spring | Summer | Fall | Winter,Day,Forbidden,Any,Epic,0.11,3,50,27,62,0.84,,,,permit browse tropical flats for crabs and shellfish with rubbery mouths. tall silver bodies carry long sickle-shaped dorsal and anal fins.,inactive
+7501,clown knifefish,Tropical freshwater fish,Fresh Water,river,Spring | Summer | Fall | Winter,Night,Any,Required,Rare,0.25,1,25,16,38,0.86,,,,clown knifefish undulate one long belly fin to glide forward or backward. dark eye-like spots line their silver nocturnal bodies.,inactive
+7502,clown loach,Tropical freshwater fish,Fresh Water,river,Spring | Summer | Fall | Winter,Day,Required,Any,Rare,0.32,0.1,2,10,22,0.9,,,,clown loaches rest in groups and make clicking sounds while feeding. orange bodies carry three broad black bands and small eye spines.,inactive
+7503,discus,Tropical freshwater fish,Fresh Water,river,Spring | Summer | Fall | Winter,Day,Any,Required,Epic,0.12,0.1,1,24,52,0.92,,,,"discus move through quiet amazon backwaters with round, flattened bodies. parents feed newly hatched young with nutrient-rich skin mucus.",inactive
+7504,freshwater angelfish,Tropical freshwater fish,Fresh Water,river,Spring | Summer | Fall | Winter,Day,Any,Any,Rare,0.3,0.05,0.5,10,22,0.95,,,,freshwater angelfish slip between tall plants on narrow triangular bodies. long fins and vertical bars resemble stems and shadows.,inactive
+7505,giant gourami,Tropical freshwater fish,Fresh Water,lake,Spring | Summer | Fall | Winter,Day,Any,Any,Rare,0.25,2,20,13,31,0.86,,,,giant gouramis breathe surface air through a specialized labyrinth organ. adults graze plants but also investigate insects and small animals.,inactive
+7506,Nile tilapia,Tropical freshwater fish,Fresh Water,lake,Spring | Summer | Fall | Winter,Day,Any,Any,Common,2.2,0.3,8,3,9,0.9,,,,nile tilapia thrive in warm lakes and rivers on a varied diet. females protect eggs and young inside their mouths.,inactive
+7507,oscar,Tropical freshwater fish,Fresh Water,river,Spring | Summer | Fall | Winter,Day,Any,Any,Uncommon,0.8,0.3,4,6,14,0.9,,,,oscars are large south american cichlids that recognize routines and defend territories. a tail eyespot may redirect attacks away from the head.,inactive
+7508,Siamese fighting fish,Tropical freshwater fish,Fresh Water,pond,Spring | Summer | Fall | Winter,Day,Required,Any,Uncommon,1,0.01,0.15,5,10,1,,,,siamese fighting fish breathe surface air with a labyrinth organ. males build bubble nests and guard the eggs beneath them.,inactive
+7509,tambaqui,Tropical freshwater fish,Fresh Water,river,Spring | Summer | Fall | Winter,Day,Required,Any,Epic,0.1,8,80,30,68,0.82,,,,tambaqui follow seasonal floods into forests to feed on fruit and seeds. strong square teeth crack tough plant material.,inactive
+7510,walking catfish,Tropical freshwater fish,Fresh Water,pond,Spring | Summer | Fall | Winter,Night,Required,Any,Rare,0.3,0.5,5,11,24,0.9,,,,walking catfish wriggle across wet ground using stiff pectoral spines for support. an air-breathing organ helps them survive the journey.,inactive
+7601,giant snakehead,Tropical freshwater predators,Fresh Water,river,Spring | Summer | Fall | Winter,Night,Any,Required,Epic,0.1,3,45,28,64,0.84,,,,giant snakeheads breathe air and guard schools of young near the surface. long bodies launch sudden attacks from flooded vegetation.,inactive
+7602,golden dorado,Tropical freshwater predators,Fresh Water,river,Spring | Summer | Fall | Winter,Day,Required,Any,Epic,0.1,5,55,29,66,0.82,,,,"golden dorado chase fish through powerful south american rivers. muscular tails, heavy jaws, and golden scales suit life in fast current.",inactive
+7603,golden mahseer,Tropical freshwater predators,Fresh Water,river,Spring | Summer | Fall | Winter,Day,Required,Any,Epic,0.09,8,90,32,72,0.82,,,,golden mahseer migrate through swift mountain rivers to reach spawning grounds. large scales shine bronze beneath reddish fins.,inactive
+7604,peacock bass,Tropical freshwater predators,Fresh Water,river,Spring | Summer | Fall | Winter,Day,Any,Any,Rare,0.25,1,25,15,36,0.84,,,,peacock bass are aggressive south american cichlids rather than true bass. adults guard nests and hunt fish in warm clear water.,inactive
+7605,red-bellied piranha,Tropical freshwater predators,Fresh Water,river,Spring | Summer | Fall | Winter,Any,Required,Any,Rare,0.3,0.2,4,12,27,0.9,,,,"red-bellied piranhas communicate with barks and drum-like sounds. schools provide protection as they search for fish, insects, and carrion.",inactive
+7606,silver arowana,Tropical freshwater predators,Fresh Water,river,Spring | Summer | Fall | Winter,Day,Any,Required,Epic,0.11,1,15,25,56,0.86,,,,silver arowanas patrol the surface with upturned mouths and long bodies. powerful jumps capture insects and small animals above the water.,inactive
+7701,blue whale,Whales and dolphins,Salt Water,ocean,Spring | Summer | Fall | Winter,Day,Any,Forbidden,Legendary,0.005,100000,330000,120,300,0.72,,,,blue whales are the largest animals known to have lived. pleated throats expand around vast mouthfuls of water that baleen filters for krill.,inactive
+7702,common bottlenose dolphin,Whales and dolphins,Salt Water,ocean,Spring | Summer | Fall | Winter,Day,Any,Forbidden,Legendary,0.012,330,1400,75,180,0.76,,,,"common bottlenose dolphins coordinate with whistles, clicks, and body signals. echolocation reveals prey hidden in dark or cloudy water.",inactive
+7703,humpback whale,Whales and dolphins,Salt Water,ocean,Spring | Fall | Winter,Day,Any,Required,Legendary,0.008,40000,90000,105,260,0.73,,,,humpback whales use long pectoral fins while turning and feeding. males produce layered songs that change across breeding seasons.,inactive
+7704,killer whale,Whales and dolphins,Salt Water,ocean,Spring | Fall | Winter,Day,Required,Any,Legendary,0.008,3000,12000,95,230,0.74,,,,killer whales live in close family groups with distinct calls and hunting traditions. different populations specialize in different prey.,inactive
+7705,sperm whale,Whales and dolphins,Salt Water,ocean,Spring | Summer | Fall | Winter,Night,Any,Required,Legendary,0.006,15000,125000,115,280,0.72,,,,sperm whales make deep dives to hunt squid with powerful echolocation clicks. their enormous square heads contain a waxy spermaceti organ.,inactive
diff --git a/docs/design/fish_catalog_proposal_final.csv b/docs/design/fish_catalog_proposal_final.csv
new file mode 100644
index 0000000..e0b402f
--- /dev/null
+++ b/docs/design/fish_catalog_proposal_final.csv
@@ -0,0 +1,314 @@
+catalog_number,id,implementation_status,active_status,art_status,fish_resource_path,display_texture_path,catch_profile_id
+101,bowfin,implemented,inactive,missing icon,res://fish/species/bowfin/bowfin.tres,,common
+102,sturgeon_lake,implemented,inactive,missing icon,res://fish/species/sturgeon_lake/sturgeon_lake.tres,,common
+103,gar_longnose,implemented,inactive,missing icon,res://fish/species/gar_longnose/gar_longnose.tres,,common
+104,paddlefish,implemented,inactive,missing icon,res://fish/species/paddlefish/paddlefish.tres,,common
+105,sturgeon_shovelnose,implemented,inactive,missing icon,res://fish/species/sturgeon_shovelnose/sturgeon_shovelnose.tres,,common
+106,gar_spotted,implemented,inactive,missing icon,res://fish/species/gar_spotted/gar_spotted.tres,,common
+107,lungfish_west_african,implemented,inactive,missing icon,res://fish/species/lungfish_west_african/lungfish_west_african.tres,,common
+201,mudskipper_atlantic,implemented,inactive,missing icon,res://fish/species/mudskipper_atlantic/mudskipper_atlantic.tres,,common
+202,foureyes_largescale,implemented,inactive,missing icon,res://fish/species/foureyes_largescale/foureyes_largescale.tres,,common
+301,mullet_red,implemented,inactive,missing icon,res://fish/species/mullet_red/mullet_red.tres,,common
+401,plaice_european,implemented,inactive,missing icon,res://fish/species/plaice_european/plaice_european.tres,,common
+402,flounder_summer,implemented,inactive,missing icon,res://fish/species/flounder_summer/flounder_summer.tres,,common
+403,flounder_winter,implemented,inactive,missing icon,res://fish/species/flounder_winter/flounder_winter.tres,,common
+501,dugong,implemented,inactive,missing icon,res://fish/species/dugong/dugong.tres,,common
+502,seal_harbor,implemented,inactive,missing icon,res://fish/species/seal_harbor/seal_harbor.tres,,common
+503,sea_otter,implemented,inactive,missing icon,res://fish/species/sea_otter/sea_otter.tres,,common
+504,walrus,implemented,inactive,missing icon,res://fish/species/walrus/walrus.tres,,common
+505,manatee_west_indian,implemented,inactive,missing icon,res://fish/species/manatee_west_indian/manatee_west_indian.tres,,common
+601,pipefish_bay,implemented,inactive,missing icon,res://fish/species/pipefish_bay/pipefish_bay.tres,,common
+602,seahorse_lined,implemented,inactive,missing icon,res://fish/species/seahorse_lined/seahorse_lined.tres,,common
+603,monkfish,implemented,inactive,missing icon,res://fish/species/monkfish/monkfish.tres,,common
+604,tripletail,implemented,inactive,missing icon,res://fish/species/tripletail/tripletail.tres,,common
+701,mackerel_atlantic,implemented,active,complete,res://fish/species/mackerel_atlantic/mackerel_atlantic.tres,res://fish/species/mackerel_atlantic/fish_mackerel_atlantic.png,common
+702,bluefish,implemented,inactive,missing icon,res://fish/species/bluefish/bluefish.tres,,common
+703,mackerel_cero,implemented,active,complete,res://fish/species/mackerel_cero/mackerel_cero.tres,res://fish/species/mackerel_cero/fish_mackerel_cero.png,common
+704,mackerel_chub,implemented,active,complete,res://fish/species/mackerel_chub/mackerel_chub.tres,res://fish/species/mackerel_chub/fish_mackerel_chub.png,common
+705,jack_crevalle,implemented,inactive,missing icon,res://fish/species/jack_crevalle/jack_crevalle.tres,,common
+706,mackerel_king,implemented,active,complete,res://fish/species/mackerel_king/mackerel_king.tres,res://fish/species/mackerel_king/fish_mackerel_king.png,common
+707,mackerel_spanish,implemented,active,complete,res://fish/species/mackerel_spanish/mackerel_spanish.tres,res://fish/species/mackerel_spanish/fish_mackerel_spanish.png,common
+708,queenfish_talang,implemented,inactive,missing icon,res://fish/species/queenfish_talang/queenfish_talang.tres,,common
+801,sand_lance_american,implemented,inactive,missing icon,res://fish/species/sand_lance_american/sand_lance_american.tres,,common
+802,menhaden_atlantic,implemented,inactive,missing icon,res://fish/species/menhaden_atlantic/menhaden_atlantic.tres,,common
+803,pomfret_black,implemented,active,complete,res://fish/species/pomfret_black/pomfret_black.tres,res://fish/species/pomfret_black/fish_pomfret_black.png,common
+804,pomfret_chinese,implemented,active,complete,res://fish/species/pomfret_chinese/pomfret_chinese.tres,res://fish/species/pomfret_chinese/fish_pomfret_chinese.png,common
+805,anchovy_european,implemented,active,complete,res://fish/species/anchovy_european/anchovy_european.tres,res://fish/species/anchovy_european/fish_anchovy_european.png,common
+806,sardine_european,implemented,inactive,missing icon,res://fish/species/sardine_european/sardine_european.tres,,common
+807,pomfret_golden,implemented,active,complete,res://fish/species/pomfret_golden/pomfret_golden.tres,res://fish/species/pomfret_golden/fish_pomfret_golden.png,common
+808,anchovy_northern,implemented,active,complete,res://fish/species/anchovy_northern/anchovy_northern.tres,res://fish/species/anchovy_northern/fish_anchovy_northern.png,common
+809,sardine_pacific,implemented,inactive,missing icon,res://fish/species/sardine_pacific/sardine_pacific.tres,,common
+810,pomfret_white,implemented,active,complete,res://fish/species/pomfret_white/pomfret_white.tres,res://fish/species/pomfret_white/fish_pomfret_white.png,common
+901,shark_blacktip,implemented,inactive,missing icon,res://fish/species/shark_blacktip/shark_blacktip.tres,,common
+902,shark_great_hammerhead,implemented,inactive,missing icon,res://fish/species/shark_great_hammerhead/shark_great_hammerhead.tres,,common
+903,shark_great_white,implemented,inactive,missing icon,res://fish/species/shark_great_white/shark_great_white.tres,,common
+904,shark_tiger,implemented,inactive,missing icon,res://fish/species/shark_tiger/shark_tiger.tres,,common
+905,shark_whale,implemented,inactive,missing icon,res://fish/species/shark_whale/shark_whale.tres,,common
+1001,burbot,implemented,inactive,missing icon,res://fish/species/burbot/burbot.tres,,common
+1002,cisco,implemented,inactive,missing icon,res://fish/species/cisco/cisco.tres,,common
+1003,whitefish_lake,implemented,inactive,missing icon,res://fish/species/whitefish_lake/whitefish_lake.tres,,common
+1101,char_arctic,implemented,inactive,missing icon,res://fish/species/char_arctic/char_arctic.tres,,common
+1102,grayling_arctic,implemented,inactive,missing icon,res://fish/species/grayling_arctic/grayling_arctic.tres,,common
+1103,trout_brown,implemented,inactive,missing icon,res://fish/species/trout_brown/trout_brown.tres,,common
+1104,trout_cutthroat,implemented,active,complete,res://fish/species/trout_cutthroat/trout_cutthroat.tres,res://fish/species/trout_cutthroat/fish_trout_cutthroat.png,common
+1105,trout_golden,implemented,active,complete,res://fish/species/trout_golden/trout_golden.tres,res://fish/species/trout_golden/fish_trout_golden.png,common
+1106,trout_rainbow,implemented,active,complete,res://fish/species/trout_rainbow/trout_rainbow.tres,res://fish/species/trout_rainbow/fish_trout_rainbow.png,common
+1201,wolffish_atlantic,implemented,inactive,missing icon,res://fish/species/wolffish_atlantic/wolffish_atlantic.tres,,common
+1202,cusk,implemented,inactive,missing icon,res://fish/species/cusk/cusk.tres,,common
+1203,wolffish_spotted,implemented,inactive,missing icon,res://fish/species/wolffish_spotted/wolffish_spotted.tres,,common
+1301,herring_atlantic,implemented,inactive,missing icon,res://fish/species/herring_atlantic/herring_atlantic.tres,,common
+1302,capelin,implemented,inactive,missing icon,res://fish/species/capelin/capelin.tres,,common
+1303,sprat_european,implemented,inactive,missing icon,res://fish/species/sprat_european/sprat_european.tres,,common
+1304,herring_pacific,implemented,inactive,missing icon,res://fish/species/herring_pacific/herring_pacific.tres,,common
+1401,pollock_alaska,implemented,inactive,missing icon,res://fish/species/pollock_alaska/pollock_alaska.tres,,common
+1402,cod_atlantic,implemented,inactive,missing icon,res://fish/species/cod_atlantic/cod_atlantic.tres,,common
+1403,pollock_atlantic,implemented,inactive,missing icon,res://fish/species/pollock_atlantic/pollock_atlantic.tres,,common
+1404,sole_dover,implemented,inactive,missing icon,res://fish/species/sole_dover/sole_dover.tres,,common
+1405,haddock,implemented,inactive,missing icon,res://fish/species/haddock/haddock.tres,,common
+1406,cod_pacific,implemented,inactive,missing icon,res://fish/species/cod_pacific/cod_pacific.tres,,common
+1407,hake_pacific,implemented,inactive,missing icon,res://fish/species/hake_pacific/hake_pacific.tres,,common
+1408,hake_silver,implemented,inactive,missing icon,res://fish/species/hake_silver/hake_silver.tres,,common
+1501,nautilus_chambered,implemented,inactive,missing icon,res://fish/species/nautilus_chambered/nautilus_chambered.tres,,common
+1502,cuttlefish_common,implemented,inactive,missing icon,res://fish/species/cuttlefish_common/cuttlefish_common.tres,,common
+1503,cuttlefish_flamboyant,implemented,inactive,missing icon,res://fish/species/cuttlefish_flamboyant/cuttlefish_flamboyant.tres,,common
+1601,tilefish_blueline,implemented,inactive,missing icon,res://fish/species/tilefish_blueline/tilefish_blueline.tres,,common
+1602,tilefish_golden,implemented,inactive,missing icon,res://fish/species/tilefish_golden/tilefish_golden.tres,,common
+1603,ocean_perch_pacific,implemented,inactive,missing icon,res://fish/species/ocean_perch_pacific/ocean_perch_pacific.tres,,common
+1604,sea_bass_chilean,implemented,inactive,missing icon,res://fish/species/sea_bass_chilean/sea_bass_chilean.tres,,common
+1605,sablefish,implemented,inactive,missing icon,res://fish/species/sablefish/sablefish.tres,,common
+1606,wreckfish,implemented,inactive,missing icon,res://fish/species/wreckfish/wreckfish.tres,,common
+1701,oarfish_giant,implemented,inactive,missing icon,res://fish/species/oarfish_giant/oarfish_giant.tres,,common
+1702,coelacanth_west_indian,implemented,inactive,missing icon,res://fish/species/coelacanth_west_indian/coelacanth_west_indian.tres,,common
+1801,anglerfish_black_seadevil,implemented,inactive,missing icon,res://fish/species/anglerfish_black_seadevil/anglerfish_black_seadevil.tres,,common
+1802,fangtooth_common,implemented,inactive,missing icon,res://fish/species/fangtooth_common/fangtooth_common.tres,,common
+1803,gulper_eel,implemented,inactive,missing icon,res://fish/species/gulper_eel/gulper_eel.tres,,common
+1804,barreleye_pacific,implemented,inactive,missing icon,res://fish/species/barreleye_pacific/barreleye_pacific.tres,,common
+1805,blobfish_smooth_head,implemented,inactive,missing icon,res://fish/species/blobfish_smooth_head/blobfish_smooth_head.tres,,common
+1806,lanternfish_spotted,implemented,inactive,missing icon,res://fish/species/lanternfish_spotted/lanternfish_spotted.tres,,common
+1901,sea_star_crown_of_thorns,implemented,inactive,missing icon,res://fish/species/sea_star_crown_of_thorns/sea_star_crown_of_thorns.tres,,common
+1902,sea_cucumber_giant_california,implemented,inactive,missing icon,res://fish/species/sea_cucumber_giant_california/sea_cucumber_giant_california.tres,,common
+1903,urchin_purple,implemented,inactive,missing icon,res://fish/species/urchin_purple/urchin_purple.tres,,common
+1904,sand_dollar,implemented,inactive,missing icon,res://fish/species/sand_dollar/sand_dollar.tres,,common
+1905,sea_star_sunflower,implemented,inactive,missing icon,res://fish/species/sea_star_sunflower/sea_star_sunflower.tres,,common
+2001,axolotl,implemented,inactive,missing icon,res://fish/species/axolotl/axolotl.tres,,common
+2002,hellbender,implemented,inactive,missing icon,res://fish/species/hellbender/hellbender.tres,,common
+2101,guppy,implemented,inactive,missing icon,res://fish/species/guppy/guppy.tres,,common
+2102,tetra_neon,implemented,inactive,missing icon,res://fish/species/tetra_neon/tetra_neon.tres,,common
+2103,danio_zebra,implemented,inactive,missing icon,res://fish/species/danio_zebra/danio_zebra.tres,,common
+2201,largemouth_bass,implemented,inactive,missing icon,res://fish/species/largemouth_bass/largemouth_bass.tres,,common
+2202,smallmouth_bass,implemented,inactive,missing icon,res://fish/species/smallmouth_bass/smallmouth_bass.tres,,common
+2301,buffalo_bigmouth,implemented,inactive,missing icon,res://fish/species/buffalo_bigmouth/buffalo_bigmouth.tres,,common
+2302,redhorse_golden,implemented,inactive,missing icon,res://fish/species/redhorse_golden/redhorse_golden.tres,,common
+2303,quillback,implemented,inactive,missing icon,res://fish/species/quillback/quillback.tres,,common
+2304,buffalo_smallmouth,implemented,inactive,missing icon,res://fish/species/buffalo_smallmouth/buffalo_smallmouth.tres,,common
+2305,sucker_white,implemented,inactive,missing icon,res://fish/species/sucker_white/sucker_white.tres,,common
+2401,bullhead_black,implemented,inactive,missing icon,res://fish/species/bullhead_black/bullhead_black.tres,,common
+2402,catfish_blue,implemented,active,complete,res://fish/species/catfish_blue/catfish_blue.tres,res://fish/species/catfish_blue/fish_catfish_blue.png,bass
+2403,bullhead_brown,implemented,inactive,missing icon,res://fish/species/bullhead_brown/bullhead_brown.tres,,common
+2404,catfish_channel,implemented,active,complete,res://fish/species/catfish_channel/catfish_channel.tres,res://fish/species/catfish_channel/fish_catfish_channel.png,common
+2405,catfish_flathead,implemented,active,complete,res://fish/species/catfish_flathead/catfish_flathead.tres,res://fish/species/catfish_flathead/fish_catfish_flathead.png,bass
+2406,madtom_tadpole,implemented,inactive,missing icon,res://fish/species/madtom_tadpole/madtom_tadpole.tres,,common
+2407,catfish_white,implemented,active,complete,res://fish/species/catfish_white/catfish_white.tres,res://fish/species/catfish_white/fish_catfish_white.png,common
+2408,bullhead_yellow,implemented,inactive,missing icon,res://fish/species/bullhead_yellow/bullhead_yellow.tres,,common
+2501,barbel_common,implemented,inactive,missing icon,res://fish/species/barbel_common/barbel_common.tres,,common
+2502,bream_common,implemented,inactive,missing icon,res://fish/species/bream_common/bream_common.tres,,common
+2503,carp,implemented,active,complete,res://fish/species/carp/carp.tres,res://fish/species/carp/fish_carp_common.png,carp
+2504,roach_common,implemented,inactive,missing icon,res://fish/species/roach_common/roach_common.tres,,common
+2505,chub_european,implemented,active,complete,res://fish/species/chub_european/chub_european.tres,res://fish/species/chub_european/fish_chub_european.png,common
+2506,fallfish,implemented,inactive,missing icon,res://fish/species/fallfish/fallfish.tres,,common
+2507,ide,implemented,inactive,missing icon,res://fish/species/ide/ide.tres,,common
+2508,rudd,implemented,inactive,missing icon,res://fish/species/rudd/rudd.tres,,common
+2509,tench,implemented,inactive,missing icon,res://fish/species/tench/tench.tres,,common
+2601,crayfish_red_swamp,implemented,inactive,missing icon,res://fish/species/crayfish_red_swamp/crayfish_red_swamp.tres,,common
+2701,gar_alligator,implemented,inactive,missing icon,res://fish/species/gar_alligator/gar_alligator.tres,,common
+2702,arapaima,implemented,inactive,missing icon,res://fish/species/arapaima/arapaima.tres,,common
+2703,tigerfish_goliath,implemented,inactive,missing icon,res://fish/species/tigerfish_goliath/tigerfish_goliath.tres,,common
+2704,muskellunge,implemented,inactive,missing icon,res://fish/species/muskellunge/muskellunge.tres,,common
+2801,shiner_common,implemented,inactive,missing icon,res://fish/species/shiner_common/shiner_common.tres,,common
+2802,chub_creek,implemented,inactive,missing icon,res://fish/species/chub_creek/chub_creek.tres,,common
+2803,shiner_emerald,implemented,inactive,missing icon,res://fish/species/shiner_emerald/shiner_emerald.tres,,common
+2804,minnow_fathead,implemented,inactive,missing icon,res://fish/species/minnow_fathead/minnow_fathead.tres,,common
+2805,chub_flame,implemented,active,complete,res://fish/species/chub_flame/chub_flame.tres,res://fish/species/chub_flame/fish_chub_flame.png,common
+2806,shiner_golden,implemented,inactive,missing icon,res://fish/species/shiner_golden/shiner_golden.tres,,common
+2807,chub_lake,implemented,active,complete,res://fish/species/chub_lake/chub_lake.tres,res://fish/species/chub_lake/fish_chub_lake.png,common
+2901,electric_eel,implemented,inactive,missing icon,res://fish/species/electric_eel/electric_eel.tres,,common
+2902,freshwater_drum,implemented,inactive,missing icon,res://fish/species/freshwater_drum/freshwater_drum.tres,,common
+2903,goldeye,implemented,inactive,missing icon,res://fish/species/goldeye/goldeye.tres,,common
+2904,mooneye,implemented,inactive,missing icon,res://fish/species/mooneye/mooneye.tres,,common
+2905,loach_weather,implemented,inactive,missing icon,res://fish/species/loach_weather/loach_weather.tres,,common
+3001,goldfish_bubbleeye,implemented,active,complete,res://fish/species/goldfish_bubbleeye/goldfish_bubbleeye.tres,res://fish/species/goldfish_bubbleeye/fish_goldfish_bubbleeye.png,common
+3002,goldfish,implemented,active,complete,res://fish/species/goldfish/goldfish.tres,res://fish/species/goldfish/fish_goldfish.png,common
+3003,koi,implemented,inactive,missing icon,res://fish/species/koi/koi.tres,,common
+3101,crappie_black,implemented,inactive,missing icon,res://fish/species/crappie_black/crappie_black.tres,,common
+3102,bluegill,implemented,active,complete,res://fish/species/bluegill/bluegill.tres,res://fish/species/bluegill/fish_bluegill.png,common
+3103,green_sunfish,implemented,inactive,missing icon,res://fish/species/green_sunfish/green_sunfish.tres,,common
+3104,pumpkinseed,implemented,inactive,missing icon,res://fish/species/pumpkinseed/pumpkinseed.tres,,common
+3105,redear_sunfish,implemented,inactive,missing icon,res://fish/species/redear_sunfish/redear_sunfish.tres,,common
+3106,warmouth,implemented,inactive,missing icon,res://fish/species/warmouth/warmouth.tres,,common
+3107,crappie_white,implemented,inactive,missing icon,res://fish/species/crappie_white/crappie_white.tres,,common
+3201,yellow_perch,implemented,inactive,missing icon,res://fish/species/yellow_perch/yellow_perch.tres,,common
+3301,pickerel_chain,implemented,inactive,missing icon,res://fish/species/pickerel_chain/pickerel_chain.tres,,common
+3302,pickerel_grass,implemented,inactive,missing icon,res://fish/species/pickerel_grass/pickerel_grass.tres,,common
+3303,pike_northern,implemented,inactive,missing icon,res://fish/species/pike_northern/pike_northern.tres,,common
+3304,sauger,implemented,active,complete,res://fish/species/sauger/sauger.tres,res://fish/species/sauger/fish_sauger.png,common
+3305,saugeye,implemented,active,complete,res://fish/species/saugeye/saugeye.tres,res://fish/species/saugeye/fish_saugeye.png,common
+3306,walleye,implemented,active,complete,res://fish/species/walleye/walleye.tres,res://fish/species/walleye/fish_walleye.png,common
+3401,goby_round,implemented,active,complete,res://fish/species/goby_round/goby_round.tres,res://fish/species/goby_round/fish_goby_round.png,common
+3501,jelly_crystal,implemented,inactive,missing icon,res://fish/species/jelly_crystal/jelly_crystal.tres,,common
+3502,jelly_lions_mane,implemented,inactive,missing icon,res://fish/species/jelly_lions_mane/jelly_lions_mane.tres,,common
+3503,jelly_moon,implemented,inactive,missing icon,res://fish/species/jelly_moon/jelly_moon.tres,,common
+3504,jelly_pacific_sea_nettle,implemented,inactive,missing icon,res://fish/species/jelly_pacific_sea_nettle/jelly_pacific_sea_nettle.tres,,common
+3505,jelly_sea_wasp,implemented,inactive,missing icon,res://fish/species/jelly_sea_wasp/jelly_sea_wasp.tres,,common
+3601,pompano_african,implemented,inactive,missing icon,res://fish/species/pompano_african/pompano_african.tres,,common
+3602,cobia,implemented,inactive,missing icon,res://fish/species/cobia/cobia.tres,,common
+3603,trevally_giant,implemented,inactive,missing icon,res://fish/species/trevally_giant/trevally_giant.tres,,common
+3604,amberjack_greater,implemented,inactive,missing icon,res://fish/species/amberjack_greater/amberjack_greater.tres,,common
+3605,amberjack_lesser,implemented,inactive,missing icon,res://fish/species/amberjack_lesser/amberjack_lesser.tres,,common
+3701,halibut_atlantic,implemented,inactive,missing icon,res://fish/species/halibut_atlantic/halibut_atlantic.tres,,common
+3702,halibut_pacific,implemented,inactive,missing icon,res://fish/species/halibut_pacific/halibut_pacific.tres,,common
+3703,turbot,implemented,inactive,missing icon,res://fish/species/turbot/turbot.tres,,common
+3801,lobster_american,implemented,inactive,missing icon,res://fish/species/lobster_american/lobster_american.tres,,common
+3802,krill_antarctic,implemented,inactive,missing icon,res://fish/species/krill_antarctic/krill_antarctic.tres,,common
+3803,lobster_caribbean_spiny,implemented,inactive,missing icon,res://fish/species/lobster_caribbean_spiny/lobster_caribbean_spiny.tres,,common
+3804,shrimp_peacock_mantis,implemented,inactive,missing icon,res://fish/species/shrimp_peacock_mantis/shrimp_peacock_mantis.tres,,common
+3805,shrimp_skunk_cleaner,implemented,inactive,missing icon,res://fish/species/shrimp_skunk_cleaner/shrimp_skunk_cleaner.tres,,common
+3901,crab_ghost,implemented,inactive,missing icon,res://fish/species/crab_ghost/crab_ghost.tres,,common
+3902,crab_blue,implemented,inactive,missing icon,res://fish/species/crab_blue/crab_blue.tres,,common
+3903,crab_dungeness,implemented,inactive,missing icon,res://fish/species/crab_dungeness/crab_dungeness.tres,,common
+3904,crab_japanese_spider,implemented,inactive,missing icon,res://fish/species/crab_japanese_spider/crab_japanese_spider.tres,,common
+3905,crab_red_king,implemented,inactive,missing icon,res://fish/species/crab_red_king/crab_red_king.tres,,common
+4001,eel_american,implemented,inactive,missing icon,res://fish/species/eel_american/eel_american.tres,,common
+4002,eel_european,implemented,inactive,missing icon,res://fish/species/eel_european/eel_european.tres,,common
+4101,smelt_rainbow,implemented,inactive,missing icon,res://fish/species/smelt_rainbow/smelt_rainbow.tres,,common
+4201,smelt_eulachon,implemented,inactive,missing icon,res://fish/species/smelt_eulachon/smelt_eulachon.tres,,common
+4301,sturgeon_white,implemented,inactive,missing icon,res://fish/species/sturgeon_white/sturgeon_white.tres,,common
+4401,salmon_atlantic,implemented,active,complete,res://fish/species/salmon_atlantic/salmon_atlantic.tres,res://fish/species/salmon_atlantic/fish_salmon_atlantic.png,bass
+4402,salmon_chum,implemented,active,complete,res://fish/species/salmon_chum/salmon_chum.tres,res://fish/species/salmon_chum/fish_salmon_chum.png,bass
+4403,salmon_coho,implemented,active,complete,res://fish/species/salmon_coho/salmon_coho.tres,res://fish/species/salmon_coho/fish_salmon_coho.png,bass
+4404,salmon_pink,implemented,active,complete,res://fish/species/salmon_pink/salmon_pink.tres,res://fish/species/salmon_pink/fish_salmon_pink.png,bass
+4405,salmon_sockeye,implemented,active,complete,res://fish/species/salmon_sockeye/salmon_sockeye.tres,res://fish/species/salmon_sockeye/fish_salmon_sockeye.png,bass
+4501,trout_steelhead,implemented,active,complete,res://fish/species/trout_steelhead/trout_steelhead.tres,res://fish/species/trout_steelhead/fish_trout_steelhead.png,common
+4601,marlin_black,implemented,active,complete,res://fish/species/marlin_black/marlin_black.tres,res://fish/species/marlin_black/fish_marlin_black.png,common
+4602,marlin_blue,implemented,active,complete,res://fish/species/marlin_blue/marlin_blue.tres,res://fish/species/marlin_blue/fish_marlin_blue.png,common
+4603,sailfish,implemented,active,complete,res://fish/species/sailfish/sailfish.tres,res://fish/species/sailfish/fish_sailfish.png,common
+4604,swordfish,implemented,active,complete,res://fish/species/swordfish/swordfish.tres,res://fish/species/swordfish/fish_swordfish.png,common
+4605,marlin_white,implemented,active,complete,res://fish/species/marlin_white/marlin_white.tres,res://fish/species/marlin_white/fish_marlin_white.png,common
+4701,mahi_mahi,implemented,inactive,missing icon,res://fish/species/mahi_mahi/mahi_mahi.tres,,common
+4702,opah,implemented,inactive,missing icon,res://fish/species/opah/opah.tres,,common
+4703,wahoo,implemented,inactive,missing icon,res://fish/species/wahoo/wahoo.tres,,common
+4801,tuna_albacore,implemented,active,complete,res://fish/species/tuna_albacore/tuna_albacore.tres,res://fish/species/tuna_albacore/fish_tuna_albacore.png,bass
+4802,tuna_bigeye,implemented,active,complete,res://fish/species/tuna_bigeye/tuna_bigeye.tres,res://fish/species/tuna_bigeye/fish_tuna_bigeye.png,bass
+4803,tuna_bluefin,implemented,active,complete,res://fish/species/tuna_bluefin/tuna_bluefin.tres,res://fish/species/tuna_bluefin/fish_tuna_bluefin.png,bass
+4804,tuna_skipjack,implemented,active,complete,res://fish/species/tuna_skipjack/tuna_skipjack.tres,res://fish/species/tuna_skipjack/fish_tuna_skipjack.png,bass
+4805,tuna_yellowfin,implemented,active,complete,res://fish/species/tuna_yellowfin/tuna_yellowfin.tres,res://fish/species/tuna_yellowfin/fish_tuna_yellowfin.png,bass
+4901,octopus_common,implemented,inactive,missing icon,res://fish/species/octopus_common/octopus_common.tres,,common
+4902,octopus_day,implemented,inactive,missing icon,res://fish/species/octopus_day/octopus_day.tres,,common
+4903,octopus_giant_pacific,implemented,inactive,missing icon,res://fish/species/octopus_giant_pacific/octopus_giant_pacific.tres,,common
+4904,octopus_greater_blue_ringed,implemented,inactive,missing icon,res://fish/species/octopus_greater_blue_ringed/octopus_greater_blue_ringed.tres,,common
+5001,sunfish,implemented,active,complete,res://fish/species/sunfish/sunfish.tres,res://fish/species/sunfish/fish_sunfish.png,common
+5101,skate_barndoor,implemented,inactive,missing icon,res://fish/species/skate_barndoor/skate_barndoor.tres,,common
+5102,manta_ray_giant,implemented,inactive,missing icon,res://fish/species/manta_ray_giant/manta_ray_giant.tres,,common
+5103,sawfish_largetooth,implemented,inactive,missing icon,res://fish/species/sawfish_largetooth/sawfish_largetooth.tres,,common
+5104,stingray_ocellate_river,implemented,inactive,missing icon,res://fish/species/stingray_ocellate_river/stingray_ocellate_river.tres,,common
+5105,stingray_southern,implemented,inactive,missing icon,res://fish/species/stingray_southern/stingray_southern.tres,,common
+5201,lionfish_red,implemented,inactive,missing icon,res://fish/species/lionfish_red/lionfish_red.tres,,common
+5202,scorpionfish_red,implemented,inactive,missing icon,res://fish/species/scorpionfish_red/scorpionfish_red.tres,,common
+5301,moray_green,implemented,inactive,missing icon,res://fish/species/moray_green/moray_green.tres,,common
+5401,flounder_peacock,implemented,inactive,missing icon,res://fish/species/flounder_peacock/flounder_peacock.tres,,common
+5501,surgeonfish_blue,implemented,inactive,missing icon,res://fish/species/surgeonfish_blue/surgeonfish_blue.tres,,common
+5502,parrotfish_rainbow,implemented,inactive,missing icon,res://fish/species/parrotfish_rainbow/parrotfish_rainbow.tres,,common
+5503,parrotfish_stoplight,implemented,inactive,missing icon,res://fish/species/parrotfish_stoplight/parrotfish_stoplight.tres,,common
+5504,tang_yellow,implemented,inactive,missing icon,res://fish/species/tang_yellow/tang_yellow.tres,,common
+5601,trumpetfish_atlantic,implemented,inactive,missing icon,res://fish/species/trumpetfish_atlantic/trumpetfish_atlantic.tres,,common
+5602,triggerfish_gray,implemented,inactive,missing icon,res://fish/species/triggerfish_gray/triggerfish_gray.tres,,common
+5603,pufferfish_guineafowl,implemented,inactive,missing icon,res://fish/species/pufferfish_guineafowl/pufferfish_guineafowl.tres,,common
+5604,cowfish_longhorn,implemented,inactive,missing icon,res://fish/species/cowfish_longhorn/cowfish_longhorn.tres,,common
+5605,triggerfish_queen,implemented,inactive,missing icon,res://fish/species/triggerfish_queen/triggerfish_queen.tres,,common
+5606,cornetfish_red,implemented,inactive,missing icon,res://fish/species/cornetfish_red/cornetfish_red.tres,,common
+5607,porcupinefish_spotted,implemented,inactive,missing icon,res://fish/species/porcupinefish_spotted/porcupinefish_spotted.tres,,common
+5608,boxfish_yellow,implemented,inactive,missing icon,res://fish/species/boxfish_yellow/boxfish_yellow.tres,,common
+5701,butterflyfish_copperband,implemented,inactive,missing icon,res://fish/species/butterflyfish_copperband/butterflyfish_copperband.tres,,common
+5702,clownfish_ocellaris,implemented,inactive,missing icon,res://fish/species/clownfish_ocellaris/clownfish_ocellaris.tres,,common
+5703,angelfish_queen,implemented,inactive,missing icon,res://fish/species/angelfish_queen/angelfish_queen.tres,,common
+5801,grouper_atlantic_goliath,implemented,inactive,missing icon,res://fish/species/grouper_atlantic_goliath/grouper_atlantic_goliath.tres,,common
+5802,trevally_bluefin,implemented,inactive,missing icon,res://fish/species/trevally_bluefin/trevally_bluefin.tres,,common
+5803,grouper_giant,implemented,inactive,missing icon,res://fish/species/grouper_giant/grouper_giant.tres,,common
+5804,barracuda_great,implemented,inactive,missing icon,res://fish/species/barracuda_great/barracuda_great.tres,,common
+5805,grouper_gulf,implemented,active,complete,res://fish/species/grouper_gulf/grouper_gulf.tres,res://fish/species/grouper_gulf/fish_grouper_gulf.png,common
+5806,grouper_nassau,implemented,inactive,missing icon,res://fish/species/grouper_nassau/grouper_nassau.tres,,common
+5807,grouper_red,implemented,active,complete,res://fish/species/grouper_red/grouper_red.tres,res://fish/species/grouper_red/fish_grouper_red.png,common
+5901,chromis_blue_green,implemented,inactive,missing icon,res://fish/species/chromis_blue_green/chromis_blue_green.tres,,common
+5902,grunt_bluestriped,implemented,inactive,missing icon,res://fish/species/grunt_bluestriped/grunt_bluestriped.tres,,common
+5903,grunt_french,implemented,inactive,missing icon,res://fish/species/grunt_french/grunt_french.tres,,common
+5904,damselfish_sergeant_major,implemented,inactive,missing icon,res://fish/species/damselfish_sergeant_major/damselfish_sergeant_major.tres,,common
+6001,snapper_lane,implemented,active,complete,res://fish/species/snapper_lane/snapper_lane.tres,res://fish/species/snapper_lane/fish_snapper_lane.png,common
+6002,snapper_mangrove,implemented,active,complete,res://fish/species/snapper_mangrove/snapper_mangrove.tres,res://fish/species/snapper_mangrove/fish_snapper_mangrove.png,common
+6003,snapper_mutton,implemented,active,complete,res://fish/species/snapper_mutton/snapper_mutton.tres,res://fish/species/snapper_mutton/fish_snapper_mutton.png,common
+6004,snapper_red,implemented,active,complete,res://fish/species/snapper_red/snapper_red.tres,res://fish/species/snapper_red/fish_snapper_red.png,common
+6101,wrasse_ballan,implemented,inactive,missing icon,res://fish/species/wrasse_ballan/wrasse_ballan.tres,,common
+6102,wrasse_cleaner,implemented,inactive,missing icon,res://fish/species/wrasse_cleaner/wrasse_cleaner.tres,,common
+6103,hogfish,implemented,inactive,missing icon,res://fish/species/hogfish/hogfish.tres,,common
+6201,turtle_green,implemented,inactive,missing icon,res://fish/species/turtle_green/turtle_green.tres,,common
+6202,turtle_hawksbill,implemented,inactive,missing icon,res://fish/species/turtle_hawksbill/turtle_hawksbill.tres,,common
+6203,turtle_leatherback,implemented,inactive,missing icon,res://fish/species/turtle_leatherback/turtle_leatherback.tres,,common
+6204,turtle_loggerhead,implemented,inactive,missing icon,res://fish/species/turtle_loggerhead/turtle_loggerhead.tres,,common
+6301,seadragon_leafy,implemented,inactive,missing icon,res://fish/species/seadragon_leafy/seadragon_leafy.tres,,common
+6302,seadragon_weedy,implemented,inactive,missing icon,res://fish/species/seadragon_weedy/seadragon_weedy.tres,,common
+6401,oyster_black_lip_pearl,implemented,inactive,missing icon,res://fish/species/oyster_black_lip_pearl/oyster_black_lip_pearl.tres,,common
+6402,clam_giant,implemented,inactive,missing icon,res://fish/species/clam_giant/clam_giant.tres,,common
+6403,clam_geoduck,implemented,inactive,missing icon,res://fish/species/clam_geoduck/clam_geoduck.tres,,common
+6404,conch_queen,implemented,inactive,missing icon,res://fish/species/conch_queen/conch_queen.tres,,common
+6405,abalone_red,implemented,inactive,missing icon,res://fish/species/abalone_red/abalone_red.tres,,common
+6501,squid_bigfin_reef,implemented,inactive,missing icon,res://fish/species/squid_bigfin_reef/squid_bigfin_reef.tres,,common
+6502,squid_colossal,implemented,inactive,missing icon,res://fish/species/squid_colossal/squid_colossal.tres,,common
+6503,squid_giant,implemented,inactive,missing icon,res://fish/species/squid_giant/squid_giant.tres,,common
+6504,squid_humboldt,implemented,inactive,missing icon,res://fish/species/squid_humboldt/squid_humboldt.tres,,common
+6601,halfbeak_ballyhoo,implemented,inactive,missing icon,res://fish/species/halfbeak_ballyhoo/halfbeak_ballyhoo.tres,,common
+6602,needlefish_hound,implemented,inactive,missing icon,res://fish/species/needlefish_hound/needlefish_hound.tres,,common
+6603,flyingfish_tropical,implemented,inactive,missing icon,res://fish/species/flyingfish_tropical/flyingfish_tropical.tres,,common
+6701,bass,implemented,active,complete,res://fish/species/bass/bass.tres,res://fish/species/bass/fish_bass_striped.png,bass
+6801,croaker_atlantic,implemented,inactive,missing icon,res://fish/species/croaker_atlantic/croaker_atlantic.tres,,common
+6802,black_drum,implemented,inactive,missing icon,res://fish/species/black_drum/black_drum.tres,,common
+6803,red_drum,implemented,inactive,missing icon,res://fish/species/red_drum/red_drum.tres,,common
+6804,mullet_striped,implemented,inactive,missing icon,res://fish/species/mullet_striped/mullet_striped.tres,,common
+6805,weakfish,implemented,inactive,missing icon,res://fish/species/weakfish/weakfish.tres,,common
+6806,white_perch,implemented,inactive,missing icon,res://fish/species/white_perch/white_perch.tres,,common
+6807,croaker_yellowfin,implemented,inactive,missing icon,res://fish/species/croaker_yellowfin/croaker_yellowfin.tres,,common
+6901,rockfish_black,implemented,inactive,missing icon,res://fish/species/rockfish_black/rockfish_black.tres,,common
+6902,sea_bass_black,implemented,inactive,missing icon,res://fish/species/sea_bass_black/sea_bass_black.tres,,common
+6903,rockfish_canary,implemented,inactive,missing icon,res://fish/species/rockfish_canary/rockfish_canary.tres,,common
+6904,porgy_scup,implemented,inactive,missing icon,res://fish/species/porgy_scup/porgy_scup.tres,,common
+6905,sheepshead,implemented,inactive,missing icon,res://fish/species/sheepshead/sheepshead.tres,,common
+6906,tautog,implemented,inactive,missing icon,res://fish/species/tautog/tautog.tres,,common
+6907,rockfish_yelloweye,implemented,inactive,missing icon,res://fish/species/rockfish_yelloweye/rockfish_yelloweye.tres,,common
+7001,lingcod,implemented,inactive,missing icon,res://fish/species/lingcod/lingcod.tres,,common
+7101,milkfish,implemented,inactive,missing icon,res://fish/species/milkfish/milkfish.tres,,common
+7201,roosterfish,implemented,inactive,missing icon,res://fish/species/roosterfish/roosterfish.tres,,common
+7301,tarpon_atlantic,implemented,inactive,missing icon,res://fish/species/tarpon_atlantic/tarpon_atlantic.tres,,common
+7302,barramundi,implemented,inactive,missing icon,res://fish/species/barramundi/barramundi.tres,,common
+7303,snook_common,implemented,inactive,missing icon,res://fish/species/snook_common/snook_common.tres,,common
+7401,bonefish,implemented,inactive,missing icon,res://fish/species/bonefish/bonefish.tres,,common
+7402,pompano_florida,implemented,inactive,missing icon,res://fish/species/pompano_florida/pompano_florida.tres,,common
+7403,ladyfish,implemented,inactive,missing icon,res://fish/species/ladyfish/ladyfish.tres,,common
+7404,permit,implemented,inactive,missing icon,res://fish/species/permit/permit.tres,,common
+7501,knifefish_clown,implemented,inactive,missing icon,res://fish/species/knifefish_clown/knifefish_clown.tres,,common
+7502,loach_clown,implemented,inactive,missing icon,res://fish/species/loach_clown/loach_clown.tres,,common
+7503,discus,implemented,inactive,missing icon,res://fish/species/discus/discus.tres,,common
+7504,angelfish_freshwater,implemented,inactive,missing icon,res://fish/species/angelfish_freshwater/angelfish_freshwater.tres,,common
+7505,gourami_giant,implemented,inactive,missing icon,res://fish/species/gourami_giant/gourami_giant.tres,,common
+7506,tilapia_nile,implemented,inactive,missing icon,res://fish/species/tilapia_nile/tilapia_nile.tres,,common
+7507,oscar,implemented,inactive,missing icon,res://fish/species/oscar/oscar.tres,,common
+7508,betta_siamese,implemented,inactive,missing icon,res://fish/species/betta_siamese/betta_siamese.tres,,common
+7509,tambaqui,implemented,inactive,missing icon,res://fish/species/tambaqui/tambaqui.tres,,common
+7510,catfish_walking,implemented,inactive,missing icon,res://fish/species/catfish_walking/catfish_walking.tres,,common
+7601,snakehead_giant,implemented,inactive,missing icon,res://fish/species/snakehead_giant/snakehead_giant.tres,,common
+7602,dorado_golden,implemented,inactive,missing icon,res://fish/species/dorado_golden/dorado_golden.tres,,common
+7603,mahseer_golden,implemented,inactive,missing icon,res://fish/species/mahseer_golden/mahseer_golden.tres,,common
+7604,peacock_bass,implemented,inactive,missing icon,res://fish/species/peacock_bass/peacock_bass.tres,,common
+7605,piranha_red_bellied,implemented,inactive,missing icon,res://fish/species/piranha_red_bellied/piranha_red_bellied.tres,,common
+7606,arowana_silver,implemented,inactive,missing icon,res://fish/species/arowana_silver/arowana_silver.tres,,common
+7701,whale_blue,implemented,inactive,missing icon,res://fish/species/whale_blue/whale_blue.tres,,common
+7702,dolphin_bottlenose,implemented,inactive,missing icon,res://fish/species/dolphin_bottlenose/dolphin_bottlenose.tres,,common
+7703,whale_humpback,implemented,inactive,missing icon,res://fish/species/whale_humpback/whale_humpback.tres,,common
+7704,whale_killer,implemented,inactive,missing icon,res://fish/species/whale_killer/whale_killer.tres,,common
+7705,whale_sperm,implemented,inactive,missing icon,res://fish/species/whale_sperm/whale_sperm.tres,,common
diff --git a/drawing/surface_drawing_canvas.gd b/drawing/surface_drawing_canvas.gd
index f6ceb94..01d34d7 100644
--- a/drawing/surface_drawing_canvas.gd
+++ b/drawing/surface_drawing_canvas.gd
@@ -2,7 +2,6 @@ class_name SurfaceDrawingCanvas
extends Node3D
const GUIDED_PIXEL_FILL: float = 0.88
-const FINISHED_PIXEL_FILL: float = 1.0
const SURFACE_OFFSET: float = 0.012
const LAYER_OFFSET_STEP: float = 0.0002
const SURFACE_SAMPLE_DEPTH: float = 0.45
@@ -13,13 +12,17 @@ var grid_height: int = 0
var cell_size: float = 0.0
var revision: int = -1
var creator_fingerprint: String = ""
+var participant_fingerprints: Array[String] = []
var _surface_origin: Vector3
var _surface_normal: Vector3
var _surface_tangent: Vector3
var _surface_bitangent: Vector3
var _cells: Dictionary[int, Dictionary] = {}
-var _color_meshes: Dictionary[StringName, MultiMeshInstance3D] = {}
+var _cell_surface_transforms: Array[Transform3D] = []
+var _pixel_image: Image
+var _pixel_texture: ImageTexture
+var _pixel_instance: MeshInstance3D
var _grid_instance: MeshInstance3D
var _guide_visible: bool = true
var _finalized: bool = false
@@ -42,6 +45,7 @@ func setup(
cell_size = float(data["cell_size"])
revision = int(data["revision"])
creator_fingerprint = str(data["creator_fingerprint"])
+ participant_fingerprints = _participants_from_state(data)
_finalized = bool(data.get("finalized", false))
_guide_visible = bool(data.get("guide_visible", true)) and not _finalized
_layer = int(data.get("layer", 0))
@@ -65,7 +69,7 @@ func setup(
var cell: Dictionary = cell_value
_cells[_cell_key(int(cell["x"]), int(cell["y"]))] = cell.duplicate(true)
_build_grid()
- _rebuild_pixels()
+ _build_pixel_renderer()
return true
@@ -83,8 +87,15 @@ func apply_update(data: Dictionary) -> bool:
_cells.erase(key)
else:
_cells[key] = edit.duplicate(true)
+ participant_fingerprints = _participants_from_update(
+ data, participant_fingerprints
+ )
revision = int(data["revision"])
- _rebuild_pixels()
+ if is_hidden_by_relationship():
+ _rebuild_pixel_image()
+ else:
+ _apply_pixel_edits(data["edits"])
+ _refresh_grid_visibility()
return true
@@ -96,15 +107,32 @@ func apply_guide_update(data: Dictionary) -> bool:
):
return false
revision = int(data["revision"])
+ participant_fingerprints = _participants_from_update(
+ data, participant_fingerprints
+ )
_finalized = bool(data["finalized"])
_guide_visible = bool(data["guide_visible"]) and not _finalized
- _refresh_grid_visibility()
- _rebuild_pixels()
+ if _finalized:
+ _destroy_grid()
+ else:
+ _refresh_grid_visibility()
return true
func refresh_relationship_visibility() -> void:
- _rebuild_pixels()
+ _rebuild_pixel_image()
+ if _pixel_instance != null:
+ _pixel_instance.visible = not is_hidden_by_relationship()
+ _refresh_grid_visibility()
+
+
+func is_hidden_by_relationship() -> bool:
+ if _relationships == null:
+ return false
+ for fingerprint: String in participant_fingerprints:
+ if _relationships.is_blocked(fingerprint):
+ return true
+ return false
func set_stencil_visible(should_be_visible: bool) -> void:
@@ -121,7 +149,27 @@ func is_finalized() -> bool:
func get_rendered_pixel_size() -> float:
- return cell_size * _pixel_fill()
+ return cell_size
+
+
+func get_rendered_pixel_count() -> int:
+ if is_hidden_by_relationship():
+ return 0
+ var count: int = 0
+ for cell: Dictionary in _cells.values():
+ if not _visible_cell_color(cell).is_equal_approx(Color.TRANSPARENT):
+ count += 1
+ return count
+
+
+func get_export_image() -> Image:
+ if _pixel_image == null or _pixel_image.is_empty():
+ return null
+ return _pixel_image.duplicate() as Image
+
+
+func has_guide_geometry() -> bool:
+ return _grid_instance != null and is_instance_valid(_grid_instance)
func contains_world_point(world_point: Vector3, tolerance: float = 0.3) -> bool:
@@ -192,7 +240,18 @@ func get_cell_surface_transform(
) -> Transform3D:
if x < 0 or x >= grid_width or y < 0 or y >= grid_height:
return Transform3D.IDENTITY
- return global_transform * _sample_cell_transform(x, y, fill)
+ var key: int = _cell_key(x, y)
+ var local_transform: Transform3D = (
+ _cell_surface_transforms[key]
+ if key >= 0 and key < _cell_surface_transforms.size()
+ else _sample_cell_transform(x, y, 1.0)
+ )
+ var scaled_basis: Basis = local_transform.basis
+ var resolved_fill: float = clampf(fill, 0.05, 1.0)
+ scaled_basis.x *= resolved_fill
+ scaled_basis.y *= resolved_fill
+ local_transform.basis = scaled_basis
+ return global_transform * local_transform
func get_authoritative_cells() -> Array[Dictionary]:
@@ -210,6 +269,9 @@ func get_authoritative_cells() -> Array[Dictionary]:
func _build_grid() -> void:
if _grid_instance != null:
_grid_instance.queue_free()
+ _grid_instance = null
+ if _finalized:
+ return
var immediate := ImmediateMesh.new()
var material := StandardMaterial3D.new()
material.shading_mode = BaseMaterial3D.SHADING_MODE_UNSHADED
@@ -219,6 +281,7 @@ func _build_grid() -> void:
immediate.surface_begin(Mesh.PRIMITIVE_LINES, material)
var half_width: float = float(grid_width) * cell_size * 0.5
var half_height: float = float(grid_height) * cell_size * 0.5
+ var sampled_vertices: Dictionary[Vector2i, Dictionary] = {}
for x: int in range(grid_width + 1):
var horizontal: float = -half_width + float(x) * cell_size
for y_segment: int in range(grid_height):
@@ -229,11 +292,15 @@ func _build_grid() -> void:
immediate,
_surface_origin + _surface_tangent * horizontal
+ _surface_bitangent * start_vertical,
+ Vector2i(x, y_segment),
+ sampled_vertices,
)
_add_grid_vertex(
immediate,
_surface_origin + _surface_tangent * horizontal
+ _surface_bitangent * (start_vertical + cell_size),
+ Vector2i(x, y_segment + 1),
+ sampled_vertices,
)
for y: int in range(grid_height + 1):
var vertical: float = -half_height + float(y) * cell_size
@@ -245,12 +312,16 @@ func _build_grid() -> void:
immediate,
_surface_origin + _surface_tangent * start_horizontal
+ _surface_bitangent * vertical,
+ Vector2i(x_segment, y),
+ sampled_vertices,
)
_add_grid_vertex(
immediate,
_surface_origin
+ _surface_tangent * (start_horizontal + cell_size)
+ _surface_bitangent * vertical,
+ Vector2i(x_segment + 1, y),
+ sampled_vertices,
)
immediate.surface_end()
_grid_instance = MeshInstance3D.new()
@@ -261,8 +332,16 @@ func _build_grid() -> void:
_refresh_grid_visibility()
-func _add_grid_vertex(immediate: ImmediateMesh, world_point: Vector3) -> void:
- var sampled: Dictionary = _sample_surface(world_point)
+func _add_grid_vertex(
+ immediate: ImmediateMesh,
+ world_point: Vector3,
+ grid_point: Vector2i,
+ sampled_vertices: Dictionary[Vector2i, Dictionary],
+) -> void:
+ var sampled: Dictionary = sampled_vertices.get(grid_point, {})
+ if sampled.is_empty():
+ sampled = _sample_surface(world_point)
+ sampled_vertices[grid_point] = sampled
var point: Vector3 = sampled["position"]
var normal: Vector3 = sampled["normal"]
immediate.surface_add_vertex(
@@ -270,56 +349,130 @@ func _add_grid_vertex(immediate: ImmediateMesh, world_point: Vector3) -> void:
)
-func _rebuild_pixels() -> void:
- for instance: MultiMeshInstance3D in _color_meshes.values():
- instance.queue_free()
- _color_meshes.clear()
- var cells_by_color: Dictionary[StringName, Array] = {}
- for cell: Dictionary in _cells.values():
- var author_fingerprint: String = str(cell.get("author_fingerprint", ""))
- if (
- _relationships != null
- and _relationships.is_blocked(author_fingerprint)
- ):
- continue
- var color_id := StringName(str(cell.get("color_id", "")))
- if not SurfaceDrawingPalette.has_color(color_id):
- continue
- var color_cells: Array = cells_by_color.get(color_id, [])
- color_cells.append(cell)
- cells_by_color[color_id] = color_cells
- for color_id: StringName in cells_by_color:
- _create_color_mesh(color_id, cells_by_color[color_id])
-
-
-func _create_color_mesh(color_id: StringName, cells: Array) -> void:
- if cells.is_empty():
- return
- var quad := QuadMesh.new()
- quad.size = Vector2.ONE
+func _build_pixel_renderer() -> void:
+ if _pixel_instance != null:
+ _pixel_instance.queue_free()
+ _pixel_instance = null
+ var cell_count: int = grid_width * grid_height
+ var vertices := PackedVector3Array()
+ var texture_coordinates := PackedVector2Array()
+ var indices := PackedInt32Array()
+ vertices.resize(cell_count * 4)
+ texture_coordinates.resize(cell_count * 4)
+ indices.resize(cell_count * 6)
+ _cell_surface_transforms.clear()
+ _cell_surface_transforms.resize(cell_count)
+ for y: int in range(grid_height):
+ for x: int in range(grid_width):
+ var cell_index: int = _cell_key(x, y)
+ var surface_transform := _sample_cell_transform(x, y, 1.0)
+ _cell_surface_transforms[cell_index] = surface_transform
+ var center: Vector3 = surface_transform.origin
+ var horizontal: Vector3 = surface_transform.basis.x * 0.5
+ var vertical: Vector3 = surface_transform.basis.y * 0.5
+ var vertex_offset: int = cell_index * 4
+ vertices[vertex_offset] = center - horizontal - vertical
+ vertices[vertex_offset + 1] = center + horizontal - vertical
+ vertices[vertex_offset + 2] = center + horizontal + vertical
+ vertices[vertex_offset + 3] = center - horizontal + vertical
+ var image_row: int = _image_y(y)
+ var u_min: float = float(x) / float(grid_width)
+ var u_max: float = float(x + 1) / float(grid_width)
+ var v_min: float = float(image_row) / float(grid_height)
+ var v_max: float = float(image_row + 1) / float(grid_height)
+ texture_coordinates[vertex_offset] = Vector2(u_min, v_max)
+ texture_coordinates[vertex_offset + 1] = Vector2(u_max, v_max)
+ texture_coordinates[vertex_offset + 2] = Vector2(u_max, v_min)
+ texture_coordinates[vertex_offset + 3] = Vector2(u_min, v_min)
+ var index_offset: int = cell_index * 6
+ indices[index_offset] = vertex_offset
+ indices[index_offset + 1] = vertex_offset + 1
+ indices[index_offset + 2] = vertex_offset + 2
+ indices[index_offset + 3] = vertex_offset
+ indices[index_offset + 4] = vertex_offset + 2
+ indices[index_offset + 5] = vertex_offset + 3
+ _rebuild_pixel_image()
+ var arrays: Array = []
+ arrays.resize(Mesh.ARRAY_MAX)
+ arrays[Mesh.ARRAY_VERTEX] = vertices
+ arrays[Mesh.ARRAY_TEX_UV] = texture_coordinates
+ arrays[Mesh.ARRAY_INDEX] = indices
+ var mesh := ArrayMesh.new()
+ mesh.add_surface_from_arrays(Mesh.PRIMITIVE_TRIANGLES, arrays)
var material := StandardMaterial3D.new()
material.shading_mode = BaseMaterial3D.SHADING_MODE_UNSHADED
- material.albedo_color = SurfaceDrawingPalette.get_color(color_id)
+ material.albedo_color = Color.WHITE
+ material.albedo_texture = _pixel_texture
+ material.texture_filter = BaseMaterial3D.TEXTURE_FILTER_NEAREST
+ material.transparency = BaseMaterial3D.TRANSPARENCY_ALPHA_SCISSOR
+ material.alpha_scissor_threshold = 0.5
material.cull_mode = BaseMaterial3D.CULL_DISABLED
- quad.material = material
- var multimesh := MultiMesh.new()
- multimesh.transform_format = MultiMesh.TRANSFORM_3D
- multimesh.mesh = quad
- multimesh.instance_count = cells.size()
- for cell_index: int in range(cells.size()):
- var cell: Dictionary = cells[cell_index]
- multimesh.set_instance_transform(
- cell_index,
- _sample_cell_transform(
- int(cell["x"]), int(cell["y"]), _pixel_fill()
- ),
+ mesh.surface_set_material(0, material)
+ _pixel_instance = MeshInstance3D.new()
+ _pixel_instance.name = "ArtworkTexture"
+ _pixel_instance.mesh = mesh
+ _pixel_instance.cast_shadow = GeometryInstance3D.SHADOW_CASTING_SETTING_OFF
+ add_child(_pixel_instance)
+ _pixel_instance.visible = not is_hidden_by_relationship()
+
+
+func _rebuild_pixel_image() -> void:
+ if (
+ _pixel_image == null
+ or _pixel_image.get_width() != grid_width
+ or _pixel_image.get_height() != grid_height
+ ):
+ _pixel_image = Image.create(
+ grid_width, grid_height, false, Image.FORMAT_RGBA8
)
- var instance := MultiMeshInstance3D.new()
- instance.name = "Pixels_%s" % color_id
- instance.multimesh = multimesh
- instance.cast_shadow = GeometryInstance3D.SHADOW_CASTING_SETTING_OFF
- add_child(instance)
- _color_meshes[color_id] = instance
+ _pixel_image.fill(Color.TRANSPARENT)
+ if not is_hidden_by_relationship():
+ for cell: Dictionary in _cells.values():
+ _set_image_cell(int(cell["x"]), int(cell["y"]), cell)
+ if _pixel_texture == null:
+ _pixel_texture = ImageTexture.create_from_image(_pixel_image)
+ else:
+ _pixel_texture.update(_pixel_image)
+
+
+func _apply_pixel_edits(edits: Array) -> void:
+ if _pixel_image == null or _pixel_texture == null:
+ _rebuild_pixel_image()
+ return
+ for edit_value: Variant in edits:
+ var edit: Dictionary = edit_value
+ var x: int = int(edit["x"])
+ var y: int = int(edit["y"])
+ var cell: Dictionary = _cells.get(_cell_key(x, y), {})
+ _set_image_cell(x, y, cell)
+ _pixel_texture.update(_pixel_image)
+
+
+func _set_image_cell(x: int, y: int, cell: Dictionary) -> void:
+ if (
+ _pixel_image == null
+ or x < 0
+ or x >= grid_width
+ or y < 0
+ or y >= grid_height
+ ):
+ return
+ _pixel_image.set_pixel(x, _image_y(y), _visible_cell_color(cell))
+
+
+func _visible_cell_color(cell: Dictionary) -> Color:
+ if cell.is_empty():
+ return Color.TRANSPARENT
+ var color_id := StringName(str(cell.get("color_id", "")))
+ return (
+ SurfaceDrawingPalette.get_color(color_id)
+ if SurfaceDrawingPalette.has_color(color_id)
+ else Color.TRANSPARENT
+ )
+
+
+func _image_y(cell_y: int) -> int:
+ return grid_height - 1 - cell_y
func _sample_cell_transform(
@@ -376,17 +529,60 @@ func _sample_surface(expected: Vector3) -> Dictionary:
return {"position": point, "normal": normal}
-func _pixel_fill() -> float:
- return GUIDED_PIXEL_FILL if _guide_visible else FINISHED_PIXEL_FILL
-
-
func _surface_offset() -> float:
return SURFACE_OFFSET + float(_layer) * LAYER_OFFSET_STEP
+func _destroy_grid() -> void:
+ if _grid_instance == null:
+ return
+ _grid_instance.queue_free()
+ _grid_instance = null
+
+
func _refresh_grid_visibility() -> void:
if _grid_instance != null:
- _grid_instance.visible = _stencil_requested_visible and _guide_visible
+ _grid_instance.visible = (
+ _stencil_requested_visible
+ and _guide_visible
+ and not is_hidden_by_relationship()
+ )
+
+
+func _participants_from_state(data: Dictionary) -> Array[String]:
+ var result: Array[String] = []
+ _append_participant(result, str(data.get("creator_fingerprint", "")))
+ for value: Variant in data.get("participant_fingerprints", []):
+ _append_participant(result, str(value))
+ for cell_value: Variant in data.get("cells", []):
+ var cell: Dictionary = cell_value
+ _append_participant(
+ result, str(cell.get("author_fingerprint", ""))
+ )
+ return result
+
+
+func _participants_from_update(
+ data: Dictionary,
+ fallback: Array[String],
+) -> Array[String]:
+ var result: Array[String] = fallback.duplicate()
+ for value: Variant in data.get("participant_fingerprints", []):
+ _append_participant(result, str(value))
+ for edit_value: Variant in data.get("edits", []):
+ var edit: Dictionary = edit_value
+ _append_participant(
+ result, str(edit.get("author_fingerprint", ""))
+ )
+ return result
+
+
+func _append_participant(result: Array[String], fingerprint: String) -> void:
+ if (
+ NetworkIdentityCrypto.valid_fingerprint(fingerprint)
+ and fingerprint not in result
+ ):
+ result.append(fingerprint)
func _cell_key(x: int, y: int) -> int:
diff --git a/drawing/surface_drawing_protocol.gd b/drawing/surface_drawing_protocol.gd
index 5ca4ae0..ff132f8 100644
--- a/drawing/surface_drawing_protocol.gd
+++ b/drawing/surface_drawing_protocol.gd
@@ -2,7 +2,7 @@ class_name SurfaceDrawingProtocol
extends RefCounted
const CAPABILITY: StringName = &"surface_drawing_v2"
-const RELIABLE_CHANNEL: int = NetworkProtocol.ITEM_RELIABLE_CHANNEL
+const RELIABLE_CHANNEL: int = NetworkProtocol.DRAWING_RELIABLE_CHANNEL
const GRID_SIZES: Array[int] = [16, 32, 64, 128]
const DEFAULT_GRID_SIZE: int = 16
const MAX_GRID_SIZE: int = 128
@@ -18,6 +18,9 @@ const MAX_CANVAS_ID_LENGTH: int = 64
const MAX_REQUEST_ID_LENGTH: int = 64
const MAX_STROKE_ID_LENGTH: int = 64
const MAX_SESSION_ID_LENGTH: int = 96
+# Sessions support up to 128 simultaneous players. Keep enough bounded history
+# for a complete room turnover while never accepting unattributed edits.
+const MAX_PARTICIPANTS: int = 256
const MAX_COORDINATE: float = 10000.0
@@ -64,6 +67,38 @@ static func validate_edit_request(data: Variant) -> bool:
return true
+static func validate_stamp_request(data: Variant) -> bool:
+ if typeof(data) != TYPE_DICTIONARY:
+ return false
+ var value: Dictionary = data
+ if (
+ not _valid_common(value)
+ or not _valid_vector(value.get("origin"))
+ or not _valid_vector(value.get("normal"))
+ or not _valid_vector(value.get("tangent"))
+ or typeof(value.get("width")) != TYPE_INT
+ or int(value["width"]) not in GRID_SIZES
+ or typeof(value.get("height")) != TYPE_INT
+ or int(value["height"]) != int(value["width"])
+ or typeof(value.get("cell_size")) not in [TYPE_FLOAT, TYPE_INT]
+ or not is_equal_approx(float(value["cell_size"]), CELL_SIZE)
+ or typeof(value.get("pixels")) != TYPE_PACKED_BYTE_ARRAY
+ ):
+ return false
+ var pixels: PackedByteArray = value["pixels"]
+ var expected_size: int = int(value["width"]) * int(value["height"])
+ if pixels.size() != expected_size:
+ return false
+ var maximum_palette_index: int = SurfaceDrawingPalette.COLORS.size()
+ var painted_count: int = 0
+ for palette_index: int in pixels:
+ if palette_index > maximum_palette_index:
+ return false
+ if palette_index > 0:
+ painted_count += 1
+ return painted_count > 0
+
+
static func validate_guide_request(data: Variant) -> bool:
if typeof(data) != TYPE_DICTIONARY:
return false
@@ -112,6 +147,9 @@ static func validate_canvas_state(data: Variant) -> bool:
or not NetworkIdentityCrypto.valid_fingerprint(
value.get("creator_fingerprint")
)
+ or not _valid_participant_fingerprints(
+ value.get("participant_fingerprints", [])
+ )
or typeof(value.get("cells")) != TYPE_ARRAY
):
return false
@@ -127,6 +165,13 @@ static func validate_canvas_state(data: Variant) -> bool:
or int((cell_value as Dictionary)["y"]) >= grid_height
):
return false
+ var participants: Array = value.get("participant_fingerprints", [])
+ if not participants.is_empty():
+ if str(value["creator_fingerprint"]) not in participants:
+ return false
+ for cell_value: Variant in cells:
+ if str((cell_value as Dictionary)["author_fingerprint"]) not in participants:
+ return false
return true
@@ -143,6 +188,9 @@ static func validate_guide_update(data: Variant) -> bool:
and int(value["revision"]) >= 1
and typeof(value.get("guide_visible")) == TYPE_BOOL
and typeof(value.get("finalized")) == TYPE_BOOL
+ and _valid_participant_fingerprints(
+ value.get("participant_fingerprints", [])
+ )
)
@@ -160,6 +208,9 @@ static func validate_canvas_update(data: Variant) -> bool:
or typeof(value.get("revision")) != TYPE_INT
or int(value["revision"]) < 1
or typeof(value.get("edits")) != TYPE_ARRAY
+ or not _valid_participant_fingerprints(
+ value.get("participant_fingerprints", [])
+ )
):
return false
var edits: Array = value["edits"]
@@ -168,6 +219,18 @@ static func validate_canvas_update(data: Variant) -> bool:
for edit_value: Variant in edits:
if not validate_authoritative_cell(edit_value, true):
return false
+ var participants: Array = value.get("participant_fingerprints", [])
+ if not participants.is_empty():
+ for edit_value: Variant in edits:
+ var edit: Dictionary = edit_value
+ var author_fingerprint: String = str(
+ edit.get("author_fingerprint", "")
+ )
+ if (
+ not str(edit.get("color_id", "")).is_empty()
+ and author_fingerprint not in participants
+ ):
+ return false
return true
@@ -242,6 +305,26 @@ static func _valid_stroke_id(value: Variant) -> bool:
)
+static func _valid_participant_fingerprints(value: Variant) -> bool:
+ if typeof(value) != TYPE_ARRAY:
+ return false
+ var fingerprints: Array = value
+ if fingerprints.size() > MAX_PARTICIPANTS:
+ return false
+ var seen: Dictionary[String, bool] = {}
+ for fingerprint_value: Variant in fingerprints:
+ if typeof(fingerprint_value) != TYPE_STRING:
+ return false
+ var fingerprint: String = str(fingerprint_value)
+ if (
+ not NetworkIdentityCrypto.valid_fingerprint(fingerprint)
+ or seen.has(fingerprint)
+ ):
+ return false
+ seen[fingerprint] = true
+ return true
+
+
static func _valid_vector(value: Variant) -> bool:
if typeof(value) != TYPE_ARRAY:
return false
diff --git a/economy/art_shop_stock.gd b/economy/art_shop_stock.gd
index acc5382..ddd6f3c 100644
--- a/economy/art_shop_stock.gd
+++ b/economy/art_shop_stock.gd
@@ -24,6 +24,9 @@ const GRID_PRODUCTS: Array[StringName] = [
&"grid_64x",
&"grid_128x",
]
+const STAMP_PRODUCTS: Array[StringName] = [
+ PlayerArtUnlocks.STAMP_PRODUCT_ID,
+]
static func get_price(product_id: StringName) -> int:
@@ -44,12 +47,14 @@ static func get_display_name(product_id: StringName) -> String:
var grid_size: int = PlayerArtUnlocks.grid_size_for_product(product_id)
if grid_size > 0:
return "%d×%d grid" % [grid_size, grid_size]
+ if product_id == PlayerArtUnlocks.STAMP_PRODUCT_ID:
+ return "Stamp tool"
return "Unknown art supply"
static func get_description(product_id: StringName) -> String:
if product_id == ART_KIT_ITEM_ID:
- return "Press P in the game world to paint!"
+ return "Select the Art Kit in the Hotbar to paint."
var color_id: StringName = PlayerArtUnlocks.color_id_for_product(product_id)
if not color_id.is_empty():
return "Unlocks this marker color in the Paint UI."
@@ -61,4 +66,6 @@ static func get_description(product_id: StringName) -> String:
return "Unlocks the %d×%d grid in the Paint UI." % [
grid_size, grid_size,
]
+ if product_id == PlayerArtUnlocks.STAMP_PRODUCT_ID:
+ return "Unlocks saved artwork stamps in the Art Kit."
return ""
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 e74232e..b9d3eaf 100644
--- a/economy/fishing_shop_stock.gd
+++ b/economy/fishing_shop_stock.gd
@@ -3,33 +3,42 @@ extends RefCounted
const ItemCatalogType = preload("res://items/item_catalog.gd")
const ItemDataType = preload("res://items/item_data.gd")
+const FishingRodDataType = preload("res://items/fishing_rod_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 MAGNET_ID: StringName = &"magnet"
const BATTERIES_ID: StringName = &"batteries"
+const CRAB_NET_ID: StringName = &"crab_net"
+const STANDARD_SHOVEL_ID: StringName = &"standard_shovel"
+const FISHING_NET_ID: StringName = &"fishing_net"
const ITEM_PRICES: Dictionary[StringName, int] = {
&"worms": 1,
&"snails": 3,
&"shrimp": 7,
&"squid_chunks": 15,
- &"whole_anchovy": 30,
- &"whole_sardine": 60,
+ &"whole_anchovy": 60,
+ &"whole_sardine": 30,
&"luminous_roe": 125,
&"the_standby": 750,
&"coffee": 20,
&"energy_drink": 35,
&"snack": 30,
FISH_FINDER_ID: 500,
+ MAGNET_ID: 250,
BATTERIES_ID: 15,
+ CRAB_NET_ID: 50,
+ STANDARD_SHOVEL_ID: 75,
+ FISHING_NET_ID: 250,
}
const BAIT_UNLOCK_PRICES: Dictionary[StringName, int] = {
&"snails": 400,
&"shrimp": 1200,
&"squid_chunks": 3500,
- &"whole_anchovy": 9000,
- &"whole_sardine": 22000,
+ &"whole_anchovy": 22000,
+ &"whole_sardine": 9000,
&"luminous_roe": 50000,
}
const ITEM_ORDER: Array[StringName] = [
@@ -37,11 +46,15 @@ const ITEM_ORDER: Array[StringName] = [
&"snails",
&"shrimp",
&"squid_chunks",
- &"whole_anchovy",
&"whole_sardine",
+ &"whole_anchovy",
&"luminous_roe",
&"the_standby",
FISH_FINDER_ID,
+ MAGNET_ID,
+ CRAB_NET_ID,
+ STANDARD_SHOVEL_ID,
+ FISHING_NET_ID,
&"coffee",
&"energy_drink",
&"snack",
@@ -57,6 +70,29 @@ static func get_stock_item_ids() -> Array[StringName]:
return ITEM_ORDER.duplicate()
+static func get_stock_order_index(item_id: StringName) -> int:
+ return ITEM_ORDER.find(item_id)
+
+
+static func get_rod_stock(
+ catalog: ItemCatalogType,
+) -> Array[FishingRodDataType]:
+ var rods: Array[FishingRodDataType] = []
+ if catalog == null:
+ return rods
+ for item: ItemDataType in catalog.get_available_items():
+ var rod := item as FishingRodDataType
+ if rod != null and rod.is_shop_available():
+ rods.append(rod)
+ rods.sort_custom(
+ func(left: FishingRodDataType, right: FishingRodDataType) -> bool:
+ if left.shop_order == right.shop_order:
+ return str(left.item_id) < str(right.item_id)
+ return left.shop_order < right.shop_order
+ )
+ return rods
+
+
static func get_unlock_price(item_id: StringName) -> int:
return BAIT_UNLOCK_PRICES.get(item_id, -1)
@@ -81,7 +117,15 @@ static func is_permanent_unlock(
) -> bool:
return (
item != null
- and (item.is_lure() or item_id == FISH_FINDER_ID)
+ and (
+ item.is_lure()
+ or item_id == FISH_FINDER_ID
+ or item_id == MAGNET_ID
+ or item_id == CRAB_NET_ID
+ or item_id == STANDARD_SHOVEL_ID
+ or item_id == FISHING_NET_ID
+ or item is FishingRodDataType
+ )
)
@@ -89,6 +133,10 @@ static func is_passive_supply(item_id: StringName) -> bool:
return item_id == BATTERIES_ID
+static func is_gathering_tool(item_id: StringName) -> bool:
+ return item_id == CRAB_NET_ID or item_id == STANDARD_SHOVEL_ID
+
+
static func get_purchase_cost(
item_id: StringName,
quantity: int,
@@ -111,6 +159,8 @@ static func purchase_one(
if wallet == null or bag == null or catalog == null:
return false
var item: ItemDataType = catalog.get_item_by_id(item_id)
+ if item == null or not item.is_available():
+ return false
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(
@@ -118,15 +168,20 @@ static func purchase_one(
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),
+ var rod := item as FishingRodDataType
+ var price: int = (
+ rod.shop_price
+ if rod != null
+ else get_purchase_cost(
+ item_id,
+ quantity,
+ bag.is_bait_unlocked(item_id),
+ )
)
if (
price < 0
- or item == null
- or not item.is_valid()
+ or not item.is_available()
+ or (rod != null and not rod.is_shop_available())
or (
item.category != ItemDataType.Category.CONSUMABLE
and not bait_topoff
diff --git a/economy/item_resale_policy.gd b/economy/item_resale_policy.gd
new file mode 100644
index 0000000..299fc48
--- /dev/null
+++ b/economy/item_resale_policy.gd
@@ -0,0 +1,21 @@
+class_name ItemResalePolicy
+extends RefCounted
+
+const FishingShopStockType = preload("res://economy/fishing_shop_stock.gd")
+const ItemDataType = preload("res://items/item_data.gd")
+
+
+static func is_sellable(item: ItemDataType) -> bool:
+ # Bait and lures live in their own tackle collection. Rods, tools, and
+ # utility gear are permanent equipment. The unified sell tray currently
+ # accepts ordinary consumable supplies only.
+ return item != null and item.is_available() and (
+ item.category == ItemDataType.Category.CONSUMABLE
+ )
+
+
+static func get_unit_value(item: ItemDataType) -> int:
+ if not is_sellable(item):
+ return -1
+ var purchase_price := FishingShopStockType.get_price(item.item_id)
+ return purchase_price / 2 if purchase_price >= 0 else -1
diff --git a/economy/item_resale_policy.gd.uid b/economy/item_resale_policy.gd.uid
new file mode 100644
index 0000000..b8270b2
--- /dev/null
+++ b/economy/item_resale_policy.gd.uid
@@ -0,0 +1 @@
+uid://brsykj7darpyw
diff --git a/export_presets.cfg b/export_presets.cfg
index 2365877..a49f01f 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.6.5-alpha/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.20.2-alpha/windows-x86_64/straywild.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="res://art/exported/system_icons/netfishing.ico"
+application/icon="res://art/exported/system_icons/straywild.ico"
application/console_wrapper_icon=""
-application/icon_interpolation=4
-application/file_version="0.6.5.0"
-application/product_version="0.6.5.0"
-application/company_name=""
-application/product_name="NETfishing"
-application/file_description="NETfishing v0.6.5-alpha"
-application/copyright=""
-application/trademarks=""
+application/icon_interpolation=0
+application/file_version="0.20.2.0"
+application/product_version="0.20.2.0"
+application/company_name="Woofmeow"
+application/product_name="straywild"
+application/file_description="straywild v0.20.2-alpha"
+application/copyright="Copyright © 2026 Woofmeow"
+application/trademarks="straywild and Woofmeow branding is reserved; see TRADEMARKS.md"
application/export_angle=0
ssh_remote_deploy/enabled=false
ssh_remote_deploy/host="user@host_ip"
@@ -60,9 +60,9 @@ advanced_options=false
dedicated_server=false
custom_features=""
export_filter="all_resources"
-include_filter=""
-exclude_filter="builds/*,playtest/*,scripts/*"
-export_path="builds/v0.6.5-alpha/linux-arm64/NETfishing.arm64"
+include_filter="LICENSE,ASSET-LICENSE.md,TRADEMARKS.md,docs/ATTRIBUTION.md,ui/fonts/*LICENSE.txt"
+exclude_filter="builds/*,scripts/*"
+export_path="builds/v0.20.2-alpha/linux-arm64/straywild.arm64"
patches=PackedStringArray()
encryption_include_filters=""
encryption_exclude_filters=""
@@ -78,8 +78,8 @@ 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
+texture_format/s3tc_bptc=false
+texture_format/etc2_astc=true
[preset.4]
@@ -90,9 +90,9 @@ advanced_options=false
dedicated_server=false
custom_features=""
export_filter="all_resources"
-include_filter=""
-exclude_filter="builds/*,playtest/*,scripts/*"
-export_path="builds/v0.6.5-alpha/macos/NETfishing.zip"
+include_filter="LICENSE,ASSET-LICENSE.md,TRADEMARKS.md,docs/ATTRIBUTION.md,ui/fonts/*LICENSE.txt"
+exclude_filter="builds/*,scripts/*"
+export_path="builds/v0.20.2-alpha/macos/straywild.zip"
patches=PackedStringArray()
encryption_include_filters=""
encryption_exclude_filters=""
@@ -106,10 +106,10 @@ script_export_mode=2
custom_template/debug=""
custom_template/release=""
application/bundle_identifier="io.woofmeow.netfishing"
-application/icon="res://art/exported/system_icons/netfishing_1024.png"
+application/icon="res://art/exported/system_icons/straywild_1024.png"
application/icon_interpolation=0
-application/short_version="0.6.4"
-application/version="0.6.4"
+application/short_version="0.20.2"
+application/version="0.20.2"
application/architecture="universal"
codesign/enable=false
notarization/enable=false
@@ -123,9 +123,9 @@ advanced_options=false
dedicated_server=true
custom_features=""
export_filter="all_resources"
-include_filter=""
-exclude_filter="builds/*,playtest/*,scripts/*,tests/*"
-export_path="builds/v0.6.5-alpha/server-linux-x86_64/NETfishingServer.x86_64"
+include_filter="LICENSE,ASSET-LICENSE.md,TRADEMARKS.md,docs/ATTRIBUTION.md,ui/fonts/*LICENSE.txt"
+exclude_filter="builds/*,scripts/*,tests/*"
+export_path="builds/v0.20.2-alpha/server-linux-x86_64/straywildServer.x86_64"
patches=PackedStringArray()
encryption_include_filters=""
encryption_exclude_filters=""
@@ -153,9 +153,9 @@ advanced_options=false
dedicated_server=false
custom_features=""
export_filter="all_resources"
-include_filter=""
-exclude_filter="builds/*,playtest/*,scripts/*"
-export_path="builds/v0.6.5-alpha/android/NETfishing.apk"
+include_filter="LICENSE,ASSET-LICENSE.md,TRADEMARKS.md,docs/ATTRIBUTION.md,ui/fonts/*LICENSE.txt"
+exclude_filter="builds/*,scripts/*"
+export_path="builds/v0.20.2-alpha/android/straywild.apk"
patches=PackedStringArray()
encryption_include_filters=""
encryption_exclude_filters=""
@@ -176,10 +176,10 @@ architectures/armeabi-v7a=false
architectures/arm64-v8a=true
architectures/x86=false
architectures/x86_64=false
-version/code=60500
-version/name="v0.6.5-alpha"
+version/code=200200
+version/name="v0.20.2-alpha"
package/unique_name="io.woofmeow.netfishing"
-package/name="NETfishing"
+package/name="straywild"
package/signed=true
package/app_category=0
package/retain_data_on_uninstall=false
@@ -187,7 +187,7 @@ 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"
+launcher_icons/main_192x192="res://art/exported/system_icons/straywild_256.png"
graphics/opengl_debug=false
screen/immersive_mode=true
screen/edge_to_edge=false
@@ -212,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.6.5-alpha/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.20.2-alpha/linux-x86_64/straywild.x86_64"
patches=PackedStringArray()
encryption_include_filters=""
encryption_exclude_filters=""
diff --git a/fish/fish_data.gd b/fish/fish_data.gd
index 2bb99af..46d4675 100644
--- a/fish/fish_data.gd
+++ b/fish/fish_data.gd
@@ -15,6 +15,7 @@ const CatchDifficultyProfileType = preload(
"res://fishing/catch_difficulty_profile.gd"
)
const FishAvailabilityType = preload("res://fish/fish_availability.gd")
+const CalendarSeasonType = preload("res://world/calendar_season.gd")
enum Rarity {
COMMON,
@@ -24,16 +25,45 @@ enum Rarity {
LEGENDARY,
}
+enum CollectionMethod {
+ FISHING,
+ NET,
+ DIGGING,
+}
+
+enum LogbookSection {
+ AUTOMATIC,
+ SHELLFISH,
+}
+
+enum CreatureGroup {
+ FISH,
+ INSECT,
+ SHELLFISH,
+}
+
@export var id: StringName
@export var display_name: String
+@export_category("Developer Catalog")
+## Disabled species remain resolvable for saves but cannot be newly selected.
+@export var active: bool = true
+@export_range(1, 999999, 1) var catalog_number: int = 1
+@export var collection_group: StringName
+@export_multiline var logbook_fact: String
+@export var collection_method: CollectionMethod = CollectionMethod.FISHING
+@export var logbook_section: LogbookSection = LogbookSection.AUTOMATIC
+@export var habitat_label: String = ""
+@export_flags("Spring", "Summer", "Fall", "Winter")
+var available_seasons: int = CalendarSeasonType.ALL_MASK
+@export_category("Fishing")
@export_flags("Fresh Water", "Salt Water")
var allowed_water_types: int = WaterType.ALL_FISHABLE_MASK
@export var rarity: Rarity = Rarity.COMMON
@export_range(0.0, 1000.0, 0.01) var base_catch_weight: float = 1.0
@export var catch_profile: CatchDifficultyProfileType
@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
+@export_range(0.001, 500000.0, 0.001) var weight_min_lb: float = 0.5
+@export_range(0.001, 500000.0, 0.001) 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
@@ -46,6 +76,10 @@ var allowed_water_types: int = WaterType.ALL_FISHABLE_MASK
func is_selectable() -> bool:
+ return active and is_valid_catalog_entry()
+
+
+func is_valid_catalog_entry() -> bool:
return (
not id.is_empty()
and base_catch_weight > 0.0
@@ -58,13 +92,40 @@ func is_selectable() -> bool:
and sell_value_min >= 0
and sell_value_max >= sell_value_min
and sell_value_curve > 0.0
+ and CalendarSeasonType.is_valid_mask(available_seasons)
)
+func is_fishable() -> bool:
+ return is_selectable() and collection_method == CollectionMethod.FISHING
+
+
+func get_creature_group() -> CreatureGroup:
+ if logbook_section == LogbookSection.SHELLFISH:
+ return CreatureGroup.SHELLFISH
+ if collection_method == CollectionMethod.FISHING:
+ return CreatureGroup.FISH
+ return CreatureGroup.INSECT
+
+
+func get_habitat_label() -> String:
+ if not habitat_label.strip_edges().is_empty():
+ return habitat_label.strip_edges().to_lower()
+ return WaterType.label(get_primary_water_type()).to_lower()
+
+
func is_allowed_in_water(type: WaterType.Type) -> bool:
return (allowed_water_types & WaterType.mask_for(type)) != 0
+func is_available_in_season(season: int) -> bool:
+ return CalendarSeasonType.includes(available_seasons, season)
+
+
+func get_season_text() -> String:
+ return CalendarSeasonType.format_mask(available_seasons)
+
+
func get_primary_water_type() -> WaterType.Type:
if allowed_water_types & WaterType.FRESH_WATER_MASK:
return WaterType.Type.FRESH_WATER
@@ -74,7 +135,7 @@ func get_primary_water_type() -> WaterType.Type:
func get_minimum_weight() -> float:
- return maxf(weight_min_lb, 0.01)
+ return maxf(weight_min_lb, 0.001)
func get_maximum_weight() -> float:
diff --git a/fish/fish_quality.gd b/fish/fish_quality.gd
index 28ca93d..61534e0 100644
--- a/fish/fish_quality.gd
+++ b/fish/fish_quality.gd
@@ -19,20 +19,15 @@ 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 SARDINE_ROLL_WEIGHTS: Array[float] = [34.0, 27.0, 20.0, 13.0, 6.0]
-const MINNOW_ROLL_WEIGHTS: Array[float] = [25.0, 28.0, 23.0, 16.0, 8.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 profile multiplier retained for serialized/profile compatibility.
-# New encounters use the weighted quality/rarity/weight bands below.
+# 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]
-# Barrier health is weighted 70% by quality, 20% by rarity, and 10% by the
-# catch's position in its authored weight range. The upper bounds define the
-# intended per-barrier challenge bands; a small seeded variance keeps barriers
-# from feeling identical without crossing those bands.
-const BARRIER_QUALITY_WEIGHT: float = 0.70
-const BARRIER_RARITY_WEIGHT: float = 0.20
-const BARRIER_WEIGHT_WEIGHT: float = 0.10
+# 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 = [
@@ -89,32 +84,26 @@ static func apply_barrier_health(base_health: int, quality: int) -> int:
)
-static func barrier_health_for_catch(
- quality: int,
- rarity: int,
- weight_percentile: float,
- variance: float = 1.0,
-) -> int:
+static func barrier_health_range(quality: int) -> Vector2i:
var safe_quality: int = (
quality if is_valid(quality) else Tier.BORING
)
- var safe_rarity: float = clampf(float(rarity) / 4.0, 0.0, 1.0)
- var safe_weight: float = clampf(weight_percentile, 0.0, 1.0)
- var weighted_health: float = float(BARRIER_HEALTH_MAXIMUMS[safe_quality]) * (
- BARRIER_QUALITY_WEIGHT
- + BARRIER_RARITY_WEIGHT * safe_rarity
- + BARRIER_WEIGHT_WEIGHT * safe_weight
- )
- var varied_health: int = roundi(
- weighted_health * clampf(variance, 0.8, 1.2)
- )
- return clampi(
- varied_health,
+ 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] = [],
@@ -158,9 +147,9 @@ static func rarity_weights_for_bait(
elif active_bait_tags.has(&"bait_rarity_6"):
weights.assign(LUMINOUS_ROE_ROLL_WEIGHTS)
elif active_bait_tags.has(&"bait_rarity_5"):
- weights.assign(MINNOW_ROLL_WEIGHTS)
- elif active_bait_tags.has(&"bait_rarity_4"):
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"):
diff --git a/fish/fish_selector.gd b/fish/fish_selector.gd
index 506a906..c2413bf 100644
--- a/fish/fish_selector.gd
+++ b/fish/fish_selector.gd
@@ -7,10 +7,12 @@ const FishDataType = preload("res://fish/fish_data.gd")
const FishPoolType = preload("res://fish/fish_pool.gd")
const FishQualityType = preload("res://fish/fish_quality.gd")
const FishingContextType = preload("res://fishing/fishing_context.gd")
+const FishingRodDataType = preload("res://items/fishing_rod_data.gd")
var undiscovered_weight_multiplier: float = 1.5
var rarity_weight_multipliers: Array[float] = []
var quality_weight_multipliers: Array[float] = []
+var active_rod: FishingRodDataType
var use_deterministic_test_seed: bool = false
var deterministic_test_seed: int = 24680
var selection_seed: int = 0
@@ -44,7 +46,9 @@ func select_fish(
var weights: Array[float] = []
var total_weight: float = 0.0
for fish: FishDataType in pool.candidates:
- if fish == null or not fish.is_selectable():
+ if fish == null or not fish.is_fishable():
+ continue
+ if not fish.is_available_in_season(context.season):
continue
if not fish.is_allowed_in_water(context.water_type):
continue
@@ -55,8 +59,16 @@ func select_fish(
# 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 active_rod != null:
+ final_weight *= maxf(
+ active_rod.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 active_rod != null:
+ final_weight *= active_rod.get_rarity_multiplier(fish.rarity)
+ final_weight *= active_rod.get_affinity_multiplier(fish, context)
if final_weight <= 0.0:
continue
all_fish.append(fish)
@@ -87,6 +99,8 @@ func _roll_rarity(context: FishingContextType) -> int:
for index: int in range(weights.size()):
if index < rarity_weight_multipliers.size():
weights[index] *= maxf(rarity_weight_multipliers[index], 0.0)
+ if active_rod != null:
+ weights[index] *= active_rod.get_rarity_multiplier(index)
var total: float = 0.0
for weight: float in weights:
total += weight
@@ -107,9 +121,13 @@ func create_catch(fish: FishDataType, bait_tags: Array[StringName] = []) -> Fish
caught_fish.fish = fish
caught_fish.fish_id = fish.id
caught_fish.ensure_identity()
- caught_fish.weight_lb = _rng.randf_range(
+ var weight_roll: float = _biased_unit_roll(
+ active_rod.weight_roll_bias if active_rod != null else 0.0
+ )
+ caught_fish.weight_lb = lerpf(
fish.get_minimum_weight(),
- fish.get_maximum_weight()
+ fish.get_maximum_weight(),
+ weight_roll,
)
caught_fish.display_scale = fish.get_display_scale_for_weight(
caught_fish.weight_lb
@@ -118,6 +136,11 @@ func create_catch(fish: FishDataType, bait_tags: Array[StringName] = []) -> Fish
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)
+ for quality: int in range(quality_multipliers.size()):
+ if active_rod != null:
+ quality_multipliers[quality] *= active_rod.get_quality_multiplier(
+ quality
+ )
caught_fish.quality = FishQualityType.roll(
_rng,
quality_multipliers,
@@ -127,3 +150,14 @@ func create_catch(fish: FishDataType, bait_tags: Array[StringName] = []) -> Fish
caught_fish.quality,
)
return caught_fish
+
+
+func _biased_unit_roll(bias: float) -> float:
+ var roll: float = _rng.randf()
+ var safe_bias: float = clampf(bias, -0.5, 0.5)
+ if is_zero_approx(safe_bias):
+ return roll
+ var exponent: float = 1.0 / (1.0 + absf(safe_bias) * 2.0)
+ if safe_bias > 0.0:
+ return pow(roll, exponent)
+ return 1.0 - pow(1.0 - roll, exponent)
diff --git a/fish/pools/fish_catalog.tres b/fish/pools/fish_catalog.tres
index 73609c8..0f27f15 100644
--- a/fish/pools/fish_catalog.tres
+++ b/fish/pools/fish_catalog.tres
@@ -1,4 +1,4 @@
-[gd_resource type="Resource" load_steps=55 format=3]
+[gd_resource type="Resource" load_steps=317 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"]
@@ -54,7 +54,270 @@
[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"]
+[ext_resource type="Resource" path="res://fish/species/bowfin/bowfin.tres" id="55_bowfin"]
+[ext_resource type="Resource" path="res://fish/species/sturgeon_lake/sturgeon_lake.tres" id="56_sturgeon_lake"]
+[ext_resource type="Resource" path="res://fish/species/gar_longnose/gar_longnose.tres" id="57_gar_longnose"]
+[ext_resource type="Resource" path="res://fish/species/paddlefish/paddlefish.tres" id="58_paddlefish"]
+[ext_resource type="Resource" path="res://fish/species/sturgeon_shovelnose/sturgeon_shovelnose.tres" id="59_sturgeon_shovelnose"]
+[ext_resource type="Resource" path="res://fish/species/gar_spotted/gar_spotted.tres" id="60_gar_spotted"]
+[ext_resource type="Resource" path="res://fish/species/lungfish_west_african/lungfish_west_african.tres" id="61_lungfish_west_african"]
+[ext_resource type="Resource" path="res://fish/species/mudskipper_atlantic/mudskipper_atlantic.tres" id="62_mudskipper_atlantic"]
+[ext_resource type="Resource" path="res://fish/species/foureyes_largescale/foureyes_largescale.tres" id="63_foureyes_largescale"]
+[ext_resource type="Resource" path="res://fish/species/mullet_red/mullet_red.tres" id="64_mullet_red"]
+[ext_resource type="Resource" path="res://fish/species/plaice_european/plaice_european.tres" id="65_plaice_european"]
+[ext_resource type="Resource" path="res://fish/species/flounder_summer/flounder_summer.tres" id="66_flounder_summer"]
+[ext_resource type="Resource" path="res://fish/species/flounder_winter/flounder_winter.tres" id="67_flounder_winter"]
+[ext_resource type="Resource" path="res://fish/species/dugong/dugong.tres" id="68_dugong"]
+[ext_resource type="Resource" path="res://fish/species/seal_harbor/seal_harbor.tres" id="69_seal_harbor"]
+[ext_resource type="Resource" path="res://fish/species/sea_otter/sea_otter.tres" id="70_sea_otter"]
+[ext_resource type="Resource" path="res://fish/species/walrus/walrus.tres" id="71_walrus"]
+[ext_resource type="Resource" path="res://fish/species/manatee_west_indian/manatee_west_indian.tres" id="72_manatee_west_indian"]
+[ext_resource type="Resource" path="res://fish/species/pipefish_bay/pipefish_bay.tres" id="73_pipefish_bay"]
+[ext_resource type="Resource" path="res://fish/species/seahorse_lined/seahorse_lined.tres" id="74_seahorse_lined"]
+[ext_resource type="Resource" path="res://fish/species/monkfish/monkfish.tres" id="75_monkfish"]
+[ext_resource type="Resource" path="res://fish/species/tripletail/tripletail.tres" id="76_tripletail"]
+[ext_resource type="Resource" path="res://fish/species/bluefish/bluefish.tres" id="77_bluefish"]
+[ext_resource type="Resource" path="res://fish/species/jack_crevalle/jack_crevalle.tres" id="78_jack_crevalle"]
+[ext_resource type="Resource" path="res://fish/species/queenfish_talang/queenfish_talang.tres" id="79_queenfish_talang"]
+[ext_resource type="Resource" path="res://fish/species/sand_lance_american/sand_lance_american.tres" id="80_sand_lance_american"]
+[ext_resource type="Resource" path="res://fish/species/menhaden_atlantic/menhaden_atlantic.tres" id="81_menhaden_atlantic"]
+[ext_resource type="Resource" path="res://fish/species/sardine_european/sardine_european.tres" id="82_sardine_european"]
+[ext_resource type="Resource" path="res://fish/species/sardine_pacific/sardine_pacific.tres" id="83_sardine_pacific"]
+[ext_resource type="Resource" path="res://fish/species/shark_blacktip/shark_blacktip.tres" id="84_shark_blacktip"]
+[ext_resource type="Resource" path="res://fish/species/shark_great_hammerhead/shark_great_hammerhead.tres" id="85_shark_great_hammerhead"]
+[ext_resource type="Resource" path="res://fish/species/shark_great_white/shark_great_white.tres" id="86_shark_great_white"]
+[ext_resource type="Resource" path="res://fish/species/shark_tiger/shark_tiger.tres" id="87_shark_tiger"]
+[ext_resource type="Resource" path="res://fish/species/shark_whale/shark_whale.tres" id="88_shark_whale"]
+[ext_resource type="Resource" path="res://fish/species/burbot/burbot.tres" id="89_burbot"]
+[ext_resource type="Resource" path="res://fish/species/cisco/cisco.tres" id="90_cisco"]
+[ext_resource type="Resource" path="res://fish/species/whitefish_lake/whitefish_lake.tres" id="91_whitefish_lake"]
+[ext_resource type="Resource" path="res://fish/species/char_arctic/char_arctic.tres" id="92_char_arctic"]
+[ext_resource type="Resource" path="res://fish/species/grayling_arctic/grayling_arctic.tres" id="93_grayling_arctic"]
+[ext_resource type="Resource" path="res://fish/species/trout_brown/trout_brown.tres" id="94_trout_brown"]
+[ext_resource type="Resource" path="res://fish/species/wolffish_atlantic/wolffish_atlantic.tres" id="95_wolffish_atlantic"]
+[ext_resource type="Resource" path="res://fish/species/cusk/cusk.tres" id="96_cusk"]
+[ext_resource type="Resource" path="res://fish/species/wolffish_spotted/wolffish_spotted.tres" id="97_wolffish_spotted"]
+[ext_resource type="Resource" path="res://fish/species/herring_atlantic/herring_atlantic.tres" id="98_herring_atlantic"]
+[ext_resource type="Resource" path="res://fish/species/capelin/capelin.tres" id="99_capelin"]
+[ext_resource type="Resource" path="res://fish/species/sprat_european/sprat_european.tres" id="100_sprat_european"]
+[ext_resource type="Resource" path="res://fish/species/herring_pacific/herring_pacific.tres" id="101_herring_pacific"]
+[ext_resource type="Resource" path="res://fish/species/pollock_alaska/pollock_alaska.tres" id="102_pollock_alaska"]
+[ext_resource type="Resource" path="res://fish/species/cod_atlantic/cod_atlantic.tres" id="103_cod_atlantic"]
+[ext_resource type="Resource" path="res://fish/species/pollock_atlantic/pollock_atlantic.tres" id="104_pollock_atlantic"]
+[ext_resource type="Resource" path="res://fish/species/sole_dover/sole_dover.tres" id="105_sole_dover"]
+[ext_resource type="Resource" path="res://fish/species/haddock/haddock.tres" id="106_haddock"]
+[ext_resource type="Resource" path="res://fish/species/cod_pacific/cod_pacific.tres" id="107_cod_pacific"]
+[ext_resource type="Resource" path="res://fish/species/hake_pacific/hake_pacific.tres" id="108_hake_pacific"]
+[ext_resource type="Resource" path="res://fish/species/hake_silver/hake_silver.tres" id="109_hake_silver"]
+[ext_resource type="Resource" path="res://fish/species/nautilus_chambered/nautilus_chambered.tres" id="110_nautilus_chambered"]
+[ext_resource type="Resource" path="res://fish/species/cuttlefish_common/cuttlefish_common.tres" id="111_cuttlefish_common"]
+[ext_resource type="Resource" path="res://fish/species/cuttlefish_flamboyant/cuttlefish_flamboyant.tres" id="112_cuttlefish_flamboyant"]
+[ext_resource type="Resource" path="res://fish/species/tilefish_blueline/tilefish_blueline.tres" id="113_tilefish_blueline"]
+[ext_resource type="Resource" path="res://fish/species/tilefish_golden/tilefish_golden.tres" id="114_tilefish_golden"]
+[ext_resource type="Resource" path="res://fish/species/ocean_perch_pacific/ocean_perch_pacific.tres" id="115_ocean_perch_pacific"]
+[ext_resource type="Resource" path="res://fish/species/sea_bass_chilean/sea_bass_chilean.tres" id="116_sea_bass_chilean"]
+[ext_resource type="Resource" path="res://fish/species/sablefish/sablefish.tres" id="117_sablefish"]
+[ext_resource type="Resource" path="res://fish/species/wreckfish/wreckfish.tres" id="118_wreckfish"]
+[ext_resource type="Resource" path="res://fish/species/oarfish_giant/oarfish_giant.tres" id="119_oarfish_giant"]
+[ext_resource type="Resource" path="res://fish/species/coelacanth_west_indian/coelacanth_west_indian.tres" id="120_coelacanth_west_indian"]
+[ext_resource type="Resource" path="res://fish/species/anglerfish_black_seadevil/anglerfish_black_seadevil.tres" id="121_anglerfish_black_seadevil"]
+[ext_resource type="Resource" path="res://fish/species/fangtooth_common/fangtooth_common.tres" id="122_fangtooth_common"]
+[ext_resource type="Resource" path="res://fish/species/gulper_eel/gulper_eel.tres" id="123_gulper_eel"]
+[ext_resource type="Resource" path="res://fish/species/barreleye_pacific/barreleye_pacific.tres" id="124_barreleye_pacific"]
+[ext_resource type="Resource" path="res://fish/species/blobfish_smooth_head/blobfish_smooth_head.tres" id="125_blobfish_smooth_head"]
+[ext_resource type="Resource" path="res://fish/species/lanternfish_spotted/lanternfish_spotted.tres" id="126_lanternfish_spotted"]
+[ext_resource type="Resource" path="res://fish/species/sea_star_crown_of_thorns/sea_star_crown_of_thorns.tres" id="127_sea_star_crown_of_thorns"]
+[ext_resource type="Resource" path="res://fish/species/sea_cucumber_giant_california/sea_cucumber_giant_california.tres" id="128_sea_cucumber_giant_california"]
+[ext_resource type="Resource" path="res://fish/species/urchin_purple/urchin_purple.tres" id="129_urchin_purple"]
+[ext_resource type="Resource" path="res://fish/species/sand_dollar/sand_dollar.tres" id="130_sand_dollar"]
+[ext_resource type="Resource" path="res://fish/species/sea_star_sunflower/sea_star_sunflower.tres" id="131_sea_star_sunflower"]
+[ext_resource type="Resource" path="res://fish/species/axolotl/axolotl.tres" id="132_axolotl"]
+[ext_resource type="Resource" path="res://fish/species/hellbender/hellbender.tres" id="133_hellbender"]
+[ext_resource type="Resource" path="res://fish/species/guppy/guppy.tres" id="134_guppy"]
+[ext_resource type="Resource" path="res://fish/species/tetra_neon/tetra_neon.tres" id="135_tetra_neon"]
+[ext_resource type="Resource" path="res://fish/species/danio_zebra/danio_zebra.tres" id="136_danio_zebra"]
+[ext_resource type="Resource" path="res://fish/species/largemouth_bass/largemouth_bass.tres" id="137_largemouth_bass"]
+[ext_resource type="Resource" path="res://fish/species/smallmouth_bass/smallmouth_bass.tres" id="138_smallmouth_bass"]
+[ext_resource type="Resource" path="res://fish/species/buffalo_bigmouth/buffalo_bigmouth.tres" id="139_buffalo_bigmouth"]
+[ext_resource type="Resource" path="res://fish/species/redhorse_golden/redhorse_golden.tres" id="140_redhorse_golden"]
+[ext_resource type="Resource" path="res://fish/species/quillback/quillback.tres" id="141_quillback"]
+[ext_resource type="Resource" path="res://fish/species/buffalo_smallmouth/buffalo_smallmouth.tres" id="142_buffalo_smallmouth"]
+[ext_resource type="Resource" path="res://fish/species/sucker_white/sucker_white.tres" id="143_sucker_white"]
+[ext_resource type="Resource" path="res://fish/species/bullhead_black/bullhead_black.tres" id="144_bullhead_black"]
+[ext_resource type="Resource" path="res://fish/species/bullhead_brown/bullhead_brown.tres" id="145_bullhead_brown"]
+[ext_resource type="Resource" path="res://fish/species/madtom_tadpole/madtom_tadpole.tres" id="146_madtom_tadpole"]
+[ext_resource type="Resource" path="res://fish/species/bullhead_yellow/bullhead_yellow.tres" id="147_bullhead_yellow"]
+[ext_resource type="Resource" path="res://fish/species/barbel_common/barbel_common.tres" id="148_barbel_common"]
+[ext_resource type="Resource" path="res://fish/species/bream_common/bream_common.tres" id="149_bream_common"]
+[ext_resource type="Resource" path="res://fish/species/roach_common/roach_common.tres" id="150_roach_common"]
+[ext_resource type="Resource" path="res://fish/species/fallfish/fallfish.tres" id="151_fallfish"]
+[ext_resource type="Resource" path="res://fish/species/ide/ide.tres" id="152_ide"]
+[ext_resource type="Resource" path="res://fish/species/rudd/rudd.tres" id="153_rudd"]
+[ext_resource type="Resource" path="res://fish/species/tench/tench.tres" id="154_tench"]
+[ext_resource type="Resource" path="res://fish/species/crayfish_red_swamp/crayfish_red_swamp.tres" id="155_crayfish_red_swamp"]
+[ext_resource type="Resource" path="res://fish/species/gar_alligator/gar_alligator.tres" id="156_gar_alligator"]
+[ext_resource type="Resource" path="res://fish/species/arapaima/arapaima.tres" id="157_arapaima"]
+[ext_resource type="Resource" path="res://fish/species/tigerfish_goliath/tigerfish_goliath.tres" id="158_tigerfish_goliath"]
+[ext_resource type="Resource" path="res://fish/species/muskellunge/muskellunge.tres" id="159_muskellunge"]
+[ext_resource type="Resource" path="res://fish/species/shiner_common/shiner_common.tres" id="160_shiner_common"]
+[ext_resource type="Resource" path="res://fish/species/chub_creek/chub_creek.tres" id="161_chub_creek"]
+[ext_resource type="Resource" path="res://fish/species/shiner_emerald/shiner_emerald.tres" id="162_shiner_emerald"]
+[ext_resource type="Resource" path="res://fish/species/minnow_fathead/minnow_fathead.tres" id="163_minnow_fathead"]
+[ext_resource type="Resource" path="res://fish/species/shiner_golden/shiner_golden.tres" id="164_shiner_golden"]
+[ext_resource type="Resource" path="res://fish/species/electric_eel/electric_eel.tres" id="165_electric_eel"]
+[ext_resource type="Resource" path="res://fish/species/freshwater_drum/freshwater_drum.tres" id="166_freshwater_drum"]
+[ext_resource type="Resource" path="res://fish/species/goldeye/goldeye.tres" id="167_goldeye"]
+[ext_resource type="Resource" path="res://fish/species/mooneye/mooneye.tres" id="168_mooneye"]
+[ext_resource type="Resource" path="res://fish/species/loach_weather/loach_weather.tres" id="169_loach_weather"]
+[ext_resource type="Resource" path="res://fish/species/koi/koi.tres" id="170_koi"]
+[ext_resource type="Resource" path="res://fish/species/crappie_black/crappie_black.tres" id="171_crappie_black"]
+[ext_resource type="Resource" path="res://fish/species/green_sunfish/green_sunfish.tres" id="172_green_sunfish"]
+[ext_resource type="Resource" path="res://fish/species/pumpkinseed/pumpkinseed.tres" id="173_pumpkinseed"]
+[ext_resource type="Resource" path="res://fish/species/redear_sunfish/redear_sunfish.tres" id="174_redear_sunfish"]
+[ext_resource type="Resource" path="res://fish/species/warmouth/warmouth.tres" id="175_warmouth"]
+[ext_resource type="Resource" path="res://fish/species/crappie_white/crappie_white.tres" id="176_crappie_white"]
+[ext_resource type="Resource" path="res://fish/species/yellow_perch/yellow_perch.tres" id="177_yellow_perch"]
+[ext_resource type="Resource" path="res://fish/species/pickerel_chain/pickerel_chain.tres" id="178_pickerel_chain"]
+[ext_resource type="Resource" path="res://fish/species/pickerel_grass/pickerel_grass.tres" id="179_pickerel_grass"]
+[ext_resource type="Resource" path="res://fish/species/pike_northern/pike_northern.tres" id="180_pike_northern"]
+[ext_resource type="Resource" path="res://fish/species/jelly_crystal/jelly_crystal.tres" id="181_jelly_crystal"]
+[ext_resource type="Resource" path="res://fish/species/jelly_lions_mane/jelly_lions_mane.tres" id="182_jelly_lions_mane"]
+[ext_resource type="Resource" path="res://fish/species/jelly_moon/jelly_moon.tres" id="183_jelly_moon"]
+[ext_resource type="Resource" path="res://fish/species/jelly_pacific_sea_nettle/jelly_pacific_sea_nettle.tres" id="184_jelly_pacific_sea_nettle"]
+[ext_resource type="Resource" path="res://fish/species/jelly_sea_wasp/jelly_sea_wasp.tres" id="185_jelly_sea_wasp"]
+[ext_resource type="Resource" path="res://fish/species/pompano_african/pompano_african.tres" id="186_pompano_african"]
+[ext_resource type="Resource" path="res://fish/species/cobia/cobia.tres" id="187_cobia"]
+[ext_resource type="Resource" path="res://fish/species/trevally_giant/trevally_giant.tres" id="188_trevally_giant"]
+[ext_resource type="Resource" path="res://fish/species/amberjack_greater/amberjack_greater.tres" id="189_amberjack_greater"]
+[ext_resource type="Resource" path="res://fish/species/amberjack_lesser/amberjack_lesser.tres" id="190_amberjack_lesser"]
+[ext_resource type="Resource" path="res://fish/species/halibut_atlantic/halibut_atlantic.tres" id="191_halibut_atlantic"]
+[ext_resource type="Resource" path="res://fish/species/halibut_pacific/halibut_pacific.tres" id="192_halibut_pacific"]
+[ext_resource type="Resource" path="res://fish/species/turbot/turbot.tres" id="193_turbot"]
+[ext_resource type="Resource" path="res://fish/species/lobster_american/lobster_american.tres" id="194_lobster_american"]
+[ext_resource type="Resource" path="res://fish/species/krill_antarctic/krill_antarctic.tres" id="195_krill_antarctic"]
+[ext_resource type="Resource" path="res://fish/species/lobster_caribbean_spiny/lobster_caribbean_spiny.tres" id="196_lobster_caribbean_spiny"]
+[ext_resource type="Resource" path="res://fish/species/shrimp_peacock_mantis/shrimp_peacock_mantis.tres" id="197_shrimp_peacock_mantis"]
+[ext_resource type="Resource" path="res://fish/species/shrimp_skunk_cleaner/shrimp_skunk_cleaner.tres" id="198_shrimp_skunk_cleaner"]
+[ext_resource type="Resource" path="res://fish/species/crab_ghost/crab_ghost.tres" id="199_crab_ghost"]
+[ext_resource type="Resource" path="res://fish/species/crab_blue/crab_blue.tres" id="200_crab_blue"]
+[ext_resource type="Resource" path="res://fish/species/crab_dungeness/crab_dungeness.tres" id="201_crab_dungeness"]
+[ext_resource type="Resource" path="res://fish/species/crab_japanese_spider/crab_japanese_spider.tres" id="202_crab_japanese_spider"]
+[ext_resource type="Resource" path="res://fish/species/crab_red_king/crab_red_king.tres" id="203_crab_red_king"]
+[ext_resource type="Resource" path="res://fish/species/eel_american/eel_american.tres" id="204_eel_american"]
+[ext_resource type="Resource" path="res://fish/species/eel_european/eel_european.tres" id="205_eel_european"]
+[ext_resource type="Resource" path="res://fish/species/smelt_rainbow/smelt_rainbow.tres" id="206_smelt_rainbow"]
+[ext_resource type="Resource" path="res://fish/species/smelt_eulachon/smelt_eulachon.tres" id="207_smelt_eulachon"]
+[ext_resource type="Resource" path="res://fish/species/sturgeon_white/sturgeon_white.tres" id="208_sturgeon_white"]
+[ext_resource type="Resource" path="res://fish/species/mahi_mahi/mahi_mahi.tres" id="209_mahi_mahi"]
+[ext_resource type="Resource" path="res://fish/species/opah/opah.tres" id="210_opah"]
+[ext_resource type="Resource" path="res://fish/species/wahoo/wahoo.tres" id="211_wahoo"]
+[ext_resource type="Resource" path="res://fish/species/octopus_common/octopus_common.tres" id="212_octopus_common"]
+[ext_resource type="Resource" path="res://fish/species/octopus_day/octopus_day.tres" id="213_octopus_day"]
+[ext_resource type="Resource" path="res://fish/species/octopus_giant_pacific/octopus_giant_pacific.tres" id="214_octopus_giant_pacific"]
+[ext_resource type="Resource" path="res://fish/species/octopus_greater_blue_ringed/octopus_greater_blue_ringed.tres" id="215_octopus_greater_blue_ringed"]
+[ext_resource type="Resource" path="res://fish/species/skate_barndoor/skate_barndoor.tres" id="216_skate_barndoor"]
+[ext_resource type="Resource" path="res://fish/species/manta_ray_giant/manta_ray_giant.tres" id="217_manta_ray_giant"]
+[ext_resource type="Resource" path="res://fish/species/sawfish_largetooth/sawfish_largetooth.tres" id="218_sawfish_largetooth"]
+[ext_resource type="Resource" path="res://fish/species/stingray_ocellate_river/stingray_ocellate_river.tres" id="219_stingray_ocellate_river"]
+[ext_resource type="Resource" path="res://fish/species/stingray_southern/stingray_southern.tres" id="220_stingray_southern"]
+[ext_resource type="Resource" path="res://fish/species/lionfish_red/lionfish_red.tres" id="221_lionfish_red"]
+[ext_resource type="Resource" path="res://fish/species/scorpionfish_red/scorpionfish_red.tres" id="222_scorpionfish_red"]
+[ext_resource type="Resource" path="res://fish/species/moray_green/moray_green.tres" id="223_moray_green"]
+[ext_resource type="Resource" path="res://fish/species/flounder_peacock/flounder_peacock.tres" id="224_flounder_peacock"]
+[ext_resource type="Resource" path="res://fish/species/surgeonfish_blue/surgeonfish_blue.tres" id="225_surgeonfish_blue"]
+[ext_resource type="Resource" path="res://fish/species/parrotfish_rainbow/parrotfish_rainbow.tres" id="226_parrotfish_rainbow"]
+[ext_resource type="Resource" path="res://fish/species/parrotfish_stoplight/parrotfish_stoplight.tres" id="227_parrotfish_stoplight"]
+[ext_resource type="Resource" path="res://fish/species/tang_yellow/tang_yellow.tres" id="228_tang_yellow"]
+[ext_resource type="Resource" path="res://fish/species/trumpetfish_atlantic/trumpetfish_atlantic.tres" id="229_trumpetfish_atlantic"]
+[ext_resource type="Resource" path="res://fish/species/triggerfish_gray/triggerfish_gray.tres" id="230_triggerfish_gray"]
+[ext_resource type="Resource" path="res://fish/species/pufferfish_guineafowl/pufferfish_guineafowl.tres" id="231_pufferfish_guineafowl"]
+[ext_resource type="Resource" path="res://fish/species/cowfish_longhorn/cowfish_longhorn.tres" id="232_cowfish_longhorn"]
+[ext_resource type="Resource" path="res://fish/species/triggerfish_queen/triggerfish_queen.tres" id="233_triggerfish_queen"]
+[ext_resource type="Resource" path="res://fish/species/cornetfish_red/cornetfish_red.tres" id="234_cornetfish_red"]
+[ext_resource type="Resource" path="res://fish/species/porcupinefish_spotted/porcupinefish_spotted.tres" id="235_porcupinefish_spotted"]
+[ext_resource type="Resource" path="res://fish/species/boxfish_yellow/boxfish_yellow.tres" id="236_boxfish_yellow"]
+[ext_resource type="Resource" path="res://fish/species/butterflyfish_copperband/butterflyfish_copperband.tres" id="237_butterflyfish_copperband"]
+[ext_resource type="Resource" path="res://fish/species/clownfish_ocellaris/clownfish_ocellaris.tres" id="238_clownfish_ocellaris"]
+[ext_resource type="Resource" path="res://fish/species/angelfish_queen/angelfish_queen.tres" id="239_angelfish_queen"]
+[ext_resource type="Resource" path="res://fish/species/grouper_atlantic_goliath/grouper_atlantic_goliath.tres" id="240_grouper_atlantic_goliath"]
+[ext_resource type="Resource" path="res://fish/species/trevally_bluefin/trevally_bluefin.tres" id="241_trevally_bluefin"]
+[ext_resource type="Resource" path="res://fish/species/grouper_giant/grouper_giant.tres" id="242_grouper_giant"]
+[ext_resource type="Resource" path="res://fish/species/barracuda_great/barracuda_great.tres" id="243_barracuda_great"]
+[ext_resource type="Resource" path="res://fish/species/grouper_nassau/grouper_nassau.tres" id="244_grouper_nassau"]
+[ext_resource type="Resource" path="res://fish/species/chromis_blue_green/chromis_blue_green.tres" id="245_chromis_blue_green"]
+[ext_resource type="Resource" path="res://fish/species/grunt_bluestriped/grunt_bluestriped.tres" id="246_grunt_bluestriped"]
+[ext_resource type="Resource" path="res://fish/species/grunt_french/grunt_french.tres" id="247_grunt_french"]
+[ext_resource type="Resource" path="res://fish/species/damselfish_sergeant_major/damselfish_sergeant_major.tres" id="248_damselfish_sergeant_major"]
+[ext_resource type="Resource" path="res://fish/species/wrasse_ballan/wrasse_ballan.tres" id="249_wrasse_ballan"]
+[ext_resource type="Resource" path="res://fish/species/wrasse_cleaner/wrasse_cleaner.tres" id="250_wrasse_cleaner"]
+[ext_resource type="Resource" path="res://fish/species/hogfish/hogfish.tres" id="251_hogfish"]
+[ext_resource type="Resource" path="res://fish/species/turtle_green/turtle_green.tres" id="252_turtle_green"]
+[ext_resource type="Resource" path="res://fish/species/turtle_hawksbill/turtle_hawksbill.tres" id="253_turtle_hawksbill"]
+[ext_resource type="Resource" path="res://fish/species/turtle_leatherback/turtle_leatherback.tres" id="254_turtle_leatherback"]
+[ext_resource type="Resource" path="res://fish/species/turtle_loggerhead/turtle_loggerhead.tres" id="255_turtle_loggerhead"]
+[ext_resource type="Resource" path="res://fish/species/seadragon_leafy/seadragon_leafy.tres" id="256_seadragon_leafy"]
+[ext_resource type="Resource" path="res://fish/species/seadragon_weedy/seadragon_weedy.tres" id="257_seadragon_weedy"]
+[ext_resource type="Resource" path="res://fish/species/oyster_black_lip_pearl/oyster_black_lip_pearl.tres" id="258_oyster_black_lip_pearl"]
+[ext_resource type="Resource" path="res://fish/species/clam_giant/clam_giant.tres" id="259_clam_giant"]
+[ext_resource type="Resource" path="res://fish/species/clam_geoduck/clam_geoduck.tres" id="260_clam_geoduck"]
+[ext_resource type="Resource" path="res://fish/species/conch_queen/conch_queen.tres" id="261_conch_queen"]
+[ext_resource type="Resource" path="res://fish/species/abalone_red/abalone_red.tres" id="262_abalone_red"]
+[ext_resource type="Resource" path="res://fish/species/squid_bigfin_reef/squid_bigfin_reef.tres" id="263_squid_bigfin_reef"]
+[ext_resource type="Resource" path="res://fish/species/squid_colossal/squid_colossal.tres" id="264_squid_colossal"]
+[ext_resource type="Resource" path="res://fish/species/squid_giant/squid_giant.tres" id="265_squid_giant"]
+[ext_resource type="Resource" path="res://fish/species/squid_humboldt/squid_humboldt.tres" id="266_squid_humboldt"]
+[ext_resource type="Resource" path="res://fish/species/halfbeak_ballyhoo/halfbeak_ballyhoo.tres" id="267_halfbeak_ballyhoo"]
+[ext_resource type="Resource" path="res://fish/species/needlefish_hound/needlefish_hound.tres" id="268_needlefish_hound"]
+[ext_resource type="Resource" path="res://fish/species/flyingfish_tropical/flyingfish_tropical.tres" id="269_flyingfish_tropical"]
+[ext_resource type="Resource" path="res://fish/species/croaker_atlantic/croaker_atlantic.tres" id="270_croaker_atlantic"]
+[ext_resource type="Resource" path="res://fish/species/black_drum/black_drum.tres" id="271_black_drum"]
+[ext_resource type="Resource" path="res://fish/species/red_drum/red_drum.tres" id="272_red_drum"]
+[ext_resource type="Resource" path="res://fish/species/mullet_striped/mullet_striped.tres" id="273_mullet_striped"]
+[ext_resource type="Resource" path="res://fish/species/weakfish/weakfish.tres" id="274_weakfish"]
+[ext_resource type="Resource" path="res://fish/species/white_perch/white_perch.tres" id="275_white_perch"]
+[ext_resource type="Resource" path="res://fish/species/croaker_yellowfin/croaker_yellowfin.tres" id="276_croaker_yellowfin"]
+[ext_resource type="Resource" path="res://fish/species/rockfish_black/rockfish_black.tres" id="277_rockfish_black"]
+[ext_resource type="Resource" path="res://fish/species/sea_bass_black/sea_bass_black.tres" id="278_sea_bass_black"]
+[ext_resource type="Resource" path="res://fish/species/rockfish_canary/rockfish_canary.tres" id="279_rockfish_canary"]
+[ext_resource type="Resource" path="res://fish/species/porgy_scup/porgy_scup.tres" id="280_porgy_scup"]
+[ext_resource type="Resource" path="res://fish/species/sheepshead/sheepshead.tres" id="281_sheepshead"]
+[ext_resource type="Resource" path="res://fish/species/tautog/tautog.tres" id="282_tautog"]
+[ext_resource type="Resource" path="res://fish/species/rockfish_yelloweye/rockfish_yelloweye.tres" id="283_rockfish_yelloweye"]
+[ext_resource type="Resource" path="res://fish/species/lingcod/lingcod.tres" id="284_lingcod"]
+[ext_resource type="Resource" path="res://fish/species/milkfish/milkfish.tres" id="285_milkfish"]
+[ext_resource type="Resource" path="res://fish/species/roosterfish/roosterfish.tres" id="286_roosterfish"]
+[ext_resource type="Resource" path="res://fish/species/tarpon_atlantic/tarpon_atlantic.tres" id="287_tarpon_atlantic"]
+[ext_resource type="Resource" path="res://fish/species/barramundi/barramundi.tres" id="288_barramundi"]
+[ext_resource type="Resource" path="res://fish/species/snook_common/snook_common.tres" id="289_snook_common"]
+[ext_resource type="Resource" path="res://fish/species/bonefish/bonefish.tres" id="290_bonefish"]
+[ext_resource type="Resource" path="res://fish/species/pompano_florida/pompano_florida.tres" id="291_pompano_florida"]
+[ext_resource type="Resource" path="res://fish/species/ladyfish/ladyfish.tres" id="292_ladyfish"]
+[ext_resource type="Resource" path="res://fish/species/permit/permit.tres" id="293_permit"]
+[ext_resource type="Resource" path="res://fish/species/knifefish_clown/knifefish_clown.tres" id="294_knifefish_clown"]
+[ext_resource type="Resource" path="res://fish/species/loach_clown/loach_clown.tres" id="295_loach_clown"]
+[ext_resource type="Resource" path="res://fish/species/discus/discus.tres" id="296_discus"]
+[ext_resource type="Resource" path="res://fish/species/angelfish_freshwater/angelfish_freshwater.tres" id="297_angelfish_freshwater"]
+[ext_resource type="Resource" path="res://fish/species/gourami_giant/gourami_giant.tres" id="298_gourami_giant"]
+[ext_resource type="Resource" path="res://fish/species/tilapia_nile/tilapia_nile.tres" id="299_tilapia_nile"]
+[ext_resource type="Resource" path="res://fish/species/oscar/oscar.tres" id="300_oscar"]
+[ext_resource type="Resource" path="res://fish/species/betta_siamese/betta_siamese.tres" id="301_betta_siamese"]
+[ext_resource type="Resource" path="res://fish/species/tambaqui/tambaqui.tres" id="302_tambaqui"]
+[ext_resource type="Resource" path="res://fish/species/catfish_walking/catfish_walking.tres" id="303_catfish_walking"]
+[ext_resource type="Resource" path="res://fish/species/snakehead_giant/snakehead_giant.tres" id="304_snakehead_giant"]
+[ext_resource type="Resource" path="res://fish/species/dorado_golden/dorado_golden.tres" id="305_dorado_golden"]
+[ext_resource type="Resource" path="res://fish/species/mahseer_golden/mahseer_golden.tres" id="306_mahseer_golden"]
+[ext_resource type="Resource" path="res://fish/species/peacock_bass/peacock_bass.tres" id="307_peacock_bass"]
+[ext_resource type="Resource" path="res://fish/species/piranha_red_bellied/piranha_red_bellied.tres" id="308_piranha_red_bellied"]
+[ext_resource type="Resource" path="res://fish/species/arowana_silver/arowana_silver.tres" id="309_arowana_silver"]
+[ext_resource type="Resource" path="res://fish/species/whale_blue/whale_blue.tres" id="310_whale_blue"]
+[ext_resource type="Resource" path="res://fish/species/dolphin_bottlenose/dolphin_bottlenose.tres" id="311_dolphin_bottlenose"]
+[ext_resource type="Resource" path="res://fish/species/whale_humpback/whale_humpback.tres" id="312_whale_humpback"]
+[ext_resource type="Resource" path="res://fish/species/whale_killer/whale_killer.tres" id="313_whale_killer"]
+[ext_resource type="Resource" path="res://fish/species/whale_sperm/whale_sperm.tres" id="314_whale_sperm"]
+[ext_resource type="Resource" path="res://fish/species/crab_brown/crab_brown.tres" id="315_crab_brown"]
+[ext_resource type="Resource" path="res://fish/species/clam_manila/clam_manila.tres" id="316_clam_manila"]
+[ext_resource type="Resource" path="res://fish/species/beetle_stag_common/beetle_stag_common.tres" id="317_beetle_stag_common"]
[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"), 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")]
+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"), ExtResource("55_bowfin"), ExtResource("56_sturgeon_lake"), ExtResource("57_gar_longnose"), ExtResource("58_paddlefish"), ExtResource("59_sturgeon_shovelnose"), ExtResource("60_gar_spotted"), ExtResource("61_lungfish_west_african"), ExtResource("62_mudskipper_atlantic"), ExtResource("63_foureyes_largescale"), ExtResource("64_mullet_red"), ExtResource("65_plaice_european"), ExtResource("66_flounder_summer"), ExtResource("67_flounder_winter"), ExtResource("68_dugong"), ExtResource("69_seal_harbor"), ExtResource("70_sea_otter"), ExtResource("71_walrus"), ExtResource("72_manatee_west_indian"), ExtResource("73_pipefish_bay"), ExtResource("74_seahorse_lined"), ExtResource("75_monkfish"), ExtResource("76_tripletail"), ExtResource("77_bluefish"), ExtResource("78_jack_crevalle"), ExtResource("79_queenfish_talang"), ExtResource("80_sand_lance_american"), ExtResource("81_menhaden_atlantic"), ExtResource("82_sardine_european"), ExtResource("83_sardine_pacific"), ExtResource("84_shark_blacktip"), ExtResource("85_shark_great_hammerhead"), ExtResource("86_shark_great_white"), ExtResource("87_shark_tiger"), ExtResource("88_shark_whale"), ExtResource("89_burbot"), ExtResource("90_cisco"), ExtResource("91_whitefish_lake"), ExtResource("92_char_arctic"), ExtResource("93_grayling_arctic"), ExtResource("94_trout_brown"), ExtResource("95_wolffish_atlantic"), ExtResource("96_cusk"), ExtResource("97_wolffish_spotted"), ExtResource("98_herring_atlantic"), ExtResource("99_capelin"), ExtResource("100_sprat_european"), ExtResource("101_herring_pacific"), ExtResource("102_pollock_alaska"), ExtResource("103_cod_atlantic"), ExtResource("104_pollock_atlantic"), ExtResource("105_sole_dover"), ExtResource("106_haddock"), ExtResource("107_cod_pacific"), ExtResource("108_hake_pacific"), ExtResource("109_hake_silver"), ExtResource("110_nautilus_chambered"), ExtResource("111_cuttlefish_common"), ExtResource("112_cuttlefish_flamboyant"), ExtResource("113_tilefish_blueline"), ExtResource("114_tilefish_golden"), ExtResource("115_ocean_perch_pacific"), ExtResource("116_sea_bass_chilean"), ExtResource("117_sablefish"), ExtResource("118_wreckfish"), ExtResource("119_oarfish_giant"), ExtResource("120_coelacanth_west_indian"), ExtResource("121_anglerfish_black_seadevil"), ExtResource("122_fangtooth_common"), ExtResource("123_gulper_eel"), ExtResource("124_barreleye_pacific"), ExtResource("125_blobfish_smooth_head"), ExtResource("126_lanternfish_spotted"), ExtResource("127_sea_star_crown_of_thorns"), ExtResource("128_sea_cucumber_giant_california"), ExtResource("129_urchin_purple"), ExtResource("130_sand_dollar"), ExtResource("131_sea_star_sunflower"), ExtResource("132_axolotl"), ExtResource("133_hellbender"), ExtResource("134_guppy"), ExtResource("135_tetra_neon"), ExtResource("136_danio_zebra"), ExtResource("137_largemouth_bass"), ExtResource("138_smallmouth_bass"), ExtResource("139_buffalo_bigmouth"), ExtResource("140_redhorse_golden"), ExtResource("141_quillback"), ExtResource("142_buffalo_smallmouth"), ExtResource("143_sucker_white"), ExtResource("144_bullhead_black"), ExtResource("145_bullhead_brown"), ExtResource("146_madtom_tadpole"), ExtResource("147_bullhead_yellow"), ExtResource("148_barbel_common"), ExtResource("149_bream_common"), ExtResource("150_roach_common"), ExtResource("151_fallfish"), ExtResource("152_ide"), ExtResource("153_rudd"), ExtResource("154_tench"), ExtResource("155_crayfish_red_swamp"), ExtResource("156_gar_alligator"), ExtResource("157_arapaima"), ExtResource("158_tigerfish_goliath"), ExtResource("159_muskellunge"), ExtResource("160_shiner_common"), ExtResource("161_chub_creek"), ExtResource("162_shiner_emerald"), ExtResource("163_minnow_fathead"), ExtResource("164_shiner_golden"), ExtResource("165_electric_eel"), ExtResource("166_freshwater_drum"), ExtResource("167_goldeye"), ExtResource("168_mooneye"), ExtResource("169_loach_weather"), ExtResource("170_koi"), ExtResource("171_crappie_black"), ExtResource("172_green_sunfish"), ExtResource("173_pumpkinseed"), ExtResource("174_redear_sunfish"), ExtResource("175_warmouth"), ExtResource("176_crappie_white"), ExtResource("177_yellow_perch"), ExtResource("178_pickerel_chain"), ExtResource("179_pickerel_grass"), ExtResource("180_pike_northern"), ExtResource("181_jelly_crystal"), ExtResource("182_jelly_lions_mane"), ExtResource("183_jelly_moon"), ExtResource("184_jelly_pacific_sea_nettle"), ExtResource("185_jelly_sea_wasp"), ExtResource("186_pompano_african"), ExtResource("187_cobia"), ExtResource("188_trevally_giant"), ExtResource("189_amberjack_greater"), ExtResource("190_amberjack_lesser"), ExtResource("191_halibut_atlantic"), ExtResource("192_halibut_pacific"), ExtResource("193_turbot"), ExtResource("194_lobster_american"), ExtResource("195_krill_antarctic"), ExtResource("196_lobster_caribbean_spiny"), ExtResource("197_shrimp_peacock_mantis"), ExtResource("198_shrimp_skunk_cleaner"), ExtResource("199_crab_ghost"), ExtResource("200_crab_blue"), ExtResource("201_crab_dungeness"), ExtResource("202_crab_japanese_spider"), ExtResource("203_crab_red_king"), ExtResource("204_eel_american"), ExtResource("205_eel_european"), ExtResource("206_smelt_rainbow"), ExtResource("207_smelt_eulachon"), ExtResource("208_sturgeon_white"), ExtResource("209_mahi_mahi"), ExtResource("210_opah"), ExtResource("211_wahoo"), ExtResource("212_octopus_common"), ExtResource("213_octopus_day"), ExtResource("214_octopus_giant_pacific"), ExtResource("215_octopus_greater_blue_ringed"), ExtResource("216_skate_barndoor"), ExtResource("217_manta_ray_giant"), ExtResource("218_sawfish_largetooth"), ExtResource("219_stingray_ocellate_river"), ExtResource("220_stingray_southern"), ExtResource("221_lionfish_red"), ExtResource("222_scorpionfish_red"), ExtResource("223_moray_green"), ExtResource("224_flounder_peacock"), ExtResource("225_surgeonfish_blue"), ExtResource("226_parrotfish_rainbow"), ExtResource("227_parrotfish_stoplight"), ExtResource("228_tang_yellow"), ExtResource("229_trumpetfish_atlantic"), ExtResource("230_triggerfish_gray"), ExtResource("231_pufferfish_guineafowl"), ExtResource("232_cowfish_longhorn"), ExtResource("233_triggerfish_queen"), ExtResource("234_cornetfish_red"), ExtResource("235_porcupinefish_spotted"), ExtResource("236_boxfish_yellow"), ExtResource("237_butterflyfish_copperband"), ExtResource("238_clownfish_ocellaris"), ExtResource("239_angelfish_queen"), ExtResource("240_grouper_atlantic_goliath"), ExtResource("241_trevally_bluefin"), ExtResource("242_grouper_giant"), ExtResource("243_barracuda_great"), ExtResource("244_grouper_nassau"), ExtResource("245_chromis_blue_green"), ExtResource("246_grunt_bluestriped"), ExtResource("247_grunt_french"), ExtResource("248_damselfish_sergeant_major"), ExtResource("249_wrasse_ballan"), ExtResource("250_wrasse_cleaner"), ExtResource("251_hogfish"), ExtResource("252_turtle_green"), ExtResource("253_turtle_hawksbill"), ExtResource("254_turtle_leatherback"), ExtResource("255_turtle_loggerhead"), ExtResource("256_seadragon_leafy"), ExtResource("257_seadragon_weedy"), ExtResource("258_oyster_black_lip_pearl"), ExtResource("259_clam_giant"), ExtResource("260_clam_geoduck"), ExtResource("261_conch_queen"), ExtResource("262_abalone_red"), ExtResource("263_squid_bigfin_reef"), ExtResource("264_squid_colossal"), ExtResource("265_squid_giant"), ExtResource("266_squid_humboldt"), ExtResource("267_halfbeak_ballyhoo"), ExtResource("268_needlefish_hound"), ExtResource("269_flyingfish_tropical"), ExtResource("270_croaker_atlantic"), ExtResource("271_black_drum"), ExtResource("272_red_drum"), ExtResource("273_mullet_striped"), ExtResource("274_weakfish"), ExtResource("275_white_perch"), ExtResource("276_croaker_yellowfin"), ExtResource("277_rockfish_black"), ExtResource("278_sea_bass_black"), ExtResource("279_rockfish_canary"), ExtResource("280_porgy_scup"), ExtResource("281_sheepshead"), ExtResource("282_tautog"), ExtResource("283_rockfish_yelloweye"), ExtResource("284_lingcod"), ExtResource("285_milkfish"), ExtResource("286_roosterfish"), ExtResource("287_tarpon_atlantic"), ExtResource("288_barramundi"), ExtResource("289_snook_common"), ExtResource("290_bonefish"), ExtResource("291_pompano_florida"), ExtResource("292_ladyfish"), ExtResource("293_permit"), ExtResource("294_knifefish_clown"), ExtResource("295_loach_clown"), ExtResource("296_discus"), ExtResource("297_angelfish_freshwater"), ExtResource("298_gourami_giant"), ExtResource("299_tilapia_nile"), ExtResource("300_oscar"), ExtResource("301_betta_siamese"), ExtResource("302_tambaqui"), ExtResource("303_catfish_walking"), ExtResource("304_snakehead_giant"), ExtResource("305_dorado_golden"), ExtResource("306_mahseer_golden"), ExtResource("307_peacock_bass"), ExtResource("308_piranha_red_bellied"), ExtResource("309_arowana_silver"), ExtResource("310_whale_blue"), ExtResource("311_dolphin_bottlenose"), ExtResource("312_whale_humpback"), ExtResource("313_whale_killer"), ExtResource("314_whale_sperm"), ExtResource("315_crab_brown"), ExtResource("316_clam_manila"), ExtResource("317_beetle_stag_common")]
diff --git a/fish/pools/generated_lake_pool.tres b/fish/pools/generated_lake_pool.tres
new file mode 100644
index 0000000..c0ff343
--- /dev/null
+++ b/fish/pools/generated_lake_pool.tres
@@ -0,0 +1,27 @@
+[gd_resource type="Resource" load_steps=22 format=3]
+
+[ext_resource type="Script" path="res://fish/fish_pool.gd" id="1_pool"]
+[ext_resource type="Resource" path="res://fish/species/sturgeon_lake/sturgeon_lake.tres" id="2_sturgeon_lake"]
+[ext_resource type="Resource" path="res://fish/species/burbot/burbot.tres" id="3_burbot"]
+[ext_resource type="Resource" path="res://fish/species/cisco/cisco.tres" id="4_cisco"]
+[ext_resource type="Resource" path="res://fish/species/whitefish_lake/whitefish_lake.tres" id="5_whitefish_lake"]
+[ext_resource type="Resource" path="res://fish/species/char_arctic/char_arctic.tres" id="6_char_arctic"]
+[ext_resource type="Resource" path="res://fish/species/trout_golden/trout_golden.tres" id="7_trout_golden"]
+[ext_resource type="Resource" path="res://fish/species/axolotl/axolotl.tres" id="8_axolotl"]
+[ext_resource type="Resource" path="res://fish/species/bream_common/bream_common.tres" id="9_bream_common"]
+[ext_resource type="Resource" path="res://fish/species/muskellunge/muskellunge.tres" id="10_muskellunge"]
+[ext_resource type="Resource" path="res://fish/species/shiner_emerald/shiner_emerald.tres" id="11_shiner_emerald"]
+[ext_resource type="Resource" path="res://fish/species/chub_lake/chub_lake.tres" id="12_chub_lake"]
+[ext_resource type="Resource" path="res://fish/species/crappie_white/crappie_white.tres" id="13_crappie_white"]
+[ext_resource type="Resource" path="res://fish/species/yellow_perch/yellow_perch.tres" id="14_yellow_perch"]
+[ext_resource type="Resource" path="res://fish/species/pike_northern/pike_northern.tres" id="15_pike_northern"]
+[ext_resource type="Resource" path="res://fish/species/saugeye/saugeye.tres" id="16_saugeye"]
+[ext_resource type="Resource" path="res://fish/species/walleye/walleye.tres" id="17_walleye"]
+[ext_resource type="Resource" path="res://fish/species/goby_round/goby_round.tres" id="18_goby_round"]
+[ext_resource type="Resource" path="res://fish/species/smelt_rainbow/smelt_rainbow.tres" id="19_smelt_rainbow"]
+[ext_resource type="Resource" path="res://fish/species/gourami_giant/gourami_giant.tres" id="20_gourami_giant"]
+[ext_resource type="Resource" path="res://fish/species/tilapia_nile/tilapia_nile.tres" id="21_tilapia_nile"]
+
+[resource]
+script = ExtResource("1_pool")
+candidates = [ExtResource("2_sturgeon_lake"), ExtResource("3_burbot"), ExtResource("4_cisco"), ExtResource("5_whitefish_lake"), ExtResource("6_char_arctic"), ExtResource("7_trout_golden"), ExtResource("8_axolotl"), ExtResource("9_bream_common"), ExtResource("10_muskellunge"), ExtResource("11_shiner_emerald"), ExtResource("12_chub_lake"), ExtResource("13_crappie_white"), ExtResource("14_yellow_perch"), ExtResource("15_pike_northern"), ExtResource("16_saugeye"), ExtResource("17_walleye"), ExtResource("18_goby_round"), ExtResource("19_smelt_rainbow"), ExtResource("20_gourami_giant"), ExtResource("21_tilapia_nile")]
diff --git a/fish/pools/generated_pond_pool.tres b/fish/pools/generated_pond_pool.tres
new file mode 100644
index 0000000..9da4ed5
--- /dev/null
+++ b/fish/pools/generated_pond_pool.tres
@@ -0,0 +1,40 @@
+[gd_resource type="Resource" load_steps=35 format=3]
+
+[ext_resource type="Script" path="res://fish/fish_pool.gd" id="1_pool"]
+[ext_resource type="Resource" path="res://fish/species/bowfin/bowfin.tres" id="2_bowfin"]
+[ext_resource type="Resource" path="res://fish/species/gar_spotted/gar_spotted.tres" id="3_gar_spotted"]
+[ext_resource type="Resource" path="res://fish/species/lungfish_west_african/lungfish_west_african.tres" id="4_lungfish_west_african"]
+[ext_resource type="Resource" path="res://fish/species/guppy/guppy.tres" id="5_guppy"]
+[ext_resource type="Resource" path="res://fish/species/tetra_neon/tetra_neon.tres" id="6_tetra_neon"]
+[ext_resource type="Resource" path="res://fish/species/danio_zebra/danio_zebra.tres" id="7_danio_zebra"]
+[ext_resource type="Resource" path="res://fish/species/largemouth_bass/largemouth_bass.tres" id="8_largemouth_bass"]
+[ext_resource type="Resource" path="res://fish/species/bullhead_black/bullhead_black.tres" id="9_bullhead_black"]
+[ext_resource type="Resource" path="res://fish/species/bullhead_brown/bullhead_brown.tres" id="10_bullhead_brown"]
+[ext_resource type="Resource" path="res://fish/species/catfish_channel/catfish_channel.tres" id="11_catfish_channel"]
+[ext_resource type="Resource" path="res://fish/species/catfish_white/catfish_white.tres" id="12_catfish_white"]
+[ext_resource type="Resource" path="res://fish/species/bullhead_yellow/bullhead_yellow.tres" id="13_bullhead_yellow"]
+[ext_resource type="Resource" path="res://fish/species/carp/carp.tres" id="14_carp"]
+[ext_resource type="Resource" path="res://fish/species/roach_common/roach_common.tres" id="15_roach_common"]
+[ext_resource type="Resource" path="res://fish/species/rudd/rudd.tres" id="16_rudd"]
+[ext_resource type="Resource" path="res://fish/species/tench/tench.tres" id="17_tench"]
+[ext_resource type="Resource" path="res://fish/species/crayfish_red_swamp/crayfish_red_swamp.tres" id="18_crayfish_red_swamp"]
+[ext_resource type="Resource" path="res://fish/species/minnow_fathead/minnow_fathead.tres" id="19_minnow_fathead"]
+[ext_resource type="Resource" path="res://fish/species/shiner_golden/shiner_golden.tres" id="20_shiner_golden"]
+[ext_resource type="Resource" path="res://fish/species/loach_weather/loach_weather.tres" id="21_loach_weather"]
+[ext_resource type="Resource" path="res://fish/species/goldfish_bubbleeye/goldfish_bubbleeye.tres" id="22_goldfish_bubbleeye"]
+[ext_resource type="Resource" path="res://fish/species/goldfish/goldfish.tres" id="23_goldfish"]
+[ext_resource type="Resource" path="res://fish/species/koi/koi.tres" id="24_koi"]
+[ext_resource type="Resource" path="res://fish/species/crappie_black/crappie_black.tres" id="25_crappie_black"]
+[ext_resource type="Resource" path="res://fish/species/bluegill/bluegill.tres" id="26_bluegill"]
+[ext_resource type="Resource" path="res://fish/species/green_sunfish/green_sunfish.tres" id="27_green_sunfish"]
+[ext_resource type="Resource" path="res://fish/species/pumpkinseed/pumpkinseed.tres" id="28_pumpkinseed"]
+[ext_resource type="Resource" path="res://fish/species/redear_sunfish/redear_sunfish.tres" id="29_redear_sunfish"]
+[ext_resource type="Resource" path="res://fish/species/warmouth/warmouth.tres" id="30_warmouth"]
+[ext_resource type="Resource" path="res://fish/species/pickerel_chain/pickerel_chain.tres" id="31_pickerel_chain"]
+[ext_resource type="Resource" path="res://fish/species/pickerel_grass/pickerel_grass.tres" id="32_pickerel_grass"]
+[ext_resource type="Resource" path="res://fish/species/betta_siamese/betta_siamese.tres" id="33_betta_siamese"]
+[ext_resource type="Resource" path="res://fish/species/catfish_walking/catfish_walking.tres" id="34_catfish_walking"]
+
+[resource]
+script = ExtResource("1_pool")
+candidates = [ExtResource("2_bowfin"), ExtResource("3_gar_spotted"), ExtResource("4_lungfish_west_african"), ExtResource("5_guppy"), ExtResource("6_tetra_neon"), ExtResource("7_danio_zebra"), ExtResource("8_largemouth_bass"), ExtResource("9_bullhead_black"), ExtResource("10_bullhead_brown"), ExtResource("11_catfish_channel"), ExtResource("12_catfish_white"), ExtResource("13_bullhead_yellow"), ExtResource("14_carp"), ExtResource("15_roach_common"), ExtResource("16_rudd"), ExtResource("17_tench"), ExtResource("18_crayfish_red_swamp"), ExtResource("19_minnow_fathead"), ExtResource("20_shiner_golden"), ExtResource("21_loach_weather"), ExtResource("22_goldfish_bubbleeye"), ExtResource("23_goldfish"), ExtResource("24_koi"), ExtResource("25_crappie_black"), ExtResource("26_bluegill"), ExtResource("27_green_sunfish"), ExtResource("28_pumpkinseed"), ExtResource("29_redear_sunfish"), ExtResource("30_warmouth"), ExtResource("31_pickerel_chain"), ExtResource("32_pickerel_grass"), ExtResource("33_betta_siamese"), ExtResource("34_catfish_walking")]
diff --git a/fish/pools/generated_river_pool.tres b/fish/pools/generated_river_pool.tres
new file mode 100644
index 0000000..66102ef
--- /dev/null
+++ b/fish/pools/generated_river_pool.tres
@@ -0,0 +1,60 @@
+[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/gar_longnose/gar_longnose.tres" id="2_gar_longnose"]
+[ext_resource type="Resource" path="res://fish/species/paddlefish/paddlefish.tres" id="3_paddlefish"]
+[ext_resource type="Resource" path="res://fish/species/sturgeon_shovelnose/sturgeon_shovelnose.tres" id="4_sturgeon_shovelnose"]
+[ext_resource type="Resource" path="res://fish/species/foureyes_largescale/foureyes_largescale.tres" id="5_foureyes_largescale"]
+[ext_resource type="Resource" path="res://fish/species/grayling_arctic/grayling_arctic.tres" id="6_grayling_arctic"]
+[ext_resource type="Resource" path="res://fish/species/trout_brown/trout_brown.tres" id="7_trout_brown"]
+[ext_resource type="Resource" path="res://fish/species/trout_cutthroat/trout_cutthroat.tres" id="8_trout_cutthroat"]
+[ext_resource type="Resource" path="res://fish/species/trout_rainbow/trout_rainbow.tres" id="9_trout_rainbow"]
+[ext_resource type="Resource" path="res://fish/species/hellbender/hellbender.tres" id="10_hellbender"]
+[ext_resource type="Resource" path="res://fish/species/smallmouth_bass/smallmouth_bass.tres" id="11_smallmouth_bass"]
+[ext_resource type="Resource" path="res://fish/species/buffalo_bigmouth/buffalo_bigmouth.tres" id="12_buffalo_bigmouth"]
+[ext_resource type="Resource" path="res://fish/species/redhorse_golden/redhorse_golden.tres" id="13_redhorse_golden"]
+[ext_resource type="Resource" path="res://fish/species/quillback/quillback.tres" id="14_quillback"]
+[ext_resource type="Resource" path="res://fish/species/buffalo_smallmouth/buffalo_smallmouth.tres" id="15_buffalo_smallmouth"]
+[ext_resource type="Resource" path="res://fish/species/sucker_white/sucker_white.tres" id="16_sucker_white"]
+[ext_resource type="Resource" path="res://fish/species/catfish_blue/catfish_blue.tres" id="17_catfish_blue"]
+[ext_resource type="Resource" path="res://fish/species/catfish_flathead/catfish_flathead.tres" id="18_catfish_flathead"]
+[ext_resource type="Resource" path="res://fish/species/madtom_tadpole/madtom_tadpole.tres" id="19_madtom_tadpole"]
+[ext_resource type="Resource" path="res://fish/species/barbel_common/barbel_common.tres" id="20_barbel_common"]
+[ext_resource type="Resource" path="res://fish/species/chub_european/chub_european.tres" id="21_chub_european"]
+[ext_resource type="Resource" path="res://fish/species/fallfish/fallfish.tres" id="22_fallfish"]
+[ext_resource type="Resource" path="res://fish/species/ide/ide.tres" id="23_ide"]
+[ext_resource type="Resource" path="res://fish/species/gar_alligator/gar_alligator.tres" id="24_gar_alligator"]
+[ext_resource type="Resource" path="res://fish/species/arapaima/arapaima.tres" id="25_arapaima"]
+[ext_resource type="Resource" path="res://fish/species/tigerfish_goliath/tigerfish_goliath.tres" id="26_tigerfish_goliath"]
+[ext_resource type="Resource" path="res://fish/species/shiner_common/shiner_common.tres" id="27_shiner_common"]
+[ext_resource type="Resource" path="res://fish/species/chub_creek/chub_creek.tres" id="28_chub_creek"]
+[ext_resource type="Resource" path="res://fish/species/chub_flame/chub_flame.tres" id="29_chub_flame"]
+[ext_resource type="Resource" path="res://fish/species/electric_eel/electric_eel.tres" id="30_electric_eel"]
+[ext_resource type="Resource" path="res://fish/species/freshwater_drum/freshwater_drum.tres" id="31_freshwater_drum"]
+[ext_resource type="Resource" path="res://fish/species/goldeye/goldeye.tres" id="32_goldeye"]
+[ext_resource type="Resource" path="res://fish/species/mooneye/mooneye.tres" id="33_mooneye"]
+[ext_resource type="Resource" path="res://fish/species/sauger/sauger.tres" id="34_sauger"]
+[ext_resource type="Resource" path="res://fish/species/eel_american/eel_american.tres" id="35_eel_american"]
+[ext_resource type="Resource" path="res://fish/species/eel_european/eel_european.tres" id="36_eel_european"]
+[ext_resource type="Resource" path="res://fish/species/smelt_eulachon/smelt_eulachon.tres" id="37_smelt_eulachon"]
+[ext_resource type="Resource" path="res://fish/species/sturgeon_white/sturgeon_white.tres" id="38_sturgeon_white"]
+[ext_resource type="Resource" path="res://fish/species/trout_steelhead/trout_steelhead.tres" id="39_trout_steelhead"]
+[ext_resource type="Resource" path="res://fish/species/stingray_ocellate_river/stingray_ocellate_river.tres" id="40_stingray_ocellate_river"]
+[ext_resource type="Resource" path="res://fish/species/white_perch/white_perch.tres" id="41_white_perch"]
+[ext_resource type="Resource" path="res://fish/species/barramundi/barramundi.tres" id="42_barramundi"]
+[ext_resource type="Resource" path="res://fish/species/knifefish_clown/knifefish_clown.tres" id="43_knifefish_clown"]
+[ext_resource type="Resource" path="res://fish/species/loach_clown/loach_clown.tres" id="44_loach_clown"]
+[ext_resource type="Resource" path="res://fish/species/discus/discus.tres" id="45_discus"]
+[ext_resource type="Resource" path="res://fish/species/angelfish_freshwater/angelfish_freshwater.tres" id="46_angelfish_freshwater"]
+[ext_resource type="Resource" path="res://fish/species/oscar/oscar.tres" id="47_oscar"]
+[ext_resource type="Resource" path="res://fish/species/tambaqui/tambaqui.tres" id="48_tambaqui"]
+[ext_resource type="Resource" path="res://fish/species/snakehead_giant/snakehead_giant.tres" id="49_snakehead_giant"]
+[ext_resource type="Resource" path="res://fish/species/dorado_golden/dorado_golden.tres" id="50_dorado_golden"]
+[ext_resource type="Resource" path="res://fish/species/mahseer_golden/mahseer_golden.tres" id="51_mahseer_golden"]
+[ext_resource type="Resource" path="res://fish/species/peacock_bass/peacock_bass.tres" id="52_peacock_bass"]
+[ext_resource type="Resource" path="res://fish/species/piranha_red_bellied/piranha_red_bellied.tres" id="53_piranha_red_bellied"]
+[ext_resource type="Resource" path="res://fish/species/arowana_silver/arowana_silver.tres" id="54_arowana_silver"]
+
+[resource]
+script = ExtResource("1_pool")
+candidates = [ExtResource("2_gar_longnose"), ExtResource("3_paddlefish"), ExtResource("4_sturgeon_shovelnose"), ExtResource("5_foureyes_largescale"), ExtResource("6_grayling_arctic"), ExtResource("7_trout_brown"), ExtResource("8_trout_cutthroat"), ExtResource("9_trout_rainbow"), ExtResource("10_hellbender"), ExtResource("11_smallmouth_bass"), ExtResource("12_buffalo_bigmouth"), ExtResource("13_redhorse_golden"), ExtResource("14_quillback"), ExtResource("15_buffalo_smallmouth"), ExtResource("16_sucker_white"), ExtResource("17_catfish_blue"), ExtResource("18_catfish_flathead"), ExtResource("19_madtom_tadpole"), ExtResource("20_barbel_common"), ExtResource("21_chub_european"), ExtResource("22_fallfish"), ExtResource("23_ide"), ExtResource("24_gar_alligator"), ExtResource("25_arapaima"), ExtResource("26_tigerfish_goliath"), ExtResource("27_shiner_common"), ExtResource("28_chub_creek"), ExtResource("29_chub_flame"), ExtResource("30_electric_eel"), ExtResource("31_freshwater_drum"), ExtResource("32_goldeye"), ExtResource("33_mooneye"), ExtResource("34_sauger"), ExtResource("35_eel_american"), ExtResource("36_eel_european"), ExtResource("37_smelt_eulachon"), ExtResource("38_sturgeon_white"), ExtResource("39_trout_steelhead"), ExtResource("40_stingray_ocellate_river"), ExtResource("41_white_perch"), ExtResource("42_barramundi"), ExtResource("43_knifefish_clown"), ExtResource("44_loach_clown"), ExtResource("45_discus"), ExtResource("46_angelfish_freshwater"), ExtResource("47_oscar"), ExtResource("48_tambaqui"), ExtResource("49_snakehead_giant"), ExtResource("50_dorado_golden"), ExtResource("51_mahseer_golden"), ExtResource("52_peacock_bass"), ExtResource("53_piranha_red_bellied"), ExtResource("54_arowana_silver")]
diff --git a/fish/pools/starter_ocean_pool.tres b/fish/pools/starter_ocean_pool.tres
index 1e47a2b..274b58d 100644
--- a/fish/pools/starter_ocean_pool.tres
+++ b/fish/pools/starter_ocean_pool.tres
@@ -1,41 +1,211 @@
-[gd_resource type="Resource" load_steps=36 format=3]
+[gd_resource type="Resource" load_steps=206 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/mudskipper_atlantic/mudskipper_atlantic.tres" id="2_mudskipper_atlantic"]
+[ext_resource type="Resource" path="res://fish/species/mullet_red/mullet_red.tres" id="3_mullet_red"]
+[ext_resource type="Resource" path="res://fish/species/plaice_european/plaice_european.tres" id="4_plaice_european"]
+[ext_resource type="Resource" path="res://fish/species/flounder_summer/flounder_summer.tres" id="5_flounder_summer"]
+[ext_resource type="Resource" path="res://fish/species/flounder_winter/flounder_winter.tres" id="6_flounder_winter"]
+[ext_resource type="Resource" path="res://fish/species/dugong/dugong.tres" id="7_dugong"]
+[ext_resource type="Resource" path="res://fish/species/seal_harbor/seal_harbor.tres" id="8_seal_harbor"]
+[ext_resource type="Resource" path="res://fish/species/sea_otter/sea_otter.tres" id="9_sea_otter"]
+[ext_resource type="Resource" path="res://fish/species/walrus/walrus.tres" id="10_walrus"]
+[ext_resource type="Resource" path="res://fish/species/manatee_west_indian/manatee_west_indian.tres" id="11_manatee_west_indian"]
+[ext_resource type="Resource" path="res://fish/species/pipefish_bay/pipefish_bay.tres" id="12_pipefish_bay"]
+[ext_resource type="Resource" path="res://fish/species/seahorse_lined/seahorse_lined.tres" id="13_seahorse_lined"]
+[ext_resource type="Resource" path="res://fish/species/monkfish/monkfish.tres" id="14_monkfish"]
+[ext_resource type="Resource" path="res://fish/species/tripletail/tripletail.tres" id="15_tripletail"]
+[ext_resource type="Resource" path="res://fish/species/mackerel_atlantic/mackerel_atlantic.tres" id="16_mackerel_atlantic"]
+[ext_resource type="Resource" path="res://fish/species/bluefish/bluefish.tres" id="17_bluefish"]
+[ext_resource type="Resource" path="res://fish/species/mackerel_cero/mackerel_cero.tres" id="18_mackerel_cero"]
+[ext_resource type="Resource" path="res://fish/species/mackerel_chub/mackerel_chub.tres" id="19_mackerel_chub"]
+[ext_resource type="Resource" path="res://fish/species/jack_crevalle/jack_crevalle.tres" id="20_jack_crevalle"]
[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/queenfish_talang/queenfish_talang.tres" id="23_queenfish_talang"]
+[ext_resource type="Resource" path="res://fish/species/sand_lance_american/sand_lance_american.tres" id="24_sand_lance_american"]
+[ext_resource type="Resource" path="res://fish/species/menhaden_atlantic/menhaden_atlantic.tres" id="25_menhaden_atlantic"]
[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"]
+[ext_resource type="Resource" path="res://fish/species/anchovy_european/anchovy_european.tres" id="28_anchovy_european"]
+[ext_resource type="Resource" path="res://fish/species/sardine_european/sardine_european.tres" id="29_sardine_european"]
+[ext_resource type="Resource" path="res://fish/species/pomfret_golden/pomfret_golden.tres" id="30_pomfret_golden"]
+[ext_resource type="Resource" path="res://fish/species/anchovy_northern/anchovy_northern.tres" id="31_anchovy_northern"]
+[ext_resource type="Resource" path="res://fish/species/sardine_pacific/sardine_pacific.tres" id="32_sardine_pacific"]
+[ext_resource type="Resource" path="res://fish/species/pomfret_white/pomfret_white.tres" id="33_pomfret_white"]
+[ext_resource type="Resource" path="res://fish/species/shark_blacktip/shark_blacktip.tres" id="34_shark_blacktip"]
+[ext_resource type="Resource" path="res://fish/species/shark_great_hammerhead/shark_great_hammerhead.tres" id="35_shark_great_hammerhead"]
+[ext_resource type="Resource" path="res://fish/species/shark_great_white/shark_great_white.tres" id="36_shark_great_white"]
+[ext_resource type="Resource" path="res://fish/species/shark_tiger/shark_tiger.tres" id="37_shark_tiger"]
+[ext_resource type="Resource" path="res://fish/species/shark_whale/shark_whale.tres" id="38_shark_whale"]
+[ext_resource type="Resource" path="res://fish/species/wolffish_atlantic/wolffish_atlantic.tres" id="39_wolffish_atlantic"]
+[ext_resource type="Resource" path="res://fish/species/cusk/cusk.tres" id="40_cusk"]
+[ext_resource type="Resource" path="res://fish/species/wolffish_spotted/wolffish_spotted.tres" id="41_wolffish_spotted"]
+[ext_resource type="Resource" path="res://fish/species/herring_atlantic/herring_atlantic.tres" id="42_herring_atlantic"]
+[ext_resource type="Resource" path="res://fish/species/capelin/capelin.tres" id="43_capelin"]
+[ext_resource type="Resource" path="res://fish/species/sprat_european/sprat_european.tres" id="44_sprat_european"]
+[ext_resource type="Resource" path="res://fish/species/herring_pacific/herring_pacific.tres" id="45_herring_pacific"]
+[ext_resource type="Resource" path="res://fish/species/pollock_alaska/pollock_alaska.tres" id="46_pollock_alaska"]
+[ext_resource type="Resource" path="res://fish/species/cod_atlantic/cod_atlantic.tres" id="47_cod_atlantic"]
+[ext_resource type="Resource" path="res://fish/species/pollock_atlantic/pollock_atlantic.tres" id="48_pollock_atlantic"]
+[ext_resource type="Resource" path="res://fish/species/sole_dover/sole_dover.tres" id="49_sole_dover"]
+[ext_resource type="Resource" path="res://fish/species/haddock/haddock.tres" id="50_haddock"]
+[ext_resource type="Resource" path="res://fish/species/cod_pacific/cod_pacific.tres" id="51_cod_pacific"]
+[ext_resource type="Resource" path="res://fish/species/hake_pacific/hake_pacific.tres" id="52_hake_pacific"]
+[ext_resource type="Resource" path="res://fish/species/hake_silver/hake_silver.tres" id="53_hake_silver"]
+[ext_resource type="Resource" path="res://fish/species/nautilus_chambered/nautilus_chambered.tres" id="54_nautilus_chambered"]
+[ext_resource type="Resource" path="res://fish/species/cuttlefish_common/cuttlefish_common.tres" id="55_cuttlefish_common"]
+[ext_resource type="Resource" path="res://fish/species/cuttlefish_flamboyant/cuttlefish_flamboyant.tres" id="56_cuttlefish_flamboyant"]
+[ext_resource type="Resource" path="res://fish/species/tilefish_blueline/tilefish_blueline.tres" id="57_tilefish_blueline"]
+[ext_resource type="Resource" path="res://fish/species/tilefish_golden/tilefish_golden.tres" id="58_tilefish_golden"]
+[ext_resource type="Resource" path="res://fish/species/ocean_perch_pacific/ocean_perch_pacific.tres" id="59_ocean_perch_pacific"]
+[ext_resource type="Resource" path="res://fish/species/sea_bass_chilean/sea_bass_chilean.tres" id="60_sea_bass_chilean"]
+[ext_resource type="Resource" path="res://fish/species/sablefish/sablefish.tres" id="61_sablefish"]
+[ext_resource type="Resource" path="res://fish/species/wreckfish/wreckfish.tres" id="62_wreckfish"]
+[ext_resource type="Resource" path="res://fish/species/oarfish_giant/oarfish_giant.tres" id="63_oarfish_giant"]
+[ext_resource type="Resource" path="res://fish/species/coelacanth_west_indian/coelacanth_west_indian.tres" id="64_coelacanth_west_indian"]
+[ext_resource type="Resource" path="res://fish/species/anglerfish_black_seadevil/anglerfish_black_seadevil.tres" id="65_anglerfish_black_seadevil"]
+[ext_resource type="Resource" path="res://fish/species/fangtooth_common/fangtooth_common.tres" id="66_fangtooth_common"]
+[ext_resource type="Resource" path="res://fish/species/gulper_eel/gulper_eel.tres" id="67_gulper_eel"]
+[ext_resource type="Resource" path="res://fish/species/barreleye_pacific/barreleye_pacific.tres" id="68_barreleye_pacific"]
+[ext_resource type="Resource" path="res://fish/species/blobfish_smooth_head/blobfish_smooth_head.tres" id="69_blobfish_smooth_head"]
+[ext_resource type="Resource" path="res://fish/species/lanternfish_spotted/lanternfish_spotted.tres" id="70_lanternfish_spotted"]
+[ext_resource type="Resource" path="res://fish/species/sea_star_crown_of_thorns/sea_star_crown_of_thorns.tres" id="71_sea_star_crown_of_thorns"]
+[ext_resource type="Resource" path="res://fish/species/sea_cucumber_giant_california/sea_cucumber_giant_california.tres" id="72_sea_cucumber_giant_california"]
+[ext_resource type="Resource" path="res://fish/species/urchin_purple/urchin_purple.tres" id="73_urchin_purple"]
+[ext_resource type="Resource" path="res://fish/species/sand_dollar/sand_dollar.tres" id="74_sand_dollar"]
+[ext_resource type="Resource" path="res://fish/species/sea_star_sunflower/sea_star_sunflower.tres" id="75_sea_star_sunflower"]
+[ext_resource type="Resource" path="res://fish/species/jelly_crystal/jelly_crystal.tres" id="76_jelly_crystal"]
+[ext_resource type="Resource" path="res://fish/species/jelly_lions_mane/jelly_lions_mane.tres" id="77_jelly_lions_mane"]
+[ext_resource type="Resource" path="res://fish/species/jelly_moon/jelly_moon.tres" id="78_jelly_moon"]
+[ext_resource type="Resource" path="res://fish/species/jelly_pacific_sea_nettle/jelly_pacific_sea_nettle.tres" id="79_jelly_pacific_sea_nettle"]
+[ext_resource type="Resource" path="res://fish/species/jelly_sea_wasp/jelly_sea_wasp.tres" id="80_jelly_sea_wasp"]
+[ext_resource type="Resource" path="res://fish/species/pompano_african/pompano_african.tres" id="81_pompano_african"]
+[ext_resource type="Resource" path="res://fish/species/cobia/cobia.tres" id="82_cobia"]
+[ext_resource type="Resource" path="res://fish/species/trevally_giant/trevally_giant.tres" id="83_trevally_giant"]
+[ext_resource type="Resource" path="res://fish/species/amberjack_greater/amberjack_greater.tres" id="84_amberjack_greater"]
+[ext_resource type="Resource" path="res://fish/species/amberjack_lesser/amberjack_lesser.tres" id="85_amberjack_lesser"]
+[ext_resource type="Resource" path="res://fish/species/halibut_atlantic/halibut_atlantic.tres" id="86_halibut_atlantic"]
+[ext_resource type="Resource" path="res://fish/species/halibut_pacific/halibut_pacific.tres" id="87_halibut_pacific"]
+[ext_resource type="Resource" path="res://fish/species/turbot/turbot.tres" id="88_turbot"]
+[ext_resource type="Resource" path="res://fish/species/lobster_american/lobster_american.tres" id="89_lobster_american"]
+[ext_resource type="Resource" path="res://fish/species/krill_antarctic/krill_antarctic.tres" id="90_krill_antarctic"]
+[ext_resource type="Resource" path="res://fish/species/lobster_caribbean_spiny/lobster_caribbean_spiny.tres" id="91_lobster_caribbean_spiny"]
+[ext_resource type="Resource" path="res://fish/species/shrimp_peacock_mantis/shrimp_peacock_mantis.tres" id="92_shrimp_peacock_mantis"]
+[ext_resource type="Resource" path="res://fish/species/shrimp_skunk_cleaner/shrimp_skunk_cleaner.tres" id="93_shrimp_skunk_cleaner"]
+[ext_resource type="Resource" path="res://fish/species/crab_japanese_spider/crab_japanese_spider.tres" id="94_crab_japanese_spider"]
+[ext_resource type="Resource" path="res://fish/species/crab_red_king/crab_red_king.tres" id="95_crab_red_king"]
+[ext_resource type="Resource" path="res://fish/species/salmon_atlantic/salmon_atlantic.tres" id="96_salmon_atlantic"]
+[ext_resource type="Resource" path="res://fish/species/salmon_chum/salmon_chum.tres" id="97_salmon_chum"]
+[ext_resource type="Resource" path="res://fish/species/salmon_coho/salmon_coho.tres" id="98_salmon_coho"]
+[ext_resource type="Resource" path="res://fish/species/salmon_pink/salmon_pink.tres" id="99_salmon_pink"]
+[ext_resource type="Resource" path="res://fish/species/salmon_sockeye/salmon_sockeye.tres" id="100_salmon_sockeye"]
+[ext_resource type="Resource" path="res://fish/species/marlin_black/marlin_black.tres" id="101_marlin_black"]
+[ext_resource type="Resource" path="res://fish/species/marlin_blue/marlin_blue.tres" id="102_marlin_blue"]
+[ext_resource type="Resource" path="res://fish/species/sailfish/sailfish.tres" id="103_sailfish"]
+[ext_resource type="Resource" path="res://fish/species/swordfish/swordfish.tres" id="104_swordfish"]
+[ext_resource type="Resource" path="res://fish/species/marlin_white/marlin_white.tres" id="105_marlin_white"]
+[ext_resource type="Resource" path="res://fish/species/mahi_mahi/mahi_mahi.tres" id="106_mahi_mahi"]
+[ext_resource type="Resource" path="res://fish/species/opah/opah.tres" id="107_opah"]
+[ext_resource type="Resource" path="res://fish/species/wahoo/wahoo.tres" id="108_wahoo"]
+[ext_resource type="Resource" path="res://fish/species/tuna_albacore/tuna_albacore.tres" id="109_tuna_albacore"]
+[ext_resource type="Resource" path="res://fish/species/tuna_bigeye/tuna_bigeye.tres" id="110_tuna_bigeye"]
+[ext_resource type="Resource" path="res://fish/species/tuna_bluefin/tuna_bluefin.tres" id="111_tuna_bluefin"]
+[ext_resource type="Resource" path="res://fish/species/tuna_skipjack/tuna_skipjack.tres" id="112_tuna_skipjack"]
+[ext_resource type="Resource" path="res://fish/species/tuna_yellowfin/tuna_yellowfin.tres" id="113_tuna_yellowfin"]
+[ext_resource type="Resource" path="res://fish/species/octopus_common/octopus_common.tres" id="114_octopus_common"]
+[ext_resource type="Resource" path="res://fish/species/octopus_day/octopus_day.tres" id="115_octopus_day"]
+[ext_resource type="Resource" path="res://fish/species/octopus_giant_pacific/octopus_giant_pacific.tres" id="116_octopus_giant_pacific"]
+[ext_resource type="Resource" path="res://fish/species/octopus_greater_blue_ringed/octopus_greater_blue_ringed.tres" id="117_octopus_greater_blue_ringed"]
+[ext_resource type="Resource" path="res://fish/species/sunfish/sunfish.tres" id="118_sunfish"]
+[ext_resource type="Resource" path="res://fish/species/skate_barndoor/skate_barndoor.tres" id="119_skate_barndoor"]
+[ext_resource type="Resource" path="res://fish/species/manta_ray_giant/manta_ray_giant.tres" id="120_manta_ray_giant"]
+[ext_resource type="Resource" path="res://fish/species/sawfish_largetooth/sawfish_largetooth.tres" id="121_sawfish_largetooth"]
+[ext_resource type="Resource" path="res://fish/species/stingray_southern/stingray_southern.tres" id="122_stingray_southern"]
+[ext_resource type="Resource" path="res://fish/species/lionfish_red/lionfish_red.tres" id="123_lionfish_red"]
+[ext_resource type="Resource" path="res://fish/species/scorpionfish_red/scorpionfish_red.tres" id="124_scorpionfish_red"]
+[ext_resource type="Resource" path="res://fish/species/moray_green/moray_green.tres" id="125_moray_green"]
+[ext_resource type="Resource" path="res://fish/species/flounder_peacock/flounder_peacock.tres" id="126_flounder_peacock"]
+[ext_resource type="Resource" path="res://fish/species/surgeonfish_blue/surgeonfish_blue.tres" id="127_surgeonfish_blue"]
+[ext_resource type="Resource" path="res://fish/species/parrotfish_rainbow/parrotfish_rainbow.tres" id="128_parrotfish_rainbow"]
+[ext_resource type="Resource" path="res://fish/species/parrotfish_stoplight/parrotfish_stoplight.tres" id="129_parrotfish_stoplight"]
+[ext_resource type="Resource" path="res://fish/species/tang_yellow/tang_yellow.tres" id="130_tang_yellow"]
+[ext_resource type="Resource" path="res://fish/species/trumpetfish_atlantic/trumpetfish_atlantic.tres" id="131_trumpetfish_atlantic"]
+[ext_resource type="Resource" path="res://fish/species/triggerfish_gray/triggerfish_gray.tres" id="132_triggerfish_gray"]
+[ext_resource type="Resource" path="res://fish/species/pufferfish_guineafowl/pufferfish_guineafowl.tres" id="133_pufferfish_guineafowl"]
+[ext_resource type="Resource" path="res://fish/species/cowfish_longhorn/cowfish_longhorn.tres" id="134_cowfish_longhorn"]
+[ext_resource type="Resource" path="res://fish/species/triggerfish_queen/triggerfish_queen.tres" id="135_triggerfish_queen"]
+[ext_resource type="Resource" path="res://fish/species/cornetfish_red/cornetfish_red.tres" id="136_cornetfish_red"]
+[ext_resource type="Resource" path="res://fish/species/porcupinefish_spotted/porcupinefish_spotted.tres" id="137_porcupinefish_spotted"]
+[ext_resource type="Resource" path="res://fish/species/boxfish_yellow/boxfish_yellow.tres" id="138_boxfish_yellow"]
+[ext_resource type="Resource" path="res://fish/species/butterflyfish_copperband/butterflyfish_copperband.tres" id="139_butterflyfish_copperband"]
+[ext_resource type="Resource" path="res://fish/species/clownfish_ocellaris/clownfish_ocellaris.tres" id="140_clownfish_ocellaris"]
+[ext_resource type="Resource" path="res://fish/species/angelfish_queen/angelfish_queen.tres" id="141_angelfish_queen"]
+[ext_resource type="Resource" path="res://fish/species/grouper_atlantic_goliath/grouper_atlantic_goliath.tres" id="142_grouper_atlantic_goliath"]
+[ext_resource type="Resource" path="res://fish/species/trevally_bluefin/trevally_bluefin.tres" id="143_trevally_bluefin"]
+[ext_resource type="Resource" path="res://fish/species/grouper_giant/grouper_giant.tres" id="144_grouper_giant"]
+[ext_resource type="Resource" path="res://fish/species/barracuda_great/barracuda_great.tres" id="145_barracuda_great"]
+[ext_resource type="Resource" path="res://fish/species/grouper_gulf/grouper_gulf.tres" id="146_grouper_gulf"]
+[ext_resource type="Resource" path="res://fish/species/grouper_nassau/grouper_nassau.tres" id="147_grouper_nassau"]
+[ext_resource type="Resource" path="res://fish/species/grouper_red/grouper_red.tres" id="148_grouper_red"]
+[ext_resource type="Resource" path="res://fish/species/chromis_blue_green/chromis_blue_green.tres" id="149_chromis_blue_green"]
+[ext_resource type="Resource" path="res://fish/species/grunt_bluestriped/grunt_bluestriped.tres" id="150_grunt_bluestriped"]
+[ext_resource type="Resource" path="res://fish/species/grunt_french/grunt_french.tres" id="151_grunt_french"]
+[ext_resource type="Resource" path="res://fish/species/damselfish_sergeant_major/damselfish_sergeant_major.tres" id="152_damselfish_sergeant_major"]
+[ext_resource type="Resource" path="res://fish/species/snapper_lane/snapper_lane.tres" id="153_snapper_lane"]
+[ext_resource type="Resource" path="res://fish/species/snapper_mangrove/snapper_mangrove.tres" id="154_snapper_mangrove"]
+[ext_resource type="Resource" path="res://fish/species/snapper_mutton/snapper_mutton.tres" id="155_snapper_mutton"]
+[ext_resource type="Resource" path="res://fish/species/snapper_red/snapper_red.tres" id="156_snapper_red"]
+[ext_resource type="Resource" path="res://fish/species/wrasse_ballan/wrasse_ballan.tres" id="157_wrasse_ballan"]
+[ext_resource type="Resource" path="res://fish/species/wrasse_cleaner/wrasse_cleaner.tres" id="158_wrasse_cleaner"]
+[ext_resource type="Resource" path="res://fish/species/hogfish/hogfish.tres" id="159_hogfish"]
+[ext_resource type="Resource" path="res://fish/species/turtle_green/turtle_green.tres" id="160_turtle_green"]
+[ext_resource type="Resource" path="res://fish/species/turtle_hawksbill/turtle_hawksbill.tres" id="161_turtle_hawksbill"]
+[ext_resource type="Resource" path="res://fish/species/turtle_leatherback/turtle_leatherback.tres" id="162_turtle_leatherback"]
+[ext_resource type="Resource" path="res://fish/species/turtle_loggerhead/turtle_loggerhead.tres" id="163_turtle_loggerhead"]
+[ext_resource type="Resource" path="res://fish/species/seadragon_leafy/seadragon_leafy.tres" id="164_seadragon_leafy"]
+[ext_resource type="Resource" path="res://fish/species/seadragon_weedy/seadragon_weedy.tres" id="165_seadragon_weedy"]
+[ext_resource type="Resource" path="res://fish/species/oyster_black_lip_pearl/oyster_black_lip_pearl.tres" id="166_oyster_black_lip_pearl"]
+[ext_resource type="Resource" path="res://fish/species/clam_giant/clam_giant.tres" id="167_clam_giant"]
+[ext_resource type="Resource" path="res://fish/species/clam_geoduck/clam_geoduck.tres" id="168_clam_geoduck"]
+[ext_resource type="Resource" path="res://fish/species/conch_queen/conch_queen.tres" id="169_conch_queen"]
+[ext_resource type="Resource" path="res://fish/species/abalone_red/abalone_red.tres" id="170_abalone_red"]
+[ext_resource type="Resource" path="res://fish/species/squid_bigfin_reef/squid_bigfin_reef.tres" id="171_squid_bigfin_reef"]
+[ext_resource type="Resource" path="res://fish/species/squid_colossal/squid_colossal.tres" id="172_squid_colossal"]
+[ext_resource type="Resource" path="res://fish/species/squid_giant/squid_giant.tres" id="173_squid_giant"]
+[ext_resource type="Resource" path="res://fish/species/squid_humboldt/squid_humboldt.tres" id="174_squid_humboldt"]
+[ext_resource type="Resource" path="res://fish/species/halfbeak_ballyhoo/halfbeak_ballyhoo.tres" id="175_halfbeak_ballyhoo"]
+[ext_resource type="Resource" path="res://fish/species/needlefish_hound/needlefish_hound.tres" id="176_needlefish_hound"]
+[ext_resource type="Resource" path="res://fish/species/flyingfish_tropical/flyingfish_tropical.tres" id="177_flyingfish_tropical"]
+[ext_resource type="Resource" path="res://fish/species/bass/bass.tres" id="178_bass"]
+[ext_resource type="Resource" path="res://fish/species/croaker_atlantic/croaker_atlantic.tres" id="179_croaker_atlantic"]
+[ext_resource type="Resource" path="res://fish/species/black_drum/black_drum.tres" id="180_black_drum"]
+[ext_resource type="Resource" path="res://fish/species/red_drum/red_drum.tres" id="181_red_drum"]
+[ext_resource type="Resource" path="res://fish/species/mullet_striped/mullet_striped.tres" id="182_mullet_striped"]
+[ext_resource type="Resource" path="res://fish/species/weakfish/weakfish.tres" id="183_weakfish"]
+[ext_resource type="Resource" path="res://fish/species/croaker_yellowfin/croaker_yellowfin.tres" id="184_croaker_yellowfin"]
+[ext_resource type="Resource" path="res://fish/species/rockfish_black/rockfish_black.tres" id="185_rockfish_black"]
+[ext_resource type="Resource" path="res://fish/species/sea_bass_black/sea_bass_black.tres" id="186_sea_bass_black"]
+[ext_resource type="Resource" path="res://fish/species/rockfish_canary/rockfish_canary.tres" id="187_rockfish_canary"]
+[ext_resource type="Resource" path="res://fish/species/porgy_scup/porgy_scup.tres" id="188_porgy_scup"]
+[ext_resource type="Resource" path="res://fish/species/sheepshead/sheepshead.tres" id="189_sheepshead"]
+[ext_resource type="Resource" path="res://fish/species/tautog/tautog.tres" id="190_tautog"]
+[ext_resource type="Resource" path="res://fish/species/rockfish_yelloweye/rockfish_yelloweye.tres" id="191_rockfish_yelloweye"]
+[ext_resource type="Resource" path="res://fish/species/lingcod/lingcod.tres" id="192_lingcod"]
+[ext_resource type="Resource" path="res://fish/species/milkfish/milkfish.tres" id="193_milkfish"]
+[ext_resource type="Resource" path="res://fish/species/roosterfish/roosterfish.tres" id="194_roosterfish"]
+[ext_resource type="Resource" path="res://fish/species/tarpon_atlantic/tarpon_atlantic.tres" id="195_tarpon_atlantic"]
+[ext_resource type="Resource" path="res://fish/species/snook_common/snook_common.tres" id="196_snook_common"]
+[ext_resource type="Resource" path="res://fish/species/bonefish/bonefish.tres" id="197_bonefish"]
+[ext_resource type="Resource" path="res://fish/species/pompano_florida/pompano_florida.tres" id="198_pompano_florida"]
+[ext_resource type="Resource" path="res://fish/species/ladyfish/ladyfish.tres" id="199_ladyfish"]
+[ext_resource type="Resource" path="res://fish/species/permit/permit.tres" id="200_permit"]
+[ext_resource type="Resource" path="res://fish/species/whale_blue/whale_blue.tres" id="201_whale_blue"]
+[ext_resource type="Resource" path="res://fish/species/dolphin_bottlenose/dolphin_bottlenose.tres" id="202_dolphin_bottlenose"]
+[ext_resource type="Resource" path="res://fish/species/whale_humpback/whale_humpback.tres" id="203_whale_humpback"]
+[ext_resource type="Resource" path="res://fish/species/whale_killer/whale_killer.tres" id="204_whale_killer"]
+[ext_resource type="Resource" path="res://fish/species/whale_sperm/whale_sperm.tres" id="205_whale_sperm"]
[resource]
script = ExtResource("1_pool")
-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")]
+candidates = [ExtResource("2_mudskipper_atlantic"), ExtResource("3_mullet_red"), ExtResource("4_plaice_european"), ExtResource("5_flounder_summer"), ExtResource("6_flounder_winter"), ExtResource("7_dugong"), ExtResource("8_seal_harbor"), ExtResource("9_sea_otter"), ExtResource("10_walrus"), ExtResource("11_manatee_west_indian"), ExtResource("12_pipefish_bay"), ExtResource("13_seahorse_lined"), ExtResource("14_monkfish"), ExtResource("15_tripletail"), ExtResource("16_mackerel_atlantic"), ExtResource("17_bluefish"), ExtResource("18_mackerel_cero"), ExtResource("19_mackerel_chub"), ExtResource("20_jack_crevalle"), ExtResource("21_mackerel_king"), ExtResource("22_mackerel_spanish"), ExtResource("23_queenfish_talang"), ExtResource("24_sand_lance_american"), ExtResource("25_menhaden_atlantic"), ExtResource("26_pomfret_black"), ExtResource("27_pomfret_chinese"), ExtResource("28_anchovy_european"), ExtResource("29_sardine_european"), ExtResource("30_pomfret_golden"), ExtResource("31_anchovy_northern"), ExtResource("32_sardine_pacific"), ExtResource("33_pomfret_white"), ExtResource("34_shark_blacktip"), ExtResource("35_shark_great_hammerhead"), ExtResource("36_shark_great_white"), ExtResource("37_shark_tiger"), ExtResource("38_shark_whale"), ExtResource("39_wolffish_atlantic"), ExtResource("40_cusk"), ExtResource("41_wolffish_spotted"), ExtResource("42_herring_atlantic"), ExtResource("43_capelin"), ExtResource("44_sprat_european"), ExtResource("45_herring_pacific"), ExtResource("46_pollock_alaska"), ExtResource("47_cod_atlantic"), ExtResource("48_pollock_atlantic"), ExtResource("49_sole_dover"), ExtResource("50_haddock"), ExtResource("51_cod_pacific"), ExtResource("52_hake_pacific"), ExtResource("53_hake_silver"), ExtResource("54_nautilus_chambered"), ExtResource("55_cuttlefish_common"), ExtResource("56_cuttlefish_flamboyant"), ExtResource("57_tilefish_blueline"), ExtResource("58_tilefish_golden"), ExtResource("59_ocean_perch_pacific"), ExtResource("60_sea_bass_chilean"), ExtResource("61_sablefish"), ExtResource("62_wreckfish"), ExtResource("63_oarfish_giant"), ExtResource("64_coelacanth_west_indian"), ExtResource("65_anglerfish_black_seadevil"), ExtResource("66_fangtooth_common"), ExtResource("67_gulper_eel"), ExtResource("68_barreleye_pacific"), ExtResource("69_blobfish_smooth_head"), ExtResource("70_lanternfish_spotted"), ExtResource("71_sea_star_crown_of_thorns"), ExtResource("72_sea_cucumber_giant_california"), ExtResource("73_urchin_purple"), ExtResource("74_sand_dollar"), ExtResource("75_sea_star_sunflower"), ExtResource("76_jelly_crystal"), ExtResource("77_jelly_lions_mane"), ExtResource("78_jelly_moon"), ExtResource("79_jelly_pacific_sea_nettle"), ExtResource("80_jelly_sea_wasp"), ExtResource("81_pompano_african"), ExtResource("82_cobia"), ExtResource("83_trevally_giant"), ExtResource("84_amberjack_greater"), ExtResource("85_amberjack_lesser"), ExtResource("86_halibut_atlantic"), ExtResource("87_halibut_pacific"), ExtResource("88_turbot"), ExtResource("89_lobster_american"), ExtResource("90_krill_antarctic"), ExtResource("91_lobster_caribbean_spiny"), ExtResource("92_shrimp_peacock_mantis"), ExtResource("93_shrimp_skunk_cleaner"), ExtResource("94_crab_japanese_spider"), ExtResource("95_crab_red_king"), ExtResource("96_salmon_atlantic"), ExtResource("97_salmon_chum"), ExtResource("98_salmon_coho"), ExtResource("99_salmon_pink"), ExtResource("100_salmon_sockeye"), ExtResource("101_marlin_black"), ExtResource("102_marlin_blue"), ExtResource("103_sailfish"), ExtResource("104_swordfish"), ExtResource("105_marlin_white"), ExtResource("106_mahi_mahi"), ExtResource("107_opah"), ExtResource("108_wahoo"), ExtResource("109_tuna_albacore"), ExtResource("110_tuna_bigeye"), ExtResource("111_tuna_bluefin"), ExtResource("112_tuna_skipjack"), ExtResource("113_tuna_yellowfin"), ExtResource("114_octopus_common"), ExtResource("115_octopus_day"), ExtResource("116_octopus_giant_pacific"), ExtResource("117_octopus_greater_blue_ringed"), ExtResource("118_sunfish"), ExtResource("119_skate_barndoor"), ExtResource("120_manta_ray_giant"), ExtResource("121_sawfish_largetooth"), ExtResource("122_stingray_southern"), ExtResource("123_lionfish_red"), ExtResource("124_scorpionfish_red"), ExtResource("125_moray_green"), ExtResource("126_flounder_peacock"), ExtResource("127_surgeonfish_blue"), ExtResource("128_parrotfish_rainbow"), ExtResource("129_parrotfish_stoplight"), ExtResource("130_tang_yellow"), ExtResource("131_trumpetfish_atlantic"), ExtResource("132_triggerfish_gray"), ExtResource("133_pufferfish_guineafowl"), ExtResource("134_cowfish_longhorn"), ExtResource("135_triggerfish_queen"), ExtResource("136_cornetfish_red"), ExtResource("137_porcupinefish_spotted"), ExtResource("138_boxfish_yellow"), ExtResource("139_butterflyfish_copperband"), ExtResource("140_clownfish_ocellaris"), ExtResource("141_angelfish_queen"), ExtResource("142_grouper_atlantic_goliath"), ExtResource("143_trevally_bluefin"), ExtResource("144_grouper_giant"), ExtResource("145_barracuda_great"), ExtResource("146_grouper_gulf"), ExtResource("147_grouper_nassau"), ExtResource("148_grouper_red"), ExtResource("149_chromis_blue_green"), ExtResource("150_grunt_bluestriped"), ExtResource("151_grunt_french"), ExtResource("152_damselfish_sergeant_major"), ExtResource("153_snapper_lane"), ExtResource("154_snapper_mangrove"), ExtResource("155_snapper_mutton"), ExtResource("156_snapper_red"), ExtResource("157_wrasse_ballan"), ExtResource("158_wrasse_cleaner"), ExtResource("159_hogfish"), ExtResource("160_turtle_green"), ExtResource("161_turtle_hawksbill"), ExtResource("162_turtle_leatherback"), ExtResource("163_turtle_loggerhead"), ExtResource("164_seadragon_leafy"), ExtResource("165_seadragon_weedy"), ExtResource("166_oyster_black_lip_pearl"), ExtResource("167_clam_giant"), ExtResource("168_clam_geoduck"), ExtResource("169_conch_queen"), ExtResource("170_abalone_red"), ExtResource("171_squid_bigfin_reef"), ExtResource("172_squid_colossal"), ExtResource("173_squid_giant"), ExtResource("174_squid_humboldt"), ExtResource("175_halfbeak_ballyhoo"), ExtResource("176_needlefish_hound"), ExtResource("177_flyingfish_tropical"), ExtResource("178_bass"), ExtResource("179_croaker_atlantic"), ExtResource("180_black_drum"), ExtResource("181_red_drum"), ExtResource("182_mullet_striped"), ExtResource("183_weakfish"), ExtResource("184_croaker_yellowfin"), ExtResource("185_rockfish_black"), ExtResource("186_sea_bass_black"), ExtResource("187_rockfish_canary"), ExtResource("188_porgy_scup"), ExtResource("189_sheepshead"), ExtResource("190_tautog"), ExtResource("191_rockfish_yelloweye"), ExtResource("192_lingcod"), ExtResource("193_milkfish"), ExtResource("194_roosterfish"), ExtResource("195_tarpon_atlantic"), ExtResource("196_snook_common"), ExtResource("197_bonefish"), ExtResource("198_pompano_florida"), ExtResource("199_ladyfish"), ExtResource("200_permit"), ExtResource("201_whale_blue"), ExtResource("202_dolphin_bottlenose"), ExtResource("203_whale_humpback"), ExtResource("204_whale_killer"), ExtResource("205_whale_sperm")]
diff --git a/fish/pools/starter_pond_pool.tres b/fish/pools/starter_pond_pool.tres
index de1a327..e9718d9 100644
--- a/fish/pools/starter_pond_pool.tres
+++ b/fish/pools/starter_pond_pool.tres
@@ -1,26 +1,113 @@
-[gd_resource type="Resource" load_steps=21 format=3]
+[gd_resource type="Resource" load_steps=108 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="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"]
+[ext_resource type="Resource" path="res://fish/species/bowfin/bowfin.tres" id="2_bowfin"]
+[ext_resource type="Resource" path="res://fish/species/gar_spotted/gar_spotted.tres" id="3_gar_spotted"]
+[ext_resource type="Resource" path="res://fish/species/lungfish_west_african/lungfish_west_african.tres" id="4_lungfish_west_african"]
+[ext_resource type="Resource" path="res://fish/species/guppy/guppy.tres" id="5_guppy"]
+[ext_resource type="Resource" path="res://fish/species/tetra_neon/tetra_neon.tres" id="6_tetra_neon"]
+[ext_resource type="Resource" path="res://fish/species/danio_zebra/danio_zebra.tres" id="7_danio_zebra"]
+[ext_resource type="Resource" path="res://fish/species/largemouth_bass/largemouth_bass.tres" id="8_largemouth_bass"]
+[ext_resource type="Resource" path="res://fish/species/bullhead_black/bullhead_black.tres" id="9_bullhead_black"]
+[ext_resource type="Resource" path="res://fish/species/bullhead_brown/bullhead_brown.tres" id="10_bullhead_brown"]
+[ext_resource type="Resource" path="res://fish/species/catfish_channel/catfish_channel.tres" id="11_catfish_channel"]
+[ext_resource type="Resource" path="res://fish/species/catfish_white/catfish_white.tres" id="12_catfish_white"]
+[ext_resource type="Resource" path="res://fish/species/bullhead_yellow/bullhead_yellow.tres" id="13_bullhead_yellow"]
+[ext_resource type="Resource" path="res://fish/species/carp/carp.tres" id="14_carp"]
+[ext_resource type="Resource" path="res://fish/species/roach_common/roach_common.tres" id="15_roach_common"]
+[ext_resource type="Resource" path="res://fish/species/rudd/rudd.tres" id="16_rudd"]
+[ext_resource type="Resource" path="res://fish/species/tench/tench.tres" id="17_tench"]
+[ext_resource type="Resource" path="res://fish/species/crayfish_red_swamp/crayfish_red_swamp.tres" id="18_crayfish_red_swamp"]
+[ext_resource type="Resource" path="res://fish/species/minnow_fathead/minnow_fathead.tres" id="19_minnow_fathead"]
+[ext_resource type="Resource" path="res://fish/species/shiner_golden/shiner_golden.tres" id="20_shiner_golden"]
+[ext_resource type="Resource" path="res://fish/species/loach_weather/loach_weather.tres" id="21_loach_weather"]
+[ext_resource type="Resource" path="res://fish/species/goldfish_bubbleeye/goldfish_bubbleeye.tres" id="22_goldfish_bubbleeye"]
+[ext_resource type="Resource" path="res://fish/species/goldfish/goldfish.tres" id="23_goldfish"]
+[ext_resource type="Resource" path="res://fish/species/koi/koi.tres" id="24_koi"]
+[ext_resource type="Resource" path="res://fish/species/crappie_black/crappie_black.tres" id="25_crappie_black"]
+[ext_resource type="Resource" path="res://fish/species/bluegill/bluegill.tres" id="26_bluegill"]
+[ext_resource type="Resource" path="res://fish/species/green_sunfish/green_sunfish.tres" id="27_green_sunfish"]
+[ext_resource type="Resource" path="res://fish/species/pumpkinseed/pumpkinseed.tres" id="28_pumpkinseed"]
+[ext_resource type="Resource" path="res://fish/species/redear_sunfish/redear_sunfish.tres" id="29_redear_sunfish"]
+[ext_resource type="Resource" path="res://fish/species/warmouth/warmouth.tres" id="30_warmouth"]
+[ext_resource type="Resource" path="res://fish/species/pickerel_chain/pickerel_chain.tres" id="31_pickerel_chain"]
+[ext_resource type="Resource" path="res://fish/species/pickerel_grass/pickerel_grass.tres" id="32_pickerel_grass"]
+[ext_resource type="Resource" path="res://fish/species/betta_siamese/betta_siamese.tres" id="33_betta_siamese"]
+[ext_resource type="Resource" path="res://fish/species/catfish_walking/catfish_walking.tres" id="34_catfish_walking"]
+[ext_resource type="Resource" path="res://fish/species/sturgeon_lake/sturgeon_lake.tres" id="35_sturgeon_lake"]
+[ext_resource type="Resource" path="res://fish/species/burbot/burbot.tres" id="36_burbot"]
+[ext_resource type="Resource" path="res://fish/species/cisco/cisco.tres" id="37_cisco"]
+[ext_resource type="Resource" path="res://fish/species/whitefish_lake/whitefish_lake.tres" id="38_whitefish_lake"]
+[ext_resource type="Resource" path="res://fish/species/char_arctic/char_arctic.tres" id="39_char_arctic"]
+[ext_resource type="Resource" path="res://fish/species/trout_golden/trout_golden.tres" id="40_trout_golden"]
+[ext_resource type="Resource" path="res://fish/species/axolotl/axolotl.tres" id="41_axolotl"]
+[ext_resource type="Resource" path="res://fish/species/bream_common/bream_common.tres" id="42_bream_common"]
+[ext_resource type="Resource" path="res://fish/species/muskellunge/muskellunge.tres" id="43_muskellunge"]
+[ext_resource type="Resource" path="res://fish/species/shiner_emerald/shiner_emerald.tres" id="44_shiner_emerald"]
+[ext_resource type="Resource" path="res://fish/species/chub_lake/chub_lake.tres" id="45_chub_lake"]
+[ext_resource type="Resource" path="res://fish/species/crappie_white/crappie_white.tres" id="46_crappie_white"]
+[ext_resource type="Resource" path="res://fish/species/yellow_perch/yellow_perch.tres" id="47_yellow_perch"]
+[ext_resource type="Resource" path="res://fish/species/pike_northern/pike_northern.tres" id="48_pike_northern"]
+[ext_resource type="Resource" path="res://fish/species/saugeye/saugeye.tres" id="49_saugeye"]
+[ext_resource type="Resource" path="res://fish/species/walleye/walleye.tres" id="50_walleye"]
+[ext_resource type="Resource" path="res://fish/species/goby_round/goby_round.tres" id="51_goby_round"]
+[ext_resource type="Resource" path="res://fish/species/smelt_rainbow/smelt_rainbow.tres" id="52_smelt_rainbow"]
+[ext_resource type="Resource" path="res://fish/species/gourami_giant/gourami_giant.tres" id="53_gourami_giant"]
+[ext_resource type="Resource" path="res://fish/species/tilapia_nile/tilapia_nile.tres" id="54_tilapia_nile"]
+[ext_resource type="Resource" path="res://fish/species/gar_longnose/gar_longnose.tres" id="55_gar_longnose"]
+[ext_resource type="Resource" path="res://fish/species/paddlefish/paddlefish.tres" id="56_paddlefish"]
+[ext_resource type="Resource" path="res://fish/species/sturgeon_shovelnose/sturgeon_shovelnose.tres" id="57_sturgeon_shovelnose"]
+[ext_resource type="Resource" path="res://fish/species/foureyes_largescale/foureyes_largescale.tres" id="58_foureyes_largescale"]
+[ext_resource type="Resource" path="res://fish/species/grayling_arctic/grayling_arctic.tres" id="59_grayling_arctic"]
+[ext_resource type="Resource" path="res://fish/species/trout_brown/trout_brown.tres" id="60_trout_brown"]
+[ext_resource type="Resource" path="res://fish/species/trout_cutthroat/trout_cutthroat.tres" id="61_trout_cutthroat"]
+[ext_resource type="Resource" path="res://fish/species/trout_rainbow/trout_rainbow.tres" id="62_trout_rainbow"]
+[ext_resource type="Resource" path="res://fish/species/hellbender/hellbender.tres" id="63_hellbender"]
+[ext_resource type="Resource" path="res://fish/species/smallmouth_bass/smallmouth_bass.tres" id="64_smallmouth_bass"]
+[ext_resource type="Resource" path="res://fish/species/buffalo_bigmouth/buffalo_bigmouth.tres" id="65_buffalo_bigmouth"]
+[ext_resource type="Resource" path="res://fish/species/redhorse_golden/redhorse_golden.tres" id="66_redhorse_golden"]
+[ext_resource type="Resource" path="res://fish/species/quillback/quillback.tres" id="67_quillback"]
+[ext_resource type="Resource" path="res://fish/species/buffalo_smallmouth/buffalo_smallmouth.tres" id="68_buffalo_smallmouth"]
+[ext_resource type="Resource" path="res://fish/species/sucker_white/sucker_white.tres" id="69_sucker_white"]
+[ext_resource type="Resource" path="res://fish/species/catfish_blue/catfish_blue.tres" id="70_catfish_blue"]
+[ext_resource type="Resource" path="res://fish/species/catfish_flathead/catfish_flathead.tres" id="71_catfish_flathead"]
+[ext_resource type="Resource" path="res://fish/species/madtom_tadpole/madtom_tadpole.tres" id="72_madtom_tadpole"]
+[ext_resource type="Resource" path="res://fish/species/barbel_common/barbel_common.tres" id="73_barbel_common"]
+[ext_resource type="Resource" path="res://fish/species/chub_european/chub_european.tres" id="74_chub_european"]
+[ext_resource type="Resource" path="res://fish/species/fallfish/fallfish.tres" id="75_fallfish"]
+[ext_resource type="Resource" path="res://fish/species/ide/ide.tres" id="76_ide"]
+[ext_resource type="Resource" path="res://fish/species/gar_alligator/gar_alligator.tres" id="77_gar_alligator"]
+[ext_resource type="Resource" path="res://fish/species/arapaima/arapaima.tres" id="78_arapaima"]
+[ext_resource type="Resource" path="res://fish/species/tigerfish_goliath/tigerfish_goliath.tres" id="79_tigerfish_goliath"]
+[ext_resource type="Resource" path="res://fish/species/shiner_common/shiner_common.tres" id="80_shiner_common"]
+[ext_resource type="Resource" path="res://fish/species/chub_creek/chub_creek.tres" id="81_chub_creek"]
+[ext_resource type="Resource" path="res://fish/species/chub_flame/chub_flame.tres" id="82_chub_flame"]
+[ext_resource type="Resource" path="res://fish/species/electric_eel/electric_eel.tres" id="83_electric_eel"]
+[ext_resource type="Resource" path="res://fish/species/freshwater_drum/freshwater_drum.tres" id="84_freshwater_drum"]
+[ext_resource type="Resource" path="res://fish/species/goldeye/goldeye.tres" id="85_goldeye"]
+[ext_resource type="Resource" path="res://fish/species/mooneye/mooneye.tres" id="86_mooneye"]
+[ext_resource type="Resource" path="res://fish/species/sauger/sauger.tres" id="87_sauger"]
+[ext_resource type="Resource" path="res://fish/species/eel_american/eel_american.tres" id="88_eel_american"]
+[ext_resource type="Resource" path="res://fish/species/eel_european/eel_european.tres" id="89_eel_european"]
+[ext_resource type="Resource" path="res://fish/species/smelt_eulachon/smelt_eulachon.tres" id="90_smelt_eulachon"]
+[ext_resource type="Resource" path="res://fish/species/sturgeon_white/sturgeon_white.tres" id="91_sturgeon_white"]
+[ext_resource type="Resource" path="res://fish/species/trout_steelhead/trout_steelhead.tres" id="92_trout_steelhead"]
+[ext_resource type="Resource" path="res://fish/species/stingray_ocellate_river/stingray_ocellate_river.tres" id="93_stingray_ocellate_river"]
+[ext_resource type="Resource" path="res://fish/species/white_perch/white_perch.tres" id="94_white_perch"]
+[ext_resource type="Resource" path="res://fish/species/barramundi/barramundi.tres" id="95_barramundi"]
+[ext_resource type="Resource" path="res://fish/species/knifefish_clown/knifefish_clown.tres" id="96_knifefish_clown"]
+[ext_resource type="Resource" path="res://fish/species/loach_clown/loach_clown.tres" id="97_loach_clown"]
+[ext_resource type="Resource" path="res://fish/species/discus/discus.tres" id="98_discus"]
+[ext_resource type="Resource" path="res://fish/species/angelfish_freshwater/angelfish_freshwater.tres" id="99_angelfish_freshwater"]
+[ext_resource type="Resource" path="res://fish/species/oscar/oscar.tres" id="100_oscar"]
+[ext_resource type="Resource" path="res://fish/species/tambaqui/tambaqui.tres" id="101_tambaqui"]
+[ext_resource type="Resource" path="res://fish/species/snakehead_giant/snakehead_giant.tres" id="102_snakehead_giant"]
+[ext_resource type="Resource" path="res://fish/species/dorado_golden/dorado_golden.tres" id="103_dorado_golden"]
+[ext_resource type="Resource" path="res://fish/species/mahseer_golden/mahseer_golden.tres" id="104_mahseer_golden"]
+[ext_resource type="Resource" path="res://fish/species/peacock_bass/peacock_bass.tres" id="105_peacock_bass"]
+[ext_resource type="Resource" path="res://fish/species/piranha_red_bellied/piranha_red_bellied.tres" id="106_piranha_red_bellied"]
+[ext_resource type="Resource" path="res://fish/species/arowana_silver/arowana_silver.tres" id="107_arowana_silver"]
[resource]
script = ExtResource("1_pool")
-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")]
+candidates = [ExtResource("2_bowfin"), ExtResource("3_gar_spotted"), ExtResource("4_lungfish_west_african"), ExtResource("5_guppy"), ExtResource("6_tetra_neon"), ExtResource("7_danio_zebra"), ExtResource("8_largemouth_bass"), ExtResource("9_bullhead_black"), ExtResource("10_bullhead_brown"), ExtResource("11_catfish_channel"), ExtResource("12_catfish_white"), ExtResource("13_bullhead_yellow"), ExtResource("14_carp"), ExtResource("15_roach_common"), ExtResource("16_rudd"), ExtResource("17_tench"), ExtResource("18_crayfish_red_swamp"), ExtResource("19_minnow_fathead"), ExtResource("20_shiner_golden"), ExtResource("21_loach_weather"), ExtResource("22_goldfish_bubbleeye"), ExtResource("23_goldfish"), ExtResource("24_koi"), ExtResource("25_crappie_black"), ExtResource("26_bluegill"), ExtResource("27_green_sunfish"), ExtResource("28_pumpkinseed"), ExtResource("29_redear_sunfish"), ExtResource("30_warmouth"), ExtResource("31_pickerel_chain"), ExtResource("32_pickerel_grass"), ExtResource("33_betta_siamese"), ExtResource("34_catfish_walking"), ExtResource("35_sturgeon_lake"), ExtResource("36_burbot"), ExtResource("37_cisco"), ExtResource("38_whitefish_lake"), ExtResource("39_char_arctic"), ExtResource("40_trout_golden"), ExtResource("41_axolotl"), ExtResource("42_bream_common"), ExtResource("43_muskellunge"), ExtResource("44_shiner_emerald"), ExtResource("45_chub_lake"), ExtResource("46_crappie_white"), ExtResource("47_yellow_perch"), ExtResource("48_pike_northern"), ExtResource("49_saugeye"), ExtResource("50_walleye"), ExtResource("51_goby_round"), ExtResource("52_smelt_rainbow"), ExtResource("53_gourami_giant"), ExtResource("54_tilapia_nile"), ExtResource("55_gar_longnose"), ExtResource("56_paddlefish"), ExtResource("57_sturgeon_shovelnose"), ExtResource("58_foureyes_largescale"), ExtResource("59_grayling_arctic"), ExtResource("60_trout_brown"), ExtResource("61_trout_cutthroat"), ExtResource("62_trout_rainbow"), ExtResource("63_hellbender"), ExtResource("64_smallmouth_bass"), ExtResource("65_buffalo_bigmouth"), ExtResource("66_redhorse_golden"), ExtResource("67_quillback"), ExtResource("68_buffalo_smallmouth"), ExtResource("69_sucker_white"), ExtResource("70_catfish_blue"), ExtResource("71_catfish_flathead"), ExtResource("72_madtom_tadpole"), ExtResource("73_barbel_common"), ExtResource("74_chub_european"), ExtResource("75_fallfish"), ExtResource("76_ide"), ExtResource("77_gar_alligator"), ExtResource("78_arapaima"), ExtResource("79_tigerfish_goliath"), ExtResource("80_shiner_common"), ExtResource("81_chub_creek"), ExtResource("82_chub_flame"), ExtResource("83_electric_eel"), ExtResource("84_freshwater_drum"), ExtResource("85_goldeye"), ExtResource("86_mooneye"), ExtResource("87_sauger"), ExtResource("88_eel_american"), ExtResource("89_eel_european"), ExtResource("90_smelt_eulachon"), ExtResource("91_sturgeon_white"), ExtResource("92_trout_steelhead"), ExtResource("93_stingray_ocellate_river"), ExtResource("94_white_perch"), ExtResource("95_barramundi"), ExtResource("96_knifefish_clown"), ExtResource("97_loach_clown"), ExtResource("98_discus"), ExtResource("99_angelfish_freshwater"), ExtResource("100_oscar"), ExtResource("101_tambaqui"), ExtResource("102_snakehead_giant"), ExtResource("103_dorado_golden"), ExtResource("104_mahseer_golden"), ExtResource("105_peacock_bass"), ExtResource("106_piranha_red_bellied"), ExtResource("107_arowana_silver")]
diff --git a/fish/species/abalone_red/abalone_red.tres b/fish/species/abalone_red/abalone_red.tres
new file mode 100644
index 0000000..6efe784
--- /dev/null
+++ b/fish/species/abalone_red/abalone_red.tres
@@ -0,0 +1,32 @@
+[gd_resource type="Resource" script_class="FishData" load_steps=5 format=3]
+
+[ext_resource type="Script" 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" path="res://fish/fish_availability.gd" id="3_availability"]
+[ext_resource type="Texture2D" path="res://art/exported/creatures/fish_placeholder.png" id="99_fish_placeholder"]
+
+[sub_resource type="Resource" id="Availability"]
+script = ExtResource("3_availability")
+allow_night = false
+require_fog = true
+
+[resource]
+script = ExtResource("1_fish_data")
+id = &"abalone_red"
+display_name = "red abalone"
+active = true
+catalog_number = 6405
+collection_group = &"Shellfish"
+logbook_fact = "red abalones cling to pacific rocks with broad muscular feet. rows of shell holes release water that has passed over the gills."
+available_seasons = 15
+allowed_water_types = 2
+rarity = 3
+base_catch_weight = 0.09
+catch_profile = ExtResource("2_profile")
+availability = SubResource("Availability")
+weight_min_lb = 0.2
+weight_max_lb = 12.0
+sell_value_min = 24
+sell_value_max = 56
+sell_value_curve = 0.88
+display_texture = ExtResource("99_fish_placeholder")
diff --git a/fish/species/amberjack_greater/amberjack_greater.tres b/fish/species/amberjack_greater/amberjack_greater.tres
new file mode 100644
index 0000000..b0e40b8
--- /dev/null
+++ b/fish/species/amberjack_greater/amberjack_greater.tres
@@ -0,0 +1,30 @@
+[gd_resource type="Resource" script_class="FishData" load_steps=5 format=3]
+
+[ext_resource type="Script" 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" path="res://fish/fish_availability.gd" id="3_availability"]
+[ext_resource type="Texture2D" path="res://art/exported/creatures/fish_placeholder.png" id="99_fish_placeholder"]
+
+[sub_resource type="Resource" id="Availability"]
+script = ExtResource("3_availability")
+
+[resource]
+script = ExtResource("1_fish_data")
+id = &"amberjack_greater"
+display_name = "greater amberjack"
+active = true
+catalog_number = 3604
+collection_group = &"Large coastal predators"
+logbook_fact = "greater amberjacks circle reefs, wrecks, and offshore platforms. a dark stripe crosses the eye of their sturdy torpedo-shaped bodies."
+available_seasons = 6
+allowed_water_types = 2
+rarity = 2
+base_catch_weight = 0.25
+catch_profile = ExtResource("2_profile")
+availability = SubResource("Availability")
+weight_min_lb = 5.0
+weight_max_lb = 90.0
+sell_value_min = 15
+sell_value_max = 42
+sell_value_curve = 0.82
+display_texture = ExtResource("99_fish_placeholder")
diff --git a/fish/species/amberjack_lesser/amberjack_lesser.tres b/fish/species/amberjack_lesser/amberjack_lesser.tres
new file mode 100644
index 0000000..1d5983c
--- /dev/null
+++ b/fish/species/amberjack_lesser/amberjack_lesser.tres
@@ -0,0 +1,30 @@
+[gd_resource type="Resource" script_class="FishData" load_steps=5 format=3]
+
+[ext_resource type="Script" 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" path="res://fish/fish_availability.gd" id="3_availability"]
+[ext_resource type="Texture2D" path="res://art/exported/creatures/fish_placeholder.png" id="99_fish_placeholder"]
+
+[sub_resource type="Resource" id="Availability"]
+script = ExtResource("3_availability")
+
+[resource]
+script = ExtResource("1_fish_data")
+id = &"amberjack_lesser"
+display_name = "lesser amberjack"
+active = true
+catalog_number = 3605
+collection_group = &"Large coastal predators"
+logbook_fact = "lesser amberjacks school around deep reefs and floating objects. an enlarged eye suits their preference for dimmer water."
+available_seasons = 6
+allowed_water_types = 2
+rarity = 1
+base_catch_weight = 0.75
+catch_profile = ExtResource("2_profile")
+availability = SubResource("Availability")
+weight_min_lb = 1.0
+weight_max_lb = 20.0
+sell_value_min = 7
+sell_value_max = 18
+sell_value_curve = 0.86
+display_texture = ExtResource("99_fish_placeholder")
diff --git a/fish/species/anchovy_european/anchovy_european.tres b/fish/species/anchovy_european/anchovy_european.tres
index fea09ed..0a7d122 100644
--- a/fish/species/anchovy_european/anchovy_european.tres
+++ b/fish/species/anchovy_european/anchovy_european.tres
@@ -3,7 +3,7 @@
[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"]
+[ext_resource type="Texture2D" path="res://art/exported/creatures/805.png" id="4_texture"]
[sub_resource type="Resource" id="AnchovyEuropeanAvailability"]
script = ExtResource("3_availability_script")
@@ -13,6 +13,11 @@ allowed_location_tags = Array[StringName]([&"ocean"])
script = ExtResource("1_fish_data")
id = &"anchovy_european"
display_name = "european anchovy"
+active = true
+catalog_number = 805
+collection_group = &"Coastal schooling fish"
+logbook_fact = "european anchovies form dense schools and feed by filtering tiny plankton from coastal water."
+available_seasons = 7
allowed_water_types = 2
base_catch_weight = 0.5
catch_profile = ExtResource("2_profile")
diff --git a/fish/species/anchovy_european/fish_anchovy_european.png b/fish/species/anchovy_european/fish_anchovy_european.png
index 44742f8..d691c72 100644
Binary files a/fish/species/anchovy_european/fish_anchovy_european.png and b/fish/species/anchovy_european/fish_anchovy_european.png differ
diff --git a/fish/species/anchovy_northern/anchovy_northern.tres b/fish/species/anchovy_northern/anchovy_northern.tres
index ad53d6e..e2c7fa0 100644
--- a/fish/species/anchovy_northern/anchovy_northern.tres
+++ b/fish/species/anchovy_northern/anchovy_northern.tres
@@ -3,7 +3,7 @@
[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"]
+[ext_resource type="Texture2D" path="res://art/exported/creatures/808.png" id="4_texture"]
[sub_resource type="Resource" id="AnchovyNorthernAvailability"]
script = ExtResource("3_availability_script")
@@ -13,6 +13,11 @@ allowed_location_tags = Array[StringName]([&"ocean"])
script = ExtResource("1_fish_data")
id = &"anchovy_northern"
display_name = "northern anchovy"
+active = true
+catalog_number = 808
+collection_group = &"Coastal schooling fish"
+logbook_fact = "northern anchovies gather in huge schools along the pacific coast and filter plankton with their fine gill rakers."
+available_seasons = 15
allowed_water_types = 2
base_catch_weight = 0.5
catch_profile = ExtResource("2_profile")
diff --git a/fish/species/anchovy_northern/fish_anchovy_northern.png b/fish/species/anchovy_northern/fish_anchovy_northern.png
index 20bacd8..f8749fb 100644
Binary files a/fish/species/anchovy_northern/fish_anchovy_northern.png and b/fish/species/anchovy_northern/fish_anchovy_northern.png differ
diff --git a/fish/species/angelfish_freshwater/angelfish_freshwater.tres b/fish/species/angelfish_freshwater/angelfish_freshwater.tres
new file mode 100644
index 0000000..7a58450
--- /dev/null
+++ b/fish/species/angelfish_freshwater/angelfish_freshwater.tres
@@ -0,0 +1,31 @@
+[gd_resource type="Resource" script_class="FishData" load_steps=5 format=3]
+
+[ext_resource type="Script" 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" path="res://fish/fish_availability.gd" id="3_availability"]
+[ext_resource type="Texture2D" path="res://art/exported/creatures/fish_placeholder.png" id="99_fish_placeholder"]
+
+[sub_resource type="Resource" id="Availability"]
+script = ExtResource("3_availability")
+allow_night = false
+
+[resource]
+script = ExtResource("1_fish_data")
+id = &"angelfish_freshwater"
+display_name = "freshwater angelfish"
+active = true
+catalog_number = 7504
+collection_group = &"Tropical freshwater fish"
+logbook_fact = "freshwater angelfish slip between tall plants on narrow triangular bodies. long fins and vertical bars resemble stems and shadows."
+available_seasons = 15
+allowed_water_types = 1
+rarity = 2
+base_catch_weight = 0.3
+catch_profile = ExtResource("2_profile")
+availability = SubResource("Availability")
+weight_min_lb = 0.05
+weight_max_lb = 0.5
+sell_value_min = 10
+sell_value_max = 22
+sell_value_curve = 0.95
+display_texture = ExtResource("99_fish_placeholder")
diff --git a/fish/species/angelfish_queen/angelfish_queen.tres b/fish/species/angelfish_queen/angelfish_queen.tres
new file mode 100644
index 0000000..5a15f5b
--- /dev/null
+++ b/fish/species/angelfish_queen/angelfish_queen.tres
@@ -0,0 +1,31 @@
+[gd_resource type="Resource" script_class="FishData" load_steps=5 format=3]
+
+[ext_resource type="Script" 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" path="res://fish/fish_availability.gd" id="3_availability"]
+[ext_resource type="Texture2D" path="res://art/exported/creatures/fish_placeholder.png" id="99_fish_placeholder"]
+
+[sub_resource type="Resource" id="Availability"]
+script = ExtResource("3_availability")
+allow_night = false
+
+[resource]
+script = ExtResource("1_fish_data")
+id = &"angelfish_queen"
+display_name = "queen angelfish"
+active = true
+catalog_number = 5703
+collection_group = &"Reef ornamentals"
+logbook_fact = "queen angelfish graze sponges from warm atlantic reefs. a blue-ringed spot on the forehead resembles a tiny crown."
+available_seasons = 15
+allowed_water_types = 2
+rarity = 2
+base_catch_weight = 0.3
+catch_profile = ExtResource("2_profile")
+availability = SubResource("Availability")
+weight_min_lb = 0.1
+weight_max_lb = 2.0
+sell_value_min = 11
+sell_value_max = 25
+sell_value_curve = 0.94
+display_texture = ExtResource("99_fish_placeholder")
diff --git a/fish/species/anglerfish_black_seadevil/anglerfish_black_seadevil.tres b/fish/species/anglerfish_black_seadevil/anglerfish_black_seadevil.tres
new file mode 100644
index 0000000..83b1fb3
--- /dev/null
+++ b/fish/species/anglerfish_black_seadevil/anglerfish_black_seadevil.tres
@@ -0,0 +1,31 @@
+[gd_resource type="Resource" script_class="FishData" load_steps=5 format=3]
+
+[ext_resource type="Script" 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" path="res://fish/fish_availability.gd" id="3_availability"]
+[ext_resource type="Texture2D" path="res://art/exported/creatures/fish_placeholder.png" id="99_fish_placeholder"]
+
+[sub_resource type="Resource" id="Availability"]
+script = ExtResource("3_availability")
+allow_day = false
+
+[resource]
+script = ExtResource("1_fish_data")
+id = &"anglerfish_black_seadevil"
+display_name = "black seadevil"
+active = true
+catalog_number = 1801
+collection_group = &"Deep-sea oddities"
+logbook_fact = "black seadevils lure prey with a glowing organ suspended above the mouth. tiny males permanently attach to much larger females."
+available_seasons = 15
+allowed_water_types = 2
+rarity = 4
+base_catch_weight = 0.05
+catch_profile = ExtResource("2_profile")
+availability = SubResource("Availability")
+weight_min_lb = 0.1
+weight_max_lb = 2.0
+sell_value_min = 48
+sell_value_max = 105
+sell_value_curve = 0.92
+display_texture = ExtResource("99_fish_placeholder")
diff --git a/fish/species/arapaima/arapaima.tres b/fish/species/arapaima/arapaima.tres
new file mode 100644
index 0000000..b43e3bd
--- /dev/null
+++ b/fish/species/arapaima/arapaima.tres
@@ -0,0 +1,31 @@
+[gd_resource type="Resource" script_class="FishData" load_steps=5 format=3]
+
+[ext_resource type="Script" 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" path="res://fish/fish_availability.gd" id="3_availability"]
+[ext_resource type="Texture2D" path="res://art/exported/creatures/fish_placeholder.png" id="99_fish_placeholder"]
+
+[sub_resource type="Resource" id="Availability"]
+script = ExtResource("3_availability")
+allow_night = false
+
+[resource]
+script = ExtResource("1_fish_data")
+id = &"arapaima"
+display_name = "arapaima"
+active = true
+catalog_number = 2702
+collection_group = &"Freshwater giants"
+logbook_fact = "arapaima must surface frequently to gulp air through a specialized swim bladder. large armored scales protect their long river-going bodies."
+available_seasons = 15
+allowed_water_types = 1
+rarity = 4
+base_catch_weight = 0.04
+catch_profile = ExtResource("2_profile")
+availability = SubResource("Availability")
+weight_min_lb = 40.0
+weight_max_lb = 400.0
+sell_value_min = 60
+sell_value_max = 140
+sell_value_curve = 0.78
+display_texture = ExtResource("99_fish_placeholder")
diff --git a/fish/species/arowana_silver/arowana_silver.tres b/fish/species/arowana_silver/arowana_silver.tres
new file mode 100644
index 0000000..42baf33
--- /dev/null
+++ b/fish/species/arowana_silver/arowana_silver.tres
@@ -0,0 +1,32 @@
+[gd_resource type="Resource" script_class="FishData" load_steps=5 format=3]
+
+[ext_resource type="Script" 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" path="res://fish/fish_availability.gd" id="3_availability"]
+[ext_resource type="Texture2D" path="res://art/exported/creatures/fish_placeholder.png" id="99_fish_placeholder"]
+
+[sub_resource type="Resource" id="Availability"]
+script = ExtResource("3_availability")
+allow_night = false
+require_fog = true
+
+[resource]
+script = ExtResource("1_fish_data")
+id = &"arowana_silver"
+display_name = "silver arowana"
+active = true
+catalog_number = 7606
+collection_group = &"Tropical freshwater predators"
+logbook_fact = "silver arowanas patrol the surface with upturned mouths and long bodies. powerful jumps capture insects and small animals above the water."
+available_seasons = 15
+allowed_water_types = 1
+rarity = 3
+base_catch_weight = 0.11
+catch_profile = ExtResource("2_profile")
+availability = SubResource("Availability")
+weight_min_lb = 1.0
+weight_max_lb = 15.0
+sell_value_min = 25
+sell_value_max = 56
+sell_value_curve = 0.86
+display_texture = ExtResource("99_fish_placeholder")
diff --git a/fish/species/axolotl/axolotl.tres b/fish/species/axolotl/axolotl.tres
new file mode 100644
index 0000000..c201f9c
--- /dev/null
+++ b/fish/species/axolotl/axolotl.tres
@@ -0,0 +1,32 @@
+[gd_resource type="Resource" script_class="FishData" load_steps=5 format=3]
+
+[ext_resource type="Script" 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" path="res://fish/fish_availability.gd" id="3_availability"]
+[ext_resource type="Texture2D" path="res://art/exported/creatures/fish_placeholder.png" id="99_fish_placeholder"]
+
+[sub_resource type="Resource" id="Availability"]
+script = ExtResource("3_availability")
+allow_day = false
+require_fog = true
+
+[resource]
+script = ExtResource("1_fish_data")
+id = &"axolotl"
+display_name = "axolotl"
+active = true
+catalog_number = 2001
+collection_group = &"Freshwater amphibians"
+logbook_fact = "axolotls remain aquatic and keep feathery external gills throughout adulthood. lost limbs and parts of several organs can regenerate."
+available_seasons = 7
+allowed_water_types = 1
+rarity = 4
+base_catch_weight = 0.04
+catch_profile = ExtResource("2_profile")
+availability = SubResource("Availability")
+weight_min_lb = 0.1
+weight_max_lb = 0.7
+sell_value_min = 36
+sell_value_max = 78
+sell_value_curve = 0.96
+display_texture = ExtResource("99_fish_placeholder")
diff --git a/fish/species/barbel_common/barbel_common.tres b/fish/species/barbel_common/barbel_common.tres
new file mode 100644
index 0000000..9cbafff
--- /dev/null
+++ b/fish/species/barbel_common/barbel_common.tres
@@ -0,0 +1,32 @@
+[gd_resource type="Resource" script_class="FishData" load_steps=5 format=3]
+
+[ext_resource type="Script" 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" path="res://fish/fish_availability.gd" id="3_availability"]
+[ext_resource type="Texture2D" path="res://art/exported/creatures/fish_placeholder.png" id="99_fish_placeholder"]
+
+[sub_resource type="Resource" id="Availability"]
+script = ExtResource("3_availability")
+allow_day = false
+require_rain = true
+
+[resource]
+script = ExtResource("1_fish_data")
+id = &"barbel_common"
+display_name = "common barbel"
+active = true
+catalog_number = 2501
+collection_group = &"Freshwater coarse fish"
+logbook_fact = "common barbels hold close to fast river bottoms with streamlined bodies and broad fins. four mouth barbels locate food among stones."
+available_seasons = 7
+allowed_water_types = 1
+rarity = 2
+base_catch_weight = 0.28
+catch_profile = ExtResource("2_profile")
+availability = SubResource("Availability")
+weight_min_lb = 1.0
+weight_max_lb = 25.0
+sell_value_min = 13
+sell_value_max = 31
+sell_value_curve = 0.85
+display_texture = ExtResource("99_fish_placeholder")
diff --git a/fish/species/barracuda_great/barracuda_great.tres b/fish/species/barracuda_great/barracuda_great.tres
new file mode 100644
index 0000000..f15092c
--- /dev/null
+++ b/fish/species/barracuda_great/barracuda_great.tres
@@ -0,0 +1,32 @@
+[gd_resource type="Resource" script_class="FishData" load_steps=5 format=3]
+
+[ext_resource type="Script" 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" path="res://fish/fish_availability.gd" id="3_availability"]
+[ext_resource type="Texture2D" path="res://art/exported/creatures/fish_placeholder.png" id="99_fish_placeholder"]
+
+[sub_resource type="Resource" id="Availability"]
+script = ExtResource("3_availability")
+allow_night = false
+require_fog = true
+
+[resource]
+script = ExtResource("1_fish_data")
+id = &"barracuda_great"
+display_name = "great barracuda"
+active = true
+catalog_number = 5804
+collection_group = &"Reef predators"
+logbook_fact = "great barracudas hover motionless before accelerating toward prey. long jaws carry two distinct rows of sharp teeth."
+available_seasons = 15
+allowed_water_types = 2
+rarity = 3
+base_catch_weight = 0.12
+catch_profile = ExtResource("2_profile")
+availability = SubResource("Availability")
+weight_min_lb = 5.0
+weight_max_lb = 80.0
+sell_value_min = 28
+sell_value_max = 65
+sell_value_curve = 0.82
+display_texture = ExtResource("99_fish_placeholder")
diff --git a/fish/species/barramundi/barramundi.tres b/fish/species/barramundi/barramundi.tres
new file mode 100644
index 0000000..705001d
--- /dev/null
+++ b/fish/species/barramundi/barramundi.tres
@@ -0,0 +1,32 @@
+[gd_resource type="Resource" script_class="FishData" load_steps=5 format=3]
+
+[ext_resource type="Script" 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" path="res://fish/fish_availability.gd" id="3_availability"]
+[ext_resource type="Texture2D" path="res://art/exported/creatures/fish_placeholder.png" id="99_fish_placeholder"]
+
+[sub_resource type="Resource" id="Availability"]
+script = ExtResource("3_availability")
+allow_day = false
+require_rain = true
+
+[resource]
+script = ExtResource("1_fish_data")
+id = &"barramundi"
+display_name = "barramundi"
+active = true
+catalog_number = 7302
+collection_group = &"Tropical estuary"
+logbook_fact = "barramundi move between rivers and coasts and can breathe in low-oxygen water. many mature first as males and later become females."
+available_seasons = 15
+allowed_water_types = 3
+rarity = 2
+base_catch_weight = 0.24
+catch_profile = ExtResource("2_profile")
+availability = SubResource("Availability")
+weight_min_lb = 2.0
+weight_max_lb = 45.0
+sell_value_min = 15
+sell_value_max = 38
+sell_value_curve = 0.84
+display_texture = ExtResource("99_fish_placeholder")
diff --git a/fish/species/barreleye_pacific/barreleye_pacific.tres b/fish/species/barreleye_pacific/barreleye_pacific.tres
new file mode 100644
index 0000000..c0705dd
--- /dev/null
+++ b/fish/species/barreleye_pacific/barreleye_pacific.tres
@@ -0,0 +1,32 @@
+[gd_resource type="Resource" script_class="FishData" load_steps=5 format=3]
+
+[ext_resource type="Script" 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" path="res://fish/fish_availability.gd" id="3_availability"]
+[ext_resource type="Texture2D" path="res://art/exported/creatures/fish_placeholder.png" id="99_fish_placeholder"]
+
+[sub_resource type="Resource" id="Availability"]
+script = ExtResource("3_availability")
+allow_day = false
+require_fog = true
+
+[resource]
+script = ExtResource("1_fish_data")
+id = &"barreleye_pacific"
+display_name = "Pacific barreleye"
+active = true
+catalog_number = 1804
+collection_group = &"Deep-sea oddities"
+logbook_fact = "pacific barreleyes peer upward through transparent shields covering the head. tubular eyes rotate forward when the fish turns to feed."
+available_seasons = 15
+allowed_water_types = 2
+rarity = 4
+base_catch_weight = 0.045
+catch_profile = ExtResource("2_profile")
+availability = SubResource("Availability")
+weight_min_lb = 0.2
+weight_max_lb = 2.5
+sell_value_min = 42
+sell_value_max = 88
+sell_value_curve = 0.9
+display_texture = ExtResource("99_fish_placeholder")
diff --git a/fish/species/bass/bass.tres b/fish/species/bass/bass.tres
index bc2b026..9d52609 100644
--- a/fish/species/bass/bass.tres
+++ b/fish/species/bass/bass.tres
@@ -3,7 +3,7 @@
[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://dvh6s86asl4ix" path="res://fish/species/bass/fish_bass_striped.png" id="4_texture"]
+[ext_resource type="Texture2D" path="res://art/exported/creatures/fish_placeholder.png" id="4_texture"]
[sub_resource type="Resource" id="BassAvailability"]
script = ExtResource("3_availability_script")
@@ -14,6 +14,11 @@ preferred_bait_weight_multiplier = 1.4
script = ExtResource("1_fish_data")
id = &"bass"
display_name = "striped bass"
+active = true
+catalog_number = 6701
+collection_group = &"Temperate coastal"
+logbook_fact = "striped bass split their lives between salt and fresh water, returning upriver to spawn. a long-lived striper may see three decades."
+available_seasons = 5
allowed_water_types = 2
rarity = 1
base_catch_weight = 4.0
diff --git a/fish/species/bass/fish_bass_striped.png b/fish/species/bass/fish_bass_striped.png
index efbf835..e39f48f 100644
Binary files a/fish/species/bass/fish_bass_striped.png and b/fish/species/bass/fish_bass_striped.png differ
diff --git a/fish/species/beetle_stag_common/beetle_stag_common.png b/fish/species/beetle_stag_common/beetle_stag_common.png
new file mode 100644
index 0000000..5ada780
Binary files /dev/null and b/fish/species/beetle_stag_common/beetle_stag_common.png differ
diff --git a/fish/species/beetle_stag_common/beetle_stag_common.png.import b/fish/species/beetle_stag_common/beetle_stag_common.png.import
new file mode 100644
index 0000000..4ad3482
--- /dev/null
+++ b/fish/species/beetle_stag_common/beetle_stag_common.png.import
@@ -0,0 +1,40 @@
+[remap]
+
+importer="texture"
+type="CompressedTexture2D"
+uid="uid://blw6lydoba8kd"
+path="res://.godot/imported/beetle_stag_common.png-5eb7426d7dbda5083e39efc4cb980c8d.ctex"
+metadata={
+"vram_texture": false
+}
+
+[deps]
+
+source_file="res://fish/species/beetle_stag_common/beetle_stag_common.png"
+dest_files=["res://.godot/imported/beetle_stag_common.png-5eb7426d7dbda5083e39efc4cb980c8d.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/beetle_stag_common/beetle_stag_common.tres b/fish/species/beetle_stag_common/beetle_stag_common.tres
new file mode 100644
index 0000000..f0f0e6d
--- /dev/null
+++ b/fish/species/beetle_stag_common/beetle_stag_common.tres
@@ -0,0 +1,27 @@
+[gd_resource type="Resource" script_class="FishData" load_steps=4 format=3]
+
+[ext_resource type="Script" 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="Texture2D" path="res://fish/species/beetle_stag_common/beetle_stag_common.png" id="3_texture"]
+
+[resource]
+script = ExtResource("1_fish_data")
+id = &"beetle_stag_common"
+display_name = "common stag beetle"
+active = true
+catalog_number = 8001
+collection_group = &"Beetles"
+logbook_fact = "common stag beetles use broad jaws to wrestle rivals and cling to tree bark. adults feed on sap and other sweet plant juices."
+collection_method = 1
+habitat_label = "tree trunks"
+available_seasons = 2
+allowed_water_types = 0
+rarity = 0
+base_catch_weight = 1.0
+catch_profile = ExtResource("2_profile")
+weight_min_lb = 0.02
+weight_max_lb = 0.12
+sell_value_min = 4
+sell_value_max = 12
+sell_value_curve = 1.0
+display_texture = ExtResource("3_texture")
diff --git a/fish/species/betta_siamese/betta_siamese.tres b/fish/species/betta_siamese/betta_siamese.tres
new file mode 100644
index 0000000..4c9ca3c
--- /dev/null
+++ b/fish/species/betta_siamese/betta_siamese.tres
@@ -0,0 +1,32 @@
+[gd_resource type="Resource" script_class="FishData" load_steps=5 format=3]
+
+[ext_resource type="Script" 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" path="res://fish/fish_availability.gd" id="3_availability"]
+[ext_resource type="Texture2D" path="res://art/exported/creatures/7508.png" id="99_fish_placeholder"]
+
+[sub_resource type="Resource" id="Availability"]
+script = ExtResource("3_availability")
+allow_night = false
+require_rain = true
+
+[resource]
+script = ExtResource("1_fish_data")
+id = &"betta_siamese"
+display_name = "Siamese fighting fish"
+active = true
+catalog_number = 7508
+collection_group = &"Tropical freshwater fish"
+logbook_fact = "siamese fighting fish breathe surface air with a labyrinth organ. males build bubble nests and guard the eggs beneath them."
+available_seasons = 15
+allowed_water_types = 1
+rarity = 1
+base_catch_weight = 1.0
+catch_profile = ExtResource("2_profile")
+availability = SubResource("Availability")
+weight_min_lb = 0.01
+weight_max_lb = 0.15
+sell_value_min = 5
+sell_value_max = 10
+sell_value_curve = 1.0
+display_texture = ExtResource("99_fish_placeholder")
diff --git a/fish/species/black_drum/black_drum.tres b/fish/species/black_drum/black_drum.tres
new file mode 100644
index 0000000..b542327
--- /dev/null
+++ b/fish/species/black_drum/black_drum.tres
@@ -0,0 +1,31 @@
+[gd_resource type="Resource" script_class="FishData" load_steps=5 format=3]
+
+[ext_resource type="Script" 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" path="res://fish/fish_availability.gd" id="3_availability"]
+[ext_resource type="Texture2D" path="res://art/exported/creatures/fish_placeholder.png" id="99_fish_placeholder"]
+
+[sub_resource type="Resource" id="Availability"]
+script = ExtResource("3_availability")
+allow_day = false
+
+[resource]
+script = ExtResource("1_fish_data")
+id = &"black_drum"
+display_name = "black drum"
+active = true
+catalog_number = 6802
+collection_group = &"Temperate estuary"
+logbook_fact = "black drum crush oysters and crabs with rounded throat teeth. large adults develop dark vertical bars that fade with age."
+available_seasons = 7
+allowed_water_types = 2
+rarity = 1
+base_catch_weight = 0.7
+catch_profile = ExtResource("2_profile")
+availability = SubResource("Availability")
+weight_min_lb = 1.0
+weight_max_lb = 70.0
+sell_value_min = 8
+sell_value_max = 27
+sell_value_curve = 0.82
+display_texture = ExtResource("99_fish_placeholder")
diff --git a/fish/species/blobfish_smooth_head/blobfish_smooth_head.tres b/fish/species/blobfish_smooth_head/blobfish_smooth_head.tres
new file mode 100644
index 0000000..4245304
--- /dev/null
+++ b/fish/species/blobfish_smooth_head/blobfish_smooth_head.tres
@@ -0,0 +1,32 @@
+[gd_resource type="Resource" script_class="FishData" load_steps=5 format=3]
+
+[ext_resource type="Script" 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" path="res://fish/fish_availability.gd" id="3_availability"]
+[ext_resource type="Texture2D" path="res://art/exported/creatures/fish_placeholder.png" id="99_fish_placeholder"]
+
+[sub_resource type="Resource" id="Availability"]
+script = ExtResource("3_availability")
+allow_day = false
+require_fog = true
+
+[resource]
+script = ExtResource("1_fish_data")
+id = &"blobfish_smooth_head"
+display_name = "smooth-head blobfish"
+active = true
+catalog_number = 1805
+collection_group = &"Deep-sea oddities"
+logbook_fact = "smooth-head blobfish rest on deep slopes where pressure supports their soft bodies. they conserve energy by waiting for edible matter nearby."
+available_seasons = 15
+allowed_water_types = 2
+rarity = 4
+base_catch_weight = 0.04
+catch_profile = ExtResource("2_profile")
+availability = SubResource("Availability")
+weight_min_lb = 0.5
+weight_max_lb = 5.0
+sell_value_min = 42
+sell_value_max = 90
+sell_value_curve = 0.86
+display_texture = ExtResource("99_fish_placeholder")
diff --git a/fish/species/bluefish/bluefish.tres b/fish/species/bluefish/bluefish.tres
new file mode 100644
index 0000000..d8888f7
--- /dev/null
+++ b/fish/species/bluefish/bluefish.tres
@@ -0,0 +1,31 @@
+[gd_resource type="Resource" script_class="FishData" load_steps=5 format=3]
+
+[ext_resource type="Script" 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" path="res://fish/fish_availability.gd" id="3_availability"]
+[ext_resource type="Texture2D" path="res://art/exported/creatures/fish_placeholder.png" id="99_fish_placeholder"]
+
+[sub_resource type="Resource" id="Availability"]
+script = ExtResource("3_availability")
+allow_night = false
+
+[resource]
+script = ExtResource("1_fish_data")
+id = &"bluefish"
+display_name = "bluefish"
+active = true
+catalog_number = 702
+collection_group = &"Coastal pelagic fish"
+logbook_fact = "bluefish race in coordinated schools while pursuing smaller fish. sharp teeth and powerful jaws make their feeding bursts especially fierce."
+available_seasons = 7
+allowed_water_types = 2
+rarity = 1
+base_catch_weight = 0.85
+catch_profile = ExtResource("2_profile")
+availability = SubResource("Availability")
+weight_min_lb = 0.5
+weight_max_lb = 25.0
+sell_value_min = 7
+sell_value_max = 19
+sell_value_curve = 0.85
+display_texture = ExtResource("99_fish_placeholder")
diff --git a/fish/species/bluegill/bluegill.tres b/fish/species/bluegill/bluegill.tres
index 9e98a5b..181b969 100644
--- a/fish/species/bluegill/bluegill.tres
+++ b/fish/species/bluegill/bluegill.tres
@@ -3,12 +3,17 @@
[ext_resource type="Script" 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="Resource" path="res://fish/day_availability.tres" id="3_availability"]
-[ext_resource type="Texture2D" path="res://fish/species/bluegill/fish_bluegill.png" id="4_texture"]
+[ext_resource type="Texture2D" path="res://art/exported/creatures/3102.png" id="4_texture"]
[resource]
script = ExtResource("1_fish_data")
id = &"bluegill"
display_name = "bluegill"
+active = true
+catalog_number = 3102
+collection_group = &"Freshwater panfish"
+logbook_fact = "bluegill fathers sweep shallow bowls into the bottom for nests, then stand guard. whole neighborhoods of nests can crowd together."
+available_seasons = 7
allowed_water_types = 1
rarity = 0
base_catch_weight = 10.0
diff --git a/fish/species/bluegill/fish_bluegill.png b/fish/species/bluegill/fish_bluegill.png
index 013db04..9ad6e4f 100644
Binary files a/fish/species/bluegill/fish_bluegill.png and b/fish/species/bluegill/fish_bluegill.png differ
diff --git a/fish/species/bonefish/bonefish.tres b/fish/species/bonefish/bonefish.tres
new file mode 100644
index 0000000..b2949d2
--- /dev/null
+++ b/fish/species/bonefish/bonefish.tres
@@ -0,0 +1,32 @@
+[gd_resource type="Resource" script_class="FishData" load_steps=5 format=3]
+
+[ext_resource type="Script" 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" path="res://fish/fish_availability.gd" id="3_availability"]
+[ext_resource type="Texture2D" path="res://art/exported/creatures/fish_placeholder.png" id="99_fish_placeholder"]
+
+[sub_resource type="Resource" id="Availability"]
+script = ExtResource("3_availability")
+allow_night = false
+forbid_fog = true
+
+[resource]
+script = ExtResource("1_fish_data")
+id = &"bonefish"
+display_name = "bonefish"
+active = true
+catalog_number = 7401
+collection_group = &"Tropical flats fish"
+logbook_fact = "bonefish sweep tropical flats in silvery schools and root for buried prey. their speed and pale bodies make them difficult to follow in shallow water."
+available_seasons = 15
+allowed_water_types = 2
+rarity = 2
+base_catch_weight = 0.25
+catch_profile = ExtResource("2_profile")
+availability = SubResource("Availability")
+weight_min_lb = 0.5
+weight_max_lb = 15.0
+sell_value_min = 13
+sell_value_max = 31
+sell_value_curve = 0.86
+display_texture = ExtResource("99_fish_placeholder")
diff --git a/fish/species/bowfin/bowfin.png b/fish/species/bowfin/bowfin.png
new file mode 100644
index 0000000..9d40491
Binary files /dev/null and b/fish/species/bowfin/bowfin.png differ
diff --git a/items/icons/shop/64_power_plus.png.import b/fish/species/bowfin/bowfin.png.import
similarity index 73%
rename from items/icons/shop/64_power_plus.png.import
rename to fish/species/bowfin/bowfin.png.import
index c82907d..b5f2a10 100644
--- a/items/icons/shop/64_power_plus.png.import
+++ b/fish/species/bowfin/bowfin.png.import
@@ -2,16 +2,16 @@
importer="texture"
type="CompressedTexture2D"
-uid="uid://xh2g4eytrjep"
-path="res://.godot/imported/64_power_plus.png-98b7e06d3090a4411d56e2046b8c9d4b.ctex"
+uid="uid://dgqegw70vkihk"
+path="res://.godot/imported/bowfin.png-062edda1eb3529c0e6365e0ca93cf3ef.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"]
+source_file="res://fish/species/bowfin/bowfin.png"
+dest_files=["res://.godot/imported/bowfin.png-062edda1eb3529c0e6365e0ca93cf3ef.ctex"]
[params]
diff --git a/fish/species/bowfin/bowfin.tres b/fish/species/bowfin/bowfin.tres
new file mode 100644
index 0000000..7b2881f
--- /dev/null
+++ b/fish/species/bowfin/bowfin.tres
@@ -0,0 +1,32 @@
+[gd_resource type="Resource" script_class="FishData" load_steps=5 format=3]
+
+[ext_resource type="Script" 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" path="res://fish/fish_availability.gd" id="3_availability"]
+[ext_resource type="Texture2D" path="res://art/exported/creatures/101.png" id="4_texture"]
+
+[sub_resource type="Resource" id="Availability"]
+script = ExtResource("3_availability")
+allow_day = false
+require_fog = true
+
+[resource]
+script = ExtResource("1_fish_data")
+id = &"bowfin"
+display_name = "bowfin"
+active = true
+catalog_number = 101
+collection_group = &"Ancient freshwater fish"
+logbook_fact = "bowfin can gulp air at the surface when warm backwaters run low on oxygen. a long dorsal fin ripples as they stalk prey."
+available_seasons = 7
+allowed_water_types = 1
+rarity = 2
+base_catch_weight = 0.28
+catch_profile = ExtResource("2_profile")
+availability = SubResource("Availability")
+weight_min_lb = 1.0
+weight_max_lb = 20.0
+sell_value_min = 13
+sell_value_max = 30
+sell_value_curve = 0.85
+display_texture = ExtResource("4_texture")
diff --git a/fish/species/boxfish_yellow/boxfish_yellow.tres b/fish/species/boxfish_yellow/boxfish_yellow.tres
new file mode 100644
index 0000000..aeda5ad
--- /dev/null
+++ b/fish/species/boxfish_yellow/boxfish_yellow.tres
@@ -0,0 +1,31 @@
+[gd_resource type="Resource" script_class="FishData" load_steps=5 format=3]
+
+[ext_resource type="Script" 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" path="res://fish/fish_availability.gd" id="3_availability"]
+[ext_resource type="Texture2D" path="res://art/exported/creatures/fish_placeholder.png" id="99_fish_placeholder"]
+
+[sub_resource type="Resource" id="Availability"]
+script = ExtResource("3_availability")
+allow_night = false
+
+[resource]
+script = ExtResource("1_fish_data")
+id = &"boxfish_yellow"
+display_name = "yellow boxfish"
+active = true
+catalog_number = 5608
+collection_group = &"Reef oddities"
+logbook_fact = "yellow boxfish juveniles are bright cubes covered in black spots. rigid body armor leaves small fins to provide careful steering."
+available_seasons = 15
+allowed_water_types = 2
+rarity = 2
+base_catch_weight = 0.32
+catch_profile = ExtResource("2_profile")
+availability = SubResource("Availability")
+weight_min_lb = 0.03
+weight_max_lb = 0.8
+sell_value_min = 10
+sell_value_max = 22
+sell_value_curve = 0.96
+display_texture = ExtResource("99_fish_placeholder")
diff --git a/fish/species/bream_common/bream_common.tres b/fish/species/bream_common/bream_common.tres
new file mode 100644
index 0000000..cc44fef
--- /dev/null
+++ b/fish/species/bream_common/bream_common.tres
@@ -0,0 +1,30 @@
+[gd_resource type="Resource" script_class="FishData" load_steps=5 format=3]
+
+[ext_resource type="Script" 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" path="res://fish/fish_availability.gd" id="3_availability"]
+[ext_resource type="Texture2D" path="res://art/exported/creatures/2502.png" id="99_fish_placeholder"]
+
+[sub_resource type="Resource" id="Availability"]
+script = ExtResource("3_availability")
+
+[resource]
+script = ExtResource("1_fish_data")
+id = &"bream_common"
+display_name = "common bream"
+active = true
+catalog_number = 2502
+collection_group = &"Freshwater coarse fish"
+logbook_fact = "common bream gather in slow rivers and lakes with silvery, deep-sided bodies. protruding mouths sift insect larvae from soft bottoms."
+available_seasons = 13
+allowed_water_types = 1
+rarity = 0
+base_catch_weight = 1.8
+catch_profile = ExtResource("2_profile")
+availability = SubResource("Availability")
+weight_min_lb = 0.3
+weight_max_lb = 15.0
+sell_value_min = 4
+sell_value_max = 12
+sell_value_curve = 0.88
+display_texture = ExtResource("99_fish_placeholder")
diff --git a/fish/species/buffalo_bigmouth/buffalo_bigmouth.tres b/fish/species/buffalo_bigmouth/buffalo_bigmouth.tres
new file mode 100644
index 0000000..bae0473
--- /dev/null
+++ b/fish/species/buffalo_bigmouth/buffalo_bigmouth.tres
@@ -0,0 +1,31 @@
+[gd_resource type="Resource" script_class="FishData" load_steps=5 format=3]
+
+[ext_resource type="Script" 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" path="res://fish/fish_availability.gd" id="3_availability"]
+[ext_resource type="Texture2D" path="res://art/exported/creatures/fish_placeholder.png" id="99_fish_placeholder"]
+
+[sub_resource type="Resource" id="Availability"]
+script = ExtResource("3_availability")
+require_fog = true
+
+[resource]
+script = ExtResource("1_fish_data")
+id = &"buffalo_bigmouth"
+display_name = "bigmouth buffalo"
+active = true
+catalog_number = 2301
+collection_group = &"Freshwater bottom fish"
+logbook_fact = "bigmouth buffalo filter plankton with fine gill rakers instead of rooting for prey. some individuals survive for more than a century."
+available_seasons = 7
+allowed_water_types = 1
+rarity = 3
+base_catch_weight = 0.12
+catch_profile = ExtResource("2_profile")
+availability = SubResource("Availability")
+weight_min_lb = 8.0
+weight_max_lb = 80.0
+sell_value_min = 28
+sell_value_max = 63
+sell_value_curve = 0.82
+display_texture = ExtResource("99_fish_placeholder")
diff --git a/fish/species/buffalo_smallmouth/buffalo_smallmouth.tres b/fish/species/buffalo_smallmouth/buffalo_smallmouth.tres
new file mode 100644
index 0000000..2756035
--- /dev/null
+++ b/fish/species/buffalo_smallmouth/buffalo_smallmouth.tres
@@ -0,0 +1,30 @@
+[gd_resource type="Resource" script_class="FishData" load_steps=5 format=3]
+
+[ext_resource type="Script" 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" path="res://fish/fish_availability.gd" id="3_availability"]
+[ext_resource type="Texture2D" path="res://art/exported/creatures/fish_placeholder.png" id="99_fish_placeholder"]
+
+[sub_resource type="Resource" id="Availability"]
+script = ExtResource("3_availability")
+
+[resource]
+script = ExtResource("1_fish_data")
+id = &"buffalo_smallmouth"
+display_name = "smallmouth buffalo"
+active = true
+catalog_number = 2304
+collection_group = &"Freshwater bottom fish"
+logbook_fact = "smallmouth buffalo use thick lips to graze bottom-dwelling food in warm rivers. their deep, heavy bodies form a pronounced arch behind the head."
+available_seasons = 7
+allowed_water_types = 1
+rarity = 2
+base_catch_weight = 0.3
+catch_profile = ExtResource("2_profile")
+availability = SubResource("Availability")
+weight_min_lb = 3.0
+weight_max_lb = 45.0
+sell_value_min = 14
+sell_value_max = 34
+sell_value_curve = 0.84
+display_texture = ExtResource("99_fish_placeholder")
diff --git a/fish/species/bullhead_black/bullhead_black.tres b/fish/species/bullhead_black/bullhead_black.tres
new file mode 100644
index 0000000..1d904bd
--- /dev/null
+++ b/fish/species/bullhead_black/bullhead_black.tres
@@ -0,0 +1,31 @@
+[gd_resource type="Resource" script_class="FishData" load_steps=5 format=3]
+
+[ext_resource type="Script" 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" path="res://fish/fish_availability.gd" id="3_availability"]
+[ext_resource type="Texture2D" path="res://art/exported/creatures/fish_placeholder.png" id="99_fish_placeholder"]
+
+[sub_resource type="Resource" id="Availability"]
+script = ExtResource("3_availability")
+allow_day = false
+
+[resource]
+script = ExtResource("1_fish_data")
+id = &"bullhead_black"
+display_name = "black bullhead"
+active = true
+catalog_number = 2401
+collection_group = &"Freshwater catfish"
+logbook_fact = "black bullheads tolerate warm, cloudy water with little oxygen. stout spines protect the dorsal and pectoral fins from predators."
+available_seasons = 7
+allowed_water_types = 1
+rarity = 1
+base_catch_weight = 1.0
+catch_profile = ExtResource("2_profile")
+availability = SubResource("Availability")
+weight_min_lb = 0.3
+weight_max_lb = 6.0
+sell_value_min = 5
+sell_value_max = 11
+sell_value_curve = 0.92
+display_texture = ExtResource("99_fish_placeholder")
diff --git a/fish/species/bullhead_brown/bullhead_brown.tres b/fish/species/bullhead_brown/bullhead_brown.tres
new file mode 100644
index 0000000..b3651b2
--- /dev/null
+++ b/fish/species/bullhead_brown/bullhead_brown.tres
@@ -0,0 +1,31 @@
+[gd_resource type="Resource" script_class="FishData" load_steps=5 format=3]
+
+[ext_resource type="Script" 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" path="res://fish/fish_availability.gd" id="3_availability"]
+[ext_resource type="Texture2D" path="res://art/exported/creatures/fish_placeholder.png" id="99_fish_placeholder"]
+
+[sub_resource type="Resource" id="Availability"]
+script = ExtResource("3_availability")
+allow_day = false
+
+[resource]
+script = ExtResource("1_fish_data")
+id = &"bullhead_brown"
+display_name = "brown bullhead"
+active = true
+catalog_number = 2403
+collection_group = &"Freshwater catfish"
+logbook_fact = "brown bullheads search muddy bottoms with eight sensitive barbels. males guard nests and herd newly hatched young in a dark moving ball."
+available_seasons = 7
+allowed_water_types = 1
+rarity = 0
+base_catch_weight = 2.3
+catch_profile = ExtResource("2_profile")
+availability = SubResource("Availability")
+weight_min_lb = 0.2
+weight_max_lb = 4.0
+sell_value_min = 3
+sell_value_max = 7
+sell_value_curve = 0.95
+display_texture = ExtResource("99_fish_placeholder")
diff --git a/fish/species/bullhead_yellow/bullhead_yellow.tres b/fish/species/bullhead_yellow/bullhead_yellow.tres
new file mode 100644
index 0000000..4dce589
--- /dev/null
+++ b/fish/species/bullhead_yellow/bullhead_yellow.tres
@@ -0,0 +1,32 @@
+[gd_resource type="Resource" script_class="FishData" load_steps=5 format=3]
+
+[ext_resource type="Script" 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" path="res://fish/fish_availability.gd" id="3_availability"]
+[ext_resource type="Texture2D" path="res://art/exported/creatures/fish_placeholder.png" id="99_fish_placeholder"]
+
+[sub_resource type="Resource" id="Availability"]
+script = ExtResource("3_availability")
+allow_day = false
+require_rain = true
+
+[resource]
+script = ExtResource("1_fish_data")
+id = &"bullhead_yellow"
+display_name = "yellow bullhead"
+active = true
+catalog_number = 2408
+collection_group = &"Freshwater catfish"
+logbook_fact = "yellow bullheads use pale chin barbels to distinguish food in murky shallows. both parents may guard the nest and schooling young."
+available_seasons = 7
+allowed_water_types = 1
+rarity = 0
+base_catch_weight = 2.0
+catch_profile = ExtResource("2_profile")
+availability = SubResource("Availability")
+weight_min_lb = 0.2
+weight_max_lb = 3.5
+sell_value_min = 3
+sell_value_max = 7
+sell_value_curve = 0.95
+display_texture = ExtResource("99_fish_placeholder")
diff --git a/fish/species/burbot/burbot.tres b/fish/species/burbot/burbot.tres
new file mode 100644
index 0000000..e46006c
--- /dev/null
+++ b/fish/species/burbot/burbot.tres
@@ -0,0 +1,32 @@
+[gd_resource type="Resource" script_class="FishData" load_steps=5 format=3]
+
+[ext_resource type="Script" 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" path="res://fish/fish_availability.gd" id="3_availability"]
+[ext_resource type="Texture2D" path="res://art/exported/creatures/fish_placeholder.png" id="99_fish_placeholder"]
+
+[sub_resource type="Resource" id="Availability"]
+script = ExtResource("3_availability")
+allow_day = false
+require_fog = true
+
+[resource]
+script = ExtResource("1_fish_data")
+id = &"burbot"
+display_name = "burbot"
+active = true
+catalog_number = 1001
+collection_group = &"Cold freshwater fish"
+logbook_fact = "burbot are the only freshwater members of the cod family. they become most active in cold darkness and may spawn beneath winter ice."
+available_seasons = 13
+allowed_water_types = 1
+rarity = 2
+base_catch_weight = 0.24
+catch_profile = ExtResource("2_profile")
+availability = SubResource("Availability")
+weight_min_lb = 0.5
+weight_max_lb = 18.0
+sell_value_min = 12
+sell_value_max = 28
+sell_value_curve = 0.86
+display_texture = ExtResource("99_fish_placeholder")
diff --git a/fish/species/butterflyfish_copperband/butterflyfish_copperband.tres b/fish/species/butterflyfish_copperband/butterflyfish_copperband.tres
new file mode 100644
index 0000000..7a7c112
--- /dev/null
+++ b/fish/species/butterflyfish_copperband/butterflyfish_copperband.tres
@@ -0,0 +1,32 @@
+[gd_resource type="Resource" script_class="FishData" load_steps=5 format=3]
+
+[ext_resource type="Script" 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" path="res://fish/fish_availability.gd" id="3_availability"]
+[ext_resource type="Texture2D" path="res://art/exported/creatures/fish_placeholder.png" id="99_fish_placeholder"]
+
+[sub_resource type="Resource" id="Availability"]
+script = ExtResource("3_availability")
+allow_night = false
+forbid_fog = true
+
+[resource]
+script = ExtResource("1_fish_data")
+id = &"butterflyfish_copperband"
+display_name = "copperband butterflyfish"
+active = true
+catalog_number = 5701
+collection_group = &"Reef ornamentals"
+logbook_fact = "copperband butterflyfish probe reef cracks with long narrow snouts. bold orange bands and a false eyespot confuse predators."
+available_seasons = 15
+allowed_water_types = 2
+rarity = 2
+base_catch_weight = 0.32
+catch_profile = ExtResource("2_profile")
+availability = SubResource("Availability")
+weight_min_lb = 0.03
+weight_max_lb = 0.4
+sell_value_min = 9
+sell_value_max = 21
+sell_value_curve = 0.98
+display_texture = ExtResource("99_fish_placeholder")
diff --git a/fish/species/capelin/capelin.tres b/fish/species/capelin/capelin.tres
new file mode 100644
index 0000000..5759cb8
--- /dev/null
+++ b/fish/species/capelin/capelin.tres
@@ -0,0 +1,31 @@
+[gd_resource type="Resource" script_class="FishData" load_steps=5 format=3]
+
+[ext_resource type="Script" 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" path="res://fish/fish_availability.gd" id="3_availability"]
+[ext_resource type="Texture2D" path="res://art/exported/creatures/fish_placeholder.png" id="99_fish_placeholder"]
+
+[sub_resource type="Resource" id="Availability"]
+script = ExtResource("3_availability")
+require_fog = true
+
+[resource]
+script = ExtResource("1_fish_data")
+id = &"capelin"
+display_name = "capelin"
+active = true
+catalog_number = 1302
+collection_group = &"Cold schooling fish"
+logbook_fact = "capelin are small polar fish that feed whales, seabirds, and larger fish. many schools roll onto beaches or shallows to release their eggs."
+available_seasons = 13
+allowed_water_types = 2
+rarity = 1
+base_catch_weight = 1.0
+catch_profile = ExtResource("2_profile")
+availability = SubResource("Availability")
+weight_min_lb = 0.03
+weight_max_lb = 0.2
+sell_value_min = 4
+sell_value_max = 8
+sell_value_curve = 1.0
+display_texture = ExtResource("99_fish_placeholder")
diff --git a/fish/species/carp/carp.tres b/fish/species/carp/carp.tres
index 5673834..24b50e7 100644
--- a/fish/species/carp/carp.tres
+++ b/fish/species/carp/carp.tres
@@ -3,7 +3,7 @@
[ext_resource type="Script" uid="uid://h7hg45b7wmst" path="res://fish/fish_data.gd" id="1_fish_data"]
[ext_resource type="Resource" path="res://fishing/carp_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://c6pa1gqwv1a5s" path="res://fish/species/carp/fish_carp_common.png" id="4_texture"]
+[ext_resource type="Texture2D" path="res://art/exported/creatures/fish_placeholder.png" id="4_texture"]
[sub_resource type="Resource" id="CarpAvailability"]
script = ExtResource("3_availability_script")
@@ -14,6 +14,11 @@ preferred_bait_weight_multiplier = 1.5
script = ExtResource("1_fish_data")
id = &"carp"
display_name = "common carp"
+active = true
+catalog_number = 2503
+collection_group = &"Freshwater coarse fish"
+logbook_fact = "whisker-like barbels help a common carp investigate the bottom. nosing through sediment can cloud the water and loosen plants."
+available_seasons = 15
allowed_water_types = 1
rarity = 2
base_catch_weight = 1.5
diff --git a/fish/species/carp/fish_carp_common.png b/fish/species/carp/fish_carp_common.png
index 709f4d4..3a98f8e 100644
Binary files a/fish/species/carp/fish_carp_common.png and b/fish/species/carp/fish_carp_common.png differ
diff --git a/fish/species/catfish_blue/catfish_blue.tres b/fish/species/catfish_blue/catfish_blue.tres
index 482520a..8df5866 100644
--- a/fish/species/catfish_blue/catfish_blue.tres
+++ b/fish/species/catfish_blue/catfish_blue.tres
@@ -3,11 +3,11 @@
[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://7f1dtcou8moe" path="res://fish/species/catfish_blue/fish_catfish_blue.png" id="4_texture"]
+[ext_resource type="Texture2D" path="res://art/exported/creatures/fish_placeholder.png" id="4_texture"]
[sub_resource type="Resource" id="BlueCatfishAvailability"]
script = ExtResource("3_availability_script")
-allowed_location_tags = Array[StringName]([&"starter_pond"])
+allowed_location_tags = Array[StringName]([&"river"])
allow_day = false
preferred_bait_tags = Array[StringName]([&"worm", &"minnow"])
preferred_bait_weight_multiplier = 1.35
@@ -16,6 +16,11 @@ preferred_bait_weight_multiplier = 1.35
script = ExtResource("1_fish_data")
id = &"catfish_blue"
display_name = "blue catfish"
+active = true
+catalog_number = 2402
+collection_group = &"Freshwater catfish"
+logbook_fact = "a deep fork in the tail inspired the blue catfish's scientific name. it rests deep by daylight and becomes more active after dark."
+available_seasons = 7
allowed_water_types = 1
rarity = 1
base_catch_weight = 1.25
diff --git a/fish/species/catfish_blue/fish_catfish_blue.png b/fish/species/catfish_blue/fish_catfish_blue.png
index 6482e15..73511b6 100644
Binary files a/fish/species/catfish_blue/fish_catfish_blue.png and b/fish/species/catfish_blue/fish_catfish_blue.png differ
diff --git a/fish/species/catfish_channel/catfish_channel.tres b/fish/species/catfish_channel/catfish_channel.tres
index 265daad..e72fdb0 100644
--- a/fish/species/catfish_channel/catfish_channel.tres
+++ b/fish/species/catfish_channel/catfish_channel.tres
@@ -3,11 +3,11 @@
[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://dvxpk2ltt3blb" path="res://fish/species/catfish_channel/fish_catfish_channel.png" id="4_texture"]
+[ext_resource type="Texture2D" path="res://art/exported/creatures/fish_placeholder.png" id="4_texture"]
[sub_resource type="Resource" id="ChannelCatfishAvailability"]
script = ExtResource("3_availability_script")
-allowed_location_tags = Array[StringName]([&"starter_pond"])
+allowed_location_tags = Array[StringName]([&"starter_pond", &"pond"])
allow_day = false
preferred_bait_tags = Array[StringName]([&"worm"])
preferred_bait_weight_multiplier = 1.35
@@ -16,6 +16,11 @@ preferred_bait_weight_multiplier = 1.35
script = ExtResource("1_fish_data")
id = &"catfish_channel"
display_name = "channel catfish"
+active = true
+catalog_number = 2404
+collection_group = &"Freshwater catfish"
+logbook_fact = "a channel catfish can taste through skin all over its body, especially near its gills and whiskers. the male watches the eggs."
+available_seasons = 7
allowed_water_types = 1
base_catch_weight = 2.0
catch_profile = ExtResource("2_profile")
diff --git a/fish/species/catfish_channel/fish_catfish_channel.png b/fish/species/catfish_channel/fish_catfish_channel.png
index b858806..566955a 100644
Binary files a/fish/species/catfish_channel/fish_catfish_channel.png and b/fish/species/catfish_channel/fish_catfish_channel.png differ
diff --git a/fish/species/catfish_flathead/catfish_flathead.tres b/fish/species/catfish_flathead/catfish_flathead.tres
index 651359a..de0e5f6 100644
--- a/fish/species/catfish_flathead/catfish_flathead.tres
+++ b/fish/species/catfish_flathead/catfish_flathead.tres
@@ -3,11 +3,11 @@
[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://bacx63lp0yh7t" path="res://fish/species/catfish_flathead/fish_catfish_flathead.png" id="4_texture"]
+[ext_resource type="Texture2D" path="res://art/exported/creatures/fish_placeholder.png" id="4_texture"]
[sub_resource type="Resource" id="FlatheadCatfishAvailability"]
script = ExtResource("3_availability_script")
-allowed_location_tags = Array[StringName]([&"starter_pond"])
+allowed_location_tags = Array[StringName]([&"river"])
allow_day = false
preferred_bait_tags = Array[StringName]([&"worm", &"minnow"])
preferred_bait_weight_multiplier = 1.35
@@ -16,6 +16,11 @@ preferred_bait_weight_multiplier = 1.35
script = ExtResource("1_fish_data")
id = &"catfish_flathead"
display_name = "flathead catfish"
+active = true
+catalog_number = 2405
+collection_group = &"Freshwater catfish"
+logbook_fact = "flatheads favor hideouts made by logs, rocks, ledges, and other underwater structure. as they grow, fish dominate their menu."
+available_seasons = 7
allowed_water_types = 1
rarity = 1
catch_profile = ExtResource("2_profile")
diff --git a/fish/species/catfish_flathead/fish_catfish_flathead.png b/fish/species/catfish_flathead/fish_catfish_flathead.png
index e248c02..0122fda 100644
Binary files a/fish/species/catfish_flathead/fish_catfish_flathead.png and b/fish/species/catfish_flathead/fish_catfish_flathead.png differ
diff --git a/fish/species/catfish_walking/catfish_walking.tres b/fish/species/catfish_walking/catfish_walking.tres
new file mode 100644
index 0000000..fe36cee
--- /dev/null
+++ b/fish/species/catfish_walking/catfish_walking.tres
@@ -0,0 +1,32 @@
+[gd_resource type="Resource" script_class="FishData" load_steps=5 format=3]
+
+[ext_resource type="Script" 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" path="res://fish/fish_availability.gd" id="3_availability"]
+[ext_resource type="Texture2D" path="res://art/exported/creatures/fish_placeholder.png" id="99_fish_placeholder"]
+
+[sub_resource type="Resource" id="Availability"]
+script = ExtResource("3_availability")
+allow_day = false
+require_rain = true
+
+[resource]
+script = ExtResource("1_fish_data")
+id = &"catfish_walking"
+display_name = "walking catfish"
+active = true
+catalog_number = 7510
+collection_group = &"Tropical freshwater fish"
+logbook_fact = "walking catfish wriggle across wet ground using stiff pectoral spines for support. an air-breathing organ helps them survive the journey."
+available_seasons = 15
+allowed_water_types = 1
+rarity = 2
+base_catch_weight = 0.3
+catch_profile = ExtResource("2_profile")
+availability = SubResource("Availability")
+weight_min_lb = 0.5
+weight_max_lb = 5.0
+sell_value_min = 11
+sell_value_max = 24
+sell_value_curve = 0.9
+display_texture = ExtResource("99_fish_placeholder")
diff --git a/fish/species/catfish_white/catfish_white.tres b/fish/species/catfish_white/catfish_white.tres
index 6ee72ea..86b799f 100644
--- a/fish/species/catfish_white/catfish_white.tres
+++ b/fish/species/catfish_white/catfish_white.tres
@@ -3,11 +3,11 @@
[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://bf6kiawwekv7g" path="res://fish/species/catfish_white/fish_catfish_white.png" id="4_texture"]
+[ext_resource type="Texture2D" path="res://art/exported/creatures/fish_placeholder.png" id="4_texture"]
[sub_resource type="Resource" id="WhiteCatfishAvailability"]
script = ExtResource("3_availability_script")
-allowed_location_tags = Array[StringName]([&"starter_pond"])
+allowed_location_tags = Array[StringName]([&"starter_pond", &"pond"])
allow_day = false
preferred_bait_tags = Array[StringName]([&"worm"])
preferred_bait_weight_multiplier = 1.35
@@ -16,6 +16,11 @@ preferred_bait_weight_multiplier = 1.35
script = ExtResource("1_fish_data")
id = &"catfish_white"
display_name = "white catfish"
+active = true
+catalog_number = 2407
+collection_group = &"Freshwater catfish"
+logbook_fact = "white catfish began along the atlantic coast between new york and florida. people later carried them far beyond that home range."
+available_seasons = 7
allowed_water_types = 1
base_catch_weight = 2.0
catch_profile = ExtResource("2_profile")
diff --git a/fish/species/catfish_white/fish_catfish_white.png b/fish/species/catfish_white/fish_catfish_white.png
index 499be44..bdcb730 100644
Binary files a/fish/species/catfish_white/fish_catfish_white.png and b/fish/species/catfish_white/fish_catfish_white.png differ
diff --git a/fish/species/char_arctic/char_arctic.tres b/fish/species/char_arctic/char_arctic.tres
new file mode 100644
index 0000000..17d08d1
--- /dev/null
+++ b/fish/species/char_arctic/char_arctic.tres
@@ -0,0 +1,31 @@
+[gd_resource type="Resource" script_class="FishData" load_steps=5 format=3]
+
+[ext_resource type="Script" 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" path="res://fish/fish_availability.gd" id="3_availability"]
+[ext_resource type="Texture2D" path="res://art/exported/creatures/fish_placeholder.png" id="99_fish_placeholder"]
+
+[sub_resource type="Resource" id="Availability"]
+script = ExtResource("3_availability")
+forbid_rain = true
+
+[resource]
+script = ExtResource("1_fish_data")
+id = &"char_arctic"
+display_name = "arctic char"
+active = true
+catalog_number = 1101
+collection_group = &"Cold freshwater salmonids"
+logbook_fact = "arctic char thrive farther north than any other freshwater fish. some remain in lakes while others migrate between rivers and the sea."
+available_seasons = 13
+allowed_water_types = 3
+rarity = 2
+base_catch_weight = 0.3
+catch_profile = ExtResource("2_profile")
+availability = SubResource("Availability")
+weight_min_lb = 1.0
+weight_max_lb = 25.0
+sell_value_min = 14
+sell_value_max = 32
+sell_value_curve = 0.86
+display_texture = ExtResource("99_fish_placeholder")
diff --git a/fish/species/chromis_blue_green/chromis_blue_green.tres b/fish/species/chromis_blue_green/chromis_blue_green.tres
new file mode 100644
index 0000000..65f35b4
--- /dev/null
+++ b/fish/species/chromis_blue_green/chromis_blue_green.tres
@@ -0,0 +1,31 @@
+[gd_resource type="Resource" script_class="FishData" load_steps=5 format=3]
+
+[ext_resource type="Script" 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" path="res://fish/fish_availability.gd" id="3_availability"]
+[ext_resource type="Texture2D" path="res://art/exported/creatures/5901.png" id="99_fish_placeholder"]
+
+[sub_resource type="Resource" id="Availability"]
+script = ExtResource("3_availability")
+allow_night = false
+
+[resource]
+script = ExtResource("1_fish_data")
+id = &"chromis_blue_green"
+display_name = "blue-green chromis"
+active = true
+catalog_number = 5901
+collection_group = &"Reef schooling fish"
+logbook_fact = "blue-green chromis form loose clouds above branching coral. the school dives into narrow shelter whenever a predator approaches."
+available_seasons = 15
+allowed_water_types = 2
+rarity = 0
+base_catch_weight = 2.8
+catch_profile = ExtResource("2_profile")
+availability = SubResource("Availability")
+weight_min_lb = 0.01
+weight_max_lb = 0.25
+sell_value_min = 2
+sell_value_max = 4
+sell_value_curve = 1.0
+display_texture = ExtResource("99_fish_placeholder")
diff --git a/fish/species/chub_creek/chub_creek.tres b/fish/species/chub_creek/chub_creek.tres
new file mode 100644
index 0000000..778e9da
--- /dev/null
+++ b/fish/species/chub_creek/chub_creek.tres
@@ -0,0 +1,31 @@
+[gd_resource type="Resource" script_class="FishData" load_steps=5 format=3]
+
+[ext_resource type="Script" 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" path="res://fish/fish_availability.gd" id="3_availability"]
+[ext_resource type="Texture2D" path="res://art/exported/creatures/2802.png" id="99_fish_placeholder"]
+
+[sub_resource type="Resource" id="Availability"]
+script = ExtResource("3_availability")
+allow_night = false
+
+[resource]
+script = ExtResource("1_fish_data")
+id = &"chub_creek"
+display_name = "creek chub"
+active = true
+catalog_number = 2802
+collection_group = &"Freshwater minnows"
+logbook_fact = "creek chubs gather around pools and undercut banks in small streams. males build gravel ridges where several fish may spawn."
+available_seasons = 7
+allowed_water_types = 1
+rarity = 0
+base_catch_weight = 3.2
+catch_profile = ExtResource("2_profile")
+availability = SubResource("Availability")
+weight_min_lb = 0.05
+weight_max_lb = 0.8
+sell_value_min = 2
+sell_value_max = 4
+sell_value_curve = 1.0
+display_texture = ExtResource("99_fish_placeholder")
diff --git a/fish/species/chub_european/chub_european.tres b/fish/species/chub_european/chub_european.tres
index e282b90..16f43a1 100644
--- a/fish/species/chub_european/chub_european.tres
+++ b/fish/species/chub_european/chub_european.tres
@@ -3,16 +3,21 @@
[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"]
+[ext_resource type="Texture2D" path="res://art/exported/creatures/2505.png" id="4_texture"]
[sub_resource type="Resource" id="ChubEuropeanAvailability"]
script = ExtResource("3_availability_script")
-allowed_location_tags = Array[StringName]([&"starter_pond"])
+allowed_location_tags = Array[StringName]([&"river"])
[resource]
script = ExtResource("1_fish_data")
id = &"chub_european"
display_name = "european chub"
+active = true
+catalog_number = 2505
+collection_group = &"Freshwater coarse fish"
+logbook_fact = "european chub are adaptable river fish. adults often patrol shallows and feed on insects, small fish, and fruit."
+available_seasons = 15
allowed_water_types = 1
base_catch_weight = 0.55
catch_profile = ExtResource("2_profile")
diff --git a/fish/species/chub_european/fish_chub_european.png b/fish/species/chub_european/fish_chub_european.png
index 03ed536..e850ada 100644
Binary files a/fish/species/chub_european/fish_chub_european.png and b/fish/species/chub_european/fish_chub_european.png differ
diff --git a/fish/species/chub_flame/chub_flame.tres b/fish/species/chub_flame/chub_flame.tres
index d631ab0..2bf815c 100644
--- a/fish/species/chub_flame/chub_flame.tres
+++ b/fish/species/chub_flame/chub_flame.tres
@@ -3,16 +3,21 @@
[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"]
+[ext_resource type="Texture2D" path="res://art/exported/creatures/fish_placeholder.png" id="4_texture"]
[sub_resource type="Resource" id="ChubFlameAvailability"]
script = ExtResource("3_availability_script")
-allowed_location_tags = Array[StringName]([&"starter_pond"])
+allowed_location_tags = Array[StringName]([&"river"])
[resource]
script = ExtResource("1_fish_data")
id = &"chub_flame"
display_name = "flame chub"
+active = true
+catalog_number = 2805
+collection_group = &"Freshwater minnows"
+logbook_fact = "flame chubs prefer cool, clear streams. their bright breeding colors make a small fish easy to spot in spring water."
+available_seasons = 7
allowed_water_types = 1
rarity = 1
base_catch_weight = 0.4
diff --git a/fish/species/chub_flame/fish_chub_flame.png b/fish/species/chub_flame/fish_chub_flame.png
index 98c67e3..a94fc71 100644
Binary files a/fish/species/chub_flame/fish_chub_flame.png and b/fish/species/chub_flame/fish_chub_flame.png differ
diff --git a/fish/species/chub_lake/chub_lake.tres b/fish/species/chub_lake/chub_lake.tres
index c04c5b3..1dc2d9a 100644
--- a/fish/species/chub_lake/chub_lake.tres
+++ b/fish/species/chub_lake/chub_lake.tres
@@ -3,16 +3,21 @@
[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"]
+[ext_resource type="Texture2D" path="res://art/exported/creatures/2807.png" id="4_texture"]
[sub_resource type="Resource" id="ChubLakeAvailability"]
script = ExtResource("3_availability_script")
-allowed_location_tags = Array[StringName]([&"starter_pond"])
+allowed_location_tags = Array[StringName]([&"lake"])
[resource]
script = ExtResource("1_fish_data")
id = &"chub_lake"
display_name = "lake chub"
+active = true
+catalog_number = 2807
+collection_group = &"Freshwater minnows"
+logbook_fact = "lake chubs school in cold northern water and use their small mouths to pick insects and other tiny prey from the current."
+available_seasons = 13
allowed_water_types = 1
base_catch_weight = 0.55
catch_profile = ExtResource("2_profile")
diff --git a/fish/species/chub_lake/fish_chub_lake.png b/fish/species/chub_lake/fish_chub_lake.png
index 73101f6..6aac214 100644
Binary files a/fish/species/chub_lake/fish_chub_lake.png and b/fish/species/chub_lake/fish_chub_lake.png differ
diff --git a/fish/species/cisco/cisco.tres b/fish/species/cisco/cisco.tres
new file mode 100644
index 0000000..1bc53b0
--- /dev/null
+++ b/fish/species/cisco/cisco.tres
@@ -0,0 +1,31 @@
+[gd_resource type="Resource" script_class="FishData" load_steps=5 format=3]
+
+[ext_resource type="Script" 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" path="res://fish/fish_availability.gd" id="3_availability"]
+[ext_resource type="Texture2D" path="res://art/exported/creatures/fish_placeholder.png" id="99_fish_placeholder"]
+
+[sub_resource type="Resource" id="Availability"]
+script = ExtResource("3_availability")
+allow_night = false
+
+[resource]
+script = ExtResource("1_fish_data")
+id = &"cisco"
+display_name = "cisco"
+active = true
+catalog_number = 1002
+collection_group = &"Cold freshwater fish"
+logbook_fact = "cisco school in the cool open water of northern lakes. these slim relatives of salmon feed on plankton and become prey for larger fish."
+available_seasons = 13
+allowed_water_types = 1
+rarity = 0
+base_catch_weight = 2.2
+catch_profile = ExtResource("2_profile")
+availability = SubResource("Availability")
+weight_min_lb = 0.1
+weight_max_lb = 3.0
+sell_value_min = 3
+sell_value_max = 7
+sell_value_curve = 0.95
+display_texture = ExtResource("99_fish_placeholder")
diff --git a/fish/species/clam_geoduck/clam_geoduck.tres b/fish/species/clam_geoduck/clam_geoduck.tres
new file mode 100644
index 0000000..492113c
--- /dev/null
+++ b/fish/species/clam_geoduck/clam_geoduck.tres
@@ -0,0 +1,32 @@
+[gd_resource type="Resource" script_class="FishData" load_steps=5 format=3]
+
+[ext_resource type="Script" 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" path="res://fish/fish_availability.gd" id="3_availability"]
+[ext_resource type="Texture2D" path="res://art/exported/creatures/fish_placeholder.png" id="99_fish_placeholder"]
+
+[sub_resource type="Resource" id="Availability"]
+script = ExtResource("3_availability")
+allow_night = false
+require_fog = true
+
+[resource]
+script = ExtResource("1_fish_data")
+id = &"clam_geoduck"
+display_name = "Pacific geoduck"
+active = true
+catalog_number = 6403
+collection_group = &"Shellfish"
+logbook_fact = "pacific geoducks bury their shells deep while a long siphon reaches the seafloor. these clams can survive for well over a century."
+available_seasons = 15
+allowed_water_types = 2
+rarity = 2
+base_catch_weight = 0.25
+catch_profile = ExtResource("2_profile")
+availability = SubResource("Availability")
+weight_min_lb = 0.5
+weight_max_lb = 15.0
+sell_value_min = 14
+sell_value_max = 34
+sell_value_curve = 0.88
+display_texture = ExtResource("99_fish_placeholder")
diff --git a/fish/species/clam_giant/clam_giant.tres b/fish/species/clam_giant/clam_giant.tres
new file mode 100644
index 0000000..2b4f59d
--- /dev/null
+++ b/fish/species/clam_giant/clam_giant.tres
@@ -0,0 +1,32 @@
+[gd_resource type="Resource" script_class="FishData" load_steps=5 format=3]
+
+[ext_resource type="Script" 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" path="res://fish/fish_availability.gd" id="3_availability"]
+[ext_resource type="Texture2D" path="res://art/exported/creatures/fish_placeholder.png" id="99_fish_placeholder"]
+
+[sub_resource type="Resource" id="Availability"]
+script = ExtResource("3_availability")
+allow_night = false
+forbid_fog = true
+
+[resource]
+script = ExtResource("1_fish_data")
+id = &"clam_giant"
+display_name = "giant clam"
+active = true
+catalog_number = 6402
+collection_group = &"Shellfish"
+logbook_fact = "giant clams host photosynthetic algae inside brightly colored mantles. heavy shells remain anchored to one reef spot throughout adulthood."
+available_seasons = 15
+allowed_water_types = 2
+rarity = 3
+base_catch_weight = 0.1
+catch_profile = ExtResource("2_profile")
+availability = SubResource("Availability")
+weight_min_lb = 5.0
+weight_max_lb = 500.0
+sell_value_min = 28
+sell_value_max = 72
+sell_value_curve = 0.8
+display_texture = ExtResource("99_fish_placeholder")
diff --git a/fish/species/clam_manila/clam_manila.png b/fish/species/clam_manila/clam_manila.png
new file mode 100644
index 0000000..72f5bfb
Binary files /dev/null and b/fish/species/clam_manila/clam_manila.png differ
diff --git a/ui/assets/title/netfishing_logo.png.import b/fish/species/clam_manila/clam_manila.png.import
similarity index 72%
rename from ui/assets/title/netfishing_logo.png.import
rename to fish/species/clam_manila/clam_manila.png.import
index 2aa351a..6e8d0bc 100644
--- a/ui/assets/title/netfishing_logo.png.import
+++ b/fish/species/clam_manila/clam_manila.png.import
@@ -2,16 +2,16 @@
importer="texture"
type="CompressedTexture2D"
-uid="uid://ckqayis1ckb2v"
-path="res://.godot/imported/netfishing_logo.png-82ab9fce5851dcc5239eca178e8cde99.ctex"
+uid="uid://tmbkp1ohcavc"
+path="res://.godot/imported/clam_manila.png-b04bc787291fc96749979a40380ba399.ctex"
metadata={
"vram_texture": false
}
[deps]
-source_file="res://ui/assets/title/netfishing_logo.png"
-dest_files=["res://.godot/imported/netfishing_logo.png-82ab9fce5851dcc5239eca178e8cde99.ctex"]
+source_file="res://fish/species/clam_manila/clam_manila.png"
+dest_files=["res://.godot/imported/clam_manila.png-b04bc787291fc96749979a40380ba399.ctex"]
[params]
diff --git a/fish/species/clam_manila/clam_manila.tres b/fish/species/clam_manila/clam_manila.tres
new file mode 100644
index 0000000..2b3fe73
--- /dev/null
+++ b/fish/species/clam_manila/clam_manila.tres
@@ -0,0 +1,28 @@
+[gd_resource type="Resource" script_class="FishData" load_steps=4 format=3]
+
+[ext_resource type="Script" 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="Texture2D" path="res://art/exported/creatures/6406.png" id="3_texture"]
+
+[resource]
+script = ExtResource("1_fish_data")
+id = &"clam_manila"
+display_name = "Manila clam"
+active = true
+catalog_number = 6406
+collection_group = &"Shellfish"
+logbook_fact = "manila clams bury themselves in wet coastal sand and reveal their hiding places with small spurts of water."
+collection_method = 2
+logbook_section = 1
+habitat_label = "wet beach sand"
+available_seasons = 15
+allowed_water_types = 2
+rarity = 0
+base_catch_weight = 1.0
+catch_profile = ExtResource("2_profile")
+weight_min_lb = 0.05
+weight_max_lb = 0.3
+sell_value_min = 3
+sell_value_max = 8
+sell_value_curve = 1.0
+display_texture = ExtResource("3_texture")
diff --git a/fish/species/clownfish_ocellaris/clownfish_ocellaris.tres b/fish/species/clownfish_ocellaris/clownfish_ocellaris.tres
new file mode 100644
index 0000000..16566cb
--- /dev/null
+++ b/fish/species/clownfish_ocellaris/clownfish_ocellaris.tres
@@ -0,0 +1,31 @@
+[gd_resource type="Resource" script_class="FishData" load_steps=5 format=3]
+
+[ext_resource type="Script" 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" path="res://fish/fish_availability.gd" id="3_availability"]
+[ext_resource type="Texture2D" path="res://art/exported/creatures/5702.png" id="99_fish_placeholder"]
+
+[sub_resource type="Resource" id="Availability"]
+script = ExtResource("3_availability")
+allow_night = false
+
+[resource]
+script = ExtResource("1_fish_data")
+id = &"clownfish_ocellaris"
+display_name = "ocellaris clownfish"
+active = true
+catalog_number = 5702
+collection_group = &"Reef ornamentals"
+logbook_fact = "ocellaris clownfish shelter safely among stinging anemone tentacles. each group is led by a female that develops from the largest male."
+available_seasons = 15
+allowed_water_types = 2
+rarity = 1
+base_catch_weight = 1.0
+catch_profile = ExtResource("2_profile")
+availability = SubResource("Availability")
+weight_min_lb = 0.01
+weight_max_lb = 0.15
+sell_value_min = 5
+sell_value_max = 10
+sell_value_curve = 1.0
+display_texture = ExtResource("99_fish_placeholder")
diff --git a/fish/species/cobia/cobia.tres b/fish/species/cobia/cobia.tres
new file mode 100644
index 0000000..51dec82
--- /dev/null
+++ b/fish/species/cobia/cobia.tres
@@ -0,0 +1,31 @@
+[gd_resource type="Resource" script_class="FishData" load_steps=5 format=3]
+
+[ext_resource type="Script" 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" path="res://fish/fish_availability.gd" id="3_availability"]
+[ext_resource type="Texture2D" path="res://art/exported/creatures/fish_placeholder.png" id="99_fish_placeholder"]
+
+[sub_resource type="Resource" id="Availability"]
+script = ExtResource("3_availability")
+allow_night = false
+
+[resource]
+script = ExtResource("1_fish_data")
+id = &"cobia"
+display_name = "cobia"
+active = true
+catalog_number = 3602
+collection_group = &"Large coastal predators"
+logbook_fact = "cobia often follow sharks, rays, and floating structures in warm seas. a broad dark stripe runs along their long, powerful bodies."
+available_seasons = 6
+allowed_water_types = 2
+rarity = 2
+base_catch_weight = 0.24
+catch_profile = ExtResource("2_profile")
+availability = SubResource("Availability")
+weight_min_lb = 5.0
+weight_max_lb = 80.0
+sell_value_min = 15
+sell_value_max = 40
+sell_value_curve = 0.82
+display_texture = ExtResource("99_fish_placeholder")
diff --git a/fish/species/cod_atlantic/cod_atlantic.tres b/fish/species/cod_atlantic/cod_atlantic.tres
new file mode 100644
index 0000000..640ccaa
--- /dev/null
+++ b/fish/species/cod_atlantic/cod_atlantic.tres
@@ -0,0 +1,30 @@
+[gd_resource type="Resource" script_class="FishData" load_steps=5 format=3]
+
+[ext_resource type="Script" 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" path="res://fish/fish_availability.gd" id="3_availability"]
+[ext_resource type="Texture2D" path="res://art/exported/creatures/fish_placeholder.png" id="99_fish_placeholder"]
+
+[sub_resource type="Resource" id="Availability"]
+script = ExtResource("3_availability")
+
+[resource]
+script = ExtResource("1_fish_data")
+id = &"cod_atlantic"
+display_name = "atlantic cod"
+active = true
+catalog_number = 1402
+collection_group = &"Cold shelf fish"
+logbook_fact = "atlantic cod carry a pale lateral line and one chin barbel. they search cold seafloors for crustaceans, mollusks, and smaller fish."
+available_seasons = 13
+allowed_water_types = 2
+rarity = 0
+base_catch_weight = 1.8
+catch_profile = ExtResource("2_profile")
+availability = SubResource("Availability")
+weight_min_lb = 1.0
+weight_max_lb = 55.0
+sell_value_min = 5
+sell_value_max = 18
+sell_value_curve = 0.85
+display_texture = ExtResource("99_fish_placeholder")
diff --git a/fish/species/cod_pacific/cod_pacific.tres b/fish/species/cod_pacific/cod_pacific.tres
new file mode 100644
index 0000000..729acc1
--- /dev/null
+++ b/fish/species/cod_pacific/cod_pacific.tres
@@ -0,0 +1,30 @@
+[gd_resource type="Resource" script_class="FishData" load_steps=5 format=3]
+
+[ext_resource type="Script" 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" path="res://fish/fish_availability.gd" id="3_availability"]
+[ext_resource type="Texture2D" path="res://art/exported/creatures/fish_placeholder.png" id="99_fish_placeholder"]
+
+[sub_resource type="Resource" id="Availability"]
+script = ExtResource("3_availability")
+
+[resource]
+script = ExtResource("1_fish_data")
+id = &"cod_pacific"
+display_name = "pacific cod"
+active = true
+catalog_number = 1406
+collection_group = &"Cold shelf fish"
+logbook_fact = "pacific cod use a chin barbel to investigate cold shelf bottoms. seasonal schools follow prey between deeper water and coastal spawning grounds."
+available_seasons = 13
+allowed_water_types = 2
+rarity = 0
+base_catch_weight = 1.8
+catch_profile = ExtResource("2_profile")
+availability = SubResource("Availability")
+weight_min_lb = 1.0
+weight_max_lb = 45.0
+sell_value_min = 5
+sell_value_max = 17
+sell_value_curve = 0.85
+display_texture = ExtResource("99_fish_placeholder")
diff --git a/fish/species/coelacanth_west_indian/coelacanth_west_indian.tres b/fish/species/coelacanth_west_indian/coelacanth_west_indian.tres
new file mode 100644
index 0000000..8b7bf18
--- /dev/null
+++ b/fish/species/coelacanth_west_indian/coelacanth_west_indian.tres
@@ -0,0 +1,32 @@
+[gd_resource type="Resource" script_class="FishData" load_steps=5 format=3]
+
+[ext_resource type="Script" 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" path="res://fish/fish_availability.gd" id="3_availability"]
+[ext_resource type="Texture2D" path="res://art/exported/creatures/fish_placeholder.png" id="99_fish_placeholder"]
+
+[sub_resource type="Resource" id="Availability"]
+script = ExtResource("3_availability")
+allow_day = false
+require_fog = true
+
+[resource]
+script = ExtResource("1_fish_data")
+id = &"coelacanth_west_indian"
+display_name = "West Indian Ocean coelacanth"
+active = true
+catalog_number = 1702
+collection_group = &"Deep-sea legends"
+logbook_fact = "west indian ocean coelacanths shelter in deep volcanic caves by day. fleshy lobed fins move in an alternating, limb-like rhythm."
+available_seasons = 15
+allowed_water_types = 2
+rarity = 4
+base_catch_weight = 0.03
+catch_profile = ExtResource("2_profile")
+availability = SubResource("Availability")
+weight_min_lb = 20.0
+weight_max_lb = 200.0
+sell_value_min = 65
+sell_value_max = 145
+sell_value_curve = 0.8
+display_texture = ExtResource("99_fish_placeholder")
diff --git a/fish/species/conch_queen/conch_queen.tres b/fish/species/conch_queen/conch_queen.tres
new file mode 100644
index 0000000..045012b
--- /dev/null
+++ b/fish/species/conch_queen/conch_queen.tres
@@ -0,0 +1,32 @@
+[gd_resource type="Resource" script_class="FishData" load_steps=5 format=3]
+
+[ext_resource type="Script" 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" path="res://fish/fish_availability.gd" id="3_availability"]
+[ext_resource type="Texture2D" path="res://art/exported/creatures/fish_placeholder.png" id="99_fish_placeholder"]
+
+[sub_resource type="Resource" id="Availability"]
+script = ExtResource("3_availability")
+allow_night = false
+forbid_fog = true
+
+[resource]
+script = ExtResource("1_fish_data")
+id = &"conch_queen"
+display_name = "queen conch"
+active = true
+catalog_number = 6404
+collection_group = &"Shellfish"
+logbook_fact = "queen conchs graze algae with a long flexible snout. adults develop a broad flared shell lip and move by vaulting on one muscular foot."
+available_seasons = 15
+allowed_water_types = 2
+rarity = 2
+base_catch_weight = 0.24
+catch_profile = ExtResource("2_profile")
+availability = SubResource("Availability")
+weight_min_lb = 1.0
+weight_max_lb = 5.0
+sell_value_min = 15
+sell_value_max = 36
+sell_value_curve = 0.91
+display_texture = ExtResource("99_fish_placeholder")
diff --git a/fish/species/cornetfish_red/cornetfish_red.tres b/fish/species/cornetfish_red/cornetfish_red.tres
new file mode 100644
index 0000000..ea750bd
--- /dev/null
+++ b/fish/species/cornetfish_red/cornetfish_red.tres
@@ -0,0 +1,32 @@
+[gd_resource type="Resource" script_class="FishData" load_steps=5 format=3]
+
+[ext_resource type="Script" 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" path="res://fish/fish_availability.gd" id="3_availability"]
+[ext_resource type="Texture2D" path="res://art/exported/creatures/fish_placeholder.png" id="99_fish_placeholder"]
+
+[sub_resource type="Resource" id="Availability"]
+script = ExtResource("3_availability")
+allow_night = false
+require_fog = true
+
+[resource]
+script = ExtResource("1_fish_data")
+id = &"cornetfish_red"
+display_name = "red cornetfish"
+active = true
+catalog_number = 5606
+collection_group = &"Reef oddities"
+logbook_fact = "red cornetfish stalk reefs with extremely long bodies and tubular snouts. a trailing filament extends from the center of the tail."
+available_seasons = 15
+allowed_water_types = 2
+rarity = 3
+base_catch_weight = 0.11
+catch_profile = ExtResource("2_profile")
+availability = SubResource("Availability")
+weight_min_lb = 0.2
+weight_max_lb = 4.0
+sell_value_min = 24
+sell_value_max = 55
+sell_value_curve = 0.92
+display_texture = ExtResource("99_fish_placeholder")
diff --git a/fish/species/cowfish_longhorn/cowfish_longhorn.tres b/fish/species/cowfish_longhorn/cowfish_longhorn.tres
new file mode 100644
index 0000000..2a848a7
--- /dev/null
+++ b/fish/species/cowfish_longhorn/cowfish_longhorn.tres
@@ -0,0 +1,32 @@
+[gd_resource type="Resource" script_class="FishData" load_steps=5 format=3]
+
+[ext_resource type="Script" 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" path="res://fish/fish_availability.gd" id="3_availability"]
+[ext_resource type="Texture2D" path="res://art/exported/creatures/fish_placeholder.png" id="99_fish_placeholder"]
+
+[sub_resource type="Resource" id="Availability"]
+script = ExtResource("3_availability")
+allow_night = false
+require_fog = true
+
+[resource]
+script = ExtResource("1_fish_data")
+id = &"cowfish_longhorn"
+display_name = "longhorn cowfish"
+active = true
+catalog_number = 5604
+collection_group = &"Reef oddities"
+logbook_fact = "longhorn cowfish wear a rigid box of fused scales with horns above the eyes. small fins steer the angular body with surprising precision."
+available_seasons = 15
+allowed_water_types = 2
+rarity = 3
+base_catch_weight = 0.13
+catch_profile = ExtResource("2_profile")
+availability = SubResource("Availability")
+weight_min_lb = 0.1
+weight_max_lb = 2.0
+sell_value_min = 23
+sell_value_max = 52
+sell_value_curve = 0.94
+display_texture = ExtResource("99_fish_placeholder")
diff --git a/fish/species/crab_blue/crab_blue.tres b/fish/species/crab_blue/crab_blue.tres
new file mode 100644
index 0000000..b715522
--- /dev/null
+++ b/fish/species/crab_blue/crab_blue.tres
@@ -0,0 +1,36 @@
+[gd_resource type="Resource" script_class="FishData" load_steps=5 format=3]
+
+[ext_resource type="Script" 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" path="res://fish/fish_availability.gd" id="3_availability"]
+
+[ext_resource type="Texture2D" path="res://art/exported/creatures/3902.png" id="99_creature_texture"]
+
+[sub_resource type="Resource" id="Availability"]
+script = ExtResource("3_availability")
+allow_night = false
+
+[resource]
+script = ExtResource("1_fish_data")
+id = &"crab_blue"
+display_name = "blue crab"
+active = true
+catalog_number = 3902
+collection_group = &"Marine crabs"
+logbook_fact = "blue crabs swim with flattened rear legs shaped like paddles. bright blue claws and an olive shell conceal a quick, defensive temperament."
+collection_method = 1
+logbook_section = 1
+habitat_label = "shallow coastal water"
+available_seasons = 6
+allowed_water_types = 2
+rarity = 0
+base_catch_weight = 1.15
+catch_profile = ExtResource("2_profile")
+availability = SubResource("Availability")
+weight_min_lb = 0.1
+weight_max_lb = 1.1
+sell_value_min = 3
+sell_value_max = 8
+sell_value_curve = 0.95
+
+display_texture = ExtResource("99_creature_texture")
diff --git a/fish/species/crab_brown/crab_brown.png b/fish/species/crab_brown/crab_brown.png
new file mode 100644
index 0000000..a06f039
Binary files /dev/null and b/fish/species/crab_brown/crab_brown.png differ
diff --git a/items/icons/equipment/64_cooler_plus.png.import b/fish/species/crab_brown/crab_brown.png.import
similarity index 72%
rename from items/icons/equipment/64_cooler_plus.png.import
rename to fish/species/crab_brown/crab_brown.png.import
index 44e493a..9faf442 100644
--- a/items/icons/equipment/64_cooler_plus.png.import
+++ b/fish/species/crab_brown/crab_brown.png.import
@@ -2,16 +2,16 @@
importer="texture"
type="CompressedTexture2D"
-uid="uid://baht2hredfky8"
-path="res://.godot/imported/64_cooler_plus.png-d0daa39246f92fd9774ec1721970efad.ctex"
+uid="uid://dmn3rafvxtnb1"
+path="res://.godot/imported/crab_brown.png-4241f8408bf426d406e45f4cce16bbc8.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"]
+source_file="res://fish/species/crab_brown/crab_brown.png"
+dest_files=["res://.godot/imported/crab_brown.png-4241f8408bf426d406e45f4cce16bbc8.ctex"]
[params]
diff --git a/fish/species/crab_brown/crab_brown.tres b/fish/species/crab_brown/crab_brown.tres
new file mode 100644
index 0000000..7dec427
--- /dev/null
+++ b/fish/species/crab_brown/crab_brown.tres
@@ -0,0 +1,28 @@
+[gd_resource type="Resource" script_class="FishData" load_steps=4 format=3]
+
+[ext_resource type="Script" 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="Texture2D" path="res://art/exported/creatures/3906.png" id="3_texture"]
+
+[resource]
+script = ExtResource("1_fish_data")
+id = &"crab_brown"
+display_name = "brown crab"
+active = true
+catalog_number = 3906
+collection_group = &"Marine crabs"
+logbook_fact = "brown crabs hide among rocks and sand along sheltered coasts. broad claws help them feed on shellfish and other small shore animals."
+collection_method = 1
+logbook_section = 1
+habitat_label = "dry beach sand"
+available_seasons = 15
+allowed_water_types = 2
+rarity = 0
+base_catch_weight = 1.0
+catch_profile = ExtResource("2_profile")
+weight_min_lb = 0.5
+weight_max_lb = 3.0
+sell_value_min = 5
+sell_value_max = 15
+sell_value_curve = 1.0
+display_texture = ExtResource("3_texture")
diff --git a/fish/species/crab_dungeness/crab_dungeness.tres b/fish/species/crab_dungeness/crab_dungeness.tres
new file mode 100644
index 0000000..bb56819
--- /dev/null
+++ b/fish/species/crab_dungeness/crab_dungeness.tres
@@ -0,0 +1,33 @@
+[gd_resource type="Resource" script_class="FishData" load_steps=4 format=3]
+
+[ext_resource type="Script" 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" path="res://fish/fish_availability.gd" id="3_availability"]
+
+[sub_resource type="Resource" id="Availability"]
+script = ExtResource("3_availability")
+allow_night = false
+require_fog = true
+
+[resource]
+script = ExtResource("1_fish_data")
+id = &"crab_dungeness"
+display_name = "Dungeness crab"
+active = false
+catalog_number = 3903
+collection_group = &"Marine crabs"
+logbook_fact = "dungeness crabs bury beneath sand with only their eyes and antennae exposed. broad toothed shells protect them along cool pacific coasts."
+collection_method = 1
+logbook_section = 1
+habitat_label = "shallow coastal sand"
+available_seasons = 13
+allowed_water_types = 2
+rarity = 1
+base_catch_weight = 0.65
+catch_profile = ExtResource("2_profile")
+availability = SubResource("Availability")
+weight_min_lb = 0.5
+weight_max_lb = 3.0
+sell_value_min = 6
+sell_value_max = 14
+sell_value_curve = 0.92
diff --git a/fish/species/crab_ghost/crab_ghost.tres b/fish/species/crab_ghost/crab_ghost.tres
new file mode 100644
index 0000000..db54293
--- /dev/null
+++ b/fish/species/crab_ghost/crab_ghost.tres
@@ -0,0 +1,33 @@
+[gd_resource type="Resource" script_class="FishData" load_steps=4 format=3]
+
+[ext_resource type="Script" 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" path="res://fish/fish_availability.gd" id="3_availability"]
+
+[sub_resource type="Resource" id="Availability"]
+script = ExtResource("3_availability")
+allow_day = false
+forbid_fog = true
+
+[resource]
+script = ExtResource("1_fish_data")
+id = &"crab_ghost"
+display_name = "Atlantic ghost crab"
+active = false
+catalog_number = 3901
+collection_group = &"Marine crabs"
+logbook_fact = "atlantic ghost crabs dash across beaches on long pale legs and retreat into deep burrows. raised eyes scan nearly every direction."
+collection_method = 1
+logbook_section = 1
+habitat_label = "dry beach sand"
+available_seasons = 6
+allowed_water_types = 2
+rarity = 2
+base_catch_weight = 0.28
+catch_profile = ExtResource("2_profile")
+availability = SubResource("Availability")
+weight_min_lb = 0.02
+weight_max_lb = 0.5
+sell_value_min = 11
+sell_value_max = 24
+sell_value_curve = 0.97
diff --git a/fish/species/crab_japanese_spider/crab_japanese_spider.tres b/fish/species/crab_japanese_spider/crab_japanese_spider.tres
new file mode 100644
index 0000000..33ffbff
--- /dev/null
+++ b/fish/species/crab_japanese_spider/crab_japanese_spider.tres
@@ -0,0 +1,31 @@
+[gd_resource type="Resource" script_class="FishData" load_steps=5 format=3]
+
+[ext_resource type="Script" 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" path="res://fish/fish_availability.gd" id="3_availability"]
+[ext_resource type="Texture2D" path="res://art/exported/creatures/fish_placeholder.png" id="99_fish_placeholder"]
+
+[sub_resource type="Resource" id="Availability"]
+script = ExtResource("3_availability")
+allow_day = false
+
+[resource]
+script = ExtResource("1_fish_data")
+id = &"crab_japanese_spider"
+display_name = "Japanese spider crab"
+active = true
+catalog_number = 3904
+collection_group = &"Marine crabs"
+logbook_fact = "japanese spider crabs carry the longest leg span of any living arthropod. adults roam deep seafloors around japan."
+available_seasons = 13
+allowed_water_types = 2
+rarity = 4
+base_catch_weight = 0.045
+catch_profile = ExtResource("2_profile")
+availability = SubResource("Availability")
+weight_min_lb = 5.0
+weight_max_lb = 44.0
+sell_value_min = 38
+sell_value_max = 88
+sell_value_curve = 0.84
+display_texture = ExtResource("99_fish_placeholder")
diff --git a/fish/species/crab_red_king/crab_red_king.tres b/fish/species/crab_red_king/crab_red_king.tres
new file mode 100644
index 0000000..c82579a
--- /dev/null
+++ b/fish/species/crab_red_king/crab_red_king.tres
@@ -0,0 +1,32 @@
+[gd_resource type="Resource" script_class="FishData" load_steps=5 format=3]
+
+[ext_resource type="Script" 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" path="res://fish/fish_availability.gd" id="3_availability"]
+[ext_resource type="Texture2D" path="res://art/exported/creatures/fish_placeholder.png" id="99_fish_placeholder"]
+
+[sub_resource type="Resource" id="Availability"]
+script = ExtResource("3_availability")
+allow_day = false
+require_fog = true
+
+[resource]
+script = ExtResource("1_fish_data")
+id = &"crab_red_king"
+display_name = "red king crab"
+active = true
+catalog_number = 3905
+collection_group = &"Marine crabs"
+logbook_fact = "red king crabs stride over cold northern bottoms on long armored legs. despite the name, they are closer to hermit crabs than true crabs."
+available_seasons = 13
+allowed_water_types = 2
+rarity = 3
+base_catch_weight = 0.11
+catch_profile = ExtResource("2_profile")
+availability = SubResource("Availability")
+weight_min_lb = 2.0
+weight_max_lb = 24.0
+sell_value_min = 24
+sell_value_max = 58
+sell_value_curve = 0.85
+display_texture = ExtResource("99_fish_placeholder")
diff --git a/fish/species/crappie_black/crappie_black.tres b/fish/species/crappie_black/crappie_black.tres
new file mode 100644
index 0000000..0359a2c
--- /dev/null
+++ b/fish/species/crappie_black/crappie_black.tres
@@ -0,0 +1,31 @@
+[gd_resource type="Resource" script_class="FishData" load_steps=5 format=3]
+
+[ext_resource type="Script" 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" path="res://fish/fish_availability.gd" id="3_availability"]
+[ext_resource type="Texture2D" path="res://art/exported/creatures/fish_placeholder.png" id="99_fish_placeholder"]
+
+[sub_resource type="Resource" id="Availability"]
+script = ExtResource("3_availability")
+require_fog = true
+
+[resource]
+script = ExtResource("1_fish_data")
+id = &"crappie_black"
+display_name = "black crappie"
+active = true
+catalog_number = 3101
+collection_group = &"Freshwater panfish"
+logbook_fact = "black crappie gather around submerged wood and vegetation in clear water. irregular dark speckles scatter across their deep silver bodies."
+available_seasons = 13
+allowed_water_types = 1
+rarity = 1
+base_catch_weight = 1.0
+catch_profile = ExtResource("2_profile")
+availability = SubResource("Availability")
+weight_min_lb = 0.2
+weight_max_lb = 4.0
+sell_value_min = 5
+sell_value_max = 11
+sell_value_curve = 0.9
+display_texture = ExtResource("99_fish_placeholder")
diff --git a/fish/species/crappie_white/crappie_white.tres b/fish/species/crappie_white/crappie_white.tres
new file mode 100644
index 0000000..6766f1e
--- /dev/null
+++ b/fish/species/crappie_white/crappie_white.tres
@@ -0,0 +1,31 @@
+[gd_resource type="Resource" script_class="FishData" load_steps=5 format=3]
+
+[ext_resource type="Script" 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" path="res://fish/fish_availability.gd" id="3_availability"]
+[ext_resource type="Texture2D" path="res://art/exported/creatures/3107.png" id="99_fish_placeholder"]
+
+[sub_resource type="Resource" id="Availability"]
+script = ExtResource("3_availability")
+allow_day = false
+
+[resource]
+script = ExtResource("1_fish_data")
+id = &"crappie_white"
+display_name = "white crappie"
+active = true
+catalog_number = 3107
+collection_group = &"Freshwater panfish"
+logbook_fact = "white crappie favor warm, cloudy water and gather around structure. vertical bars cross their silver sides instead of scattered speckles."
+available_seasons = 13
+allowed_water_types = 1
+rarity = 0
+base_catch_weight = 1.7
+catch_profile = ExtResource("2_profile")
+availability = SubResource("Availability")
+weight_min_lb = 0.2
+weight_max_lb = 4.5
+sell_value_min = 4
+sell_value_max = 9
+sell_value_curve = 0.9
+display_texture = ExtResource("99_fish_placeholder")
diff --git a/fish/species/crayfish_red_swamp/crayfish_red_swamp.tres b/fish/species/crayfish_red_swamp/crayfish_red_swamp.tres
new file mode 100644
index 0000000..6c055d2
--- /dev/null
+++ b/fish/species/crayfish_red_swamp/crayfish_red_swamp.tres
@@ -0,0 +1,32 @@
+[gd_resource type="Resource" script_class="FishData" load_steps=5 format=3]
+
+[ext_resource type="Script" 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" path="res://fish/fish_availability.gd" id="3_availability"]
+[ext_resource type="Texture2D" path="res://art/exported/creatures/2601.png" id="99_fish_placeholder"]
+
+[sub_resource type="Resource" id="Availability"]
+script = ExtResource("3_availability")
+allow_day = false
+require_rain = true
+
+[resource]
+script = ExtResource("1_fish_data")
+id = &"crayfish_red_swamp"
+display_name = "red swamp crayfish"
+active = true
+catalog_number = 2601
+collection_group = &"Freshwater crustaceans"
+logbook_fact = "red swamp crayfish burrow into wet ground when water recedes. adaptable populations feed on plants, carrion, and small aquatic animals."
+available_seasons = 7
+allowed_water_types = 1
+rarity = 0
+base_catch_weight = 1.1
+catch_profile = ExtResource("2_profile")
+availability = SubResource("Availability")
+weight_min_lb = 0.03
+weight_max_lb = 0.25
+sell_value_min = 3
+sell_value_max = 7
+sell_value_curve = 0.98
+display_texture = ExtResource("99_fish_placeholder")
diff --git a/fish/species/croaker_atlantic/croaker_atlantic.tres b/fish/species/croaker_atlantic/croaker_atlantic.tres
new file mode 100644
index 0000000..d0fb147
--- /dev/null
+++ b/fish/species/croaker_atlantic/croaker_atlantic.tres
@@ -0,0 +1,32 @@
+[gd_resource type="Resource" script_class="FishData" load_steps=5 format=3]
+
+[ext_resource type="Script" 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" path="res://fish/fish_availability.gd" id="3_availability"]
+[ext_resource type="Texture2D" path="res://art/exported/creatures/6801.png" id="99_fish_placeholder"]
+
+[sub_resource type="Resource" id="Availability"]
+script = ExtResource("3_availability")
+allow_day = false
+require_rain = true
+
+[resource]
+script = ExtResource("1_fish_data")
+id = &"croaker_atlantic"
+display_name = "atlantic croaker"
+active = true
+catalog_number = 6801
+collection_group = &"Temperate estuary"
+logbook_fact = "atlantic croakers vibrate muscles against the swim bladder to make a croaking sound. chin barbels locate worms in sandy bays."
+available_seasons = 7
+allowed_water_types = 2
+rarity = 0
+base_catch_weight = 2.1
+catch_profile = ExtResource("2_profile")
+availability = SubResource("Availability")
+weight_min_lb = 0.1
+weight_max_lb = 5.0
+sell_value_min = 3
+sell_value_max = 8
+sell_value_curve = 0.92
+display_texture = ExtResource("99_fish_placeholder")
diff --git a/fish/species/croaker_yellowfin/croaker_yellowfin.tres b/fish/species/croaker_yellowfin/croaker_yellowfin.tres
new file mode 100644
index 0000000..1e282da
--- /dev/null
+++ b/fish/species/croaker_yellowfin/croaker_yellowfin.tres
@@ -0,0 +1,30 @@
+[gd_resource type="Resource" script_class="FishData" load_steps=5 format=3]
+
+[ext_resource type="Script" 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" path="res://fish/fish_availability.gd" id="3_availability"]
+[ext_resource type="Texture2D" path="res://art/exported/creatures/6807.png" id="99_fish_placeholder"]
+
+[sub_resource type="Resource" id="Availability"]
+script = ExtResource("3_availability")
+
+[resource]
+script = ExtResource("1_fish_data")
+id = &"croaker_yellowfin"
+display_name = "yellowfin croaker"
+active = true
+catalog_number = 6807
+collection_group = &"Temperate estuary"
+logbook_fact = "yellowfin croakers search sandy surf zones with a small chin barbel. internal muscles produce croaks and drumming calls."
+available_seasons = 7
+allowed_water_types = 2
+rarity = 0
+base_catch_weight = 2.0
+catch_profile = ExtResource("2_profile")
+availability = SubResource("Availability")
+weight_min_lb = 0.1
+weight_max_lb = 4.0
+sell_value_min = 3
+sell_value_max = 8
+sell_value_curve = 0.92
+display_texture = ExtResource("99_fish_placeholder")
diff --git a/fish/species/cusk/cusk.tres b/fish/species/cusk/cusk.tres
new file mode 100644
index 0000000..b223ba1
--- /dev/null
+++ b/fish/species/cusk/cusk.tres
@@ -0,0 +1,31 @@
+[gd_resource type="Resource" script_class="FishData" load_steps=5 format=3]
+
+[ext_resource type="Script" 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" path="res://fish/fish_availability.gd" id="3_availability"]
+[ext_resource type="Texture2D" path="res://art/exported/creatures/fish_placeholder.png" id="99_fish_placeholder"]
+
+[sub_resource type="Resource" id="Availability"]
+script = ExtResource("3_availability")
+allow_day = false
+
+[resource]
+script = ExtResource("1_fish_data")
+id = &"cusk"
+display_name = "cusk"
+active = true
+catalog_number = 1202
+collection_group = &"Cold reef fish"
+logbook_fact = "cusk move slowly across cold rocky bottoms, sheltering in cracks and caves. a single long fin runs around much of the rear body."
+available_seasons = 13
+allowed_water_types = 2
+rarity = 1
+base_catch_weight = 0.75
+catch_profile = ExtResource("2_profile")
+availability = SubResource("Availability")
+weight_min_lb = 0.5
+weight_max_lb = 30.0
+sell_value_min = 7
+sell_value_max = 19
+sell_value_curve = 0.85
+display_texture = ExtResource("99_fish_placeholder")
diff --git a/fish/species/cuttlefish_common/cuttlefish_common.tres b/fish/species/cuttlefish_common/cuttlefish_common.tres
new file mode 100644
index 0000000..c1ef5ec
--- /dev/null
+++ b/fish/species/cuttlefish_common/cuttlefish_common.tres
@@ -0,0 +1,31 @@
+[gd_resource type="Resource" script_class="FishData" load_steps=5 format=3]
+
+[ext_resource type="Script" 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" path="res://fish/fish_availability.gd" id="3_availability"]
+[ext_resource type="Texture2D" path="res://art/exported/creatures/fish_placeholder.png" id="99_fish_placeholder"]
+
+[sub_resource type="Resource" id="Availability"]
+script = ExtResource("3_availability")
+allow_night = false
+
+[resource]
+script = ExtResource("1_fish_data")
+id = &"cuttlefish_common"
+display_name = "common cuttlefish"
+active = true
+catalog_number = 1502
+collection_group = &"Cuttlefish and nautiluses"
+logbook_fact = "common cuttlefish flash changing colors and patterns through specialized skin cells. an internal cuttlebone controls buoyancy."
+available_seasons = 15
+allowed_water_types = 2
+rarity = 1
+base_catch_weight = 0.55
+catch_profile = ExtResource("2_profile")
+availability = SubResource("Availability")
+weight_min_lb = 0.2
+weight_max_lb = 12.0
+sell_value_min = 7
+sell_value_max = 17
+sell_value_curve = 0.9
+display_texture = ExtResource("99_fish_placeholder")
diff --git a/fish/species/cuttlefish_flamboyant/cuttlefish_flamboyant.tres b/fish/species/cuttlefish_flamboyant/cuttlefish_flamboyant.tres
new file mode 100644
index 0000000..496ade9
--- /dev/null
+++ b/fish/species/cuttlefish_flamboyant/cuttlefish_flamboyant.tres
@@ -0,0 +1,32 @@
+[gd_resource type="Resource" script_class="FishData" load_steps=5 format=3]
+
+[ext_resource type="Script" 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" path="res://fish/fish_availability.gd" id="3_availability"]
+[ext_resource type="Texture2D" path="res://art/exported/creatures/fish_placeholder.png" id="99_fish_placeholder"]
+
+[sub_resource type="Resource" id="Availability"]
+script = ExtResource("3_availability")
+allow_night = false
+require_rain = true
+
+[resource]
+script = ExtResource("1_fish_data")
+id = &"cuttlefish_flamboyant"
+display_name = "flamboyant cuttlefish"
+active = true
+catalog_number = 1503
+collection_group = &"Cuttlefish and nautiluses"
+logbook_fact = "flamboyant cuttlefish walk along the seafloor on their arms and fins. vivid displays warn predators of potent toxins."
+available_seasons = 15
+allowed_water_types = 2
+rarity = 3
+base_catch_weight = 0.1
+catch_profile = ExtResource("2_profile")
+availability = SubResource("Availability")
+weight_min_lb = 0.05
+weight_max_lb = 0.4
+sell_value_min = 26
+sell_value_max = 58
+sell_value_curve = 0.96
+display_texture = ExtResource("99_fish_placeholder")
diff --git a/fish/species/damselfish_sergeant_major/damselfish_sergeant_major.tres b/fish/species/damselfish_sergeant_major/damselfish_sergeant_major.tres
new file mode 100644
index 0000000..b758404
--- /dev/null
+++ b/fish/species/damselfish_sergeant_major/damselfish_sergeant_major.tres
@@ -0,0 +1,31 @@
+[gd_resource type="Resource" script_class="FishData" load_steps=5 format=3]
+
+[ext_resource type="Script" 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" path="res://fish/fish_availability.gd" id="3_availability"]
+[ext_resource type="Texture2D" path="res://art/exported/creatures/5904.png" id="99_fish_placeholder"]
+
+[sub_resource type="Resource" id="Availability"]
+script = ExtResource("3_availability")
+allow_night = false
+
+[resource]
+script = ExtResource("1_fish_data")
+id = &"damselfish_sergeant_major"
+display_name = "sergeant major"
+active = true
+catalog_number = 5904
+collection_group = &"Reef schooling fish"
+logbook_fact = "sergeant majors guard purple egg patches on hard reef surfaces. five dark bars cross their yellow and silver bodies."
+available_seasons = 15
+allowed_water_types = 2
+rarity = 0
+base_catch_weight = 2.5
+catch_profile = ExtResource("2_profile")
+availability = SubResource("Availability")
+weight_min_lb = 0.02
+weight_max_lb = 0.5
+sell_value_min = 2
+sell_value_max = 5
+sell_value_curve = 0.98
+display_texture = ExtResource("99_fish_placeholder")
diff --git a/fish/species/danio_zebra/danio_zebra.tres b/fish/species/danio_zebra/danio_zebra.tres
new file mode 100644
index 0000000..0f73935
--- /dev/null
+++ b/fish/species/danio_zebra/danio_zebra.tres
@@ -0,0 +1,31 @@
+[gd_resource type="Resource" script_class="FishData" load_steps=5 format=3]
+
+[ext_resource type="Script" 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" path="res://fish/fish_availability.gd" id="3_availability"]
+[ext_resource type="Texture2D" path="res://art/exported/creatures/fish_placeholder.png" id="99_fish_placeholder"]
+
+[sub_resource type="Resource" id="Availability"]
+script = ExtResource("3_availability")
+allow_night = false
+
+[resource]
+script = ExtResource("1_fish_data")
+id = &"danio_zebra"
+display_name = "zebrafish"
+active = true
+catalog_number = 2103
+collection_group = &"Freshwater aquarium fish"
+logbook_fact = "zebrafish carry horizontal blue stripes from gill to tail. hardy schools thrive in shallow streams, flooded fields, and quiet pools."
+available_seasons = 15
+allowed_water_types = 1
+rarity = 0
+base_catch_weight = 1.35
+catch_profile = ExtResource("2_profile")
+availability = SubResource("Availability")
+weight_min_lb = 0.01
+weight_max_lb = 0.12
+sell_value_min = 2
+sell_value_max = 5
+sell_value_curve = 0.98
+display_texture = ExtResource("99_fish_placeholder")
diff --git a/fish/species/discus/discus.tres b/fish/species/discus/discus.tres
new file mode 100644
index 0000000..9803f77
--- /dev/null
+++ b/fish/species/discus/discus.tres
@@ -0,0 +1,32 @@
+[gd_resource type="Resource" script_class="FishData" load_steps=5 format=3]
+
+[ext_resource type="Script" 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" path="res://fish/fish_availability.gd" id="3_availability"]
+[ext_resource type="Texture2D" path="res://art/exported/creatures/fish_placeholder.png" id="99_fish_placeholder"]
+
+[sub_resource type="Resource" id="Availability"]
+script = ExtResource("3_availability")
+allow_night = false
+require_fog = true
+
+[resource]
+script = ExtResource("1_fish_data")
+id = &"discus"
+display_name = "discus"
+active = true
+catalog_number = 7503
+collection_group = &"Tropical freshwater fish"
+logbook_fact = "discus move through quiet amazon backwaters with round, flattened bodies. parents feed newly hatched young with nutrient-rich skin mucus."
+available_seasons = 15
+allowed_water_types = 1
+rarity = 3
+base_catch_weight = 0.12
+catch_profile = ExtResource("2_profile")
+availability = SubResource("Availability")
+weight_min_lb = 0.1
+weight_max_lb = 1.0
+sell_value_min = 24
+sell_value_max = 52
+sell_value_curve = 0.92
+display_texture = ExtResource("99_fish_placeholder")
diff --git a/fish/species/dolphin_bottlenose/dolphin_bottlenose.tres b/fish/species/dolphin_bottlenose/dolphin_bottlenose.tres
new file mode 100644
index 0000000..2b410ea
--- /dev/null
+++ b/fish/species/dolphin_bottlenose/dolphin_bottlenose.tres
@@ -0,0 +1,32 @@
+[gd_resource type="Resource" script_class="FishData" load_steps=5 format=3]
+
+[ext_resource type="Script" 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" path="res://fish/fish_availability.gd" id="3_availability"]
+[ext_resource type="Texture2D" path="res://art/exported/creatures/fish_placeholder.png" id="99_fish_placeholder"]
+
+[sub_resource type="Resource" id="Availability"]
+script = ExtResource("3_availability")
+allow_night = false
+forbid_fog = true
+
+[resource]
+script = ExtResource("1_fish_data")
+id = &"dolphin_bottlenose"
+display_name = "common bottlenose dolphin"
+active = true
+catalog_number = 7702
+collection_group = &"Whales and dolphins"
+logbook_fact = "common bottlenose dolphins coordinate with whistles, clicks, and body signals. echolocation reveals prey hidden in dark or cloudy water."
+available_seasons = 15
+allowed_water_types = 2
+rarity = 4
+base_catch_weight = 0.012
+catch_profile = ExtResource("2_profile")
+availability = SubResource("Availability")
+weight_min_lb = 330.0
+weight_max_lb = 1400.0
+sell_value_min = 75
+sell_value_max = 180
+sell_value_curve = 0.76
+display_texture = ExtResource("99_fish_placeholder")
diff --git a/fish/species/dorado_golden/dorado_golden.tres b/fish/species/dorado_golden/dorado_golden.tres
new file mode 100644
index 0000000..e22f7c9
--- /dev/null
+++ b/fish/species/dorado_golden/dorado_golden.tres
@@ -0,0 +1,32 @@
+[gd_resource type="Resource" script_class="FishData" load_steps=5 format=3]
+
+[ext_resource type="Script" 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" path="res://fish/fish_availability.gd" id="3_availability"]
+[ext_resource type="Texture2D" path="res://art/exported/creatures/fish_placeholder.png" id="99_fish_placeholder"]
+
+[sub_resource type="Resource" id="Availability"]
+script = ExtResource("3_availability")
+allow_night = false
+require_rain = true
+
+[resource]
+script = ExtResource("1_fish_data")
+id = &"dorado_golden"
+display_name = "golden dorado"
+active = true
+catalog_number = 7602
+collection_group = &"Tropical freshwater predators"
+logbook_fact = "golden dorado chase fish through powerful south american rivers. muscular tails, heavy jaws, and golden scales suit life in fast current."
+available_seasons = 15
+allowed_water_types = 1
+rarity = 3
+base_catch_weight = 0.1
+catch_profile = ExtResource("2_profile")
+availability = SubResource("Availability")
+weight_min_lb = 5.0
+weight_max_lb = 55.0
+sell_value_min = 29
+sell_value_max = 66
+sell_value_curve = 0.82
+display_texture = ExtResource("99_fish_placeholder")
diff --git a/fish/species/dugong/dugong.tres b/fish/species/dugong/dugong.tres
new file mode 100644
index 0000000..0c81d2e
--- /dev/null
+++ b/fish/species/dugong/dugong.tres
@@ -0,0 +1,32 @@
+[gd_resource type="Resource" script_class="FishData" load_steps=5 format=3]
+
+[ext_resource type="Script" 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" path="res://fish/fish_availability.gd" id="3_availability"]
+[ext_resource type="Texture2D" path="res://art/exported/creatures/fish_placeholder.png" id="99_fish_placeholder"]
+
+[sub_resource type="Resource" id="Availability"]
+script = ExtResource("3_availability")
+allow_night = false
+forbid_fog = true
+
+[resource]
+script = ExtResource("1_fish_data")
+id = &"dugong"
+display_name = "dugong"
+active = true
+catalog_number = 501
+collection_group = &"Coastal marine mammals"
+logbook_fact = "dugongs graze seagrass meadows with a downturned, bristled snout. they are the only fully marine members of the sirenian family."
+available_seasons = 15
+allowed_water_types = 2
+rarity = 4
+base_catch_weight = 0.011
+catch_profile = ExtResource("2_profile")
+availability = SubResource("Availability")
+weight_min_lb = 500.0
+weight_max_lb = 1100.0
+sell_value_min = 72
+sell_value_max = 165
+sell_value_curve = 0.77
+display_texture = ExtResource("99_fish_placeholder")
diff --git a/fish/species/eel_american/eel_american.tres b/fish/species/eel_american/eel_american.tres
new file mode 100644
index 0000000..a433afa
--- /dev/null
+++ b/fish/species/eel_american/eel_american.tres
@@ -0,0 +1,32 @@
+[gd_resource type="Resource" script_class="FishData" load_steps=5 format=3]
+
+[ext_resource type="Script" 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" path="res://fish/fish_availability.gd" id="3_availability"]
+[ext_resource type="Texture2D" path="res://art/exported/creatures/fish_placeholder.png" id="99_fish_placeholder"]
+
+[sub_resource type="Resource" id="Availability"]
+script = ExtResource("3_availability")
+allow_day = false
+require_rain = true
+
+[resource]
+script = ExtResource("1_fish_data")
+id = &"eel_american"
+display_name = "american eel"
+active = true
+catalog_number = 4001
+collection_group = &"Migratory eels"
+logbook_fact = "american eels hatch far out in the sargasso sea before entering freshwater rivers. adults later reverse the journey to spawn once."
+available_seasons = 12
+allowed_water_types = 3
+rarity = 2
+base_catch_weight = 0.25
+catch_profile = ExtResource("2_profile")
+availability = SubResource("Availability")
+weight_min_lb = 0.3
+weight_max_lb = 8.0
+sell_value_min = 12
+sell_value_max = 27
+sell_value_curve = 0.88
+display_texture = ExtResource("99_fish_placeholder")
diff --git a/fish/species/eel_european/eel_european.tres b/fish/species/eel_european/eel_european.tres
new file mode 100644
index 0000000..7683d92
--- /dev/null
+++ b/fish/species/eel_european/eel_european.tres
@@ -0,0 +1,32 @@
+[gd_resource type="Resource" script_class="FishData" load_steps=5 format=3]
+
+[ext_resource type="Script" 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" path="res://fish/fish_availability.gd" id="3_availability"]
+[ext_resource type="Texture2D" path="res://art/exported/creatures/fish_placeholder.png" id="99_fish_placeholder"]
+
+[sub_resource type="Resource" id="Availability"]
+script = ExtResource("3_availability")
+allow_day = false
+require_fog = true
+
+[resource]
+script = ExtResource("1_fish_data")
+id = &"eel_european"
+display_name = "european eel"
+active = true
+catalog_number = 4002
+collection_group = &"Migratory eels"
+logbook_fact = "european eels cross the atlantic as transparent larvae before entering rivers and lakes. mature silver eels return to sea to reproduce."
+available_seasons = 12
+allowed_water_types = 3
+rarity = 2
+base_catch_weight = 0.2
+catch_profile = ExtResource("2_profile")
+availability = SubResource("Availability")
+weight_min_lb = 0.3
+weight_max_lb = 10.0
+sell_value_min = 13
+sell_value_max = 30
+sell_value_curve = 0.88
+display_texture = ExtResource("99_fish_placeholder")
diff --git a/fish/species/electric_eel/electric_eel.tres b/fish/species/electric_eel/electric_eel.tres
new file mode 100644
index 0000000..ef5b6df
--- /dev/null
+++ b/fish/species/electric_eel/electric_eel.tres
@@ -0,0 +1,32 @@
+[gd_resource type="Resource" script_class="FishData" load_steps=5 format=3]
+
+[ext_resource type="Script" 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" path="res://fish/fish_availability.gd" id="3_availability"]
+[ext_resource type="Texture2D" path="res://art/exported/creatures/fish_placeholder.png" id="99_fish_placeholder"]
+
+[sub_resource type="Resource" id="Availability"]
+script = ExtResource("3_availability")
+allow_day = false
+require_rain = true
+
+[resource]
+script = ExtResource("1_fish_data")
+id = &"electric_eel"
+display_name = "electric eel"
+active = true
+catalog_number = 2901
+collection_group = &"Freshwater oddities"
+logbook_fact = "electric eels navigate and hunt with electrical pulses produced along most of the body. they must surface regularly to breathe air."
+available_seasons = 15
+allowed_water_types = 1
+rarity = 3
+base_catch_weight = 0.1
+catch_profile = ExtResource("2_profile")
+availability = SubResource("Availability")
+weight_min_lb = 1.0
+weight_max_lb = 44.0
+sell_value_min = 25
+sell_value_max = 60
+sell_value_curve = 0.82
+display_texture = ExtResource("99_fish_placeholder")
diff --git a/fish/species/fallfish/fallfish.tres b/fish/species/fallfish/fallfish.tres
new file mode 100644
index 0000000..53ef7b6
--- /dev/null
+++ b/fish/species/fallfish/fallfish.tres
@@ -0,0 +1,32 @@
+[gd_resource type="Resource" script_class="FishData" load_steps=5 format=3]
+
+[ext_resource type="Script" 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" path="res://fish/fish_availability.gd" id="3_availability"]
+[ext_resource type="Texture2D" path="res://art/exported/creatures/fish_placeholder.png" id="99_fish_placeholder"]
+
+[sub_resource type="Resource" id="Availability"]
+script = ExtResource("3_availability")
+allow_night = false
+require_rain = true
+
+[resource]
+script = ExtResource("1_fish_data")
+id = &"fallfish"
+display_name = "fallfish"
+active = true
+catalog_number = 2506
+collection_group = &"Freshwater coarse fish"
+logbook_fact = "fallfish are large minnows that build broad stone nests in flowing water. other stream fish often reuse the gravel mounds for spawning."
+available_seasons = 7
+allowed_water_types = 1
+rarity = 1
+base_catch_weight = 0.9
+catch_profile = ExtResource("2_profile")
+availability = SubResource("Availability")
+weight_min_lb = 0.2
+weight_max_lb = 3.5
+sell_value_min = 5
+sell_value_max = 10
+sell_value_curve = 0.92
+display_texture = ExtResource("99_fish_placeholder")
diff --git a/fish/species/fangtooth_common/fangtooth_common.tres b/fish/species/fangtooth_common/fangtooth_common.tres
new file mode 100644
index 0000000..1004279
--- /dev/null
+++ b/fish/species/fangtooth_common/fangtooth_common.tres
@@ -0,0 +1,31 @@
+[gd_resource type="Resource" script_class="FishData" load_steps=5 format=3]
+
+[ext_resource type="Script" 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" path="res://fish/fish_availability.gd" id="3_availability"]
+[ext_resource type="Texture2D" path="res://art/exported/creatures/fish_placeholder.png" id="99_fish_placeholder"]
+
+[sub_resource type="Resource" id="Availability"]
+script = ExtResource("3_availability")
+allow_day = false
+
+[resource]
+script = ExtResource("1_fish_data")
+id = &"fangtooth_common"
+display_name = "common fangtooth"
+active = true
+catalog_number = 1802
+collection_group = &"Deep-sea oddities"
+logbook_fact = "common fangtooths carry outsized teeth that fit into pockets in the roof of the mouth. they migrate upward from deep water after dark."
+available_seasons = 15
+allowed_water_types = 2
+rarity = 3
+base_catch_weight = 0.1
+catch_profile = ExtResource("2_profile")
+availability = SubResource("Availability")
+weight_min_lb = 0.05
+weight_max_lb = 0.5
+sell_value_min = 25
+sell_value_max = 56
+sell_value_curve = 0.96
+display_texture = ExtResource("99_fish_placeholder")
diff --git a/fish/species/flounder_peacock/flounder_peacock.tres b/fish/species/flounder_peacock/flounder_peacock.tres
new file mode 100644
index 0000000..3d096df
--- /dev/null
+++ b/fish/species/flounder_peacock/flounder_peacock.tres
@@ -0,0 +1,31 @@
+[gd_resource type="Resource" script_class="FishData" load_steps=5 format=3]
+
+[ext_resource type="Script" 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" path="res://fish/fish_availability.gd" id="3_availability"]
+[ext_resource type="Texture2D" path="res://art/exported/creatures/fish_placeholder.png" id="99_fish_placeholder"]
+
+[sub_resource type="Resource" id="Availability"]
+script = ExtResource("3_availability")
+allow_night = false
+
+[resource]
+script = ExtResource("1_fish_data")
+id = &"flounder_peacock"
+display_name = "peacock flounder"
+active = true
+catalog_number = 5401
+collection_group = &"Reef flatfish"
+logbook_fact = "peacock flounders change their spots and colors to match tropical sand. both eyes scan above while the flat body remains concealed."
+available_seasons = 15
+allowed_water_types = 2
+rarity = 2
+base_catch_weight = 0.3
+catch_profile = ExtResource("2_profile")
+availability = SubResource("Availability")
+weight_min_lb = 0.2
+weight_max_lb = 5.0
+sell_value_min = 11
+sell_value_max = 24
+sell_value_curve = 0.9
+display_texture = ExtResource("99_fish_placeholder")
diff --git a/fish/species/flounder_summer/flounder_summer.tres b/fish/species/flounder_summer/flounder_summer.tres
new file mode 100644
index 0000000..680738d
--- /dev/null
+++ b/fish/species/flounder_summer/flounder_summer.tres
@@ -0,0 +1,31 @@
+[gd_resource type="Resource" script_class="FishData" load_steps=5 format=3]
+
+[ext_resource type="Script" 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" path="res://fish/fish_availability.gd" id="3_availability"]
+[ext_resource type="Texture2D" path="res://art/exported/creatures/fish_placeholder.png" id="99_fish_placeholder"]
+
+[sub_resource type="Resource" id="Availability"]
+script = ExtResource("3_availability")
+allow_night = false
+
+[resource]
+script = ExtResource("1_fish_data")
+id = &"flounder_summer"
+display_name = "summer flounder"
+active = true
+catalog_number = 402
+collection_group = &"Coastal flatfish"
+logbook_fact = "summer flounder bury themselves in sand with only their eyes exposed. a sudden burst carries them upward to seize passing prey."
+available_seasons = 7
+allowed_water_types = 2
+rarity = 1
+base_catch_weight = 0.9
+catch_profile = ExtResource("2_profile")
+availability = SubResource("Availability")
+weight_min_lb = 0.3
+weight_max_lb = 15.0
+sell_value_min = 6
+sell_value_max = 16
+sell_value_curve = 0.88
+display_texture = ExtResource("99_fish_placeholder")
diff --git a/fish/species/flounder_winter/flounder_winter.tres b/fish/species/flounder_winter/flounder_winter.tres
new file mode 100644
index 0000000..c3ae09b
--- /dev/null
+++ b/fish/species/flounder_winter/flounder_winter.tres
@@ -0,0 +1,32 @@
+[gd_resource type="Resource" script_class="FishData" load_steps=5 format=3]
+
+[ext_resource type="Script" 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" path="res://fish/fish_availability.gd" id="3_availability"]
+[ext_resource type="Texture2D" path="res://art/exported/creatures/403.png" id="99_fish_placeholder"]
+
+[sub_resource type="Resource" id="Availability"]
+script = ExtResource("3_availability")
+allow_night = false
+forbid_rain = true
+
+[resource]
+script = ExtResource("1_fish_data")
+id = &"flounder_winter"
+display_name = "winter flounder"
+active = true
+catalog_number = 403
+collection_group = &"Coastal flatfish"
+logbook_fact = "winter flounder favor cool bays and estuaries where they rest on muddy bottoms. their eyes sit on the right side of the body."
+available_seasons = 13
+allowed_water_types = 2
+rarity = 0
+base_catch_weight = 1.8
+catch_profile = ExtResource("2_profile")
+availability = SubResource("Availability")
+weight_min_lb = 0.2
+weight_max_lb = 8.0
+sell_value_min = 4
+sell_value_max = 9
+sell_value_curve = 0.9
+display_texture = ExtResource("99_fish_placeholder")
diff --git a/fish/species/flyingfish_tropical/flyingfish_tropical.tres b/fish/species/flyingfish_tropical/flyingfish_tropical.tres
new file mode 100644
index 0000000..12fd4bb
--- /dev/null
+++ b/fish/species/flyingfish_tropical/flyingfish_tropical.tres
@@ -0,0 +1,32 @@
+[gd_resource type="Resource" script_class="FishData" load_steps=5 format=3]
+
+[ext_resource type="Script" 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" path="res://fish/fish_availability.gd" id="3_availability"]
+[ext_resource type="Texture2D" path="res://art/exported/creatures/fish_placeholder.png" id="99_fish_placeholder"]
+
+[sub_resource type="Resource" id="Availability"]
+script = ExtResource("3_availability")
+allow_night = false
+forbid_rain = true
+
+[resource]
+script = ExtResource("1_fish_data")
+id = &"flyingfish_tropical"
+display_name = "tropical flyingfish"
+active = true
+catalog_number = 6603
+collection_group = &"Surface ocean fish"
+logbook_fact = "tropical flyingfish accelerate underwater before spreading enlarged fins above the surface. glides help them escape pursuing predators."
+available_seasons = 15
+allowed_water_types = 2
+rarity = 2
+base_catch_weight = 0.35
+catch_profile = ExtResource("2_profile")
+availability = SubResource("Availability")
+weight_min_lb = 0.05
+weight_max_lb = 1.5
+sell_value_min = 10
+sell_value_max = 22
+sell_value_curve = 0.94
+display_texture = ExtResource("99_fish_placeholder")
diff --git a/fish/species/foureyes_largescale/foureyes_largescale.tres b/fish/species/foureyes_largescale/foureyes_largescale.tres
new file mode 100644
index 0000000..7ebba2f
--- /dev/null
+++ b/fish/species/foureyes_largescale/foureyes_largescale.tres
@@ -0,0 +1,31 @@
+[gd_resource type="Resource" script_class="FishData" load_steps=5 format=3]
+
+[ext_resource type="Script" 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" path="res://fish/fish_availability.gd" id="3_availability"]
+[ext_resource type="Texture2D" path="res://art/exported/creatures/fish_placeholder.png" id="99_fish_placeholder"]
+
+[sub_resource type="Resource" id="Availability"]
+script = ExtResource("3_availability")
+allow_night = false
+
+[resource]
+script = ExtResource("1_fish_data")
+id = &"foureyes_largescale"
+display_name = "largescale foureyes"
+active = true
+catalog_number = 202
+collection_group = &"Brackish oddities"
+logbook_fact = "largescale foureyes cruise with each eye divided across the waterline. one half watches above while the other scans below."
+available_seasons = 15
+allowed_water_types = 3
+rarity = 2
+base_catch_weight = 0.32
+catch_profile = ExtResource("2_profile")
+availability = SubResource("Availability")
+weight_min_lb = 0.05
+weight_max_lb = 1.0
+sell_value_min = 10
+sell_value_max = 23
+sell_value_curve = 0.95
+display_texture = ExtResource("99_fish_placeholder")
diff --git a/fish/species/freshwater_drum/freshwater_drum.tres b/fish/species/freshwater_drum/freshwater_drum.tres
new file mode 100644
index 0000000..f52f9fe
--- /dev/null
+++ b/fish/species/freshwater_drum/freshwater_drum.tres
@@ -0,0 +1,32 @@
+[gd_resource type="Resource" script_class="FishData" load_steps=5 format=3]
+
+[ext_resource type="Script" 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" path="res://fish/fish_availability.gd" id="3_availability"]
+[ext_resource type="Texture2D" path="res://art/exported/creatures/fish_placeholder.png" id="99_fish_placeholder"]
+
+[sub_resource type="Resource" id="Availability"]
+script = ExtResource("3_availability")
+allow_day = false
+require_rain = true
+
+[resource]
+script = ExtResource("1_fish_data")
+id = &"freshwater_drum"
+display_name = "freshwater drum"
+active = true
+catalog_number = 2902
+collection_group = &"Freshwater oddities"
+logbook_fact = "freshwater drum crush snails and mussels with powerful throat teeth. muscles vibrating against the swim bladder produce a low drumming sound."
+available_seasons = 7
+allowed_water_types = 1
+rarity = 1
+base_catch_weight = 0.7
+catch_profile = ExtResource("2_profile")
+availability = SubResource("Availability")
+weight_min_lb = 1.0
+weight_max_lb = 35.0
+sell_value_min = 7
+sell_value_max = 20
+sell_value_curve = 0.85
+display_texture = ExtResource("99_fish_placeholder")
diff --git a/fish/species/gar_alligator/gar_alligator.tres b/fish/species/gar_alligator/gar_alligator.tres
new file mode 100644
index 0000000..f5439a4
--- /dev/null
+++ b/fish/species/gar_alligator/gar_alligator.tres
@@ -0,0 +1,31 @@
+[gd_resource type="Resource" script_class="FishData" load_steps=5 format=3]
+
+[ext_resource type="Script" 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" path="res://fish/fish_availability.gd" id="3_availability"]
+[ext_resource type="Texture2D" path="res://art/exported/creatures/fish_placeholder.png" id="99_fish_placeholder"]
+
+[sub_resource type="Resource" id="Availability"]
+script = ExtResource("3_availability")
+allow_day = false
+
+[resource]
+script = ExtResource("1_fish_data")
+id = &"gar_alligator"
+display_name = "alligator gar"
+active = true
+catalog_number = 2701
+collection_group = &"Freshwater giants"
+logbook_fact = "alligator gar combine a broad tooth-filled snout with heavy armor-like scales. a vascular swim bladder lets them breathe air at the surface."
+available_seasons = 7
+allowed_water_types = 1
+rarity = 4
+base_catch_weight = 0.055
+catch_profile = ExtResource("2_profile")
+availability = SubResource("Availability")
+weight_min_lb = 20.0
+weight_max_lb = 300.0
+sell_value_min = 55
+sell_value_max = 125
+sell_value_curve = 0.8
+display_texture = ExtResource("99_fish_placeholder")
diff --git a/fish/species/gar_longnose/gar_longnose.png b/fish/species/gar_longnose/gar_longnose.png
new file mode 100644
index 0000000..2cfa552
Binary files /dev/null and b/fish/species/gar_longnose/gar_longnose.png differ
diff --git a/fish/species/gar_longnose/gar_longnose.png.import b/fish/species/gar_longnose/gar_longnose.png.import
new file mode 100644
index 0000000..385d22c
--- /dev/null
+++ b/fish/species/gar_longnose/gar_longnose.png.import
@@ -0,0 +1,40 @@
+[remap]
+
+importer="texture"
+type="CompressedTexture2D"
+uid="uid://brb46wv5v6htb"
+path="res://.godot/imported/gar_longnose.png-91bf63255a3f7c479162d07d1e71edf8.ctex"
+metadata={
+"vram_texture": false
+}
+
+[deps]
+
+source_file="res://fish/species/gar_longnose/gar_longnose.png"
+dest_files=["res://.godot/imported/gar_longnose.png-91bf63255a3f7c479162d07d1e71edf8.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/gar_longnose/gar_longnose.tres b/fish/species/gar_longnose/gar_longnose.tres
new file mode 100644
index 0000000..aaabbfe
--- /dev/null
+++ b/fish/species/gar_longnose/gar_longnose.tres
@@ -0,0 +1,31 @@
+[gd_resource type="Resource" script_class="FishData" load_steps=5 format=3]
+
+[ext_resource type="Script" 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" path="res://fish/fish_availability.gd" id="3_availability"]
+[ext_resource type="Texture2D" path="res://art/exported/creatures/103.png" id="4_texture"]
+
+[sub_resource type="Resource" id="Availability"]
+script = ExtResource("3_availability")
+allow_night = false
+
+[resource]
+script = ExtResource("1_fish_data")
+id = &"gar_longnose"
+display_name = "longnose gar"
+active = true
+catalog_number = 103
+collection_group = &"Ancient freshwater fish"
+logbook_fact = "longnose gar wait near the surface before snapping sideways at passing fish. their narrow jaws carry rows of fine needle-like teeth."
+available_seasons = 7
+allowed_water_types = 1
+rarity = 2
+base_catch_weight = 0.24
+catch_profile = ExtResource("2_profile")
+availability = SubResource("Availability")
+weight_min_lb = 2.0
+weight_max_lb = 35.0
+sell_value_min = 14
+sell_value_max = 34
+sell_value_curve = 0.85
+display_texture = ExtResource("4_texture")
diff --git a/fish/species/gar_spotted/gar_spotted.png b/fish/species/gar_spotted/gar_spotted.png
new file mode 100644
index 0000000..80cdfdf
Binary files /dev/null and b/fish/species/gar_spotted/gar_spotted.png differ
diff --git a/fish/species/gar_spotted/gar_spotted.png.import b/fish/species/gar_spotted/gar_spotted.png.import
new file mode 100644
index 0000000..95d737f
--- /dev/null
+++ b/fish/species/gar_spotted/gar_spotted.png.import
@@ -0,0 +1,40 @@
+[remap]
+
+importer="texture"
+type="CompressedTexture2D"
+uid="uid://b81tl0mmq7gxx"
+path="res://.godot/imported/gar_spotted.png-0361bdcc323bb7449e240ef3835b7e18.ctex"
+metadata={
+"vram_texture": false
+}
+
+[deps]
+
+source_file="res://fish/species/gar_spotted/gar_spotted.png"
+dest_files=["res://.godot/imported/gar_spotted.png-0361bdcc323bb7449e240ef3835b7e18.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/gar_spotted/gar_spotted.tres b/fish/species/gar_spotted/gar_spotted.tres
new file mode 100644
index 0000000..3405ee9
--- /dev/null
+++ b/fish/species/gar_spotted/gar_spotted.tres
@@ -0,0 +1,32 @@
+[gd_resource type="Resource" script_class="FishData" load_steps=5 format=3]
+
+[ext_resource type="Script" 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" path="res://fish/fish_availability.gd" id="3_availability"]
+[ext_resource type="Texture2D" path="res://art/exported/creatures/106.png" id="4_texture"]
+
+[sub_resource type="Resource" id="Availability"]
+script = ExtResource("3_availability")
+allow_night = false
+require_fog = true
+
+[resource]
+script = ExtResource("1_fish_data")
+id = &"gar_spotted"
+display_name = "spotted gar"
+active = true
+catalog_number = 106
+collection_group = &"Ancient freshwater fish"
+logbook_fact = "spotted gar linger among vegetation where their mottled bodies break up their outline. they rise to gulp air as well as breathing through gills."
+available_seasons = 7
+allowed_water_types = 1
+rarity = 2
+base_catch_weight = 0.2
+catch_profile = ExtResource("2_profile")
+availability = SubResource("Availability")
+weight_min_lb = 1.0
+weight_max_lb = 15.0
+sell_value_min = 13
+sell_value_max = 29
+sell_value_curve = 0.88
+display_texture = ExtResource("4_texture")
diff --git a/fish/species/goby_round/fish_goby_round.png b/fish/species/goby_round/fish_goby_round.png
index 89e3f78..02a5855 100644
Binary files a/fish/species/goby_round/fish_goby_round.png and b/fish/species/goby_round/fish_goby_round.png differ
diff --git a/fish/species/goby_round/goby_round.tres b/fish/species/goby_round/goby_round.tres
index 4c8872e..3a8ffee 100644
--- a/fish/species/goby_round/goby_round.tres
+++ b/fish/species/goby_round/goby_round.tres
@@ -3,7 +3,7 @@
[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"]
+[ext_resource type="Texture2D" path="res://art/exported/creatures/3401.png" id="4_texture"]
[sub_resource type="Resource" id="RoundGobyAvailability"]
script = ExtResource("3_availability_script")
@@ -14,6 +14,11 @@ preferred_bait_weight_multiplier = 1.35
script = ExtResource("1_fish_data")
id = &"goby_round"
display_name = "round goby"
+active = true
+catalog_number = 3401
+collection_group = &"Freshwater small fish"
+logbook_fact = "round gobies use fused pelvic fins like a suction cup to hold onto rocks. they are small fish with a famously large appetite."
+available_seasons = 15
allowed_water_types = 1
base_catch_weight = 8.0
catch_profile = ExtResource("2_profile")
diff --git a/fish/species/goldeye/goldeye.tres b/fish/species/goldeye/goldeye.tres
new file mode 100644
index 0000000..1d09b7c
--- /dev/null
+++ b/fish/species/goldeye/goldeye.tres
@@ -0,0 +1,32 @@
+[gd_resource type="Resource" script_class="FishData" load_steps=5 format=3]
+
+[ext_resource type="Script" 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" path="res://fish/fish_availability.gd" id="3_availability"]
+[ext_resource type="Texture2D" path="res://art/exported/creatures/fish_placeholder.png" id="99_fish_placeholder"]
+
+[sub_resource type="Resource" id="Availability"]
+script = ExtResource("3_availability")
+allow_day = false
+require_fog = true
+
+[resource]
+script = ExtResource("1_fish_data")
+id = &"goldeye"
+display_name = "goldeye"
+active = true
+catalog_number = 2903
+collection_group = &"Freshwater oddities"
+logbook_fact = "goldeye patrol turbid rivers with large light-sensitive eyes and a toothed tongue. they rise toward the surface to hunt insects and small fish."
+available_seasons = 13
+allowed_water_types = 1
+rarity = 2
+base_catch_weight = 0.3
+catch_profile = ExtResource("2_profile")
+availability = SubResource("Availability")
+weight_min_lb = 0.3
+weight_max_lb = 6.0
+sell_value_min = 12
+sell_value_max = 25
+sell_value_curve = 0.9
+display_texture = ExtResource("99_fish_placeholder")
diff --git a/fish/species/goldfish/fish_goldfish.png b/fish/species/goldfish/fish_goldfish.png
index 7ca035b..7214195 100644
Binary files a/fish/species/goldfish/fish_goldfish.png and b/fish/species/goldfish/fish_goldfish.png differ
diff --git a/fish/species/goldfish/goldfish.tres b/fish/species/goldfish/goldfish.tres
index 153365d..58c7934 100644
--- a/fish/species/goldfish/goldfish.tres
+++ b/fish/species/goldfish/goldfish.tres
@@ -3,16 +3,21 @@
[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"]
+[ext_resource type="Texture2D" path="res://art/exported/creatures/3002.png" id="4_texture"]
[sub_resource type="Resource" id="GoldfishAvailability"]
script = ExtResource("3_availability_script")
-allowed_location_tags = Array[StringName]([&"starter_pond"])
+allowed_location_tags = Array[StringName]([&"starter_pond", &"pond"])
[resource]
script = ExtResource("1_fish_data")
id = &"goldfish"
display_name = "goldfish"
+active = true
+catalog_number = 3002
+collection_group = &"Freshwater ornamental"
+logbook_fact = "goldfish can tolerate a surprisingly wide range of conditions. their wild relatives often live in slow, weedy water."
+available_seasons = 15
allowed_water_types = 1
base_catch_weight = 0.5
catch_profile = ExtResource("2_profile")
diff --git a/fish/species/goldfish_bubbleeye/fish_goldfish_bubbleeye.png b/fish/species/goldfish_bubbleeye/fish_goldfish_bubbleeye.png
index b621e16..c25b2f7 100644
Binary files a/fish/species/goldfish_bubbleeye/fish_goldfish_bubbleeye.png and b/fish/species/goldfish_bubbleeye/fish_goldfish_bubbleeye.png differ
diff --git a/fish/species/goldfish_bubbleeye/goldfish_bubbleeye.tres b/fish/species/goldfish_bubbleeye/goldfish_bubbleeye.tres
index d6a288a..d88719c 100644
--- a/fish/species/goldfish_bubbleeye/goldfish_bubbleeye.tres
+++ b/fish/species/goldfish_bubbleeye/goldfish_bubbleeye.tres
@@ -3,16 +3,21 @@
[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"]
+[ext_resource type="Texture2D" path="res://art/exported/creatures/fish_placeholder.png" id="4_texture"]
[sub_resource type="Resource" id="GoldfishBubbleeyeAvailability"]
script = ExtResource("3_availability_script")
-allowed_location_tags = Array[StringName]([&"starter_pond"])
+allowed_location_tags = Array[StringName]([&"starter_pond", &"pond"])
[resource]
script = ExtResource("1_fish_data")
id = &"goldfish_bubbleeye"
display_name = "bubble-eye goldfish"
+active = true
+catalog_number = 3001
+collection_group = &"Freshwater ornamental"
+logbook_fact = "bubble-eye goldfish have delicate fluid-filled sacs beneath their eyes, making careful handling especially important."
+available_seasons = 15
allowed_water_types = 1
rarity = 1
base_catch_weight = 0.35
diff --git a/fish/species/gourami_giant/gourami_giant.tres b/fish/species/gourami_giant/gourami_giant.tres
new file mode 100644
index 0000000..b4f8f52
--- /dev/null
+++ b/fish/species/gourami_giant/gourami_giant.tres
@@ -0,0 +1,31 @@
+[gd_resource type="Resource" script_class="FishData" load_steps=5 format=3]
+
+[ext_resource type="Script" 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" path="res://fish/fish_availability.gd" id="3_availability"]
+[ext_resource type="Texture2D" path="res://art/exported/creatures/fish_placeholder.png" id="99_fish_placeholder"]
+
+[sub_resource type="Resource" id="Availability"]
+script = ExtResource("3_availability")
+allow_night = false
+
+[resource]
+script = ExtResource("1_fish_data")
+id = &"gourami_giant"
+display_name = "giant gourami"
+active = true
+catalog_number = 7505
+collection_group = &"Tropical freshwater fish"
+logbook_fact = "giant gouramis breathe surface air through a specialized labyrinth organ. adults graze plants but also investigate insects and small animals."
+available_seasons = 15
+allowed_water_types = 1
+rarity = 2
+base_catch_weight = 0.25
+catch_profile = ExtResource("2_profile")
+availability = SubResource("Availability")
+weight_min_lb = 2.0
+weight_max_lb = 20.0
+sell_value_min = 13
+sell_value_max = 31
+sell_value_curve = 0.86
+display_texture = ExtResource("99_fish_placeholder")
diff --git a/fish/species/grayling_arctic/grayling_arctic.tres b/fish/species/grayling_arctic/grayling_arctic.tres
new file mode 100644
index 0000000..ae6321a
--- /dev/null
+++ b/fish/species/grayling_arctic/grayling_arctic.tres
@@ -0,0 +1,32 @@
+[gd_resource type="Resource" script_class="FishData" load_steps=5 format=3]
+
+[ext_resource type="Script" 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" path="res://fish/fish_availability.gd" id="3_availability"]
+[ext_resource type="Texture2D" path="res://art/exported/creatures/fish_placeholder.png" id="99_fish_placeholder"]
+
+[sub_resource type="Resource" id="Availability"]
+script = ExtResource("3_availability")
+allow_night = false
+forbid_fog = true
+
+[resource]
+script = ExtResource("1_fish_data")
+id = &"grayling_arctic"
+display_name = "arctic grayling"
+active = true
+catalog_number = 1102
+collection_group = &"Cold freshwater salmonids"
+logbook_fact = "arctic grayling carry a tall, colorful dorsal fin like a sail. they feed on drifting insects in cold, clear rivers and lakes."
+available_seasons = 13
+allowed_water_types = 1
+rarity = 1
+base_catch_weight = 0.75
+catch_profile = ExtResource("2_profile")
+availability = SubResource("Availability")
+weight_min_lb = 0.3
+weight_max_lb = 6.0
+sell_value_min = 6
+sell_value_max = 14
+sell_value_curve = 0.9
+display_texture = ExtResource("99_fish_placeholder")
diff --git a/fish/species/green_sunfish/green_sunfish.tres b/fish/species/green_sunfish/green_sunfish.tres
new file mode 100644
index 0000000..1733035
--- /dev/null
+++ b/fish/species/green_sunfish/green_sunfish.tres
@@ -0,0 +1,31 @@
+[gd_resource type="Resource" script_class="FishData" load_steps=5 format=3]
+
+[ext_resource type="Script" 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" path="res://fish/fish_availability.gd" id="3_availability"]
+[ext_resource type="Texture2D" path="res://art/exported/creatures/3103.png" id="99_fish_placeholder"]
+
+[sub_resource type="Resource" id="Availability"]
+script = ExtResource("3_availability")
+allow_night = false
+
+[resource]
+script = ExtResource("1_fish_data")
+id = &"green_sunfish"
+display_name = "green sunfish"
+active = true
+catalog_number = 3103
+collection_group = &"Freshwater panfish"
+logbook_fact = "green sunfish tolerate small, warm, cloudy pools better than many relatives. a large mouth lets them tackle insects, crayfish, and fish."
+available_seasons = 7
+allowed_water_types = 1
+rarity = 0
+base_catch_weight = 3.2
+catch_profile = ExtResource("2_profile")
+availability = SubResource("Availability")
+weight_min_lb = 0.1
+weight_max_lb = 1.5
+sell_value_min = 2
+sell_value_max = 5
+sell_value_curve = 1.0
+display_texture = ExtResource("99_fish_placeholder")
diff --git a/fish/species/grouper_atlantic_goliath/grouper_atlantic_goliath.tres b/fish/species/grouper_atlantic_goliath/grouper_atlantic_goliath.tres
new file mode 100644
index 0000000..eb2d2bf
--- /dev/null
+++ b/fish/species/grouper_atlantic_goliath/grouper_atlantic_goliath.tres
@@ -0,0 +1,31 @@
+[gd_resource type="Resource" script_class="FishData" load_steps=5 format=3]
+
+[ext_resource type="Script" 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" path="res://fish/fish_availability.gd" id="3_availability"]
+[ext_resource type="Texture2D" path="res://art/exported/creatures/fish_placeholder.png" id="99_fish_placeholder"]
+
+[sub_resource type="Resource" id="Availability"]
+script = ExtResource("3_availability")
+require_fog = true
+
+[resource]
+script = ExtResource("1_fish_data")
+id = &"grouper_atlantic_goliath"
+display_name = "Atlantic goliath grouper"
+active = true
+catalog_number = 5801
+collection_group = &"Reef predators"
+logbook_fact = "atlantic goliath groupers occupy reefs, wrecks, and mangrove roots. enormous mouths pull prey inward with a sudden pressure drop."
+available_seasons = 15
+allowed_water_types = 2
+rarity = 4
+base_catch_weight = 0.05
+catch_profile = ExtResource("2_profile")
+availability = SubResource("Availability")
+weight_min_lb = 20.0
+weight_max_lb = 800.0
+sell_value_min = 50
+sell_value_max = 125
+sell_value_curve = 0.78
+display_texture = ExtResource("99_fish_placeholder")
diff --git a/fish/species/grouper_giant/grouper_giant.tres b/fish/species/grouper_giant/grouper_giant.tres
new file mode 100644
index 0000000..9051115
--- /dev/null
+++ b/fish/species/grouper_giant/grouper_giant.tres
@@ -0,0 +1,31 @@
+[gd_resource type="Resource" script_class="FishData" load_steps=5 format=3]
+
+[ext_resource type="Script" 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" path="res://fish/fish_availability.gd" id="3_availability"]
+[ext_resource type="Texture2D" path="res://art/exported/creatures/fish_placeholder.png" id="99_fish_placeholder"]
+
+[sub_resource type="Resource" id="Availability"]
+script = ExtResource("3_availability")
+require_fog = true
+
+[resource]
+script = ExtResource("1_fish_data")
+id = &"grouper_giant"
+display_name = "giant grouper"
+active = true
+catalog_number = 5803
+collection_group = &"Reef predators"
+logbook_fact = "giant groupers become massive ambush predators around indo-pacific reefs. juveniles carry bold black-and-yellow markings."
+available_seasons = 15
+allowed_water_types = 2
+rarity = 4
+base_catch_weight = 0.045
+catch_profile = ExtResource("2_profile")
+availability = SubResource("Availability")
+weight_min_lb = 20.0
+weight_max_lb = 880.0
+sell_value_min = 52
+sell_value_max = 128
+sell_value_curve = 0.78
+display_texture = ExtResource("99_fish_placeholder")
diff --git a/fish/species/grouper_gulf/fish_grouper_gulf.png b/fish/species/grouper_gulf/fish_grouper_gulf.png
index e74ea10..b1dd35a 100644
Binary files a/fish/species/grouper_gulf/fish_grouper_gulf.png and b/fish/species/grouper_gulf/fish_grouper_gulf.png differ
diff --git a/fish/species/grouper_gulf/grouper_gulf.tres b/fish/species/grouper_gulf/grouper_gulf.tres
index 1e0446e..ec4b583 100644
--- a/fish/species/grouper_gulf/grouper_gulf.tres
+++ b/fish/species/grouper_gulf/grouper_gulf.tres
@@ -3,7 +3,7 @@
[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"]
+[ext_resource type="Texture2D" path="res://art/exported/creatures/fish_placeholder.png" id="4_texture"]
[sub_resource type="Resource" id="GrouperGulfAvailability"]
script = ExtResource("3_availability_script")
@@ -13,6 +13,11 @@ allowed_location_tags = Array[StringName]([&"ocean"])
script = ExtResource("1_fish_data")
id = &"grouper_gulf"
display_name = "gulf grouper"
+active = true
+catalog_number = 5805
+collection_group = &"Reef predators"
+logbook_fact = "gulf grouper are ambush predators that use a sudden gulp to pull prey into their cavernous mouths."
+available_seasons = 15
allowed_water_types = 2
rarity = 1
base_catch_weight = 0.3
diff --git a/fish/species/grouper_nassau/grouper_nassau.tres b/fish/species/grouper_nassau/grouper_nassau.tres
new file mode 100644
index 0000000..de4b0fd
--- /dev/null
+++ b/fish/species/grouper_nassau/grouper_nassau.tres
@@ -0,0 +1,31 @@
+[gd_resource type="Resource" script_class="FishData" load_steps=5 format=3]
+
+[ext_resource type="Script" 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" path="res://fish/fish_availability.gd" id="3_availability"]
+[ext_resource type="Texture2D" path="res://art/exported/creatures/fish_placeholder.png" id="99_fish_placeholder"]
+
+[sub_resource type="Resource" id="Availability"]
+script = ExtResource("3_availability")
+forbid_fog = true
+
+[resource]
+script = ExtResource("1_fish_data")
+id = &"grouper_nassau"
+display_name = "Nassau grouper"
+active = true
+catalog_number = 5806
+collection_group = &"Reef predators"
+logbook_fact = "nassau groupers shift their bars and colors with mood and surroundings. adults gather at traditional reef sites to spawn."
+available_seasons = 15
+allowed_water_types = 2
+rarity = 3
+base_catch_weight = 0.12
+catch_profile = ExtResource("2_profile")
+availability = SubResource("Availability")
+weight_min_lb = 2.0
+weight_max_lb = 55.0
+sell_value_min = 25
+sell_value_max = 62
+sell_value_curve = 0.82
+display_texture = ExtResource("99_fish_placeholder")
diff --git a/fish/species/grouper_red/fish_grouper_red.png b/fish/species/grouper_red/fish_grouper_red.png
index 33b5481..f0564b7 100644
Binary files a/fish/species/grouper_red/fish_grouper_red.png and b/fish/species/grouper_red/fish_grouper_red.png differ
diff --git a/fish/species/grouper_red/grouper_red.tres b/fish/species/grouper_red/grouper_red.tres
index 5594641..d746cc2 100644
--- a/fish/species/grouper_red/grouper_red.tres
+++ b/fish/species/grouper_red/grouper_red.tres
@@ -3,7 +3,7 @@
[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"]
+[ext_resource type="Texture2D" path="res://art/exported/creatures/fish_placeholder.png" id="4_texture"]
[sub_resource type="Resource" id="GrouperRedAvailability"]
script = ExtResource("3_availability_script")
@@ -13,6 +13,11 @@ allowed_location_tags = Array[StringName]([&"ocean"])
script = ExtResource("1_fish_data")
id = &"grouper_red"
display_name = "red grouper"
+active = true
+catalog_number = 5807
+collection_group = &"Reef predators"
+logbook_fact = "red grouper excavate shelters in reef rubble. the hollows they make can become hiding places for many smaller animals."
+available_seasons = 15
allowed_water_types = 2
rarity = 2
base_catch_weight = 0.2
diff --git a/fish/species/grunt_bluestriped/grunt_bluestriped.tres b/fish/species/grunt_bluestriped/grunt_bluestriped.tres
new file mode 100644
index 0000000..5f247a8
--- /dev/null
+++ b/fish/species/grunt_bluestriped/grunt_bluestriped.tres
@@ -0,0 +1,31 @@
+[gd_resource type="Resource" script_class="FishData" load_steps=5 format=3]
+
+[ext_resource type="Script" 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" path="res://fish/fish_availability.gd" id="3_availability"]
+[ext_resource type="Texture2D" path="res://art/exported/creatures/5902.png" id="99_fish_placeholder"]
+
+[sub_resource type="Resource" id="Availability"]
+script = ExtResource("3_availability")
+allow_night = false
+
+[resource]
+script = ExtResource("1_fish_data")
+id = &"grunt_bluestriped"
+display_name = "bluestriped grunt"
+active = true
+catalog_number = 5902
+collection_group = &"Reef schooling fish"
+logbook_fact = "bluestriped grunts shelter on reefs by day and spread over seagrass to feed at night. grinding throat teeth produce audible grunts."
+available_seasons = 15
+allowed_water_types = 2
+rarity = 0
+base_catch_weight = 2.0
+catch_profile = ExtResource("2_profile")
+availability = SubResource("Availability")
+weight_min_lb = 0.1
+weight_max_lb = 3.0
+sell_value_min = 3
+sell_value_max = 7
+sell_value_curve = 0.94
+display_texture = ExtResource("99_fish_placeholder")
diff --git a/fish/species/grunt_french/grunt_french.tres b/fish/species/grunt_french/grunt_french.tres
new file mode 100644
index 0000000..8e8161e
--- /dev/null
+++ b/fish/species/grunt_french/grunt_french.tres
@@ -0,0 +1,31 @@
+[gd_resource type="Resource" script_class="FishData" load_steps=5 format=3]
+
+[ext_resource type="Script" 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" path="res://fish/fish_availability.gd" id="3_availability"]
+[ext_resource type="Texture2D" path="res://art/exported/creatures/5903.png" id="99_fish_placeholder"]
+
+[sub_resource type="Resource" id="Availability"]
+script = ExtResource("3_availability")
+allow_night = false
+
+[resource]
+script = ExtResource("1_fish_data")
+id = &"grunt_french"
+display_name = "French grunt"
+active = true
+catalog_number = 5903
+collection_group = &"Reef schooling fish"
+logbook_fact = "french grunts gather in yellow-striped schools around reefs and wrecks. they travel into nearby sand and grass beds after dark."
+available_seasons = 15
+allowed_water_types = 2
+rarity = 0
+base_catch_weight = 2.0
+catch_profile = ExtResource("2_profile")
+availability = SubResource("Availability")
+weight_min_lb = 0.1
+weight_max_lb = 3.5
+sell_value_min = 3
+sell_value_max = 7
+sell_value_curve = 0.94
+display_texture = ExtResource("99_fish_placeholder")
diff --git a/fish/species/gulper_eel/gulper_eel.tres b/fish/species/gulper_eel/gulper_eel.tres
new file mode 100644
index 0000000..11245c9
--- /dev/null
+++ b/fish/species/gulper_eel/gulper_eel.tres
@@ -0,0 +1,32 @@
+[gd_resource type="Resource" script_class="FishData" load_steps=5 format=3]
+
+[ext_resource type="Script" 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" path="res://fish/fish_availability.gd" id="3_availability"]
+[ext_resource type="Texture2D" path="res://art/exported/creatures/fish_placeholder.png" id="99_fish_placeholder"]
+
+[sub_resource type="Resource" id="Availability"]
+script = ExtResource("3_availability")
+allow_day = false
+require_fog = true
+
+[resource]
+script = ExtResource("1_fish_data")
+id = &"gulper_eel"
+display_name = "gulper eel"
+active = true
+catalog_number = 1803
+collection_group = &"Deep-sea oddities"
+logbook_fact = "gulper eels unfold enormous mouths to engulf prey in the deep sea. a whip-like tail ends in a small light-producing organ."
+available_seasons = 15
+allowed_water_types = 2
+rarity = 4
+base_catch_weight = 0.04
+catch_profile = ExtResource("2_profile")
+availability = SubResource("Availability")
+weight_min_lb = 0.5
+weight_max_lb = 18.0
+sell_value_min = 44
+sell_value_max = 92
+sell_value_curve = 0.86
+display_texture = ExtResource("99_fish_placeholder")
diff --git a/fish/species/guppy/guppy.tres b/fish/species/guppy/guppy.tres
new file mode 100644
index 0000000..9771dfb
--- /dev/null
+++ b/fish/species/guppy/guppy.tres
@@ -0,0 +1,31 @@
+[gd_resource type="Resource" script_class="FishData" load_steps=5 format=3]
+
+[ext_resource type="Script" 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" path="res://fish/fish_availability.gd" id="3_availability"]
+[ext_resource type="Texture2D" path="res://art/exported/creatures/fish_placeholder.png" id="99_fish_placeholder"]
+
+[sub_resource type="Resource" id="Availability"]
+script = ExtResource("3_availability")
+allow_night = false
+
+[resource]
+script = ExtResource("1_fish_data")
+id = &"guppy"
+display_name = "guppy"
+active = true
+catalog_number = 2101
+collection_group = &"Freshwater aquarium fish"
+logbook_fact = "guppies are livebearers whose young arrive ready to swim. males display bright colors while females often favor larger, less flashy bodies."
+available_seasons = 15
+allowed_water_types = 1
+rarity = 0
+base_catch_weight = 1.5
+catch_profile = ExtResource("2_profile")
+availability = SubResource("Availability")
+weight_min_lb = 0.01
+weight_max_lb = 0.15
+sell_value_min = 2
+sell_value_max = 5
+sell_value_curve = 0.98
+display_texture = ExtResource("99_fish_placeholder")
diff --git a/fish/species/haddock/haddock.tres b/fish/species/haddock/haddock.tres
new file mode 100644
index 0000000..ec5db7d
--- /dev/null
+++ b/fish/species/haddock/haddock.tres
@@ -0,0 +1,31 @@
+[gd_resource type="Resource" script_class="FishData" load_steps=5 format=3]
+
+[ext_resource type="Script" 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" path="res://fish/fish_availability.gd" id="3_availability"]
+[ext_resource type="Texture2D" path="res://art/exported/creatures/fish_placeholder.png" id="99_fish_placeholder"]
+
+[sub_resource type="Resource" id="Availability"]
+script = ExtResource("3_availability")
+allow_night = false
+
+[resource]
+script = ExtResource("1_fish_data")
+id = &"haddock"
+display_name = "haddock"
+active = true
+catalog_number = 1405
+collection_group = &"Cold shelf fish"
+logbook_fact = "haddock are recognized by a dark shoulder mark and black lateral line. schools forage over cold seafloors for small bottom animals."
+available_seasons = 13
+allowed_water_types = 2
+rarity = 0
+base_catch_weight = 2.0
+catch_profile = ExtResource("2_profile")
+availability = SubResource("Availability")
+weight_min_lb = 0.5
+weight_max_lb = 20.0
+sell_value_min = 4
+sell_value_max = 11
+sell_value_curve = 0.9
+display_texture = ExtResource("99_fish_placeholder")
diff --git a/fish/species/hake_pacific/hake_pacific.tres b/fish/species/hake_pacific/hake_pacific.tres
new file mode 100644
index 0000000..2a7293b
--- /dev/null
+++ b/fish/species/hake_pacific/hake_pacific.tres
@@ -0,0 +1,31 @@
+[gd_resource type="Resource" script_class="FishData" load_steps=5 format=3]
+
+[ext_resource type="Script" 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" path="res://fish/fish_availability.gd" id="3_availability"]
+[ext_resource type="Texture2D" path="res://art/exported/creatures/fish_placeholder.png" id="99_fish_placeholder"]
+
+[sub_resource type="Resource" id="Availability"]
+script = ExtResource("3_availability")
+allow_day = false
+
+[resource]
+script = ExtResource("1_fish_data")
+id = &"hake_pacific"
+display_name = "pacific hake"
+active = true
+catalog_number = 1407
+collection_group = &"Cold shelf fish"
+logbook_fact = "pacific hake rise from deep water at night to feed on krill and fish. large schools migrate along the west coast with changing seasons."
+available_seasons = 13
+allowed_water_types = 2
+rarity = 0
+base_catch_weight = 1.7
+catch_profile = ExtResource("2_profile")
+availability = SubResource("Availability")
+weight_min_lb = 0.3
+weight_max_lb = 8.0
+sell_value_min = 3
+sell_value_max = 9
+sell_value_curve = 0.92
+display_texture = ExtResource("99_fish_placeholder")
diff --git a/fish/species/hake_silver/hake_silver.tres b/fish/species/hake_silver/hake_silver.tres
new file mode 100644
index 0000000..ffb42ff
--- /dev/null
+++ b/fish/species/hake_silver/hake_silver.tres
@@ -0,0 +1,31 @@
+[gd_resource type="Resource" script_class="FishData" load_steps=5 format=3]
+
+[ext_resource type="Script" 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" path="res://fish/fish_availability.gd" id="3_availability"]
+[ext_resource type="Texture2D" path="res://art/exported/creatures/fish_placeholder.png" id="99_fish_placeholder"]
+
+[sub_resource type="Resource" id="Availability"]
+script = ExtResource("3_availability")
+allow_day = false
+
+[resource]
+script = ExtResource("1_fish_data")
+id = &"hake_silver"
+display_name = "silver hake"
+active = true
+catalog_number = 1408
+collection_group = &"Cold shelf fish"
+logbook_fact = "silver hake hunt fish and crustaceans over sandy atlantic shelves. they move upward after dark and return toward the bottom by day."
+available_seasons = 13
+allowed_water_types = 2
+rarity = 0
+base_catch_weight = 1.7
+catch_profile = ExtResource("2_profile")
+availability = SubResource("Availability")
+weight_min_lb = 0.2
+weight_max_lb = 5.0
+sell_value_min = 3
+sell_value_max = 8
+sell_value_curve = 0.92
+display_texture = ExtResource("99_fish_placeholder")
diff --git a/fish/species/halfbeak_ballyhoo/halfbeak_ballyhoo.tres b/fish/species/halfbeak_ballyhoo/halfbeak_ballyhoo.tres
new file mode 100644
index 0000000..6d7fe32
--- /dev/null
+++ b/fish/species/halfbeak_ballyhoo/halfbeak_ballyhoo.tres
@@ -0,0 +1,31 @@
+[gd_resource type="Resource" script_class="FishData" load_steps=5 format=3]
+
+[ext_resource type="Script" 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" path="res://fish/fish_availability.gd" id="3_availability"]
+[ext_resource type="Texture2D" path="res://art/exported/creatures/6601.png" id="99_fish_placeholder"]
+
+[sub_resource type="Resource" id="Availability"]
+script = ExtResource("3_availability")
+allow_night = false
+
+[resource]
+script = ExtResource("1_fish_data")
+id = &"halfbeak_ballyhoo"
+display_name = "ballyhoo"
+active = true
+catalog_number = 6601
+collection_group = &"Surface ocean fish"
+logbook_fact = "ballyhoo skim warm ocean surfaces with an extended lower jaw. tight schools scatter into quick leaps when predators strike."
+available_seasons = 15
+allowed_water_types = 2
+rarity = 0
+base_catch_weight = 2.5
+catch_profile = ExtResource("2_profile")
+availability = SubResource("Availability")
+weight_min_lb = 0.05
+weight_max_lb = 1.0
+sell_value_min = 2
+sell_value_max = 5
+sell_value_curve = 0.98
+display_texture = ExtResource("99_fish_placeholder")
diff --git a/fish/species/halibut_atlantic/halibut_atlantic.tres b/fish/species/halibut_atlantic/halibut_atlantic.tres
new file mode 100644
index 0000000..ead724a
--- /dev/null
+++ b/fish/species/halibut_atlantic/halibut_atlantic.tres
@@ -0,0 +1,30 @@
+[gd_resource type="Resource" script_class="FishData" load_steps=5 format=3]
+
+[ext_resource type="Script" 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" path="res://fish/fish_availability.gd" id="3_availability"]
+[ext_resource type="Texture2D" path="res://art/exported/creatures/fish_placeholder.png" id="99_fish_placeholder"]
+
+[sub_resource type="Resource" id="Availability"]
+script = ExtResource("3_availability")
+
+[resource]
+script = ExtResource("1_fish_data")
+id = &"halibut_atlantic"
+display_name = "atlantic halibut"
+active = true
+catalog_number = 3701
+collection_group = &"Large flatfish"
+logbook_fact = "atlantic halibut are immense right-eyed flatfish of cold northern seas. adults ambush fish and squid from deep rocky or sandy bottoms."
+available_seasons = 13
+allowed_water_types = 2
+rarity = 3
+base_catch_weight = 0.1
+catch_profile = ExtResource("2_profile")
+availability = SubResource("Availability")
+weight_min_lb = 10.0
+weight_max_lb = 300.0
+sell_value_min = 32
+sell_value_max = 82
+sell_value_curve = 0.8
+display_texture = ExtResource("99_fish_placeholder")
diff --git a/fish/species/halibut_pacific/halibut_pacific.tres b/fish/species/halibut_pacific/halibut_pacific.tres
new file mode 100644
index 0000000..3856b5a
--- /dev/null
+++ b/fish/species/halibut_pacific/halibut_pacific.tres
@@ -0,0 +1,30 @@
+[gd_resource type="Resource" script_class="FishData" load_steps=5 format=3]
+
+[ext_resource type="Script" 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" path="res://fish/fish_availability.gd" id="3_availability"]
+[ext_resource type="Texture2D" path="res://art/exported/creatures/fish_placeholder.png" id="99_fish_placeholder"]
+
+[sub_resource type="Resource" id="Availability"]
+script = ExtResource("3_availability")
+
+[resource]
+script = ExtResource("1_fish_data")
+id = &"halibut_pacific"
+display_name = "pacific halibut"
+active = true
+catalog_number = 3702
+collection_group = &"Large flatfish"
+logbook_fact = "pacific halibut lie camouflaged on cold continental shelves. powerful flat bodies can travel long distances between feeding and spawning grounds."
+available_seasons = 13
+allowed_water_types = 2
+rarity = 3
+base_catch_weight = 0.1
+catch_profile = ExtResource("2_profile")
+availability = SubResource("Availability")
+weight_min_lb = 10.0
+weight_max_lb = 350.0
+sell_value_min = 34
+sell_value_max = 86
+sell_value_curve = 0.8
+display_texture = ExtResource("99_fish_placeholder")
diff --git a/fish/species/hellbender/hellbender.tres b/fish/species/hellbender/hellbender.tres
new file mode 100644
index 0000000..2c55863
--- /dev/null
+++ b/fish/species/hellbender/hellbender.tres
@@ -0,0 +1,32 @@
+[gd_resource type="Resource" script_class="FishData" load_steps=5 format=3]
+
+[ext_resource type="Script" 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" path="res://fish/fish_availability.gd" id="3_availability"]
+[ext_resource type="Texture2D" path="res://art/exported/creatures/fish_placeholder.png" id="99_fish_placeholder"]
+
+[sub_resource type="Resource" id="Availability"]
+script = ExtResource("3_availability")
+allow_day = false
+require_rain = true
+
+[resource]
+script = ExtResource("1_fish_data")
+id = &"hellbender"
+display_name = "hellbender"
+active = true
+catalog_number = 2002
+collection_group = &"Freshwater amphibians"
+logbook_fact = "hellbenders hide beneath large rocks in cool, clean rivers. folds of loose skin absorb oxygen while the flattened body grips the current."
+available_seasons = 7
+allowed_water_types = 1
+rarity = 3
+base_catch_weight = 0.1
+catch_profile = ExtResource("2_profile")
+availability = SubResource("Availability")
+weight_min_lb = 0.5
+weight_max_lb = 5.5
+sell_value_min = 24
+sell_value_max = 54
+sell_value_curve = 0.91
+display_texture = ExtResource("99_fish_placeholder")
diff --git a/fish/species/herring_atlantic/herring_atlantic.tres b/fish/species/herring_atlantic/herring_atlantic.tres
new file mode 100644
index 0000000..e193dcc
--- /dev/null
+++ b/fish/species/herring_atlantic/herring_atlantic.tres
@@ -0,0 +1,30 @@
+[gd_resource type="Resource" script_class="FishData" load_steps=5 format=3]
+
+[ext_resource type="Script" 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" path="res://fish/fish_availability.gd" id="3_availability"]
+[ext_resource type="Texture2D" path="res://art/exported/creatures/fish_placeholder.png" id="99_fish_placeholder"]
+
+[sub_resource type="Resource" id="Availability"]
+script = ExtResource("3_availability")
+
+[resource]
+script = ExtResource("1_fish_data")
+id = &"herring_atlantic"
+display_name = "atlantic herring"
+active = true
+catalog_number = 1301
+collection_group = &"Cold schooling fish"
+logbook_fact = "atlantic herring gather in immense schools that move between feeding and spawning grounds. they filter plankton with fine gill rakers."
+available_seasons = 13
+allowed_water_types = 2
+rarity = 0
+base_catch_weight = 3.0
+catch_profile = ExtResource("2_profile")
+availability = SubResource("Availability")
+weight_min_lb = 0.1
+weight_max_lb = 1.5
+sell_value_min = 2
+sell_value_max = 5
+sell_value_curve = 0.98
+display_texture = ExtResource("99_fish_placeholder")
diff --git a/fish/species/herring_pacific/herring_pacific.tres b/fish/species/herring_pacific/herring_pacific.tres
new file mode 100644
index 0000000..7d8e754
--- /dev/null
+++ b/fish/species/herring_pacific/herring_pacific.tres
@@ -0,0 +1,31 @@
+[gd_resource type="Resource" script_class="FishData" load_steps=5 format=3]
+
+[ext_resource type="Script" 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" path="res://fish/fish_availability.gd" id="3_availability"]
+[ext_resource type="Texture2D" path="res://art/exported/creatures/fish_placeholder.png" id="99_fish_placeholder"]
+
+[sub_resource type="Resource" id="Availability"]
+script = ExtResource("3_availability")
+require_rain = true
+
+[resource]
+script = ExtResource("1_fish_data")
+id = &"herring_pacific"
+display_name = "pacific herring"
+active = true
+catalog_number = 1304
+collection_group = &"Cold schooling fish"
+logbook_fact = "pacific herring return to coastal shallows to coat plants and rocks with eggs. their oily schools support entire marine food webs."
+available_seasons = 13
+allowed_water_types = 2
+rarity = 0
+base_catch_weight = 2.8
+catch_profile = ExtResource("2_profile")
+availability = SubResource("Availability")
+weight_min_lb = 0.1
+weight_max_lb = 1.5
+sell_value_min = 2
+sell_value_max = 5
+sell_value_curve = 0.98
+display_texture = ExtResource("99_fish_placeholder")
diff --git a/fish/species/hogfish/hogfish.tres b/fish/species/hogfish/hogfish.tres
new file mode 100644
index 0000000..0e94a20
--- /dev/null
+++ b/fish/species/hogfish/hogfish.tres
@@ -0,0 +1,31 @@
+[gd_resource type="Resource" script_class="FishData" load_steps=5 format=3]
+
+[ext_resource type="Script" 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" path="res://fish/fish_availability.gd" id="3_availability"]
+[ext_resource type="Texture2D" path="res://art/exported/creatures/6103.png" id="99_fish_placeholder"]
+
+[sub_resource type="Resource" id="Availability"]
+script = ExtResource("3_availability")
+allow_night = false
+
+[resource]
+script = ExtResource("1_fish_data")
+id = &"hogfish"
+display_name = "hogfish"
+active = true
+catalog_number = 6103
+collection_group = &"Reef wrasses"
+logbook_fact = "hogfish root through sand with long pig-like snouts in search of shellfish. adults can change from female to male as social groups shift."
+available_seasons = 15
+allowed_water_types = 2
+rarity = 2
+base_catch_weight = 0.3
+catch_profile = ExtResource("2_profile")
+availability = SubResource("Availability")
+weight_min_lb = 0.5
+weight_max_lb = 20.0
+sell_value_min = 12
+sell_value_max = 30
+sell_value_curve = 0.86
+display_texture = ExtResource("99_fish_placeholder")
diff --git a/fish/species/ide/ide.tres b/fish/species/ide/ide.tres
new file mode 100644
index 0000000..aea8907
--- /dev/null
+++ b/fish/species/ide/ide.tres
@@ -0,0 +1,31 @@
+[gd_resource type="Resource" script_class="FishData" load_steps=5 format=3]
+
+[ext_resource type="Script" 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" path="res://fish/fish_availability.gd" id="3_availability"]
+[ext_resource type="Texture2D" path="res://art/exported/creatures/fish_placeholder.png" id="99_fish_placeholder"]
+
+[sub_resource type="Resource" id="Availability"]
+script = ExtResource("3_availability")
+allow_night = false
+
+[resource]
+script = ExtResource("1_fish_data")
+id = &"ide"
+display_name = "ide"
+active = true
+catalog_number = 2507
+collection_group = &"Freshwater coarse fish"
+logbook_fact = "ide move between rivers, lakes, and brackish coastal water in loose schools. adults eat insects, crustaceans, plants, and small fish."
+available_seasons = 7
+allowed_water_types = 1
+rarity = 1
+base_catch_weight = 0.8
+catch_profile = ExtResource("2_profile")
+availability = SubResource("Availability")
+weight_min_lb = 0.3
+weight_max_lb = 9.0
+sell_value_min = 6
+sell_value_max = 14
+sell_value_curve = 0.9
+display_texture = ExtResource("99_fish_placeholder")
diff --git a/fish/species/jack_crevalle/jack_crevalle.tres b/fish/species/jack_crevalle/jack_crevalle.tres
new file mode 100644
index 0000000..4110dcc
--- /dev/null
+++ b/fish/species/jack_crevalle/jack_crevalle.tres
@@ -0,0 +1,32 @@
+[gd_resource type="Resource" script_class="FishData" load_steps=5 format=3]
+
+[ext_resource type="Script" 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" path="res://fish/fish_availability.gd" id="3_availability"]
+[ext_resource type="Texture2D" path="res://art/exported/creatures/fish_placeholder.png" id="99_fish_placeholder"]
+
+[sub_resource type="Resource" id="Availability"]
+script = ExtResource("3_availability")
+allow_night = false
+require_rain = true
+
+[resource]
+script = ExtResource("1_fish_data")
+id = &"jack_crevalle"
+display_name = "crevalle jack"
+active = true
+catalog_number = 705
+collection_group = &"Coastal pelagic fish"
+logbook_fact = "crevalle jacks patrol warm coasts in fast-moving schools. their deep bodies and forked tails deliver long, powerful runs."
+available_seasons = 6
+allowed_water_types = 2
+rarity = 1
+base_catch_weight = 0.8
+catch_profile = ExtResource("2_profile")
+availability = SubResource("Availability")
+weight_min_lb = 1.0
+weight_max_lb = 35.0
+sell_value_min = 7
+sell_value_max = 20
+sell_value_curve = 0.84
+display_texture = ExtResource("99_fish_placeholder")
diff --git a/fish/species/jelly_crystal/jelly_crystal.tres b/fish/species/jelly_crystal/jelly_crystal.tres
new file mode 100644
index 0000000..f8746fa
--- /dev/null
+++ b/fish/species/jelly_crystal/jelly_crystal.tres
@@ -0,0 +1,32 @@
+[gd_resource type="Resource" script_class="FishData" load_steps=5 format=3]
+
+[ext_resource type="Script" 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" path="res://fish/fish_availability.gd" id="3_availability"]
+[ext_resource type="Texture2D" path="res://art/exported/creatures/fish_placeholder.png" id="99_fish_placeholder"]
+
+[sub_resource type="Resource" id="Availability"]
+script = ExtResource("3_availability")
+allow_day = false
+forbid_fog = true
+
+[resource]
+script = ExtResource("1_fish_data")
+id = &"jelly_crystal"
+display_name = "crystal jelly"
+active = true
+catalog_number = 3501
+collection_group = &"Jellies"
+logbook_fact = "crystal jellies are nearly transparent except for glowing points around the bell. their green fluorescent protein transformed biological research."
+available_seasons = 9
+allowed_water_types = 2
+rarity = 2
+base_catch_weight = 0.24
+catch_profile = ExtResource("2_profile")
+availability = SubResource("Availability")
+weight_min_lb = 0.05
+weight_max_lb = 4.0
+sell_value_min = 15
+sell_value_max = 34
+sell_value_curve = 0.93
+display_texture = ExtResource("99_fish_placeholder")
diff --git a/fish/species/jelly_lions_mane/jelly_lions_mane.tres b/fish/species/jelly_lions_mane/jelly_lions_mane.tres
new file mode 100644
index 0000000..c7c6fbb
--- /dev/null
+++ b/fish/species/jelly_lions_mane/jelly_lions_mane.tres
@@ -0,0 +1,32 @@
+[gd_resource type="Resource" script_class="FishData" load_steps=5 format=3]
+
+[ext_resource type="Script" 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" path="res://fish/fish_availability.gd" id="3_availability"]
+[ext_resource type="Texture2D" path="res://art/exported/creatures/fish_placeholder.png" id="99_fish_placeholder"]
+
+[sub_resource type="Resource" id="Availability"]
+script = ExtResource("3_availability")
+allow_day = false
+require_fog = true
+
+[resource]
+script = ExtResource("1_fish_data")
+id = &"jelly_lions_mane"
+display_name = "lion's mane jelly"
+active = true
+catalog_number = 3502
+collection_group = &"Jellies"
+logbook_fact = "lion's mane jellies trail dense curtains of tentacles through cold seas. the bell darkens from yellow to deep red as the animal grows."
+available_seasons = 13
+allowed_water_types = 2
+rarity = 3
+base_catch_weight = 0.11
+catch_profile = ExtResource("2_profile")
+availability = SubResource("Availability")
+weight_min_lb = 1.0
+weight_max_lb = 200.0
+sell_value_min = 24
+sell_value_max = 58
+sell_value_curve = 0.82
+display_texture = ExtResource("99_fish_placeholder")
diff --git a/fish/species/jelly_moon/jelly_moon.tres b/fish/species/jelly_moon/jelly_moon.tres
new file mode 100644
index 0000000..378c11d
--- /dev/null
+++ b/fish/species/jelly_moon/jelly_moon.tres
@@ -0,0 +1,31 @@
+[gd_resource type="Resource" script_class="FishData" load_steps=5 format=3]
+
+[ext_resource type="Script" 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" path="res://fish/fish_availability.gd" id="3_availability"]
+[ext_resource type="Texture2D" path="res://art/exported/creatures/fish_placeholder.png" id="99_fish_placeholder"]
+
+[sub_resource type="Resource" id="Availability"]
+script = ExtResource("3_availability")
+allow_day = false
+
+[resource]
+script = ExtResource("1_fish_data")
+id = &"jelly_moon"
+display_name = "moon jelly"
+active = true
+catalog_number = 3503
+collection_group = &"Jellies"
+logbook_fact = "moon jellies pulse gently with four horseshoe-shaped organs visible through the bell. short tentacles sweep plankton toward the mouth."
+available_seasons = 15
+allowed_water_types = 2
+rarity = 1
+base_catch_weight = 0.7
+catch_profile = ExtResource("2_profile")
+availability = SubResource("Availability")
+weight_min_lb = 0.05
+weight_max_lb = 5.0
+sell_value_min = 6
+sell_value_max = 14
+sell_value_curve = 0.94
+display_texture = ExtResource("99_fish_placeholder")
diff --git a/fish/species/jelly_pacific_sea_nettle/jelly_pacific_sea_nettle.tres b/fish/species/jelly_pacific_sea_nettle/jelly_pacific_sea_nettle.tres
new file mode 100644
index 0000000..b31f4b3
--- /dev/null
+++ b/fish/species/jelly_pacific_sea_nettle/jelly_pacific_sea_nettle.tres
@@ -0,0 +1,32 @@
+[gd_resource type="Resource" script_class="FishData" load_steps=5 format=3]
+
+[ext_resource type="Script" 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" path="res://fish/fish_availability.gd" id="3_availability"]
+[ext_resource type="Texture2D" path="res://art/exported/creatures/fish_placeholder.png" id="99_fish_placeholder"]
+
+[sub_resource type="Resource" id="Availability"]
+script = ExtResource("3_availability")
+allow_night = false
+forbid_fog = true
+
+[resource]
+script = ExtResource("1_fish_data")
+id = &"jelly_pacific_sea_nettle"
+display_name = "Pacific sea nettle"
+active = true
+catalog_number = 3504
+collection_group = &"Jellies"
+logbook_fact = "pacific sea nettles carry rust-colored stripes across pale bells. long tentacles and frilled mouth arms capture plankton and small fish."
+available_seasons = 6
+allowed_water_types = 2
+rarity = 2
+base_catch_weight = 0.28
+catch_profile = ExtResource("2_profile")
+availability = SubResource("Availability")
+weight_min_lb = 0.2
+weight_max_lb = 12.0
+sell_value_min = 14
+sell_value_max = 32
+sell_value_curve = 0.9
+display_texture = ExtResource("99_fish_placeholder")
diff --git a/fish/species/jelly_sea_wasp/jelly_sea_wasp.tres b/fish/species/jelly_sea_wasp/jelly_sea_wasp.tres
new file mode 100644
index 0000000..29bed0a
--- /dev/null
+++ b/fish/species/jelly_sea_wasp/jelly_sea_wasp.tres
@@ -0,0 +1,32 @@
+[gd_resource type="Resource" script_class="FishData" load_steps=5 format=3]
+
+[ext_resource type="Script" 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" path="res://fish/fish_availability.gd" id="3_availability"]
+[ext_resource type="Texture2D" path="res://art/exported/creatures/fish_placeholder.png" id="99_fish_placeholder"]
+
+[sub_resource type="Resource" id="Availability"]
+script = ExtResource("3_availability")
+allow_night = false
+require_rain = true
+
+[resource]
+script = ExtResource("1_fish_data")
+id = &"jelly_sea_wasp"
+display_name = "sea wasp"
+active = true
+catalog_number = 3505
+collection_group = &"Jellies"
+logbook_fact = "sea wasps have box-shaped bells and clusters of eyes along the lower edge. their long tentacles deliver exceptionally potent venom."
+available_seasons = 15
+allowed_water_types = 2
+rarity = 4
+base_catch_weight = 0.035
+catch_profile = ExtResource("2_profile")
+availability = SubResource("Availability")
+weight_min_lb = 0.2
+weight_max_lb = 6.0
+sell_value_min = 42
+sell_value_max = 90
+sell_value_curve = 0.9
+display_texture = ExtResource("99_fish_placeholder")
diff --git a/fish/species/knifefish_clown/knifefish_clown.tres b/fish/species/knifefish_clown/knifefish_clown.tres
new file mode 100644
index 0000000..c83b877
--- /dev/null
+++ b/fish/species/knifefish_clown/knifefish_clown.tres
@@ -0,0 +1,32 @@
+[gd_resource type="Resource" script_class="FishData" load_steps=5 format=3]
+
+[ext_resource type="Script" 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" path="res://fish/fish_availability.gd" id="3_availability"]
+[ext_resource type="Texture2D" path="res://art/exported/creatures/fish_placeholder.png" id="99_fish_placeholder"]
+
+[sub_resource type="Resource" id="Availability"]
+script = ExtResource("3_availability")
+allow_day = false
+require_fog = true
+
+[resource]
+script = ExtResource("1_fish_data")
+id = &"knifefish_clown"
+display_name = "clown knifefish"
+active = true
+catalog_number = 7501
+collection_group = &"Tropical freshwater fish"
+logbook_fact = "clown knifefish undulate one long belly fin to glide forward or backward. dark eye-like spots line their silver nocturnal bodies."
+available_seasons = 15
+allowed_water_types = 1
+rarity = 2
+base_catch_weight = 0.25
+catch_profile = ExtResource("2_profile")
+availability = SubResource("Availability")
+weight_min_lb = 1.0
+weight_max_lb = 25.0
+sell_value_min = 16
+sell_value_max = 38
+sell_value_curve = 0.86
+display_texture = ExtResource("99_fish_placeholder")
diff --git a/fish/species/koi/koi.tres b/fish/species/koi/koi.tres
new file mode 100644
index 0000000..58c7c29
--- /dev/null
+++ b/fish/species/koi/koi.tres
@@ -0,0 +1,32 @@
+[gd_resource type="Resource" script_class="FishData" load_steps=5 format=3]
+
+[ext_resource type="Script" 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" path="res://fish/fish_availability.gd" id="3_availability"]
+[ext_resource type="Texture2D" path="res://art/exported/creatures/fish_placeholder.png" id="99_fish_placeholder"]
+
+[sub_resource type="Resource" id="Availability"]
+script = ExtResource("3_availability")
+allow_night = false
+forbid_fog = true
+
+[resource]
+script = ExtResource("1_fish_data")
+id = &"koi"
+display_name = "koi"
+active = true
+catalog_number = 3003
+collection_group = &"Freshwater ornamental"
+logbook_fact = "koi are colorful domesticated forms of common carp selected over many generations. each fish carries a distinctive arrangement of patches and scales."
+available_seasons = 15
+allowed_water_types = 1
+rarity = 2
+base_catch_weight = 0.28
+catch_profile = ExtResource("2_profile")
+availability = SubResource("Availability")
+weight_min_lb = 0.5
+weight_max_lb = 35.0
+sell_value_min = 14
+sell_value_max = 34
+sell_value_curve = 0.87
+display_texture = ExtResource("99_fish_placeholder")
diff --git a/fish/species/krill_antarctic/krill_antarctic.tres b/fish/species/krill_antarctic/krill_antarctic.tres
new file mode 100644
index 0000000..8cdf8e6
--- /dev/null
+++ b/fish/species/krill_antarctic/krill_antarctic.tres
@@ -0,0 +1,31 @@
+[gd_resource type="Resource" script_class="FishData" load_steps=5 format=3]
+
+[ext_resource type="Script" 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" path="res://fish/fish_availability.gd" id="3_availability"]
+[ext_resource type="Texture2D" path="res://art/exported/creatures/fish_placeholder.png" id="99_fish_placeholder"]
+
+[sub_resource type="Resource" id="Availability"]
+script = ExtResource("3_availability")
+allow_day = false
+
+[resource]
+script = ExtResource("1_fish_data")
+id = &"krill_antarctic"
+display_name = "Antarctic krill"
+active = true
+catalog_number = 3802
+collection_group = &"Lobsters and shrimp"
+logbook_fact = "antarctic krill form enormous swarms beneath southern seas and ice. these small crustaceans feed whales, seals, penguins, and fish."
+available_seasons = 15
+allowed_water_types = 2
+rarity = 2
+base_catch_weight = 0.3
+catch_profile = ExtResource("2_profile")
+availability = SubResource("Availability")
+weight_min_lb = 0.001
+weight_max_lb = 0.01
+sell_value_min = 10
+sell_value_max = 22
+sell_value_curve = 0.99
+display_texture = ExtResource("99_fish_placeholder")
diff --git a/fish/species/ladyfish/ladyfish.tres b/fish/species/ladyfish/ladyfish.tres
new file mode 100644
index 0000000..57c277c
--- /dev/null
+++ b/fish/species/ladyfish/ladyfish.tres
@@ -0,0 +1,32 @@
+[gd_resource type="Resource" script_class="FishData" load_steps=5 format=3]
+
+[ext_resource type="Script" 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" path="res://fish/fish_availability.gd" id="3_availability"]
+[ext_resource type="Texture2D" path="res://art/exported/creatures/7403.png" id="99_fish_placeholder"]
+
+[sub_resource type="Resource" id="Availability"]
+script = ExtResource("3_availability")
+allow_night = false
+require_rain = true
+
+[resource]
+script = ExtResource("1_fish_data")
+id = &"ladyfish"
+display_name = "ladyfish"
+active = true
+catalog_number = 7403
+collection_group = &"Tropical flats fish"
+logbook_fact = "ladyfish race through warm shallows after schooling prey and often leap when pursued. a deeply forked tail powers their slender bodies."
+available_seasons = 15
+allowed_water_types = 2
+rarity = 0
+base_catch_weight = 1.7
+catch_profile = ExtResource("2_profile")
+availability = SubResource("Availability")
+weight_min_lb = 0.2
+weight_max_lb = 8.0
+sell_value_min = 4
+sell_value_max = 10
+sell_value_curve = 0.9
+display_texture = ExtResource("99_fish_placeholder")
diff --git a/fish/species/lanternfish_spotted/lanternfish_spotted.tres b/fish/species/lanternfish_spotted/lanternfish_spotted.tres
new file mode 100644
index 0000000..28446b7
--- /dev/null
+++ b/fish/species/lanternfish_spotted/lanternfish_spotted.tres
@@ -0,0 +1,31 @@
+[gd_resource type="Resource" script_class="FishData" load_steps=5 format=3]
+
+[ext_resource type="Script" 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" path="res://fish/fish_availability.gd" id="3_availability"]
+[ext_resource type="Texture2D" path="res://art/exported/creatures/fish_placeholder.png" id="99_fish_placeholder"]
+
+[sub_resource type="Resource" id="Availability"]
+script = ExtResource("3_availability")
+allow_day = false
+
+[resource]
+script = ExtResource("1_fish_data")
+id = &"lanternfish_spotted"
+display_name = "spotted lanternfish"
+active = true
+catalog_number = 1806
+collection_group = &"Deep-sea oddities"
+logbook_fact = "spotted lanternfish carry rows of light-producing organs along their bodies. immense schools rise toward the surface each night."
+available_seasons = 15
+allowed_water_types = 2
+rarity = 2
+base_catch_weight = 0.24
+catch_profile = ExtResource("2_profile")
+availability = SubResource("Availability")
+weight_min_lb = 0.05
+weight_max_lb = 0.5
+sell_value_min = 14
+sell_value_max = 30
+sell_value_curve = 0.95
+display_texture = ExtResource("99_fish_placeholder")
diff --git a/fish/species/largemouth_bass/largemouth_bass.tres b/fish/species/largemouth_bass/largemouth_bass.tres
new file mode 100644
index 0000000..aa89d04
--- /dev/null
+++ b/fish/species/largemouth_bass/largemouth_bass.tres
@@ -0,0 +1,30 @@
+[gd_resource type="Resource" script_class="FishData" load_steps=5 format=3]
+
+[ext_resource type="Script" 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" path="res://fish/fish_availability.gd" id="3_availability"]
+[ext_resource type="Texture2D" path="res://art/exported/creatures/fish_placeholder.png" id="99_fish_placeholder"]
+
+[sub_resource type="Resource" id="Availability"]
+script = ExtResource("3_availability")
+
+[resource]
+script = ExtResource("1_fish_data")
+id = &"largemouth_bass"
+display_name = "largemouth bass"
+active = true
+catalog_number = 2201
+collection_group = &"Freshwater bass"
+logbook_fact = "largemouth bass wait among weeds and timber before lunging at prey. the upper jaw extends beyond the eye when the mouth is closed."
+available_seasons = 7
+allowed_water_types = 1
+rarity = 0
+base_catch_weight = 2.5
+catch_profile = ExtResource("2_profile")
+availability = SubResource("Availability")
+weight_min_lb = 0.5
+weight_max_lb = 18.0
+sell_value_min = 4
+sell_value_max = 12
+sell_value_curve = 0.9
+display_texture = ExtResource("99_fish_placeholder")
diff --git a/fish/species/lingcod/lingcod.tres b/fish/species/lingcod/lingcod.tres
new file mode 100644
index 0000000..807dc8b
--- /dev/null
+++ b/fish/species/lingcod/lingcod.tres
@@ -0,0 +1,30 @@
+[gd_resource type="Resource" script_class="FishData" load_steps=5 format=3]
+
+[ext_resource type="Script" 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" path="res://fish/fish_availability.gd" id="3_availability"]
+[ext_resource type="Texture2D" path="res://art/exported/creatures/fish_placeholder.png" id="99_fish_placeholder"]
+
+[sub_resource type="Resource" id="Availability"]
+script = ExtResource("3_availability")
+
+[resource]
+script = ExtResource("1_fish_data")
+id = &"lingcod"
+display_name = "lingcod"
+active = true
+catalog_number = 7001
+collection_group = &"Temperate reef predators"
+logbook_fact = "lingcod guard rocky dens and ambush fish with a huge tooth-lined mouth. despite the name, they are greenling relatives rather than cod."
+available_seasons = 13
+allowed_water_types = 2
+rarity = 2
+base_catch_weight = 0.25
+catch_profile = ExtResource("2_profile")
+availability = SubResource("Availability")
+weight_min_lb = 2.0
+weight_max_lb = 60.0
+sell_value_min = 14
+sell_value_max = 38
+sell_value_curve = 0.83
+display_texture = ExtResource("99_fish_placeholder")
diff --git a/fish/species/lionfish_red/lionfish_red.tres b/fish/species/lionfish_red/lionfish_red.tres
new file mode 100644
index 0000000..f3c3b3b
--- /dev/null
+++ b/fish/species/lionfish_red/lionfish_red.tres
@@ -0,0 +1,31 @@
+[gd_resource type="Resource" script_class="FishData" load_steps=5 format=3]
+
+[ext_resource type="Script" 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" path="res://fish/fish_availability.gd" id="3_availability"]
+[ext_resource type="Texture2D" path="res://art/exported/creatures/fish_placeholder.png" id="99_fish_placeholder"]
+
+[sub_resource type="Resource" id="Availability"]
+script = ExtResource("3_availability")
+allow_day = false
+
+[resource]
+script = ExtResource("1_fish_data")
+id = &"lionfish_red"
+display_name = "red lionfish"
+active = true
+catalog_number = 5201
+collection_group = &"Reef ambush fish"
+logbook_fact = "red lionfish spread ornate venomous spines when threatened. broad fins herd smaller fish into corners before a rapid gulp."
+available_seasons = 15
+allowed_water_types = 2
+rarity = 2
+base_catch_weight = 0.3
+catch_profile = ExtResource("2_profile")
+availability = SubResource("Availability")
+weight_min_lb = 0.2
+weight_max_lb = 3.0
+sell_value_min = 11
+sell_value_max = 25
+sell_value_curve = 0.92
+display_texture = ExtResource("99_fish_placeholder")
diff --git a/fish/species/loach_clown/loach_clown.tres b/fish/species/loach_clown/loach_clown.tres
new file mode 100644
index 0000000..f99a1be
--- /dev/null
+++ b/fish/species/loach_clown/loach_clown.tres
@@ -0,0 +1,32 @@
+[gd_resource type="Resource" script_class="FishData" load_steps=5 format=3]
+
+[ext_resource type="Script" 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" path="res://fish/fish_availability.gd" id="3_availability"]
+[ext_resource type="Texture2D" path="res://art/exported/creatures/fish_placeholder.png" id="99_fish_placeholder"]
+
+[sub_resource type="Resource" id="Availability"]
+script = ExtResource("3_availability")
+allow_night = false
+require_rain = true
+
+[resource]
+script = ExtResource("1_fish_data")
+id = &"loach_clown"
+display_name = "clown loach"
+active = true
+catalog_number = 7502
+collection_group = &"Tropical freshwater fish"
+logbook_fact = "clown loaches rest in groups and make clicking sounds while feeding. orange bodies carry three broad black bands and small eye spines."
+available_seasons = 15
+allowed_water_types = 1
+rarity = 2
+base_catch_weight = 0.32
+catch_profile = ExtResource("2_profile")
+availability = SubResource("Availability")
+weight_min_lb = 0.1
+weight_max_lb = 2.0
+sell_value_min = 10
+sell_value_max = 22
+sell_value_curve = 0.9
+display_texture = ExtResource("99_fish_placeholder")
diff --git a/fish/species/loach_weather/loach_weather.tres b/fish/species/loach_weather/loach_weather.tres
new file mode 100644
index 0000000..cc7afef
--- /dev/null
+++ b/fish/species/loach_weather/loach_weather.tres
@@ -0,0 +1,31 @@
+[gd_resource type="Resource" script_class="FishData" load_steps=5 format=3]
+
+[ext_resource type="Script" 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" path="res://fish/fish_availability.gd" id="3_availability"]
+[ext_resource type="Texture2D" path="res://art/exported/creatures/fish_placeholder.png" id="99_fish_placeholder"]
+
+[sub_resource type="Resource" id="Availability"]
+script = ExtResource("3_availability")
+require_rain = true
+
+[resource]
+script = ExtResource("1_fish_data")
+id = &"loach_weather"
+display_name = "weather loach"
+active = true
+catalog_number = 2905
+collection_group = &"Freshwater oddities"
+logbook_fact = "weather loaches gulp air and pass it through the intestine when oxygen runs low. restless activity often increases as air pressure falls."
+available_seasons = 15
+allowed_water_types = 1
+rarity = 1
+base_catch_weight = 0.85
+catch_profile = ExtResource("2_profile")
+availability = SubResource("Availability")
+weight_min_lb = 0.05
+weight_max_lb = 0.7
+sell_value_min = 5
+sell_value_max = 11
+sell_value_curve = 0.95
+display_texture = ExtResource("99_fish_placeholder")
diff --git a/fish/species/lobster_american/lobster_american.tres b/fish/species/lobster_american/lobster_american.tres
new file mode 100644
index 0000000..b70b809
--- /dev/null
+++ b/fish/species/lobster_american/lobster_american.tres
@@ -0,0 +1,32 @@
+[gd_resource type="Resource" script_class="FishData" load_steps=5 format=3]
+
+[ext_resource type="Script" 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" path="res://fish/fish_availability.gd" id="3_availability"]
+[ext_resource type="Texture2D" path="res://art/exported/creatures/fish_placeholder.png" id="99_fish_placeholder"]
+
+[sub_resource type="Resource" id="Availability"]
+script = ExtResource("3_availability")
+allow_day = false
+require_fog = true
+
+[resource]
+script = ExtResource("1_fish_data")
+id = &"lobster_american"
+display_name = "American lobster"
+active = true
+catalog_number = 3801
+collection_group = &"Lobsters and shrimp"
+logbook_fact = "american lobsters shelter in rocky crevices and emerge to forage along the bottom. one claw crushes while the other cuts and grips."
+available_seasons = 15
+allowed_water_types = 2
+rarity = 2
+base_catch_weight = 0.28
+catch_profile = ExtResource("2_profile")
+availability = SubResource("Availability")
+weight_min_lb = 0.5
+weight_max_lb = 44.0
+sell_value_min = 16
+sell_value_max = 42
+sell_value_curve = 0.86
+display_texture = ExtResource("99_fish_placeholder")
diff --git a/fish/species/lobster_caribbean_spiny/lobster_caribbean_spiny.tres b/fish/species/lobster_caribbean_spiny/lobster_caribbean_spiny.tres
new file mode 100644
index 0000000..cdd828c
--- /dev/null
+++ b/fish/species/lobster_caribbean_spiny/lobster_caribbean_spiny.tres
@@ -0,0 +1,32 @@
+[gd_resource type="Resource" script_class="FishData" load_steps=5 format=3]
+
+[ext_resource type="Script" 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" path="res://fish/fish_availability.gd" id="3_availability"]
+[ext_resource type="Texture2D" path="res://art/exported/creatures/fish_placeholder.png" id="99_fish_placeholder"]
+
+[sub_resource type="Resource" id="Availability"]
+script = ExtResource("3_availability")
+allow_day = false
+forbid_fog = true
+
+[resource]
+script = ExtResource("1_fish_data")
+id = &"lobster_caribbean_spiny"
+display_name = "Caribbean spiny lobster"
+active = true
+catalog_number = 3803
+collection_group = &"Lobsters and shrimp"
+logbook_fact = "caribbean spiny lobsters rely on long antennae and a thorny shell instead of large claws. groups march together across the seafloor."
+available_seasons = 15
+allowed_water_types = 2
+rarity = 2
+base_catch_weight = 0.25
+catch_profile = ExtResource("2_profile")
+availability = SubResource("Availability")
+weight_min_lb = 0.5
+weight_max_lb = 26.0
+sell_value_min = 15
+sell_value_max = 38
+sell_value_curve = 0.87
+display_texture = ExtResource("99_fish_placeholder")
diff --git a/fish/species/lungfish_west_african/lungfish_west_african.png b/fish/species/lungfish_west_african/lungfish_west_african.png
new file mode 100644
index 0000000..9960ab0
Binary files /dev/null and b/fish/species/lungfish_west_african/lungfish_west_african.png differ
diff --git a/fish/species/lungfish_west_african/lungfish_west_african.png.import b/fish/species/lungfish_west_african/lungfish_west_african.png.import
new file mode 100644
index 0000000..dfb1875
--- /dev/null
+++ b/fish/species/lungfish_west_african/lungfish_west_african.png.import
@@ -0,0 +1,40 @@
+[remap]
+
+importer="texture"
+type="CompressedTexture2D"
+uid="uid://i7uefoxij0xr"
+path="res://.godot/imported/lungfish_west_african.png-39f7845a079da2da99841f01c797b183.ctex"
+metadata={
+"vram_texture": false
+}
+
+[deps]
+
+source_file="res://fish/species/lungfish_west_african/lungfish_west_african.png"
+dest_files=["res://.godot/imported/lungfish_west_african.png-39f7845a079da2da99841f01c797b183.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/lungfish_west_african/lungfish_west_african.tres b/fish/species/lungfish_west_african/lungfish_west_african.tres
new file mode 100644
index 0000000..f049cff
--- /dev/null
+++ b/fish/species/lungfish_west_african/lungfish_west_african.tres
@@ -0,0 +1,32 @@
+[gd_resource type="Resource" script_class="FishData" load_steps=5 format=3]
+
+[ext_resource type="Script" 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" path="res://fish/fish_availability.gd" id="3_availability"]
+[ext_resource type="Texture2D" path="res://art/exported/creatures/107.png" id="4_texture"]
+
+[sub_resource type="Resource" id="Availability"]
+script = ExtResource("3_availability")
+allow_day = false
+require_rain = true
+
+[resource]
+script = ExtResource("1_fish_data")
+id = &"lungfish_west_african"
+display_name = "West African lungfish"
+active = true
+catalog_number = 107
+collection_group = &"Ancient freshwater fish"
+logbook_fact = "west african lungfish breathe air with paired lungs. when floodplains dry, they seal themselves in a mud cocoon until water returns."
+available_seasons = 6
+allowed_water_types = 1
+rarity = 2
+base_catch_weight = 0.3
+catch_profile = ExtResource("2_profile")
+availability = SubResource("Availability")
+weight_min_lb = 1.0
+weight_max_lb = 10.0
+sell_value_min = 12
+sell_value_max = 28
+sell_value_curve = 0.86
+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
index 67a2410..e5fbd98 100644
Binary files a/fish/species/mackerel_atlantic/fish_mackerel_atlantic.png and b/fish/species/mackerel_atlantic/fish_mackerel_atlantic.png differ
diff --git a/fish/species/mackerel_atlantic/mackerel_atlantic.tres b/fish/species/mackerel_atlantic/mackerel_atlantic.tres
index 514ef07..ba6e0d8 100644
--- a/fish/species/mackerel_atlantic/mackerel_atlantic.tres
+++ b/fish/species/mackerel_atlantic/mackerel_atlantic.tres
@@ -3,7 +3,7 @@
[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"]
+[ext_resource type="Texture2D" path="res://art/exported/creatures/701.png" id="4_texture"]
[sub_resource type="Resource" id="MackerelAtlanticAvailability"]
script = ExtResource("3_availability_script")
@@ -13,6 +13,11 @@ allowed_location_tags = Array[StringName]([&"ocean"])
script = ExtResource("1_fish_data")
id = &"mackerel_atlantic"
display_name = "atlantic mackerel"
+active = true
+catalog_number = 701
+collection_group = &"Coastal pelagic fish"
+logbook_fact = "atlantic mackerel travel in fast schools across the north atlantic and feed on plankton and small schooling fish."
+available_seasons = 7
allowed_water_types = 2
base_catch_weight = 0.5
catch_profile = ExtResource("2_profile")
diff --git a/fish/species/mackerel_cero/fish_mackerel_cero.png b/fish/species/mackerel_cero/fish_mackerel_cero.png
index 82cb340..610b99c 100644
Binary files a/fish/species/mackerel_cero/fish_mackerel_cero.png and b/fish/species/mackerel_cero/fish_mackerel_cero.png differ
diff --git a/fish/species/mackerel_cero/mackerel_cero.tres b/fish/species/mackerel_cero/mackerel_cero.tres
index fbbb612..25cd4c5 100644
--- a/fish/species/mackerel_cero/mackerel_cero.tres
+++ b/fish/species/mackerel_cero/mackerel_cero.tres
@@ -3,7 +3,7 @@
[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"]
+[ext_resource type="Texture2D" path="res://art/exported/creatures/fish_placeholder.png" id="4_texture"]
[sub_resource type="Resource" id="MackerelCeroAvailability"]
script = ExtResource("3_availability_script")
@@ -13,6 +13,11 @@ allowed_location_tags = Array[StringName]([&"ocean"])
script = ExtResource("1_fish_data")
id = &"mackerel_cero"
display_name = "cero mackerel"
+active = true
+catalog_number = 703
+collection_group = &"Coastal pelagic fish"
+logbook_fact = "cero mackerel are streamlined coastal hunters. sharp teeth help them slash through schools of smaller fish."
+available_seasons = 15
allowed_water_types = 2
rarity = 1
base_catch_weight = 0.35
diff --git a/fish/species/mackerel_chub/fish_mackerel_chub.png b/fish/species/mackerel_chub/fish_mackerel_chub.png
index c0687ff..2d668fd 100644
Binary files a/fish/species/mackerel_chub/fish_mackerel_chub.png and b/fish/species/mackerel_chub/fish_mackerel_chub.png differ
diff --git a/fish/species/mackerel_chub/mackerel_chub.tres b/fish/species/mackerel_chub/mackerel_chub.tres
index f4ceca1..891abe4 100644
--- a/fish/species/mackerel_chub/mackerel_chub.tres
+++ b/fish/species/mackerel_chub/mackerel_chub.tres
@@ -3,7 +3,7 @@
[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"]
+[ext_resource type="Texture2D" path="res://art/exported/creatures/704.png" id="4_texture"]
[sub_resource type="Resource" id="MackerelChubAvailability"]
script = ExtResource("3_availability_script")
@@ -13,6 +13,11 @@ allowed_location_tags = Array[StringName]([&"ocean"])
script = ExtResource("1_fish_data")
id = &"mackerel_chub"
display_name = "chub mackerel"
+active = true
+catalog_number = 704
+collection_group = &"Coastal pelagic fish"
+logbook_fact = "chub mackerel school near the surface and follow plankton blooms. their dark wavy bars help identify them at a glance."
+available_seasons = 7
allowed_water_types = 2
base_catch_weight = 0.5
catch_profile = ExtResource("2_profile")
diff --git a/fish/species/mackerel_king/fish_mackerel_king.png b/fish/species/mackerel_king/fish_mackerel_king.png
index 01fd603..7ec6614 100644
Binary files a/fish/species/mackerel_king/fish_mackerel_king.png and b/fish/species/mackerel_king/fish_mackerel_king.png differ
diff --git a/fish/species/mackerel_king/mackerel_king.tres b/fish/species/mackerel_king/mackerel_king.tres
index 2c28071..9c00470 100644
--- a/fish/species/mackerel_king/mackerel_king.tres
+++ b/fish/species/mackerel_king/mackerel_king.tres
@@ -3,7 +3,7 @@
[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"]
+[ext_resource type="Texture2D" path="res://art/exported/creatures/fish_placeholder.png" id="4_texture"]
[sub_resource type="Resource" id="MackerelKingAvailability"]
script = ExtResource("3_availability_script")
@@ -13,6 +13,11 @@ allowed_location_tags = Array[StringName]([&"ocean"])
script = ExtResource("1_fish_data")
id = &"mackerel_king"
display_name = "king mackerel"
+active = true
+catalog_number = 706
+collection_group = &"Coastal pelagic fish"
+logbook_fact = "king mackerel are swift coastal predators. their pointed teeth and long body are built for sudden bursts of speed."
+available_seasons = 6
allowed_water_types = 2
rarity = 1
base_catch_weight = 0.3
diff --git a/fish/species/mackerel_spanish/fish_mackerel_spanish.png b/fish/species/mackerel_spanish/fish_mackerel_spanish.png
index 2a97bed..5cbe847 100644
Binary files a/fish/species/mackerel_spanish/fish_mackerel_spanish.png and b/fish/species/mackerel_spanish/fish_mackerel_spanish.png differ
diff --git a/fish/species/mackerel_spanish/mackerel_spanish.tres b/fish/species/mackerel_spanish/mackerel_spanish.tres
index 0319917..1f0bcf4 100644
--- a/fish/species/mackerel_spanish/mackerel_spanish.tres
+++ b/fish/species/mackerel_spanish/mackerel_spanish.tres
@@ -3,7 +3,7 @@
[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"]
+[ext_resource type="Texture2D" path="res://art/exported/creatures/fish_placeholder.png" id="4_texture"]
[sub_resource type="Resource" id="MackerelSpanishAvailability"]
script = ExtResource("3_availability_script")
@@ -13,6 +13,11 @@ allowed_location_tags = Array[StringName]([&"ocean"])
script = ExtResource("1_fish_data")
id = &"mackerel_spanish"
display_name = "spanish mackerel"
+active = true
+catalog_number = 707
+collection_group = &"Coastal pelagic fish"
+logbook_fact = "spanish mackerel hunt in warm coastal waters and often travel in loose schools while chasing baitfish."
+available_seasons = 6
allowed_water_types = 2
rarity = 1
base_catch_weight = 0.35
diff --git a/fish/species/madtom_tadpole/madtom_tadpole.tres b/fish/species/madtom_tadpole/madtom_tadpole.tres
new file mode 100644
index 0000000..d0a5545
--- /dev/null
+++ b/fish/species/madtom_tadpole/madtom_tadpole.tres
@@ -0,0 +1,31 @@
+[gd_resource type="Resource" script_class="FishData" load_steps=5 format=3]
+
+[ext_resource type="Script" 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" path="res://fish/fish_availability.gd" id="3_availability"]
+[ext_resource type="Texture2D" path="res://art/exported/creatures/fish_placeholder.png" id="99_fish_placeholder"]
+
+[sub_resource type="Resource" id="Availability"]
+script = ExtResource("3_availability")
+allow_day = false
+
+[resource]
+script = ExtResource("1_fish_data")
+id = &"madtom_tadpole"
+display_name = "tadpole madtom"
+active = true
+catalog_number = 2406
+collection_group = &"Freshwater catfish"
+logbook_fact = "tadpole madtoms are tiny nocturnal catfish that shelter beneath vegetation and debris. venomous fin spines discourage larger predators."
+available_seasons = 7
+allowed_water_types = 1
+rarity = 2
+base_catch_weight = 0.35
+catch_profile = ExtResource("2_profile")
+availability = SubResource("Availability")
+weight_min_lb = 0.02
+weight_max_lb = 0.15
+sell_value_min = 8
+sell_value_max = 16
+sell_value_curve = 0.95
+display_texture = ExtResource("99_fish_placeholder")
diff --git a/fish/species/mahi_mahi/mahi_mahi.tres b/fish/species/mahi_mahi/mahi_mahi.tres
new file mode 100644
index 0000000..691a52a
--- /dev/null
+++ b/fish/species/mahi_mahi/mahi_mahi.tres
@@ -0,0 +1,32 @@
+[gd_resource type="Resource" script_class="FishData" load_steps=5 format=3]
+
+[ext_resource type="Script" 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" path="res://fish/fish_availability.gd" id="3_availability"]
+[ext_resource type="Texture2D" path="res://art/exported/creatures/4701.png" id="99_fish_placeholder"]
+
+[sub_resource type="Resource" id="Availability"]
+script = ExtResource("3_availability")
+allow_night = false
+forbid_fog = true
+
+[resource]
+script = ExtResource("1_fish_data")
+id = &"mahi_mahi"
+display_name = "mahi-mahi"
+active = true
+catalog_number = 4701
+collection_group = &"Oceanic pelagic fish"
+logbook_fact = "mahi-mahi flash brilliant blue, green, and gold while chasing prey near the surface. they grow quickly and gather around floating objects."
+available_seasons = 6
+allowed_water_types = 2
+rarity = 2
+base_catch_weight = 0.28
+catch_profile = ExtResource("2_profile")
+availability = SubResource("Availability")
+weight_min_lb = 2.0
+weight_max_lb = 50.0
+sell_value_min = 15
+sell_value_max = 38
+sell_value_curve = 0.84
+display_texture = ExtResource("99_fish_placeholder")
diff --git a/fish/species/mahseer_golden/mahseer_golden.tres b/fish/species/mahseer_golden/mahseer_golden.tres
new file mode 100644
index 0000000..1459de1
--- /dev/null
+++ b/fish/species/mahseer_golden/mahseer_golden.tres
@@ -0,0 +1,32 @@
+[gd_resource type="Resource" script_class="FishData" load_steps=5 format=3]
+
+[ext_resource type="Script" 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" path="res://fish/fish_availability.gd" id="3_availability"]
+[ext_resource type="Texture2D" path="res://art/exported/creatures/fish_placeholder.png" id="99_fish_placeholder"]
+
+[sub_resource type="Resource" id="Availability"]
+script = ExtResource("3_availability")
+allow_night = false
+require_rain = true
+
+[resource]
+script = ExtResource("1_fish_data")
+id = &"mahseer_golden"
+display_name = "golden mahseer"
+active = true
+catalog_number = 7603
+collection_group = &"Tropical freshwater predators"
+logbook_fact = "golden mahseer migrate through swift mountain rivers to reach spawning grounds. large scales shine bronze beneath reddish fins."
+available_seasons = 15
+allowed_water_types = 1
+rarity = 3
+base_catch_weight = 0.09
+catch_profile = ExtResource("2_profile")
+availability = SubResource("Availability")
+weight_min_lb = 8.0
+weight_max_lb = 90.0
+sell_value_min = 32
+sell_value_max = 72
+sell_value_curve = 0.82
+display_texture = ExtResource("99_fish_placeholder")
diff --git a/fish/species/manatee_west_indian/manatee_west_indian.tres b/fish/species/manatee_west_indian/manatee_west_indian.tres
new file mode 100644
index 0000000..7154157
--- /dev/null
+++ b/fish/species/manatee_west_indian/manatee_west_indian.tres
@@ -0,0 +1,32 @@
+[gd_resource type="Resource" script_class="FishData" load_steps=5 format=3]
+
+[ext_resource type="Script" 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" path="res://fish/fish_availability.gd" id="3_availability"]
+[ext_resource type="Texture2D" path="res://art/exported/creatures/fish_placeholder.png" id="99_fish_placeholder"]
+
+[sub_resource type="Resource" id="Availability"]
+script = ExtResource("3_availability")
+allow_night = false
+forbid_fog = true
+
+[resource]
+script = ExtResource("1_fish_data")
+id = &"manatee_west_indian"
+display_name = "West Indian manatee"
+active = true
+catalog_number = 505
+collection_group = &"Coastal marine mammals"
+logbook_fact = "west indian manatees graze aquatic plants in warm coastal water and rivers. they surface regularly to breathe through nostrils atop the snout."
+available_seasons = 15
+allowed_water_types = 2
+rarity = 4
+base_catch_weight = 0.012
+catch_profile = ExtResource("2_profile")
+availability = SubResource("Availability")
+weight_min_lb = 440.0
+weight_max_lb = 1300.0
+sell_value_min = 70
+sell_value_max = 160
+sell_value_curve = 0.77
+display_texture = ExtResource("99_fish_placeholder")
diff --git a/fish/species/manta_ray_giant/manta_ray_giant.tres b/fish/species/manta_ray_giant/manta_ray_giant.tres
new file mode 100644
index 0000000..13eef47
--- /dev/null
+++ b/fish/species/manta_ray_giant/manta_ray_giant.tres
@@ -0,0 +1,32 @@
+[gd_resource type="Resource" script_class="FishData" load_steps=5 format=3]
+
+[ext_resource type="Script" 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" path="res://fish/fish_availability.gd" id="3_availability"]
+[ext_resource type="Texture2D" path="res://art/exported/creatures/fish_placeholder.png" id="99_fish_placeholder"]
+
+[sub_resource type="Resource" id="Availability"]
+script = ExtResource("3_availability")
+allow_night = false
+forbid_fog = true
+
+[resource]
+script = ExtResource("1_fish_data")
+id = &"manta_ray_giant"
+display_name = "giant manta ray"
+active = true
+catalog_number = 5102
+collection_group = &"Rays and skates"
+logbook_fact = "giant manta rays funnel plankton into wide mouths with two flexible head fins. each belly carries a unique pattern of dark spots."
+available_seasons = 15
+allowed_water_types = 2
+rarity = 4
+base_catch_weight = 0.025
+catch_profile = ExtResource("2_profile")
+availability = SubResource("Availability")
+weight_min_lb = 100.0
+weight_max_lb = 1000.0
+sell_value_min = 65
+sell_value_max = 150
+sell_value_curve = 0.76
+display_texture = ExtResource("99_fish_placeholder")
diff --git a/fish/species/marlin_black/fish_marlin_black.png b/fish/species/marlin_black/fish_marlin_black.png
index 6cbcccd..ae4d9ab 100644
Binary files a/fish/species/marlin_black/fish_marlin_black.png and b/fish/species/marlin_black/fish_marlin_black.png differ
diff --git a/fish/species/marlin_black/marlin_black.tres b/fish/species/marlin_black/marlin_black.tres
index 39110c5..26f506a 100644
--- a/fish/species/marlin_black/marlin_black.tres
+++ b/fish/species/marlin_black/marlin_black.tres
@@ -3,7 +3,7 @@
[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"]
+[ext_resource type="Texture2D" path="res://art/exported/creatures/fish_placeholder.png" id="4_texture"]
[sub_resource type="Resource" id="MarlinBlackAvailability"]
script = ExtResource("3_availability_script")
@@ -13,6 +13,11 @@ allowed_location_tags = Array[StringName]([&"ocean"])
script = ExtResource("1_fish_data")
id = &"marlin_black"
display_name = "black marlin"
+active = true
+catalog_number = 4601
+collection_group = &"Oceanic billfish"
+logbook_fact = "black marlin are powerful billfish that spend much of their lives in warm open ocean and can make blistering runs."
+available_seasons = 6
allowed_water_types = 2
rarity = 3
base_catch_weight = 0.08
diff --git a/fish/species/marlin_blue/fish_marlin_blue.png b/fish/species/marlin_blue/fish_marlin_blue.png
index 7df059f..0f4c357 100644
Binary files a/fish/species/marlin_blue/fish_marlin_blue.png and b/fish/species/marlin_blue/fish_marlin_blue.png differ
diff --git a/fish/species/marlin_blue/marlin_blue.tres b/fish/species/marlin_blue/marlin_blue.tres
index b3328ba..c8ee35d 100644
--- a/fish/species/marlin_blue/marlin_blue.tres
+++ b/fish/species/marlin_blue/marlin_blue.tres
@@ -3,7 +3,7 @@
[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"]
+[ext_resource type="Texture2D" path="res://art/exported/creatures/fish_placeholder.png" id="4_texture"]
[sub_resource type="Resource" id="MarlinBlueAvailability"]
script = ExtResource("3_availability_script")
@@ -13,6 +13,11 @@ allowed_location_tags = Array[StringName]([&"ocean"])
script = ExtResource("1_fish_data")
id = &"marlin_blue"
display_name = "blue marlin"
+active = true
+catalog_number = 4602
+collection_group = &"Oceanic billfish"
+logbook_fact = "blue marlin are highly migratory open-ocean hunters. their long bill helps them stun prey before turning back to feed."
+available_seasons = 6
allowed_water_types = 2
rarity = 4
base_catch_weight = 0.05
diff --git a/fish/species/marlin_white/fish_marlin_white.png b/fish/species/marlin_white/fish_marlin_white.png
index 76fb780..327bd91 100644
Binary files a/fish/species/marlin_white/fish_marlin_white.png and b/fish/species/marlin_white/fish_marlin_white.png differ
diff --git a/fish/species/marlin_white/marlin_white.tres b/fish/species/marlin_white/marlin_white.tres
index e90651c..a69d01d 100644
--- a/fish/species/marlin_white/marlin_white.tres
+++ b/fish/species/marlin_white/marlin_white.tres
@@ -3,7 +3,7 @@
[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"]
+[ext_resource type="Texture2D" path="res://art/exported/creatures/fish_placeholder.png" id="4_texture"]
[sub_resource type="Resource" id="MarlinWhiteAvailability"]
script = ExtResource("3_availability_script")
@@ -13,6 +13,11 @@ allowed_location_tags = Array[StringName]([&"ocean"])
script = ExtResource("1_fish_data")
id = &"marlin_white"
display_name = "white marlin"
+active = true
+catalog_number = 4605
+collection_group = &"Oceanic billfish"
+logbook_fact = "white marlin favor warm offshore water and use their rounded dorsal fin and bill to weave through schools of prey."
+available_seasons = 6
allowed_water_types = 2
rarity = 3
base_catch_weight = 0.1
diff --git a/fish/species/menhaden_atlantic/menhaden_atlantic.tres b/fish/species/menhaden_atlantic/menhaden_atlantic.tres
new file mode 100644
index 0000000..53b4d6e
--- /dev/null
+++ b/fish/species/menhaden_atlantic/menhaden_atlantic.tres
@@ -0,0 +1,31 @@
+[gd_resource type="Resource" script_class="FishData" load_steps=5 format=3]
+
+[ext_resource type="Script" 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" path="res://fish/fish_availability.gd" id="3_availability"]
+[ext_resource type="Texture2D" path="res://art/exported/creatures/802.png" id="99_fish_placeholder"]
+
+[sub_resource type="Resource" id="Availability"]
+script = ExtResource("3_availability")
+allow_night = false
+
+[resource]
+script = ExtResource("1_fish_data")
+id = &"menhaden_atlantic"
+display_name = "atlantic menhaden"
+active = true
+catalog_number = 802
+collection_group = &"Coastal schooling fish"
+logbook_fact = "atlantic menhaden strain plankton from coastal water with fine gill rakers. dense schools feed whales, seabirds, and predatory fish."
+available_seasons = 7
+allowed_water_types = 2
+rarity = 0
+base_catch_weight = 3.0
+catch_profile = ExtResource("2_profile")
+availability = SubResource("Availability")
+weight_min_lb = 0.1
+weight_max_lb = 1.5
+sell_value_min = 2
+sell_value_max = 5
+sell_value_curve = 0.98
+display_texture = ExtResource("99_fish_placeholder")
diff --git a/fish/species/milkfish/milkfish.tres b/fish/species/milkfish/milkfish.tres
new file mode 100644
index 0000000..9c8362a
--- /dev/null
+++ b/fish/species/milkfish/milkfish.tres
@@ -0,0 +1,31 @@
+[gd_resource type="Resource" script_class="FishData" load_steps=5 format=3]
+
+[ext_resource type="Script" 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" path="res://fish/fish_availability.gd" id="3_availability"]
+[ext_resource type="Texture2D" path="res://art/exported/creatures/7101.png" id="99_fish_placeholder"]
+
+[sub_resource type="Resource" id="Availability"]
+script = ExtResource("3_availability")
+allow_night = false
+
+[resource]
+script = ExtResource("1_fish_data")
+id = &"milkfish"
+display_name = "milkfish"
+active = true
+catalog_number = 7101
+collection_group = &"Tropical coastal fish"
+logbook_fact = "milkfish graze algae and tiny organisms in warm coastal shallows. deeply forked tails drive large schools through lagoons and estuaries."
+available_seasons = 15
+allowed_water_types = 2
+rarity = 0
+base_catch_weight = 1.8
+catch_profile = ExtResource("2_profile")
+availability = SubResource("Availability")
+weight_min_lb = 0.5
+weight_max_lb = 25.0
+sell_value_min = 4
+sell_value_max = 12
+sell_value_curve = 0.88
+display_texture = ExtResource("99_fish_placeholder")
diff --git a/fish/species/minnow_fathead/minnow_fathead.tres b/fish/species/minnow_fathead/minnow_fathead.tres
new file mode 100644
index 0000000..c4a3aff
--- /dev/null
+++ b/fish/species/minnow_fathead/minnow_fathead.tres
@@ -0,0 +1,31 @@
+[gd_resource type="Resource" script_class="FishData" load_steps=5 format=3]
+
+[ext_resource type="Script" 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" path="res://fish/fish_availability.gd" id="3_availability"]
+[ext_resource type="Texture2D" path="res://art/exported/creatures/2804.png" id="99_fish_placeholder"]
+
+[sub_resource type="Resource" id="Availability"]
+script = ExtResource("3_availability")
+allow_night = false
+
+[resource]
+script = ExtResource("1_fish_data")
+id = &"minnow_fathead"
+display_name = "fathead minnow"
+active = true
+catalog_number = 2804
+collection_group = &"Freshwater minnows"
+logbook_fact = "fathead minnows tolerate warm, muddy ponds where many fish struggle. males guard eggs laid beneath rocks, logs, and other cover."
+available_seasons = 7
+allowed_water_types = 1
+rarity = 0
+base_catch_weight = 4.5
+catch_profile = ExtResource("2_profile")
+availability = SubResource("Availability")
+weight_min_lb = 0.02
+weight_max_lb = 0.2
+sell_value_min = 1
+sell_value_max = 2
+sell_value_curve = 1.0
+display_texture = ExtResource("99_fish_placeholder")
diff --git a/fish/species/monkfish/monkfish.tres b/fish/species/monkfish/monkfish.tres
new file mode 100644
index 0000000..d485570
--- /dev/null
+++ b/fish/species/monkfish/monkfish.tres
@@ -0,0 +1,32 @@
+[gd_resource type="Resource" script_class="FishData" load_steps=5 format=3]
+
+[ext_resource type="Script" 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" path="res://fish/fish_availability.gd" id="3_availability"]
+[ext_resource type="Texture2D" path="res://art/exported/creatures/fish_placeholder.png" id="99_fish_placeholder"]
+
+[sub_resource type="Resource" id="Availability"]
+script = ExtResource("3_availability")
+allow_day = false
+require_fog = true
+
+[resource]
+script = ExtResource("1_fish_data")
+id = &"monkfish"
+display_name = "monkfish"
+active = true
+catalog_number = 603
+collection_group = &"Coastal oddities"
+logbook_fact = "monkfish hide against the bottom and wave a fleshy lure above a cavernous mouth. a sudden gulp draws fish and crustaceans inward."
+available_seasons = 13
+allowed_water_types = 2
+rarity = 2
+base_catch_weight = 0.25
+catch_profile = ExtResource("2_profile")
+availability = SubResource("Availability")
+weight_min_lb = 2.0
+weight_max_lb = 55.0
+sell_value_min = 14
+sell_value_max = 35
+sell_value_curve = 0.84
+display_texture = ExtResource("99_fish_placeholder")
diff --git a/fish/species/mooneye/mooneye.tres b/fish/species/mooneye/mooneye.tres
new file mode 100644
index 0000000..3dff8a1
--- /dev/null
+++ b/fish/species/mooneye/mooneye.tres
@@ -0,0 +1,31 @@
+[gd_resource type="Resource" script_class="FishData" load_steps=5 format=3]
+
+[ext_resource type="Script" 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" path="res://fish/fish_availability.gd" id="3_availability"]
+[ext_resource type="Texture2D" path="res://art/exported/creatures/fish_placeholder.png" id="99_fish_placeholder"]
+
+[sub_resource type="Resource" id="Availability"]
+script = ExtResource("3_availability")
+allow_day = false
+
+[resource]
+script = ExtResource("1_fish_data")
+id = &"mooneye"
+display_name = "mooneye"
+active = true
+catalog_number = 2904
+collection_group = &"Freshwater oddities"
+logbook_fact = "mooneyes flash silver in clear rivers and lakes while feeding near the surface. their large eyes suit evening hunts for insects and small fish."
+available_seasons = 13
+allowed_water_types = 1
+rarity = 1
+base_catch_weight = 0.85
+catch_profile = ExtResource("2_profile")
+availability = SubResource("Availability")
+weight_min_lb = 0.2
+weight_max_lb = 4.0
+sell_value_min = 5
+sell_value_max = 11
+sell_value_curve = 0.92
+display_texture = ExtResource("99_fish_placeholder")
diff --git a/fish/species/moray_green/moray_green.tres b/fish/species/moray_green/moray_green.tres
new file mode 100644
index 0000000..e0d49ae
--- /dev/null
+++ b/fish/species/moray_green/moray_green.tres
@@ -0,0 +1,31 @@
+[gd_resource type="Resource" script_class="FishData" load_steps=5 format=3]
+
+[ext_resource type="Script" 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" path="res://fish/fish_availability.gd" id="3_availability"]
+[ext_resource type="Texture2D" path="res://art/exported/creatures/fish_placeholder.png" id="99_fish_placeholder"]
+
+[sub_resource type="Resource" id="Availability"]
+script = ExtResource("3_availability")
+allow_day = false
+
+[resource]
+script = ExtResource("1_fish_data")
+id = &"moray_green"
+display_name = "green moray"
+active = true
+catalog_number = 5301
+collection_group = &"Reef eels"
+logbook_fact = "green morays appear green because yellow mucus coats blue-gray skin. a second set of jaws pulls captured prey deeper into the throat."
+available_seasons = 15
+allowed_water_types = 2
+rarity = 2
+base_catch_weight = 0.28
+catch_profile = ExtResource("2_profile")
+availability = SubResource("Availability")
+weight_min_lb = 1.0
+weight_max_lb = 30.0
+sell_value_min = 13
+sell_value_max = 32
+sell_value_curve = 0.86
+display_texture = ExtResource("99_fish_placeholder")
diff --git a/fish/species/mudskipper_atlantic/mudskipper_atlantic.tres b/fish/species/mudskipper_atlantic/mudskipper_atlantic.tres
new file mode 100644
index 0000000..7df958a
--- /dev/null
+++ b/fish/species/mudskipper_atlantic/mudskipper_atlantic.tres
@@ -0,0 +1,32 @@
+[gd_resource type="Resource" script_class="FishData" load_steps=5 format=3]
+
+[ext_resource type="Script" 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" path="res://fish/fish_availability.gd" id="3_availability"]
+[ext_resource type="Texture2D" path="res://art/exported/creatures/201.png" id="99_fish_placeholder"]
+
+[sub_resource type="Resource" id="Availability"]
+script = ExtResource("3_availability")
+allow_night = false
+require_rain = true
+
+[resource]
+script = ExtResource("1_fish_data")
+id = &"mudskipper_atlantic"
+display_name = "atlantic mudskipper"
+active = true
+catalog_number = 201
+collection_group = &"Brackish oddities"
+logbook_fact = "atlantic mudskippers use sturdy fins to hop and crawl across exposed mud. moist skin and mouth linings let them breathe out of water."
+available_seasons = 15
+allowed_water_types = 2
+rarity = 1
+base_catch_weight = 1.0
+catch_profile = ExtResource("2_profile")
+availability = SubResource("Availability")
+weight_min_lb = 0.03
+weight_max_lb = 0.5
+sell_value_min = 5
+sell_value_max = 11
+sell_value_curve = 0.95
+display_texture = ExtResource("99_fish_placeholder")
diff --git a/fish/species/mullet_red/mullet_red.tres b/fish/species/mullet_red/mullet_red.tres
new file mode 100644
index 0000000..67a35fa
--- /dev/null
+++ b/fish/species/mullet_red/mullet_red.tres
@@ -0,0 +1,31 @@
+[gd_resource type="Resource" script_class="FishData" load_steps=5 format=3]
+
+[ext_resource type="Script" 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" path="res://fish/fish_availability.gd" id="3_availability"]
+[ext_resource type="Texture2D" path="res://art/exported/creatures/fish_placeholder.png" id="99_fish_placeholder"]
+
+[sub_resource type="Resource" id="Availability"]
+script = ExtResource("3_availability")
+allow_night = false
+
+[resource]
+script = ExtResource("1_fish_data")
+id = &"mullet_red"
+display_name = "red mullet"
+active = true
+catalog_number = 301
+collection_group = &"Coastal bottom fish"
+logbook_fact = "red mullet comb sandy bottoms with two chin barbels that taste hidden prey. their foraging leaves winding trails in the sediment."
+available_seasons = 7
+allowed_water_types = 2
+rarity = 1
+base_catch_weight = 0.9
+catch_profile = ExtResource("2_profile")
+availability = SubResource("Availability")
+weight_min_lb = 0.1
+weight_max_lb = 3.0
+sell_value_min = 5
+sell_value_max = 11
+sell_value_curve = 0.92
+display_texture = ExtResource("99_fish_placeholder")
diff --git a/fish/species/mullet_striped/mullet_striped.tres b/fish/species/mullet_striped/mullet_striped.tres
new file mode 100644
index 0000000..a285ee7
--- /dev/null
+++ b/fish/species/mullet_striped/mullet_striped.tres
@@ -0,0 +1,31 @@
+[gd_resource type="Resource" script_class="FishData" load_steps=5 format=3]
+
+[ext_resource type="Script" 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" path="res://fish/fish_availability.gd" id="3_availability"]
+[ext_resource type="Texture2D" path="res://art/exported/creatures/6804.png" id="99_fish_placeholder"]
+
+[sub_resource type="Resource" id="Availability"]
+script = ExtResource("3_availability")
+allow_night = false
+
+[resource]
+script = ExtResource("1_fish_data")
+id = &"mullet_striped"
+display_name = "striped mullet"
+active = true
+catalog_number = 6804
+collection_group = &"Temperate estuary"
+logbook_fact = "striped mullet leap clear of the water and travel in coastal schools. they graze algae and detritus with specialized digestive systems."
+available_seasons = 7
+allowed_water_types = 3
+rarity = 0
+base_catch_weight = 2.5
+catch_profile = ExtResource("2_profile")
+availability = SubResource("Availability")
+weight_min_lb = 0.3
+weight_max_lb = 8.0
+sell_value_min = 3
+sell_value_max = 8
+sell_value_curve = 0.92
+display_texture = ExtResource("99_fish_placeholder")
diff --git a/fish/species/muskellunge/muskellunge.tres b/fish/species/muskellunge/muskellunge.tres
new file mode 100644
index 0000000..e747d07
--- /dev/null
+++ b/fish/species/muskellunge/muskellunge.tres
@@ -0,0 +1,32 @@
+[gd_resource type="Resource" script_class="FishData" load_steps=5 format=3]
+
+[ext_resource type="Script" 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" path="res://fish/fish_availability.gd" id="3_availability"]
+[ext_resource type="Texture2D" path="res://art/exported/creatures/fish_placeholder.png" id="99_fish_placeholder"]
+
+[sub_resource type="Resource" id="Availability"]
+script = ExtResource("3_availability")
+allow_night = false
+require_fog = true
+
+[resource]
+script = ExtResource("1_fish_data")
+id = &"muskellunge"
+display_name = "muskellunge"
+active = true
+catalog_number = 2704
+collection_group = &"Freshwater giants"
+logbook_fact = "muskellunge wait motionless among weeds before exploding toward prey. pale bodies carry bars or spots that fade into broken underwater light."
+available_seasons = 13
+allowed_water_types = 1
+rarity = 3
+base_catch_weight = 0.11
+catch_profile = ExtResource("2_profile")
+availability = SubResource("Availability")
+weight_min_lb = 8.0
+weight_max_lb = 60.0
+sell_value_min = 32
+sell_value_max = 72
+sell_value_curve = 0.82
+display_texture = ExtResource("99_fish_placeholder")
diff --git a/fish/species/nautilus_chambered/nautilus_chambered.tres b/fish/species/nautilus_chambered/nautilus_chambered.tres
new file mode 100644
index 0000000..562f16b
--- /dev/null
+++ b/fish/species/nautilus_chambered/nautilus_chambered.tres
@@ -0,0 +1,32 @@
+[gd_resource type="Resource" script_class="FishData" load_steps=5 format=3]
+
+[ext_resource type="Script" 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" path="res://fish/fish_availability.gd" id="3_availability"]
+[ext_resource type="Texture2D" path="res://art/exported/creatures/fish_placeholder.png" id="99_fish_placeholder"]
+
+[sub_resource type="Resource" id="Availability"]
+script = ExtResource("3_availability")
+allow_day = false
+require_fog = true
+
+[resource]
+script = ExtResource("1_fish_data")
+id = &"nautilus_chambered"
+display_name = "chambered nautilus"
+active = true
+catalog_number = 1501
+collection_group = &"Cuttlefish and nautiluses"
+logbook_fact = "chambered nautiluses regulate buoyancy with gas-filled shell chambers. dozens of small tentacles feel for food in dark reef slopes."
+available_seasons = 15
+allowed_water_types = 2
+rarity = 4
+base_catch_weight = 0.05
+catch_profile = ExtResource("2_profile")
+availability = SubResource("Availability")
+weight_min_lb = 0.5
+weight_max_lb = 3.0
+sell_value_min = 34
+sell_value_max = 76
+sell_value_curve = 0.92
+display_texture = ExtResource("99_fish_placeholder")
diff --git a/fish/species/needlefish_hound/needlefish_hound.tres b/fish/species/needlefish_hound/needlefish_hound.tres
new file mode 100644
index 0000000..5d97670
--- /dev/null
+++ b/fish/species/needlefish_hound/needlefish_hound.tres
@@ -0,0 +1,31 @@
+[gd_resource type="Resource" script_class="FishData" load_steps=5 format=3]
+
+[ext_resource type="Script" 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" path="res://fish/fish_availability.gd" id="3_availability"]
+[ext_resource type="Texture2D" path="res://art/exported/creatures/fish_placeholder.png" id="99_fish_placeholder"]
+
+[sub_resource type="Resource" id="Availability"]
+script = ExtResource("3_availability")
+allow_night = false
+
+[resource]
+script = ExtResource("1_fish_data")
+id = &"needlefish_hound"
+display_name = "houndfish"
+active = true
+catalog_number = 6602
+collection_group = &"Surface ocean fish"
+logbook_fact = "houndfish hunt near the surface with long jaws filled with fine teeth. powerful leaps can carry their narrow bodies far above the water."
+available_seasons = 15
+allowed_water_types = 2
+rarity = 2
+base_catch_weight = 0.3
+catch_profile = ExtResource("2_profile")
+availability = SubResource("Availability")
+weight_min_lb = 0.5
+weight_max_lb = 10.0
+sell_value_min = 11
+sell_value_max = 25
+sell_value_curve = 0.88
+display_texture = ExtResource("99_fish_placeholder")
diff --git a/fish/species/oarfish_giant/oarfish_giant.tres b/fish/species/oarfish_giant/oarfish_giant.tres
new file mode 100644
index 0000000..2bcd4ab
--- /dev/null
+++ b/fish/species/oarfish_giant/oarfish_giant.tres
@@ -0,0 +1,32 @@
+[gd_resource type="Resource" script_class="FishData" load_steps=5 format=3]
+
+[ext_resource type="Script" 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" path="res://fish/fish_availability.gd" id="3_availability"]
+[ext_resource type="Texture2D" path="res://art/exported/creatures/fish_placeholder.png" id="99_fish_placeholder"]
+
+[sub_resource type="Resource" id="Availability"]
+script = ExtResource("3_availability")
+allow_day = false
+require_fog = true
+
+[resource]
+script = ExtResource("1_fish_data")
+id = &"oarfish_giant"
+display_name = "giant oarfish"
+active = true
+catalog_number = 1701
+collection_group = &"Deep-sea legends"
+logbook_fact = "giant oarfish drift vertically through deep open water, rowing with a crimson dorsal fin. their ribbon-like bodies can reach remarkable lengths."
+available_seasons = 15
+allowed_water_types = 2
+rarity = 4
+base_catch_weight = 0.035
+catch_profile = ExtResource("2_profile")
+availability = SubResource("Availability")
+weight_min_lb = 20.0
+weight_max_lb = 600.0
+sell_value_min = 60
+sell_value_max = 140
+sell_value_curve = 0.78
+display_texture = ExtResource("99_fish_placeholder")
diff --git a/fish/species/ocean_perch_pacific/ocean_perch_pacific.tres b/fish/species/ocean_perch_pacific/ocean_perch_pacific.tres
new file mode 100644
index 0000000..6b0cdcc
--- /dev/null
+++ b/fish/species/ocean_perch_pacific/ocean_perch_pacific.tres
@@ -0,0 +1,30 @@
+[gd_resource type="Resource" script_class="FishData" load_steps=5 format=3]
+
+[ext_resource type="Script" 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" path="res://fish/fish_availability.gd" id="3_availability"]
+[ext_resource type="Texture2D" path="res://art/exported/creatures/fish_placeholder.png" id="99_fish_placeholder"]
+
+[sub_resource type="Resource" id="Availability"]
+script = ExtResource("3_availability")
+
+[resource]
+script = ExtResource("1_fish_data")
+id = &"ocean_perch_pacific"
+display_name = "Pacific ocean perch"
+active = true
+catalog_number = 1603
+collection_group = &"Deep shelf fish"
+logbook_fact = "pacific ocean perch hover around deep rocky reefs in the north pacific. these slow-growing fish give birth to live young."
+available_seasons = 15
+allowed_water_types = 2
+rarity = 1
+base_catch_weight = 0.8
+catch_profile = ExtResource("2_profile")
+availability = SubResource("Availability")
+weight_min_lb = 0.3
+weight_max_lb = 12.0
+sell_value_min = 7
+sell_value_max = 17
+sell_value_curve = 0.88
+display_texture = ExtResource("99_fish_placeholder")
diff --git a/fish/species/octopus_common/octopus_common.tres b/fish/species/octopus_common/octopus_common.tres
new file mode 100644
index 0000000..e379010
--- /dev/null
+++ b/fish/species/octopus_common/octopus_common.tres
@@ -0,0 +1,31 @@
+[gd_resource type="Resource" script_class="FishData" load_steps=5 format=3]
+
+[ext_resource type="Script" 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" path="res://fish/fish_availability.gd" id="3_availability"]
+[ext_resource type="Texture2D" path="res://art/exported/creatures/fish_placeholder.png" id="99_fish_placeholder"]
+
+[sub_resource type="Resource" id="Availability"]
+script = ExtResource("3_availability")
+allow_day = false
+
+[resource]
+script = ExtResource("1_fish_data")
+id = &"octopus_common"
+display_name = "common octopus"
+active = true
+catalog_number = 4901
+collection_group = &"Octopuses"
+logbook_fact = "common octopuses squeeze through narrow cracks and solve problems while hunting reefs. skin cells rapidly change color and texture."
+available_seasons = 15
+allowed_water_types = 2
+rarity = 1
+base_catch_weight = 0.65
+catch_profile = ExtResource("2_profile")
+availability = SubResource("Availability")
+weight_min_lb = 0.2
+weight_max_lb = 22.0
+sell_value_min = 7
+sell_value_max = 18
+sell_value_curve = 0.87
+display_texture = ExtResource("99_fish_placeholder")
diff --git a/fish/species/octopus_day/octopus_day.tres b/fish/species/octopus_day/octopus_day.tres
new file mode 100644
index 0000000..4384f38
--- /dev/null
+++ b/fish/species/octopus_day/octopus_day.tres
@@ -0,0 +1,32 @@
+[gd_resource type="Resource" script_class="FishData" load_steps=5 format=3]
+
+[ext_resource type="Script" 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" path="res://fish/fish_availability.gd" id="3_availability"]
+[ext_resource type="Texture2D" path="res://art/exported/creatures/fish_placeholder.png" id="99_fish_placeholder"]
+
+[sub_resource type="Resource" id="Availability"]
+script = ExtResource("3_availability")
+allow_night = false
+forbid_fog = true
+
+[resource]
+script = ExtResource("1_fish_data")
+id = &"octopus_day"
+display_name = "day octopus"
+active = true
+catalog_number = 4902
+collection_group = &"Octopuses"
+logbook_fact = "day octopuses forage across tropical reefs while the sun is high. shifting bars and spots communicate alarm, camouflage, and threat."
+available_seasons = 15
+allowed_water_types = 2
+rarity = 2
+base_catch_weight = 0.24
+catch_profile = ExtResource("2_profile")
+availability = SubResource("Availability")
+weight_min_lb = 0.2
+weight_max_lb = 15.0
+sell_value_min = 15
+sell_value_max = 36
+sell_value_curve = 0.88
+display_texture = ExtResource("99_fish_placeholder")
diff --git a/fish/species/octopus_giant_pacific/octopus_giant_pacific.tres b/fish/species/octopus_giant_pacific/octopus_giant_pacific.tres
new file mode 100644
index 0000000..1091088
--- /dev/null
+++ b/fish/species/octopus_giant_pacific/octopus_giant_pacific.tres
@@ -0,0 +1,32 @@
+[gd_resource type="Resource" script_class="FishData" load_steps=5 format=3]
+
+[ext_resource type="Script" 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" path="res://fish/fish_availability.gd" id="3_availability"]
+[ext_resource type="Texture2D" path="res://art/exported/creatures/fish_placeholder.png" id="99_fish_placeholder"]
+
+[sub_resource type="Resource" id="Availability"]
+script = ExtResource("3_availability")
+allow_day = false
+require_fog = true
+
+[resource]
+script = ExtResource("1_fish_data")
+id = &"octopus_giant_pacific"
+display_name = "giant Pacific octopus"
+active = true
+catalog_number = 4903
+collection_group = &"Octopuses"
+logbook_fact = "giant pacific octopuses explore cold rocky dens with thousands of arm suckers. females guard their eggs without feeding until they hatch."
+available_seasons = 15
+allowed_water_types = 2
+rarity = 3
+base_catch_weight = 0.1
+catch_profile = ExtResource("2_profile")
+availability = SubResource("Availability")
+weight_min_lb = 5.0
+weight_max_lb = 200.0
+sell_value_min = 28
+sell_value_max = 70
+sell_value_curve = 0.81
+display_texture = ExtResource("99_fish_placeholder")
diff --git a/fish/species/octopus_greater_blue_ringed/octopus_greater_blue_ringed.tres b/fish/species/octopus_greater_blue_ringed/octopus_greater_blue_ringed.tres
new file mode 100644
index 0000000..f2a21d9
--- /dev/null
+++ b/fish/species/octopus_greater_blue_ringed/octopus_greater_blue_ringed.tres
@@ -0,0 +1,32 @@
+[gd_resource type="Resource" script_class="FishData" load_steps=5 format=3]
+
+[ext_resource type="Script" 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" path="res://fish/fish_availability.gd" id="3_availability"]
+[ext_resource type="Texture2D" path="res://art/exported/creatures/fish_placeholder.png" id="99_fish_placeholder"]
+
+[sub_resource type="Resource" id="Availability"]
+script = ExtResource("3_availability")
+allow_night = false
+require_rain = true
+
+[resource]
+script = ExtResource("1_fish_data")
+id = &"octopus_greater_blue_ringed"
+display_name = "greater blue-ringed octopus"
+active = true
+catalog_number = 4904
+collection_group = &"Octopuses"
+logbook_fact = "greater blue-ringed octopuses reveal vivid rings when threatened. a tiny beak can deliver venom strong enough to stop breathing."
+available_seasons = 15
+allowed_water_types = 2
+rarity = 4
+base_catch_weight = 0.04
+catch_profile = ExtResource("2_profile")
+availability = SubResource("Availability")
+weight_min_lb = 0.05
+weight_max_lb = 0.3
+sell_value_min = 38
+sell_value_max = 82
+sell_value_curve = 0.96
+display_texture = ExtResource("99_fish_placeholder")
diff --git a/fish/species/opah/opah.tres b/fish/species/opah/opah.tres
new file mode 100644
index 0000000..f9b202a
--- /dev/null
+++ b/fish/species/opah/opah.tres
@@ -0,0 +1,32 @@
+[gd_resource type="Resource" script_class="FishData" load_steps=5 format=3]
+
+[ext_resource type="Script" 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" path="res://fish/fish_availability.gd" id="3_availability"]
+[ext_resource type="Texture2D" path="res://art/exported/creatures/fish_placeholder.png" id="99_fish_placeholder"]
+
+[sub_resource type="Resource" id="Availability"]
+script = ExtResource("3_availability")
+allow_night = false
+forbid_fog = true
+
+[resource]
+script = ExtResource("1_fish_data")
+id = &"opah"
+display_name = "opah"
+active = true
+catalog_number = 4702
+collection_group = &"Oceanic pelagic fish"
+logbook_fact = "opah circulate warm blood through much of the body, an unusual trait among fish. round silver flanks glow with red fins and pale spots."
+available_seasons = 6
+allowed_water_types = 2
+rarity = 3
+base_catch_weight = 0.09
+catch_profile = ExtResource("2_profile")
+availability = SubResource("Availability")
+weight_min_lb = 20.0
+weight_max_lb = 600.0
+sell_value_min = 36
+sell_value_max = 88
+sell_value_curve = 0.8
+display_texture = ExtResource("99_fish_placeholder")
diff --git a/fish/species/oscar/oscar.tres b/fish/species/oscar/oscar.tres
new file mode 100644
index 0000000..8e780cb
--- /dev/null
+++ b/fish/species/oscar/oscar.tres
@@ -0,0 +1,31 @@
+[gd_resource type="Resource" script_class="FishData" load_steps=5 format=3]
+
+[ext_resource type="Script" 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" path="res://fish/fish_availability.gd" id="3_availability"]
+[ext_resource type="Texture2D" path="res://art/exported/creatures/fish_placeholder.png" id="99_fish_placeholder"]
+
+[sub_resource type="Resource" id="Availability"]
+script = ExtResource("3_availability")
+allow_night = false
+
+[resource]
+script = ExtResource("1_fish_data")
+id = &"oscar"
+display_name = "oscar"
+active = true
+catalog_number = 7507
+collection_group = &"Tropical freshwater fish"
+logbook_fact = "oscars are large south american cichlids that recognize routines and defend territories. a tail eyespot may redirect attacks away from the head."
+available_seasons = 15
+allowed_water_types = 1
+rarity = 1
+base_catch_weight = 0.8
+catch_profile = ExtResource("2_profile")
+availability = SubResource("Availability")
+weight_min_lb = 0.3
+weight_max_lb = 4.0
+sell_value_min = 6
+sell_value_max = 14
+sell_value_curve = 0.9
+display_texture = ExtResource("99_fish_placeholder")
diff --git a/fish/species/oyster_black_lip_pearl/oyster_black_lip_pearl.tres b/fish/species/oyster_black_lip_pearl/oyster_black_lip_pearl.tres
new file mode 100644
index 0000000..776218d
--- /dev/null
+++ b/fish/species/oyster_black_lip_pearl/oyster_black_lip_pearl.tres
@@ -0,0 +1,31 @@
+[gd_resource type="Resource" script_class="FishData" load_steps=5 format=3]
+
+[ext_resource type="Script" 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" path="res://fish/fish_availability.gd" id="3_availability"]
+[ext_resource type="Texture2D" path="res://art/exported/creatures/fish_placeholder.png" id="99_fish_placeholder"]
+
+[sub_resource type="Resource" id="Availability"]
+script = ExtResource("3_availability")
+allow_night = false
+
+[resource]
+script = ExtResource("1_fish_data")
+id = &"oyster_black_lip_pearl"
+display_name = "black-lip pearl oyster"
+active = true
+catalog_number = 6401
+collection_group = &"Shellfish"
+logbook_fact = "black-lip pearl oysters line their shells with dark-edged nacre. a trapped irritant can become coated in the same pearly material."
+available_seasons = 15
+allowed_water_types = 2
+rarity = 2
+base_catch_weight = 0.22
+catch_profile = ExtResource("2_profile")
+availability = SubResource("Availability")
+weight_min_lb = 0.1
+weight_max_lb = 5.0
+sell_value_min = 14
+sell_value_max = 40
+sell_value_curve = 0.91
+display_texture = ExtResource("99_fish_placeholder")
diff --git a/fish/species/paddlefish/paddlefish.png b/fish/species/paddlefish/paddlefish.png
new file mode 100644
index 0000000..4a564d2
Binary files /dev/null and b/fish/species/paddlefish/paddlefish.png differ
diff --git a/fish/species/paddlefish/paddlefish.png.import b/fish/species/paddlefish/paddlefish.png.import
new file mode 100644
index 0000000..543fc80
--- /dev/null
+++ b/fish/species/paddlefish/paddlefish.png.import
@@ -0,0 +1,40 @@
+[remap]
+
+importer="texture"
+type="CompressedTexture2D"
+uid="uid://djnsidpdhgay5"
+path="res://.godot/imported/paddlefish.png-396db174ea9cb0a42741f573e0ac5be3.ctex"
+metadata={
+"vram_texture": false
+}
+
+[deps]
+
+source_file="res://fish/species/paddlefish/paddlefish.png"
+dest_files=["res://.godot/imported/paddlefish.png-396db174ea9cb0a42741f573e0ac5be3.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/paddlefish/paddlefish.tres b/fish/species/paddlefish/paddlefish.tres
new file mode 100644
index 0000000..498730b
--- /dev/null
+++ b/fish/species/paddlefish/paddlefish.tres
@@ -0,0 +1,32 @@
+[gd_resource type="Resource" script_class="FishData" load_steps=5 format=3]
+
+[ext_resource type="Script" 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" path="res://fish/fish_availability.gd" id="3_availability"]
+[ext_resource type="Texture2D" path="res://art/exported/creatures/104.png" id="4_texture"]
+
+[sub_resource type="Resource" id="Availability"]
+script = ExtResource("3_availability")
+allow_night = false
+require_rain = true
+
+[resource]
+script = ExtResource("1_fish_data")
+id = &"paddlefish"
+display_name = "paddlefish"
+active = true
+catalog_number = 104
+collection_group = &"Ancient freshwater fish"
+logbook_fact = "paddlefish cruise open rivers with their mouths wide, filtering tiny plankton from the water. the broad snout senses faint electrical signals."
+available_seasons = 3
+allowed_water_types = 1
+rarity = 3
+base_catch_weight = 0.1
+catch_profile = ExtResource("2_profile")
+availability = SubResource("Availability")
+weight_min_lb = 10.0
+weight_max_lb = 120.0
+sell_value_min = 34
+sell_value_max = 76
+sell_value_curve = 0.82
+display_texture = ExtResource("4_texture")
diff --git a/fish/species/parrotfish_rainbow/parrotfish_rainbow.tres b/fish/species/parrotfish_rainbow/parrotfish_rainbow.tres
new file mode 100644
index 0000000..c0b0dc2
--- /dev/null
+++ b/fish/species/parrotfish_rainbow/parrotfish_rainbow.tres
@@ -0,0 +1,31 @@
+[gd_resource type="Resource" script_class="FishData" load_steps=5 format=3]
+
+[ext_resource type="Script" 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" path="res://fish/fish_availability.gd" id="3_availability"]
+[ext_resource type="Texture2D" path="res://art/exported/creatures/fish_placeholder.png" id="99_fish_placeholder"]
+
+[sub_resource type="Resource" id="Availability"]
+script = ExtResource("3_availability")
+allow_night = false
+
+[resource]
+script = ExtResource("1_fish_data")
+id = &"parrotfish_rainbow"
+display_name = "rainbow parrotfish"
+active = true
+catalog_number = 5502
+collection_group = &"Reef grazers"
+logbook_fact = "rainbow parrotfish scrape algae from reefs with fused beak-like teeth. crushed coral passes through them and returns to the reef as sand."
+available_seasons = 15
+allowed_water_types = 2
+rarity = 3
+base_catch_weight = 0.12
+catch_profile = ExtResource("2_profile")
+availability = SubResource("Availability")
+weight_min_lb = 2.0
+weight_max_lb = 40.0
+sell_value_min = 25
+sell_value_max = 58
+sell_value_curve = 0.84
+display_texture = ExtResource("99_fish_placeholder")
diff --git a/fish/species/parrotfish_stoplight/parrotfish_stoplight.tres b/fish/species/parrotfish_stoplight/parrotfish_stoplight.tres
new file mode 100644
index 0000000..50962f4
--- /dev/null
+++ b/fish/species/parrotfish_stoplight/parrotfish_stoplight.tres
@@ -0,0 +1,31 @@
+[gd_resource type="Resource" script_class="FishData" load_steps=5 format=3]
+
+[ext_resource type="Script" 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" path="res://fish/fish_availability.gd" id="3_availability"]
+[ext_resource type="Texture2D" path="res://art/exported/creatures/fish_placeholder.png" id="99_fish_placeholder"]
+
+[sub_resource type="Resource" id="Availability"]
+script = ExtResource("3_availability")
+allow_night = false
+
+[resource]
+script = ExtResource("1_fish_data")
+id = &"parrotfish_stoplight"
+display_name = "stoplight parrotfish"
+active = true
+catalog_number = 5503
+collection_group = &"Reef grazers"
+logbook_fact = "stoplight parrotfish change color dramatically as they mature. powerful dental plates bite algae from hard reef surfaces."
+available_seasons = 15
+allowed_water_types = 2
+rarity = 2
+base_catch_weight = 0.28
+catch_profile = ExtResource("2_profile")
+availability = SubResource("Availability")
+weight_min_lb = 0.5
+weight_max_lb = 15.0
+sell_value_min = 12
+sell_value_max = 29
+sell_value_curve = 0.88
+display_texture = ExtResource("99_fish_placeholder")
diff --git a/fish/species/peacock_bass/peacock_bass.tres b/fish/species/peacock_bass/peacock_bass.tres
new file mode 100644
index 0000000..7cfbb5d
--- /dev/null
+++ b/fish/species/peacock_bass/peacock_bass.tres
@@ -0,0 +1,31 @@
+[gd_resource type="Resource" script_class="FishData" load_steps=5 format=3]
+
+[ext_resource type="Script" 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" path="res://fish/fish_availability.gd" id="3_availability"]
+[ext_resource type="Texture2D" path="res://art/exported/creatures/fish_placeholder.png" id="99_fish_placeholder"]
+
+[sub_resource type="Resource" id="Availability"]
+script = ExtResource("3_availability")
+allow_night = false
+
+[resource]
+script = ExtResource("1_fish_data")
+id = &"peacock_bass"
+display_name = "peacock bass"
+active = true
+catalog_number = 7604
+collection_group = &"Tropical freshwater predators"
+logbook_fact = "peacock bass are aggressive south american cichlids rather than true bass. adults guard nests and hunt fish in warm clear water."
+available_seasons = 15
+allowed_water_types = 1
+rarity = 2
+base_catch_weight = 0.25
+catch_profile = ExtResource("2_profile")
+availability = SubResource("Availability")
+weight_min_lb = 1.0
+weight_max_lb = 25.0
+sell_value_min = 15
+sell_value_max = 36
+sell_value_curve = 0.84
+display_texture = ExtResource("99_fish_placeholder")
diff --git a/fish/species/permit/permit.tres b/fish/species/permit/permit.tres
new file mode 100644
index 0000000..f58283c
--- /dev/null
+++ b/fish/species/permit/permit.tres
@@ -0,0 +1,32 @@
+[gd_resource type="Resource" script_class="FishData" load_steps=5 format=3]
+
+[ext_resource type="Script" 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" path="res://fish/fish_availability.gd" id="3_availability"]
+[ext_resource type="Texture2D" path="res://art/exported/creatures/fish_placeholder.png" id="99_fish_placeholder"]
+
+[sub_resource type="Resource" id="Availability"]
+script = ExtResource("3_availability")
+allow_night = false
+forbid_rain = true
+
+[resource]
+script = ExtResource("1_fish_data")
+id = &"permit"
+display_name = "permit"
+active = true
+catalog_number = 7404
+collection_group = &"Tropical flats fish"
+logbook_fact = "permit browse tropical flats for crabs and shellfish with rubbery mouths. tall silver bodies carry long sickle-shaped dorsal and anal fins."
+available_seasons = 15
+allowed_water_types = 2
+rarity = 3
+base_catch_weight = 0.11
+catch_profile = ExtResource("2_profile")
+availability = SubResource("Availability")
+weight_min_lb = 3.0
+weight_max_lb = 50.0
+sell_value_min = 27
+sell_value_max = 62
+sell_value_curve = 0.84
+display_texture = ExtResource("99_fish_placeholder")
diff --git a/fish/species/pickerel_chain/pickerel_chain.tres b/fish/species/pickerel_chain/pickerel_chain.tres
new file mode 100644
index 0000000..db02b7f
--- /dev/null
+++ b/fish/species/pickerel_chain/pickerel_chain.tres
@@ -0,0 +1,32 @@
+[gd_resource type="Resource" script_class="FishData" load_steps=5 format=3]
+
+[ext_resource type="Script" 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" path="res://fish/fish_availability.gd" id="3_availability"]
+[ext_resource type="Texture2D" path="res://art/exported/creatures/fish_placeholder.png" id="99_fish_placeholder"]
+
+[sub_resource type="Resource" id="Availability"]
+script = ExtResource("3_availability")
+allow_night = false
+require_rain = true
+
+[resource]
+script = ExtResource("1_fish_data")
+id = &"pickerel_chain"
+display_name = "chain pickerel"
+active = true
+catalog_number = 3301
+collection_group = &"Freshwater predators"
+logbook_fact = "chain pickerel hover among vegetation before darting at fish and frogs. a chain-like pattern breaks up their green flanks."
+available_seasons = 13
+allowed_water_types = 1
+rarity = 1
+base_catch_weight = 0.8
+catch_profile = ExtResource("2_profile")
+availability = SubResource("Availability")
+weight_min_lb = 0.5
+weight_max_lb = 9.0
+sell_value_min = 6
+sell_value_max = 15
+sell_value_curve = 0.9
+display_texture = ExtResource("99_fish_placeholder")
diff --git a/fish/species/pickerel_grass/pickerel_grass.tres b/fish/species/pickerel_grass/pickerel_grass.tres
new file mode 100644
index 0000000..9ef8952
--- /dev/null
+++ b/fish/species/pickerel_grass/pickerel_grass.tres
@@ -0,0 +1,31 @@
+[gd_resource type="Resource" script_class="FishData" load_steps=5 format=3]
+
+[ext_resource type="Script" 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" path="res://fish/fish_availability.gd" id="3_availability"]
+[ext_resource type="Texture2D" path="res://art/exported/creatures/fish_placeholder.png" id="99_fish_placeholder"]
+
+[sub_resource type="Resource" id="Availability"]
+script = ExtResource("3_availability")
+allow_night = false
+
+[resource]
+script = ExtResource("1_fish_data")
+id = &"pickerel_grass"
+display_name = "grass pickerel"
+active = true
+catalog_number = 3302
+collection_group = &"Freshwater predators"
+logbook_fact = "grass pickerel remain small enough to hunt in narrow, weedy creeks and marshes. dark bars camouflage their slim bodies among stems."
+available_seasons = 13
+allowed_water_types = 1
+rarity = 0
+base_catch_weight = 2.1
+catch_profile = ExtResource("2_profile")
+availability = SubResource("Availability")
+weight_min_lb = 0.1
+weight_max_lb = 1.5
+sell_value_min = 2
+sell_value_max = 6
+sell_value_curve = 0.95
+display_texture = ExtResource("99_fish_placeholder")
diff --git a/fish/species/pike_northern/pike_northern.tres b/fish/species/pike_northern/pike_northern.tres
new file mode 100644
index 0000000..05f1ed7
--- /dev/null
+++ b/fish/species/pike_northern/pike_northern.tres
@@ -0,0 +1,31 @@
+[gd_resource type="Resource" script_class="FishData" load_steps=5 format=3]
+
+[ext_resource type="Script" 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" path="res://fish/fish_availability.gd" id="3_availability"]
+[ext_resource type="Texture2D" path="res://art/exported/creatures/fish_placeholder.png" id="99_fish_placeholder"]
+
+[sub_resource type="Resource" id="Availability"]
+script = ExtResource("3_availability")
+allow_night = false
+
+[resource]
+script = ExtResource("1_fish_data")
+id = &"pike_northern"
+display_name = "northern pike"
+active = true
+catalog_number = 3303
+collection_group = &"Freshwater predators"
+logbook_fact = "northern pike are solitary ambush hunters built for sudden acceleration. pale bean-shaped spots cover their long green bodies."
+available_seasons = 13
+allowed_water_types = 1
+rarity = 1
+base_catch_weight = 0.75
+catch_profile = ExtResource("2_profile")
+availability = SubResource("Availability")
+weight_min_lb = 1.0
+weight_max_lb = 30.0
+sell_value_min = 8
+sell_value_max = 24
+sell_value_curve = 0.85
+display_texture = ExtResource("99_fish_placeholder")
diff --git a/fish/species/pipefish_bay/pipefish_bay.tres b/fish/species/pipefish_bay/pipefish_bay.tres
new file mode 100644
index 0000000..0436a8a
--- /dev/null
+++ b/fish/species/pipefish_bay/pipefish_bay.tres
@@ -0,0 +1,31 @@
+[gd_resource type="Resource" script_class="FishData" load_steps=5 format=3]
+
+[ext_resource type="Script" 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" path="res://fish/fish_availability.gd" id="3_availability"]
+[ext_resource type="Texture2D" path="res://art/exported/creatures/fish_placeholder.png" id="99_fish_placeholder"]
+
+[sub_resource type="Resource" id="Availability"]
+script = ExtResource("3_availability")
+allow_night = false
+
+[resource]
+script = ExtResource("1_fish_data")
+id = &"pipefish_bay"
+display_name = "bay pipefish"
+active = true
+catalog_number = 601
+collection_group = &"Coastal oddities"
+logbook_fact = "bay pipefish hide upright among seagrass, matching the blades with their slender bodies. males carry developing eggs in a brood pouch."
+available_seasons = 15
+allowed_water_types = 2
+rarity = 1
+base_catch_weight = 0.9
+catch_profile = ExtResource("2_profile")
+availability = SubResource("Availability")
+weight_min_lb = 0.005
+weight_max_lb = 0.04
+sell_value_min = 4
+sell_value_max = 9
+sell_value_curve = 1.0
+display_texture = ExtResource("99_fish_placeholder")
diff --git a/fish/species/piranha_red_bellied/piranha_red_bellied.tres b/fish/species/piranha_red_bellied/piranha_red_bellied.tres
new file mode 100644
index 0000000..ebc9fab
--- /dev/null
+++ b/fish/species/piranha_red_bellied/piranha_red_bellied.tres
@@ -0,0 +1,31 @@
+[gd_resource type="Resource" script_class="FishData" load_steps=5 format=3]
+
+[ext_resource type="Script" 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" path="res://fish/fish_availability.gd" id="3_availability"]
+[ext_resource type="Texture2D" path="res://art/exported/creatures/fish_placeholder.png" id="99_fish_placeholder"]
+
+[sub_resource type="Resource" id="Availability"]
+script = ExtResource("3_availability")
+require_rain = true
+
+[resource]
+script = ExtResource("1_fish_data")
+id = &"piranha_red_bellied"
+display_name = "red-bellied piranha"
+active = true
+catalog_number = 7605
+collection_group = &"Tropical freshwater predators"
+logbook_fact = "red-bellied piranhas communicate with barks and drum-like sounds. schools provide protection as they search for fish, insects, and carrion."
+available_seasons = 15
+allowed_water_types = 1
+rarity = 2
+base_catch_weight = 0.3
+catch_profile = ExtResource("2_profile")
+availability = SubResource("Availability")
+weight_min_lb = 0.2
+weight_max_lb = 4.0
+sell_value_min = 12
+sell_value_max = 27
+sell_value_curve = 0.9
+display_texture = ExtResource("99_fish_placeholder")
diff --git a/fish/species/plaice_european/plaice_european.tres b/fish/species/plaice_european/plaice_european.tres
new file mode 100644
index 0000000..e66f162
--- /dev/null
+++ b/fish/species/plaice_european/plaice_european.tres
@@ -0,0 +1,31 @@
+[gd_resource type="Resource" script_class="FishData" load_steps=5 format=3]
+
+[ext_resource type="Script" 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" path="res://fish/fish_availability.gd" id="3_availability"]
+[ext_resource type="Texture2D" path="res://art/exported/creatures/fish_placeholder.png" id="99_fish_placeholder"]
+
+[sub_resource type="Resource" id="Availability"]
+script = ExtResource("3_availability")
+allow_night = false
+
+[resource]
+script = ExtResource("1_fish_data")
+id = &"plaice_european"
+display_name = "European plaice"
+active = true
+catalog_number = 401
+collection_group = &"Coastal flatfish"
+logbook_fact = "european plaice lie flat on the seafloor with both eyes on the same side. orange spots help distinguish them from neighboring flatfish."
+available_seasons = 13
+allowed_water_types = 2
+rarity = 0
+base_catch_weight = 1.8
+catch_profile = ExtResource("2_profile")
+availability = SubResource("Availability")
+weight_min_lb = 0.2
+weight_max_lb = 10.0
+sell_value_min = 4
+sell_value_max = 10
+sell_value_curve = 0.9
+display_texture = ExtResource("99_fish_placeholder")
diff --git a/fish/species/pollock_alaska/pollock_alaska.tres b/fish/species/pollock_alaska/pollock_alaska.tres
new file mode 100644
index 0000000..e9add13
--- /dev/null
+++ b/fish/species/pollock_alaska/pollock_alaska.tres
@@ -0,0 +1,30 @@
+[gd_resource type="Resource" script_class="FishData" load_steps=5 format=3]
+
+[ext_resource type="Script" 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" path="res://fish/fish_availability.gd" id="3_availability"]
+[ext_resource type="Texture2D" path="res://art/exported/creatures/fish_placeholder.png" id="99_fish_placeholder"]
+
+[sub_resource type="Resource" id="Availability"]
+script = ExtResource("3_availability")
+
+[resource]
+script = ExtResource("1_fish_data")
+id = &"pollock_alaska"
+display_name = "Alaska pollock"
+active = true
+catalog_number = 1401
+collection_group = &"Cold shelf fish"
+logbook_fact = "alaska pollock form vast schools over cold northern shelves. they feed on plankton and fish while supporting many larger predators."
+available_seasons = 13
+allowed_water_types = 2
+rarity = 0
+base_catch_weight = 2.5
+catch_profile = ExtResource("2_profile")
+availability = SubResource("Availability")
+weight_min_lb = 0.3
+weight_max_lb = 8.0
+sell_value_min = 3
+sell_value_max = 8
+sell_value_curve = 0.92
+display_texture = ExtResource("99_fish_placeholder")
diff --git a/fish/species/pollock_atlantic/pollock_atlantic.tres b/fish/species/pollock_atlantic/pollock_atlantic.tres
new file mode 100644
index 0000000..494dd23
--- /dev/null
+++ b/fish/species/pollock_atlantic/pollock_atlantic.tres
@@ -0,0 +1,32 @@
+[gd_resource type="Resource" script_class="FishData" load_steps=5 format=3]
+
+[ext_resource type="Script" 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" path="res://fish/fish_availability.gd" id="3_availability"]
+[ext_resource type="Texture2D" path="res://art/exported/creatures/fish_placeholder.png" id="99_fish_placeholder"]
+
+[sub_resource type="Resource" id="Availability"]
+script = ExtResource("3_availability")
+allow_night = false
+forbid_fog = true
+
+[resource]
+script = ExtResource("1_fish_data")
+id = &"pollock_atlantic"
+display_name = "Atlantic pollock"
+active = true
+catalog_number = 1403
+collection_group = &"Cold shelf fish"
+logbook_fact = "atlantic pollock are swift relatives of cod with dark backs and pale lateral lines. adults gather around rocky banks and wrecks."
+available_seasons = 13
+allowed_water_types = 2
+rarity = 1
+base_catch_weight = 0.9
+catch_profile = ExtResource("2_profile")
+availability = SubResource("Availability")
+weight_min_lb = 1.0
+weight_max_lb = 35.0
+sell_value_min = 7
+sell_value_max = 18
+sell_value_curve = 0.86
+display_texture = ExtResource("99_fish_placeholder")
diff --git a/fish/species/pomfret_black/fish_pomfret_black.png b/fish/species/pomfret_black/fish_pomfret_black.png
index 14ae878..3fd52d8 100644
Binary files a/fish/species/pomfret_black/fish_pomfret_black.png and b/fish/species/pomfret_black/fish_pomfret_black.png differ
diff --git a/fish/species/pomfret_black/pomfret_black.tres b/fish/species/pomfret_black/pomfret_black.tres
index 8020100..a3c9e54 100644
--- a/fish/species/pomfret_black/pomfret_black.tres
+++ b/fish/species/pomfret_black/pomfret_black.tres
@@ -3,7 +3,7 @@
[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"]
+[ext_resource type="Texture2D" path="res://art/exported/creatures/fish_placeholder.png" id="4_texture"]
[sub_resource type="Resource" id="PomfretBlackAvailability"]
script = ExtResource("3_availability_script")
@@ -13,6 +13,11 @@ allowed_location_tags = Array[StringName]([&"ocean"])
script = ExtResource("1_fish_data")
id = &"pomfret_black"
display_name = "black pomfret"
+active = true
+catalog_number = 803
+collection_group = &"Coastal schooling fish"
+logbook_fact = "black pomfret have a deep, laterally compressed body that lets them maneuver neatly through open water."
+available_seasons = 15
allowed_water_types = 2
rarity = 1
base_catch_weight = 0.3
diff --git a/fish/species/pomfret_chinese/fish_pomfret_chinese.png b/fish/species/pomfret_chinese/fish_pomfret_chinese.png
index a7bb55e..2280027 100644
Binary files a/fish/species/pomfret_chinese/fish_pomfret_chinese.png and b/fish/species/pomfret_chinese/fish_pomfret_chinese.png differ
diff --git a/fish/species/pomfret_chinese/pomfret_chinese.tres b/fish/species/pomfret_chinese/pomfret_chinese.tres
index 2eaf34b..e3f236c 100644
--- a/fish/species/pomfret_chinese/pomfret_chinese.tres
+++ b/fish/species/pomfret_chinese/pomfret_chinese.tres
@@ -3,7 +3,7 @@
[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"]
+[ext_resource type="Texture2D" path="res://art/exported/creatures/fish_placeholder.png" id="4_texture"]
[sub_resource type="Resource" id="PomfretChineseAvailability"]
script = ExtResource("3_availability_script")
@@ -13,6 +13,11 @@ allowed_location_tags = Array[StringName]([&"ocean"])
script = ExtResource("1_fish_data")
id = &"pomfret_chinese"
display_name = "chinese pomfret"
+active = true
+catalog_number = 804
+collection_group = &"Coastal schooling fish"
+logbook_fact = "chinese pomfret are silvery schooling fish with a tall, flattened body and long sickle-shaped fins."
+available_seasons = 15
allowed_water_types = 2
rarity = 1
base_catch_weight = 0.3
diff --git a/fish/species/pomfret_golden/fish_pomfret_golden.png b/fish/species/pomfret_golden/fish_pomfret_golden.png
index a844b87..3929da6 100644
Binary files a/fish/species/pomfret_golden/fish_pomfret_golden.png and b/fish/species/pomfret_golden/fish_pomfret_golden.png differ
diff --git a/fish/species/pomfret_golden/pomfret_golden.tres b/fish/species/pomfret_golden/pomfret_golden.tres
index da1f561..162a2e7 100644
--- a/fish/species/pomfret_golden/pomfret_golden.tres
+++ b/fish/species/pomfret_golden/pomfret_golden.tres
@@ -3,7 +3,7 @@
[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"]
+[ext_resource type="Texture2D" path="res://art/exported/creatures/fish_placeholder.png" id="4_texture"]
[sub_resource type="Resource" id="PomfretGoldenAvailability"]
script = ExtResource("3_availability_script")
@@ -13,6 +13,11 @@ allowed_location_tags = Array[StringName]([&"ocean"])
script = ExtResource("1_fish_data")
id = &"pomfret_golden"
display_name = "golden pomfret"
+active = true
+catalog_number = 807
+collection_group = &"Coastal schooling fish"
+logbook_fact = "golden pomfret are warm-water swimmers whose golden sheen is most noticeable along the fins and flanks."
+available_seasons = 15
allowed_water_types = 2
rarity = 2
base_catch_weight = 0.18
diff --git a/fish/species/pomfret_white/fish_pomfret_white.png b/fish/species/pomfret_white/fish_pomfret_white.png
index 0f2d426..cf020d5 100644
Binary files a/fish/species/pomfret_white/fish_pomfret_white.png and b/fish/species/pomfret_white/fish_pomfret_white.png differ
diff --git a/fish/species/pomfret_white/pomfret_white.tres b/fish/species/pomfret_white/pomfret_white.tres
index fa317c7..8652e63 100644
--- a/fish/species/pomfret_white/pomfret_white.tres
+++ b/fish/species/pomfret_white/pomfret_white.tres
@@ -3,7 +3,7 @@
[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"]
+[ext_resource type="Texture2D" path="res://art/exported/creatures/fish_placeholder.png" id="4_texture"]
[sub_resource type="Resource" id="PomfretWhiteAvailability"]
script = ExtResource("3_availability_script")
@@ -13,6 +13,11 @@ allowed_location_tags = Array[StringName]([&"ocean"])
script = ExtResource("1_fish_data")
id = &"pomfret_white"
display_name = "white pomfret"
+active = true
+catalog_number = 810
+collection_group = &"Coastal schooling fish"
+logbook_fact = "white pomfret gather over coastal grounds and use their compact, deep bodies to turn quickly while feeding."
+available_seasons = 15
allowed_water_types = 2
base_catch_weight = 0.45
catch_profile = ExtResource("2_profile")
diff --git a/fish/species/pompano_african/pompano_african.tres b/fish/species/pompano_african/pompano_african.tres
new file mode 100644
index 0000000..598da61
--- /dev/null
+++ b/fish/species/pompano_african/pompano_african.tres
@@ -0,0 +1,30 @@
+[gd_resource type="Resource" script_class="FishData" load_steps=5 format=3]
+
+[ext_resource type="Script" 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" path="res://fish/fish_availability.gd" id="3_availability"]
+[ext_resource type="Texture2D" path="res://art/exported/creatures/fish_placeholder.png" id="99_fish_placeholder"]
+
+[sub_resource type="Resource" id="Availability"]
+script = ExtResource("3_availability")
+
+[resource]
+script = ExtResource("1_fish_data")
+id = &"pompano_african"
+display_name = "African pompano"
+active = true
+catalog_number = 3601
+collection_group = &"Large coastal predators"
+logbook_fact = "african pompano develop long streaming fin rays while young. adults become deep-bodied silver hunters around reefs and offshore structure."
+available_seasons = 6
+allowed_water_types = 2
+rarity = 2
+base_catch_weight = 0.26
+catch_profile = ExtResource("2_profile")
+availability = SubResource("Availability")
+weight_min_lb = 2.0
+weight_max_lb = 50.0
+sell_value_min = 14
+sell_value_max = 35
+sell_value_curve = 0.84
+display_texture = ExtResource("99_fish_placeholder")
diff --git a/fish/species/pompano_florida/pompano_florida.tres b/fish/species/pompano_florida/pompano_florida.tres
new file mode 100644
index 0000000..b21035c
--- /dev/null
+++ b/fish/species/pompano_florida/pompano_florida.tres
@@ -0,0 +1,32 @@
+[gd_resource type="Resource" script_class="FishData" load_steps=5 format=3]
+
+[ext_resource type="Script" 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" path="res://fish/fish_availability.gd" id="3_availability"]
+[ext_resource type="Texture2D" path="res://art/exported/creatures/fish_placeholder.png" id="99_fish_placeholder"]
+
+[sub_resource type="Resource" id="Availability"]
+script = ExtResource("3_availability")
+allow_night = false
+forbid_fog = true
+
+[resource]
+script = ExtResource("1_fish_data")
+id = &"pompano_florida"
+display_name = "Florida pompano"
+active = true
+catalog_number = 7402
+collection_group = &"Tropical flats fish"
+logbook_fact = "florida pompano search warm surf for crabs, clams, and sand fleas. short blunt snouts lead into deep, silvery bodies."
+available_seasons = 15
+allowed_water_types = 2
+rarity = 1
+base_catch_weight = 0.85
+catch_profile = ExtResource("2_profile")
+availability = SubResource("Availability")
+weight_min_lb = 0.5
+weight_max_lb = 9.0
+sell_value_min = 7
+sell_value_max = 16
+sell_value_curve = 0.88
+display_texture = ExtResource("99_fish_placeholder")
diff --git a/fish/species/porcupinefish_spotted/porcupinefish_spotted.tres b/fish/species/porcupinefish_spotted/porcupinefish_spotted.tres
new file mode 100644
index 0000000..93d5dca
--- /dev/null
+++ b/fish/species/porcupinefish_spotted/porcupinefish_spotted.tres
@@ -0,0 +1,32 @@
+[gd_resource type="Resource" script_class="FishData" load_steps=5 format=3]
+
+[ext_resource type="Script" 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" path="res://fish/fish_availability.gd" id="3_availability"]
+[ext_resource type="Texture2D" path="res://art/exported/creatures/fish_placeholder.png" id="99_fish_placeholder"]
+
+[sub_resource type="Resource" id="Availability"]
+script = ExtResource("3_availability")
+allow_day = false
+require_fog = true
+
+[resource]
+script = ExtResource("1_fish_data")
+id = &"porcupinefish_spotted"
+display_name = "spotted porcupinefish"
+active = true
+catalog_number = 5607
+collection_group = &"Reef oddities"
+logbook_fact = "spotted porcupinefish inflate until long spines stand outward. fused teeth form a strong beak for crushing shelled prey."
+available_seasons = 15
+allowed_water_types = 2
+rarity = 3
+base_catch_weight = 0.12
+catch_profile = ExtResource("2_profile")
+availability = SubResource("Availability")
+weight_min_lb = 0.5
+weight_max_lb = 7.0
+sell_value_min = 24
+sell_value_max = 54
+sell_value_curve = 0.9
+display_texture = ExtResource("99_fish_placeholder")
diff --git a/fish/species/porgy_scup/porgy_scup.tres b/fish/species/porgy_scup/porgy_scup.tres
new file mode 100644
index 0000000..ad96831
--- /dev/null
+++ b/fish/species/porgy_scup/porgy_scup.tres
@@ -0,0 +1,31 @@
+[gd_resource type="Resource" script_class="FishData" load_steps=5 format=3]
+
+[ext_resource type="Script" 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" path="res://fish/fish_availability.gd" id="3_availability"]
+[ext_resource type="Texture2D" path="res://art/exported/creatures/6904.png" id="99_fish_placeholder"]
+
+[sub_resource type="Resource" id="Availability"]
+script = ExtResource("3_availability")
+allow_night = false
+
+[resource]
+script = ExtResource("1_fish_data")
+id = &"porgy_scup"
+display_name = "scup"
+active = true
+catalog_number = 6904
+collection_group = &"Temperate reef fish"
+logbook_fact = "scup use small strong teeth to crush shellfish and other bottom prey. deep silver bodies gather around piers, reefs, and eelgrass."
+available_seasons = 13
+allowed_water_types = 2
+rarity = 0
+base_catch_weight = 2.2
+catch_profile = ExtResource("2_profile")
+availability = SubResource("Availability")
+weight_min_lb = 0.1
+weight_max_lb = 5.0
+sell_value_min = 3
+sell_value_max = 8
+sell_value_curve = 0.92
+display_texture = ExtResource("99_fish_placeholder")
diff --git a/fish/species/pufferfish_guineafowl/pufferfish_guineafowl.tres b/fish/species/pufferfish_guineafowl/pufferfish_guineafowl.tres
new file mode 100644
index 0000000..754cf4d
--- /dev/null
+++ b/fish/species/pufferfish_guineafowl/pufferfish_guineafowl.tres
@@ -0,0 +1,31 @@
+[gd_resource type="Resource" script_class="FishData" load_steps=5 format=3]
+
+[ext_resource type="Script" 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" path="res://fish/fish_availability.gd" id="3_availability"]
+[ext_resource type="Texture2D" path="res://art/exported/creatures/fish_placeholder.png" id="99_fish_placeholder"]
+
+[sub_resource type="Resource" id="Availability"]
+script = ExtResource("3_availability")
+allow_night = false
+
+[resource]
+script = ExtResource("1_fish_data")
+id = &"pufferfish_guineafowl"
+display_name = "guineafowl puffer"
+active = true
+catalog_number = 5603
+collection_group = &"Reef oddities"
+logbook_fact = "guineafowl puffers swallow water to inflate when threatened. adults may shift from a spotted dark pattern to a vivid yellow form."
+available_seasons = 15
+allowed_water_types = 2
+rarity = 2
+base_catch_weight = 0.3
+catch_profile = ExtResource("2_profile")
+availability = SubResource("Availability")
+weight_min_lb = 0.2
+weight_max_lb = 5.0
+sell_value_min = 11
+sell_value_max = 26
+sell_value_curve = 0.9
+display_texture = ExtResource("99_fish_placeholder")
diff --git a/fish/species/pumpkinseed/pumpkinseed.tres b/fish/species/pumpkinseed/pumpkinseed.tres
new file mode 100644
index 0000000..7a4f090
--- /dev/null
+++ b/fish/species/pumpkinseed/pumpkinseed.tres
@@ -0,0 +1,31 @@
+[gd_resource type="Resource" script_class="FishData" load_steps=5 format=3]
+
+[ext_resource type="Script" 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" path="res://fish/fish_availability.gd" id="3_availability"]
+[ext_resource type="Texture2D" path="res://art/exported/creatures/3104.png" id="99_fish_placeholder"]
+
+[sub_resource type="Resource" id="Availability"]
+script = ExtResource("3_availability")
+allow_night = false
+
+[resource]
+script = ExtResource("1_fish_data")
+id = &"pumpkinseed"
+display_name = "pumpkinseed"
+active = true
+catalog_number = 3104
+collection_group = &"Freshwater panfish"
+logbook_fact = "pumpkinseeds use compact teeth to crush snails and other hard prey. blue facial lines and an orange gill spot brighten their round bodies."
+available_seasons = 7
+allowed_water_types = 1
+rarity = 0
+base_catch_weight = 3.0
+catch_profile = ExtResource("2_profile")
+availability = SubResource("Availability")
+weight_min_lb = 0.1
+weight_max_lb = 1.3
+sell_value_min = 2
+sell_value_max = 5
+sell_value_curve = 1.0
+display_texture = ExtResource("99_fish_placeholder")
diff --git a/fish/species/queenfish_talang/queenfish_talang.tres b/fish/species/queenfish_talang/queenfish_talang.tres
new file mode 100644
index 0000000..d6ee3be
--- /dev/null
+++ b/fish/species/queenfish_talang/queenfish_talang.tres
@@ -0,0 +1,31 @@
+[gd_resource type="Resource" script_class="FishData" load_steps=5 format=3]
+
+[ext_resource type="Script" 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" path="res://fish/fish_availability.gd" id="3_availability"]
+[ext_resource type="Texture2D" path="res://art/exported/creatures/fish_placeholder.png" id="99_fish_placeholder"]
+
+[sub_resource type="Resource" id="Availability"]
+script = ExtResource("3_availability")
+allow_night = false
+
+[resource]
+script = ExtResource("1_fish_data")
+id = &"queenfish_talang"
+display_name = "talang queenfish"
+active = true
+catalog_number = 708
+collection_group = &"Coastal pelagic fish"
+logbook_fact = "talang queenfish chase schooling prey near warm coasts and estuaries. hooked teeth and a deeply forked tail suit their rapid attacks."
+available_seasons = 15
+allowed_water_types = 2
+rarity = 1
+base_catch_weight = 0.8
+catch_profile = ExtResource("2_profile")
+availability = SubResource("Availability")
+weight_min_lb = 0.5
+weight_max_lb = 20.0
+sell_value_min = 7
+sell_value_max = 18
+sell_value_curve = 0.86
+display_texture = ExtResource("99_fish_placeholder")
diff --git a/fish/species/quillback/quillback.tres b/fish/species/quillback/quillback.tres
new file mode 100644
index 0000000..2ca6399
--- /dev/null
+++ b/fish/species/quillback/quillback.tres
@@ -0,0 +1,31 @@
+[gd_resource type="Resource" script_class="FishData" load_steps=5 format=3]
+
+[ext_resource type="Script" 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" path="res://fish/fish_availability.gd" id="3_availability"]
+[ext_resource type="Texture2D" path="res://art/exported/creatures/fish_placeholder.png" id="99_fish_placeholder"]
+
+[sub_resource type="Resource" id="Availability"]
+script = ExtResource("3_availability")
+allow_night = false
+
+[resource]
+script = ExtResource("1_fish_data")
+id = &"quillback"
+display_name = "quillback"
+active = true
+catalog_number = 2303
+collection_group = &"Freshwater bottom fish"
+logbook_fact = "quillbacks carry a long first dorsal ray that rises like a quill. small mouths gather insect larvae and other food from river and lake bottoms."
+available_seasons = 7
+allowed_water_types = 1
+rarity = 1
+base_catch_weight = 0.8
+catch_profile = ExtResource("2_profile")
+availability = SubResource("Availability")
+weight_min_lb = 0.5
+weight_max_lb = 15.0
+sell_value_min = 6
+sell_value_max = 16
+sell_value_curve = 0.88
+display_texture = ExtResource("99_fish_placeholder")
diff --git a/fish/species/red_drum/red_drum.tres b/fish/species/red_drum/red_drum.tres
new file mode 100644
index 0000000..393a002
--- /dev/null
+++ b/fish/species/red_drum/red_drum.tres
@@ -0,0 +1,31 @@
+[gd_resource type="Resource" script_class="FishData" load_steps=5 format=3]
+
+[ext_resource type="Script" 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" path="res://fish/fish_availability.gd" id="3_availability"]
+[ext_resource type="Texture2D" path="res://art/exported/creatures/fish_placeholder.png" id="99_fish_placeholder"]
+
+[sub_resource type="Resource" id="Availability"]
+script = ExtResource("3_availability")
+require_rain = true
+
+[resource]
+script = ExtResource("1_fish_data")
+id = &"red_drum"
+display_name = "red drum"
+active = true
+catalog_number = 6803
+collection_group = &"Temperate estuary"
+logbook_fact = "red drum use powerful tails to patrol surf and estuaries. one or more black spots near the tail may confuse predators about which end is the head."
+available_seasons = 7
+allowed_water_types = 2
+rarity = 1
+base_catch_weight = 0.75
+catch_profile = ExtResource("2_profile")
+availability = SubResource("Availability")
+weight_min_lb = 1.0
+weight_max_lb = 45.0
+sell_value_min = 8
+sell_value_max = 23
+sell_value_curve = 0.84
+display_texture = ExtResource("99_fish_placeholder")
diff --git a/fish/species/redear_sunfish/redear_sunfish.tres b/fish/species/redear_sunfish/redear_sunfish.tres
new file mode 100644
index 0000000..6078d20
--- /dev/null
+++ b/fish/species/redear_sunfish/redear_sunfish.tres
@@ -0,0 +1,31 @@
+[gd_resource type="Resource" script_class="FishData" load_steps=5 format=3]
+
+[ext_resource type="Script" 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" path="res://fish/fish_availability.gd" id="3_availability"]
+[ext_resource type="Texture2D" path="res://art/exported/creatures/fish_placeholder.png" id="99_fish_placeholder"]
+
+[sub_resource type="Resource" id="Availability"]
+script = ExtResource("3_availability")
+allow_night = false
+
+[resource]
+script = ExtResource("1_fish_data")
+id = &"redear_sunfish"
+display_name = "redear sunfish"
+active = true
+catalog_number = 3105
+collection_group = &"Freshwater panfish"
+logbook_fact = "redear sunfish specialize in snails, crushing shells with strong throat teeth. a red or orange margin marks the dark flap behind the gill."
+available_seasons = 7
+allowed_water_types = 1
+rarity = 1
+base_catch_weight = 1.1
+catch_profile = ExtResource("2_profile")
+availability = SubResource("Availability")
+weight_min_lb = 0.2
+weight_max_lb = 3.0
+sell_value_min = 4
+sell_value_max = 9
+sell_value_curve = 0.95
+display_texture = ExtResource("99_fish_placeholder")
diff --git a/fish/species/redhorse_golden/redhorse_golden.tres b/fish/species/redhorse_golden/redhorse_golden.tres
new file mode 100644
index 0000000..5ba103b
--- /dev/null
+++ b/fish/species/redhorse_golden/redhorse_golden.tres
@@ -0,0 +1,31 @@
+[gd_resource type="Resource" script_class="FishData" load_steps=5 format=3]
+
+[ext_resource type="Script" 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" path="res://fish/fish_availability.gd" id="3_availability"]
+[ext_resource type="Texture2D" path="res://art/exported/creatures/fish_placeholder.png" id="99_fish_placeholder"]
+
+[sub_resource type="Resource" id="Availability"]
+script = ExtResource("3_availability")
+allow_night = false
+
+[resource]
+script = ExtResource("1_fish_data")
+id = &"redhorse_golden"
+display_name = "golden redhorse"
+active = true
+catalog_number = 2302
+collection_group = &"Freshwater bottom fish"
+logbook_fact = "golden redhorses probe flowing river bottoms with fleshy, downward-facing mouths. coppery scales and reddish fins give them a warm glow."
+available_seasons = 7
+allowed_water_types = 1
+rarity = 1
+base_catch_weight = 0.9
+catch_profile = ExtResource("2_profile")
+availability = SubResource("Availability")
+weight_min_lb = 0.5
+weight_max_lb = 8.0
+sell_value_min = 6
+sell_value_max = 14
+sell_value_curve = 0.9
+display_texture = ExtResource("99_fish_placeholder")
diff --git a/fish/species/roach_common/roach_common.tres b/fish/species/roach_common/roach_common.tres
new file mode 100644
index 0000000..c12d380
--- /dev/null
+++ b/fish/species/roach_common/roach_common.tres
@@ -0,0 +1,30 @@
+[gd_resource type="Resource" script_class="FishData" load_steps=5 format=3]
+
+[ext_resource type="Script" 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" path="res://fish/fish_availability.gd" id="3_availability"]
+[ext_resource type="Texture2D" path="res://art/exported/creatures/2504.png" id="99_fish_placeholder"]
+
+[sub_resource type="Resource" id="Availability"]
+script = ExtResource("3_availability")
+
+[resource]
+script = ExtResource("1_fish_data")
+id = &"roach_common"
+display_name = "common roach"
+active = true
+catalog_number = 2504
+collection_group = &"Freshwater coarse fish"
+logbook_fact = "common roach thrive in many lakes and rivers, often schooling near vegetation. red fins and a red-orange eye brighten their silver bodies."
+available_seasons = 15
+allowed_water_types = 1
+rarity = 0
+base_catch_weight = 2.8
+catch_profile = ExtResource("2_profile")
+availability = SubResource("Availability")
+weight_min_lb = 0.1
+weight_max_lb = 4.0
+sell_value_min = 2
+sell_value_max = 6
+sell_value_curve = 0.95
+display_texture = ExtResource("99_fish_placeholder")
diff --git a/fish/species/rockfish_black/rockfish_black.tres b/fish/species/rockfish_black/rockfish_black.tres
new file mode 100644
index 0000000..c37b427
--- /dev/null
+++ b/fish/species/rockfish_black/rockfish_black.tres
@@ -0,0 +1,30 @@
+[gd_resource type="Resource" script_class="FishData" load_steps=5 format=3]
+
+[ext_resource type="Script" 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" path="res://fish/fish_availability.gd" id="3_availability"]
+[ext_resource type="Texture2D" path="res://art/exported/creatures/6901.png" id="99_fish_placeholder"]
+
+[sub_resource type="Resource" id="Availability"]
+script = ExtResource("3_availability")
+
+[resource]
+script = ExtResource("1_fish_data")
+id = &"rockfish_black"
+display_name = "black rockfish"
+active = true
+catalog_number = 6901
+collection_group = &"Temperate reef fish"
+logbook_fact = "black rockfish gather above reefs and kelp along the north pacific coast. like other rockfish, they give birth to live young."
+available_seasons = 13
+allowed_water_types = 2
+rarity = 0
+base_catch_weight = 1.7
+catch_profile = ExtResource("2_profile")
+availability = SubResource("Availability")
+weight_min_lb = 0.3
+weight_max_lb = 10.0
+sell_value_min = 4
+sell_value_max = 11
+sell_value_curve = 0.9
+display_texture = ExtResource("99_fish_placeholder")
diff --git a/fish/species/rockfish_canary/rockfish_canary.tres b/fish/species/rockfish_canary/rockfish_canary.tres
new file mode 100644
index 0000000..1e65d72
--- /dev/null
+++ b/fish/species/rockfish_canary/rockfish_canary.tres
@@ -0,0 +1,31 @@
+[gd_resource type="Resource" script_class="FishData" load_steps=5 format=3]
+
+[ext_resource type="Script" 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" path="res://fish/fish_availability.gd" id="3_availability"]
+[ext_resource type="Texture2D" path="res://art/exported/creatures/fish_placeholder.png" id="99_fish_placeholder"]
+
+[sub_resource type="Resource" id="Availability"]
+script = ExtResource("3_availability")
+allow_night = false
+
+[resource]
+script = ExtResource("1_fish_data")
+id = &"rockfish_canary"
+display_name = "canary rockfish"
+active = true
+catalog_number = 6903
+collection_group = &"Temperate reef fish"
+logbook_fact = "canary rockfish glow orange-yellow over deep pacific reefs. these long-lived fish mature slowly and give birth to live young."
+available_seasons = 13
+allowed_water_types = 2
+rarity = 1
+base_catch_weight = 0.7
+catch_profile = ExtResource("2_profile")
+availability = SubResource("Availability")
+weight_min_lb = 0.5
+weight_max_lb = 12.0
+sell_value_min = 7
+sell_value_max = 17
+sell_value_curve = 0.88
+display_texture = ExtResource("99_fish_placeholder")
diff --git a/fish/species/rockfish_yelloweye/rockfish_yelloweye.tres b/fish/species/rockfish_yelloweye/rockfish_yelloweye.tres
new file mode 100644
index 0000000..89aab8e
--- /dev/null
+++ b/fish/species/rockfish_yelloweye/rockfish_yelloweye.tres
@@ -0,0 +1,31 @@
+[gd_resource type="Resource" script_class="FishData" load_steps=5 format=3]
+
+[ext_resource type="Script" 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" path="res://fish/fish_availability.gd" id="3_availability"]
+[ext_resource type="Texture2D" path="res://art/exported/creatures/fish_placeholder.png" id="99_fish_placeholder"]
+
+[sub_resource type="Resource" id="Availability"]
+script = ExtResource("3_availability")
+require_fog = true
+
+[resource]
+script = ExtResource("1_fish_data")
+id = &"rockfish_yelloweye"
+display_name = "yelloweye rockfish"
+active = true
+catalog_number = 6907
+collection_group = &"Temperate reef fish"
+logbook_fact = "yelloweye rockfish can live for more than a century on deep pacific reefs. bright golden eyes stand out against orange-red bodies."
+available_seasons = 13
+allowed_water_types = 2
+rarity = 3
+base_catch_weight = 0.09
+catch_profile = ExtResource("2_profile")
+availability = SubResource("Availability")
+weight_min_lb = 2.0
+weight_max_lb = 35.0
+sell_value_min = 28
+sell_value_max = 64
+sell_value_curve = 0.84
+display_texture = ExtResource("99_fish_placeholder")
diff --git a/fish/species/roosterfish/roosterfish.tres b/fish/species/roosterfish/roosterfish.tres
new file mode 100644
index 0000000..388dd70
--- /dev/null
+++ b/fish/species/roosterfish/roosterfish.tres
@@ -0,0 +1,31 @@
+[gd_resource type="Resource" script_class="FishData" load_steps=5 format=3]
+
+[ext_resource type="Script" 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" path="res://fish/fish_availability.gd" id="3_availability"]
+[ext_resource type="Texture2D" path="res://art/exported/creatures/fish_placeholder.png" id="99_fish_placeholder"]
+
+[sub_resource type="Resource" id="Availability"]
+script = ExtResource("3_availability")
+allow_night = false
+
+[resource]
+script = ExtResource("1_fish_data")
+id = &"roosterfish"
+display_name = "roosterfish"
+active = true
+catalog_number = 7201
+collection_group = &"Tropical coastal predators"
+logbook_fact = "roosterfish raise seven long dorsal spines while chasing prey through tropical surf. dark bands cross their sleek silver bodies."
+available_seasons = 15
+allowed_water_types = 2
+rarity = 3
+base_catch_weight = 0.11
+catch_profile = ExtResource("2_profile")
+availability = SubResource("Availability")
+weight_min_lb = 5.0
+weight_max_lb = 100.0
+sell_value_min = 30
+sell_value_max = 70
+sell_value_curve = 0.82
+display_texture = ExtResource("99_fish_placeholder")
diff --git a/fish/species/rudd/rudd.tres b/fish/species/rudd/rudd.tres
new file mode 100644
index 0000000..5de8d57
--- /dev/null
+++ b/fish/species/rudd/rudd.tres
@@ -0,0 +1,31 @@
+[gd_resource type="Resource" script_class="FishData" load_steps=5 format=3]
+
+[ext_resource type="Script" 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" path="res://fish/fish_availability.gd" id="3_availability"]
+[ext_resource type="Texture2D" path="res://art/exported/creatures/2508.png" id="99_fish_placeholder"]
+
+[sub_resource type="Resource" id="Availability"]
+script = ExtResource("3_availability")
+allow_night = false
+
+[resource]
+script = ExtResource("1_fish_data")
+id = &"rudd"
+display_name = "rudd"
+active = true
+catalog_number = 2508
+collection_group = &"Freshwater coarse fish"
+logbook_fact = "rudd feed near the surface with an upturned mouth and red fins. golden-green bodies disappear easily among warm, weedy shallows."
+available_seasons = 7
+allowed_water_types = 1
+rarity = 0
+base_catch_weight = 2.4
+catch_profile = ExtResource("2_profile")
+availability = SubResource("Availability")
+weight_min_lb = 0.1
+weight_max_lb = 4.5
+sell_value_min = 3
+sell_value_max = 7
+sell_value_curve = 0.95
+display_texture = ExtResource("99_fish_placeholder")
diff --git a/fish/species/sablefish/sablefish.tres b/fish/species/sablefish/sablefish.tres
new file mode 100644
index 0000000..e481e52
--- /dev/null
+++ b/fish/species/sablefish/sablefish.tres
@@ -0,0 +1,31 @@
+[gd_resource type="Resource" script_class="FishData" load_steps=5 format=3]
+
+[ext_resource type="Script" 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" path="res://fish/fish_availability.gd" id="3_availability"]
+[ext_resource type="Texture2D" path="res://art/exported/creatures/fish_placeholder.png" id="99_fish_placeholder"]
+
+[sub_resource type="Resource" id="Availability"]
+script = ExtResource("3_availability")
+allow_day = false
+
+[resource]
+script = ExtResource("1_fish_data")
+id = &"sablefish"
+display_name = "sablefish"
+active = true
+catalog_number = 1605
+collection_group = &"Deep shelf fish"
+logbook_fact = "sablefish roam deep north pacific slopes and can live for many decades. soft dark bodies conceal rich, oil-filled flesh."
+available_seasons = 15
+allowed_water_types = 2
+rarity = 2
+base_catch_weight = 0.22
+catch_profile = ExtResource("2_profile")
+availability = SubResource("Availability")
+weight_min_lb = 2.0
+weight_max_lb = 40.0
+sell_value_min = 15
+sell_value_max = 36
+sell_value_curve = 0.84
+display_texture = ExtResource("99_fish_placeholder")
diff --git a/fish/species/sailfish/fish_sailfish.png b/fish/species/sailfish/fish_sailfish.png
index e359a3e..32b447e 100644
Binary files a/fish/species/sailfish/fish_sailfish.png and b/fish/species/sailfish/fish_sailfish.png differ
diff --git a/fish/species/sailfish/sailfish.tres b/fish/species/sailfish/sailfish.tres
index 7f6bea9..98e958d 100644
--- a/fish/species/sailfish/sailfish.tres
+++ b/fish/species/sailfish/sailfish.tres
@@ -3,7 +3,7 @@
[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"]
+[ext_resource type="Texture2D" path="res://art/exported/creatures/fish_placeholder.png" id="4_texture"]
[sub_resource type="Resource" id="SailfishAvailability"]
script = ExtResource("3_availability_script")
@@ -13,6 +13,11 @@ allowed_location_tags = Array[StringName]([&"ocean"])
script = ExtResource("1_fish_data")
id = &"sailfish"
display_name = "sailfish"
+active = true
+catalog_number = 4603
+collection_group = &"Oceanic billfish"
+logbook_fact = "sailfish raise their enormous dorsal fin while herding baitfish. they are among the fastest fish in the sea."
+available_seasons = 15
allowed_water_types = 2
rarity = 3
base_catch_weight = 0.1
diff --git a/fish/species/salmon_atlantic/fish_salmon_atlantic.png b/fish/species/salmon_atlantic/fish_salmon_atlantic.png
index ea6164b..a28192e 100644
Binary files a/fish/species/salmon_atlantic/fish_salmon_atlantic.png and b/fish/species/salmon_atlantic/fish_salmon_atlantic.png differ
diff --git a/fish/species/salmon_atlantic/salmon_atlantic.tres b/fish/species/salmon_atlantic/salmon_atlantic.tres
index fa3f0d6..1c77325 100644
--- a/fish/species/salmon_atlantic/salmon_atlantic.tres
+++ b/fish/species/salmon_atlantic/salmon_atlantic.tres
@@ -3,7 +3,7 @@
[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"]
+[ext_resource type="Texture2D" path="res://art/exported/creatures/4401.png" id="4_texture"]
[sub_resource type="Resource" id="AtlanticSalmonAvailability"]
script = ExtResource("3_availability_script")
@@ -14,6 +14,11 @@ preferred_bait_weight_multiplier = 1.4
script = ExtResource("1_fish_data")
id = &"salmon_atlantic"
display_name = "atlantic salmon"
+active = true
+catalog_number = 4401
+collection_group = &"Ocean-run salmon"
+logbook_fact = "atlantic salmon can return from the ocean to the river where they hatched. unlike many salmon, some survive to make the trip again."
+available_seasons = 5
allowed_water_types = 2
rarity = 3
base_catch_weight = 0.65
diff --git a/fish/species/salmon_chum/fish_salmon_chum.png b/fish/species/salmon_chum/fish_salmon_chum.png
index 920b39d..25a94eb 100644
Binary files a/fish/species/salmon_chum/fish_salmon_chum.png and b/fish/species/salmon_chum/fish_salmon_chum.png differ
diff --git a/fish/species/salmon_chum/salmon_chum.tres b/fish/species/salmon_chum/salmon_chum.tres
index 748295b..8fcabd0 100644
--- a/fish/species/salmon_chum/salmon_chum.tres
+++ b/fish/species/salmon_chum/salmon_chum.tres
@@ -3,7 +3,7 @@
[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"]
+[ext_resource type="Texture2D" path="res://art/exported/creatures/fish_placeholder.png" id="4_texture"]
[sub_resource type="Resource" id="ChumSalmonAvailability"]
script = ExtResource("3_availability_script")
@@ -14,6 +14,11 @@ preferred_bait_weight_multiplier = 1.4
script = ExtResource("1_fish_data")
id = &"salmon_chum"
display_name = "chum salmon"
+active = true
+catalog_number = 4402
+collection_group = &"Ocean-run salmon"
+logbook_fact = "chum salmon travel immense distances and develop striking bars when they return to fresh water to spawn."
+available_seasons = 4
allowed_water_types = 2
rarity = 1
base_catch_weight = 2.5
diff --git a/fish/species/salmon_coho/fish_salmon_coho.png b/fish/species/salmon_coho/fish_salmon_coho.png
index 2caf696..a7fe5f0 100644
Binary files a/fish/species/salmon_coho/fish_salmon_coho.png and b/fish/species/salmon_coho/fish_salmon_coho.png differ
diff --git a/fish/species/salmon_coho/salmon_coho.tres b/fish/species/salmon_coho/salmon_coho.tres
index 1c1a783..b1160b1 100644
--- a/fish/species/salmon_coho/salmon_coho.tres
+++ b/fish/species/salmon_coho/salmon_coho.tres
@@ -3,7 +3,7 @@
[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"]
+[ext_resource type="Texture2D" path="res://art/exported/creatures/fish_placeholder.png" id="4_texture"]
[sub_resource type="Resource" id="CohoSalmonAvailability"]
script = ExtResource("3_availability_script")
@@ -14,6 +14,11 @@ preferred_bait_weight_multiplier = 1.4
script = ExtResource("1_fish_data")
id = &"salmon_coho"
display_name = "coho salmon"
+active = true
+catalog_number = 4403
+collection_group = &"Ocean-run salmon"
+logbook_fact = "coho salmon are agile hunters known for powerful leaps. young coho may spend more than a year growing in streams."
+available_seasons = 12
allowed_water_types = 2
rarity = 2
base_catch_weight = 1.4
diff --git a/fish/species/salmon_pink/fish_salmon_pink.png b/fish/species/salmon_pink/fish_salmon_pink.png
index 85b1d88..1fe2554 100644
Binary files a/fish/species/salmon_pink/fish_salmon_pink.png and b/fish/species/salmon_pink/fish_salmon_pink.png differ
diff --git a/fish/species/salmon_pink/salmon_pink.tres b/fish/species/salmon_pink/salmon_pink.tres
index d390cf1..bdfa32e 100644
--- a/fish/species/salmon_pink/salmon_pink.tres
+++ b/fish/species/salmon_pink/salmon_pink.tres
@@ -3,7 +3,7 @@
[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"]
+[ext_resource type="Texture2D" path="res://art/exported/creatures/fish_placeholder.png" id="4_texture"]
[sub_resource type="Resource" id="PinkSalmonAvailability"]
script = ExtResource("3_availability_script")
@@ -14,6 +14,11 @@ preferred_bait_weight_multiplier = 1.4
script = ExtResource("1_fish_data")
id = &"salmon_pink"
display_name = "pink salmon"
+active = true
+catalog_number = 4404
+collection_group = &"Ocean-run salmon"
+logbook_fact = "pink salmon usually follow a strict two-year life cycle. spawning males develop the hump that inspired their nickname."
+available_seasons = 6
allowed_water_types = 2
rarity = 1
base_catch_weight = 3.0
diff --git a/fish/species/salmon_sockeye/fish_salmon_sockeye.png b/fish/species/salmon_sockeye/fish_salmon_sockeye.png
index bb98433..0569084 100644
Binary files a/fish/species/salmon_sockeye/fish_salmon_sockeye.png and b/fish/species/salmon_sockeye/fish_salmon_sockeye.png differ
diff --git a/fish/species/salmon_sockeye/salmon_sockeye.tres b/fish/species/salmon_sockeye/salmon_sockeye.tres
index deeda39..7bd4b1a 100644
--- a/fish/species/salmon_sockeye/salmon_sockeye.tres
+++ b/fish/species/salmon_sockeye/salmon_sockeye.tres
@@ -3,7 +3,7 @@
[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"]
+[ext_resource type="Texture2D" path="res://art/exported/creatures/fish_placeholder.png" id="4_texture"]
[sub_resource type="Resource" id="SockeyeSalmonAvailability"]
script = ExtResource("3_availability_script")
@@ -14,6 +14,11 @@ preferred_bait_weight_multiplier = 1.4
script = ExtResource("1_fish_data")
id = &"salmon_sockeye"
display_name = "sockeye salmon"
+active = true
+catalog_number = 4405
+collection_group = &"Ocean-run salmon"
+logbook_fact = "sockeye salmon feed heavily on plankton at sea. adults turn vivid red as they return inland to spawn."
+available_seasons = 6
allowed_water_types = 2
rarity = 2
base_catch_weight = 1.6
diff --git a/fish/species/sand_dollar/sand_dollar.tres b/fish/species/sand_dollar/sand_dollar.tres
new file mode 100644
index 0000000..f780203
--- /dev/null
+++ b/fish/species/sand_dollar/sand_dollar.tres
@@ -0,0 +1,32 @@
+[gd_resource type="Resource" script_class="FishData" load_steps=5 format=3]
+
+[ext_resource type="Script" 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" path="res://fish/fish_availability.gd" id="3_availability"]
+[ext_resource type="Texture2D" path="res://art/exported/creatures/fish_placeholder.png" id="99_fish_placeholder"]
+
+[sub_resource type="Resource" id="Availability"]
+script = ExtResource("3_availability")
+allow_night = false
+forbid_fog = true
+
+[resource]
+script = ExtResource("1_fish_data")
+id = &"sand_dollar"
+display_name = "sand dollar"
+active = true
+catalog_number = 1904
+collection_group = &"Echinoderms"
+logbook_fact = "sand dollars burrow just beneath sandy seafloors and filter tiny food particles. the familiar star pattern marks their five-part body plan."
+available_seasons = 15
+allowed_water_types = 2
+rarity = 1
+base_catch_weight = 0.8
+catch_profile = ExtResource("2_profile")
+availability = SubResource("Availability")
+weight_min_lb = 0.01
+weight_max_lb = 0.2
+sell_value_min = 4
+sell_value_max = 10
+sell_value_curve = 0.98
+display_texture = ExtResource("99_fish_placeholder")
diff --git a/fish/species/sand_lance_american/sand_lance_american.tres b/fish/species/sand_lance_american/sand_lance_american.tres
new file mode 100644
index 0000000..18f16be
--- /dev/null
+++ b/fish/species/sand_lance_american/sand_lance_american.tres
@@ -0,0 +1,31 @@
+[gd_resource type="Resource" script_class="FishData" load_steps=5 format=3]
+
+[ext_resource type="Script" 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" path="res://fish/fish_availability.gd" id="3_availability"]
+[ext_resource type="Texture2D" path="res://art/exported/creatures/801.png" id="99_fish_placeholder"]
+
+[sub_resource type="Resource" id="Availability"]
+script = ExtResource("3_availability")
+allow_night = false
+
+[resource]
+script = ExtResource("1_fish_data")
+id = &"sand_lance_american"
+display_name = "American sand lance"
+active = true
+catalog_number = 801
+collection_group = &"Coastal schooling fish"
+logbook_fact = "american sand lance dive headfirst into loose sand when danger approaches. immense schools carry energy from plankton to larger predators."
+available_seasons = 13
+allowed_water_types = 2
+rarity = 0
+base_catch_weight = 3.0
+catch_profile = ExtResource("2_profile")
+availability = SubResource("Availability")
+weight_min_lb = 0.02
+weight_max_lb = 0.4
+sell_value_min = 1
+sell_value_max = 3
+sell_value_curve = 1.0
+display_texture = ExtResource("99_fish_placeholder")
diff --git a/fish/species/sardine_european/sardine_european.tres b/fish/species/sardine_european/sardine_european.tres
new file mode 100644
index 0000000..54008f8
--- /dev/null
+++ b/fish/species/sardine_european/sardine_european.tres
@@ -0,0 +1,30 @@
+[gd_resource type="Resource" script_class="FishData" load_steps=5 format=3]
+
+[ext_resource type="Script" 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" path="res://fish/fish_availability.gd" id="3_availability"]
+[ext_resource type="Texture2D" path="res://art/exported/creatures/806.png" id="99_fish_placeholder"]
+
+[sub_resource type="Resource" id="Availability"]
+script = ExtResource("3_availability")
+
+[resource]
+script = ExtResource("1_fish_data")
+id = &"sardine_european"
+display_name = "European pilchard"
+active = true
+catalog_number = 806
+collection_group = &"Coastal schooling fish"
+logbook_fact = "european pilchards form shimmering schools along atlantic and mediterranean coasts. they feed mainly on plankton near the surface."
+available_seasons = 13
+allowed_water_types = 2
+rarity = 0
+base_catch_weight = 3.5
+catch_profile = ExtResource("2_profile")
+availability = SubResource("Availability")
+weight_min_lb = 0.05
+weight_max_lb = 0.6
+sell_value_min = 2
+sell_value_max = 4
+sell_value_curve = 1.0
+display_texture = ExtResource("99_fish_placeholder")
diff --git a/fish/species/sardine_pacific/sardine_pacific.tres b/fish/species/sardine_pacific/sardine_pacific.tres
new file mode 100644
index 0000000..bea772d
--- /dev/null
+++ b/fish/species/sardine_pacific/sardine_pacific.tres
@@ -0,0 +1,30 @@
+[gd_resource type="Resource" script_class="FishData" load_steps=5 format=3]
+
+[ext_resource type="Script" 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" path="res://fish/fish_availability.gd" id="3_availability"]
+[ext_resource type="Texture2D" path="res://art/exported/creatures/fish_placeholder.png" id="99_fish_placeholder"]
+
+[sub_resource type="Resource" id="Availability"]
+script = ExtResource("3_availability")
+
+[resource]
+script = ExtResource("1_fish_data")
+id = &"sardine_pacific"
+display_name = "Pacific sardine"
+active = true
+catalog_number = 809
+collection_group = &"Coastal schooling fish"
+logbook_fact = "pacific sardines follow cool, productive water in enormous schools. their movements shift as currents and plankton supplies change."
+available_seasons = 15
+allowed_water_types = 2
+rarity = 0
+base_catch_weight = 3.5
+catch_profile = ExtResource("2_profile")
+availability = SubResource("Availability")
+weight_min_lb = 0.05
+weight_max_lb = 0.5
+sell_value_min = 2
+sell_value_max = 4
+sell_value_curve = 1.0
+display_texture = ExtResource("99_fish_placeholder")
diff --git a/fish/species/sauger/fish_sauger.png b/fish/species/sauger/fish_sauger.png
index c4e2763..188be07 100644
Binary files a/fish/species/sauger/fish_sauger.png and b/fish/species/sauger/fish_sauger.png differ
diff --git a/fish/species/sauger/sauger.tres b/fish/species/sauger/sauger.tres
index 49cf5ad..3248bcb 100644
--- a/fish/species/sauger/sauger.tres
+++ b/fish/species/sauger/sauger.tres
@@ -3,16 +3,21 @@
[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"]
+[ext_resource type="Texture2D" path="res://art/exported/creatures/fish_placeholder.png" id="4_texture"]
[sub_resource type="Resource" id="SaugerAvailability"]
script = ExtResource("3_availability_script")
-allowed_location_tags = Array[StringName]([&"starter_pond"])
+allowed_location_tags = Array[StringName]([&"river"])
[resource]
script = ExtResource("1_fish_data")
id = &"sauger"
display_name = "sauger"
+active = true
+catalog_number = 3304
+collection_group = &"Freshwater predators"
+logbook_fact = "sauger favor turbid rivers and reservoirs. their mottled backs blend into the dim, shifting light near the bottom."
+available_seasons = 13
allowed_water_types = 1
rarity = 1
base_catch_weight = 0.35
diff --git a/fish/species/saugeye/fish_saugeye.png b/fish/species/saugeye/fish_saugeye.png
index 0674902..d106625 100644
Binary files a/fish/species/saugeye/fish_saugeye.png and b/fish/species/saugeye/fish_saugeye.png differ
diff --git a/fish/species/saugeye/saugeye.tres b/fish/species/saugeye/saugeye.tres
index e834005..d6f09cf 100644
--- a/fish/species/saugeye/saugeye.tres
+++ b/fish/species/saugeye/saugeye.tres
@@ -3,16 +3,21 @@
[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"]
+[ext_resource type="Texture2D" path="res://art/exported/creatures/fish_placeholder.png" id="4_texture"]
[sub_resource type="Resource" id="SaugeyeAvailability"]
script = ExtResource("3_availability_script")
-allowed_location_tags = Array[StringName]([&"starter_pond"])
+allowed_location_tags = Array[StringName]([&"lake"])
[resource]
script = ExtResource("1_fish_data")
id = &"saugeye"
display_name = "saugeye"
+active = true
+catalog_number = 3305
+collection_group = &"Freshwater predators"
+logbook_fact = "saugeye are a fertile hybrid of sauger and walleye. they combine traits from both parent species and often grow quickly."
+available_seasons = 13
allowed_water_types = 1
rarity = 2
base_catch_weight = 0.2
diff --git a/fish/species/sawfish_largetooth/sawfish_largetooth.tres b/fish/species/sawfish_largetooth/sawfish_largetooth.tres
new file mode 100644
index 0000000..ac4680b
--- /dev/null
+++ b/fish/species/sawfish_largetooth/sawfish_largetooth.tres
@@ -0,0 +1,32 @@
+[gd_resource type="Resource" script_class="FishData" load_steps=5 format=3]
+
+[ext_resource type="Script" 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" path="res://fish/fish_availability.gd" id="3_availability"]
+[ext_resource type="Texture2D" path="res://art/exported/creatures/fish_placeholder.png" id="99_fish_placeholder"]
+
+[sub_resource type="Resource" id="Availability"]
+script = ExtResource("3_availability")
+allow_day = false
+require_rain = true
+
+[resource]
+script = ExtResource("1_fish_data")
+id = &"sawfish_largetooth"
+display_name = "largetooth sawfish"
+active = true
+catalog_number = 5103
+collection_group = &"Rays and skates"
+logbook_fact = "largetooth sawfish detect hidden prey with a long sensory rostrum. a swift sideways sweep stuns fish and exposes animals buried in mud."
+available_seasons = 15
+allowed_water_types = 2
+rarity = 4
+base_catch_weight = 0.035
+catch_profile = ExtResource("2_profile")
+availability = SubResource("Availability")
+weight_min_lb = 25.0
+weight_max_lb = 1300.0
+sell_value_min = 58
+sell_value_max = 135
+sell_value_curve = 0.78
+display_texture = ExtResource("99_fish_placeholder")
diff --git a/fish/species/scorpionfish_red/scorpionfish_red.tres b/fish/species/scorpionfish_red/scorpionfish_red.tres
new file mode 100644
index 0000000..baf9046
--- /dev/null
+++ b/fish/species/scorpionfish_red/scorpionfish_red.tres
@@ -0,0 +1,31 @@
+[gd_resource type="Resource" script_class="FishData" load_steps=5 format=3]
+
+[ext_resource type="Script" 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" path="res://fish/fish_availability.gd" id="3_availability"]
+[ext_resource type="Texture2D" path="res://art/exported/creatures/fish_placeholder.png" id="99_fish_placeholder"]
+
+[sub_resource type="Resource" id="Availability"]
+script = ExtResource("3_availability")
+allow_day = false
+
+[resource]
+script = ExtResource("1_fish_data")
+id = &"scorpionfish_red"
+display_name = "red scorpionfish"
+active = true
+catalog_number = 5202
+collection_group = &"Reef ambush fish"
+logbook_fact = "red scorpionfish vanish against rocky reefs through mottled color and skin flaps. venomous dorsal spines punish careless predators."
+available_seasons = 15
+allowed_water_types = 2
+rarity = 2
+base_catch_weight = 0.3
+catch_profile = ExtResource("2_profile")
+availability = SubResource("Availability")
+weight_min_lb = 0.2
+weight_max_lb = 7.0
+sell_value_min = 11
+sell_value_max = 25
+sell_value_curve = 0.9
+display_texture = ExtResource("99_fish_placeholder")
diff --git a/fish/species/sea_bass_black/sea_bass_black.tres b/fish/species/sea_bass_black/sea_bass_black.tres
new file mode 100644
index 0000000..7a5b567
--- /dev/null
+++ b/fish/species/sea_bass_black/sea_bass_black.tres
@@ -0,0 +1,31 @@
+[gd_resource type="Resource" script_class="FishData" load_steps=5 format=3]
+
+[ext_resource type="Script" 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" path="res://fish/fish_availability.gd" id="3_availability"]
+[ext_resource type="Texture2D" path="res://art/exported/creatures/6902.png" id="99_fish_placeholder"]
+
+[sub_resource type="Resource" id="Availability"]
+script = ExtResource("3_availability")
+allow_night = false
+
+[resource]
+script = ExtResource("1_fish_data")
+id = &"sea_bass_black"
+display_name = "black sea bass"
+active = true
+catalog_number = 6902
+collection_group = &"Temperate reef fish"
+logbook_fact = "black sea bass shelter around reefs, piers, and wrecks. many begin life as female and later become male."
+available_seasons = 13
+allowed_water_types = 2
+rarity = 0
+base_catch_weight = 1.7
+catch_profile = ExtResource("2_profile")
+availability = SubResource("Availability")
+weight_min_lb = 0.3
+weight_max_lb = 8.0
+sell_value_min = 4
+sell_value_max = 10
+sell_value_curve = 0.9
+display_texture = ExtResource("99_fish_placeholder")
diff --git a/fish/species/sea_bass_chilean/sea_bass_chilean.tres b/fish/species/sea_bass_chilean/sea_bass_chilean.tres
new file mode 100644
index 0000000..dd9afac
--- /dev/null
+++ b/fish/species/sea_bass_chilean/sea_bass_chilean.tres
@@ -0,0 +1,31 @@
+[gd_resource type="Resource" script_class="FishData" load_steps=5 format=3]
+
+[ext_resource type="Script" 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" path="res://fish/fish_availability.gd" id="3_availability"]
+[ext_resource type="Texture2D" path="res://art/exported/creatures/fish_placeholder.png" id="99_fish_placeholder"]
+
+[sub_resource type="Resource" id="Availability"]
+script = ExtResource("3_availability")
+allow_day = false
+
+[resource]
+script = ExtResource("1_fish_data")
+id = &"sea_bass_chilean"
+display_name = "Patagonian toothfish"
+active = true
+catalog_number = 1604
+collection_group = &"Deep shelf fish"
+logbook_fact = "patagonian toothfish patrol cold, deep southern water with large canine teeth. natural antifreeze proteins protect their blood from freezing."
+available_seasons = 15
+allowed_water_types = 2
+rarity = 3
+base_catch_weight = 0.09
+catch_profile = ExtResource("2_profile")
+availability = SubResource("Availability")
+weight_min_lb = 10.0
+weight_max_lb = 220.0
+sell_value_min = 32
+sell_value_max = 78
+sell_value_curve = 0.8
+display_texture = ExtResource("99_fish_placeholder")
diff --git a/fish/species/sea_cucumber_giant_california/sea_cucumber_giant_california.tres b/fish/species/sea_cucumber_giant_california/sea_cucumber_giant_california.tres
new file mode 100644
index 0000000..bc1ca41
--- /dev/null
+++ b/fish/species/sea_cucumber_giant_california/sea_cucumber_giant_california.tres
@@ -0,0 +1,32 @@
+[gd_resource type="Resource" script_class="FishData" load_steps=5 format=3]
+
+[ext_resource type="Script" 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" path="res://fish/fish_availability.gd" id="3_availability"]
+[ext_resource type="Texture2D" path="res://art/exported/creatures/fish_placeholder.png" id="99_fish_placeholder"]
+
+[sub_resource type="Resource" id="Availability"]
+script = ExtResource("3_availability")
+allow_day = false
+require_fog = true
+
+[resource]
+script = ExtResource("1_fish_data")
+id = &"sea_cucumber_giant_california"
+display_name = "giant California sea cucumber"
+active = true
+catalog_number = 1902
+collection_group = &"Echinoderms"
+logbook_fact = "giant california sea cucumbers sweep organic particles from the seafloor with branching tentacles. soft tube feet anchor their long bodies."
+available_seasons = 15
+allowed_water_types = 2
+rarity = 2
+base_catch_weight = 0.24
+catch_profile = ExtResource("2_profile")
+availability = SubResource("Availability")
+weight_min_lb = 0.5
+weight_max_lb = 5.0
+sell_value_min = 13
+sell_value_max = 29
+sell_value_curve = 0.93
+display_texture = ExtResource("99_fish_placeholder")
diff --git a/fish/species/sea_otter/sea_otter.tres b/fish/species/sea_otter/sea_otter.tres
new file mode 100644
index 0000000..c5f4bfc
--- /dev/null
+++ b/fish/species/sea_otter/sea_otter.tres
@@ -0,0 +1,31 @@
+[gd_resource type="Resource" script_class="FishData" load_steps=5 format=3]
+
+[ext_resource type="Script" 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" path="res://fish/fish_availability.gd" id="3_availability"]
+[ext_resource type="Texture2D" path="res://art/exported/creatures/fish_placeholder.png" id="99_fish_placeholder"]
+
+[sub_resource type="Resource" id="Availability"]
+script = ExtResource("3_availability")
+allow_night = false
+
+[resource]
+script = ExtResource("1_fish_data")
+id = &"sea_otter"
+display_name = "sea otter"
+active = true
+catalog_number = 503
+collection_group = &"Coastal marine mammals"
+logbook_fact = "sea otters float on their backs while opening shellfish with stones. dense fur traps insulating air because they lack a thick blubber layer."
+available_seasons = 15
+allowed_water_types = 2
+rarity = 4
+base_catch_weight = 0.02
+catch_profile = ExtResource("2_profile")
+availability = SubResource("Availability")
+weight_min_lb = 30.0
+weight_max_lb = 100.0
+sell_value_min = 55
+sell_value_max = 125
+sell_value_curve = 0.84
+display_texture = ExtResource("99_fish_placeholder")
diff --git a/fish/species/sea_star_crown_of_thorns/sea_star_crown_of_thorns.tres b/fish/species/sea_star_crown_of_thorns/sea_star_crown_of_thorns.tres
new file mode 100644
index 0000000..5e73beb
--- /dev/null
+++ b/fish/species/sea_star_crown_of_thorns/sea_star_crown_of_thorns.tres
@@ -0,0 +1,32 @@
+[gd_resource type="Resource" script_class="FishData" load_steps=5 format=3]
+
+[ext_resource type="Script" 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" path="res://fish/fish_availability.gd" id="3_availability"]
+[ext_resource type="Texture2D" path="res://art/exported/creatures/fish_placeholder.png" id="99_fish_placeholder"]
+
+[sub_resource type="Resource" id="Availability"]
+script = ExtResource("3_availability")
+allow_day = false
+require_rain = true
+
+[resource]
+script = ExtResource("1_fish_data")
+id = &"sea_star_crown_of_thorns"
+display_name = "crown-of-thorns sea star"
+active = true
+catalog_number = 1901
+collection_group = &"Echinoderms"
+logbook_fact = "crown-of-thorns sea stars crawl over reefs and digest coral tissue outside the body. venomous spines discourage many predators."
+available_seasons = 15
+allowed_water_types = 2
+rarity = 3
+base_catch_weight = 0.1
+catch_profile = ExtResource("2_profile")
+availability = SubResource("Availability")
+weight_min_lb = 0.5
+weight_max_lb = 7.0
+sell_value_min = 23
+sell_value_max = 52
+sell_value_curve = 0.91
+display_texture = ExtResource("99_fish_placeholder")
diff --git a/fish/species/sea_star_sunflower/sea_star_sunflower.tres b/fish/species/sea_star_sunflower/sea_star_sunflower.tres
new file mode 100644
index 0000000..6bf1dc2
--- /dev/null
+++ b/fish/species/sea_star_sunflower/sea_star_sunflower.tres
@@ -0,0 +1,32 @@
+[gd_resource type="Resource" script_class="FishData" load_steps=5 format=3]
+
+[ext_resource type="Script" 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" path="res://fish/fish_availability.gd" id="3_availability"]
+[ext_resource type="Texture2D" path="res://art/exported/creatures/fish_placeholder.png" id="99_fish_placeholder"]
+
+[sub_resource type="Resource" id="Availability"]
+script = ExtResource("3_availability")
+allow_night = false
+require_fog = true
+
+[resource]
+script = ExtResource("1_fish_data")
+id = &"sea_star_sunflower"
+display_name = "sunflower sea star"
+active = true
+catalog_number = 1905
+collection_group = &"Echinoderms"
+logbook_fact = "sunflower sea stars move quickly on thousands of tube feet and may carry many arms. they hunt urchins, clams, and other bottom animals."
+available_seasons = 15
+allowed_water_types = 2
+rarity = 2
+base_catch_weight = 0.25
+catch_profile = ExtResource("2_profile")
+availability = SubResource("Availability")
+weight_min_lb = 0.5
+weight_max_lb = 13.0
+sell_value_min = 14
+sell_value_max = 32
+sell_value_curve = 0.89
+display_texture = ExtResource("99_fish_placeholder")
diff --git a/fish/species/seadragon_leafy/seadragon_leafy.tres b/fish/species/seadragon_leafy/seadragon_leafy.tres
new file mode 100644
index 0000000..1481d73
--- /dev/null
+++ b/fish/species/seadragon_leafy/seadragon_leafy.tres
@@ -0,0 +1,32 @@
+[gd_resource type="Resource" script_class="FishData" load_steps=5 format=3]
+
+[ext_resource type="Script" 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" path="res://fish/fish_availability.gd" id="3_availability"]
+[ext_resource type="Texture2D" path="res://art/exported/creatures/fish_placeholder.png" id="99_fish_placeholder"]
+
+[sub_resource type="Resource" id="Availability"]
+script = ExtResource("3_availability")
+allow_night = false
+forbid_fog = true
+
+[resource]
+script = ExtResource("1_fish_data")
+id = &"seadragon_leafy"
+display_name = "leafy seadragon"
+active = true
+catalog_number = 6301
+collection_group = &"Seahorses and seadragons"
+logbook_fact = "leafy seadragons drift among seaweed with elaborate skin lobes that resemble leaves. males carry eggs beneath the tail."
+available_seasons = 15
+allowed_water_types = 2
+rarity = 3
+base_catch_weight = 0.1
+catch_profile = ExtResource("2_profile")
+availability = SubResource("Availability")
+weight_min_lb = 0.1
+weight_max_lb = 0.5
+sell_value_min = 24
+sell_value_max = 55
+sell_value_curve = 0.94
+display_texture = ExtResource("99_fish_placeholder")
diff --git a/fish/species/seadragon_weedy/seadragon_weedy.tres b/fish/species/seadragon_weedy/seadragon_weedy.tres
new file mode 100644
index 0000000..bc1768b
--- /dev/null
+++ b/fish/species/seadragon_weedy/seadragon_weedy.tres
@@ -0,0 +1,32 @@
+[gd_resource type="Resource" script_class="FishData" load_steps=5 format=3]
+
+[ext_resource type="Script" 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" path="res://fish/fish_availability.gd" id="3_availability"]
+[ext_resource type="Texture2D" path="res://art/exported/creatures/fish_placeholder.png" id="99_fish_placeholder"]
+
+[sub_resource type="Resource" id="Availability"]
+script = ExtResource("3_availability")
+allow_night = false
+require_fog = true
+
+[resource]
+script = ExtResource("1_fish_data")
+id = &"seadragon_weedy"
+display_name = "weedy seadragon"
+active = true
+catalog_number = 6302
+collection_group = &"Seahorses and seadragons"
+logbook_fact = "weedy seadragons rely on slender bodies and small leaf-like lobes for camouflage. males brood rows of eggs on the underside of the tail."
+available_seasons = 15
+allowed_water_types = 2
+rarity = 2
+base_catch_weight = 0.22
+catch_profile = ExtResource("2_profile")
+availability = SubResource("Availability")
+weight_min_lb = 0.1
+weight_max_lb = 0.6
+sell_value_min = 16
+sell_value_max = 38
+sell_value_curve = 0.93
+display_texture = ExtResource("99_fish_placeholder")
diff --git a/fish/species/seahorse_lined/seahorse_lined.tres b/fish/species/seahorse_lined/seahorse_lined.tres
new file mode 100644
index 0000000..0121fab
--- /dev/null
+++ b/fish/species/seahorse_lined/seahorse_lined.tres
@@ -0,0 +1,32 @@
+[gd_resource type="Resource" script_class="FishData" load_steps=5 format=3]
+
+[ext_resource type="Script" 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" path="res://fish/fish_availability.gd" id="3_availability"]
+[ext_resource type="Texture2D" path="res://art/exported/creatures/fish_placeholder.png" id="99_fish_placeholder"]
+
+[sub_resource type="Resource" id="Availability"]
+script = ExtResource("3_availability")
+allow_night = false
+forbid_rain = true
+
+[resource]
+script = ExtResource("1_fish_data")
+id = &"seahorse_lined"
+display_name = "lined seahorse"
+active = true
+catalog_number = 602
+collection_group = &"Coastal oddities"
+logbook_fact = "lined seahorses anchor to grass and coral with curling tails. the male broods eggs in a pouch and releases fully formed young."
+available_seasons = 15
+allowed_water_types = 2
+rarity = 2
+base_catch_weight = 0.28
+catch_profile = ExtResource("2_profile")
+availability = SubResource("Availability")
+weight_min_lb = 0.005
+weight_max_lb = 0.05
+sell_value_min = 9
+sell_value_max = 20
+sell_value_curve = 1.0
+display_texture = ExtResource("99_fish_placeholder")
diff --git a/fish/species/seal_harbor/seal_harbor.tres b/fish/species/seal_harbor/seal_harbor.tres
new file mode 100644
index 0000000..5689a14
--- /dev/null
+++ b/fish/species/seal_harbor/seal_harbor.tres
@@ -0,0 +1,32 @@
+[gd_resource type="Resource" script_class="FishData" load_steps=5 format=3]
+
+[ext_resource type="Script" 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" path="res://fish/fish_availability.gd" id="3_availability"]
+[ext_resource type="Texture2D" path="res://art/exported/creatures/fish_placeholder.png" id="99_fish_placeholder"]
+
+[sub_resource type="Resource" id="Availability"]
+script = ExtResource("3_availability")
+allow_night = false
+require_fog = true
+
+[resource]
+script = ExtResource("1_fish_data")
+id = &"seal_harbor"
+display_name = "harbor seal"
+active = true
+catalog_number = 502
+collection_group = &"Coastal marine mammals"
+logbook_fact = "harbor seals rest on rocky shores and sandbars between hunting trips. sensitive whiskers can trace the water trails left by moving fish."
+available_seasons = 15
+allowed_water_types = 2
+rarity = 4
+base_catch_weight = 0.018
+catch_profile = ExtResource("2_profile")
+availability = SubResource("Availability")
+weight_min_lb = 120.0
+weight_max_lb = 370.0
+sell_value_min = 60
+sell_value_max = 140
+sell_value_curve = 0.79
+display_texture = ExtResource("99_fish_placeholder")
diff --git a/fish/species/shark_blacktip/shark_blacktip.tres b/fish/species/shark_blacktip/shark_blacktip.tres
new file mode 100644
index 0000000..e7fc4e6
--- /dev/null
+++ b/fish/species/shark_blacktip/shark_blacktip.tres
@@ -0,0 +1,32 @@
+[gd_resource type="Resource" script_class="FishData" load_steps=5 format=3]
+
+[ext_resource type="Script" 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" path="res://fish/fish_availability.gd" id="3_availability"]
+[ext_resource type="Texture2D" path="res://art/exported/creatures/fish_placeholder.png" id="99_fish_placeholder"]
+
+[sub_resource type="Resource" id="Availability"]
+script = ExtResource("3_availability")
+allow_night = false
+require_rain = true
+
+[resource]
+script = ExtResource("1_fish_data")
+id = &"shark_blacktip"
+display_name = "blacktip shark"
+active = true
+catalog_number = 901
+collection_group = &"Coastal sharks"
+logbook_fact = "blacktip sharks are named for the dark markings on their fins. they hunt schooling fish in warm shallows and sometimes leap clear of the water."
+available_seasons = 6
+allowed_water_types = 2
+rarity = 3
+base_catch_weight = 0.1
+catch_profile = ExtResource("2_profile")
+availability = SubResource("Availability")
+weight_min_lb = 15.0
+weight_max_lb = 150.0
+sell_value_min = 32
+sell_value_max = 75
+sell_value_curve = 0.8
+display_texture = ExtResource("99_fish_placeholder")
diff --git a/fish/species/shark_great_hammerhead/shark_great_hammerhead.tres b/fish/species/shark_great_hammerhead/shark_great_hammerhead.tres
new file mode 100644
index 0000000..a9b612e
--- /dev/null
+++ b/fish/species/shark_great_hammerhead/shark_great_hammerhead.tres
@@ -0,0 +1,32 @@
+[gd_resource type="Resource" script_class="FishData" load_steps=5 format=3]
+
+[ext_resource type="Script" 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" path="res://fish/fish_availability.gd" id="3_availability"]
+[ext_resource type="Texture2D" path="res://art/exported/creatures/fish_placeholder.png" id="99_fish_placeholder"]
+
+[sub_resource type="Resource" id="Availability"]
+script = ExtResource("3_availability")
+allow_night = false
+forbid_fog = true
+
+[resource]
+script = ExtResource("1_fish_data")
+id = &"shark_great_hammerhead"
+display_name = "great hammerhead shark"
+active = true
+catalog_number = 902
+collection_group = &"Coastal sharks"
+logbook_fact = "great hammerheads sweep their wide heads over the bottom while searching for rays. widely spaced senses help pinpoint hidden prey."
+available_seasons = 6
+allowed_water_types = 2
+rarity = 4
+base_catch_weight = 0.035
+catch_profile = ExtResource("2_profile")
+availability = SubResource("Availability")
+weight_min_lb = 50.0
+weight_max_lb = 1000.0
+sell_value_min = 62
+sell_value_max = 145
+sell_value_curve = 0.78
+display_texture = ExtResource("99_fish_placeholder")
diff --git a/fish/species/shark_great_white/shark_great_white.tres b/fish/species/shark_great_white/shark_great_white.tres
new file mode 100644
index 0000000..afd1cd0
--- /dev/null
+++ b/fish/species/shark_great_white/shark_great_white.tres
@@ -0,0 +1,31 @@
+[gd_resource type="Resource" script_class="FishData" load_steps=5 format=3]
+
+[ext_resource type="Script" 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" path="res://fish/fish_availability.gd" id="3_availability"]
+[ext_resource type="Texture2D" path="res://art/exported/creatures/fish_placeholder.png" id="99_fish_placeholder"]
+
+[sub_resource type="Resource" id="Availability"]
+script = ExtResource("3_availability")
+allow_night = false
+
+[resource]
+script = ExtResource("1_fish_data")
+id = &"shark_great_white"
+display_name = "great white shark"
+active = true
+catalog_number = 903
+collection_group = &"Coastal sharks"
+logbook_fact = "great white sharks conserve heat in their swimming muscles while crossing cool seas. rows of serrated teeth are replaced throughout life."
+available_seasons = 15
+allowed_water_types = 2
+rarity = 4
+base_catch_weight = 0.025
+catch_profile = ExtResource("2_profile")
+availability = SubResource("Availability")
+weight_min_lb = 100.0
+weight_max_lb = 1000.0
+sell_value_min = 70
+sell_value_max = 150
+sell_value_curve = 0.76
+display_texture = ExtResource("99_fish_placeholder")
diff --git a/fish/species/shark_tiger/shark_tiger.tres b/fish/species/shark_tiger/shark_tiger.tres
new file mode 100644
index 0000000..72e2577
--- /dev/null
+++ b/fish/species/shark_tiger/shark_tiger.tres
@@ -0,0 +1,32 @@
+[gd_resource type="Resource" script_class="FishData" load_steps=5 format=3]
+
+[ext_resource type="Script" 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" path="res://fish/fish_availability.gd" id="3_availability"]
+[ext_resource type="Texture2D" path="res://art/exported/creatures/fish_placeholder.png" id="99_fish_placeholder"]
+
+[sub_resource type="Resource" id="Availability"]
+script = ExtResource("3_availability")
+allow_day = false
+require_rain = true
+
+[resource]
+script = ExtResource("1_fish_data")
+id = &"shark_tiger"
+display_name = "tiger shark"
+active = true
+catalog_number = 904
+collection_group = &"Coastal sharks"
+logbook_fact = "tiger sharks carry dark vertical bars that fade as they mature. broad jaws let them investigate an unusually varied range of prey."
+available_seasons = 15
+allowed_water_types = 2
+rarity = 3
+base_catch_weight = 0.06
+catch_profile = ExtResource("2_profile")
+availability = SubResource("Availability")
+weight_min_lb = 50.0
+weight_max_lb = 1000.0
+sell_value_min = 50
+sell_value_max = 120
+sell_value_curve = 0.8
+display_texture = ExtResource("99_fish_placeholder")
diff --git a/fish/species/shark_whale/shark_whale.tres b/fish/species/shark_whale/shark_whale.tres
new file mode 100644
index 0000000..81739db
--- /dev/null
+++ b/fish/species/shark_whale/shark_whale.tres
@@ -0,0 +1,32 @@
+[gd_resource type="Resource" script_class="FishData" load_steps=5 format=3]
+
+[ext_resource type="Script" 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" path="res://fish/fish_availability.gd" id="3_availability"]
+[ext_resource type="Texture2D" path="res://art/exported/creatures/fish_placeholder.png" id="99_fish_placeholder"]
+
+[sub_resource type="Resource" id="Availability"]
+script = ExtResource("3_availability")
+allow_night = false
+forbid_fog = true
+
+[resource]
+script = ExtResource("1_fish_data")
+id = &"shark_whale"
+display_name = "whale shark"
+active = true
+catalog_number = 905
+collection_group = &"Coastal sharks"
+logbook_fact = "whale sharks are enormous filter feeders covered in unique spot patterns. they cruise with open mouths to collect plankton and tiny fish."
+available_seasons = 6
+allowed_water_types = 2
+rarity = 4
+base_catch_weight = 0.025
+catch_profile = ExtResource("2_profile")
+availability = SubResource("Availability")
+weight_min_lb = 500.0
+weight_max_lb = 45000.0
+sell_value_min = 70
+sell_value_max = 160
+sell_value_curve = 0.76
+display_texture = ExtResource("99_fish_placeholder")
diff --git a/fish/species/sheepshead/sheepshead.tres b/fish/species/sheepshead/sheepshead.tres
new file mode 100644
index 0000000..d4b8cfa
--- /dev/null
+++ b/fish/species/sheepshead/sheepshead.tres
@@ -0,0 +1,31 @@
+[gd_resource type="Resource" script_class="FishData" load_steps=5 format=3]
+
+[ext_resource type="Script" 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" path="res://fish/fish_availability.gd" id="3_availability"]
+[ext_resource type="Texture2D" path="res://art/exported/creatures/6905.png" id="99_fish_placeholder"]
+
+[sub_resource type="Resource" id="Availability"]
+script = ExtResource("3_availability")
+allow_night = false
+
+[resource]
+script = ExtResource("1_fish_data")
+id = &"sheepshead"
+display_name = "sheepshead"
+active = true
+catalog_number = 6905
+collection_group = &"Temperate reef fish"
+logbook_fact = "sheepshead carry flat human-like front teeth for biting barnacles and crabs. dark bars cross their high silver bodies."
+available_seasons = 13
+allowed_water_types = 2
+rarity = 1
+base_catch_weight = 0.75
+catch_profile = ExtResource("2_profile")
+availability = SubResource("Availability")
+weight_min_lb = 0.5
+weight_max_lb = 20.0
+sell_value_min = 7
+sell_value_max = 18
+sell_value_curve = 0.86
+display_texture = ExtResource("99_fish_placeholder")
diff --git a/fish/species/shiner_common/shiner_common.tres b/fish/species/shiner_common/shiner_common.tres
new file mode 100644
index 0000000..5a8a2f3
--- /dev/null
+++ b/fish/species/shiner_common/shiner_common.tres
@@ -0,0 +1,31 @@
+[gd_resource type="Resource" script_class="FishData" load_steps=5 format=3]
+
+[ext_resource type="Script" 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" path="res://fish/fish_availability.gd" id="3_availability"]
+[ext_resource type="Texture2D" path="res://art/exported/creatures/2801.png" id="99_fish_placeholder"]
+
+[sub_resource type="Resource" id="Availability"]
+script = ExtResource("3_availability")
+allow_night = false
+
+[resource]
+script = ExtResource("1_fish_data")
+id = &"shiner_common"
+display_name = "common shiner"
+active = true
+catalog_number = 2801
+collection_group = &"Freshwater minnows"
+logbook_fact = "common shiners flash silver in clear streams and small rivers. breeding males develop rosy fins and a blue-violet sheen."
+available_seasons = 7
+allowed_water_types = 1
+rarity = 0
+base_catch_weight = 4.0
+catch_profile = ExtResource("2_profile")
+availability = SubResource("Availability")
+weight_min_lb = 0.03
+weight_max_lb = 0.3
+sell_value_min = 1
+sell_value_max = 3
+sell_value_curve = 1.0
+display_texture = ExtResource("99_fish_placeholder")
diff --git a/fish/species/shiner_emerald/shiner_emerald.tres b/fish/species/shiner_emerald/shiner_emerald.tres
new file mode 100644
index 0000000..8a39e5c
--- /dev/null
+++ b/fish/species/shiner_emerald/shiner_emerald.tres
@@ -0,0 +1,30 @@
+[gd_resource type="Resource" script_class="FishData" load_steps=5 format=3]
+
+[ext_resource type="Script" 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" path="res://fish/fish_availability.gd" id="3_availability"]
+[ext_resource type="Texture2D" path="res://art/exported/creatures/2803.png" id="99_fish_placeholder"]
+
+[sub_resource type="Resource" id="Availability"]
+script = ExtResource("3_availability")
+
+[resource]
+script = ExtResource("1_fish_data")
+id = &"shiner_emerald"
+display_name = "emerald shiner"
+active = true
+catalog_number = 2803
+collection_group = &"Freshwater minnows"
+logbook_fact = "emerald shiners form bright surface schools in large lakes and rivers. slender bodies feed on plankton and drifting insects."
+available_seasons = 7
+allowed_water_types = 1
+rarity = 0
+base_catch_weight = 3.5
+catch_profile = ExtResource("2_profile")
+availability = SubResource("Availability")
+weight_min_lb = 0.03
+weight_max_lb = 0.25
+sell_value_min = 1
+sell_value_max = 3
+sell_value_curve = 1.0
+display_texture = ExtResource("99_fish_placeholder")
diff --git a/fish/species/shiner_golden/shiner_golden.tres b/fish/species/shiner_golden/shiner_golden.tres
new file mode 100644
index 0000000..af57441
--- /dev/null
+++ b/fish/species/shiner_golden/shiner_golden.tres
@@ -0,0 +1,31 @@
+[gd_resource type="Resource" script_class="FishData" load_steps=5 format=3]
+
+[ext_resource type="Script" 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" path="res://fish/fish_availability.gd" id="3_availability"]
+[ext_resource type="Texture2D" path="res://art/exported/creatures/2806.png" id="99_fish_placeholder"]
+
+[sub_resource type="Resource" id="Availability"]
+script = ExtResource("3_availability")
+allow_night = false
+
+[resource]
+script = ExtResource("1_fish_data")
+id = &"shiner_golden"
+display_name = "golden shiner"
+active = true
+catalog_number = 2806
+collection_group = &"Freshwater minnows"
+logbook_fact = "golden shiners school around dense vegetation in warm water. an upturned mouth takes insects and plankton from near the surface."
+available_seasons = 7
+allowed_water_types = 1
+rarity = 0
+base_catch_weight = 3.8
+catch_profile = ExtResource("2_profile")
+availability = SubResource("Availability")
+weight_min_lb = 0.05
+weight_max_lb = 1.0
+sell_value_min = 2
+sell_value_max = 4
+sell_value_curve = 1.0
+display_texture = ExtResource("99_fish_placeholder")
diff --git a/fish/species/shrimp_peacock_mantis/shrimp_peacock_mantis.tres b/fish/species/shrimp_peacock_mantis/shrimp_peacock_mantis.tres
new file mode 100644
index 0000000..daf7662
--- /dev/null
+++ b/fish/species/shrimp_peacock_mantis/shrimp_peacock_mantis.tres
@@ -0,0 +1,32 @@
+[gd_resource type="Resource" script_class="FishData" load_steps=5 format=3]
+
+[ext_resource type="Script" 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" path="res://fish/fish_availability.gd" id="3_availability"]
+[ext_resource type="Texture2D" path="res://art/exported/creatures/fish_placeholder.png" id="99_fish_placeholder"]
+
+[sub_resource type="Resource" id="Availability"]
+script = ExtResource("3_availability")
+allow_night = false
+require_rain = true
+
+[resource]
+script = ExtResource("1_fish_data")
+id = &"shrimp_peacock_mantis"
+display_name = "peacock mantis shrimp"
+active = true
+catalog_number = 3804
+collection_group = &"Lobsters and shrimp"
+logbook_fact = "peacock mantis shrimp strike hard-shelled prey with club-like limbs. complex eyes move independently and detect colors beyond human vision."
+available_seasons = 15
+allowed_water_types = 2
+rarity = 3
+base_catch_weight = 0.1
+catch_profile = ExtResource("2_profile")
+availability = SubResource("Availability")
+weight_min_lb = 0.05
+weight_max_lb = 0.4
+sell_value_min = 22
+sell_value_max = 50
+sell_value_curve = 0.96
+display_texture = ExtResource("99_fish_placeholder")
diff --git a/fish/species/shrimp_skunk_cleaner/shrimp_skunk_cleaner.tres b/fish/species/shrimp_skunk_cleaner/shrimp_skunk_cleaner.tres
new file mode 100644
index 0000000..fdd6730
--- /dev/null
+++ b/fish/species/shrimp_skunk_cleaner/shrimp_skunk_cleaner.tres
@@ -0,0 +1,32 @@
+[gd_resource type="Resource" script_class="FishData" load_steps=5 format=3]
+
+[ext_resource type="Script" 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" path="res://fish/fish_availability.gd" id="3_availability"]
+[ext_resource type="Texture2D" path="res://art/exported/creatures/fish_placeholder.png" id="99_fish_placeholder"]
+
+[sub_resource type="Resource" id="Availability"]
+script = ExtResource("3_availability")
+allow_night = false
+forbid_fog = true
+
+[resource]
+script = ExtResource("1_fish_data")
+id = &"shrimp_skunk_cleaner"
+display_name = "skunk cleaner shrimp"
+active = true
+catalog_number = 3805
+collection_group = &"Lobsters and shrimp"
+logbook_fact = "skunk cleaner shrimp advertise at reef cleaning stations with bright white antennae. visiting fish pause while parasites are picked away."
+available_seasons = 15
+allowed_water_types = 2
+rarity = 1
+base_catch_weight = 0.75
+catch_profile = ExtResource("2_profile")
+availability = SubResource("Availability")
+weight_min_lb = 0.01
+weight_max_lb = 0.15
+sell_value_min = 5
+sell_value_max = 11
+sell_value_curve = 0.98
+display_texture = ExtResource("99_fish_placeholder")
diff --git a/fish/species/skate_barndoor/skate_barndoor.tres b/fish/species/skate_barndoor/skate_barndoor.tres
new file mode 100644
index 0000000..5ccb967
--- /dev/null
+++ b/fish/species/skate_barndoor/skate_barndoor.tres
@@ -0,0 +1,31 @@
+[gd_resource type="Resource" script_class="FishData" load_steps=5 format=3]
+
+[ext_resource type="Script" 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" path="res://fish/fish_availability.gd" id="3_availability"]
+[ext_resource type="Texture2D" path="res://art/exported/creatures/fish_placeholder.png" id="99_fish_placeholder"]
+
+[sub_resource type="Resource" id="Availability"]
+script = ExtResource("3_availability")
+require_fog = true
+
+[resource]
+script = ExtResource("1_fish_data")
+id = &"skate_barndoor"
+display_name = "barndoor skate"
+active = true
+catalog_number = 5101
+collection_group = &"Rays and skates"
+logbook_fact = "barndoor skates glide over cold atlantic bottoms with broad wing-like fins. rows of thorny spines protect the back and tail."
+available_seasons = 13
+allowed_water_types = 2
+rarity = 3
+base_catch_weight = 0.1
+catch_profile = ExtResource("2_profile")
+availability = SubResource("Availability")
+weight_min_lb = 5.0
+weight_max_lb = 150.0
+sell_value_min = 28
+sell_value_max = 68
+sell_value_curve = 0.8
+display_texture = ExtResource("99_fish_placeholder")
diff --git a/fish/species/smallmouth_bass/smallmouth_bass.tres b/fish/species/smallmouth_bass/smallmouth_bass.tres
new file mode 100644
index 0000000..d840f32
--- /dev/null
+++ b/fish/species/smallmouth_bass/smallmouth_bass.tres
@@ -0,0 +1,31 @@
+[gd_resource type="Resource" script_class="FishData" load_steps=5 format=3]
+
+[ext_resource type="Script" 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" path="res://fish/fish_availability.gd" id="3_availability"]
+[ext_resource type="Texture2D" path="res://art/exported/creatures/fish_placeholder.png" id="99_fish_placeholder"]
+
+[sub_resource type="Resource" id="Availability"]
+script = ExtResource("3_availability")
+allow_night = false
+
+[resource]
+script = ExtResource("1_fish_data")
+id = &"smallmouth_bass"
+display_name = "smallmouth bass"
+active = true
+catalog_number = 2202
+collection_group = &"Freshwater bass"
+logbook_fact = "smallmouth bass favor clear water with rock and moving current. bronze flanks and dark vertical bars distinguish them from largemouth bass."
+available_seasons = 7
+allowed_water_types = 1
+rarity = 1
+base_catch_weight = 1.2
+catch_profile = ExtResource("2_profile")
+availability = SubResource("Availability")
+weight_min_lb = 0.5
+weight_max_lb = 12.0
+sell_value_min = 6
+sell_value_max = 15
+sell_value_curve = 0.9
+display_texture = ExtResource("99_fish_placeholder")
diff --git a/fish/species/smelt_eulachon/smelt_eulachon.tres b/fish/species/smelt_eulachon/smelt_eulachon.tres
new file mode 100644
index 0000000..f3be455
--- /dev/null
+++ b/fish/species/smelt_eulachon/smelt_eulachon.tres
@@ -0,0 +1,32 @@
+[gd_resource type="Resource" script_class="FishData" load_steps=5 format=3]
+
+[ext_resource type="Script" 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" path="res://fish/fish_availability.gd" id="3_availability"]
+[ext_resource type="Texture2D" path="res://art/exported/creatures/fish_placeholder.png" id="99_fish_placeholder"]
+
+[sub_resource type="Resource" id="Availability"]
+script = ExtResource("3_availability")
+allow_day = false
+require_rain = true
+
+[resource]
+script = ExtResource("1_fish_data")
+id = &"smelt_eulachon"
+display_name = "eulachon"
+active = true
+catalog_number = 4201
+collection_group = &"Ocean-run forage fish"
+logbook_fact = "eulachon return from the pacific to spawn in coastal rivers. their oil-rich bodies once served as candles when dried and fitted with wicks."
+available_seasons = 1
+allowed_water_types = 3
+rarity = 1
+base_catch_weight = 0.9
+catch_profile = ExtResource("2_profile")
+availability = SubResource("Availability")
+weight_min_lb = 0.05
+weight_max_lb = 0.5
+sell_value_min = 4
+sell_value_max = 9
+sell_value_curve = 0.98
+display_texture = ExtResource("99_fish_placeholder")
diff --git a/fish/species/smelt_rainbow/smelt_rainbow.tres b/fish/species/smelt_rainbow/smelt_rainbow.tres
new file mode 100644
index 0000000..34691f9
--- /dev/null
+++ b/fish/species/smelt_rainbow/smelt_rainbow.tres
@@ -0,0 +1,31 @@
+[gd_resource type="Resource" script_class="FishData" load_steps=5 format=3]
+
+[ext_resource type="Script" 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" path="res://fish/fish_availability.gd" id="3_availability"]
+[ext_resource type="Texture2D" path="res://art/exported/creatures/4101.png" id="99_fish_placeholder"]
+
+[sub_resource type="Resource" id="Availability"]
+script = ExtResource("3_availability")
+allow_day = false
+
+[resource]
+script = ExtResource("1_fish_data")
+id = &"smelt_rainbow"
+display_name = "rainbow smelt"
+active = true
+catalog_number = 4101
+collection_group = &"Migratory forage fish"
+logbook_fact = "rainbow smelt move between coastal water and rivers, though some remain landlocked. cucumber-like aromas cling to freshly caught fish."
+available_seasons = 9
+allowed_water_types = 3
+rarity = 0
+base_catch_weight = 2.5
+catch_profile = ExtResource("2_profile")
+availability = SubResource("Availability")
+weight_min_lb = 0.05
+weight_max_lb = 0.8
+sell_value_min = 2
+sell_value_max = 5
+sell_value_curve = 1.0
+display_texture = ExtResource("99_fish_placeholder")
diff --git a/fish/species/snakehead_giant/snakehead_giant.tres b/fish/species/snakehead_giant/snakehead_giant.tres
new file mode 100644
index 0000000..069d723
--- /dev/null
+++ b/fish/species/snakehead_giant/snakehead_giant.tres
@@ -0,0 +1,32 @@
+[gd_resource type="Resource" script_class="FishData" load_steps=5 format=3]
+
+[ext_resource type="Script" 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" path="res://fish/fish_availability.gd" id="3_availability"]
+[ext_resource type="Texture2D" path="res://art/exported/creatures/fish_placeholder.png" id="99_fish_placeholder"]
+
+[sub_resource type="Resource" id="Availability"]
+script = ExtResource("3_availability")
+allow_day = false
+require_fog = true
+
+[resource]
+script = ExtResource("1_fish_data")
+id = &"snakehead_giant"
+display_name = "giant snakehead"
+active = true
+catalog_number = 7601
+collection_group = &"Tropical freshwater predators"
+logbook_fact = "giant snakeheads breathe air and guard schools of young near the surface. long bodies launch sudden attacks from flooded vegetation."
+available_seasons = 15
+allowed_water_types = 1
+rarity = 3
+base_catch_weight = 0.1
+catch_profile = ExtResource("2_profile")
+availability = SubResource("Availability")
+weight_min_lb = 3.0
+weight_max_lb = 45.0
+sell_value_min = 28
+sell_value_max = 64
+sell_value_curve = 0.84
+display_texture = ExtResource("99_fish_placeholder")
diff --git a/fish/species/snapper_lane/fish_snapper_lane.png b/fish/species/snapper_lane/fish_snapper_lane.png
index afedbc3..a9c3270 100644
Binary files a/fish/species/snapper_lane/fish_snapper_lane.png and b/fish/species/snapper_lane/fish_snapper_lane.png differ
diff --git a/fish/species/snapper_lane/snapper_lane.tres b/fish/species/snapper_lane/snapper_lane.tres
index e9e7422..d91444f 100644
--- a/fish/species/snapper_lane/snapper_lane.tres
+++ b/fish/species/snapper_lane/snapper_lane.tres
@@ -3,7 +3,7 @@
[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"]
+[ext_resource type="Texture2D" path="res://art/exported/creatures/6001.png" id="4_texture"]
[sub_resource type="Resource" id="SnapperLaneAvailability"]
script = ExtResource("3_availability_script")
@@ -13,6 +13,11 @@ allowed_location_tags = Array[StringName]([&"ocean"])
script = ExtResource("1_fish_data")
id = &"snapper_lane"
display_name = "lane snapper"
+active = true
+catalog_number = 6001
+collection_group = &"Reef snapper"
+logbook_fact = "lane snapper use reef structure for cover and hunt small crustaceans and fish when the light begins to fade."
+available_seasons = 15
allowed_water_types = 2
base_catch_weight = 0.45
catch_profile = ExtResource("2_profile")
diff --git a/fish/species/snapper_mangrove/fish_snapper_mangrove.png b/fish/species/snapper_mangrove/fish_snapper_mangrove.png
index 758ee16..37f2850 100644
Binary files a/fish/species/snapper_mangrove/fish_snapper_mangrove.png and b/fish/species/snapper_mangrove/fish_snapper_mangrove.png differ
diff --git a/fish/species/snapper_mangrove/snapper_mangrove.tres b/fish/species/snapper_mangrove/snapper_mangrove.tres
index 58226ad..9a0ede5 100644
--- a/fish/species/snapper_mangrove/snapper_mangrove.tres
+++ b/fish/species/snapper_mangrove/snapper_mangrove.tres
@@ -3,7 +3,7 @@
[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"]
+[ext_resource type="Texture2D" path="res://art/exported/creatures/fish_placeholder.png" id="4_texture"]
[sub_resource type="Resource" id="SnapperMangroveAvailability"]
script = ExtResource("3_availability_script")
@@ -13,6 +13,11 @@ allowed_location_tags = Array[StringName]([&"ocean"])
script = ExtResource("1_fish_data")
id = &"snapper_mangrove"
display_name = "mangrove snapper"
+active = true
+catalog_number = 6002
+collection_group = &"Reef snapper"
+logbook_fact = "mangrove snapper shelter among roots and docks when young, then move toward reefs as they grow."
+available_seasons = 15
allowed_water_types = 2
rarity = 1
base_catch_weight = 0.35
diff --git a/fish/species/snapper_mutton/fish_snapper_mutton.png b/fish/species/snapper_mutton/fish_snapper_mutton.png
index f8de3ff..7c99847 100644
Binary files a/fish/species/snapper_mutton/fish_snapper_mutton.png and b/fish/species/snapper_mutton/fish_snapper_mutton.png differ
diff --git a/fish/species/snapper_mutton/snapper_mutton.tres b/fish/species/snapper_mutton/snapper_mutton.tres
index 6d4441a..7c5aee9 100644
--- a/fish/species/snapper_mutton/snapper_mutton.tres
+++ b/fish/species/snapper_mutton/snapper_mutton.tres
@@ -3,7 +3,7 @@
[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"]
+[ext_resource type="Texture2D" path="res://art/exported/creatures/fish_placeholder.png" id="4_texture"]
[sub_resource type="Resource" id="SnapperMuttonAvailability"]
script = ExtResource("3_availability_script")
@@ -13,6 +13,11 @@ allowed_location_tags = Array[StringName]([&"ocean"])
script = ExtResource("1_fish_data")
id = &"snapper_mutton"
display_name = "mutton snapper"
+active = true
+catalog_number = 6003
+collection_group = &"Reef snapper"
+logbook_fact = "mutton snapper have canine teeth that help them pick crabs, shrimp, and other hard-shelled prey from the reef."
+available_seasons = 15
allowed_water_types = 2
rarity = 2
base_catch_weight = 0.2
diff --git a/fish/species/snapper_red/fish_snapper_red.png b/fish/species/snapper_red/fish_snapper_red.png
index 435a95e..20285eb 100644
Binary files a/fish/species/snapper_red/fish_snapper_red.png and b/fish/species/snapper_red/fish_snapper_red.png differ
diff --git a/fish/species/snapper_red/snapper_red.tres b/fish/species/snapper_red/snapper_red.tres
index 8778c98..247edc0 100644
--- a/fish/species/snapper_red/snapper_red.tres
+++ b/fish/species/snapper_red/snapper_red.tres
@@ -3,7 +3,7 @@
[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"]
+[ext_resource type="Texture2D" path="res://art/exported/creatures/fish_placeholder.png" id="4_texture"]
[sub_resource type="Resource" id="SnapperRedAvailability"]
script = ExtResource("3_availability_script")
@@ -13,6 +13,11 @@ allowed_location_tags = Array[StringName]([&"ocean"])
script = ExtResource("1_fish_data")
id = &"snapper_red"
display_name = "red snapper"
+active = true
+catalog_number = 6004
+collection_group = &"Reef snapper"
+logbook_fact = "red snapper gather around reefs, wrecks, and ledges. their diet includes small fish, shrimp, and squid."
+available_seasons = 15
allowed_water_types = 2
rarity = 1
base_catch_weight = 0.35
diff --git a/fish/species/snook_common/snook_common.tres b/fish/species/snook_common/snook_common.tres
new file mode 100644
index 0000000..5b84e99
--- /dev/null
+++ b/fish/species/snook_common/snook_common.tres
@@ -0,0 +1,32 @@
+[gd_resource type="Resource" script_class="FishData" load_steps=5 format=3]
+
+[ext_resource type="Script" 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" path="res://fish/fish_availability.gd" id="3_availability"]
+[ext_resource type="Texture2D" path="res://art/exported/creatures/fish_placeholder.png" id="99_fish_placeholder"]
+
+[sub_resource type="Resource" id="Availability"]
+script = ExtResource("3_availability")
+allow_day = false
+require_rain = true
+
+[resource]
+script = ExtResource("1_fish_data")
+id = &"snook_common"
+display_name = "common snook"
+active = true
+catalog_number = 7303
+collection_group = &"Tropical estuary"
+logbook_fact = "common snook wait around mangrove roots and current seams to ambush prey. a bold black lateral line divides their silver flanks."
+available_seasons = 15
+allowed_water_types = 2
+rarity = 2
+base_catch_weight = 0.3
+catch_profile = ExtResource("2_profile")
+availability = SubResource("Availability")
+weight_min_lb = 1.0
+weight_max_lb = 30.0
+sell_value_min = 13
+sell_value_max = 32
+sell_value_curve = 0.85
+display_texture = ExtResource("99_fish_placeholder")
diff --git a/fish/species/sole_dover/sole_dover.tres b/fish/species/sole_dover/sole_dover.tres
new file mode 100644
index 0000000..2b522a0
--- /dev/null
+++ b/fish/species/sole_dover/sole_dover.tres
@@ -0,0 +1,31 @@
+[gd_resource type="Resource" script_class="FishData" load_steps=5 format=3]
+
+[ext_resource type="Script" 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" path="res://fish/fish_availability.gd" id="3_availability"]
+[ext_resource type="Texture2D" path="res://art/exported/creatures/fish_placeholder.png" id="99_fish_placeholder"]
+
+[sub_resource type="Resource" id="Availability"]
+script = ExtResource("3_availability")
+allow_day = false
+
+[resource]
+script = ExtResource("1_fish_data")
+id = &"sole_dover"
+display_name = "Dover sole"
+active = true
+catalog_number = 1404
+collection_group = &"Cold shelf fish"
+logbook_fact = "dover sole glide over deep muddy bottoms of the eastern pacific. both eyes migrate to the right side as the young fish becomes flat."
+available_seasons = 13
+allowed_water_types = 2
+rarity = 1
+base_catch_weight = 0.8
+catch_profile = ExtResource("2_profile")
+availability = SubResource("Availability")
+weight_min_lb = 0.2
+weight_max_lb = 8.0
+sell_value_min = 6
+sell_value_max = 14
+sell_value_curve = 0.9
+display_texture = ExtResource("99_fish_placeholder")
diff --git a/fish/species/sprat_european/sprat_european.tres b/fish/species/sprat_european/sprat_european.tres
new file mode 100644
index 0000000..82f4876
--- /dev/null
+++ b/fish/species/sprat_european/sprat_european.tres
@@ -0,0 +1,30 @@
+[gd_resource type="Resource" script_class="FishData" load_steps=5 format=3]
+
+[ext_resource type="Script" 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" path="res://fish/fish_availability.gd" id="3_availability"]
+[ext_resource type="Texture2D" path="res://art/exported/creatures/fish_placeholder.png" id="99_fish_placeholder"]
+
+[sub_resource type="Resource" id="Availability"]
+script = ExtResource("3_availability")
+
+[resource]
+script = ExtResource("1_fish_data")
+id = &"sprat_european"
+display_name = "European sprat"
+active = true
+catalog_number = 1303
+collection_group = &"Cold schooling fish"
+logbook_fact = "european sprat form compact schools in northern coastal seas. these tiny herring relatives feed on plankton near the surface."
+available_seasons = 13
+allowed_water_types = 2
+rarity = 0
+base_catch_weight = 3.2
+catch_profile = ExtResource("2_profile")
+availability = SubResource("Availability")
+weight_min_lb = 0.03
+weight_max_lb = 0.3
+sell_value_min = 1
+sell_value_max = 3
+sell_value_curve = 1.0
+display_texture = ExtResource("99_fish_placeholder")
diff --git a/fish/species/squid_bigfin_reef/squid_bigfin_reef.tres b/fish/species/squid_bigfin_reef/squid_bigfin_reef.tres
new file mode 100644
index 0000000..68f8993
--- /dev/null
+++ b/fish/species/squid_bigfin_reef/squid_bigfin_reef.tres
@@ -0,0 +1,32 @@
+[gd_resource type="Resource" script_class="FishData" load_steps=5 format=3]
+
+[ext_resource type="Script" 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" path="res://fish/fish_availability.gd" id="3_availability"]
+[ext_resource type="Texture2D" path="res://art/exported/creatures/fish_placeholder.png" id="99_fish_placeholder"]
+
+[sub_resource type="Resource" id="Availability"]
+script = ExtResource("3_availability")
+allow_night = false
+forbid_fog = true
+
+[resource]
+script = ExtResource("1_fish_data")
+id = &"squid_bigfin_reef"
+display_name = "bigfin reef squid"
+active = true
+catalog_number = 6501
+collection_group = &"Squid"
+logbook_fact = "bigfin reef squid hover in coordinated groups around warm reefs. shifting colors and body patterns signal to nearby squid."
+available_seasons = 15
+allowed_water_types = 2
+rarity = 2
+base_catch_weight = 0.3
+catch_profile = ExtResource("2_profile")
+availability = SubResource("Availability")
+weight_min_lb = 0.2
+weight_max_lb = 5.0
+sell_value_min = 13
+sell_value_max = 30
+sell_value_curve = 0.91
+display_texture = ExtResource("99_fish_placeholder")
diff --git a/fish/species/squid_colossal/squid_colossal.tres b/fish/species/squid_colossal/squid_colossal.tres
new file mode 100644
index 0000000..4e38896
--- /dev/null
+++ b/fish/species/squid_colossal/squid_colossal.tres
@@ -0,0 +1,32 @@
+[gd_resource type="Resource" script_class="FishData" load_steps=5 format=3]
+
+[ext_resource type="Script" 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" path="res://fish/fish_availability.gd" id="3_availability"]
+[ext_resource type="Texture2D" path="res://art/exported/creatures/fish_placeholder.png" id="99_fish_placeholder"]
+
+[sub_resource type="Resource" id="Availability"]
+script = ExtResource("3_availability")
+allow_day = false
+require_rain = true
+
+[resource]
+script = ExtResource("1_fish_data")
+id = &"squid_colossal"
+display_name = "colossal squid"
+active = true
+catalog_number = 6502
+collection_group = &"Squid"
+logbook_fact = "colossal squid carry swiveling hooks on their arms and tentacles. enormous eyes gather faint light in the deep southern ocean."
+available_seasons = 15
+allowed_water_types = 2
+rarity = 4
+base_catch_weight = 0.015
+catch_profile = ExtResource("2_profile")
+availability = SubResource("Availability")
+weight_min_lb = 400.0
+weight_max_lb = 1100.0
+sell_value_min = 82
+sell_value_max = 190
+sell_value_curve = 0.76
+display_texture = ExtResource("99_fish_placeholder")
diff --git a/fish/species/squid_giant/squid_giant.tres b/fish/species/squid_giant/squid_giant.tres
new file mode 100644
index 0000000..4f76512
--- /dev/null
+++ b/fish/species/squid_giant/squid_giant.tres
@@ -0,0 +1,32 @@
+[gd_resource type="Resource" script_class="FishData" load_steps=5 format=3]
+
+[ext_resource type="Script" 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" path="res://fish/fish_availability.gd" id="3_availability"]
+[ext_resource type="Texture2D" path="res://art/exported/creatures/fish_placeholder.png" id="99_fish_placeholder"]
+
+[sub_resource type="Resource" id="Availability"]
+script = ExtResource("3_availability")
+allow_day = false
+require_fog = true
+
+[resource]
+script = ExtResource("1_fish_data")
+id = &"squid_giant"
+display_name = "giant squid"
+active = true
+catalog_number = 6503
+collection_group = &"Squid"
+logbook_fact = "giant squid hunt deep ocean prey with two long feeding tentacles. the largest eyes in the animal world watch for movement in darkness."
+available_seasons = 15
+allowed_water_types = 2
+rarity = 4
+base_catch_weight = 0.02
+catch_profile = ExtResource("2_profile")
+availability = SubResource("Availability")
+weight_min_lb = 100.0
+weight_max_lb = 600.0
+sell_value_min = 70
+sell_value_max = 165
+sell_value_curve = 0.77
+display_texture = ExtResource("99_fish_placeholder")
diff --git a/fish/species/squid_humboldt/squid_humboldt.tres b/fish/species/squid_humboldt/squid_humboldt.tres
new file mode 100644
index 0000000..14abcfb
--- /dev/null
+++ b/fish/species/squid_humboldt/squid_humboldt.tres
@@ -0,0 +1,31 @@
+[gd_resource type="Resource" script_class="FishData" load_steps=5 format=3]
+
+[ext_resource type="Script" 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" path="res://fish/fish_availability.gd" id="3_availability"]
+[ext_resource type="Texture2D" path="res://art/exported/creatures/fish_placeholder.png" id="99_fish_placeholder"]
+
+[sub_resource type="Resource" id="Availability"]
+script = ExtResource("3_availability")
+allow_day = false
+
+[resource]
+script = ExtResource("1_fish_data")
+id = &"squid_humboldt"
+display_name = "Humboldt squid"
+active = true
+catalog_number = 6504
+collection_group = &"Squid"
+logbook_fact = "humboldt squid flash red and white while hunting in large groups. powerful beaks and grasping suckers handle fish and other squid."
+available_seasons = 15
+allowed_water_types = 2
+rarity = 3
+base_catch_weight = 0.09
+catch_profile = ExtResource("2_profile")
+availability = SubResource("Availability")
+weight_min_lb = 5.0
+weight_max_lb = 110.0
+sell_value_min = 26
+sell_value_max = 64
+sell_value_curve = 0.82
+display_texture = ExtResource("99_fish_placeholder")
diff --git a/fish/species/stingray_ocellate_river/stingray_ocellate_river.tres b/fish/species/stingray_ocellate_river/stingray_ocellate_river.tres
new file mode 100644
index 0000000..10bf3dc
--- /dev/null
+++ b/fish/species/stingray_ocellate_river/stingray_ocellate_river.tres
@@ -0,0 +1,32 @@
+[gd_resource type="Resource" script_class="FishData" load_steps=5 format=3]
+
+[ext_resource type="Script" 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" path="res://fish/fish_availability.gd" id="3_availability"]
+[ext_resource type="Texture2D" path="res://art/exported/creatures/fish_placeholder.png" id="99_fish_placeholder"]
+
+[sub_resource type="Resource" id="Availability"]
+script = ExtResource("3_availability")
+allow_night = false
+require_rain = true
+
+[resource]
+script = ExtResource("1_fish_data")
+id = &"stingray_ocellate_river"
+display_name = "ocellate river stingray"
+active = true
+catalog_number = 5104
+collection_group = &"Rays and skates"
+logbook_fact = "ocellate river stingrays carry bright eye-like spots across a round disc. they hide beneath sand in south american rivers."
+available_seasons = 15
+allowed_water_types = 1
+rarity = 3
+base_catch_weight = 0.09
+catch_profile = ExtResource("2_profile")
+availability = SubResource("Availability")
+weight_min_lb = 2.0
+weight_max_lb = 35.0
+sell_value_min = 28
+sell_value_max = 65
+sell_value_curve = 0.82
+display_texture = ExtResource("99_fish_placeholder")
diff --git a/fish/species/stingray_southern/stingray_southern.tres b/fish/species/stingray_southern/stingray_southern.tres
new file mode 100644
index 0000000..c0b03f0
--- /dev/null
+++ b/fish/species/stingray_southern/stingray_southern.tres
@@ -0,0 +1,31 @@
+[gd_resource type="Resource" script_class="FishData" load_steps=5 format=3]
+
+[ext_resource type="Script" 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" path="res://fish/fish_availability.gd" id="3_availability"]
+[ext_resource type="Texture2D" path="res://art/exported/creatures/fish_placeholder.png" id="99_fish_placeholder"]
+
+[sub_resource type="Resource" id="Availability"]
+script = ExtResource("3_availability")
+allow_night = false
+
+[resource]
+script = ExtResource("1_fish_data")
+id = &"stingray_southern"
+display_name = "southern stingray"
+active = true
+catalog_number = 5105
+collection_group = &"Rays and skates"
+logbook_fact = "southern stingrays bury in warm sand with only their eyes and spiracles visible. crushing plates break open clams and crustaceans."
+available_seasons = 15
+allowed_water_types = 2
+rarity = 2
+base_catch_weight = 0.23
+catch_profile = ExtResource("2_profile")
+availability = SubResource("Availability")
+weight_min_lb = 2.0
+weight_max_lb = 80.0
+sell_value_min = 15
+sell_value_max = 40
+sell_value_curve = 0.82
+display_texture = ExtResource("99_fish_placeholder")
diff --git a/fish/species/sturgeon_lake/sturgeon_lake.png b/fish/species/sturgeon_lake/sturgeon_lake.png
new file mode 100644
index 0000000..68c9a74
Binary files /dev/null and b/fish/species/sturgeon_lake/sturgeon_lake.png differ
diff --git a/fish/species/sturgeon_lake/sturgeon_lake.png.import b/fish/species/sturgeon_lake/sturgeon_lake.png.import
new file mode 100644
index 0000000..505b308
--- /dev/null
+++ b/fish/species/sturgeon_lake/sturgeon_lake.png.import
@@ -0,0 +1,40 @@
+[remap]
+
+importer="texture"
+type="CompressedTexture2D"
+uid="uid://515k3s5wc3ct"
+path="res://.godot/imported/sturgeon_lake.png-ab8bbee2fea49b2a23bc376500df6ae1.ctex"
+metadata={
+"vram_texture": false
+}
+
+[deps]
+
+source_file="res://fish/species/sturgeon_lake/sturgeon_lake.png"
+dest_files=["res://.godot/imported/sturgeon_lake.png-ab8bbee2fea49b2a23bc376500df6ae1.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/sturgeon_lake/sturgeon_lake.tres b/fish/species/sturgeon_lake/sturgeon_lake.tres
new file mode 100644
index 0000000..eef7789
--- /dev/null
+++ b/fish/species/sturgeon_lake/sturgeon_lake.tres
@@ -0,0 +1,31 @@
+[gd_resource type="Resource" script_class="FishData" load_steps=5 format=3]
+
+[ext_resource type="Script" 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" path="res://fish/fish_availability.gd" id="3_availability"]
+[ext_resource type="Texture2D" path="res://art/exported/creatures/102.png" id="4_texture"]
+
+[sub_resource type="Resource" id="Availability"]
+script = ExtResource("3_availability")
+allow_day = false
+
+[resource]
+script = ExtResource("1_fish_data")
+id = &"sturgeon_lake"
+display_name = "lake sturgeon"
+active = true
+catalog_number = 102
+collection_group = &"Ancient freshwater fish"
+logbook_fact = "lake sturgeon sweep the bottom with four sensitive barbels before extending a tube-like mouth to feed. they can live for decades."
+available_seasons = 15
+allowed_water_types = 1
+rarity = 3
+base_catch_weight = 0.09
+catch_profile = ExtResource("2_profile")
+availability = SubResource("Availability")
+weight_min_lb = 10.0
+weight_max_lb = 180.0
+sell_value_min = 36
+sell_value_max = 82
+sell_value_curve = 0.8
+display_texture = ExtResource("4_texture")
diff --git a/fish/species/sturgeon_shovelnose/sturgeon_shovelnose.png b/fish/species/sturgeon_shovelnose/sturgeon_shovelnose.png
new file mode 100644
index 0000000..197c90a
Binary files /dev/null and b/fish/species/sturgeon_shovelnose/sturgeon_shovelnose.png differ
diff --git a/fish/species/sturgeon_shovelnose/sturgeon_shovelnose.png.import b/fish/species/sturgeon_shovelnose/sturgeon_shovelnose.png.import
new file mode 100644
index 0000000..c66c0ec
--- /dev/null
+++ b/fish/species/sturgeon_shovelnose/sturgeon_shovelnose.png.import
@@ -0,0 +1,40 @@
+[remap]
+
+importer="texture"
+type="CompressedTexture2D"
+uid="uid://d3jk1u2bkisil"
+path="res://.godot/imported/sturgeon_shovelnose.png-abfc7dc07c08b02b361c4ea424ee5ece.ctex"
+metadata={
+"vram_texture": false
+}
+
+[deps]
+
+source_file="res://fish/species/sturgeon_shovelnose/sturgeon_shovelnose.png"
+dest_files=["res://.godot/imported/sturgeon_shovelnose.png-abfc7dc07c08b02b361c4ea424ee5ece.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/sturgeon_shovelnose/sturgeon_shovelnose.tres b/fish/species/sturgeon_shovelnose/sturgeon_shovelnose.tres
new file mode 100644
index 0000000..9f02593
--- /dev/null
+++ b/fish/species/sturgeon_shovelnose/sturgeon_shovelnose.tres
@@ -0,0 +1,31 @@
+[gd_resource type="Resource" script_class="FishData" load_steps=5 format=3]
+
+[ext_resource type="Script" 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" path="res://fish/fish_availability.gd" id="3_availability"]
+[ext_resource type="Texture2D" path="res://art/exported/creatures/105.png" id="4_texture"]
+
+[sub_resource type="Resource" id="Availability"]
+script = ExtResource("3_availability")
+allow_day = false
+
+[resource]
+script = ExtResource("1_fish_data")
+id = &"sturgeon_shovelnose"
+display_name = "shovelnose sturgeon"
+active = true
+catalog_number = 105
+collection_group = &"Ancient freshwater fish"
+logbook_fact = "shovelnose sturgeon hug flowing river bottoms and probe for insect larvae. a flattened snout helps them hold position in strong current."
+available_seasons = 15
+allowed_water_types = 1
+rarity = 2
+base_catch_weight = 0.22
+catch_profile = ExtResource("2_profile")
+availability = SubResource("Availability")
+weight_min_lb = 1.0
+weight_max_lb = 8.0
+sell_value_min = 12
+sell_value_max = 25
+sell_value_curve = 0.9
+display_texture = ExtResource("4_texture")
diff --git a/fish/species/sturgeon_white/sturgeon_white.tres b/fish/species/sturgeon_white/sturgeon_white.tres
new file mode 100644
index 0000000..961411d
--- /dev/null
+++ b/fish/species/sturgeon_white/sturgeon_white.tres
@@ -0,0 +1,31 @@
+[gd_resource type="Resource" script_class="FishData" load_steps=5 format=3]
+
+[ext_resource type="Script" 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" path="res://fish/fish_availability.gd" id="3_availability"]
+[ext_resource type="Texture2D" path="res://art/exported/creatures/fish_placeholder.png" id="99_fish_placeholder"]
+
+[sub_resource type="Resource" id="Availability"]
+script = ExtResource("3_availability")
+allow_day = false
+
+[resource]
+script = ExtResource("1_fish_data")
+id = &"sturgeon_white"
+display_name = "white sturgeon"
+active = true
+catalog_number = 4301
+collection_group = &"Ocean-run giants"
+logbook_fact = "white sturgeon are the largest freshwater fish in north america. armored plates line bodies built for long lives in rivers and estuaries."
+available_seasons = 5
+allowed_water_types = 3
+rarity = 4
+base_catch_weight = 0.045
+catch_profile = ExtResource("2_profile")
+availability = SubResource("Availability")
+weight_min_lb = 20.0
+weight_max_lb = 500.0
+sell_value_min = 58
+sell_value_max = 135
+sell_value_curve = 0.78
+display_texture = ExtResource("99_fish_placeholder")
diff --git a/fish/species/sucker_white/sucker_white.tres b/fish/species/sucker_white/sucker_white.tres
new file mode 100644
index 0000000..ff48770
--- /dev/null
+++ b/fish/species/sucker_white/sucker_white.tres
@@ -0,0 +1,30 @@
+[gd_resource type="Resource" script_class="FishData" load_steps=5 format=3]
+
+[ext_resource type="Script" 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" path="res://fish/fish_availability.gd" id="3_availability"]
+[ext_resource type="Texture2D" path="res://art/exported/creatures/fish_placeholder.png" id="99_fish_placeholder"]
+
+[sub_resource type="Resource" id="Availability"]
+script = ExtResource("3_availability")
+
+[resource]
+script = ExtResource("1_fish_data")
+id = &"sucker_white"
+display_name = "white sucker"
+active = true
+catalog_number = 2305
+collection_group = &"Freshwater bottom fish"
+logbook_fact = "white suckers vacuum insects and organic matter from cool freshwater bottoms. spring runs carry large groups into shallow streams."
+available_seasons = 7
+allowed_water_types = 1
+rarity = 0
+base_catch_weight = 2.0
+catch_profile = ExtResource("2_profile")
+availability = SubResource("Availability")
+weight_min_lb = 0.3
+weight_max_lb = 8.0
+sell_value_min = 3
+sell_value_max = 8
+sell_value_curve = 0.9
+display_texture = ExtResource("99_fish_placeholder")
diff --git a/fish/species/sunfish/fish_sunfish.png b/fish/species/sunfish/fish_sunfish.png
index 4b7b084..ca8a340 100644
Binary files a/fish/species/sunfish/fish_sunfish.png and b/fish/species/sunfish/fish_sunfish.png differ
diff --git a/fish/species/sunfish/sunfish.tres b/fish/species/sunfish/sunfish.tres
index 14d2ced..5efd181 100644
--- a/fish/species/sunfish/sunfish.tres
+++ b/fish/species/sunfish/sunfish.tres
@@ -3,12 +3,17 @@
[ext_resource type="Script" 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="Resource" path="res://fish/day_availability.tres" id="3_availability"]
-[ext_resource type="Texture2D" path="res://fish/species/sunfish/fish_sunfish.png" id="4_texture"]
+[ext_resource type="Texture2D" path="res://art/exported/creatures/5001.png" id="4_texture"]
[resource]
script = ExtResource("1_fish_data")
id = &"sunfish"
display_name = "sunfish"
+active = true
+catalog_number = 5001
+collection_group = &"Open-ocean giants"
+logbook_fact = "ocean sunfish are built more like enormous swimming heads than ordinary fish. they visit the surface and turn sideways to warm up."
+available_seasons = 7
allowed_water_types = 2
rarity = 0
base_catch_weight = 7.0
diff --git a/fish/species/surgeonfish_blue/surgeonfish_blue.tres b/fish/species/surgeonfish_blue/surgeonfish_blue.tres
new file mode 100644
index 0000000..d0a903e
--- /dev/null
+++ b/fish/species/surgeonfish_blue/surgeonfish_blue.tres
@@ -0,0 +1,31 @@
+[gd_resource type="Resource" script_class="FishData" load_steps=5 format=3]
+
+[ext_resource type="Script" 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" path="res://fish/fish_availability.gd" id="3_availability"]
+[ext_resource type="Texture2D" path="res://art/exported/creatures/fish_placeholder.png" id="99_fish_placeholder"]
+
+[sub_resource type="Resource" id="Availability"]
+script = ExtResource("3_availability")
+allow_night = false
+
+[resource]
+script = ExtResource("1_fish_data")
+id = &"surgeonfish_blue"
+display_name = "blue tang"
+active = true
+catalog_number = 5501
+collection_group = &"Reef grazers"
+logbook_fact = "blue tangs graze algae from coral reefs with small close-set teeth. sharp folding spines near the tail give surgeonfish their name."
+available_seasons = 15
+allowed_water_types = 2
+rarity = 1
+base_catch_weight = 0.9
+catch_profile = ExtResource("2_profile")
+availability = SubResource("Availability")
+weight_min_lb = 0.05
+weight_max_lb = 1.5
+sell_value_min = 5
+sell_value_max = 11
+sell_value_curve = 0.95
+display_texture = ExtResource("99_fish_placeholder")
diff --git a/fish/species/swordfish/fish_swordfish.png b/fish/species/swordfish/fish_swordfish.png
index a43d703..3b1917b 100644
Binary files a/fish/species/swordfish/fish_swordfish.png and b/fish/species/swordfish/fish_swordfish.png differ
diff --git a/fish/species/swordfish/swordfish.tres b/fish/species/swordfish/swordfish.tres
index 2184395..24319f1 100644
--- a/fish/species/swordfish/swordfish.tres
+++ b/fish/species/swordfish/swordfish.tres
@@ -3,7 +3,7 @@
[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"]
+[ext_resource type="Texture2D" path="res://art/exported/creatures/fish_placeholder.png" id="4_texture"]
[sub_resource type="Resource" id="SwordfishAvailability"]
script = ExtResource("3_availability_script")
@@ -13,6 +13,11 @@ allowed_location_tags = Array[StringName]([&"ocean"])
script = ExtResource("1_fish_data")
id = &"swordfish"
display_name = "swordfish"
+active = true
+catalog_number = 4604
+collection_group = &"Oceanic billfish"
+logbook_fact = "swordfish are fast, wide-ranging predators with a flattened bill. they can cross deep water and warm surface layers in one day."
+available_seasons = 15
allowed_water_types = 2
rarity = 4
base_catch_weight = 0.06
diff --git a/fish/species/tambaqui/tambaqui.tres b/fish/species/tambaqui/tambaqui.tres
new file mode 100644
index 0000000..4bfd4a1
--- /dev/null
+++ b/fish/species/tambaqui/tambaqui.tres
@@ -0,0 +1,32 @@
+[gd_resource type="Resource" script_class="FishData" load_steps=5 format=3]
+
+[ext_resource type="Script" 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" path="res://fish/fish_availability.gd" id="3_availability"]
+[ext_resource type="Texture2D" path="res://art/exported/creatures/fish_placeholder.png" id="99_fish_placeholder"]
+
+[sub_resource type="Resource" id="Availability"]
+script = ExtResource("3_availability")
+allow_night = false
+require_rain = true
+
+[resource]
+script = ExtResource("1_fish_data")
+id = &"tambaqui"
+display_name = "tambaqui"
+active = true
+catalog_number = 7509
+collection_group = &"Tropical freshwater fish"
+logbook_fact = "tambaqui follow seasonal floods into forests to feed on fruit and seeds. strong square teeth crack tough plant material."
+available_seasons = 15
+allowed_water_types = 1
+rarity = 3
+base_catch_weight = 0.1
+catch_profile = ExtResource("2_profile")
+availability = SubResource("Availability")
+weight_min_lb = 8.0
+weight_max_lb = 80.0
+sell_value_min = 30
+sell_value_max = 68
+sell_value_curve = 0.82
+display_texture = ExtResource("99_fish_placeholder")
diff --git a/fish/species/tang_yellow/tang_yellow.tres b/fish/species/tang_yellow/tang_yellow.tres
new file mode 100644
index 0000000..501f2a3
--- /dev/null
+++ b/fish/species/tang_yellow/tang_yellow.tres
@@ -0,0 +1,32 @@
+[gd_resource type="Resource" script_class="FishData" load_steps=5 format=3]
+
+[ext_resource type="Script" 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" path="res://fish/fish_availability.gd" id="3_availability"]
+[ext_resource type="Texture2D" path="res://art/exported/creatures/fish_placeholder.png" id="99_fish_placeholder"]
+
+[sub_resource type="Resource" id="Availability"]
+script = ExtResource("3_availability")
+allow_night = false
+forbid_rain = true
+
+[resource]
+script = ExtResource("1_fish_data")
+id = &"tang_yellow"
+display_name = "yellow tang"
+active = true
+catalog_number = 5504
+collection_group = &"Reef grazers"
+logbook_fact = "yellow tangs browse algae across hawaiian reefs during daylight. at night their bright bodies darken and show a pale horizontal stripe."
+available_seasons = 15
+allowed_water_types = 2
+rarity = 2
+base_catch_weight = 0.35
+catch_profile = ExtResource("2_profile")
+availability = SubResource("Availability")
+weight_min_lb = 0.05
+weight_max_lb = 0.8
+sell_value_min = 9
+sell_value_max = 20
+sell_value_curve = 0.96
+display_texture = ExtResource("99_fish_placeholder")
diff --git a/fish/species/tarpon_atlantic/tarpon_atlantic.tres b/fish/species/tarpon_atlantic/tarpon_atlantic.tres
new file mode 100644
index 0000000..0b8c6aa
--- /dev/null
+++ b/fish/species/tarpon_atlantic/tarpon_atlantic.tres
@@ -0,0 +1,31 @@
+[gd_resource type="Resource" script_class="FishData" load_steps=5 format=3]
+
+[ext_resource type="Script" 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" path="res://fish/fish_availability.gd" id="3_availability"]
+[ext_resource type="Texture2D" path="res://art/exported/creatures/fish_placeholder.png" id="99_fish_placeholder"]
+
+[sub_resource type="Resource" id="Availability"]
+script = ExtResource("3_availability")
+require_rain = true
+
+[resource]
+script = ExtResource("1_fish_data")
+id = &"tarpon_atlantic"
+display_name = "atlantic tarpon"
+active = true
+catalog_number = 7301
+collection_group = &"Tropical estuary"
+logbook_fact = "atlantic tarpon gulp air with a lung-like swim bladder when coastal water runs low on oxygen. giant silver scales flash during leaps."
+available_seasons = 15
+allowed_water_types = 3
+rarity = 3
+base_catch_weight = 0.09
+catch_profile = ExtResource("2_profile")
+availability = SubResource("Availability")
+weight_min_lb = 10.0
+weight_max_lb = 180.0
+sell_value_min = 34
+sell_value_max = 82
+sell_value_curve = 0.8
+display_texture = ExtResource("99_fish_placeholder")
diff --git a/fish/species/tautog/tautog.tres b/fish/species/tautog/tautog.tres
new file mode 100644
index 0000000..7028401
--- /dev/null
+++ b/fish/species/tautog/tautog.tres
@@ -0,0 +1,31 @@
+[gd_resource type="Resource" script_class="FishData" load_steps=5 format=3]
+
+[ext_resource type="Script" 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" path="res://fish/fish_availability.gd" id="3_availability"]
+[ext_resource type="Texture2D" path="res://art/exported/creatures/fish_placeholder.png" id="99_fish_placeholder"]
+
+[sub_resource type="Resource" id="Availability"]
+script = ExtResource("3_availability")
+allow_night = false
+
+[resource]
+script = ExtResource("1_fish_data")
+id = &"tautog"
+display_name = "tautog"
+active = true
+catalog_number = 6906
+collection_group = &"Temperate reef fish"
+logbook_fact = "tautog pry mussels and crabs from rocky structure with thick lips and strong teeth. adults return to familiar shelters year after year."
+available_seasons = 13
+allowed_water_types = 2
+rarity = 1
+base_catch_weight = 0.8
+catch_profile = ExtResource("2_profile")
+availability = SubResource("Availability")
+weight_min_lb = 0.5
+weight_max_lb = 20.0
+sell_value_min = 7
+sell_value_max = 18
+sell_value_curve = 0.86
+display_texture = ExtResource("99_fish_placeholder")
diff --git a/fish/species/tench/tench.tres b/fish/species/tench/tench.tres
new file mode 100644
index 0000000..7ffe273
--- /dev/null
+++ b/fish/species/tench/tench.tres
@@ -0,0 +1,32 @@
+[gd_resource type="Resource" script_class="FishData" load_steps=5 format=3]
+
+[ext_resource type="Script" 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" path="res://fish/fish_availability.gd" id="3_availability"]
+[ext_resource type="Texture2D" path="res://art/exported/creatures/fish_placeholder.png" id="99_fish_placeholder"]
+
+[sub_resource type="Resource" id="Availability"]
+script = ExtResource("3_availability")
+allow_day = false
+require_fog = true
+
+[resource]
+script = ExtResource("1_fish_data")
+id = &"tench"
+display_name = "tench"
+active = true
+catalog_number = 2509
+collection_group = &"Freshwater coarse fish"
+logbook_fact = "tench browse quiet, muddy bottoms with two small mouth barbels. a thick coat of slime protects their olive-green scales."
+available_seasons = 7
+allowed_water_types = 1
+rarity = 1
+base_catch_weight = 0.75
+catch_profile = ExtResource("2_profile")
+availability = SubResource("Availability")
+weight_min_lb = 0.5
+weight_max_lb = 12.0
+sell_value_min = 7
+sell_value_max = 17
+sell_value_curve = 0.88
+display_texture = ExtResource("99_fish_placeholder")
diff --git a/fish/species/tetra_neon/tetra_neon.tres b/fish/species/tetra_neon/tetra_neon.tres
new file mode 100644
index 0000000..da42e36
--- /dev/null
+++ b/fish/species/tetra_neon/tetra_neon.tres
@@ -0,0 +1,31 @@
+[gd_resource type="Resource" script_class="FishData" load_steps=5 format=3]
+
+[ext_resource type="Script" 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" path="res://fish/fish_availability.gd" id="3_availability"]
+[ext_resource type="Texture2D" path="res://art/exported/creatures/fish_placeholder.png" id="99_fish_placeholder"]
+
+[sub_resource type="Resource" id="Availability"]
+script = ExtResource("3_availability")
+allow_day = false
+
+[resource]
+script = ExtResource("1_fish_data")
+id = &"tetra_neon"
+display_name = "neon tetra"
+active = true
+catalog_number = 2102
+collection_group = &"Freshwater aquarium fish"
+logbook_fact = "neon tetras school through shaded forest streams. an iridescent blue stripe remains visible in the dim, tannin-stained water."
+available_seasons = 15
+allowed_water_types = 1
+rarity = 1
+base_catch_weight = 0.9
+catch_profile = ExtResource("2_profile")
+availability = SubResource("Availability")
+weight_min_lb = 0.01
+weight_max_lb = 0.08
+sell_value_min = 4
+sell_value_max = 8
+sell_value_curve = 0.98
+display_texture = ExtResource("99_fish_placeholder")
diff --git a/fish/species/tigerfish_goliath/tigerfish_goliath.tres b/fish/species/tigerfish_goliath/tigerfish_goliath.tres
new file mode 100644
index 0000000..f34d449
--- /dev/null
+++ b/fish/species/tigerfish_goliath/tigerfish_goliath.tres
@@ -0,0 +1,31 @@
+[gd_resource type="Resource" script_class="FishData" load_steps=5 format=3]
+
+[ext_resource type="Script" 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" path="res://fish/fish_availability.gd" id="3_availability"]
+[ext_resource type="Texture2D" path="res://art/exported/creatures/fish_placeholder.png" id="99_fish_placeholder"]
+
+[sub_resource type="Resource" id="Availability"]
+script = ExtResource("3_availability")
+allow_night = false
+
+[resource]
+script = ExtResource("1_fish_data")
+id = &"tigerfish_goliath"
+display_name = "goliath tigerfish"
+active = true
+catalog_number = 2703
+collection_group = &"Freshwater giants"
+logbook_fact = "goliath tigerfish patrol powerful african rivers with interlocking dagger-like teeth. a muscular forked tail drives sudden attacks."
+available_seasons = 15
+allowed_water_types = 1
+rarity = 4
+base_catch_weight = 0.035
+catch_profile = ExtResource("2_profile")
+availability = SubResource("Availability")
+weight_min_lb = 20.0
+weight_max_lb = 150.0
+sell_value_min = 58
+sell_value_max = 132
+sell_value_curve = 0.8
+display_texture = ExtResource("99_fish_placeholder")
diff --git a/fish/species/tilapia_nile/tilapia_nile.tres b/fish/species/tilapia_nile/tilapia_nile.tres
new file mode 100644
index 0000000..d7a2eed
--- /dev/null
+++ b/fish/species/tilapia_nile/tilapia_nile.tres
@@ -0,0 +1,31 @@
+[gd_resource type="Resource" script_class="FishData" load_steps=5 format=3]
+
+[ext_resource type="Script" 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" path="res://fish/fish_availability.gd" id="3_availability"]
+[ext_resource type="Texture2D" path="res://art/exported/creatures/7506.png" id="99_fish_placeholder"]
+
+[sub_resource type="Resource" id="Availability"]
+script = ExtResource("3_availability")
+allow_night = false
+
+[resource]
+script = ExtResource("1_fish_data")
+id = &"tilapia_nile"
+display_name = "Nile tilapia"
+active = true
+catalog_number = 7506
+collection_group = &"Tropical freshwater fish"
+logbook_fact = "nile tilapia thrive in warm lakes and rivers on a varied diet. females protect eggs and young inside their mouths."
+available_seasons = 15
+allowed_water_types = 1
+rarity = 0
+base_catch_weight = 2.2
+catch_profile = ExtResource("2_profile")
+availability = SubResource("Availability")
+weight_min_lb = 0.3
+weight_max_lb = 8.0
+sell_value_min = 3
+sell_value_max = 9
+sell_value_curve = 0.9
+display_texture = ExtResource("99_fish_placeholder")
diff --git a/fish/species/tilefish_blueline/tilefish_blueline.tres b/fish/species/tilefish_blueline/tilefish_blueline.tres
new file mode 100644
index 0000000..aff98b6
--- /dev/null
+++ b/fish/species/tilefish_blueline/tilefish_blueline.tres
@@ -0,0 +1,31 @@
+[gd_resource type="Resource" script_class="FishData" load_steps=5 format=3]
+
+[ext_resource type="Script" 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" path="res://fish/fish_availability.gd" id="3_availability"]
+[ext_resource type="Texture2D" path="res://art/exported/creatures/fish_placeholder.png" id="99_fish_placeholder"]
+
+[sub_resource type="Resource" id="Availability"]
+script = ExtResource("3_availability")
+allow_night = false
+
+[resource]
+script = ExtResource("1_fish_data")
+id = &"tilefish_blueline"
+display_name = "blueline tilefish"
+active = true
+catalog_number = 1601
+collection_group = &"Deep shelf fish"
+logbook_fact = "blueline tilefish shelter in burrows and rocky openings along deep shelves. a narrow blue stripe traces the edge of the dorsal fin."
+available_seasons = 15
+allowed_water_types = 2
+rarity = 1
+base_catch_weight = 0.7
+catch_profile = ExtResource("2_profile")
+availability = SubResource("Availability")
+weight_min_lb = 1.0
+weight_max_lb = 20.0
+sell_value_min = 7
+sell_value_max = 18
+sell_value_curve = 0.86
+display_texture = ExtResource("99_fish_placeholder")
diff --git a/fish/species/tilefish_golden/tilefish_golden.tres b/fish/species/tilefish_golden/tilefish_golden.tres
new file mode 100644
index 0000000..ed203c8
--- /dev/null
+++ b/fish/species/tilefish_golden/tilefish_golden.tres
@@ -0,0 +1,31 @@
+[gd_resource type="Resource" script_class="FishData" load_steps=5 format=3]
+
+[ext_resource type="Script" 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" path="res://fish/fish_availability.gd" id="3_availability"]
+[ext_resource type="Texture2D" path="res://art/exported/creatures/fish_placeholder.png" id="99_fish_placeholder"]
+
+[sub_resource type="Resource" id="Availability"]
+script = ExtResource("3_availability")
+allow_night = false
+
+[resource]
+script = ExtResource("1_fish_data")
+id = &"tilefish_golden"
+display_name = "golden tilefish"
+active = true
+catalog_number = 1602
+collection_group = &"Deep shelf fish"
+logbook_fact = "golden tilefish excavate burrows in soft clay along deep ocean slopes. their bright yellow spots contrast with blue-green backs."
+available_seasons = 15
+allowed_water_types = 2
+rarity = 2
+base_catch_weight = 0.22
+catch_profile = ExtResource("2_profile")
+availability = SubResource("Availability")
+weight_min_lb = 2.0
+weight_max_lb = 50.0
+sell_value_min = 15
+sell_value_max = 37
+sell_value_curve = 0.84
+display_texture = ExtResource("99_fish_placeholder")
diff --git a/fish/species/trevally_bluefin/trevally_bluefin.tres b/fish/species/trevally_bluefin/trevally_bluefin.tres
new file mode 100644
index 0000000..3cf8dee
--- /dev/null
+++ b/fish/species/trevally_bluefin/trevally_bluefin.tres
@@ -0,0 +1,32 @@
+[gd_resource type="Resource" script_class="FishData" load_steps=5 format=3]
+
+[ext_resource type="Script" 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" path="res://fish/fish_availability.gd" id="3_availability"]
+[ext_resource type="Texture2D" path="res://art/exported/creatures/fish_placeholder.png" id="99_fish_placeholder"]
+
+[sub_resource type="Resource" id="Availability"]
+script = ExtResource("3_availability")
+allow_night = false
+forbid_fog = true
+
+[resource]
+script = ExtResource("1_fish_data")
+id = &"trevally_bluefin"
+display_name = "bluefin trevally"
+active = true
+catalog_number = 5802
+collection_group = &"Reef predators"
+logbook_fact = "bluefin trevally hunt reef fish in coordinated groups. electric blue fins and scattered blue spots contrast with a golden body."
+available_seasons = 15
+allowed_water_types = 2
+rarity = 2
+base_catch_weight = 0.3
+catch_profile = ExtResource("2_profile")
+availability = SubResource("Availability")
+weight_min_lb = 1.0
+weight_max_lb = 25.0
+sell_value_min = 14
+sell_value_max = 32
+sell_value_curve = 0.86
+display_texture = ExtResource("99_fish_placeholder")
diff --git a/fish/species/trevally_giant/trevally_giant.tres b/fish/species/trevally_giant/trevally_giant.tres
new file mode 100644
index 0000000..62ee42b
--- /dev/null
+++ b/fish/species/trevally_giant/trevally_giant.tres
@@ -0,0 +1,31 @@
+[gd_resource type="Resource" script_class="FishData" load_steps=5 format=3]
+
+[ext_resource type="Script" 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" path="res://fish/fish_availability.gd" id="3_availability"]
+[ext_resource type="Texture2D" path="res://art/exported/creatures/fish_placeholder.png" id="99_fish_placeholder"]
+
+[sub_resource type="Resource" id="Availability"]
+script = ExtResource("3_availability")
+allow_night = false
+
+[resource]
+script = ExtResource("1_fish_data")
+id = &"trevally_giant"
+display_name = "giant trevally"
+active = true
+catalog_number = 3603
+collection_group = &"Large coastal predators"
+logbook_fact = "giant trevally hunt reef edges with bursts of speed and force. large individuals sometimes charge prey from surf and shallow lagoons."
+available_seasons = 6
+allowed_water_types = 2
+rarity = 3
+base_catch_weight = 0.1
+catch_profile = ExtResource("2_profile")
+availability = SubResource("Availability")
+weight_min_lb = 10.0
+weight_max_lb = 150.0
+sell_value_min = 32
+sell_value_max = 78
+sell_value_curve = 0.8
+display_texture = ExtResource("99_fish_placeholder")
diff --git a/fish/species/triggerfish_gray/triggerfish_gray.tres b/fish/species/triggerfish_gray/triggerfish_gray.tres
new file mode 100644
index 0000000..0b205a2
--- /dev/null
+++ b/fish/species/triggerfish_gray/triggerfish_gray.tres
@@ -0,0 +1,31 @@
+[gd_resource type="Resource" script_class="FishData" load_steps=5 format=3]
+
+[ext_resource type="Script" 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" path="res://fish/fish_availability.gd" id="3_availability"]
+[ext_resource type="Texture2D" path="res://art/exported/creatures/fish_placeholder.png" id="99_fish_placeholder"]
+
+[sub_resource type="Resource" id="Availability"]
+script = ExtResource("3_availability")
+allow_night = false
+
+[resource]
+script = ExtResource("1_fish_data")
+id = &"triggerfish_gray"
+display_name = "gray triggerfish"
+active = true
+catalog_number = 5602
+collection_group = &"Reef oddities"
+logbook_fact = "gray triggerfish lock a stout dorsal spine upright when wedged into shelter. strong teeth crack barnacles, crabs, and shellfish."
+available_seasons = 15
+allowed_water_types = 2
+rarity = 1
+base_catch_weight = 0.8
+catch_profile = ExtResource("2_profile")
+availability = SubResource("Availability")
+weight_min_lb = 0.3
+weight_max_lb = 12.0
+sell_value_min = 7
+sell_value_max = 17
+sell_value_curve = 0.88
+display_texture = ExtResource("99_fish_placeholder")
diff --git a/fish/species/triggerfish_queen/triggerfish_queen.tres b/fish/species/triggerfish_queen/triggerfish_queen.tres
new file mode 100644
index 0000000..e747f25
--- /dev/null
+++ b/fish/species/triggerfish_queen/triggerfish_queen.tres
@@ -0,0 +1,32 @@
+[gd_resource type="Resource" script_class="FishData" load_steps=5 format=3]
+
+[ext_resource type="Script" 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" path="res://fish/fish_availability.gd" id="3_availability"]
+[ext_resource type="Texture2D" path="res://art/exported/creatures/fish_placeholder.png" id="99_fish_placeholder"]
+
+[sub_resource type="Resource" id="Availability"]
+script = ExtResource("3_availability")
+allow_night = false
+forbid_fog = true
+
+[resource]
+script = ExtResource("1_fish_data")
+id = &"triggerfish_queen"
+display_name = "queen triggerfish"
+active = true
+catalog_number = 5605
+collection_group = &"Reef oddities"
+logbook_fact = "queen triggerfish blow jets of water to uncover buried prey and flip sea urchins. vivid blue lines radiate across the face."
+available_seasons = 15
+allowed_water_types = 2
+rarity = 2
+base_catch_weight = 0.25
+catch_profile = ExtResource("2_profile")
+availability = SubResource("Availability")
+weight_min_lb = 0.5
+weight_max_lb = 15.0
+sell_value_min = 14
+sell_value_max = 32
+sell_value_curve = 0.88
+display_texture = ExtResource("99_fish_placeholder")
diff --git a/fish/species/tripletail/tripletail.tres b/fish/species/tripletail/tripletail.tres
new file mode 100644
index 0000000..7bb151b
--- /dev/null
+++ b/fish/species/tripletail/tripletail.tres
@@ -0,0 +1,32 @@
+[gd_resource type="Resource" script_class="FishData" load_steps=5 format=3]
+
+[ext_resource type="Script" 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" path="res://fish/fish_availability.gd" id="3_availability"]
+[ext_resource type="Texture2D" path="res://art/exported/creatures/fish_placeholder.png" id="99_fish_placeholder"]
+
+[sub_resource type="Resource" id="Availability"]
+script = ExtResource("3_availability")
+allow_night = false
+require_fog = true
+
+[resource]
+script = ExtResource("1_fish_data")
+id = &"tripletail"
+display_name = "tripletail"
+active = true
+catalog_number = 604
+collection_group = &"Coastal oddities"
+logbook_fact = "tripletail often drift sideways near floating debris, resembling a loose leaf. rounded rear fins create the three-tailed silhouette."
+available_seasons = 6
+allowed_water_types = 2
+rarity = 2
+base_catch_weight = 0.28
+catch_profile = ExtResource("2_profile")
+availability = SubResource("Availability")
+weight_min_lb = 1.0
+weight_max_lb = 30.0
+sell_value_min = 13
+sell_value_max = 31
+sell_value_curve = 0.85
+display_texture = ExtResource("99_fish_placeholder")
diff --git a/fish/species/trout_brown/trout_brown.tres b/fish/species/trout_brown/trout_brown.tres
new file mode 100644
index 0000000..6beb1ba
--- /dev/null
+++ b/fish/species/trout_brown/trout_brown.tres
@@ -0,0 +1,31 @@
+[gd_resource type="Resource" script_class="FishData" load_steps=5 format=3]
+
+[ext_resource type="Script" 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" path="res://fish/fish_availability.gd" id="3_availability"]
+[ext_resource type="Texture2D" path="res://art/exported/creatures/fish_placeholder.png" id="99_fish_placeholder"]
+
+[sub_resource type="Resource" id="Availability"]
+script = ExtResource("3_availability")
+allow_day = false
+
+[resource]
+script = ExtResource("1_fish_data")
+id = &"trout_brown"
+display_name = "brown trout"
+active = true
+catalog_number = 1103
+collection_group = &"Cold freshwater salmonids"
+logbook_fact = "brown trout often shelter by day and hunt more freely after dark. adaptable populations occupy streams, lakes, and even coastal water."
+available_seasons = 15
+allowed_water_types = 1
+rarity = 1
+base_catch_weight = 0.8
+catch_profile = ExtResource("2_profile")
+availability = SubResource("Availability")
+weight_min_lb = 0.5
+weight_max_lb = 25.0
+sell_value_min = 7
+sell_value_max = 20
+sell_value_curve = 0.86
+display_texture = ExtResource("99_fish_placeholder")
diff --git a/fish/species/trout_cutthroat/fish_trout_cutthroat.png b/fish/species/trout_cutthroat/fish_trout_cutthroat.png
index 9e45417..9287c2f 100644
Binary files a/fish/species/trout_cutthroat/fish_trout_cutthroat.png and b/fish/species/trout_cutthroat/fish_trout_cutthroat.png differ
diff --git a/fish/species/trout_cutthroat/trout_cutthroat.tres b/fish/species/trout_cutthroat/trout_cutthroat.tres
index fa29c47..6ea9bff 100644
--- a/fish/species/trout_cutthroat/trout_cutthroat.tres
+++ b/fish/species/trout_cutthroat/trout_cutthroat.tres
@@ -3,16 +3,21 @@
[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"]
+[ext_resource type="Texture2D" path="res://art/exported/creatures/fish_placeholder.png" id="4_texture"]
[sub_resource type="Resource" id="TroutCutthroatAvailability"]
script = ExtResource("3_availability_script")
-allowed_location_tags = Array[StringName]([&"starter_pond"])
+allowed_location_tags = Array[StringName]([&"river"])
[resource]
script = ExtResource("1_fish_data")
id = &"trout_cutthroat"
display_name = "cutthroat trout"
+active = true
+catalog_number = 1104
+collection_group = &"Cold freshwater salmonids"
+logbook_fact = "cutthroat trout take their name from the red slash beneath the jaw. many populations rely on cold, well-oxygenated streams."
+available_seasons = 13
allowed_water_types = 1
rarity = 1
base_catch_weight = 0.4
diff --git a/fish/species/trout_golden/fish_trout_golden.png b/fish/species/trout_golden/fish_trout_golden.png
index 209b82d..8b64101 100644
Binary files a/fish/species/trout_golden/fish_trout_golden.png and b/fish/species/trout_golden/fish_trout_golden.png differ
diff --git a/fish/species/trout_golden/trout_golden.tres b/fish/species/trout_golden/trout_golden.tres
index 8aa3201..bb6732b 100644
--- a/fish/species/trout_golden/trout_golden.tres
+++ b/fish/species/trout_golden/trout_golden.tres
@@ -3,16 +3,21 @@
[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"]
+[ext_resource type="Texture2D" path="res://art/exported/creatures/fish_placeholder.png" id="4_texture"]
[sub_resource type="Resource" id="TroutGoldenAvailability"]
script = ExtResource("3_availability_script")
-allowed_location_tags = Array[StringName]([&"starter_pond"])
+allowed_location_tags = Array[StringName]([&"lake"])
[resource]
script = ExtResource("1_fish_data")
id = &"trout_golden"
display_name = "golden trout"
+active = true
+catalog_number = 1105
+collection_group = &"Cold freshwater salmonids"
+logbook_fact = "golden trout evolved in clear, high-elevation california streams. their bright flanks stand out against rocky alpine water."
+available_seasons = 7
allowed_water_types = 1
rarity = 2
base_catch_weight = 0.2
diff --git a/fish/species/trout_rainbow/fish_trout_rainbow.png b/fish/species/trout_rainbow/fish_trout_rainbow.png
index ff6fd06..3f4dfe4 100644
Binary files a/fish/species/trout_rainbow/fish_trout_rainbow.png and b/fish/species/trout_rainbow/fish_trout_rainbow.png differ
diff --git a/fish/species/trout_rainbow/trout_rainbow.tres b/fish/species/trout_rainbow/trout_rainbow.tres
index f9c74f6..69d40bd 100644
--- a/fish/species/trout_rainbow/trout_rainbow.tres
+++ b/fish/species/trout_rainbow/trout_rainbow.tres
@@ -3,16 +3,21 @@
[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"]
+[ext_resource type="Texture2D" path="res://art/exported/creatures/fish_placeholder.png" id="4_texture"]
[sub_resource type="Resource" id="TroutRainbowAvailability"]
script = ExtResource("3_availability_script")
-allowed_location_tags = Array[StringName]([&"starter_pond"])
+allowed_location_tags = Array[StringName]([&"river"])
[resource]
script = ExtResource("1_fish_data")
id = &"trout_rainbow"
display_name = "rainbow trout"
+active = true
+catalog_number = 1106
+collection_group = &"Cold freshwater salmonids"
+logbook_fact = "rainbow trout are adaptable stream hunters. the pink lateral stripe is especially vivid when the fish is in breeding condition."
+available_seasons = 15
allowed_water_types = 1
rarity = 1
base_catch_weight = 0.45
diff --git a/fish/species/trout_steelhead/fish_trout_steelhead.png b/fish/species/trout_steelhead/fish_trout_steelhead.png
index b2600e0..45cc1b1 100644
Binary files a/fish/species/trout_steelhead/fish_trout_steelhead.png and b/fish/species/trout_steelhead/fish_trout_steelhead.png differ
diff --git a/fish/species/trout_steelhead/trout_steelhead.tres b/fish/species/trout_steelhead/trout_steelhead.tres
index 57887f7..ed052d9 100644
--- a/fish/species/trout_steelhead/trout_steelhead.tres
+++ b/fish/species/trout_steelhead/trout_steelhead.tres
@@ -3,16 +3,21 @@
[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"]
+[ext_resource type="Texture2D" path="res://art/exported/creatures/fish_placeholder.png" id="4_texture"]
[sub_resource type="Resource" id="TroutSteelheadAvailability"]
script = ExtResource("3_availability_script")
-allowed_location_tags = Array[StringName]([&"starter_pond"])
+allowed_location_tags = Array[StringName]([&"river"])
[resource]
script = ExtResource("1_fish_data")
id = &"trout_steelhead"
display_name = "steelhead trout"
+active = true
+catalog_number = 4501
+collection_group = &"Ocean-run salmonids"
+logbook_fact = "steelhead are the ocean-going form of rainbow trout. adults can leave fresh water and later return to spawn."
+available_seasons = 13
allowed_water_types = 1
rarity = 2
base_catch_weight = 0.2
diff --git a/fish/species/trumpetfish_atlantic/trumpetfish_atlantic.tres b/fish/species/trumpetfish_atlantic/trumpetfish_atlantic.tres
new file mode 100644
index 0000000..3ff3cc3
--- /dev/null
+++ b/fish/species/trumpetfish_atlantic/trumpetfish_atlantic.tres
@@ -0,0 +1,31 @@
+[gd_resource type="Resource" script_class="FishData" load_steps=5 format=3]
+
+[ext_resource type="Script" 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" path="res://fish/fish_availability.gd" id="3_availability"]
+[ext_resource type="Texture2D" path="res://art/exported/creatures/fish_placeholder.png" id="99_fish_placeholder"]
+
+[sub_resource type="Resource" id="Availability"]
+script = ExtResource("3_availability")
+allow_night = false
+
+[resource]
+script = ExtResource("1_fish_data")
+id = &"trumpetfish_atlantic"
+display_name = "atlantic trumpetfish"
+active = true
+catalog_number = 5601
+collection_group = &"Reef oddities"
+logbook_fact = "atlantic trumpetfish hover head-down among corals and sea fans. long tubular snouts expand suddenly to draw in small fish."
+available_seasons = 15
+allowed_water_types = 2
+rarity = 2
+base_catch_weight = 0.3
+catch_profile = ExtResource("2_profile")
+availability = SubResource("Availability")
+weight_min_lb = 0.1
+weight_max_lb = 2.0
+sell_value_min = 10
+sell_value_max = 24
+sell_value_curve = 0.94
+display_texture = ExtResource("99_fish_placeholder")
diff --git a/fish/species/tuna_albacore/fish_tuna_albacore.png b/fish/species/tuna_albacore/fish_tuna_albacore.png
index 3769c08..3fde5de 100644
Binary files a/fish/species/tuna_albacore/fish_tuna_albacore.png and b/fish/species/tuna_albacore/fish_tuna_albacore.png differ
diff --git a/fish/species/tuna_albacore/tuna_albacore.tres b/fish/species/tuna_albacore/tuna_albacore.tres
index 21a9155..c8cdfdf 100644
--- a/fish/species/tuna_albacore/tuna_albacore.tres
+++ b/fish/species/tuna_albacore/tuna_albacore.tres
@@ -3,7 +3,7 @@
[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"]
+[ext_resource type="Texture2D" path="res://art/exported/creatures/fish_placeholder.png" id="4_texture"]
[sub_resource type="Resource" id="AlbacoreAvailability"]
script = ExtResource("3_availability_script")
@@ -14,6 +14,11 @@ preferred_bait_weight_multiplier = 1.4
script = ExtResource("1_fish_data")
id = &"tuna_albacore"
display_name = "albacore tuna"
+active = true
+catalog_number = 4801
+collection_group = &"Oceanic tuna"
+logbook_fact = "albacore have unusually long pectoral fins. their warm muscles help them cruise across wide stretches of open ocean."
+available_seasons = 7
allowed_water_types = 2
rarity = 2
base_catch_weight = 1.5
diff --git a/fish/species/tuna_bigeye/fish_tuna_bigeye.png b/fish/species/tuna_bigeye/fish_tuna_bigeye.png
index b4a36c3..ac52961 100644
Binary files a/fish/species/tuna_bigeye/fish_tuna_bigeye.png and b/fish/species/tuna_bigeye/fish_tuna_bigeye.png differ
diff --git a/fish/species/tuna_bigeye/tuna_bigeye.tres b/fish/species/tuna_bigeye/tuna_bigeye.tres
index e5e4b7c..1c31d1a 100644
--- a/fish/species/tuna_bigeye/tuna_bigeye.tres
+++ b/fish/species/tuna_bigeye/tuna_bigeye.tres
@@ -3,7 +3,7 @@
[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"]
+[ext_resource type="Texture2D" path="res://art/exported/creatures/fish_placeholder.png" id="4_texture"]
[sub_resource type="Resource" id="BigeyeAvailability"]
script = ExtResource("3_availability_script")
@@ -14,6 +14,11 @@ preferred_bait_weight_multiplier = 1.4
script = ExtResource("1_fish_data")
id = &"tuna_bigeye"
display_name = "bigeye tuna"
+active = true
+catalog_number = 4802
+collection_group = &"Oceanic tuna"
+logbook_fact = "bigeye tuna make daily trips between deep, cool water and warmer surface layers. their large eyes suit those dim depths."
+available_seasons = 15
allowed_water_types = 2
rarity = 3
base_catch_weight = 0.55
diff --git a/fish/species/tuna_bluefin/fish_tuna_bluefin.png b/fish/species/tuna_bluefin/fish_tuna_bluefin.png
index a2b3cad..9f44ad8 100644
Binary files a/fish/species/tuna_bluefin/fish_tuna_bluefin.png and b/fish/species/tuna_bluefin/fish_tuna_bluefin.png differ
diff --git a/fish/species/tuna_bluefin/tuna_bluefin.tres b/fish/species/tuna_bluefin/tuna_bluefin.tres
index a669b5d..4bf5033 100644
--- a/fish/species/tuna_bluefin/tuna_bluefin.tres
+++ b/fish/species/tuna_bluefin/tuna_bluefin.tres
@@ -3,7 +3,7 @@
[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"]
+[ext_resource type="Texture2D" path="res://art/exported/creatures/fish_placeholder.png" id="4_texture"]
[sub_resource type="Resource" id="BluefinAvailability"]
script = ExtResource("3_availability_script")
@@ -14,6 +14,11 @@ preferred_bait_weight_multiplier = 1.4
script = ExtResource("1_fish_data")
id = &"tuna_bluefin"
display_name = "bluefin tuna"
+active = true
+catalog_number = 4803
+collection_group = &"Oceanic tuna"
+logbook_fact = "bluefin tuna are powerful ocean travelers. heat retained in their swimming muscles lets them thrive in surprisingly cold seas."
+available_seasons = 5
allowed_water_types = 2
rarity = 4
base_catch_weight = 0.18
diff --git a/fish/species/tuna_skipjack/fish_tuna_skipjack.png b/fish/species/tuna_skipjack/fish_tuna_skipjack.png
index 5a7f74d..3d0be56 100644
Binary files a/fish/species/tuna_skipjack/fish_tuna_skipjack.png and b/fish/species/tuna_skipjack/fish_tuna_skipjack.png differ
diff --git a/fish/species/tuna_skipjack/tuna_skipjack.tres b/fish/species/tuna_skipjack/tuna_skipjack.tres
index 4d9e5a8..cc1988e 100644
--- a/fish/species/tuna_skipjack/tuna_skipjack.tres
+++ b/fish/species/tuna_skipjack/tuna_skipjack.tres
@@ -3,7 +3,7 @@
[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"]
+[ext_resource type="Texture2D" path="res://art/exported/creatures/fish_placeholder.png" id="4_texture"]
[sub_resource type="Resource" id="SkipjackAvailability"]
script = ExtResource("3_availability_script")
@@ -14,6 +14,11 @@ preferred_bait_weight_multiplier = 1.4
script = ExtResource("1_fish_data")
id = &"tuna_skipjack"
display_name = "skipjack tuna"
+active = true
+catalog_number = 4804
+collection_group = &"Oceanic tuna"
+logbook_fact = "skipjack travel in fast-moving schools and have dark stripes along their lower sides. they rarely stop swimming."
+available_seasons = 15
allowed_water_types = 2
rarity = 1
base_catch_weight = 3.0
diff --git a/fish/species/tuna_yellowfin/fish_tuna_yellowfin.png b/fish/species/tuna_yellowfin/fish_tuna_yellowfin.png
index a1afc81..ad47a68 100644
Binary files a/fish/species/tuna_yellowfin/fish_tuna_yellowfin.png and b/fish/species/tuna_yellowfin/fish_tuna_yellowfin.png differ
diff --git a/fish/species/tuna_yellowfin/tuna_yellowfin.tres b/fish/species/tuna_yellowfin/tuna_yellowfin.tres
index cbd4950..665228e 100644
--- a/fish/species/tuna_yellowfin/tuna_yellowfin.tres
+++ b/fish/species/tuna_yellowfin/tuna_yellowfin.tres
@@ -3,7 +3,7 @@
[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"]
+[ext_resource type="Texture2D" path="res://art/exported/creatures/fish_placeholder.png" id="4_texture"]
[sub_resource type="Resource" id="YellowfinAvailability"]
script = ExtResource("3_availability_script")
@@ -14,6 +14,11 @@ preferred_bait_weight_multiplier = 1.4
script = ExtResource("1_fish_data")
id = &"tuna_yellowfin"
display_name = "yellowfin tuna"
+active = true
+catalog_number = 4805
+collection_group = &"Oceanic tuna"
+logbook_fact = "yellowfin are named for their bright yellow fins and finlets. large schools often gather with other open-ocean hunters."
+available_seasons = 15
allowed_water_types = 2
rarity = 2
catch_profile = ExtResource("2_profile")
diff --git a/fish/species/turbot/turbot.tres b/fish/species/turbot/turbot.tres
new file mode 100644
index 0000000..c3ab7bd
--- /dev/null
+++ b/fish/species/turbot/turbot.tres
@@ -0,0 +1,31 @@
+[gd_resource type="Resource" script_class="FishData" load_steps=5 format=3]
+
+[ext_resource type="Script" 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" path="res://fish/fish_availability.gd" id="3_availability"]
+[ext_resource type="Texture2D" path="res://art/exported/creatures/fish_placeholder.png" id="99_fish_placeholder"]
+
+[sub_resource type="Resource" id="Availability"]
+script = ExtResource("3_availability")
+require_fog = true
+
+[resource]
+script = ExtResource("1_fish_data")
+id = &"turbot"
+display_name = "turbot"
+active = true
+catalog_number = 3703
+collection_group = &"Large flatfish"
+logbook_fact = "turbot have broad, nearly circular bodies covered with bony bumps instead of scales. they bury into sand before ambushing fish and crustaceans."
+available_seasons = 13
+allowed_water_types = 2
+rarity = 2
+base_catch_weight = 0.24
+catch_profile = ExtResource("2_profile")
+availability = SubResource("Availability")
+weight_min_lb = 1.0
+weight_max_lb = 35.0
+sell_value_min = 14
+sell_value_max = 34
+sell_value_curve = 0.85
+display_texture = ExtResource("99_fish_placeholder")
diff --git a/fish/species/turtle_green/turtle_green.tres b/fish/species/turtle_green/turtle_green.tres
new file mode 100644
index 0000000..b1e8e71
--- /dev/null
+++ b/fish/species/turtle_green/turtle_green.tres
@@ -0,0 +1,32 @@
+[gd_resource type="Resource" script_class="FishData" load_steps=5 format=3]
+
+[ext_resource type="Script" 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" path="res://fish/fish_availability.gd" id="3_availability"]
+[ext_resource type="Texture2D" path="res://art/exported/creatures/fish_placeholder.png" id="99_fish_placeholder"]
+
+[sub_resource type="Resource" id="Availability"]
+script = ExtResource("3_availability")
+allow_night = false
+forbid_fog = true
+
+[resource]
+script = ExtResource("1_fish_data")
+id = &"turtle_green"
+display_name = "green sea turtle"
+active = true
+catalog_number = 6201
+collection_group = &"Sea turtles"
+logbook_fact = "green sea turtles graze seagrass meadows with finely serrated jaws. adults migrate long distances between feeding and nesting grounds."
+available_seasons = 6
+allowed_water_types = 2
+rarity = 4
+base_catch_weight = 0.018
+catch_profile = ExtResource("2_profile")
+availability = SubResource("Availability")
+weight_min_lb = 65.0
+weight_max_lb = 500.0
+sell_value_min = 62
+sell_value_max = 145
+sell_value_curve = 0.78
+display_texture = ExtResource("99_fish_placeholder")
diff --git a/fish/species/turtle_hawksbill/turtle_hawksbill.tres b/fish/species/turtle_hawksbill/turtle_hawksbill.tres
new file mode 100644
index 0000000..5b5a5c8
--- /dev/null
+++ b/fish/species/turtle_hawksbill/turtle_hawksbill.tres
@@ -0,0 +1,32 @@
+[gd_resource type="Resource" script_class="FishData" load_steps=5 format=3]
+
+[ext_resource type="Script" 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" path="res://fish/fish_availability.gd" id="3_availability"]
+[ext_resource type="Texture2D" path="res://art/exported/creatures/fish_placeholder.png" id="99_fish_placeholder"]
+
+[sub_resource type="Resource" id="Availability"]
+script = ExtResource("3_availability")
+allow_night = false
+require_rain = true
+
+[resource]
+script = ExtResource("1_fish_data")
+id = &"turtle_hawksbill"
+display_name = "hawksbill sea turtle"
+active = true
+catalog_number = 6202
+collection_group = &"Sea turtles"
+logbook_fact = "hawksbill sea turtles reach into reef crevices with narrow hooked beaks. a diet rich in sponges helps shape coral communities."
+available_seasons = 6
+allowed_water_types = 2
+rarity = 4
+base_catch_weight = 0.014
+catch_profile = ExtResource("2_profile")
+availability = SubResource("Availability")
+weight_min_lb = 75.0
+weight_max_lb = 250.0
+sell_value_min = 66
+sell_value_max = 150
+sell_value_curve = 0.8
+display_texture = ExtResource("99_fish_placeholder")
diff --git a/fish/species/turtle_leatherback/turtle_leatherback.tres b/fish/species/turtle_leatherback/turtle_leatherback.tres
new file mode 100644
index 0000000..3a8acf3
--- /dev/null
+++ b/fish/species/turtle_leatherback/turtle_leatherback.tres
@@ -0,0 +1,32 @@
+[gd_resource type="Resource" script_class="FishData" load_steps=5 format=3]
+
+[ext_resource type="Script" 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" path="res://fish/fish_availability.gd" id="3_availability"]
+[ext_resource type="Texture2D" path="res://art/exported/creatures/fish_placeholder.png" id="99_fish_placeholder"]
+
+[sub_resource type="Resource" id="Availability"]
+script = ExtResource("3_availability")
+allow_day = false
+require_fog = true
+
+[resource]
+script = ExtResource("1_fish_data")
+id = &"turtle_leatherback"
+display_name = "leatherback sea turtle"
+active = true
+catalog_number = 6203
+collection_group = &"Sea turtles"
+logbook_fact = "leatherback sea turtles cross entire oceans while hunting jellies. a flexible ridged shell and internal heat help them enter cold water."
+available_seasons = 15
+allowed_water_types = 2
+rarity = 4
+base_catch_weight = 0.012
+catch_profile = ExtResource("2_profile")
+availability = SubResource("Availability")
+weight_min_lb = 500.0
+weight_max_lb = 2000.0
+sell_value_min = 75
+sell_value_max = 175
+sell_value_curve = 0.76
+display_texture = ExtResource("99_fish_placeholder")
diff --git a/fish/species/turtle_loggerhead/turtle_loggerhead.tres b/fish/species/turtle_loggerhead/turtle_loggerhead.tres
new file mode 100644
index 0000000..e4bd898
--- /dev/null
+++ b/fish/species/turtle_loggerhead/turtle_loggerhead.tres
@@ -0,0 +1,31 @@
+[gd_resource type="Resource" script_class="FishData" load_steps=5 format=3]
+
+[ext_resource type="Script" 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" path="res://fish/fish_availability.gd" id="3_availability"]
+[ext_resource type="Texture2D" path="res://art/exported/creatures/fish_placeholder.png" id="99_fish_placeholder"]
+
+[sub_resource type="Resource" id="Availability"]
+script = ExtResource("3_availability")
+allow_night = false
+
+[resource]
+script = ExtResource("1_fish_data")
+id = &"turtle_loggerhead"
+display_name = "loggerhead sea turtle"
+active = true
+catalog_number = 6204
+collection_group = &"Sea turtles"
+logbook_fact = "loggerhead sea turtles crush crabs and mollusks with massive jaws. females often return near their birth beaches to nest."
+available_seasons = 6
+allowed_water_types = 2
+rarity = 4
+base_catch_weight = 0.016
+catch_profile = ExtResource("2_profile")
+availability = SubResource("Availability")
+weight_min_lb = 150.0
+weight_max_lb = 1200.0
+sell_value_min = 68
+sell_value_max = 155
+sell_value_curve = 0.77
+display_texture = ExtResource("99_fish_placeholder")
diff --git a/fish/species/urchin_purple/urchin_purple.tres b/fish/species/urchin_purple/urchin_purple.tres
new file mode 100644
index 0000000..6546627
--- /dev/null
+++ b/fish/species/urchin_purple/urchin_purple.tres
@@ -0,0 +1,31 @@
+[gd_resource type="Resource" script_class="FishData" load_steps=5 format=3]
+
+[ext_resource type="Script" 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" path="res://fish/fish_availability.gd" id="3_availability"]
+[ext_resource type="Texture2D" path="res://art/exported/creatures/fish_placeholder.png" id="99_fish_placeholder"]
+
+[sub_resource type="Resource" id="Availability"]
+script = ExtResource("3_availability")
+allow_night = false
+
+[resource]
+script = ExtResource("1_fish_data")
+id = &"urchin_purple"
+display_name = "purple sea urchin"
+active = true
+catalog_number = 1903
+collection_group = &"Echinoderms"
+logbook_fact = "purple sea urchins scrape algae from rocks with a five-toothed feeding structure. dense groups can clear kelp from entire reefs."
+available_seasons = 15
+allowed_water_types = 2
+rarity = 1
+base_catch_weight = 0.75
+catch_profile = ExtResource("2_profile")
+availability = SubResource("Availability")
+weight_min_lb = 0.05
+weight_max_lb = 1.0
+sell_value_min = 5
+sell_value_max = 12
+sell_value_curve = 0.96
+display_texture = ExtResource("99_fish_placeholder")
diff --git a/fish/species/wahoo/wahoo.tres b/fish/species/wahoo/wahoo.tres
new file mode 100644
index 0000000..9e045de
--- /dev/null
+++ b/fish/species/wahoo/wahoo.tres
@@ -0,0 +1,31 @@
+[gd_resource type="Resource" script_class="FishData" load_steps=5 format=3]
+
+[ext_resource type="Script" 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" path="res://fish/fish_availability.gd" id="3_availability"]
+[ext_resource type="Texture2D" path="res://art/exported/creatures/fish_placeholder.png" id="99_fish_placeholder"]
+
+[sub_resource type="Resource" id="Availability"]
+script = ExtResource("3_availability")
+allow_night = false
+
+[resource]
+script = ExtResource("1_fish_data")
+id = &"wahoo"
+display_name = "wahoo"
+active = true
+catalog_number = 4703
+collection_group = &"Oceanic pelagic fish"
+logbook_fact = "wahoo are streamlined open-ocean hunters with rows of sharp teeth. blue vertical bars flicker across their sides during fast pursuits."
+available_seasons = 6
+allowed_water_types = 2
+rarity = 3
+base_catch_weight = 0.11
+catch_profile = ExtResource("2_profile")
+availability = SubResource("Availability")
+weight_min_lb = 10.0
+weight_max_lb = 100.0
+sell_value_min = 30
+sell_value_max = 70
+sell_value_curve = 0.82
+display_texture = ExtResource("99_fish_placeholder")
diff --git a/fish/species/walleye/fish_walleye.png b/fish/species/walleye/fish_walleye.png
index ed3eb96..f4fd40a 100644
Binary files a/fish/species/walleye/fish_walleye.png and b/fish/species/walleye/fish_walleye.png differ
diff --git a/fish/species/walleye/walleye.tres b/fish/species/walleye/walleye.tres
index ce833c9..277b3d8 100644
--- a/fish/species/walleye/walleye.tres
+++ b/fish/species/walleye/walleye.tres
@@ -3,16 +3,21 @@
[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"]
+[ext_resource type="Texture2D" path="res://art/exported/creatures/fish_placeholder.png" id="4_texture"]
[sub_resource type="Resource" id="WalleyeAvailability"]
script = ExtResource("3_availability_script")
-allowed_location_tags = Array[StringName]([&"starter_pond"])
+allowed_location_tags = Array[StringName]([&"lake"])
[resource]
script = ExtResource("1_fish_data")
id = &"walleye"
display_name = "walleye"
+active = true
+catalog_number = 3306
+collection_group = &"Freshwater predators"
+logbook_fact = "walleye have light-sensitive eyes that help them hunt in murky water and around dusk. their reflective layer gives the eyes a pale glow."
+available_seasons = 13
allowed_water_types = 1
rarity = 1
base_catch_weight = 0.4
diff --git a/fish/species/walrus/walrus.tres b/fish/species/walrus/walrus.tres
new file mode 100644
index 0000000..ff74bf6
--- /dev/null
+++ b/fish/species/walrus/walrus.tres
@@ -0,0 +1,32 @@
+[gd_resource type="Resource" script_class="FishData" load_steps=5 format=3]
+
+[ext_resource type="Script" 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" path="res://fish/fish_availability.gd" id="3_availability"]
+[ext_resource type="Texture2D" path="res://art/exported/creatures/fish_placeholder.png" id="99_fish_placeholder"]
+
+[sub_resource type="Resource" id="Availability"]
+script = ExtResource("3_availability")
+allow_night = false
+require_fog = true
+
+[resource]
+script = ExtResource("1_fish_data")
+id = &"walrus"
+display_name = "walrus"
+active = true
+catalog_number = 504
+collection_group = &"Coastal marine mammals"
+logbook_fact = "walruses sweep the seafloor for clams and other soft-bodied prey. long tusks help them haul onto ice and settle disputes."
+available_seasons = 9
+allowed_water_types = 2
+rarity = 4
+base_catch_weight = 0.012
+catch_profile = ExtResource("2_profile")
+availability = SubResource("Availability")
+weight_min_lb = 800.0
+weight_max_lb = 3700.0
+sell_value_min = 74
+sell_value_max = 170
+sell_value_curve = 0.76
+display_texture = ExtResource("99_fish_placeholder")
diff --git a/fish/species/warmouth/warmouth.tres b/fish/species/warmouth/warmouth.tres
new file mode 100644
index 0000000..c58e8c5
--- /dev/null
+++ b/fish/species/warmouth/warmouth.tres
@@ -0,0 +1,32 @@
+[gd_resource type="Resource" script_class="FishData" load_steps=5 format=3]
+
+[ext_resource type="Script" 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" path="res://fish/fish_availability.gd" id="3_availability"]
+[ext_resource type="Texture2D" path="res://art/exported/creatures/fish_placeholder.png" id="99_fish_placeholder"]
+
+[sub_resource type="Resource" id="Availability"]
+script = ExtResource("3_availability")
+allow_day = false
+require_rain = true
+
+[resource]
+script = ExtResource("1_fish_data")
+id = &"warmouth"
+display_name = "warmouth"
+active = true
+catalog_number = 3106
+collection_group = &"Freshwater panfish"
+logbook_fact = "warmouth hide in weeds, roots, and murky backwaters before ambushing prey. red eyes and dark cheek stripes frame a notably large mouth."
+available_seasons = 7
+allowed_water_types = 1
+rarity = 1
+base_catch_weight = 0.8
+catch_profile = ExtResource("2_profile")
+availability = SubResource("Availability")
+weight_min_lb = 0.2
+weight_max_lb = 2.0
+sell_value_min = 5
+sell_value_max = 10
+sell_value_curve = 0.95
+display_texture = ExtResource("99_fish_placeholder")
diff --git a/fish/species/weakfish/weakfish.tres b/fish/species/weakfish/weakfish.tres
new file mode 100644
index 0000000..83a5f11
--- /dev/null
+++ b/fish/species/weakfish/weakfish.tres
@@ -0,0 +1,32 @@
+[gd_resource type="Resource" script_class="FishData" load_steps=5 format=3]
+
+[ext_resource type="Script" 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" path="res://fish/fish_availability.gd" id="3_availability"]
+[ext_resource type="Texture2D" path="res://art/exported/creatures/fish_placeholder.png" id="99_fish_placeholder"]
+
+[sub_resource type="Resource" id="Availability"]
+script = ExtResource("3_availability")
+allow_day = false
+require_fog = true
+
+[resource]
+script = ExtResource("1_fish_data")
+id = &"weakfish"
+display_name = "weakfish"
+active = true
+catalog_number = 6805
+collection_group = &"Temperate estuary"
+logbook_fact = "weakfish are named for delicate mouth tissue that tears easily around a hook. iridescent spots scatter across their bronze backs."
+available_seasons = 7
+allowed_water_types = 2
+rarity = 2
+base_catch_weight = 0.32
+catch_profile = ExtResource("2_profile")
+availability = SubResource("Availability")
+weight_min_lb = 0.5
+weight_max_lb = 18.0
+sell_value_min = 12
+sell_value_max = 29
+sell_value_curve = 0.86
+display_texture = ExtResource("99_fish_placeholder")
diff --git a/fish/species/whale_blue/whale_blue.tres b/fish/species/whale_blue/whale_blue.tres
new file mode 100644
index 0000000..415008e
--- /dev/null
+++ b/fish/species/whale_blue/whale_blue.tres
@@ -0,0 +1,32 @@
+[gd_resource type="Resource" script_class="FishData" load_steps=5 format=3]
+
+[ext_resource type="Script" 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" path="res://fish/fish_availability.gd" id="3_availability"]
+[ext_resource type="Texture2D" path="res://art/exported/creatures/fish_placeholder.png" id="99_fish_placeholder"]
+
+[sub_resource type="Resource" id="Availability"]
+script = ExtResource("3_availability")
+allow_night = false
+forbid_fog = true
+
+[resource]
+script = ExtResource("1_fish_data")
+id = &"whale_blue"
+display_name = "blue whale"
+active = true
+catalog_number = 7701
+collection_group = &"Whales and dolphins"
+logbook_fact = "blue whales are the largest animals known to have lived. pleated throats expand around vast mouthfuls of water that baleen filters for krill."
+available_seasons = 15
+allowed_water_types = 2
+rarity = 4
+base_catch_weight = 0.005
+catch_profile = ExtResource("2_profile")
+availability = SubResource("Availability")
+weight_min_lb = 100000.0
+weight_max_lb = 330000.0
+sell_value_min = 120
+sell_value_max = 300
+sell_value_curve = 0.72
+display_texture = ExtResource("99_fish_placeholder")
diff --git a/fish/species/whale_humpback/whale_humpback.tres b/fish/species/whale_humpback/whale_humpback.tres
new file mode 100644
index 0000000..7ad7161
--- /dev/null
+++ b/fish/species/whale_humpback/whale_humpback.tres
@@ -0,0 +1,32 @@
+[gd_resource type="Resource" script_class="FishData" load_steps=5 format=3]
+
+[ext_resource type="Script" 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" path="res://fish/fish_availability.gd" id="3_availability"]
+[ext_resource type="Texture2D" path="res://art/exported/creatures/fish_placeholder.png" id="99_fish_placeholder"]
+
+[sub_resource type="Resource" id="Availability"]
+script = ExtResource("3_availability")
+allow_night = false
+require_fog = true
+
+[resource]
+script = ExtResource("1_fish_data")
+id = &"whale_humpback"
+display_name = "humpback whale"
+active = true
+catalog_number = 7703
+collection_group = &"Whales and dolphins"
+logbook_fact = "humpback whales use long pectoral fins while turning and feeding. males produce layered songs that change across breeding seasons."
+available_seasons = 13
+allowed_water_types = 2
+rarity = 4
+base_catch_weight = 0.008
+catch_profile = ExtResource("2_profile")
+availability = SubResource("Availability")
+weight_min_lb = 40000.0
+weight_max_lb = 90000.0
+sell_value_min = 105
+sell_value_max = 260
+sell_value_curve = 0.73
+display_texture = ExtResource("99_fish_placeholder")
diff --git a/fish/species/whale_killer/whale_killer.tres b/fish/species/whale_killer/whale_killer.tres
new file mode 100644
index 0000000..7952089
--- /dev/null
+++ b/fish/species/whale_killer/whale_killer.tres
@@ -0,0 +1,32 @@
+[gd_resource type="Resource" script_class="FishData" load_steps=5 format=3]
+
+[ext_resource type="Script" 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" path="res://fish/fish_availability.gd" id="3_availability"]
+[ext_resource type="Texture2D" path="res://art/exported/creatures/fish_placeholder.png" id="99_fish_placeholder"]
+
+[sub_resource type="Resource" id="Availability"]
+script = ExtResource("3_availability")
+allow_night = false
+require_rain = true
+
+[resource]
+script = ExtResource("1_fish_data")
+id = &"whale_killer"
+display_name = "killer whale"
+active = true
+catalog_number = 7704
+collection_group = &"Whales and dolphins"
+logbook_fact = "killer whales live in close family groups with distinct calls and hunting traditions. different populations specialize in different prey."
+available_seasons = 13
+allowed_water_types = 2
+rarity = 4
+base_catch_weight = 0.008
+catch_profile = ExtResource("2_profile")
+availability = SubResource("Availability")
+weight_min_lb = 3000.0
+weight_max_lb = 12000.0
+sell_value_min = 95
+sell_value_max = 230
+sell_value_curve = 0.74
+display_texture = ExtResource("99_fish_placeholder")
diff --git a/fish/species/whale_sperm/whale_sperm.tres b/fish/species/whale_sperm/whale_sperm.tres
new file mode 100644
index 0000000..eef3aed
--- /dev/null
+++ b/fish/species/whale_sperm/whale_sperm.tres
@@ -0,0 +1,32 @@
+[gd_resource type="Resource" script_class="FishData" load_steps=5 format=3]
+
+[ext_resource type="Script" 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" path="res://fish/fish_availability.gd" id="3_availability"]
+[ext_resource type="Texture2D" path="res://art/exported/creatures/fish_placeholder.png" id="99_fish_placeholder"]
+
+[sub_resource type="Resource" id="Availability"]
+script = ExtResource("3_availability")
+allow_day = false
+require_fog = true
+
+[resource]
+script = ExtResource("1_fish_data")
+id = &"whale_sperm"
+display_name = "sperm whale"
+active = true
+catalog_number = 7705
+collection_group = &"Whales and dolphins"
+logbook_fact = "sperm whales make deep dives to hunt squid with powerful echolocation clicks. their enormous square heads contain a waxy spermaceti organ."
+available_seasons = 15
+allowed_water_types = 2
+rarity = 4
+base_catch_weight = 0.006
+catch_profile = ExtResource("2_profile")
+availability = SubResource("Availability")
+weight_min_lb = 15000.0
+weight_max_lb = 125000.0
+sell_value_min = 115
+sell_value_max = 280
+sell_value_curve = 0.72
+display_texture = ExtResource("99_fish_placeholder")
diff --git a/fish/species/white_perch/white_perch.tres b/fish/species/white_perch/white_perch.tres
new file mode 100644
index 0000000..5543307
--- /dev/null
+++ b/fish/species/white_perch/white_perch.tres
@@ -0,0 +1,30 @@
+[gd_resource type="Resource" script_class="FishData" load_steps=5 format=3]
+
+[ext_resource type="Script" 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" path="res://fish/fish_availability.gd" id="3_availability"]
+[ext_resource type="Texture2D" path="res://art/exported/creatures/fish_placeholder.png" id="99_fish_placeholder"]
+
+[sub_resource type="Resource" id="Availability"]
+script = ExtResource("3_availability")
+
+[resource]
+script = ExtResource("1_fish_data")
+id = &"white_perch"
+display_name = "white perch"
+active = true
+catalog_number = 6806
+collection_group = &"Temperate estuary"
+logbook_fact = "white perch tolerate fresh and brackish water along the atlantic coast. deep silver bodies gather in schools around river mouths."
+available_seasons = 7
+allowed_water_types = 3
+rarity = 1
+base_catch_weight = 1.1
+catch_profile = ExtResource("2_profile")
+availability = SubResource("Availability")
+weight_min_lb = 0.2
+weight_max_lb = 3.5
+sell_value_min = 5
+sell_value_max = 11
+sell_value_curve = 0.9
+display_texture = ExtResource("99_fish_placeholder")
diff --git a/fish/species/whitefish_lake/whitefish_lake.tres b/fish/species/whitefish_lake/whitefish_lake.tres
new file mode 100644
index 0000000..51246f5
--- /dev/null
+++ b/fish/species/whitefish_lake/whitefish_lake.tres
@@ -0,0 +1,30 @@
+[gd_resource type="Resource" script_class="FishData" load_steps=5 format=3]
+
+[ext_resource type="Script" 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" path="res://fish/fish_availability.gd" id="3_availability"]
+[ext_resource type="Texture2D" path="res://art/exported/creatures/fish_placeholder.png" id="99_fish_placeholder"]
+
+[sub_resource type="Resource" id="Availability"]
+script = ExtResource("3_availability")
+
+[resource]
+script = ExtResource("1_fish_data")
+id = &"whitefish_lake"
+display_name = "lake whitefish"
+active = true
+catalog_number = 1003
+collection_group = &"Cold freshwater fish"
+logbook_fact = "lake whitefish browse deep, cold bottoms with small downward-pointing mouths. large schools move toward shallows to spawn in autumn."
+available_seasons = 13
+allowed_water_types = 1
+rarity = 0
+base_catch_weight = 1.5
+catch_profile = ExtResource("2_profile")
+availability = SubResource("Availability")
+weight_min_lb = 0.5
+weight_max_lb = 12.0
+sell_value_min = 4
+sell_value_max = 11
+sell_value_curve = 0.9
+display_texture = ExtResource("99_fish_placeholder")
diff --git a/fish/species/wolffish_atlantic/wolffish_atlantic.tres b/fish/species/wolffish_atlantic/wolffish_atlantic.tres
new file mode 100644
index 0000000..ca67a92
--- /dev/null
+++ b/fish/species/wolffish_atlantic/wolffish_atlantic.tres
@@ -0,0 +1,31 @@
+[gd_resource type="Resource" script_class="FishData" load_steps=5 format=3]
+
+[ext_resource type="Script" 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" path="res://fish/fish_availability.gd" id="3_availability"]
+[ext_resource type="Texture2D" path="res://art/exported/creatures/fish_placeholder.png" id="99_fish_placeholder"]
+
+[sub_resource type="Resource" id="Availability"]
+script = ExtResource("3_availability")
+forbid_rain = true
+
+[resource]
+script = ExtResource("1_fish_data")
+id = &"wolffish_atlantic"
+display_name = "atlantic wolffish"
+active = true
+catalog_number = 1201
+collection_group = &"Cold reef fish"
+logbook_fact = "atlantic wolffish use heavy jaws and blunt teeth to crush shellfish and urchins. natural antifreeze proteins help them endure icy water."
+available_seasons = 13
+allowed_water_types = 2
+rarity = 2
+base_catch_weight = 0.25
+catch_profile = ExtResource("2_profile")
+availability = SubResource("Availability")
+weight_min_lb = 2.0
+weight_max_lb = 40.0
+sell_value_min = 14
+sell_value_max = 34
+sell_value_curve = 0.84
+display_texture = ExtResource("99_fish_placeholder")
diff --git a/fish/species/wolffish_spotted/wolffish_spotted.tres b/fish/species/wolffish_spotted/wolffish_spotted.tres
new file mode 100644
index 0000000..36b4575
--- /dev/null
+++ b/fish/species/wolffish_spotted/wolffish_spotted.tres
@@ -0,0 +1,31 @@
+[gd_resource type="Resource" script_class="FishData" load_steps=5 format=3]
+
+[ext_resource type="Script" 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" path="res://fish/fish_availability.gd" id="3_availability"]
+[ext_resource type="Texture2D" path="res://art/exported/creatures/fish_placeholder.png" id="99_fish_placeholder"]
+
+[sub_resource type="Resource" id="Availability"]
+script = ExtResource("3_availability")
+require_fog = true
+
+[resource]
+script = ExtResource("1_fish_data")
+id = &"wolffish_spotted"
+display_name = "spotted wolffish"
+active = true
+catalog_number = 1203
+collection_group = &"Cold reef fish"
+logbook_fact = "spotted wolffish crush hard-shelled prey with broad molar-like teeth. their pale spots stand out against a dark cold-water body."
+available_seasons = 13
+allowed_water_types = 2
+rarity = 3
+base_catch_weight = 0.11
+catch_profile = ExtResource("2_profile")
+availability = SubResource("Availability")
+weight_min_lb = 3.0
+weight_max_lb = 50.0
+sell_value_min = 26
+sell_value_max = 60
+sell_value_curve = 0.84
+display_texture = ExtResource("99_fish_placeholder")
diff --git a/fish/species/wrasse_ballan/wrasse_ballan.tres b/fish/species/wrasse_ballan/wrasse_ballan.tres
new file mode 100644
index 0000000..a1beebc
--- /dev/null
+++ b/fish/species/wrasse_ballan/wrasse_ballan.tres
@@ -0,0 +1,31 @@
+[gd_resource type="Resource" script_class="FishData" load_steps=5 format=3]
+
+[ext_resource type="Script" 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" path="res://fish/fish_availability.gd" id="3_availability"]
+[ext_resource type="Texture2D" path="res://art/exported/creatures/fish_placeholder.png" id="99_fish_placeholder"]
+
+[sub_resource type="Resource" id="Availability"]
+script = ExtResource("3_availability")
+allow_night = false
+
+[resource]
+script = ExtResource("1_fish_data")
+id = &"wrasse_ballan"
+display_name = "ballan wrasse"
+active = true
+catalog_number = 6101
+collection_group = &"Reef wrasses"
+logbook_fact = "ballan wrasse crush shellfish and sea urchins with strong throat teeth. thick lips and mottled colors suit rocky european reefs."
+available_seasons = 15
+allowed_water_types = 2
+rarity = 1
+base_catch_weight = 0.8
+catch_profile = ExtResource("2_profile")
+availability = SubResource("Availability")
+weight_min_lb = 0.2
+weight_max_lb = 10.0
+sell_value_min = 6
+sell_value_max = 15
+sell_value_curve = 0.9
+display_texture = ExtResource("99_fish_placeholder")
diff --git a/fish/species/wrasse_cleaner/wrasse_cleaner.tres b/fish/species/wrasse_cleaner/wrasse_cleaner.tres
new file mode 100644
index 0000000..85db0fc
--- /dev/null
+++ b/fish/species/wrasse_cleaner/wrasse_cleaner.tres
@@ -0,0 +1,32 @@
+[gd_resource type="Resource" script_class="FishData" load_steps=5 format=3]
+
+[ext_resource type="Script" 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" path="res://fish/fish_availability.gd" id="3_availability"]
+[ext_resource type="Texture2D" path="res://art/exported/creatures/fish_placeholder.png" id="99_fish_placeholder"]
+
+[sub_resource type="Resource" id="Availability"]
+script = ExtResource("3_availability")
+allow_night = false
+forbid_fog = true
+
+[resource]
+script = ExtResource("1_fish_data")
+id = &"wrasse_cleaner"
+display_name = "cleaner wrasse"
+active = true
+catalog_number = 6102
+collection_group = &"Reef wrasses"
+logbook_fact = "cleaner wrasses advertise with a bright stripe and dance at reef stations. larger fish wait while parasites are picked from skin and gills."
+available_seasons = 15
+allowed_water_types = 2
+rarity = 2
+base_catch_weight = 0.35
+catch_profile = ExtResource("2_profile")
+availability = SubResource("Availability")
+weight_min_lb = 0.01
+weight_max_lb = 0.2
+sell_value_min = 9
+sell_value_max = 20
+sell_value_curve = 1.0
+display_texture = ExtResource("99_fish_placeholder")
diff --git a/fish/species/wreckfish/wreckfish.tres b/fish/species/wreckfish/wreckfish.tres
new file mode 100644
index 0000000..345d274
--- /dev/null
+++ b/fish/species/wreckfish/wreckfish.tres
@@ -0,0 +1,31 @@
+[gd_resource type="Resource" script_class="FishData" load_steps=5 format=3]
+
+[ext_resource type="Script" 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" path="res://fish/fish_availability.gd" id="3_availability"]
+[ext_resource type="Texture2D" path="res://art/exported/creatures/fish_placeholder.png" id="99_fish_placeholder"]
+
+[sub_resource type="Resource" id="Availability"]
+script = ExtResource("3_availability")
+require_fog = true
+
+[resource]
+script = ExtResource("1_fish_data")
+id = &"wreckfish"
+display_name = "wreckfish"
+active = true
+catalog_number = 1606
+collection_group = &"Deep shelf fish"
+logbook_fact = "wreckfish gather around caves, ledges, and shipwrecks in deep water. powerful jaws let them ambush fish and large crustaceans."
+available_seasons = 15
+allowed_water_types = 2
+rarity = 3
+base_catch_weight = 0.1
+catch_profile = ExtResource("2_profile")
+availability = SubResource("Availability")
+weight_min_lb = 10.0
+weight_max_lb = 180.0
+sell_value_min = 31
+sell_value_max = 76
+sell_value_curve = 0.8
+display_texture = ExtResource("99_fish_placeholder")
diff --git a/fish/species/yellow_perch/yellow_perch.tres b/fish/species/yellow_perch/yellow_perch.tres
new file mode 100644
index 0000000..3e0501f
--- /dev/null
+++ b/fish/species/yellow_perch/yellow_perch.tres
@@ -0,0 +1,31 @@
+[gd_resource type="Resource" script_class="FishData" load_steps=5 format=3]
+
+[ext_resource type="Script" 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" path="res://fish/fish_availability.gd" id="3_availability"]
+[ext_resource type="Texture2D" path="res://art/exported/creatures/3201.png" id="99_fish_placeholder"]
+
+[sub_resource type="Resource" id="Availability"]
+script = ExtResource("3_availability")
+allow_night = false
+
+[resource]
+script = ExtResource("1_fish_data")
+id = &"yellow_perch"
+display_name = "yellow perch"
+active = true
+catalog_number = 3201
+collection_group = &"Freshwater perch"
+logbook_fact = "yellow perch travel in schools through cool lakes and slow rivers. dark vertical bars and orange lower fins make them easy to recognize."
+available_seasons = 13
+allowed_water_types = 1
+rarity = 0
+base_catch_weight = 2.2
+catch_profile = ExtResource("2_profile")
+availability = SubResource("Availability")
+weight_min_lb = 0.2
+weight_max_lb = 4.0
+sell_value_min = 3
+sell_value_max = 8
+sell_value_curve = 0.9
+display_texture = ExtResource("99_fish_placeholder")
diff --git a/fishing/bass_catch_profile.tres b/fishing/bass_catch_profile.tres
index 6385eec..24024a7 100644
--- a/fishing/bass_catch_profile.tres
+++ b/fishing/bass_catch_profile.tres
@@ -6,8 +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
diff --git a/fishing/carp_catch_profile.tres b/fishing/carp_catch_profile.tres
index 94a8470..e1e0efe 100644
--- a/fishing/carp_catch_profile.tres
+++ b/fishing/carp_catch_profile.tres
@@ -6,8 +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
diff --git a/fishing/catch_controller.gd b/fishing/catch_controller.gd
index 62769aa..12cc431 100644
--- a/fishing/catch_controller.gd
+++ b/fishing/catch_controller.gd
@@ -46,7 +46,7 @@ class Barrier:
# 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_DELAY: float = 1.5
const CHASE_START_OFFSET: float = 0.04
const CHASE_SPEED: float = 0.07
@@ -69,8 +69,6 @@ var _reel_input_held: bool = false
var _reel_speed: float = 0.0
var _click_power: int = 1
var _fish_quality: int = FishQualityType.Tier.BORING
-var _fish_rarity: int = 0
-var _fish_weight_percentile: float = 0.0
var _chase_delay_remaining: float = 0.0
var _failure_epsilon: float = 0.0001
var _auto_click_accumulator: float = 0.0
@@ -113,8 +111,6 @@ func start_encounter(
reel_speed: float,
click_power: int,
fish_quality: int = FishQualityType.Tier.BORING,
- fish_rarity: int = 0,
- fish_weight_percentile: float = 0.0,
) -> void:
reset()
if profile == null:
@@ -128,8 +124,6 @@ func start_encounter(
if FishQualityType.is_valid(fish_quality)
else FishQualityType.Tier.BORING
)
- _fish_rarity = clampi(fish_rarity, 0, 4)
- _fish_weight_percentile = clampf(fish_weight_percentile, 0.0, 1.0)
_chase_delay_remaining = CHASE_START_DELAY
chase_progress = -CHASE_START_OFFSET
_seed_encounter_rng()
@@ -145,8 +139,6 @@ func start_authoritative_encounter(
click_power: int,
encounter_seed_value: int,
fish_quality: int = FishQualityType.Tier.BORING,
- fish_rarity: int = 0,
- fish_weight_percentile: float = 0.0,
) -> void:
var previous_test_mode: bool = use_deterministic_test_seed
var previous_seed: int = deterministic_test_seed
@@ -157,8 +149,6 @@ func start_authoritative_encounter(
reel_speed,
click_power,
fish_quality,
- fish_rarity,
- fish_weight_percentile,
)
use_deterministic_test_seed = previous_test_mode
deterministic_test_seed = previous_seed
@@ -327,11 +317,8 @@ func _generate_barriers(profile: CatchDifficultyProfileType) -> void:
+ spacing * float(barrier_index)
+ random_offsets[barrier_index] * random_slack
)
- var health: int = FishQualityType.barrier_health_for_catch(
- _fish_quality,
- _fish_rarity,
- _fish_weight_percentile,
- _rng.randf_range(0.9, 1.1),
+ 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 bd2508c..60aa190 100644
--- a/fishing/catch_difficulty_profile.gd
+++ b/fishing/catch_difficulty_profile.gd
@@ -3,8 +3,6 @@ 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
@@ -15,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 4af3284..86c2b74 100644
--- a/fishing/common_catch_profile.tres
+++ b/fishing/common_catch_profile.tres
@@ -6,8 +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
diff --git a/fishing/fishing_context.gd b/fishing/fishing_context.gd
index b3f1acd..d9fcdb5 100644
--- a/fishing/fishing_context.gd
+++ b/fishing/fishing_context.gd
@@ -1,6 +1,8 @@
class_name FishingContext
extends RefCounted
+const CalendarSeasonType = preload("res://world/calendar_season.gd")
+
var location_tags: Array[StringName] = []
var water_type: WaterType.Type = WaterType.Type.FRESH_WATER
var active_event_tags: Array[StringName] = []
@@ -9,3 +11,4 @@ var is_night: bool = false
var is_day_night_transition: bool = false
var is_raining: bool = false
var is_foggy: bool = false
+var season: int = CalendarSeasonType.UNKNOWN
diff --git a/fishing/fishing_presentation.gd b/fishing/fishing_presentation.gd
index c38ddd6..947976c 100644
--- a/fishing/fishing_presentation.gd
+++ b/fishing/fishing_presentation.gd
@@ -1,10 +1,6 @@
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
@@ -58,7 +54,6 @@ 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
@@ -76,9 +71,6 @@ 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)
@@ -173,8 +165,6 @@ func begin_cast(
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
_begin_rod_release()
@@ -311,7 +301,6 @@ func cleanup() -> void:
_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)
@@ -329,11 +318,6 @@ func _set_cast_sample(
) -> void:
var sample: Vector3 = start.lerp(target, progress)
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
@@ -431,7 +415,7 @@ func _draw_line_ribbon(points: PackedVector3Array) -> void:
return
var half_thickness: float = line_thickness * 0.5
- _line_mesh.surface_begin(Mesh.PRIMITIVE_TRIANGLES)
+ 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]
@@ -452,12 +436,17 @@ func _draw_line_ribbon(points: PackedVector3Array) -> void:
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)
+ 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()
diff --git a/fishing/fishing_spot.gd b/fishing/fishing_spot.gd
index e0db860..fdfad53 100644
--- a/fishing/fishing_spot.gd
+++ b/fishing/fishing_spot.gd
@@ -15,6 +15,7 @@ const PlayerExperienceType = preload(
)
const ItemCatalogType = preload("res://items/item_catalog.gd")
const ItemDataType = preload("res://items/item_data.gd")
+const FishingRodDataType = preload("res://items/fishing_rod_data.gd")
const PlayerBagType = preload("res://inventory/player_bag.gd")
const PlayerHotbarType = preload("res://inventory/player_hotbar.gd")
const PlayerFishingUpgradesType = preload(
@@ -40,7 +41,6 @@ const FishingSurfaceSampleType = preload(
const FishingSurfaceResolverType = preload(
"res://fishing/fishing_surface_resolver.gd"
)
-const ArtShopStockType = preload("res://economy/art_shop_stock.gd")
const WorldTimeServiceType = preload("res://world/world_time_service.gd")
const WorldWeatherServiceType = preload(
"res://world/world_weather_service.gd"
@@ -66,6 +66,7 @@ signal showcase_changed(
)
signal bite_activated
signal bite_prompt_changed(is_visible: bool)
+signal fishing_input_priority_changed(active: bool)
signal ready_for_equipment_refresh
enum FishingState {
@@ -91,7 +92,7 @@ enum FishingState {
@export_range(1.0, 200.0, 1.0) var preview_ray_start_height: float = 100.0
@export_range(1.0, 400.0, 1.0) var preview_surface_ray_length: float = 240.0
@export_range(0.01, 1.0, 0.01) var preview_marker_vertical_offset: float = 0.06
-@export_range(0.0, 0.5, 0.01) var water_occlusion_tolerance: float = 0.04
+@export_range(0.0, 0.5, 0.01) var solid_probe_clearance: float = 0.04
@export_range(0.01, 0.5, 0.01) var solid_bobber_clearance: float = 0.13
@export_category("Withdrawal")
@@ -109,7 +110,9 @@ const BITE_QUICK_PROBABILITY: float = 0.30
const BITE_TYPICAL_PROBABILITY: float = 0.65
const BITE_LONG_PROBABILITY: float = 0.04
const NETWORK_INPUT_RESEND_INTERVAL_SECONDS: float = 0.1
+const ALTERNATE_REEL_ACTION: StringName = &"reel_alternate"
@export_range(0.1, 10.0, 0.1) var cooldown_duration: float = 1.0
+@export_range(0.1, 5.0, 0.05) var minimum_showcase_duration: float = 0.8
@export_category("Selection")
@export_range(0.0, 10.0, 0.05) var undiscovered_weight_multiplier: float = 1.5
@@ -129,6 +132,7 @@ const NETWORK_INPUT_RESEND_INTERVAL_SECONDS: float = 0.1
var state: FishingState = FishingState.READY
var _external_input_blocked: bool = false
var _gameplay_input_enabled: bool = false
+var _fishing_environment_enabled: bool = true
var _local_menu_input_owners: Dictionary[StringName, bool] = {}
var _local_player: PlayerType
var _local_inventory: FishInventoryType
@@ -145,6 +149,7 @@ var _network_session: NetworkSessionType
var _network_fishing: NetworkFishingServiceType
var _world_time: WorldTimeServiceType
var _world_weather: WorldWeatherServiceType
+var _fishable_water_regions_resolver := Callable()
var _active_player: PlayerType
var _state_time_remaining: float = 0.0
var _cast_charge: float = 0.0
@@ -170,6 +175,7 @@ var _selected_fish: FishDataType
var _pending_catch: FishCatchType
var _showcase_ready: bool = false
var _put_away_press_armed: bool = false
+var _showcase_input_lock_remaining: float = 0.0
var _showcase_restore_generation: int = 0
var _showcase_outcome_completed: bool = false
var _network_auto_click_accumulator: float = 0.0
@@ -177,6 +183,7 @@ 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 _fishing_input_priority_active: bool = false
var _bite_confirmation_requested: bool = false
@@ -215,6 +222,7 @@ func setup(
network_fishing: NetworkFishingServiceType = null,
world_time: WorldTimeServiceType = null,
world_weather: WorldWeatherServiceType = null,
+ fishable_water_regions_resolver: Callable = Callable(),
) -> void:
_local_player = local_player
_local_inventory = local_inventory
@@ -235,6 +243,7 @@ func setup(
_network_fishing = network_fishing
_world_time = world_time
_world_weather = world_weather
+ _fishable_water_regions_resolver = fishable_water_regions_resolver
if (
_network_session != null
and not _network_session.state_changed.is_connected(
@@ -383,6 +392,40 @@ func set_gameplay_input_enabled(enabled: bool) -> void:
if not enabled:
_local_menu_input_owners.clear()
_stop_reeling_audio()
+ # FishingSpot survives session and gameplay transitions. Restore the
+ # player's canonical movement state before input is disabled so a cast
+ # lock cannot follow that player into the next session.
+ reset_transient_state()
+
+
+func set_fishing_environment_enabled(enabled: bool) -> void:
+ if _fishing_environment_enabled == enabled:
+ return
+ _fishing_environment_enabled = enabled
+ if enabled:
+ _new_cast_press_armed = not Input.is_action_pressed("fish_primary")
+ return
+ if _network_fishing != null and _network_fishing.has_local_attempt():
+ _network_fishing.cancel_local_attempt("")
+ reset_transient_state()
+
+
+func is_fishing_environment_enabled() -> bool:
+ return _fishing_environment_enabled
+
+
+func reset_transient_state(restore_movement: bool = true) -> void:
+ var local_state_was_active: bool = (
+ state != FishingState.READY or _active_player != null
+ )
+ if local_state_was_active and _active_player == null and _local_player != null:
+ _local_player.set_fighting_visual(false)
+ _local_player.set_fishing_visual(false)
+ _local_player.end_catch_showcase(Callable(), true)
+ if restore_movement:
+ _local_player.set_movement_enabled(true)
+ _finalize_attempt_cleanup("", restore_movement)
+ _new_cast_press_armed = not Input.is_action_pressed("fish_primary")
func configure_accessibility_auto_click(
@@ -394,20 +437,21 @@ func configure_accessibility_auto_click(
func begin_water_recovery() -> void:
_external_input_blocked = true
- if state in [
- FishingState.AIMING_CAST,
- FishingState.CASTING,
- FishingState.WAITING_FOR_BITE,
- 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:
+ if state == FishingState.SHOWING_CATCH:
_secure_showcase_catch_for_recovery()
+ elif is_fishing_sequence_active():
+ if state in [
+ FishingState.AIMING_CAST,
+ FishingState.CASTING,
+ FishingState.WAITING_FOR_BITE,
+ FishingState.FIGHTING,
+ ]:
+ _cancel_attempt()
+ # Recovery replaces every in-flight rod return. This includes RETURNING,
+ # which can be entered by Escape a frame before the water trigger fires.
+ # Finish cleanup now so recovery never snapshots the cast movement lock.
+ if state != FishingState.READY or _active_player != null:
+ _finalize_attempt_cleanup("")
func end_water_recovery() -> void:
@@ -424,6 +468,7 @@ func _secure_showcase_catch_for_recovery() -> void:
_pending_catch = null
_showcase_ready = false
_put_away_press_armed = false
+ _showcase_input_lock_remaining = 0.0
showcase_changed.emit("", "", 0.0, 0, false)
if _active_player != null:
_active_player.end_catch_showcase(Callable(), true)
@@ -433,6 +478,7 @@ func _secure_showcase_catch_for_recovery() -> void:
func _exit_tree() -> void:
_stop_fight_audio()
_stop_reeling_audio()
+ _set_fishing_input_priority(false)
_showcase_restore_generation += 1
if (
state == FishingState.SHOWING_CATCH
@@ -453,14 +499,21 @@ func _exit_tree() -> void:
_selected_fish = null
_showcase_ready = false
_put_away_press_armed = false
+ _showcase_input_lock_remaining = 0.0
func _process(delta: float) -> void:
if state == FishingState.READY and not Input.is_action_pressed("fish_primary"):
_new_cast_press_armed = true
+ if state == FishingState.SHOWING_CATCH:
+ _showcase_input_lock_remaining = maxf(
+ _showcase_input_lock_remaining - delta,
+ 0.0,
+ )
if (
state == FishingState.SHOWING_CATCH
and _showcase_ready
+ and _showcase_input_lock_remaining <= 0.0
and not Input.is_action_pressed("fish_primary")
):
_put_away_press_armed = true
@@ -481,7 +534,7 @@ func _process(delta: float) -> void:
_get_effective_reel_speed(),
_get_effective_barrier_damage()
)
- if not Input.is_action_pressed("fish_primary"):
+ if not _is_reel_input_pressed():
_catch_controller.set_reel_input(false)
else:
_resend_network_input_state(delta)
@@ -495,51 +548,41 @@ func _process(delta: float) -> void:
func _unhandled_input(event: InputEvent) -> void:
if (
not _gameplay_input_enabled
+ or not _fishing_environment_enabled
or _external_input_blocked
or not _local_menu_input_owners.is_empty()
):
return
if _local_player == null or not _local_player.is_local_control_enabled():
return
- if not event.is_action("fish_primary"):
+ var primary_action: bool = event.is_action("fish_primary")
+ var alternate_reel: bool = event.is_action(ALTERNATE_REEL_ACTION)
+ if not primary_action and not (
+ alternate_reel and state == FishingState.FIGHTING
+ ):
+ return
+ # Fishing owns the primary action only after a rod starts a cast, or while
+ # completing an already-active fishing sequence. An empty hand, decor,
+ # gathering tools, and other items must remain available to their own world
+ # interaction controllers.
+ if state == FishingState.READY and not has_active_fishing_rod():
+ return
+ # A held keyboard key emits echo events. Treat the alternate reel exactly
+ # like a mouse button: one barrier hit per physical press, while the normal
+ # held-input path continues driving free reeling between barriers.
+ if event is InputEventKey and event.echo:
+ get_viewport().set_input_as_handled()
return
if event.is_pressed():
match state:
FishingState.READY:
if not _new_cast_press_armed:
return
- var active_item: ItemDataType = _get_active_item()
if (
- active_item != null
- 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_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():
- get_viewport().set_input_as_handled()
- return
- if (
- _is_cooler_full()
+ _is_inventory_full()
):
status_changed.emit(
- "cooler full. sell fish before casting again."
+ "inventory full. store or sell something before casting."
)
get_viewport().set_input_as_handled()
return
@@ -555,7 +598,11 @@ func _unhandled_input(event: InputEvent) -> void:
FishingPresentationType.LineMode.TAUT
)
FishingState.SHOWING_CATCH:
- if _showcase_ready and _put_away_press_armed:
+ if (
+ _showcase_ready
+ and _showcase_input_lock_remaining <= 0.0
+ and _put_away_press_armed
+ ):
_put_away_catch()
else:
return
@@ -592,15 +639,55 @@ func _unhandled_input(event: InputEvent) -> void:
_presentation.set_line_mode(FishingPresentationType.LineMode.SLACK)
get_viewport().set_input_as_handled()
elif state == FishingState.FIGHTING:
+ var reel_input_held: bool = _is_reel_input_pressed()
if _network_fishing != null:
- _network_primary_input_held = false
+ _network_primary_input_held = reel_input_held
_network_input_resend_elapsed = 0.0
- _network_fishing.submit_local_input(false, false)
+ _network_fishing.submit_local_input(reel_input_held, false)
else:
- _catch_controller.set_reel_input(false)
+ _catch_controller.set_reel_input(reel_input_held)
get_viewport().set_input_as_handled()
+func try_use_active_non_fishing_item() -> bool:
+ if (
+ not _gameplay_input_enabled
+ or _external_input_blocked
+ or not _local_menu_input_owners.is_empty()
+ or state != FishingState.READY
+ ):
+ return false
+ var active_item: ItemDataType = _get_active_item()
+ if (
+ active_item == null
+ or (
+ active_item.category != ItemDataType.Category.CONSUMABLE
+ and active_item.item_id != PlayerItemEffectsType.FISH_FINDER_ID
+ )
+ ):
+ return false
+ if _network_item_use != null:
+ _network_item_use.request_use(active_item.item_id)
+ 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
+ )
+ return true
+
+
+func _is_reel_input_pressed() -> bool:
+ return (
+ Input.is_action_pressed("fish_primary")
+ or Input.is_action_pressed(ALTERNATE_REEL_ACTION)
+ )
+
+
+func is_reel_input_active() -> bool:
+ return state == FishingState.FIGHTING and _is_reel_input_pressed()
+
+
func _begin_aiming(player: PlayerType) -> void:
if state != FishingState.READY or _active_player != null:
return
@@ -658,7 +745,7 @@ func has_active_fishing_rod() -> bool:
var item: ItemDataType = _get_active_item()
return (
item != null
- and item.is_valid()
+ and item.is_available()
and item.category == ItemDataType.Category.ROD
and item.usable
and item.equippable
@@ -676,20 +763,22 @@ func _get_active_item() -> ItemDataType:
if item_id.is_empty() or not _local_bag.owns_item(item_id):
return null
var item: ItemDataType = _item_catalog.get_item_by_id(item_id)
- return item if item != null and item.is_valid() else null
+ return item if item != null and item.is_available() else null
-func _is_cooler_full() -> bool:
+func _get_active_rod() -> FishingRodDataType:
+ return _get_active_item() as FishingRodDataType
+
+
+func _is_inventory_full() -> bool:
return (
- _cooler_capacity != null
- and _local_inventory != null
- and _local_inventory.get_all_catches().size()
- >= _cooler_capacity.get_capacity()
+ _local_inventory != null
+ and not _local_inventory.can_accept_catch()
)
func _on_cooler_availability_changed() -> void:
- if state == FishingState.READY and not _is_cooler_full():
+ if state == FishingState.READY and not _is_inventory_full():
refresh_active_item_status()
@@ -714,6 +803,11 @@ func confirm_pending_bite() -> void:
func _set_bite_confirmation_pending(is_pending: bool) -> void:
+ if is_pending:
+ # The Standby deliberately pauses before the fight begins. Keep chat and
+ # menus available during that pause; fishing only owns input once the
+ # player confirms the bite and enters the actual minigame.
+ _set_fishing_input_priority(false)
if _bite_confirmation_pending == is_pending:
if not is_pending:
_bite_confirmation_requested = false
@@ -723,15 +817,68 @@ func _set_bite_confirmation_pending(is_pending: bool) -> void:
bite_prompt_changed.emit(is_pending)
+func _set_fishing_input_priority(active: bool) -> void:
+ if _fishing_input_priority_active == active:
+ return
+ _fishing_input_priority_active = active
+ fishing_input_priority_changed.emit(active)
+
+
+func is_fishing_input_priority_active() -> bool:
+ return _fishing_input_priority_active
+
+
func is_returning() -> bool:
return state == FishingState.RETURNING
+func is_fishing_sequence_active() -> bool:
+ return state in [
+ FishingState.AIMING_CAST,
+ FishingState.CASTING,
+ FishingState.WAITING_FOR_BITE,
+ FishingState.FIGHTING,
+ FishingState.SHOWING_CATCH,
+ FishingState.RETURNING,
+ ]
+
+
func refresh_active_item_status() -> void:
if state == FishingState.READY and has_active_fishing_rod():
status_changed.emit("")
+func report_external_status(message: String) -> void:
+ status_changed.emit(message)
+
+
+func present_external_catch(fish_catch: FishCatchType) -> bool:
+ if (
+ fish_catch == null
+ or not fish_catch.is_valid()
+ or state != FishingState.READY
+ or _local_player == null
+ ):
+ return false
+ _active_player = _local_player
+ _pending_catch = fish_catch
+ _active_player.set_movement_enabled(false)
+ state = FishingState.SHOWING_CATCH
+ _showcase_ready = true
+ _showcase_outcome_completed = true
+ _put_away_press_armed = false
+ _showcase_input_lock_remaining = minimum_showcase_duration
+ _active_player.begin_catch_showcase(fish_catch)
+ showcase_changed.emit(
+ fish_catch.fish.display_name,
+ fish_catch.fish.get_rarity_name(),
+ fish_catch.weight_lb,
+ fish_catch.quality,
+ true,
+ )
+ return true
+
+
func _update_cast_charge(delta: float) -> void:
if state != FishingState.AIMING_CAST:
return
@@ -829,7 +976,9 @@ func _on_cast_completed() -> void:
_selected_water_region = get_fishable_water_region(_cast_target)
_selection_context = _build_fishing_context(_selected_water_region)
+ var active_rod: FishingRodDataType = _get_active_rod()
_fish_selector.undiscovered_weight_multiplier = undiscovered_weight_multiplier
+ _fish_selector.active_rod = active_rod
_fish_selector.rarity_weight_multipliers = []
for rarity: int in range(FishDataType.Rarity.size()):
_fish_selector.rarity_weight_multipliers.append(
@@ -848,7 +997,6 @@ 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)
@@ -856,6 +1004,10 @@ func _on_cast_completed() -> void:
_item_effects.get_bite_time_multiplier()
if _item_effects != null
else 1.0
+ ) * (
+ active_rod.bite_time_multiplier
+ if active_rod != null
+ else 1.0
)
_withdrawal_progress = 0.0
_withdrawal_input_held = false
@@ -868,14 +1020,16 @@ 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:
+func _consume_active_bait() -> bool:
if _active_player == null or _active_player.active_bait_id.is_empty():
- return
+ return true
if _local_bag == null or not _local_bag.remove_item(_active_player.active_bait_id, 1):
_active_player.unequip_bait()
+ return false
+ return true
func roll_bite_wait_time() -> float:
@@ -1026,15 +1180,20 @@ func _activate_bite(confirmation_override: bool = false) -> void:
_presentation.show_bite()
return
+ _set_fishing_input_priority(true)
_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
+ if not _consume_active_bait():
+ _pending_catch = null
+ _cleanup_attempt("No bait available.", &"cancel")
+ return
+ state = FishingState.FIGHTING
+ _active_player.set_fighting_visual(true)
+ _state_time_remaining = 0.0
+ _withdrawal_input_held = false
_start_fight_audio()
bite_activated.emit()
status_changed.emit("fish on!")
@@ -1045,11 +1204,9 @@ func _activate_bite(confirmation_override: bool = false) -> void:
_get_effective_reel_speed(),
_get_effective_barrier_damage(),
_pending_catch.quality,
- int(_pending_catch.fish.rarity),
- _pending_catch.fish.get_weight_percentile(_pending_catch.weight_lb),
)
_catch_controller.set_reel_input(
- Input.is_action_pressed("fish_primary")
+ _is_reel_input_pressed()
)
_presentation.show_bite()
@@ -1057,6 +1214,7 @@ func _activate_bite(confirmation_override: bool = false) -> void:
func _get_effective_reel_speed() -> float:
if _active_player == null:
return 0.0
+ var rod: FishingRodDataType = _get_active_rod()
return _active_player.reel_speed * (
_fishing_upgrades.get_reel_speed_multiplier()
if _fishing_upgrades != null
@@ -1065,13 +1223,16 @@ func _get_effective_reel_speed() -> float:
_item_effects.get_reel_multiplier()
if _item_effects != null
else 1.0
+ ) * (
+ rod.reel_speed_multiplier if rod != null else 1.0
)
func _get_effective_barrier_damage() -> int:
if _active_player == null:
return 1
- return (
+ var rod: FishingRodDataType = _get_active_rod()
+ var damage: int = (
_fishing_upgrades.get_barrier_damage()
if _fishing_upgrades != null
else _active_player.click_power
@@ -1080,6 +1241,13 @@ func _get_effective_barrier_damage() -> int:
if _item_effects != null
else 0
)
+ return maxi(
+ roundi(
+ float(damage)
+ * (rod.barrier_power_multiplier if rod != null else 1.0)
+ ),
+ 1,
+ )
func _on_catch_encounter_updated(
@@ -1123,6 +1291,7 @@ func _on_catch_completed() -> void:
_showcase_ready = false
_showcase_outcome_completed = false
_put_away_press_armed = false
+ _showcase_input_lock_remaining = 0.0
_catch_controller.reset()
_presentation.set_line_mode(FishingPresentationType.LineMode.TAUT)
_presentation.play_outcome(&"catch")
@@ -1133,10 +1302,7 @@ func _on_catch_escaped() -> void:
return
_stop_fight_audio()
- 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")
+ _cleanup_attempt("", &"escape")
func _on_outcome_completed(outcome: StringName) -> void:
@@ -1161,6 +1327,7 @@ func _on_outcome_completed(outcome: StringName) -> void:
return
_showcase_outcome_completed = true
_showcase_ready = true
+ _showcase_input_lock_remaining = minimum_showcase_duration
_active_player.begin_catch_showcase(_pending_catch)
showcase_changed.emit(
_pending_catch.fish.display_name,
@@ -1194,6 +1361,7 @@ func _put_away_catch() -> void:
_showcase_ready = false
_showcase_outcome_completed = false
_put_away_press_armed = false
+ _showcase_input_lock_remaining = 0.0
showcase_changed.emit("", "", 0.0, 0, false)
_showcase_restore_generation += 1
var restore_generation: int = _showcase_restore_generation
@@ -1221,7 +1389,7 @@ func _store_catch_progression(fish_catch: FishCatchType) -> void:
)
)
_local_inventory.add_catch(fish_catch)
- _local_collection_log.mark_quality_discovered(
+ _local_collection_log.record_catch(
fish_catch.fish_id,
fish_catch.quality,
)
@@ -1267,6 +1435,7 @@ func _cleanup_attempt(
_showcase_ready = false
_showcase_outcome_completed = false
_put_away_press_armed = false
+ _showcase_input_lock_remaining = 0.0
showcase_changed.emit("", "", 0.0, 0, false)
_catch_controller.reset()
state = FishingState.RETURNING
@@ -1284,13 +1453,18 @@ func _cleanup_attempt(
_finalize_attempt_cleanup(cooldown_message)
-func _finalize_attempt_cleanup(cooldown_message: String) -> void:
+func _finalize_attempt_cleanup(
+ cooldown_message: String,
+ restore_movement: bool = true,
+) -> void:
+ _set_bite_confirmation_pending(false)
_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)
+ if restore_movement:
+ _active_player.set_movement_enabled(true)
_active_player = null
_state_time_remaining = 0.0
_cast_charge = 0.0
@@ -1312,6 +1486,7 @@ func _finalize_attempt_cleanup(cooldown_message: String) -> void:
_showcase_ready = false
_showcase_outcome_completed = false
_put_away_press_armed = false
+ _showcase_input_lock_remaining = 0.0
showcase_changed.emit("", "", 0.0, 0, false)
_catch_controller.reset()
_presentation.cleanup()
@@ -1327,6 +1502,7 @@ func _finalize_attempt_cleanup(cooldown_message: String) -> void:
_state_time_remaining = cooldown_duration
_cooldown_status = cooldown_message
status_changed.emit(_cooldown_status)
+ _set_fishing_input_priority(false)
func _return_to_ready() -> void:
@@ -1335,6 +1511,7 @@ func _return_to_ready() -> void:
_cooldown_status = ""
status_changed.emit("")
ready_for_equipment_refresh.emit()
+ _set_fishing_input_priority(false)
func _cancel_attempt() -> void:
@@ -1402,7 +1579,6 @@ func _configure_surface_resolver() -> void:
_surface_resolver.query_radius = fishable_query_radius
_surface_resolver.ray_start_height = preview_ray_start_height
_surface_resolver.ray_length = preview_surface_ray_length
- _surface_resolver.water_occlusion_tolerance = water_occlusion_tolerance
func resolve_fishing_surface(
@@ -1424,7 +1600,7 @@ func resolve_fishing_surface(
get_world_3d().direct_space_state,
target,
resolved_reference_y,
- water_occlusion_tolerance,
+ solid_probe_clearance,
)
@@ -1498,6 +1674,7 @@ func _build_fishing_context(
if _world_time != null:
context.is_night = _world_time.is_night_period()
context.is_day_night_transition = _world_time.is_transition()
+ context.season = _world_time.get_season()
if _world_weather != null:
context.is_raining = _world_weather.is_raining()
context.is_foggy = _world_weather.is_foggy()
@@ -1510,6 +1687,47 @@ func build_network_context(
return _build_fishing_context(region)
+func is_species_available_now(fish: FishDataType) -> bool:
+ if fish == null or not fish.is_selectable():
+ return false
+ var season := (
+ _world_time.get_season()
+ if _world_time != null
+ else CalendarSeason.UNKNOWN
+ )
+ if not fish.is_available_in_season(season):
+ return false
+ if fish.collection_method != FishDataType.CollectionMethod.FISHING:
+ return true
+ for region: FishableWaterRegionType in _get_logbook_water_regions():
+ if (
+ region == null
+ or region.fish_pool == null
+ or region.fish_pool.get_fish_by_id(fish.id) == null
+ ):
+ continue
+ var context := _build_fishing_context(region)
+ if not fish.is_allowed_in_water(context.water_type):
+ continue
+ if fish.availability == null or fish.availability.is_available(context):
+ return true
+ return false
+
+
+func _get_logbook_water_regions() -> Array[FishableWaterRegionType]:
+ var regions: Array[FishableWaterRegionType] = []
+ if not _fishable_water_regions_resolver.is_valid():
+ return regions
+ var resolved: Variant = _fishable_water_regions_resolver.call()
+ if resolved is not Array:
+ return regions
+ for value: Variant in resolved:
+ var region := value as FishableWaterRegionType
+ if region != null and is_instance_valid(region):
+ regions.append(region)
+ return regions
+
+
func _get_active_bait_tags() -> Array[StringName]:
if _local_player == null or _item_catalog == null:
return []
@@ -1517,7 +1735,11 @@ func _get_active_bait_tags() -> Array[StringName]:
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 []
+ return (
+ item.bait_tags.duplicate()
+ if item != null and item.is_available() and item.is_bait()
+ else []
+ )
func _active_lure_has_effect(effect_id: StringName) -> bool:
@@ -1532,7 +1754,12 @@ func _active_lure_has_effect(effect_id: StringName) -> bool:
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
+ return (
+ lure != null
+ and lure.is_available()
+ and lure.is_lure()
+ and effect_id in lure.lure_effects
+ )
func _build_network_evidence() -> Dictionary:
@@ -1559,7 +1786,7 @@ func _build_network_evidence() -> Dictionary:
"bite_multiplier": 1.0,
"rarity_multipliers": rarity_multipliers,
"discovered_fish_ids": discovered_ids,
- "capacity_available": not _is_cooler_full(),
+ "capacity_available": not _is_inventory_full(),
}
@@ -1599,7 +1826,7 @@ func _on_network_cast_accepted(
_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:
@@ -1635,12 +1862,13 @@ func _on_network_bite_started(_attempt_id: String) -> void:
if state != FishingState.WAITING_FOR_BITE:
return
_stop_reeling_audio()
+ _set_fishing_input_priority(true)
_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_primary_input_held = _is_reel_input_pressed()
_network_input_resend_elapsed = 0.0
_network_auto_click_accumulator = 0.0
_network_active_barrier_index = -1
@@ -1712,7 +1940,7 @@ func _on_network_fishing_snapshot(snapshot: Dictionary) -> void:
func _update_network_auto_click(delta: float) -> void:
if (
not _catch_controller.auto_click_enabled
- or not Input.is_action_pressed("fish_primary")
+ or not _is_reel_input_pressed()
or _network_active_barrier_index < 0
):
_network_auto_click_accumulator = 0.0
@@ -1739,6 +1967,7 @@ func _on_network_catch_received(fish_catch: FishCatchType) -> void:
_showcase_ready = false
_showcase_outcome_completed = false
_put_away_press_armed = false
+ _showcase_input_lock_remaining = 0.0
catch_display_changed.emit(
0.0, 0.0, PackedFloat32Array(), PackedInt32Array(),
PackedInt32Array(), -1, false
@@ -1776,6 +2005,7 @@ func _on_network_session_state_changed(state_value: NetworkSession.State) -> voi
]:
_stop_fight_audio()
_stop_reeling_audio()
+ reset_transient_state()
func _start_fight_audio() -> void:
diff --git a/fishing/fishing_surface_resolver.gd b/fishing/fishing_surface_resolver.gd
index 821d8cc..c439aac 100644
--- a/fishing/fishing_surface_resolver.gd
+++ b/fishing/fishing_surface_resolver.gd
@@ -13,7 +13,6 @@ var solid_surface_mask: int = 1
var query_radius: float = 0.08
var ray_start_height: float = 100.0
var ray_length: float = 240.0
-var water_occlusion_tolerance: float = 0.04
var arc_sample_spacing: float = 0.3
var _water_query_shape: CylinderShape3D = CylinderShape3D.new()
@@ -52,9 +51,9 @@ func resolve_surface(
var solid_is_above_water: bool = false
if not solid_hit.is_empty():
var solid_position: Vector3 = solid_hit["position"]
- solid_is_above_water = (
- solid_position.y >= water_height - water_occlusion_tolerance
- )
+ # The water body's authored height is the exact shoreline authority:
+ # submerged terrain is fishable and terrain at or above it is dry.
+ solid_is_above_water = solid_position.y >= water_height
if not solid_is_above_water:
sample.has_surface = true
sample.position = Vector3(
diff --git a/fishing/fishing_surface_sample.gd b/fishing/fishing_surface_sample.gd
index 121658b..4d2dd2c 100644
--- a/fishing/fishing_surface_sample.gd
+++ b/fishing/fishing_surface_sample.gd
@@ -4,9 +4,6 @@ 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
@@ -22,12 +19,7 @@ func is_fishable() -> bool:
func get_marker_position(vertical_offset: float) -> Vector3:
if not has_surface:
return position
- var presentation_offset := vertical_offset
- if is_water_surface:
- presentation_offset += (
- WaterSurfaceMotionType.get_default_height_offset()
- )
- return position + normal.normalized() * presentation_offset
+ return position + normal.normalized() * vertical_offset
func get_bobber_position(solid_clearance: float) -> Vector3:
diff --git a/fishing/remote_fishing_presentation.gd b/fishing/remote_fishing_presentation.gd
index 5a5b05b..380afdf 100644
--- a/fishing/remote_fishing_presentation.gd
+++ b/fishing/remote_fishing_presentation.gd
@@ -1,9 +1,6 @@
class_name RemoteFishingPresentation
extends Node3D
-const WaterSurfaceMotionType = preload(
- "res://world/water_surface_motion.gd"
-)
const LINE_THICKNESS: float = 0.016
signal return_completed
@@ -21,6 +18,8 @@ var _showcase_tween: Tween
var _return_showcase_catch: FishCatch
var _bobber_idle_elapsed: float = 0.0
var _bobber_base_scale: Vector3 = Vector3.ONE
+var _attempt_id: String = ""
+var _observer_fighting_active: bool = false
func setup(owning_player: Player) -> void:
@@ -46,7 +45,11 @@ func setup(owning_player: Player) -> void:
cleanup()
-func show_cast(origin: Vector3, target: Vector3) -> void:
+func show_cast(
+ origin: Vector3,
+ target: Vector3,
+ attempt_id: String = "",
+) -> void:
if (
_owner == null
or not origin.is_finite()
@@ -55,6 +58,8 @@ func show_cast(origin: Vector3, target: Vector3) -> void:
return
_kill_cast_tween()
_active = true
+ _attempt_id = attempt_id
+ _observer_fighting_active = false
_bobber_base_scale = Vector3.ONE * _owner.get_character_visual_scale()
_target = origin
_pending_target = target
@@ -89,10 +94,48 @@ func update_bobber(world_position: Vector3) -> void:
_redraw_line()
+func synchronize_active(
+ attempt_id: String,
+ world_position: Vector3,
+ fighting: bool,
+) -> void:
+ if (
+ _owner == null
+ or attempt_id.is_empty()
+ or not world_position.is_finite()
+ ):
+ return
+ if _active and not _attempt_id.is_empty() and _attempt_id != attempt_id:
+ cleanup()
+ if _attempt_id.is_empty():
+ _attempt_id = attempt_id
+ if not _active:
+ _active = true
+ _bobber_base_scale = Vector3.ONE * _owner.get_character_visual_scale()
+ _target = world_position
+ _pending_target = world_position
+ _bobber_idle_elapsed = 0.0
+ _bobber.global_position = world_position
+ _bobber.scale = _bobber_base_scale
+ _bobber.visible = true
+ _line.visible = true
+ _owner.set_active_item_is_rod(true)
+ _owner.set_fishing_visual(true)
+ _redraw_line()
+ else:
+ update_bobber(world_position)
+ if fighting != _observer_fighting_active:
+ _observer_fighting_active = fighting
+ _owner.set_fighting_visual(fighting)
+ if not fighting:
+ _owner.set_fishing_visual(true)
+
+
func show_bite() -> void:
if not _active:
return
if _owner != null and is_instance_valid(_owner):
+ _observer_fighting_active = true
_owner.set_fighting_visual(true)
var tween: Tween = create_tween()
tween.tween_property(_bobber, "scale", _bobber_base_scale * 0.7, 0.08)
@@ -134,6 +177,8 @@ func cleanup() -> void:
_kill_return_tween()
_kill_showcase_tween()
_active = false
+ _attempt_id = ""
+ _observer_fighting_active = false
_pending_target = Vector3.ZERO
_return_showcase_catch = null
_bobber_idle_elapsed = 0.0
@@ -157,10 +202,6 @@ 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
@@ -175,10 +216,7 @@ func _on_cast_finished() -> void:
func _apply_bobber_idle_motion() -> void:
var phase: float = _bobber_idle_elapsed * 0.7 * TAU
- _target = _pending_target + Vector3.UP * (
- WaterSurfaceMotionType.get_default_height_offset()
- + sin(phase) * 0.035
- )
+ _target = _pending_target + Vector3.UP * sin(phase) * 0.035
_bobber.global_position = _target
_bobber.rotation.z = deg_to_rad(4.0) * sin(phase * 0.73)
diff --git a/gathering/catalog/beetle_stag_common.tres b/gathering/catalog/beetle_stag_common.tres
new file mode 100644
index 0000000..c8b7d85
--- /dev/null
+++ b/gathering/catalog/beetle_stag_common.tres
@@ -0,0 +1,28 @@
+[gd_resource type="Resource" script_class="GatherableData" load_steps=3 format=3]
+
+[ext_resource type="Script" path="res://gathering/gatherable_data.gd" id="1_data"]
+[ext_resource type="Resource" path="res://fish/species/beetle_stag_common/beetle_stag_common.tres" id="2_catch"]
+
+[resource]
+script = ExtResource("1_data")
+type_id = &"beetle_stag_common"
+catch_data = ExtResource("2_catch")
+required_tool_id = &"crab_net"
+spawn_anchor_set_id = &"starter_reachable_tree_trunks"
+population = 3
+spawn_anchor_occupancy_ratio = 0.35
+maximum_anchor_population = 64
+requires_sneaking = false
+movement_speed = 0.0
+roam_radius = 0.1
+scare_radius = 0.1
+capture_radius = 0.34
+interaction_range = 2.8
+charge_duration = 1.0
+sprite_pixel_size = 0.0075
+sprite_tilt_degrees = 0.0
+capture_respawn_min_seconds = 90.0
+capture_respawn_max_seconds = 150.0
+scare_respawn_min_seconds = 0.0
+scare_respawn_max_seconds = 0.0
+minimum_respawn_spacing_seconds = 30.0
diff --git a/gathering/catalog/clam_manila.tres b/gathering/catalog/clam_manila.tres
new file mode 100644
index 0000000..69a9c89
--- /dev/null
+++ b/gathering/catalog/clam_manila.tres
@@ -0,0 +1,27 @@
+[gd_resource type="Resource" script_class="GatherableData" load_steps=3 format=3]
+
+[ext_resource type="Script" path="res://gathering/gatherable_data.gd" id="1_data"]
+[ext_resource type="Resource" path="res://fish/species/clam_manila/clam_manila.tres" id="2_catch"]
+
+[resource]
+script = ExtResource("1_data")
+type_id = &"clam_manila"
+catch_data = ExtResource("2_catch")
+required_tool_id = &"standard_shovel"
+diggable_area_id = &"starter_beach"
+minimum_surface_y = -0.44
+population = 3
+presentation_mode = 1
+requires_sneaking = false
+active_lifetime_seconds = 10.0
+movement_speed = 0.0
+roam_radius = 0.1
+scare_radius = 0.1
+capture_radius = 0.38
+interaction_range = 2.4
+charge_duration = 0.55
+capture_respawn_min_seconds = 8.0
+capture_respawn_max_seconds = 14.0
+scare_respawn_min_seconds = 0.0
+scare_respawn_max_seconds = 0.0
+minimum_respawn_spacing_seconds = 4.0
diff --git a/gathering/catalog/crab_blue.tres b/gathering/catalog/crab_blue.tres
new file mode 100644
index 0000000..705948e
--- /dev/null
+++ b/gathering/catalog/crab_blue.tres
@@ -0,0 +1,17 @@
+[gd_resource type="Resource" script_class="GatherableData" load_steps=3 format=3]
+
+[ext_resource type="Script" path="res://gathering/gatherable_data.gd" id="1_data"]
+[ext_resource type="Resource" path="res://fish/species/crab_blue/crab_blue.tres" id="2_catch"]
+
+[resource]
+script = ExtResource("1_data")
+type_id = &"crab_blue"
+catch_data = ExtResource("2_catch")
+required_tool_id = &"crab_net"
+surface_materials = Array[StringName]([&"sand"])
+minimum_surface_y = -0.8
+surface_water_relation = 2
+population = 4
+surface_population_per_100_square_meters = 0.4
+maximum_surface_population = 16
+sprite_pixel_size = 0.0005
diff --git a/gathering/catalog/crab_brown.tres b/gathering/catalog/crab_brown.tres
new file mode 100644
index 0000000..b27bb7e
--- /dev/null
+++ b/gathering/catalog/crab_brown.tres
@@ -0,0 +1,29 @@
+[gd_resource type="Resource" script_class="GatherableData" load_steps=3 format=3]
+
+[ext_resource type="Script" path="res://gathering/gatherable_data.gd" id="1_data"]
+[ext_resource type="Resource" path="res://fish/species/crab_brown/crab_brown.tres" id="2_catch"]
+
+[resource]
+script = ExtResource("1_data")
+type_id = &"crab_brown"
+catch_data = ExtResource("2_catch")
+required_tool_id = &"crab_net"
+surface_materials = Array[StringName]([&"sand"])
+minimum_surface_y = -0.44
+surface_water_relation = 1
+population = 2
+surface_population_per_100_square_meters = 0.15
+maximum_surface_population = 24
+movement_speed = 0.35
+roam_radius = 3.5
+scare_radius = 2.8
+capture_radius = 0.7
+interaction_range = 2.6
+charge_duration = 2.0
+sprite_pixel_size = 0.0005
+sprite_tilt_degrees = -45.0
+capture_respawn_min_seconds = 480.0
+capture_respawn_max_seconds = 720.0
+scare_respawn_min_seconds = 45.0
+scare_respawn_max_seconds = 90.0
+minimum_respawn_spacing_seconds = 180.0
diff --git a/gathering/catalog/crab_dungeness.tres b/gathering/catalog/crab_dungeness.tres
new file mode 100644
index 0000000..12eb7a6
--- /dev/null
+++ b/gathering/catalog/crab_dungeness.tres
@@ -0,0 +1,14 @@
+[gd_resource type="Resource" script_class="GatherableData" load_steps=3 format=3]
+
+[ext_resource type="Script" path="res://gathering/gatherable_data.gd" id="1_data"]
+[ext_resource type="Resource" path="res://fish/species/crab_dungeness/crab_dungeness.tres" id="2_catch"]
+
+[resource]
+script = ExtResource("1_data")
+type_id = &"crab_dungeness"
+catch_data = ExtResource("2_catch")
+required_tool_id = &"crab_net"
+surface_materials = Array[StringName]([&"sand"])
+minimum_surface_y = -0.5
+population = 4
+sprite_pixel_size = 0.0005
diff --git a/gathering/catalog/crab_ghost.tres b/gathering/catalog/crab_ghost.tres
new file mode 100644
index 0000000..62ce103
--- /dev/null
+++ b/gathering/catalog/crab_ghost.tres
@@ -0,0 +1,14 @@
+[gd_resource type="Resource" script_class="GatherableData" load_steps=3 format=3]
+
+[ext_resource type="Script" path="res://gathering/gatherable_data.gd" id="1_data"]
+[ext_resource type="Resource" path="res://fish/species/crab_ghost/crab_ghost.tres" id="2_catch"]
+
+[resource]
+script = ExtResource("1_data")
+type_id = &"crab_ghost"
+catch_data = ExtResource("2_catch")
+required_tool_id = &"crab_net"
+surface_materials = Array[StringName]([&"sand"])
+minimum_surface_y = 0.08
+population = 4
+sprite_pixel_size = 0.0005
diff --git a/gathering/catalog/gatherable_catalog.tres b/gathering/catalog/gatherable_catalog.tres
new file mode 100644
index 0000000..4ea1ff4
--- /dev/null
+++ b/gathering/catalog/gatherable_catalog.tres
@@ -0,0 +1,13 @@
+[gd_resource type="Resource" script_class="GatherableCatalog" load_steps=8 format=3]
+
+[ext_resource type="Script" path="res://gathering/gatherable_catalog.gd" id="1_catalog"]
+[ext_resource type="Resource" path="res://gathering/catalog/crab_brown.tres" id="2_brown"]
+[ext_resource type="Resource" path="res://gathering/catalog/crab_ghost.tres" id="3_ghost"]
+[ext_resource type="Resource" path="res://gathering/catalog/crab_blue.tres" id="4_blue"]
+[ext_resource type="Resource" path="res://gathering/catalog/crab_dungeness.tres" id="5_dungeness"]
+[ext_resource type="Resource" path="res://gathering/catalog/clam_manila.tres" id="6_clam"]
+[ext_resource type="Resource" path="res://gathering/catalog/beetle_stag_common.tres" id="7_beetle"]
+
+[resource]
+script = ExtResource("1_catalog")
+entries = [ExtResource("2_brown"), ExtResource("3_ghost"), ExtResource("4_blue"), ExtResource("5_dungeness"), ExtResource("6_clam"), ExtResource("7_beetle")]
diff --git a/gathering/gatherable_catalog.gd b/gathering/gatherable_catalog.gd
new file mode 100644
index 0000000..3fbb06a
--- /dev/null
+++ b/gathering/gatherable_catalog.gd
@@ -0,0 +1,26 @@
+class_name GatherableCatalog
+extends Resource
+
+const GatherableDataType = preload("res://gathering/gatherable_data.gd")
+const CalendarSeasonType = preload("res://world/calendar_season.gd")
+
+@export var entries: Array[GatherableDataType] = []
+
+
+func get_entry(type_id: StringName) -> GatherableDataType:
+ if type_id.is_empty():
+ return null
+ for entry: GatherableDataType in entries:
+ if entry != null and entry.type_id == type_id:
+ return entry
+ return null
+
+
+func get_available_entries(
+ season: int = CalendarSeasonType.UNKNOWN,
+) -> Array[GatherableDataType]:
+ var available: Array[GatherableDataType] = []
+ for entry: GatherableDataType in entries:
+ if entry != null and entry.is_available(season):
+ available.append(entry)
+ return available
diff --git a/gathering/gatherable_catalog.gd.uid b/gathering/gatherable_catalog.gd.uid
new file mode 100644
index 0000000..7f74b07
--- /dev/null
+++ b/gathering/gatherable_catalog.gd.uid
@@ -0,0 +1 @@
+uid://bpsncwh7qlfk1
diff --git a/gathering/gatherable_data.gd b/gathering/gatherable_data.gd
new file mode 100644
index 0000000..41de7fe
--- /dev/null
+++ b/gathering/gatherable_data.gd
@@ -0,0 +1,221 @@
+class_name GatherableData
+extends Resource
+
+const FishDataType = preload("res://fish/fish_data.gd")
+const FishQualityType = preload("res://fish/fish_quality.gd")
+const CalendarSeasonType = preload("res://world/calendar_season.gd")
+
+enum PresentationMode {
+ VISIBLE_CREATURE,
+ WATER_SPURT,
+}
+
+enum SurfaceWaterRelation {
+ ANY,
+ ABOVE_SALT_WATER,
+ BELOW_SALT_WATER,
+}
+
+@export var type_id: StringName
+@export var catch_data: FishDataType
+@export var required_tool_id: StringName
+@export var surface_materials: Array[StringName] = []
+@export var diggable_area_id: StringName
+@export var spawn_anchor_set_id: StringName
+@export_range(-100.0, 100.0, 0.01) var minimum_surface_y: float = 0.08
+@export_range(-100.0, 100.0, 0.01) var maximum_surface_y: float = 100.0
+@export var surface_water_relation := SurfaceWaterRelation.ANY
+@export_range(0.0, 2.0, 0.01) var waterline_margin: float = 0.01
+@export_range(0, 64, 1) var population: int = 0
+## Anchored gatherables can scale with authored/generated attachment geometry.
+## Zero preserves the fixed population above.
+@export_range(0.0, 1.0, 0.01) var spawn_anchor_occupancy_ratio := 0.0
+## Zero leaves the anchor count as the only ceiling.
+@export_range(0, 256, 1) var maximum_anchor_population := 0
+## Optional density scaling for large generated surfaces. The authored
+## population remains the minimum while the maximum keeps large maps bounded.
+@export_range(0.0, 8.0, 0.01)
+var surface_population_per_100_square_meters := 0.0
+@export_range(0, 256, 1) var maximum_surface_population := 0
+@export var presentation_mode: PresentationMode = PresentationMode.VISIBLE_CREATURE
+@export var requires_sneaking: bool = true
+@export_range(0.0, 120.0, 0.1) var active_lifetime_seconds: float = 0.0
+@export_range(0.0, 10.0, 0.05) var movement_speed: float = 0.35
+@export_range(0.1, 20.0, 0.1) var roam_radius: float = 3.5
+@export_range(0.1, 20.0, 0.1) var scare_radius: float = 2.8
+@export var quality_movement_speed_multipliers: Array[float] = [
+ 1.0,
+ 1.15,
+ 1.35,
+ 1.6,
+ 2.0,
+]
+@export var quality_scare_radius_multipliers: Array[float] = [
+ 1.0,
+ 1.1,
+ 1.25,
+ 1.45,
+ 1.7,
+]
+@export_range(0.1, 5.0, 0.05) var capture_radius: float = 0.7
+@export_range(0.1, 10.0, 0.05) var interaction_range: float = 2.6
+@export_range(0.1, 10.0, 0.05) var charge_duration: float = 2.0
+@export_range(0.0001, 0.01, 0.00005) var sprite_pixel_size: float = 0.001
+@export_range(-90.0, 90.0, 1.0) var sprite_tilt_degrees: float = -45.0
+@export_category("Respawn Budget")
+@export_range(0.0, 3600.0, 1.0) var capture_respawn_min_seconds: float = 480.0
+@export_range(0.0, 3600.0, 1.0) var capture_respawn_max_seconds: float = 720.0
+@export_range(0.0, 3600.0, 1.0) var scare_respawn_min_seconds: float = 45.0
+@export_range(0.0, 3600.0, 1.0) var scare_respawn_max_seconds: float = 90.0
+@export_range(0.0, 3600.0, 1.0) var minimum_respawn_spacing_seconds: float = 180.0
+
+
+func is_valid() -> bool:
+ var uses_diggable_area: bool = not diggable_area_id.is_empty()
+ var uses_spawn_anchors: bool = not spawn_anchor_set_id.is_empty()
+ var movement_parameters_valid: bool = (
+ movement_speed >= 0.0
+ and (
+ presentation_mode == PresentationMode.WATER_SPURT
+ or (roam_radius > 0.0 and scare_radius > 0.0)
+ )
+ )
+ return (
+ not type_id.is_empty()
+ and catch_data != null
+ and catch_data.is_valid_catalog_entry()
+ and catch_data.collection_method != FishDataType.CollectionMethod.FISHING
+ and not required_tool_id.is_empty()
+ and (
+ uses_diggable_area
+ or uses_spawn_anchors
+ or not surface_materials.is_empty()
+ )
+ and maximum_surface_y >= minimum_surface_y
+ and (
+ not uses_diggable_area
+ or catch_data.collection_method
+ == FishDataType.CollectionMethod.DIGGING
+ )
+ and population > 0
+ and (
+ maximum_anchor_population == 0
+ or maximum_anchor_population >= population
+ )
+ and (
+ maximum_surface_population == 0
+ or maximum_surface_population >= population
+ )
+ and movement_parameters_valid
+ and _quality_multipliers_are_valid(
+ quality_movement_speed_multipliers
+ )
+ and _quality_multipliers_are_valid(
+ quality_scare_radius_multipliers
+ )
+ and capture_radius > 0.0
+ and interaction_range > 0.0
+ and charge_duration > 0.0
+ and sprite_pixel_size > 0.0
+ and capture_respawn_max_seconds >= capture_respawn_min_seconds
+ and scare_respawn_max_seconds >= scare_respawn_min_seconds
+ )
+
+
+func is_stationary_hotspot() -> bool:
+ return presentation_mode == PresentationMode.WATER_SPURT
+
+
+func is_stationary_spawn() -> bool:
+ return is_stationary_hotspot() or not spawn_anchor_set_id.is_empty()
+
+
+func target_population_for_anchor_count(anchor_count: int) -> int:
+ if spawn_anchor_set_id.is_empty() or spawn_anchor_occupancy_ratio <= 0.0:
+ return population
+ if anchor_count <= 0:
+ return 0
+ var target := maxi(
+ population,
+ ceili(float(anchor_count) * spawn_anchor_occupancy_ratio),
+ )
+ target = mini(target, anchor_count)
+ if maximum_anchor_population > 0:
+ target = mini(target, maximum_anchor_population)
+ return target
+
+
+func target_population_for_surface_area(surface_area: float) -> int:
+ if surface_population_per_100_square_meters <= 0.0:
+ return population
+ if surface_area <= 0.0:
+ return 0
+ var target := maxi(
+ population,
+ ceili(
+ surface_area
+ * surface_population_per_100_square_meters
+ / 100.0
+ ),
+ )
+ if maximum_surface_population > 0:
+ target = mini(target, maximum_surface_population)
+ return target
+
+
+func can_be_scared() -> bool:
+ return not is_stationary_spawn() and scare_radius > 0.0
+
+
+func get_movement_speed_for_quality(quality: int) -> float:
+ return movement_speed * _quality_multiplier(
+ quality_movement_speed_multipliers,
+ quality,
+ )
+
+
+func get_scare_radius_for_quality(quality: int) -> float:
+ return scare_radius * _quality_multiplier(
+ quality_scare_radius_multipliers,
+ quality,
+ )
+
+
+func get_respawn_delay(reason: StringName, rng: RandomNumberGenerator) -> float:
+ var minimum_seconds: float = capture_respawn_min_seconds
+ var maximum_seconds: float = capture_respawn_max_seconds
+ if reason == &"scared":
+ minimum_seconds = scare_respawn_min_seconds
+ maximum_seconds = scare_respawn_max_seconds
+ if rng == null or maximum_seconds <= minimum_seconds:
+ return minimum_seconds
+ return rng.randf_range(minimum_seconds, maximum_seconds)
+
+
+func is_available(season: int = CalendarSeasonType.UNKNOWN) -> bool:
+ return (
+ catch_data != null
+ and catch_data.active
+ and catch_data.is_available_in_season(season)
+ and is_valid()
+ )
+
+
+func _quality_multiplier(values: Array[float], quality: int) -> float:
+ if values.size() != FishQualityType.TIER_COUNT:
+ return 1.0
+ var safe_quality: int = (
+ quality
+ if FishQualityType.is_valid(quality)
+ else FishQualityType.Tier.BORING
+ )
+ return maxf(values[safe_quality], 0.0)
+
+
+func _quality_multipliers_are_valid(values: Array[float]) -> bool:
+ if values.size() != FishQualityType.TIER_COUNT:
+ return false
+ for multiplier: float in values:
+ if not is_finite(multiplier) or multiplier <= 0.0:
+ return false
+ return true
diff --git a/gathering/gatherable_data.gd.uid b/gathering/gatherable_data.gd.uid
new file mode 100644
index 0000000..5875fff
--- /dev/null
+++ b/gathering/gatherable_data.gd.uid
@@ -0,0 +1 @@
+uid://doheo0308illx
diff --git a/gathering/gathering_controller.gd b/gathering/gathering_controller.gd
new file mode 100644
index 0000000..c061db5
--- /dev/null
+++ b/gathering/gathering_controller.gd
@@ -0,0 +1,367 @@
+class_name GatheringController
+extends Node3D
+
+const FishingShopStockType = preload("res://economy/fishing_shop_stock.gd")
+const FishingSpotType = preload("res://fishing/fishing_spot.gd")
+const NetworkWorldSpawnServiceType = preload(
+ "res://network/network_world_spawn_service.gd"
+)
+const MARKER_INVALID_COLOR := Color(1.0, 1.0, 1.0, 1.0)
+const MARKER_VALID_COLOR := Color(0.25, 0.9, 0.36, 1.0)
+const MARKER_DISC_THICKNESS := 0.006
+
+signal status_changed(message: String)
+
+@export_range(0.5, 5.0, 0.05) var marker_distance: float = 0.85
+@export_range(0.025, 2.0, 0.025) var marker_radius: float = 0.175
+@export_range(0.0, 1.0, 0.01) var marker_surface_offset: float = 0.045
+@export_range(0.0, 2.0, 0.05) var forward_probe_extra_distance: float = 0.6
+@export_flags_3d_physics var terrain_collision_mask: int = 1
+@export_range(0.5, 20.0, 0.5) var ray_height: float = 5.0
+@export_range(0.5, 20.0, 0.5) var ray_depth: float = 8.0
+
+var _player: Player
+var _bag: PlayerBag
+var _hotbar: PlayerHotbar
+var _fishing_spot: FishingSpotType
+var _service: NetworkWorldSpawnServiceType
+var _marker: MeshInstance3D
+var _marker_invalid_material: StandardMaterial3D
+var _marker_valid_material: StandardMaterial3D
+var _marker_position: Vector3
+var _marker_has_surface: bool = false
+var _target_entity_id: String = ""
+var _charging: bool = false
+var _charge_elapsed: float = 0.0
+var _charge_duration: float = 2.0
+var _request_id: String = ""
+var _active_tool_id: StringName
+var _gameplay_input_enabled: bool = false
+
+
+func _ready() -> void:
+ _build_marker()
+ set_process_unhandled_input(true)
+
+
+func setup(
+ player: Player,
+ bag: PlayerBag,
+ hotbar: PlayerHotbar,
+ fishing_spot: FishingSpotType,
+ service: NetworkWorldSpawnServiceType,
+) -> void:
+ _player = player
+ _bag = bag
+ _hotbar = hotbar
+ _fishing_spot = fishing_spot
+ _service = service
+ _service.local_interaction_finished.connect(
+ _on_interaction_finished
+ )
+
+
+func set_gameplay_input_enabled(enabled: bool) -> void:
+ _gameplay_input_enabled = enabled
+ if not enabled:
+ _cancel_charge()
+
+
+func is_net_selected() -> bool:
+ return (
+ _bag != null
+ and _hotbar != null
+ and _hotbar.get_selected_item_id()
+ == FishingShopStockType.CRAB_NET_ID
+ and _bag.owns_item(FishingShopStockType.CRAB_NET_ID)
+ )
+
+
+func is_shovel_selected() -> bool:
+ return _is_owned_tool_selected(FishingShopStockType.STANDARD_SHOVEL_ID)
+
+
+func _is_owned_tool_selected(tool_id: StringName) -> bool:
+ return (
+ _bag != null
+ and _hotbar != null
+ and _hotbar.get_selected_item_id() == tool_id
+ and _bag.owns_item(tool_id)
+ )
+
+
+func _get_selected_gathering_tool_id() -> StringName:
+ if is_net_selected():
+ return FishingShopStockType.CRAB_NET_ID
+ if is_shovel_selected():
+ return FishingShopStockType.STANDARD_SHOVEL_ID
+ return StringName()
+
+
+func _process(delta: float) -> void:
+ var selected_tool_id := _get_selected_gathering_tool_id()
+ var input_available: bool = (
+ _gameplay_input_enabled
+ and not selected_tool_id.is_empty()
+ and _player != null
+ and _player.is_local_control_enabled()
+ and _fishing_spot != null
+ and _fishing_spot.can_change_hotbar_selection()
+ )
+ if _charging and selected_tool_id != _active_tool_id:
+ _cancel_charge()
+ input_available = false
+ if not input_available:
+ if _charging:
+ _cancel_charge()
+ _set_marker_visible(false)
+ return
+ if not _charging:
+ _set_marker_visible(false)
+ return
+ _charge_elapsed += delta
+ _update_marker_target()
+ _update_target_entity()
+ var target_entry: GatherableData = _service.get_entry_for_entity(
+ _target_entity_id
+ )
+ if target_entry != null:
+ _charge_duration = target_entry.charge_duration
+ var fully_charged: bool = _charge_elapsed >= _charge_duration
+ var valid_target: bool = (
+ fully_charged
+ and not _target_entity_id.is_empty()
+ and (
+ target_entry == null
+ or not target_entry.requires_sneaking
+ or _player.is_sneaking()
+ )
+ )
+ _marker.material_override = (
+ _marker_valid_material if valid_target else _marker_invalid_material
+ )
+ _set_marker_visible(_marker_has_surface)
+
+
+func _unhandled_input(event: InputEvent) -> void:
+ if not event.is_action("fish_primary"):
+ return
+ if event.is_pressed():
+ if (
+ _gameplay_input_enabled
+ and _player != null
+ and _player.release_net_strike_hold()
+ ):
+ get_viewport().set_input_as_handled()
+ return
+ if (
+ _charging
+ or not _gameplay_input_enabled
+ or _get_selected_gathering_tool_id().is_empty()
+ or _fishing_spot == null
+ or not _fishing_spot.can_change_hotbar_selection()
+ ):
+ return
+ _request_id = _service.begin_local_interaction()
+ if _request_id.is_empty():
+ return
+ _charging = true
+ _active_tool_id = _get_selected_gathering_tool_id()
+ _charge_elapsed = 0.0
+ _charge_duration = maxf(
+ _service.get_charge_duration_for_tool(
+ _active_tool_id
+ ),
+ 0.1,
+ )
+ _update_marker_target()
+ _update_target_entity()
+ _player.begin_net_draw_visual()
+ get_viewport().set_input_as_handled()
+ return
+ if not _charging:
+ return
+ var fully_charged: bool = _charge_elapsed >= _charge_duration
+ var target_entry: GatherableData = _service.get_entry_for_entity(
+ _target_entity_id
+ )
+ var valid_capture: bool = (
+ fully_charged
+ and _marker_has_surface
+ and not _target_entity_id.is_empty()
+ and (
+ target_entry == null
+ or not target_entry.requires_sneaking
+ or _player.is_sneaking()
+ )
+ )
+ _player.play_net_strike_visual()
+ if valid_capture:
+ _service.finish_local_interaction(
+ _request_id,
+ _target_entity_id,
+ _marker_position,
+ )
+ else:
+ _service.cancel_local_interaction(_request_id)
+ _player.resolve_net_strike_visual(false)
+ _reset_charge_state()
+ get_viewport().set_input_as_handled()
+
+
+func _update_marker_target() -> void:
+ _marker_has_surface = false
+ if _player == null or get_world_3d() == null:
+ return
+ var facing: Vector3 = _player.get_facing_direction()
+ facing.y = 0.0
+ if facing.is_zero_approx():
+ facing = -_player.global_basis.z
+ facing.y = 0.0
+ if facing.is_zero_approx():
+ return
+ facing = facing.normalized()
+ var probe_origin: Vector3 = _player.get_body_center_position()
+ var probe_direction: Vector3 = _get_surface_probe_direction(facing)
+ var forward_hit: Dictionary = _cast_marker_ray(
+ probe_origin,
+ probe_origin
+ + probe_direction
+ * (marker_distance + forward_probe_extra_distance),
+ )
+ if _apply_marker_surface(forward_hit, facing):
+ return
+ var query_center: Vector3 = _player.global_position + facing * marker_distance
+ var ground_hit: Dictionary = _cast_marker_ray(
+ query_center + Vector3.UP * ray_height,
+ query_center - Vector3.UP * ray_depth,
+ )
+ _apply_marker_surface(ground_hit, facing)
+
+
+func _get_surface_probe_direction(facing: Vector3) -> Vector3:
+ var vertical_aim: float = 0.0
+ var camera := _player.get_active_gameplay_camera()
+ if camera != null:
+ vertical_aim = clampf((-camera.global_basis.z).y, -0.75, 0.75)
+ return Vector3(facing.x, vertical_aim, facing.z).normalized()
+
+
+func _cast_marker_ray(from: Vector3, to: Vector3) -> Dictionary:
+ var query := PhysicsRayQueryParameters3D.create(
+ from,
+ to,
+ terrain_collision_mask,
+ [_player.get_rid()],
+ )
+ query.collide_with_areas = false
+ query.collide_with_bodies = true
+ return get_world_3d().direct_space_state.intersect_ray(query)
+
+
+func _apply_marker_surface(hit: Dictionary, facing: Vector3) -> bool:
+ if hit.is_empty():
+ return false
+ var position: Variant = hit.get("position")
+ var normal_value: Variant = hit.get("normal")
+ if (
+ typeof(position) != TYPE_VECTOR3
+ or typeof(normal_value) != TYPE_VECTOR3
+ ):
+ return false
+ var surface_normal := normal_value as Vector3
+ if not surface_normal.is_finite() or surface_normal.is_zero_approx():
+ return false
+ surface_normal = surface_normal.normalized()
+ _marker_position = (
+ (position as Vector3) + surface_normal * marker_surface_offset
+ )
+ _marker.global_position = _marker_position
+ _marker.global_basis = marker_basis_for_surface(surface_normal, facing)
+ _marker_has_surface = true
+ return true
+
+
+static func marker_basis_for_surface(
+ surface_normal: Vector3,
+ facing: Vector3,
+) -> Basis:
+ var normal := surface_normal.normalized()
+ var reference := Vector3.UP
+ if absf(normal.dot(Vector3.UP)) > 0.95:
+ reference = facing.normalized()
+ if reference.is_zero_approx() or absf(normal.dot(reference)) > 0.95:
+ reference = Vector3.FORWARD
+ var x_axis := reference.cross(normal).normalized()
+ if x_axis.is_zero_approx():
+ x_axis = Vector3.RIGHT
+ var z_axis := x_axis.cross(normal).normalized()
+ return Basis(x_axis, normal, z_axis).orthonormalized()
+
+
+func _update_target_entity() -> void:
+ _target_entity_id = (
+ _service.find_capture_target(
+ _marker_position,
+ _active_tool_id,
+ )
+ if _marker_has_surface and _service != null
+ else ""
+ )
+
+
+func _cancel_charge() -> void:
+ if _charging and not _request_id.is_empty() and _service != null:
+ _service.cancel_local_interaction(_request_id)
+ if _charging and _player != null:
+ _player.cancel_net_action_visual()
+ _reset_charge_state()
+
+
+func _reset_charge_state() -> void:
+ _charging = false
+ _charge_elapsed = 0.0
+ _request_id = ""
+ _active_tool_id = StringName()
+ _target_entity_id = ""
+ _marker_has_surface = false
+ _set_marker_visible(false)
+
+
+func _on_interaction_finished(accepted: bool, message: String) -> void:
+ if _player != null:
+ _player.resolve_net_strike_visual(accepted)
+ _reset_charge_state()
+ if not accepted or not message.is_empty():
+ status_changed.emit(message)
+
+
+func _build_marker() -> void:
+ _marker_invalid_material = StandardMaterial3D.new()
+ _marker_invalid_material.albedo_color = MARKER_INVALID_COLOR
+ _marker_invalid_material.shading_mode = BaseMaterial3D.SHADING_MODE_UNSHADED
+ _marker_invalid_material.roughness = 1.0
+ _marker_invalid_material.no_depth_test = true
+ _marker_valid_material = StandardMaterial3D.new()
+ _marker_valid_material.albedo_color = MARKER_VALID_COLOR
+ _marker_valid_material.shading_mode = BaseMaterial3D.SHADING_MODE_UNSHADED
+ _marker_valid_material.roughness = 1.0
+ _marker_valid_material.no_depth_test = true
+ var disc := CylinderMesh.new()
+ disc.top_radius = marker_radius
+ disc.bottom_radius = marker_radius
+ disc.height = MARKER_DISC_THICKNESS
+ disc.radial_segments = 32
+ disc.rings = 1
+ _marker = MeshInstance3D.new()
+ _marker.name = "GatheringTargetMarker"
+ _marker.mesh = disc
+ _marker.material_override = _marker_invalid_material
+ _marker.cast_shadow = GeometryInstance3D.SHADOW_CASTING_SETTING_OFF
+ _marker.visible = false
+ add_child(_marker)
+
+
+func _set_marker_visible(value: bool) -> void:
+ if _marker != null:
+ _marker.visible = value
diff --git a/gathering/gathering_controller.gd.uid b/gathering/gathering_controller.gd.uid
new file mode 100644
index 0000000..30e9ead
--- /dev/null
+++ b/gathering/gathering_controller.gd.uid
@@ -0,0 +1 @@
+uid://cec7nwk815d8a
diff --git a/gathering/world_gatherable.gd b/gathering/world_gatherable.gd
new file mode 100644
index 0000000..1a71477
--- /dev/null
+++ b/gathering/world_gatherable.gd
@@ -0,0 +1,212 @@
+class_name WorldGatherable
+extends Node3D
+
+const GatherableDataType = preload("res://gathering/gatherable_data.gd")
+const REFERENCE_SPRITE_PIXEL_SIZE: float = 0.001
+const REFERENCE_SPRITE_HEIGHT: float = 0.22
+const WATER_SPURT_INTERVAL_SECONDS: float = 1.15
+const WATER_SPURT_COLOR := Color(0.48, 0.82, 0.84, 1.0)
+const WATER_SPURT_HOLE_COLOR := Color(0.23, 0.27, 0.24, 1.0)
+
+var entity_id: String = ""
+var type_id: StringName
+var data: GatherableDataType
+var _sprite: Sprite3D
+var _water_spurt_root: Node3D
+var _water_spurt_elapsed: float = 0.0
+var _target_position: Vector3
+var _target_yaw: float = 0.0
+var _has_state: bool = false
+var _despawning: bool = false
+
+
+func configure(
+ configured_entity_id: String,
+ configured_data: GatherableDataType,
+ position: Vector3,
+ yaw: float,
+) -> void:
+ entity_id = configured_entity_id
+ data = configured_data
+ type_id = data.type_id if data != null else StringName()
+ # Establish the authoritative transform before constructing transient visuals.
+ # Effects emitted before this point would begin at the default world origin.
+ apply_network_state(position, yaw, true)
+ if data != null and data.is_stationary_hotspot():
+ _ensure_water_spurt_visual()
+ _water_spurt_elapsed = (
+ float(abs(hash(entity_id)) % 1000) / 1000.0
+ * WATER_SPURT_INTERVAL_SECONDS
+ )
+ _emit_water_spurt()
+ else:
+ _ensure_visual()
+ if data != null and data.catch_data != null and _sprite != null:
+ _sprite.texture = data.catch_data.display_texture
+ _sprite.pixel_size = data.sprite_pixel_size
+ _sprite.position.y = (
+ 0.0
+ if not data.spawn_anchor_set_id.is_empty()
+ else (
+ REFERENCE_SPRITE_HEIGHT
+ * data.sprite_pixel_size
+ / REFERENCE_SPRITE_PIXEL_SIZE
+ )
+ )
+ _sprite.rotation_degrees.x = data.sprite_tilt_degrees
+
+
+func apply_network_state(
+ position: Vector3,
+ yaw: float,
+ immediate: bool = false,
+) -> void:
+ if _despawning or not position.is_finite() or not is_finite(yaw):
+ return
+ _target_position = position
+ _target_yaw = yaw
+ if immediate or not _has_state:
+ global_position = position
+ rotation.y = yaw
+ _has_state = true
+
+
+func play_despawn(with_dust: bool) -> void:
+ if _despawning:
+ return
+ _despawning = true
+ if with_dust:
+ _emit_dust()
+ if _water_spurt_root != null:
+ _water_spurt_root.visible = false
+ queue_free()
+ return
+ var tween: Tween = create_tween()
+ tween.set_parallel(true)
+ tween.set_trans(Tween.TRANS_QUAD)
+ tween.set_ease(Tween.EASE_IN)
+ if _sprite != null:
+ tween.tween_property(_sprite, "position:y", -0.3, 0.3)
+ tween.tween_property(_sprite, "scale", Vector3(0.75, 0.75, 0.75), 0.3)
+ tween.chain().tween_callback(queue_free)
+
+
+func _process(delta: float) -> void:
+ if not _has_state or _despawning:
+ return
+ global_position = global_position.lerp(
+ _target_position,
+ 1.0 - exp(-10.0 * delta),
+ )
+ rotation.y = lerp_angle(
+ rotation.y,
+ _target_yaw,
+ 1.0 - exp(-8.0 * delta),
+ )
+ if _water_spurt_root != null:
+ _water_spurt_elapsed += delta
+ if _water_spurt_elapsed >= WATER_SPURT_INTERVAL_SECONDS:
+ _water_spurt_elapsed = fmod(
+ _water_spurt_elapsed,
+ WATER_SPURT_INTERVAL_SECONDS,
+ )
+ _emit_water_spurt()
+
+
+func _ensure_visual() -> void:
+ if _sprite != null:
+ return
+ _sprite = Sprite3D.new()
+ _sprite.name = "GatherableSprite"
+ _sprite.position.y = REFERENCE_SPRITE_HEIGHT
+ _sprite.shaded = false
+ _sprite.double_sided = true
+ _sprite.billboard = BaseMaterial3D.BILLBOARD_ENABLED
+ _sprite.texture_filter = BaseMaterial3D.TEXTURE_FILTER_NEAREST
+ add_child(_sprite)
+
+
+func _ensure_water_spurt_visual() -> void:
+ if _water_spurt_root != null:
+ return
+ _water_spurt_root = Node3D.new()
+ _water_spurt_root.name = "WaterSpurt"
+ add_child(_water_spurt_root)
+ var hole_material := StandardMaterial3D.new()
+ hole_material.albedo_color = WATER_SPURT_HOLE_COLOR
+ hole_material.shading_mode = BaseMaterial3D.SHADING_MODE_UNSHADED
+ hole_material.roughness = 1.0
+ hole_material.metallic = 0.0
+ var hole_mesh := CylinderMesh.new()
+ hole_mesh.top_radius = 0.035
+ hole_mesh.bottom_radius = 0.035
+ hole_mesh.height = 0.004
+ hole_mesh.radial_segments = 12
+ hole_mesh.rings = 1
+ hole_mesh.material = hole_material
+ var hole := MeshInstance3D.new()
+ hole.name = "BurrowMark"
+ hole.position.y = 0.008
+ hole.mesh = hole_mesh
+ hole.cast_shadow = GeometryInstance3D.SHADOW_CASTING_SETTING_OFF
+ _water_spurt_root.add_child(hole)
+
+
+func _emit_water_spurt() -> void:
+ if _water_spurt_root == null or not _water_spurt_root.visible:
+ return
+ var particles := CPUParticles3D.new()
+ particles.name = "WaterDroplets"
+ particles.position.y = 0.035
+ particles.amount = 6
+ particles.lifetime = 0.42
+ particles.one_shot = true
+ particles.local_coords = true
+ particles.explosiveness = 1.0
+ particles.direction = Vector3.UP
+ particles.spread = 32.0
+ particles.gravity = Vector3(0.0, -3.4, 0.0)
+ particles.initial_velocity_min = 0.8
+ particles.initial_velocity_max = 1.2
+ particles.scale_amount_min = 0.75
+ particles.scale_amount_max = 1.0
+ var water_material := StandardMaterial3D.new()
+ water_material.albedo_color = WATER_SPURT_COLOR
+ water_material.shading_mode = BaseMaterial3D.SHADING_MODE_UNSHADED
+ water_material.roughness = 1.0
+ water_material.metallic = 0.0
+ var droplet_mesh := BoxMesh.new()
+ droplet_mesh.size = Vector3(0.022, 0.035, 0.022)
+ droplet_mesh.material = water_material
+ particles.mesh = droplet_mesh
+ _water_spurt_root.add_child(particles)
+ particles.emitting = true
+ var cleanup := create_tween()
+ cleanup.tween_interval(0.55)
+ cleanup.tween_callback(particles.queue_free)
+
+
+func _emit_dust() -> void:
+ var particles := CPUParticles3D.new()
+ particles.name = "DustPoof"
+ particles.amount = 9
+ particles.lifetime = 0.55
+ particles.one_shot = true
+ particles.explosiveness = 1.0
+ particles.direction = Vector3.UP
+ particles.spread = 58.0
+ particles.gravity = Vector3(0.0, -2.4, 0.0)
+ particles.initial_velocity_min = 0.65
+ particles.initial_velocity_max = 1.25
+ particles.scale_amount_min = 0.7
+ particles.scale_amount_max = 1.35
+ var dust_material := StandardMaterial3D.new()
+ dust_material.albedo_color = Color(0.58, 0.46, 0.31, 0.85)
+ dust_material.transparency = BaseMaterial3D.TRANSPARENCY_ALPHA
+ dust_material.shading_mode = BaseMaterial3D.SHADING_MODE_UNSHADED
+ var dust_mesh := BoxMesh.new()
+ dust_mesh.size = Vector3(0.1, 0.1, 0.1)
+ dust_mesh.material = dust_material
+ particles.mesh = dust_mesh
+ add_child(particles)
+ particles.emitting = true
diff --git a/gathering/world_gatherable.gd.uid b/gathering/world_gatherable.gd.uid
new file mode 100644
index 0000000..f0de7f7
--- /dev/null
+++ b/gathering/world_gatherable.gd.uid
@@ -0,0 +1 @@
+uid://3t8ygawpkgwj
diff --git a/homes/assets/starter_motorcoach.glb b/homes/assets/starter_motorcoach.glb
new file mode 100644
index 0000000..becd54e
Binary files /dev/null and b/homes/assets/starter_motorcoach.glb differ
diff --git a/homes/assets/starter_motorcoach.glb.import b/homes/assets/starter_motorcoach.glb.import
new file mode 100644
index 0000000..5cba35b
--- /dev/null
+++ b/homes/assets/starter_motorcoach.glb.import
@@ -0,0 +1,45 @@
+[remap]
+
+importer="scene"
+importer_version=1
+type="PackedScene"
+uid="uid://bhph22caipcj6"
+path="res://.godot/imported/starter_motorcoach.glb-153681ef1e622a556fba4df576dc6f84.scn"
+
+[deps]
+
+source_file="res://homes/assets/starter_motorcoach.glb"
+dest_files=["res://.godot/imported/starter_motorcoach.glb-153681ef1e622a556fba4df576dc6f84.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/homes/assets/starter_rv_interior.glb b/homes/assets/starter_rv_interior.glb
new file mode 100644
index 0000000..21608ea
Binary files /dev/null and b/homes/assets/starter_rv_interior.glb differ
diff --git a/homes/assets/starter_rv_interior.glb.import b/homes/assets/starter_rv_interior.glb.import
new file mode 100644
index 0000000..947870a
--- /dev/null
+++ b/homes/assets/starter_rv_interior.glb.import
@@ -0,0 +1,45 @@
+[remap]
+
+importer="scene"
+importer_version=1
+type="PackedScene"
+uid="uid://b815y7svs3ern"
+path="res://.godot/imported/starter_rv_interior.glb-9ad8a345e9e831e82874b4b276341224.scn"
+
+[deps]
+
+source_file="res://homes/assets/starter_rv_interior.glb"
+dest_files=["res://.godot/imported/starter_rv_interior.glb-9ad8a345e9e831e82874b4b276341224.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/homes/assets/tent_interior.glb b/homes/assets/tent_interior.glb
new file mode 100644
index 0000000..3ba2b76
Binary files /dev/null and b/homes/assets/tent_interior.glb differ
diff --git a/homes/assets/tent_interior.glb.import b/homes/assets/tent_interior.glb.import
new file mode 100644
index 0000000..fc01ba7
--- /dev/null
+++ b/homes/assets/tent_interior.glb.import
@@ -0,0 +1,45 @@
+[remap]
+
+importer="scene"
+importer_version=1
+type="PackedScene"
+uid="uid://dkfqs051shmxe"
+path="res://.godot/imported/tent_interior.glb-576cc47c2c633f7ae635651598663518.scn"
+
+[deps]
+
+source_file="res://homes/assets/tent_interior.glb"
+dest_files=["res://.godot/imported/tent_interior.glb-576cc47c2c633f7ae635651598663518.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/homes/decor_catalog.gd b/homes/decor_catalog.gd
new file mode 100644
index 0000000..14e5e29
--- /dev/null
+++ b/homes/decor_catalog.gd
@@ -0,0 +1,59 @@
+class_name DecorCatalog
+extends RefCounted
+
+const BASIC_CUBE_ID: StringName = &"basic_cube"
+const MAX_OWNED_PER_PRODUCT: int = 32
+
+const _PRODUCTS: Dictionary = {
+ BASIC_CUBE_ID: {
+ "display_name": "basic cube",
+ "description": "a simple piece of decor for testing your RV layout.",
+ "price": 1,
+ "footprint": Vector2(1.0, 0.8),
+ "height": 0.8,
+ },
+}
+
+
+static func get_product_ids() -> Array[StringName]:
+ var ids: Array[StringName] = []
+ for value: Variant in _PRODUCTS.keys():
+ ids.append(StringName(str(value)))
+ ids.sort_custom(func(first: StringName, second: StringName) -> bool:
+ return String(first) < String(second)
+ )
+ return ids
+
+
+static func has_product(product_id: StringName) -> bool:
+ return _PRODUCTS.has(product_id)
+
+
+static func get_product(product_id: StringName) -> Dictionary:
+ var value: Variant = _PRODUCTS.get(product_id)
+ return (
+ (value as Dictionary).duplicate(true)
+ if typeof(value) == TYPE_DICTIONARY
+ else {}
+ )
+
+
+static func get_display_name(product_id: StringName) -> String:
+ return str(get_product(product_id).get("display_name", product_id))
+
+
+static func get_description(product_id: StringName) -> String:
+ return str(get_product(product_id).get("description", ""))
+
+
+static func get_price(product_id: StringName) -> int:
+ return int(get_product(product_id).get("price", -1))
+
+
+static func get_footprint(product_id: StringName) -> Vector2:
+ var value: Variant = get_product(product_id).get("footprint", Vector2.ZERO)
+ return value as Vector2 if value is Vector2 else Vector2.ZERO
+
+
+static func get_height(product_id: StringName) -> float:
+ return float(get_product(product_id).get("height", 0.0))
diff --git a/homes/decor_catalog.gd.uid b/homes/decor_catalog.gd.uid
new file mode 100644
index 0000000..10d2530
--- /dev/null
+++ b/homes/decor_catalog.gd.uid
@@ -0,0 +1 @@
+uid://bvgwmexas2gps
diff --git a/homes/home_decor_controller.gd b/homes/home_decor_controller.gd
new file mode 100644
index 0000000..f67207f
--- /dev/null
+++ b/homes/home_decor_controller.gd
@@ -0,0 +1,512 @@
+class_name HomeDecorController
+extends Node
+
+const DecorCatalogType = preload("res://homes/decor_catalog.gd")
+const DRAG_RESPONSE: float = 22.0
+const DRAG_POSITION_EPSILON: float = 0.005
+
+var _player: Player
+var _hotbar: PlayerHotbar
+var _bag: PlayerBag
+var _home_state: PlayerHomeState
+var _network_home: NetworkHomeService
+var _home_world: HomeWorldService
+var _fishing_spot: FishingSpot
+var _toolbar: HomeDecorToolbar
+var _mode: PlayerHomeState.PlacementMode = PlayerHomeState.PlacementMode.GRID
+var _selected_instance_id: String = ""
+var _drag_instance_id: String = ""
+var _drag_owner_fingerprint: String = ""
+var _drag_pointer_offset: Vector3 = Vector3.ZERO
+var _drag_original_position: Vector3 = Vector3.ZERO
+var _drag_original_yaw: float = 0.0
+var _drag_target_position: Vector3 = Vector3.ZERO
+var _drag_target_yaw: float = 0.0
+var _drag_target_valid: bool = false
+
+
+func setup(
+ player: Player,
+ hotbar: PlayerHotbar,
+ bag: PlayerBag,
+ home_state: PlayerHomeState,
+ network_home: NetworkHomeService,
+ home_world: HomeWorldService,
+ fishing_spot: FishingSpot,
+ toolbar: HomeDecorToolbar,
+) -> void:
+ _player = player
+ _hotbar = hotbar
+ _bag = bag
+ _home_state = home_state
+ _network_home = network_home
+ _home_world = home_world
+ _fishing_spot = fishing_spot
+ _toolbar = toolbar
+ if not _network_home.local_space_changed.is_connected(
+ _on_local_space_changed
+ ):
+ _network_home.local_space_changed.connect(_on_local_space_changed)
+ if not _network_home.local_manifest_rejected.is_connected(
+ _on_local_manifest_rejected
+ ):
+ _network_home.local_manifest_rejected.connect(
+ _on_local_manifest_rejected
+ )
+ if not _home_state.changed.is_connected(_on_home_changed):
+ _home_state.changed.connect(_on_home_changed)
+ if not _toolbar.placement_mode_requested.is_connected(
+ _on_placement_mode_requested
+ ):
+ _toolbar.placement_mode_requested.connect(
+ _on_placement_mode_requested
+ )
+ _toolbar.rotate_requested.connect(_rotate_selected)
+ _toolbar.store_requested.connect(_store_selected)
+ _toolbar.cancel_requested.connect(_clear_selection)
+ _toolbar.set_mode(_mode)
+ _refresh_toolbar()
+
+
+func begin_decorating() -> void:
+ _refresh_toolbar()
+ if _toolbar.visible:
+ _toolbar.report_status(
+ "use held decor to place it; click or drag placed decor"
+ )
+
+
+func _input(event: InputEvent) -> void:
+ if _drag_instance_id.is_empty():
+ return
+ var pointer_motion := event as InputEventMouseMotion
+ if pointer_motion != null:
+ _update_drag(pointer_motion.position)
+ get_viewport().set_input_as_handled()
+ return
+ var pointer_button := event as InputEventMouseButton
+ if (
+ pointer_button != null
+ and pointer_button.button_index == MOUSE_BUTTON_LEFT
+ and not pointer_button.pressed
+ ):
+ # A gameplay menu can take ownership between the press and release. Do
+ # not let that release finalize decor behind the menu; preserve the
+ # original transform instead.
+ if _can_decorate():
+ _finish_drag()
+ else:
+ _cancel_drag()
+ get_viewport().set_input_as_handled()
+
+
+func _unhandled_input(event: InputEvent) -> void:
+ var pointer_button := event as InputEventMouseButton
+ if (
+ pointer_button != null
+ and pointer_button.button_index == MOUSE_BUTTON_LEFT
+ ):
+ if not pointer_button.pressed or not _can_decorate():
+ return
+ if _begin_drag(pointer_button.position):
+ get_viewport().set_input_as_handled()
+ return
+ var pointer_decor_id: StringName = _get_selected_decor_id()
+ if (
+ not pointer_decor_id.is_empty()
+ and _bag.get_quantity(pointer_decor_id) > 0
+ ):
+ _place_carried_decor(pointer_decor_id)
+ get_viewport().set_input_as_handled()
+ return
+ if not _can_decorate() or not event.is_action_pressed("fish_primary"):
+ return
+ if event is InputEventKey and event.echo:
+ return
+ var decor_id: StringName = _get_selected_decor_id()
+ if not decor_id.is_empty() and _bag.get_quantity(decor_id) > 0:
+ _place_carried_decor(decor_id)
+ get_viewport().set_input_as_handled()
+
+
+func _process(delta: float) -> void:
+ if _toolbar != null and _toolbar.visible:
+ _toolbar.set_interactive(_can_decorate())
+ if _drag_instance_id.is_empty():
+ return
+ # Menus and other gameplay input handoffs must cancel an active drag before
+ # evaluating a held/released pointer. This prevents a same-frame TAB press
+ # followed by left-button release from committing a move behind the UI.
+ if not _can_decorate():
+ _cancel_drag()
+ return
+ if not Input.is_mouse_button_pressed(MOUSE_BUTTON_LEFT):
+ _finish_drag()
+ return
+ if not _drag_target_valid:
+ return
+ var current: Dictionary = _home_world.get_decor_preview_transform(
+ _drag_owner_fingerprint, _drag_instance_id
+ )
+ if current.is_empty():
+ _cancel_drag()
+ return
+ var current_position: Vector3 = current["position"]
+ var current_yaw: float = float(current["yaw"])
+ var blend: float = 1.0 - exp(-DRAG_RESPONSE * delta)
+ _home_world.set_decor_preview_transform(
+ _drag_owner_fingerprint,
+ _drag_instance_id,
+ current_position.lerp(_drag_target_position, blend),
+ lerp_angle(current_yaw, _drag_target_yaw, blend),
+ )
+
+
+func _begin_drag(screen_position: Vector2) -> bool:
+ if _network_home.has_pending_local_decor_mutation():
+ _toolbar.report_status("waiting for the previous decor update")
+ return false
+ var owner_fingerprint: String = (
+ _network_home.get_local_home_owner_fingerprint()
+ )
+ var camera: Camera3D = _player.get_active_gameplay_camera()
+ var clicked_instance: String = _home_world.find_decor_instance_at_screen(
+ owner_fingerprint, camera, screen_position
+ )
+ if clicked_instance.is_empty():
+ return false
+ var placement: Dictionary = _home_state.get_placement(clicked_instance)
+ if placement.is_empty():
+ return false
+ var original_position: Vector3 = _placement_position(placement)
+ if not original_position.is_finite():
+ return false
+ if not _home_world.set_decor_manipulation_active(
+ owner_fingerprint, clicked_instance, true
+ ):
+ return false
+ var pointer_position: Vector3 = _home_world.get_floor_position_from_screen(
+ owner_fingerprint, camera, screen_position
+ )
+ _drag_instance_id = clicked_instance
+ _drag_owner_fingerprint = owner_fingerprint
+ _drag_original_position = original_position
+ _drag_original_yaw = float(placement.get("yaw", 0.0))
+ _drag_target_position = original_position
+ _drag_target_yaw = _drag_original_yaw
+ _drag_target_valid = true
+ _drag_pointer_offset = (
+ original_position - pointer_position
+ if pointer_position.is_finite()
+ else Vector3.ZERO
+ )
+ _drag_pointer_offset.y = 0.0
+ _select_instance(clicked_instance)
+ _toolbar.report_status("drag decor, then release to place it")
+ return true
+
+
+func _update_drag(screen_position: Vector2) -> void:
+ if not _can_decorate():
+ _cancel_drag()
+ return
+ var camera: Camera3D = _player.get_active_gameplay_camera()
+ var pointer_position: Vector3 = _home_world.get_floor_position_from_screen(
+ _drag_owner_fingerprint, camera, screen_position
+ )
+ if not pointer_position.is_finite():
+ # Keep the most recent valid target when the pointer briefly leaves the
+ # RV floor. Releasing there should place at that target, not throw the
+ # decor all the way back to where the drag began.
+ _toolbar.report_status("keep the pointer over the RV floor")
+ return
+ var placement: Dictionary = _home_state.get_placement(_drag_instance_id)
+ if placement.is_empty():
+ _clear_selection()
+ return
+ var candidate: Vector3 = pointer_position + _drag_pointer_offset
+ candidate.y = 0.0
+ var normalized: Dictionary = _network_home.get_valid_local_decor_transform(
+ _drag_instance_id,
+ candidate,
+ float(placement.get("yaw", _drag_original_yaw)),
+ _mode,
+ )
+ if normalized.is_empty():
+ # A densely furnished area may have no nearby solution. Preserve the
+ # last valid preview so the interaction remains stable while the player
+ # moves the pointer toward open space.
+ _toolbar.report_status("holding at the nearest available position")
+ return
+ _drag_target_position = normalized["position"]
+ _drag_target_yaw = float(normalized["yaw"])
+ _drag_target_valid = true
+ _toolbar.report_status("release to place decor")
+
+
+func _finish_drag() -> void:
+ var instance_id: String = _drag_instance_id
+ var owner_fingerprint: String = _drag_owner_fingerprint
+ var original_position: Vector3 = _drag_original_position
+ var original_yaw: float = _drag_original_yaw
+ var target_position: Vector3 = _drag_target_position
+ var target_yaw: float = _drag_target_yaw
+ var target_valid: bool = _drag_target_valid
+ var placement: Dictionary = _home_state.get_placement(instance_id)
+ var decor_id := StringName(str(placement.get("decor_id", "")))
+ if (
+ not target_valid
+ or decor_id.is_empty()
+ or not _home_world.is_decor_placement_clear_of_players(
+ owner_fingerprint, decor_id, target_position, target_yaw
+ )
+ ):
+ _home_world.set_decor_preview_transform(
+ owner_fingerprint,
+ instance_id,
+ original_position,
+ original_yaw,
+ )
+ _home_world.set_decor_manipulation_active(
+ owner_fingerprint, instance_id, false
+ )
+ _reset_drag_state()
+ _toolbar.report_status(
+ "a player is in the way"
+ if target_valid
+ else "that space is not available"
+ )
+ return
+ _home_world.set_decor_preview_transform(
+ owner_fingerprint, instance_id, target_position, target_yaw
+ )
+ if (
+ original_position.distance_to(target_position)
+ <= DRAG_POSITION_EPSILON
+ and is_zero_approx(angle_difference(original_yaw, target_yaw))
+ ):
+ _home_world.set_decor_manipulation_active(
+ owner_fingerprint, instance_id, false
+ )
+ _reset_drag_state()
+ _select_instance(instance_id)
+ return
+ if _network_home.update_local_decor(
+ instance_id, target_position, target_yaw, _mode
+ ):
+ _home_world.set_decor_manipulation_active(
+ owner_fingerprint, instance_id, false
+ )
+ _reset_drag_state()
+ _toolbar.report_status("decor moved")
+ return
+ _home_world.set_decor_preview_transform(
+ owner_fingerprint,
+ instance_id,
+ original_position,
+ original_yaw,
+ )
+ _home_world.set_decor_manipulation_active(
+ owner_fingerprint, instance_id, false
+ )
+ _reset_drag_state()
+ _toolbar.report_status("that space is not available")
+
+
+func _cancel_drag() -> void:
+ if _drag_instance_id.is_empty():
+ return
+ _home_world.set_decor_preview_transform(
+ _drag_owner_fingerprint,
+ _drag_instance_id,
+ _drag_original_position,
+ _drag_original_yaw,
+ )
+ _home_world.set_decor_manipulation_active(
+ _drag_owner_fingerprint, _drag_instance_id, false
+ )
+ _reset_drag_state()
+
+
+func _reset_drag_state() -> void:
+ _drag_instance_id = ""
+ _drag_owner_fingerprint = ""
+ _drag_pointer_offset = Vector3.ZERO
+ _drag_original_position = Vector3.ZERO
+ _drag_original_yaw = 0.0
+ _drag_target_position = Vector3.ZERO
+ _drag_target_yaw = 0.0
+ _drag_target_valid = false
+
+
+static func _placement_position(placement: Dictionary) -> Vector3:
+ if placement.is_empty():
+ return Vector3.INF
+ var values: Array = placement.get("position", [])
+ if values.size() != 3:
+ return Vector3.INF
+ return Vector3(
+ float(values[0]), float(values[1]), float(values[2])
+ )
+
+
+func _place_carried_decor(decor_id: StringName) -> void:
+ if _network_home.has_pending_local_decor_mutation():
+ _toolbar.report_status("waiting for the previous decor update")
+ return
+ var owner_fingerprint: String = (
+ _network_home.get_local_home_owner_fingerprint()
+ )
+ # RV transitions keep the gameplay root axis-aligned so its transform can be
+ # synchronized independently from the presentation. Use the visual facing
+ # direction instead; otherwise carried decor is always placed along world -Z
+ # instead of in front of the character the player sees.
+ var forward: Vector3 = _player.get_facing_direction()
+ var global_position: Vector3 = _player.global_position + forward * 1.45
+ var local_position: Vector3 = _home_world.interior_global_to_local(
+ owner_fingerprint, global_position
+ )
+ var placement_yaw: float = atan2(-forward.x, -forward.z)
+ var instance_id: String = _network_home.place_local_decor(
+ decor_id, local_position, placement_yaw, _mode
+ )
+ if instance_id.is_empty():
+ _toolbar.report_status("make room in front of you to place this decor")
+ return
+ # A local host can reject its own optimistic manifest synchronously. Do not
+ # claim success after that rollback or leave the player looking for decor that
+ # was never accepted into the room.
+ if _home_state.get_placement(instance_id).is_empty():
+ _toolbar.report_status("decor placement was not accepted")
+ return
+ _select_instance(instance_id)
+ _toolbar.report_status("decor placed")
+
+
+func _select_instance(instance_id: String) -> void:
+ var placement: Dictionary = _home_state.get_placement(instance_id)
+ if placement.is_empty():
+ _clear_selection()
+ return
+ _selected_instance_id = instance_id
+ var decor_id := StringName(str(placement.get("decor_id", "")))
+ _toolbar.set_selection(
+ instance_id, DecorCatalogType.get_display_name(decor_id)
+ )
+
+
+func _rotate_selected() -> void:
+ if not _can_decorate() or _selected_instance_id.is_empty():
+ return
+ if _network_home.has_pending_local_decor_mutation():
+ _toolbar.report_status("waiting for the previous decor update")
+ return
+ var placement: Dictionary = _home_state.get_placement(
+ _selected_instance_id
+ )
+ if placement.is_empty():
+ _clear_selection()
+ return
+ var values: Array = placement.get("position", [])
+ if values.size() != 3:
+ return
+ var position := Vector3(
+ float(values[0]), float(values[1]), float(values[2])
+ )
+ if _network_home.update_local_decor(
+ _selected_instance_id,
+ position,
+ float(placement.get("yaw", 0.0)) + PI * 0.5,
+ _mode,
+ ):
+ _toolbar.report_status("decor rotated")
+ else:
+ _toolbar.report_status("not enough room to rotate here")
+
+
+func _store_selected() -> void:
+ if _selected_instance_id.is_empty():
+ return
+ if _network_home.has_pending_local_decor_mutation():
+ _toolbar.report_status("waiting for the previous decor update")
+ return
+ if _network_home.remove_local_decor(_selected_instance_id):
+ _clear_selection()
+ _toolbar.report_status("decor packed into storage")
+ else:
+ _toolbar.report_status("inventory and storage are full")
+
+
+func _clear_selection() -> void:
+ _cancel_drag()
+ _selected_instance_id = ""
+ if _toolbar != null:
+ _toolbar.set_selection("")
+
+
+func _on_placement_mode_requested(mode: int) -> void:
+ _mode = mode as PlayerHomeState.PlacementMode
+
+
+func _on_local_space_changed(
+ _space_id: StringName,
+ _owner_fingerprint: String,
+) -> void:
+ _clear_selection()
+ _refresh_toolbar()
+
+
+func _on_local_manifest_rejected(message: String) -> void:
+ _clear_selection()
+ if _toolbar != null and _toolbar.visible:
+ _toolbar.report_status(message)
+
+
+func _on_home_changed() -> void:
+ if (
+ not _selected_instance_id.is_empty()
+ and _home_state.get_placement(_selected_instance_id).is_empty()
+ ):
+ _clear_selection()
+ _refresh_toolbar()
+
+
+func _refresh_toolbar() -> void:
+ if _toolbar == null:
+ return
+ var inside_home: bool = _is_inside_home_space()
+ _toolbar.set_active(inside_home)
+ _toolbar.set_interactive(_can_decorate())
+ if not _toolbar.visible:
+ _clear_selection()
+ elif not _can_decorate():
+ _toolbar.report_status("decor tools pause while another menu is open")
+
+
+func _is_inside_home_space() -> bool:
+ return (
+ _network_home != null
+ and String(_network_home.get_local_space()).begins_with("rv:")
+ )
+
+
+func _can_decorate() -> bool:
+ return (
+ _player != null
+ and _network_home != null
+ and _network_home.can_local_decorate()
+ and _fishing_spot != null
+ and _fishing_spot.can_open_fishing_shop()
+ and _player.is_movement_enabled()
+ )
+
+
+func _get_selected_decor_id() -> StringName:
+ if (
+ _hotbar == null
+ or _hotbar.get_selected_assignment_kind()
+ != PlayerHotbar.AssignmentKind.ITEM
+ ):
+ return StringName()
+ var item_id: StringName = _hotbar.get_selected_item_id()
+ return item_id if DecorCatalogType.has_product(item_id) else StringName()
diff --git a/homes/home_decor_controller.gd.uid b/homes/home_decor_controller.gd.uid
new file mode 100644
index 0000000..1778125
--- /dev/null
+++ b/homes/home_decor_controller.gd.uid
@@ -0,0 +1 @@
+uid://b5j0mqcvhmi54
diff --git a/homes/home_world_service.gd b/homes/home_world_service.gd
new file mode 100644
index 0000000..4ec0645
--- /dev/null
+++ b/homes/home_world_service.gd
@@ -0,0 +1,1504 @@
+class_name HomeWorldService
+extends Node3D
+
+const NetworkHomeProtocolType = preload(
+ "res://network/network_home_protocol.gd"
+)
+const PlayerHomeStateType = preload("res://homes/player_home_state.gd")
+const DecorCatalogType = preload("res://homes/decor_catalog.gd")
+const STARTER_MOTORCOACH_SCENE: PackedScene = preload(
+ "res://homes/assets/starter_motorcoach.glb"
+)
+const TENT_INTERIOR_SCENE: PackedScene = preload(
+ "res://homes/assets/tent_interior.glb"
+)
+const STARTER_RV_INTERIOR_SCENE: PackedScene = preload(
+ "res://homes/assets/starter_rv_interior.glb"
+)
+const HOME_PROMPT_FONT: Font = preload("res://ui/fonts/Tuffy_Bold.otf")
+
+const RV_EXTERIOR_BODY_SIZE := Vector3(11.2, 5.0, 4.7)
+const RV_EXTERIOR_BODY_CENTER_Y: float = 2.7
+const RV_EXTERIOR_DOOR_LOCAL := Vector3(-0.4, 1.7, 2.96)
+const RV_EXTERIOR_SPAWN_LOCAL := Vector3(-0.4, 0.25, 3.95)
+const TENT_EXTERIOR_BODY_SIZE := Vector3(4.6, 3.0, 4.6)
+const TENT_EXTERIOR_BODY_CENTER_Y: float = 1.5
+const TENT_EXTERIOR_DOOR_LOCAL := Vector3(0.0, 1.0, 2.3)
+const TENT_EXTERIOR_SPAWN_LOCAL := Vector3(0.0, 0.25, 3.3)
+const RV_EXTERIOR_PROMPT_LOCAL := Vector3(-0.4, 3.72, 3.02)
+const TENT_EXTERIOR_PROMPT_LOCAL := Vector3(0.0, 2.48, 2.36)
+const HOME_PROMPT_SIZE := Vector2(0.95, 0.28)
+const HOME_PROMPT_CORNER_RADIUS: float = 0.08
+const HOME_PROMPT_TEXT_PIXEL_SIZE: float = 0.0045
+const HOME_PROMPT_TEXT_COLOR := Color("c3dfe6")
+const HOME_PROMPT_BACKGROUND_COLOR := Color("0a2d3a")
+const EXTERIOR_INTERACTION_DISTANCE: float = 2.7
+const INTERIOR_EXIT_DISTANCE: float = 2.1
+const INTERIOR_ORIGIN := Vector3(1000.0, 300.0, 1000.0)
+const INTERIOR_SPACING: float = 14.0
+const INTERIOR_MAX_SPAWN_SLOTS: int = 64
+const TENT_INTERIOR_SIZE := Vector2(4.0, 4.0)
+const STARTER_RV_INTERIOR_SIZE := Vector2(4.0, 6.0)
+const MIN_EXTERIOR_TRIANGLE_AREA: float = 1.0
+const MIN_EXTERIOR_SEPARATION: float = 14.6
+const EXTERIOR_FOOTPRINT_HALF_EXTENTS := Vector2(6.65, 3.73)
+const EXTERIOR_SURFACE_INDEX_CELL_SIZE: float = 5.0
+const EXTERIOR_MAX_SURFACE_HEIGHT_DELTA: float = 0.24
+const EXTERIOR_CLEARANCE_HEIGHT: float = 5.5
+const WORLD_GEOMETRY_COLLISION_LAYER: int = 1
+const DECOR_SELECTION_COLLISION_LAYER: int = 1 << 8
+# Includes the player's 0.45 m capsule radius plus enough movement margin for
+# a full low-tick-rate frame before a newly solid placement can be reached.
+const PLAYER_DECOR_CLEARANCE_RADIUS: float = 0.8
+const BASIC_CUBE_SIZE := Vector3(1.0, 0.8, 0.8)
+
+var _world: TestWorld
+var _spawn_service: PlayerSpawnService
+var _session: NetworkSession
+var _dedicated_runtime: bool = false
+var _exteriors_root: Node3D
+var _interiors_root: Node3D
+var _presentations_by_fingerprint: Dictionary[String, Dictionary] = {}
+var _local_space: StringName = NetworkHomeProtocolType.WORLD_SPACE
+var _decor_under_manipulation: Dictionary[String, bool] = {}
+var _pending_solid_decor: Dictionary[String, Dictionary] = {}
+
+
+func setup(
+ world: TestWorld,
+ spawn_service: PlayerSpawnService,
+ session: NetworkSession,
+ dedicated_runtime: bool,
+) -> void:
+ _world = world
+ _spawn_service = spawn_service
+ _session = session
+ _dedicated_runtime = dedicated_runtime
+ _ensure_roots()
+ set_process(true)
+
+
+func is_world_ready() -> bool:
+ return _world != null and _world.is_world_ready()
+
+
+func clear_presentations() -> void:
+ _presentations_by_fingerprint.clear()
+ _decor_under_manipulation.clear()
+ _pending_solid_decor.clear()
+ if is_instance_valid(_exteriors_root):
+ for child: Node in _exteriors_root.get_children():
+ child.queue_free()
+ if is_instance_valid(_interiors_root):
+ for child: Node in _interiors_root.get_children():
+ child.queue_free()
+
+
+func remove_presentation(owner_fingerprint: String) -> void:
+ var presentation: Dictionary = _presentations_by_fingerprint.get(
+ owner_fingerprint, {}
+ )
+ if presentation.is_empty():
+ return
+ _clear_decor_collision_tracking(owner_fingerprint)
+ _free_presentation_nodes(presentation)
+ _presentations_by_fingerprint.erase(owner_fingerprint)
+
+
+func apply_manifest(data: Dictionary) -> bool:
+ if (
+ not is_world_ready()
+ or not NetworkHomeProtocolType.validate_manifest_broadcast(data)
+ ):
+ return false
+ _ensure_roots()
+ var fingerprint: String = str(data["owner_fingerprint"])
+ var prior: Dictionary = _presentations_by_fingerprint.get(fingerprint, {})
+ var prior_instance_ids: Dictionary[String, bool] = {}
+ if not prior.is_empty():
+ var prior_home: Dictionary = prior.get("home", {})
+ for value: Variant in prior_home.get("placements", []):
+ if typeof(value) == TYPE_DICTIONARY:
+ prior_instance_ids[str(
+ (value as Dictionary).get("instance_id", "")
+ )] = true
+ if not prior.is_empty():
+ _clear_decor_collision_tracking(fingerprint)
+ _free_presentation_nodes(prior)
+ var exterior_transform := NetworkHomeProtocolType.array_to_transform(
+ data["exterior_transform"]
+ )
+ var interior_index: int = int(data["interior_index"])
+ var home_data: Dictionary = PlayerHomeStateType.sanitize_save_data(
+ data["home"]
+ )
+ var exterior := _build_exterior(
+ fingerprint, exterior_transform, home_data
+ )
+ var interior := _build_interior(fingerprint, interior_index, home_data)
+ var presentation: Dictionary = {
+ "owner_peer_id": int(data["owner_peer_id"]),
+ "fingerprint": fingerprint,
+ "home": home_data,
+ "exterior_transform": exterior_transform,
+ "interior_index": interior_index,
+ "exterior": exterior,
+ "interior": interior,
+ }
+ _presentations_by_fingerprint[fingerprint] = presentation
+ _refresh_local_visibility()
+ if not prior.is_empty():
+ for value: Variant in home_data.get("placements", []):
+ if typeof(value) != TYPE_DICTIONARY:
+ continue
+ var instance_id: String = str(
+ (value as Dictionary).get("instance_id", "")
+ )
+ if not instance_id.is_empty() and not prior_instance_ids.has(
+ instance_id
+ ):
+ call_deferred(
+ "_spawn_decor_poof", fingerprint, instance_id
+ )
+ return true
+
+
+func choose_exterior_transform(
+ owner_peer_id: int,
+ reserved_transforms: Array[Transform3D],
+) -> Transform3D:
+ var spawn_transform: Transform3D = (
+ _world.get_player_spawn_transform()
+ if _world != null else Transform3D.IDENTITY
+ )
+ var triangles: Array[PackedVector3Array] = (
+ _world.get_spawn_surface_triangles(
+ [&"grass_lite", &"grass", &"sand"], -0.5
+ )
+ if _world != null else []
+ )
+ if not triangles.is_empty():
+ var surface_index: Dictionary = _build_surface_index(triangles)
+ var strict_result: Transform3D = _find_exterior_transform(
+ owner_peer_id,
+ triangles,
+ surface_index,
+ spawn_transform,
+ reserved_transforms,
+ true,
+ true,
+ )
+ if strict_result != Transform3D.IDENTITY:
+ return strict_result
+ var relaxed_result: Transform3D = _find_exterior_transform(
+ owner_peer_id,
+ triangles,
+ surface_index,
+ spawn_transform,
+ reserved_transforms,
+ false,
+ true,
+ )
+ if relaxed_result != Transform3D.IDENTITY:
+ return relaxed_result
+ var dry_result: Transform3D = _find_exterior_transform(
+ owner_peer_id,
+ triangles,
+ surface_index,
+ spawn_transform,
+ reserved_transforms,
+ false,
+ false,
+ )
+ if dry_result != Transform3D.IDENTITY:
+ push_warning(
+ "Home exterior used a dry fallback with obstructed clearance."
+ )
+ return dry_result
+ var emergency_result: Transform3D = _find_exterior_transform(
+ owner_peer_id,
+ triangles,
+ surface_index,
+ spawn_transform,
+ [],
+ false,
+ false,
+ )
+ if emergency_result != Transform3D.IDENTITY:
+ push_warning(
+ "Home exterior used an accessible emergency placement."
+ )
+ return emergency_result
+ push_error("No dry, level home exterior footprint was available.")
+ # The authored spawn is the only universally reachable point. Never strand
+ # a home on an unchecked elevation when malformed terrain offers no site.
+ return spawn_transform
+
+
+func _find_exterior_transform(
+ owner_peer_id: int,
+ triangles: Array[PackedVector3Array],
+ surface_index: Dictionary,
+ spawn_transform: Transform3D,
+ reserved_transforms: Array[Transform3D],
+ require_spawn_clearance: bool,
+ require_obstacle_clearance: bool,
+) -> Transform3D:
+ var triangle_count: int = triangles.size()
+ var start: int = posmod(owner_peer_id * 37, triangle_count)
+ var step: int = _coprime_step(triangle_count, 29)
+ for offset: int in triangle_count:
+ var triangle: PackedVector3Array = triangles[
+ (start + offset * step) % triangle_count
+ ]
+ if not _is_exterior_candidate_triangle(triangle):
+ continue
+ var position: Vector3 = (
+ triangle[0] + triangle[1] + triangle[2]
+ ) / 3.0
+ if (
+ require_spawn_clearance
+ and position.distance_to(spawn_transform.origin) < 9.0
+ ):
+ continue
+ if _too_close_to_reserved(position, reserved_transforms):
+ continue
+ var yaw: float = float(posmod(owner_peer_id + offset, 4)) * PI * 0.5
+ var grounded_position: Vector3 = _ground_exterior_footprint(
+ position, yaw, triangles, surface_index
+ )
+ if not grounded_position.is_finite():
+ continue
+ if (
+ _world != null
+ and not _world.is_home_exterior_position_accessible(
+ grounded_position
+ )
+ ):
+ continue
+ if (
+ require_obstacle_clearance
+ and not _exterior_volume_is_clear(grounded_position, yaw)
+ ):
+ continue
+ return Transform3D(Basis(Vector3.UP, yaw), grounded_position)
+ return Transform3D.IDENTITY
+
+
+func _is_exterior_candidate_triangle(triangle: PackedVector3Array) -> bool:
+ if triangle.size() != 3:
+ return false
+ var normal: Vector3 = (triangle[1] - triangle[0]).cross(
+ triangle[2] - triangle[0]
+ )
+ var area: float = normal.length() * 0.5
+ return (
+ area >= MIN_EXTERIOR_TRIANGLE_AREA
+ and absf(normal.normalized().y) >= 0.94
+ )
+
+
+func _ground_exterior_footprint(
+ position: Vector3,
+ yaw: float,
+ triangles: Array[PackedVector3Array],
+ surface_index: Dictionary,
+) -> Vector3:
+ var heights: Array[float] = []
+ for local_sample: Vector2 in _exterior_footprint_samples():
+ var rotated_sample: Vector3 = Vector3(
+ local_sample.x, 0.0, local_sample.y
+ ).rotated(Vector3.UP, yaw)
+ var sample := Vector2(
+ position.x + rotated_sample.x,
+ position.z + rotated_sample.z,
+ )
+ var height: float = _surface_height_at(
+ sample, position.y, triangles, surface_index
+ )
+ if not is_finite(height):
+ return Vector3.INF
+ heights.append(height)
+ var minimum_height: float = heights.min()
+ var maximum_height: float = heights.max()
+ if maximum_height - minimum_height > EXTERIOR_MAX_SURFACE_HEIGHT_DELTA:
+ return Vector3.INF
+ return Vector3(position.x, maximum_height, position.z)
+
+
+func _exterior_footprint_samples() -> Array[Vector2]:
+ var samples: Array[Vector2] = []
+ for x_factor: float in [-1.0, -0.5, 0.0, 0.5, 1.0]:
+ for z_factor: float in [-1.0, 0.0, 1.0]:
+ samples.append(Vector2(
+ EXTERIOR_FOOTPRINT_HALF_EXTENTS.x * x_factor,
+ EXTERIOR_FOOTPRINT_HALF_EXTENTS.y * z_factor,
+ ))
+ return samples
+
+
+func _build_surface_index(
+ triangles: Array[PackedVector3Array],
+) -> Dictionary:
+ var result: Dictionary = {}
+ for triangle_index: int in triangles.size():
+ var triangle: PackedVector3Array = triangles[triangle_index]
+ if triangle.size() != 3:
+ continue
+ var minimum_x: float = minf(
+ triangle[0].x, minf(triangle[1].x, triangle[2].x)
+ )
+ var maximum_x: float = maxf(
+ triangle[0].x, maxf(triangle[1].x, triangle[2].x)
+ )
+ var minimum_z: float = minf(
+ triangle[0].z, minf(triangle[1].z, triangle[2].z)
+ )
+ var maximum_z: float = maxf(
+ triangle[0].z, maxf(triangle[1].z, triangle[2].z)
+ )
+ var minimum_cell := Vector2i(
+ floori(minimum_x / EXTERIOR_SURFACE_INDEX_CELL_SIZE),
+ floori(minimum_z / EXTERIOR_SURFACE_INDEX_CELL_SIZE),
+ )
+ var maximum_cell := Vector2i(
+ floori(maximum_x / EXTERIOR_SURFACE_INDEX_CELL_SIZE),
+ floori(maximum_z / EXTERIOR_SURFACE_INDEX_CELL_SIZE),
+ )
+ for cell_x: int in range(minimum_cell.x, maximum_cell.x + 1):
+ for cell_y: int in range(minimum_cell.y, maximum_cell.y + 1):
+ var cell := Vector2i(cell_x, cell_y)
+ var bucket: Array = result.get(cell, [])
+ bucket.append(triangle_index)
+ result[cell] = bucket
+ return result
+
+
+func _surface_height_at(
+ point: Vector2,
+ preferred_height: float,
+ triangles: Array[PackedVector3Array],
+ surface_index: Dictionary,
+) -> float:
+ var cell := Vector2i(
+ floori(point.x / EXTERIOR_SURFACE_INDEX_CELL_SIZE),
+ floori(point.y / EXTERIOR_SURFACE_INDEX_CELL_SIZE),
+ )
+ var best_height: float = INF
+ var best_delta: float = INF
+ for value: Variant in surface_index.get(cell, []):
+ var triangle: PackedVector3Array = triangles[int(value)]
+ var height: float = _triangle_height_at(point, triangle)
+ if not is_finite(height):
+ continue
+ var delta: float = absf(height - preferred_height)
+ if delta < best_delta:
+ best_delta = delta
+ best_height = height
+ return best_height
+
+
+func _triangle_height_at(
+ point: Vector2,
+ triangle: PackedVector3Array,
+) -> float:
+ var a := Vector2(triangle[0].x, triangle[0].z)
+ var b := Vector2(triangle[1].x, triangle[1].z)
+ var c := Vector2(triangle[2].x, triangle[2].z)
+ var denominator: float = (
+ (b.y - c.y) * (a.x - c.x)
+ + (c.x - b.x) * (a.y - c.y)
+ )
+ if absf(denominator) <= 0.000001:
+ return INF
+ var weight_a: float = (
+ (b.y - c.y) * (point.x - c.x)
+ + (c.x - b.x) * (point.y - c.y)
+ ) / denominator
+ var weight_b: float = (
+ (c.y - a.y) * (point.x - c.x)
+ + (a.x - c.x) * (point.y - c.y)
+ ) / denominator
+ var weight_c: float = 1.0 - weight_a - weight_b
+ const EDGE_EPSILON: float = 0.001
+ if (
+ weight_a < -EDGE_EPSILON
+ or weight_b < -EDGE_EPSILON
+ or weight_c < -EDGE_EPSILON
+ ):
+ return INF
+ return (
+ weight_a * triangle[0].y
+ + weight_b * triangle[1].y
+ + weight_c * triangle[2].y
+ )
+
+
+func _exterior_volume_is_clear(position: Vector3, yaw: float) -> bool:
+ if not is_inside_tree() or get_world_3d() == null:
+ return true
+ var shape := BoxShape3D.new()
+ shape.size = Vector3(
+ EXTERIOR_FOOTPRINT_HALF_EXTENTS.x * 2.0,
+ EXTERIOR_CLEARANCE_HEIGHT,
+ EXTERIOR_FOOTPRINT_HALF_EXTENTS.y * 2.0,
+ )
+ var query := PhysicsShapeQueryParameters3D.new()
+ query.shape = shape
+ query.transform = Transform3D(
+ Basis(Vector3.UP, yaw),
+ position + Vector3.UP * (EXTERIOR_CLEARANCE_HEIGHT * 0.5 + 0.2),
+ )
+ query.collision_mask = 1
+ query.collide_with_areas = false
+ query.collide_with_bodies = true
+ return get_world_3d().direct_space_state.intersect_shape(query, 1).is_empty()
+
+
+func _coprime_step(value: int, preferred_step: int) -> int:
+ var step: int = mini(preferred_step, maxi(value - 1, 1))
+ while step > 1 and _greatest_common_divisor(step, value) != 1:
+ step -= 1
+ return step
+
+
+func _greatest_common_divisor(first: int, second: int) -> int:
+ var a: int = absi(first)
+ var b: int = absi(second)
+ while b != 0:
+ var remainder: int = a % b
+ a = b
+ b = remainder
+ return a
+
+
+func get_exterior_transform(owner_fingerprint: String) -> Transform3D:
+ var presentation: Dictionary = _presentations_by_fingerprint.get(
+ owner_fingerprint, {}
+ )
+ return presentation.get("exterior_transform", Transform3D.IDENTITY)
+
+
+func get_exterior_prompt_position(owner_fingerprint: String) -> Vector3:
+ return get_exterior_transform(owner_fingerprint) * (
+ _exterior_prompt_local(owner_fingerprint)
+ )
+
+
+func set_exterior_prompt_visible(
+ owner_fingerprint: String,
+ requested_visible: bool,
+ action_text: String = "enter home",
+) -> void:
+ for fingerprint: String in _presentations_by_fingerprint:
+ var presentation: Dictionary = _presentations_by_fingerprint[fingerprint]
+ var exterior: Node3D = presentation.get("exterior")
+ if not is_instance_valid(exterior):
+ continue
+ var prompt := exterior.get_node_or_null("DoorPrompt") as Node3D
+ if prompt == null:
+ continue
+ prompt.visible = (
+ requested_visible
+ and fingerprint == owner_fingerprint
+ and not _dedicated_runtime
+ )
+ if prompt.visible:
+ var label := prompt.get_node_or_null("Message") as Label3D
+ if label != null:
+ label.text = action_text
+
+
+func get_exterior_spawn_transform(owner_fingerprint: String) -> Transform3D:
+ var exterior_transform: Transform3D = get_exterior_transform(
+ owner_fingerprint
+ )
+ # Exterior local +Z leads away from the doorway. Player visuals face local
+ # -Z, so turn the authoritative spawn basis around to start the day facing
+ # outward instead of looking back into the home.
+ var outward_spawn_basis: Basis = (
+ exterior_transform.basis * Basis(Vector3.UP, PI)
+ )
+ return Transform3D(
+ outward_spawn_basis,
+ exterior_transform * _exterior_spawn_local(owner_fingerprint),
+ )
+
+
+func get_interior_spawn_transform(
+ owner_fingerprint: String,
+ visitor_offset: int = 0,
+) -> Transform3D:
+ # `visitor_offset` is allocated by the host as a compact room-local slot.
+ # Keep it bounded as a last line of defense if a future caller accidentally
+ # supplies an engine peer ID instead of a slot.
+ var visitor_slot: int = posmod(visitor_offset, INTERIOR_MAX_SPAWN_SLOTS)
+ var exit_direction: Vector3 = get_interior_exit_direction(owner_fingerprint)
+ var lane_offset: float = (
+ float(posmod(visitor_slot, 4)) - 1.5
+ ) * 0.28
+ var row_offset: float = (
+ float(floori(float(visitor_slot) / 4.0)) * 0.28
+ )
+ # Spawn just through the authored doorway. Local presentation then walks the
+ # avatar farther into the room before returning movement control.
+ var spawn_origin: Vector3 = (
+ get_interior_exit_position(owner_fingerprint)
+ + exit_direction * 0.42
+ - exit_direction * row_offset
+ + Vector3(0.0, 0.0, lane_offset)
+ )
+ spawn_origin.y = get_interior_anchor(owner_fingerprint).y
+ return Transform3D(Basis.IDENTITY, spawn_origin)
+
+
+func get_interior_anchor(owner_fingerprint: String) -> Vector3:
+ var presentation: Dictionary = _presentations_by_fingerprint.get(
+ owner_fingerprint, {}
+ )
+ var index: int = int(presentation.get("interior_index", 0))
+ return INTERIOR_ORIGIN + Vector3(float(index) * INTERIOR_SPACING, 0.0, 0.0)
+
+
+func is_avatar_near_exterior(avatar: Player, owner_fingerprint: String) -> bool:
+ if avatar == null or not _presentations_by_fingerprint.has(owner_fingerprint):
+ return false
+ var transform: Transform3D = get_exterior_transform(owner_fingerprint)
+ var door_position: Vector3 = (
+ transform * _exterior_door_local(owner_fingerprint)
+ )
+ return avatar.global_position.distance_to(door_position) <= (
+ EXTERIOR_INTERACTION_DISTANCE
+ )
+
+
+func is_avatar_near_interior_exit(
+ avatar: Player,
+ owner_fingerprint: String,
+) -> bool:
+ if avatar == null:
+ return false
+ return avatar.global_position.distance_to(
+ get_interior_exit_position(owner_fingerprint)
+ ) <= INTERIOR_EXIT_DISTANCE
+
+
+func has_avatar_crossed_interior_exit_threshold(
+ avatar: Player,
+ owner_fingerprint: String,
+) -> bool:
+ if avatar == null:
+ return false
+ var local_position: Vector3 = interior_global_to_local(
+ owner_fingerprint,
+ avatar.global_position,
+ )
+ var threshold_position: Vector3 = interior_global_to_local(
+ owner_fingerprint,
+ get_interior_exit_position(owner_fingerprint),
+ )
+ var exit_direction: Vector3 = get_interior_exit_direction(
+ owner_fingerprint
+ )
+ return (
+ (local_position - threshold_position).dot(exit_direction) >= 0.0
+ and absf(local_position.z - threshold_position.z) <= 0.85
+ and local_position.y >= -0.25
+ and local_position.y <= 2.25
+ )
+
+
+func get_interior_exit_direction(owner_fingerprint: String) -> Vector3:
+ return (
+ Vector3.LEFT
+ if _home_tier(owner_fingerprint)
+ >= PlayerHomeStateType.STARTER_RV_TIER
+ else Vector3.RIGHT
+ )
+
+
+func get_interior_exit_position(owner_fingerprint: String) -> Vector3:
+ var tier: int = _home_tier(owner_fingerprint)
+ return get_interior_anchor(owner_fingerprint) + (
+ Vector3(-STARTER_RV_INTERIOR_SIZE.x * 0.5 + 0.6, 0.35, 0.0)
+ if tier >= PlayerHomeStateType.STARTER_RV_TIER
+ else Vector3(TENT_INTERIOR_SIZE.x * 0.5 - 0.55, 0.35, 0.0)
+ )
+
+
+func get_interior_prompt_position(owner_fingerprint: String) -> Vector3:
+ return get_interior_exit_position(owner_fingerprint) + Vector3.UP * 1.2
+
+
+func interior_local_to_global(
+ owner_fingerprint: String,
+ local_position: Vector3,
+) -> Vector3:
+ return get_interior_anchor(owner_fingerprint) + local_position
+
+
+func interior_global_to_local(
+ owner_fingerprint: String,
+ global_position: Vector3,
+) -> Vector3:
+ return global_position - get_interior_anchor(owner_fingerprint)
+
+
+func get_floor_position_from_screen(
+ owner_fingerprint: String,
+ camera: Camera3D,
+ screen_position: Vector2,
+) -> Vector3:
+ if camera == null or owner_fingerprint.is_empty():
+ return Vector3.INF
+ var ray_origin: Vector3 = camera.project_ray_origin(screen_position)
+ var ray_direction: Vector3 = camera.project_ray_normal(screen_position)
+ if absf(ray_direction.y) <= 0.0001:
+ return Vector3.INF
+ var floor_y: float = get_interior_anchor(owner_fingerprint).y + 0.1
+ var distance: float = (floor_y - ray_origin.y) / ray_direction.y
+ if distance <= 0.0:
+ return Vector3.INF
+ return interior_global_to_local(
+ owner_fingerprint,
+ ray_origin + ray_direction * distance,
+ )
+
+
+func find_decor_instance_at_screen(
+ owner_fingerprint: String,
+ camera: Camera3D,
+ screen_position: Vector2,
+) -> String:
+ if camera == null or owner_fingerprint.is_empty():
+ return ""
+ var ray_origin: Vector3 = camera.project_ray_origin(screen_position)
+ var ray_end: Vector3 = ray_origin + camera.project_ray_normal(
+ screen_position
+ ) * 50.0
+ var query := PhysicsRayQueryParameters3D.create(ray_origin, ray_end)
+ query.collision_mask = DECOR_SELECTION_COLLISION_LAYER
+ query.collide_with_areas = false
+ var result: Dictionary = camera.get_world_3d().direct_space_state.intersect_ray(
+ query
+ )
+ var collider: Object = result.get("collider")
+ if collider is Node and (collider as Node).has_meta(&"decor_instance_id"):
+ if str((collider as Node).get_meta(&"decor_owner_fingerprint")) == (
+ owner_fingerprint
+ ):
+ return str((collider as Node).get_meta(&"decor_instance_id"))
+ return ""
+
+
+func get_decor_preview_transform(
+ owner_fingerprint: String,
+ instance_id: String,
+) -> Dictionary:
+ var decor: Node3D = _get_decor_presentation(
+ owner_fingerprint, instance_id
+ )
+ if decor == null:
+ return {}
+ return {
+ "position": Vector3(decor.position.x, 0.0, decor.position.z),
+ "yaw": decor.rotation.y,
+ }
+
+
+func set_decor_preview_transform(
+ owner_fingerprint: String,
+ instance_id: String,
+ position: Vector3,
+ yaw_radians: float,
+) -> bool:
+ var decor: Node3D = _get_decor_presentation(
+ owner_fingerprint, instance_id
+ )
+ if decor == null or not position.is_finite() or not is_finite(yaw_radians):
+ return false
+ decor.position = Vector3(position.x, 0.18, position.z)
+ decor.rotation.y = yaw_radians
+ return true
+
+
+func set_decor_manipulation_active(
+ owner_fingerprint: String,
+ instance_id: String,
+ active: bool,
+) -> bool:
+ var decor: Node3D = _get_decor_presentation(
+ owner_fingerprint, instance_id
+ )
+ if decor == null:
+ return false
+ var body: StaticBody3D = decor.get_node_or_null(
+ "BasicCubeCollision"
+ ) as StaticBody3D
+ if body == null:
+ return false
+ var key: String = _decor_collision_key(
+ owner_fingerprint, instance_id
+ )
+ if active:
+ _decor_under_manipulation[key] = true
+ _pending_solid_decor.erase(key)
+ body.collision_layer = DECOR_SELECTION_COLLISION_LAYER
+ return true
+ _decor_under_manipulation.erase(key)
+ _queue_decor_world_collision(
+ owner_fingerprint,
+ instance_id,
+ StringName(str(decor.get_meta(&"decor_id", ""))),
+ body,
+ )
+ return true
+
+
+func is_decor_placement_clear_of_players(
+ owner_fingerprint: String,
+ decor_id: StringName,
+ local_position: Vector3,
+ yaw_radians: float,
+) -> bool:
+ if (
+ owner_fingerprint.is_empty()
+ or not DecorCatalogType.has_product(decor_id)
+ or not local_position.is_finite()
+ or not is_finite(yaw_radians)
+ ):
+ return false
+ if _spawn_service == null:
+ return true
+ var global_center: Vector3 = interior_local_to_global(
+ owner_fingerprint, local_position
+ )
+ var half_size: Vector2 = DecorCatalogType.get_footprint(decor_id) * 0.5
+ for peer_id: int in _spawn_service.get_peer_ids():
+ var avatar: Player = _spawn_service.get_avatar(peer_id)
+ if avatar == null or not is_instance_valid(avatar):
+ continue
+ var offset := Vector2(
+ avatar.global_position.x - global_center.x,
+ avatar.global_position.z - global_center.z,
+ ).rotated(-yaw_radians)
+ var nearest := Vector2(
+ clampf(offset.x, -half_size.x, half_size.x),
+ clampf(offset.y, -half_size.y, half_size.y),
+ )
+ if offset.distance_squared_to(nearest) < (
+ PLAYER_DECOR_CLEARANCE_RADIUS
+ * PLAYER_DECOR_CLEARANCE_RADIUS
+ ):
+ return false
+ return true
+
+
+func _get_decor_presentation(
+ owner_fingerprint: String,
+ instance_id: String,
+) -> Node3D:
+ if owner_fingerprint.is_empty() or instance_id.is_empty():
+ return null
+ var presentation: Dictionary = _presentations_by_fingerprint.get(
+ owner_fingerprint, {}
+ )
+ var interior: Node3D = presentation.get("interior")
+ if not is_instance_valid(interior):
+ return null
+ return interior.get_node_or_null(
+ "Decor/%s" % instance_id
+ ) as Node3D
+
+
+func find_nearby_exterior_owner(avatar: Player) -> String:
+ if avatar == null:
+ return ""
+ var closest: String = ""
+ var closest_distance: float = EXTERIOR_INTERACTION_DISTANCE
+ for fingerprint: String in _presentations_by_fingerprint:
+ var transform: Transform3D = get_exterior_transform(fingerprint)
+ var distance: float = avatar.global_position.distance_to(
+ transform * _exterior_door_local(fingerprint)
+ )
+ if distance <= closest_distance:
+ closest = fingerprint
+ closest_distance = distance
+ return closest
+
+
+func _home_tier(owner_fingerprint: String) -> int:
+ var presentation: Dictionary = _presentations_by_fingerprint.get(
+ owner_fingerprint, {}
+ )
+ var home_data: Dictionary = presentation.get("home", {})
+ return int(home_data.get("tier", PlayerHomeStateType.STARTER_TIER))
+
+
+func _exterior_door_local(owner_fingerprint: String) -> Vector3:
+ return (
+ RV_EXTERIOR_DOOR_LOCAL
+ if _home_tier(owner_fingerprint)
+ >= PlayerHomeStateType.STARTER_RV_TIER
+ else TENT_EXTERIOR_DOOR_LOCAL
+ )
+
+
+func _exterior_spawn_local(owner_fingerprint: String) -> Vector3:
+ return (
+ RV_EXTERIOR_SPAWN_LOCAL
+ if _home_tier(owner_fingerprint)
+ >= PlayerHomeStateType.STARTER_RV_TIER
+ else TENT_EXTERIOR_SPAWN_LOCAL
+ )
+
+
+func _exterior_prompt_local(owner_fingerprint: String) -> Vector3:
+ return (
+ RV_EXTERIOR_PROMPT_LOCAL
+ if _home_tier(owner_fingerprint)
+ >= PlayerHomeStateType.STARTER_RV_TIER
+ else TENT_EXTERIOR_PROMPT_LOCAL
+ )
+
+
+func set_local_space(space_id: StringName) -> void:
+ _local_space = space_id
+ _refresh_local_visibility()
+
+
+func get_manifest(owner_fingerprint: String) -> Dictionary:
+ var presentation: Dictionary = _presentations_by_fingerprint.get(
+ owner_fingerprint, {}
+ )
+ return (presentation.get("home", {}) as Dictionary).duplicate(true)
+
+
+func _process(_delta: float) -> void:
+ _refresh_pending_decor_collisions()
+ if not _dedicated_runtime:
+ _refresh_nearest_wall()
+
+
+func _queue_decor_world_collision(
+ owner_fingerprint: String,
+ instance_id: String,
+ decor_id: StringName,
+ body: StaticBody3D,
+) -> void:
+ if (
+ owner_fingerprint.is_empty()
+ or instance_id.is_empty()
+ or not DecorCatalogType.has_product(decor_id)
+ or body == null
+ ):
+ return
+ var key: String = _decor_collision_key(
+ owner_fingerprint, instance_id
+ )
+ body.collision_layer = DECOR_SELECTION_COLLISION_LAYER
+ _pending_solid_decor[key] = {
+ "owner_fingerprint": owner_fingerprint,
+ "instance_id": instance_id,
+ "decor_id": decor_id,
+ "body": body,
+ }
+
+
+func _refresh_pending_decor_collisions() -> void:
+ if _pending_solid_decor.is_empty():
+ return
+ var keys: Array[String] = []
+ keys.assign(_pending_solid_decor.keys())
+ for key: String in keys:
+ if _decor_under_manipulation.has(key):
+ continue
+ var pending: Dictionary = _pending_solid_decor.get(key, {})
+ var body: StaticBody3D = pending.get("body") as StaticBody3D
+ var owner_fingerprint: String = str(
+ pending.get("owner_fingerprint", "")
+ )
+ var instance_id: String = str(pending.get("instance_id", ""))
+ var decor_id := StringName(str(pending.get("decor_id", "")))
+ var decor: Node3D = _get_decor_presentation(
+ owner_fingerprint, instance_id
+ )
+ if body == null or decor == null or not is_instance_valid(body):
+ _pending_solid_decor.erase(key)
+ continue
+ if not is_decor_placement_clear_of_players(
+ owner_fingerprint,
+ decor_id,
+ Vector3(decor.position.x, 0.0, decor.position.z),
+ decor.rotation.y,
+ ):
+ continue
+ body.collision_layer = (
+ WORLD_GEOMETRY_COLLISION_LAYER
+ | DECOR_SELECTION_COLLISION_LAYER
+ )
+ _pending_solid_decor.erase(key)
+
+
+func _clear_decor_collision_tracking(owner_fingerprint: String) -> void:
+ var prefix: String = "%s/" % owner_fingerprint
+ for key: String in _decor_under_manipulation.keys():
+ if key.begins_with(prefix):
+ _decor_under_manipulation.erase(key)
+ for key: String in _pending_solid_decor.keys():
+ if key.begins_with(prefix):
+ _pending_solid_decor.erase(key)
+
+
+static func _decor_collision_key(
+ owner_fingerprint: String,
+ instance_id: String,
+) -> String:
+ return "%s/%s" % [owner_fingerprint, instance_id]
+
+
+func _ensure_roots() -> void:
+ if not is_instance_valid(_exteriors_root):
+ _exteriors_root = Node3D.new()
+ _exteriors_root.name = "RVExteriors"
+ add_child(_exteriors_root)
+ if not is_instance_valid(_interiors_root):
+ _interiors_root = Node3D.new()
+ _interiors_root.name = "RVInteriors"
+ add_child(_interiors_root)
+
+
+func _build_exterior(
+ fingerprint: String,
+ world_transform: Transform3D,
+ home_data: Dictionary,
+) -> Node3D:
+ var root := Node3D.new()
+ root.name = "Home_%s" % fingerprint.left(8)
+ _exteriors_root.add_child(root)
+ root.global_transform = world_transform
+ var tier: int = int(home_data.get(
+ "tier", PlayerHomeStateType.STARTER_TIER
+ ))
+ if tier >= PlayerHomeStateType.STARTER_RV_TIER:
+ if not _dedicated_runtime:
+ var motorcoach: Node3D = (
+ STARTER_MOTORCOACH_SCENE.instantiate() as Node3D
+ )
+ if motorcoach != null:
+ motorcoach.name = "StarterMotorcoach"
+ root.add_child(motorcoach)
+ else:
+ push_error(
+ "The starter motorcoach exterior could not be instantiated."
+ )
+ _add_box_collision(
+ root,
+ "Body",
+ RV_EXTERIOR_BODY_SIZE,
+ Vector3(0.0, RV_EXTERIOR_BODY_CENTER_Y, 0.0),
+ )
+ else:
+ if not _dedicated_runtime:
+ _add_tent_exterior(root)
+ _add_box_collision(
+ root,
+ "Body",
+ TENT_EXTERIOR_BODY_SIZE,
+ Vector3(0.0, TENT_EXTERIOR_BODY_CENTER_Y, 0.0),
+ )
+ if not _dedicated_runtime:
+ _add_exterior_prompt(root, tier)
+ root.visible = not _dedicated_runtime
+ return root
+
+
+func _build_interior(
+ fingerprint: String,
+ interior_index: int,
+ home_data: Dictionary,
+) -> Node3D:
+ var root := Node3D.new()
+ root.name = "HomeInterior_%s" % fingerprint.left(8)
+ root.position = INTERIOR_ORIGIN + Vector3(
+ float(interior_index) * INTERIOR_SPACING, 0.0, 0.0
+ )
+ _interiors_root.add_child(root)
+ var tier: int = int(home_data.get(
+ "tier", PlayerHomeStateType.STARTER_TIER
+ ))
+ var scene: PackedScene = (
+ STARTER_RV_INTERIOR_SCENE
+ if tier >= PlayerHomeStateType.STARTER_RV_TIER
+ else TENT_INTERIOR_SCENE
+ )
+ var shell: Node3D = scene.instantiate() as Node3D
+ if shell == null:
+ push_error("The home interior shell could not be instantiated.")
+ else:
+ shell.name = "InteriorShell"
+ root.add_child(shell)
+ _prepare_imported_interior(shell)
+ var decor := Node3D.new()
+ decor.name = "Decor"
+ root.add_child(decor)
+ _rebuild_decor(decor, home_data, fingerprint)
+ root.visible = false
+ if _dedicated_runtime:
+ _set_geometry_visible(root, false)
+ return root
+
+
+func _add_tent_exterior(parent: Node3D) -> void:
+ var tent := MeshInstance3D.new()
+ tent.name = "StarterTent"
+ var mesh := CylinderMesh.new()
+ mesh.top_radius = 0.0
+ mesh.bottom_radius = 3.05
+ mesh.height = 3.0
+ mesh.radial_segments = 4
+ mesh.rings = 1
+ mesh.cap_top = false
+ mesh.material = _material(Color("9a7049"))
+ tent.mesh = mesh
+ tent.position.y = 1.5
+ tent.rotation.y = PI * 0.25
+ parent.add_child(tent)
+ _add_box(
+ parent,
+ "TentDoor",
+ Vector3(1.25, 1.85, 0.04),
+ Vector3(0.0, 0.95, 2.18),
+ Color("173b32"),
+ false,
+ )
+
+
+func _add_exterior_prompt(parent: Node3D, tier: int) -> void:
+ var prompt := Node3D.new()
+ prompt.name = "DoorPrompt"
+ prompt.position = (
+ RV_EXTERIOR_PROMPT_LOCAL
+ if tier >= PlayerHomeStateType.STARTER_RV_TIER
+ else TENT_EXTERIOR_PROMPT_LOCAL
+ )
+ prompt.visible = false
+ parent.add_child(prompt)
+
+ var background := MeshInstance3D.new()
+ background.name = "Background"
+ background.mesh = _rounded_rectangle_mesh(
+ HOME_PROMPT_SIZE,
+ HOME_PROMPT_CORNER_RADIUS,
+ )
+ background.material_override = _prompt_material(
+ HOME_PROMPT_BACKGROUND_COLOR
+ )
+ prompt.add_child(background)
+
+ var label := Label3D.new()
+ label.name = "Message"
+ label.text = "enter home"
+ label.position.z = 0.012
+ label.font = HOME_PROMPT_FONT
+ label.font_size = 28
+ label.pixel_size = HOME_PROMPT_TEXT_PIXEL_SIZE
+ label.modulate = HOME_PROMPT_TEXT_COLOR
+ label.outline_size = 0
+ label.horizontal_alignment = HORIZONTAL_ALIGNMENT_CENTER
+ label.vertical_alignment = VERTICAL_ALIGNMENT_CENTER
+ label.width = HOME_PROMPT_SIZE.x / HOME_PROMPT_TEXT_PIXEL_SIZE
+ label.double_sided = true
+ prompt.add_child(label)
+
+
+func _rounded_rectangle_mesh(size: Vector2, radius: float) -> ArrayMesh:
+ var half_size: Vector2 = size * 0.5
+ var clamped_radius: float = minf(
+ radius,
+ minf(half_size.x, half_size.y),
+ )
+ var corners: Array[Vector2] = [
+ Vector2(half_size.x - clamped_radius, half_size.y - clamped_radius),
+ Vector2(-half_size.x + clamped_radius, half_size.y - clamped_radius),
+ Vector2(-half_size.x + clamped_radius, -half_size.y + clamped_radius),
+ Vector2(half_size.x - clamped_radius, -half_size.y + clamped_radius),
+ ]
+ var start_angles: Array[float] = [0.0, PI * 0.5, PI, PI * 1.5]
+ var perimeter: Array[Vector2] = []
+ const CORNER_SEGMENTS: int = 4
+ for corner_index: int in corners.size():
+ for segment_index: int in range(CORNER_SEGMENTS + 1):
+ var angle: float = start_angles[corner_index] + (
+ PI * 0.5 * float(segment_index) / float(CORNER_SEGMENTS)
+ )
+ perimeter.append(
+ corners[corner_index]
+ + Vector2(cos(angle), sin(angle)) * clamped_radius
+ )
+ var surface := SurfaceTool.new()
+ surface.begin(Mesh.PRIMITIVE_TRIANGLES)
+ for index: int in perimeter.size():
+ var first: Vector2 = perimeter[index]
+ var second: Vector2 = perimeter[(index + 1) % perimeter.size()]
+ for point: Vector2 in [Vector2.ZERO, first, second]:
+ surface.set_normal(Vector3.FORWARD)
+ surface.add_vertex(Vector3(point.x, point.y, 0.0))
+ return surface.commit()
+
+
+func _prompt_material(color: Color) -> StandardMaterial3D:
+ var material := StandardMaterial3D.new()
+ material.albedo_color = color
+ material.shading_mode = BaseMaterial3D.SHADING_MODE_UNSHADED
+ material.roughness = 1.0
+ material.metallic = 0.0
+ material.cull_mode = BaseMaterial3D.CULL_DISABLED
+ return material
+
+
+func _prepare_imported_interior(shell: Node3D) -> void:
+ for value: Node in shell.find_children("*", "MeshInstance3D", true, false):
+ var mesh_instance := value as MeshInstance3D
+ if mesh_instance == null or mesh_instance.mesh == null:
+ continue
+ for surface_index: int in mesh_instance.mesh.get_surface_count():
+ var source_material: Material = mesh_instance.get_active_material(
+ surface_index
+ )
+ var material := source_material as StandardMaterial3D
+ if material == null:
+ continue
+ var flat_material := material.duplicate() as StandardMaterial3D
+ flat_material.shading_mode = BaseMaterial3D.SHADING_MODE_UNSHADED
+ flat_material.texture_filter = BaseMaterial3D.TEXTURE_FILTER_NEAREST
+ flat_material.roughness = 1.0
+ flat_material.metallic = 0.0
+ mesh_instance.set_surface_override_material(
+ surface_index, flat_material
+ )
+ _add_imported_mesh_collision(mesh_instance)
+
+
+func _add_imported_mesh_collision(mesh_instance: MeshInstance3D) -> void:
+ var parent: Node = mesh_instance.get_parent()
+ if parent == null or mesh_instance.mesh == null:
+ return
+ var shape: ConcavePolygonShape3D = mesh_instance.mesh.create_trimesh_shape()
+ if shape == null:
+ return
+ var body := StaticBody3D.new()
+ body.name = "%sCollision" % mesh_instance.name
+ body.transform = mesh_instance.transform
+ var collision := CollisionShape3D.new()
+ collision.shape = shape
+ body.add_child(collision)
+ parent.add_child(body)
+
+
+func _rebuild_decor(
+ parent: Node3D,
+ home_data: Dictionary,
+ owner_fingerprint: String,
+) -> void:
+ for value: Variant in home_data.get("placements", []):
+ if typeof(value) != TYPE_DICTIONARY:
+ continue
+ var placement: Dictionary = value
+ var decor_id := StringName(str(placement["decor_id"]))
+ if decor_id != DecorCatalogType.BASIC_CUBE_ID:
+ continue
+ var position_values: Array = placement["position"]
+ var placement_root := Node3D.new()
+ placement_root.name = str(placement["instance_id"])
+ placement_root.position = Vector3(
+ float(position_values[0]), 0.18, float(position_values[2])
+ )
+ placement_root.rotation.y = float(placement["yaw"])
+ placement_root.set_meta(
+ &"decor_instance_id", str(placement["instance_id"])
+ )
+ placement_root.set_meta(
+ &"decor_owner_fingerprint", owner_fingerprint
+ )
+ placement_root.set_meta(&"decor_id", String(decor_id))
+ parent.add_child(placement_root)
+ var body := _add_box(
+ placement_root, "BasicCube", BASIC_CUBE_SIZE,
+ Vector3(0.0, BASIC_CUBE_SIZE.y * 0.5, 0.0),
+ Color("b77a55"), true,
+ )
+ if body != null:
+ body.collision_layer = DECOR_SELECTION_COLLISION_LAYER
+ body.set_meta(
+ &"decor_instance_id", str(placement["instance_id"])
+ )
+ body.set_meta(
+ &"decor_owner_fingerprint", owner_fingerprint
+ )
+ body.set_meta(&"decor_id", String(decor_id))
+ _queue_decor_world_collision(
+ owner_fingerprint,
+ str(placement["instance_id"]),
+ decor_id,
+ body,
+ )
+ _add_box(
+ placement_root,
+ "BasicCubeFront",
+ Vector3(BASIC_CUBE_SIZE.x * 0.82, BASIC_CUBE_SIZE.y * 0.68, 0.025),
+ Vector3(
+ 0.0,
+ BASIC_CUBE_SIZE.y * 0.5,
+ -BASIC_CUBE_SIZE.z * 0.5 - 0.013,
+ ),
+ Color("2897a8"),
+ false,
+ )
+
+
+func _add_wall(
+ parent: Node3D,
+ node_name: String,
+ size: Vector3,
+ position: Vector3,
+) -> void:
+ _add_box(parent, node_name, size, position, Color("d6caaa"), true)
+
+
+func _add_box(
+ parent: Node3D,
+ node_name: String,
+ size: Vector3,
+ position: Vector3,
+ color: Color,
+ with_collision: bool,
+ invert_faces: bool = false,
+) -> StaticBody3D:
+ var mesh_instance := MeshInstance3D.new()
+ mesh_instance.name = node_name
+ var mesh := BoxMesh.new()
+ mesh.size = size
+ mesh.material = _material(color)
+ if invert_faces:
+ mesh.flip_faces = true
+ mesh_instance.mesh = mesh
+ mesh_instance.position = position
+ parent.add_child(mesh_instance)
+ if with_collision:
+ return _add_box_collision(parent, node_name, size, position)
+ return null
+
+
+func _add_box_collision(
+ parent: Node3D,
+ node_name: String,
+ size: Vector3,
+ position: Vector3,
+) -> StaticBody3D:
+ var body := StaticBody3D.new()
+ body.name = "%sCollision" % node_name
+ body.position = position
+ var collision := CollisionShape3D.new()
+ var shape := BoxShape3D.new()
+ shape.size = size
+ collision.shape = shape
+ body.add_child(collision)
+ parent.add_child(body)
+ return body
+
+
+func _spawn_decor_poof(
+ owner_fingerprint: String,
+ instance_id: String,
+) -> void:
+ var presentation: Dictionary = _presentations_by_fingerprint.get(
+ owner_fingerprint, {}
+ )
+ var interior: Node3D = presentation.get("interior")
+ if not is_instance_valid(interior):
+ return
+ var placement_root: Node3D = interior.get_node_or_null(
+ "Decor/%s" % instance_id
+ )
+ if placement_root == null:
+ return
+ var poof := CPUParticles3D.new()
+ poof.name = "DecorPoof"
+ poof.amount = 12
+ poof.lifetime = 0.45
+ poof.one_shot = true
+ poof.explosiveness = 0.9
+ poof.direction = Vector3.UP
+ poof.spread = 80.0
+ poof.initial_velocity_min = 0.7
+ poof.initial_velocity_max = 1.5
+ poof.scale_amount_min = 0.08
+ poof.scale_amount_max = 0.16
+ var particle_mesh := SphereMesh.new()
+ particle_mesh.radius = 0.06
+ particle_mesh.height = 0.12
+ particle_mesh.material = _material(Color("d9c5a3"))
+ poof.mesh = particle_mesh
+ placement_root.add_child(poof)
+ poof.emitting = true
+ get_tree().create_timer(poof.lifetime + 0.1).timeout.connect(
+ poof.queue_free
+ )
+
+
+func _material(color: Color) -> StandardMaterial3D:
+ var material := StandardMaterial3D.new()
+ material.albedo_color = color
+ material.roughness = 1.0
+ material.shading_mode = BaseMaterial3D.SHADING_MODE_PER_VERTEX
+ return material
+
+
+func _free_presentation_nodes(presentation: Dictionary) -> void:
+ for key: String in ["exterior", "interior"]:
+ var node: Node = presentation.get(key)
+ if is_instance_valid(node):
+ node.queue_free()
+
+
+func _refresh_local_visibility() -> void:
+ var active_fingerprint: String = ""
+ if String(_local_space).begins_with("rv:"):
+ active_fingerprint = String(_local_space).trim_prefix("rv:")
+ if _world != null:
+ _world.set_local_home_interior_presentation_active(
+ not active_fingerprint.is_empty()
+ )
+ for fingerprint: String in _presentations_by_fingerprint:
+ var presentation: Dictionary = _presentations_by_fingerprint[fingerprint]
+ var interior: Node3D = presentation.get("interior")
+ if is_instance_valid(interior):
+ interior.visible = (
+ not _dedicated_runtime and fingerprint == active_fingerprint
+ )
+ var exterior: Node3D = presentation.get("exterior")
+ if is_instance_valid(exterior):
+ exterior.visible = (
+ not _dedicated_runtime and active_fingerprint.is_empty()
+ )
+ var local_player: Player = (
+ _spawn_service.get_local_player()
+ if _spawn_service != null else null
+ )
+ if local_player == null:
+ return
+ var excluded_wall_rids: Array[RID] = []
+ if not active_fingerprint.is_empty():
+ var active: Dictionary = _presentations_by_fingerprint.get(
+ active_fingerprint, {}
+ )
+ var active_interior: Node3D = active.get("interior")
+ if is_instance_valid(active_interior):
+ for wall_name: String in [
+ "WallNorth", "WallSouth", "WallWest", "WallEast",
+ ]:
+ var body := active_interior.find_child(
+ "%sCollision" % wall_name, true, false
+ ) as StaticBody3D
+ if body != null:
+ excluded_wall_rids.append(body.get_rid())
+ local_player.set_rv_interior_camera_mode(
+ not active_fingerprint.is_empty(), excluded_wall_rids
+ )
+
+
+func _refresh_nearest_wall() -> void:
+ if not String(_local_space).begins_with("rv:") or _spawn_service == null:
+ return
+ var fingerprint: String = String(_local_space).trim_prefix("rv:")
+ var presentation: Dictionary = _presentations_by_fingerprint.get(
+ fingerprint, {}
+ )
+ var interior: Node3D = presentation.get("interior")
+ var player: Player = _spawn_service.get_local_player()
+ if not is_instance_valid(interior) or player == null:
+ return
+ var camera: Camera3D = player.get_active_gameplay_camera()
+ if camera == null:
+ return
+ var local_camera: Vector3 = interior.to_local(camera.global_position)
+ var local_view_direction: Vector3 = (
+ interior.global_basis.inverse() * -camera.global_basis.z
+ ).normalized()
+ var horizontal_view_weight: float = absf(local_view_direction.x)
+ var depth_view_weight: float = absf(local_view_direction.z)
+ var tier: int = int((presentation.get("home", {}) as Dictionary).get(
+ "tier", PlayerHomeStateType.STARTER_TIER
+ ))
+ var interior_size: Vector2 = (
+ STARTER_RV_INTERIOR_SIZE
+ if tier >= PlayerHomeStateType.STARTER_RV_TIER
+ else TENT_INTERIOR_SIZE
+ )
+ var hidden_walls: Dictionary[String, bool] = {}
+ var outside_horizontal_wall: bool = (
+ absf(local_camera.x) >= interior_size.x * 0.5 - 0.05
+ )
+ var outside_depth_wall: bool = (
+ absf(local_camera.z) >= interior_size.y * 0.5 - 0.05
+ )
+ # At a diagonal, both near walls occupy the view. Use the camera's viewing
+ # direction to hide both instead of choosing only the dominant position
+ # axis and leaving the adjacent wall across the screen.
+ if (
+ outside_horizontal_wall
+ and horizontal_view_weight >= depth_view_weight * 0.35
+ ):
+ hidden_walls[
+ "WallEast" if local_camera.x >= 0.0 else "WallWest"
+ ] = true
+ if (
+ outside_depth_wall
+ and depth_view_weight >= horizontal_view_weight * 0.35
+ ):
+ hidden_walls[
+ "WallSouth" if local_camera.z >= 0.0 else "WallNorth"
+ ] = true
+ if hidden_walls.is_empty():
+ var fallback_wall: String = (
+ "WallEast" if local_camera.x >= 0.0 else "WallWest"
+ )
+ if depth_view_weight > horizontal_view_weight:
+ fallback_wall = (
+ "WallSouth" if local_camera.z >= 0.0 else "WallNorth"
+ )
+ hidden_walls[fallback_wall] = true
+ for wall_name: String in [
+ "WallNorth", "WallSouth", "WallWest", "WallEast",
+ ]:
+ var wall := interior.find_child(
+ wall_name, true, false
+ ) as MeshInstance3D
+ if wall != null:
+ wall.visible = not hidden_walls.has(wall_name)
+ for child: Node in interior.find_children(
+ "%s*" % wall_name, "GeometryInstance3D", true, false
+ ):
+ if (
+ child is GeometryInstance3D
+ and String(child.name).begins_with(wall_name)
+ ):
+ (child as GeometryInstance3D).visible = (
+ not hidden_walls.has(wall_name)
+ )
+
+
+func _too_close_to_reserved(
+ position: Vector3,
+ reserved_transforms: Array[Transform3D],
+) -> bool:
+ for transform: Transform3D in reserved_transforms:
+ if position.distance_to(transform.origin) < MIN_EXTERIOR_SEPARATION:
+ return true
+ return false
+
+
+func _set_geometry_visible(root: Node, visible: bool) -> void:
+ for node: Node in root.find_children("*", "GeometryInstance3D", true, false):
+ (node as GeometryInstance3D).visible = visible
diff --git a/homes/home_world_service.gd.uid b/homes/home_world_service.gd.uid
new file mode 100644
index 0000000..5505d06
--- /dev/null
+++ b/homes/home_world_service.gd.uid
@@ -0,0 +1 @@
+uid://cy2kjoxhwxujd
diff --git a/homes/player_home_state.gd b/homes/player_home_state.gd
new file mode 100644
index 0000000..a89d6a3
--- /dev/null
+++ b/homes/player_home_state.gd
@@ -0,0 +1,841 @@
+class_name PlayerHomeState
+extends Node
+
+signal changed
+
+const DecorCatalogType = preload("res://homes/decor_catalog.gd")
+
+const HOME_SCHEMA_VERSION: int = 1
+const STARTER_TIER: int = 0
+const STARTER_EXTERIOR_ID: StringName = &"starter_tent"
+const STARTER_RV_TIER: int = 1
+const STARTER_RV_EXTERIOR_ID: StringName = &"starter_motorcoach"
+const MAX_TIER: int = STARTER_RV_TIER
+const HOME_UPGRADE_COSTS: Array[int] = [50]
+const STORAGE_CAPACITIES: Array[int] = [9, 18]
+const MAX_PLACEMENTS: int = 128
+const MAX_INSTANCE_ID_LENGTH: int = 96
+const MAX_REVISION: int = 2147483647
+const GRID_SIZE: float = 0.5
+const FREE_PLACEMENT_SEARCH_STEP: float = 0.1
+const FREE_PLACEMENT_SEARCH_ANGLES: int = 24
+const NEAREST_PLACEMENT_SEARCH_DISTANCE: float = 2.0
+const TENT_INTERIOR_HALF_EXTENTS := Vector2(1.91, 1.91)
+const STARTER_RV_INTERIOR_HALF_EXTENTS := Vector2(1.91, 2.91)
+const PLACEMENT_EDGE_PADDING: float = 0.02
+
+enum Privacy {
+ OPEN,
+ CLOSED,
+}
+
+enum PlacementMode {
+ GRID,
+ FREE,
+}
+
+var _tier: int = STARTER_TIER
+var _exterior_id: StringName = STARTER_EXTERIOR_ID
+var _privacy: Privacy = Privacy.OPEN
+var _owned_decor: Dictionary[StringName, int] = {}
+var _placements: Array[Dictionary] = []
+var _revision: int = 0
+var _next_instance_sequence: int = 1
+
+
+func get_tier() -> int:
+ return _tier
+
+
+func get_exterior_id() -> StringName:
+ return _exterior_id
+
+
+func get_storage_capacity() -> int:
+ return storage_capacity_for_tier(_tier)
+
+
+func get_next_upgrade_cost() -> int:
+ if _tier >= MAX_TIER:
+ return -1
+ return HOME_UPGRADE_COSTS[_tier]
+
+
+func get_next_storage_capacity() -> int:
+ if _tier >= MAX_TIER:
+ return -1
+ return storage_capacity_for_tier(_tier + 1)
+
+
+func upgrade_to_next_tier() -> bool:
+ if _tier >= MAX_TIER:
+ return false
+ _tier += 1
+ _exterior_id = exterior_id_for_tier(_tier)
+ _touch()
+ return true
+
+
+static func exterior_id_for_tier(tier: int) -> StringName:
+ return (
+ STARTER_RV_EXTERIOR_ID
+ if tier >= STARTER_RV_TIER
+ else STARTER_EXTERIOR_ID
+ )
+
+
+static func storage_capacity_for_tier(tier: int) -> int:
+ return STORAGE_CAPACITIES[clampi(tier, STARTER_TIER, MAX_TIER)]
+
+
+static func interior_half_extents_for_tier(tier: int) -> Vector2:
+ return (
+ STARTER_RV_INTERIOR_HALF_EXTENTS
+ if tier >= STARTER_RV_TIER
+ else TENT_INTERIOR_HALF_EXTENTS
+ )
+
+
+func get_privacy() -> Privacy:
+ return _privacy
+
+
+func get_revision() -> int:
+ return _revision
+
+
+func get_owned_count(decor_id: StringName) -> int:
+ return int(_owned_decor.get(decor_id, 0))
+
+
+func get_placed_count(decor_id: StringName) -> int:
+ var count: int = 0
+ for placement: Dictionary in _placements:
+ if StringName(str(placement.get("decor_id", ""))) == decor_id:
+ count += 1
+ return count
+
+
+func get_available_count(decor_id: StringName) -> int:
+ return maxi(
+ get_owned_count(decor_id) - get_placed_count(decor_id),
+ 0,
+ )
+
+
+func get_owned_decor() -> Dictionary[StringName, int]:
+ return _owned_decor.duplicate()
+
+
+func get_placements() -> Array[Dictionary]:
+ var result: Array[Dictionary] = []
+ for placement: Dictionary in _placements:
+ result.append(placement.duplicate(true))
+ return result
+
+
+func get_placement(instance_id: String) -> Dictionary:
+ var index: int = _placement_index(instance_id)
+ return _placements[index].duplicate(true) if index >= 0 else {}
+
+
+func can_place_decor(
+ decor_id: StringName,
+ position: Vector3,
+ yaw_radians: float,
+ mode: PlacementMode,
+) -> bool:
+ if (
+ get_available_count(decor_id) <= 0
+ or _placements.size() >= MAX_PLACEMENTS
+ or not _valid_mode(mode)
+ ):
+ return false
+ return not get_nearest_valid_placement_transform(
+ decor_id,
+ position,
+ yaw_radians,
+ mode,
+ "",
+ ).is_empty()
+
+
+func set_privacy(value: Privacy) -> bool:
+ if value < Privacy.OPEN or value > Privacy.CLOSED:
+ return false
+ if _privacy == value:
+ return true
+ _privacy = value
+ _touch()
+ return true
+
+
+func add_owned_decor(
+ decor_id: StringName,
+ quantity: int = 1,
+) -> bool:
+ if (
+ not DecorCatalogType.has_product(decor_id)
+ or quantity <= 0
+ ):
+ return false
+ var current: int = get_owned_count(decor_id)
+ if current + quantity > DecorCatalogType.MAX_OWNED_PER_PRODUCT:
+ return false
+ _owned_decor[decor_id] = current + quantity
+ _touch()
+ return true
+
+
+func remove_owned_decor(
+ decor_id: StringName,
+ quantity: int = 1,
+) -> bool:
+ if quantity <= 0:
+ return false
+ var current: int = get_owned_count(decor_id)
+ if current - quantity < get_placed_count(decor_id):
+ return false
+ var remaining: int = current - quantity
+ if remaining < 0:
+ return false
+ if remaining == 0:
+ _owned_decor.erase(decor_id)
+ else:
+ _owned_decor[decor_id] = remaining
+ _touch()
+ return true
+
+
+func place_decor(
+ decor_id: StringName,
+ position: Vector3,
+ yaw_radians: float,
+ mode: PlacementMode,
+) -> String:
+ if (
+ get_available_count(decor_id) <= 0
+ or _placements.size() >= MAX_PLACEMENTS
+ or not _valid_mode(mode)
+ ):
+ return ""
+ var normalized := get_nearest_valid_placement_transform(
+ decor_id,
+ position,
+ yaw_radians,
+ mode,
+ "",
+ )
+ if normalized.is_empty():
+ return ""
+ var instance_id := _make_instance_id()
+ var placement: Dictionary = {
+ "instance_id": instance_id,
+ "decor_id": String(decor_id),
+ "mode": int(mode),
+ "position": _vector3_to_array(normalized["position"]),
+ "yaw": float(normalized["yaw"]),
+ }
+ _placements.append(placement)
+ _touch()
+ return instance_id
+
+
+func update_placement(
+ instance_id: String,
+ position: Vector3,
+ yaw_radians: float,
+ mode: PlacementMode,
+) -> bool:
+ var index: int = _placement_index(instance_id)
+ var normalized: Dictionary = get_valid_update_transform(
+ instance_id, position, yaw_radians, mode
+ )
+ if index < 0 or normalized.is_empty():
+ return false
+ var placement: Dictionary = _placements[index].duplicate(true)
+ placement["mode"] = int(mode)
+ placement["position"] = _vector3_to_array(normalized["position"])
+ placement["yaw"] = float(normalized["yaw"])
+ _placements[index] = placement
+ _touch()
+ return true
+
+
+func get_valid_update_transform(
+ instance_id: String,
+ position: Vector3,
+ yaw_radians: float,
+ mode: PlacementMode,
+ candidate_validator: Callable = Callable(),
+) -> Dictionary:
+ var index: int = _placement_index(instance_id)
+ if index < 0 or not _valid_mode(mode):
+ return {}
+ var decor_id := StringName(
+ str(_placements[index].get("decor_id", ""))
+ )
+ return get_nearest_valid_placement_transform(
+ decor_id,
+ position,
+ yaw_radians,
+ mode,
+ instance_id,
+ candidate_validator,
+ )
+
+
+func get_nearest_valid_placement_transform(
+ decor_id: StringName,
+ position: Vector3,
+ yaw_radians: float,
+ mode: PlacementMode,
+ ignored_instance_id: String = "",
+ candidate_validator: Callable = Callable(),
+) -> Dictionary:
+ var normalized: Dictionary = normalized_placement_transform(
+ decor_id, position, yaw_radians, mode, _tier
+ )
+ if normalized.is_empty():
+ return {}
+ if (
+ not _overlaps_existing(
+ decor_id, normalized, ignored_instance_id
+ )
+ and _candidate_is_accepted(normalized, candidate_validator)
+ ):
+ return normalized
+ return (
+ _find_nearest_grid_transform(
+ decor_id,
+ normalized,
+ ignored_instance_id,
+ candidate_validator,
+ )
+ if mode == PlacementMode.GRID
+ else _find_nearest_free_transform(
+ decor_id,
+ normalized,
+ ignored_instance_id,
+ candidate_validator,
+ )
+ )
+
+
+func remove_placement(instance_id: String) -> bool:
+ var index: int = _placement_index(instance_id)
+ if index < 0:
+ return false
+ _placements.remove_at(index)
+ _touch()
+ return true
+
+
+func reset_to_defaults() -> void:
+ _tier = STARTER_TIER
+ _exterior_id = STARTER_EXTERIOR_ID
+ _privacy = Privacy.OPEN
+ _owned_decor.clear()
+ _placements.clear()
+ _revision = 0
+ _next_instance_sequence = 1
+ changed.emit()
+
+
+func to_save_data() -> Dictionary:
+ var owned: Dictionary = {}
+ var ids: Array[StringName] = []
+ ids.assign(_owned_decor.keys())
+ ids.sort_custom(func(first: StringName, second: StringName) -> bool:
+ return String(first) < String(second)
+ )
+ for decor_id: StringName in ids:
+ owned[String(decor_id)] = _owned_decor[decor_id]
+ return {
+ "schema_version": HOME_SCHEMA_VERSION,
+ "tier": _tier,
+ "exterior_id": String(_exterior_id),
+ "privacy": int(_privacy),
+ "owned_decor": owned,
+ "placements": get_placements(),
+ "revision": _revision,
+ "next_instance_sequence": _next_instance_sequence,
+ }
+
+
+func restore_from_save_data(data: Dictionary) -> bool:
+ var sanitized: Dictionary = sanitize_save_data(data)
+ if sanitized.is_empty():
+ return false
+ _tier = int(sanitized["tier"])
+ _exterior_id = exterior_id_for_tier(_tier)
+ _privacy = int(sanitized["privacy"])
+ _owned_decor.clear()
+ for key: Variant in (sanitized["owned_decor"] as Dictionary):
+ _owned_decor[StringName(str(key))] = int(
+ (sanitized["owned_decor"] as Dictionary)[key]
+ )
+ _placements.clear()
+ for value: Variant in sanitized["placements"] as Array:
+ _placements.append((value as Dictionary).duplicate(true))
+ _revision = int(sanitized["revision"])
+ _next_instance_sequence = int(sanitized["next_instance_sequence"])
+ changed.emit()
+ return true
+
+
+static func default_save_data() -> Dictionary:
+ return {
+ "schema_version": HOME_SCHEMA_VERSION,
+ "tier": STARTER_TIER,
+ "exterior_id": String(STARTER_EXTERIOR_ID),
+ "privacy": int(Privacy.OPEN),
+ "owned_decor": {},
+ "placements": [],
+ "revision": 0,
+ "next_instance_sequence": 1,
+ }
+
+
+static func sanitize_save_data(data: Dictionary) -> Dictionary:
+ if (
+ not _is_integer_value(data.get("schema_version"))
+ or int(data.get("schema_version", -1)) != HOME_SCHEMA_VERSION
+ or not _is_integer_value(data.get("tier"))
+ or typeof(data.get("exterior_id")) not in [
+ TYPE_STRING, TYPE_STRING_NAME,
+ ]
+ or not _is_integer_value(data.get("privacy"))
+ or typeof(data.get("owned_decor")) != TYPE_DICTIONARY
+ or typeof(data.get("placements")) != TYPE_ARRAY
+ or not _is_integer_value(data.get("revision"))
+ or not _is_integer_value(data.get("next_instance_sequence"))
+ ):
+ return {}
+ var tier: int = int(data["tier"])
+ var exterior_id: String = str(data["exterior_id"])
+ var privacy: int = int(data["privacy"])
+ var revision: int = int(data["revision"])
+ var next_sequence: int = int(data["next_instance_sequence"])
+ if (
+ tier < STARTER_TIER
+ or tier > MAX_TIER
+ or exterior_id.is_empty()
+ or exterior_id.length() > 96
+ or privacy < Privacy.OPEN
+ or privacy > Privacy.CLOSED
+ or revision < 0
+ or revision > MAX_REVISION
+ or next_sequence < 1
+ or next_sequence > MAX_REVISION
+ ):
+ return {}
+ exterior_id = String(exterior_id_for_tier(tier))
+ var sanitized_owned: Dictionary = {}
+ var owned: Dictionary = data["owned_decor"]
+ for key: Variant in owned:
+ if typeof(key) not in [TYPE_STRING, TYPE_STRING_NAME]:
+ return {}
+ var decor_id := StringName(str(key))
+ var quantity_value: Variant = owned[key]
+ if (
+ not DecorCatalogType.has_product(decor_id)
+ or not _is_integer_value(quantity_value)
+ or int(quantity_value) <= 0
+ or int(quantity_value) > DecorCatalogType.MAX_OWNED_PER_PRODUCT
+ ):
+ return {}
+ sanitized_owned[String(decor_id)] = int(quantity_value)
+ var values: Array = data["placements"]
+ if values.size() > MAX_PLACEMENTS:
+ return {}
+ var sanitized_placements: Array[Dictionary] = []
+ var seen_instance_ids: Dictionary[String, bool] = {}
+ var placed_counts: Dictionary[StringName, int] = {}
+ for value: Variant in values:
+ if typeof(value) != TYPE_DICTIONARY:
+ return {}
+ var sanitized: Dictionary = sanitize_placement(value, tier)
+ if sanitized.is_empty():
+ return {}
+ var instance_id: String = str(sanitized["instance_id"])
+ var decor_id := StringName(str(sanitized["decor_id"]))
+ if seen_instance_ids.has(instance_id):
+ return {}
+ seen_instance_ids[instance_id] = true
+ placed_counts[decor_id] = int(
+ placed_counts.get(decor_id, 0)
+ ) + 1
+ if (
+ int(placed_counts[decor_id])
+ > int(sanitized_owned.get(String(decor_id), 0))
+ ):
+ return {}
+ for previous: Dictionary in sanitized_placements:
+ if placements_overlap(previous, sanitized):
+ return {}
+ sanitized_placements.append(sanitized)
+ return {
+ "schema_version": HOME_SCHEMA_VERSION,
+ "tier": tier,
+ "exterior_id": exterior_id,
+ "privacy": privacy,
+ "owned_decor": sanitized_owned,
+ "placements": sanitized_placements,
+ "revision": revision,
+ "next_instance_sequence": next_sequence,
+ }
+
+
+static func sanitize_placement(
+ value: Dictionary,
+ home_tier: int = STARTER_TIER,
+) -> Dictionary:
+ for key: String in [
+ "instance_id", "decor_id", "mode", "position", "yaw",
+ ]:
+ if not value.has(key):
+ return {}
+ if (
+ typeof(value["instance_id"]) != TYPE_STRING
+ or typeof(value["decor_id"]) not in [
+ TYPE_STRING, TYPE_STRING_NAME,
+ ]
+ or not _is_integer_value(value["mode"])
+ or typeof(value["position"]) != TYPE_ARRAY
+ or typeof(value["yaw"]) not in [TYPE_FLOAT, TYPE_INT]
+ ):
+ return {}
+ var instance_id: String = value["instance_id"]
+ var decor_id := StringName(str(value["decor_id"]))
+ var mode: int = int(value["mode"])
+ var position_values: Array = value["position"]
+ var yaw: float = float(value["yaw"])
+ if (
+ instance_id.is_empty()
+ or instance_id.length() > MAX_INSTANCE_ID_LENGTH
+ or not DecorCatalogType.has_product(decor_id)
+ or not _valid_mode(mode)
+ or position_values.size() != 3
+ or not is_finite(yaw)
+ ):
+ return {}
+ for component: Variant in position_values:
+ if typeof(component) not in [TYPE_FLOAT, TYPE_INT]:
+ return {}
+ var position := Vector3(
+ float(position_values[0]),
+ float(position_values[1]),
+ float(position_values[2]),
+ )
+ var normalized := normalized_placement_transform(
+ decor_id,
+ position,
+ yaw,
+ mode as PlacementMode,
+ home_tier,
+ )
+ if normalized.is_empty():
+ return {}
+ return {
+ "instance_id": instance_id,
+ "decor_id": String(decor_id),
+ "mode": mode,
+ "position": _vector3_to_array(normalized["position"]),
+ "yaw": float(normalized["yaw"]),
+ }
+
+
+static func _is_integer_value(value: Variant) -> bool:
+ if typeof(value) == TYPE_INT:
+ return true
+ if typeof(value) != TYPE_FLOAT:
+ return false
+ var number: float = float(value)
+ return is_finite(number) and is_equal_approx(number, roundf(number))
+
+
+static func normalized_placement_transform(
+ decor_id: StringName,
+ position: Vector3,
+ yaw_radians: float,
+ mode: PlacementMode,
+ home_tier: int = STARTER_TIER,
+) -> Dictionary:
+ if (
+ not DecorCatalogType.has_product(decor_id)
+ or not position.is_finite()
+ or not is_finite(yaw_radians)
+ or not _valid_mode(mode)
+ ):
+ return {}
+ var normalized_position := Vector3(position.x, 0.0, position.z)
+ var normalized_yaw: float = wrapf(yaw_radians, -PI, PI)
+ if mode == PlacementMode.GRID:
+ normalized_position.x = snappedf(normalized_position.x, GRID_SIZE)
+ normalized_position.z = snappedf(normalized_position.z, GRID_SIZE)
+ normalized_yaw = snappedf(normalized_yaw, PI * 0.5)
+ var footprint: Vector2 = DecorCatalogType.get_footprint(decor_id)
+ var cosine: float = absf(cos(normalized_yaw))
+ var sine: float = absf(sin(normalized_yaw))
+ var half_size := Vector2(
+ (footprint.x * cosine + footprint.y * sine) * 0.5,
+ (footprint.x * sine + footprint.y * cosine) * 0.5,
+ )
+ var interior_half_extents := interior_half_extents_for_tier(home_tier)
+ var maximum_x: float = (
+ interior_half_extents.x - half_size.x - PLACEMENT_EDGE_PADDING
+ )
+ var maximum_z: float = (
+ interior_half_extents.y - half_size.y - PLACEMENT_EDGE_PADDING
+ )
+ if maximum_x < 0.0 or maximum_z < 0.0:
+ return {}
+ # Clamp after grid snapping so a grid-placed item can still sit flush at a
+ # wall even when the wall itself is not on an exact grid interval.
+ normalized_position.x = clampf(
+ normalized_position.x, -maximum_x, maximum_x
+ )
+ normalized_position.z = clampf(
+ normalized_position.z, -maximum_z, maximum_z
+ )
+ return {
+ "position": normalized_position,
+ "yaw": normalized_yaw,
+ }
+
+
+func _find_nearest_grid_transform(
+ decor_id: StringName,
+ origin: Dictionary,
+ ignored_instance_id: String,
+ candidate_validator: Callable,
+) -> Dictionary:
+ var origin_position: Vector3 = origin["position"]
+ var yaw: float = float(origin["yaw"])
+ var maximum_ring: int = ceili(
+ NEAREST_PLACEMENT_SEARCH_DISTANCE / GRID_SIZE
+ )
+ var seen: Dictionary[String, bool] = {}
+ for ring: int in range(1, maximum_ring + 1):
+ var nearest: Dictionary = {}
+ var nearest_distance_squared: float = INF
+ for x_offset: int in range(-ring, ring + 1):
+ for z_offset: int in range(-ring, ring + 1):
+ if maxi(absi(x_offset), absi(z_offset)) != ring:
+ continue
+ var candidate: Dictionary = normalized_placement_transform(
+ decor_id,
+ origin_position + Vector3(
+ float(x_offset) * GRID_SIZE,
+ 0.0,
+ float(z_offset) * GRID_SIZE,
+ ),
+ yaw,
+ PlacementMode.GRID,
+ _tier,
+ )
+ if candidate.is_empty() or not _mark_candidate_unseen(
+ candidate, seen
+ ):
+ continue
+ if _overlaps_existing(
+ decor_id, candidate, ignored_instance_id
+ ) or not _candidate_is_accepted(
+ candidate, candidate_validator
+ ):
+ continue
+ var candidate_position: Vector3 = candidate["position"]
+ var distance_squared: float = origin_position.distance_squared_to(
+ candidate_position
+ )
+ if distance_squared < nearest_distance_squared:
+ nearest = candidate
+ nearest_distance_squared = distance_squared
+ if not nearest.is_empty():
+ return nearest
+ return {}
+
+
+func _find_nearest_free_transform(
+ decor_id: StringName,
+ origin: Dictionary,
+ ignored_instance_id: String,
+ candidate_validator: Callable,
+) -> Dictionary:
+ var origin_position: Vector3 = origin["position"]
+ var yaw: float = float(origin["yaw"])
+ var maximum_ring: int = ceili(
+ NEAREST_PLACEMENT_SEARCH_DISTANCE / FREE_PLACEMENT_SEARCH_STEP
+ )
+ var seen: Dictionary[String, bool] = {}
+ for ring: int in range(1, maximum_ring + 1):
+ var radius: float = float(ring) * FREE_PLACEMENT_SEARCH_STEP
+ var nearest: Dictionary = {}
+ var nearest_distance_squared: float = INF
+ for angle_index: int in FREE_PLACEMENT_SEARCH_ANGLES:
+ var angle: float = (
+ TAU * float(angle_index) / float(FREE_PLACEMENT_SEARCH_ANGLES)
+ )
+ var candidate: Dictionary = normalized_placement_transform(
+ decor_id,
+ origin_position + Vector3(
+ cos(angle) * radius, 0.0, sin(angle) * radius
+ ),
+ yaw,
+ PlacementMode.FREE,
+ _tier,
+ )
+ if candidate.is_empty() or not _mark_candidate_unseen(
+ candidate, seen
+ ):
+ continue
+ if _overlaps_existing(
+ decor_id, candidate, ignored_instance_id
+ ) or not _candidate_is_accepted(
+ candidate, candidate_validator
+ ):
+ continue
+ var candidate_position: Vector3 = candidate["position"]
+ var distance_squared: float = origin_position.distance_squared_to(
+ candidate_position
+ )
+ if distance_squared < nearest_distance_squared:
+ nearest = candidate
+ nearest_distance_squared = distance_squared
+ if not nearest.is_empty():
+ return nearest
+ return {}
+
+
+static func _mark_candidate_unseen(
+ candidate: Dictionary,
+ seen: Dictionary[String, bool],
+) -> bool:
+ var position: Vector3 = candidate["position"]
+ var key := "%d:%d" % [
+ roundi(position.x * 10000.0),
+ roundi(position.z * 10000.0),
+ ]
+ if seen.has(key):
+ return false
+ seen[key] = true
+ return true
+
+
+static func _candidate_is_accepted(
+ candidate: Dictionary,
+ validator: Callable,
+) -> bool:
+ return not validator.is_valid() or bool(validator.call(candidate))
+
+
+static func placements_overlap(first: Dictionary, second: Dictionary) -> bool:
+ var first_id := StringName(str(first.get("decor_id", "")))
+ var second_id := StringName(str(second.get("decor_id", "")))
+ if (
+ not DecorCatalogType.has_product(first_id)
+ or not DecorCatalogType.has_product(second_id)
+ ):
+ return true
+ var first_position: Vector3 = _array_to_vector3(first.get("position", []))
+ var second_position: Vector3 = _array_to_vector3(second.get("position", []))
+ var first_yaw: float = float(first.get("yaw", 0.0))
+ var second_yaw: float = float(second.get("yaw", 0.0))
+ var first_half: Vector2 = (
+ DecorCatalogType.get_footprint(first_id) * 0.5
+ )
+ var second_half: Vector2 = (
+ DecorCatalogType.get_footprint(second_id) * 0.5
+ )
+ var first_axes: Array[Vector2] = _placement_axes(first_yaw)
+ var second_axes: Array[Vector2] = _placement_axes(second_yaw)
+ var delta := Vector2(
+ second_position.x - first_position.x,
+ second_position.z - first_position.z,
+ )
+ for axis: Vector2 in [
+ first_axes[0], first_axes[1], second_axes[0], second_axes[1],
+ ]:
+ var first_radius: float = (
+ absf(axis.dot(first_axes[0])) * first_half.x
+ + absf(axis.dot(first_axes[1])) * first_half.y
+ )
+ var second_radius: float = (
+ absf(axis.dot(second_axes[0])) * second_half.x
+ + absf(axis.dot(second_axes[1])) * second_half.y
+ )
+ if absf(delta.dot(axis)) >= (
+ first_radius + second_radius - 0.001
+ ):
+ return false
+ return true
+
+
+func _make_instance_id() -> String:
+ var result := "decor-%d-%d" % [
+ Time.get_ticks_usec(),
+ _next_instance_sequence,
+ ]
+ _next_instance_sequence = mini(
+ _next_instance_sequence + 1,
+ MAX_REVISION,
+ )
+ return result
+
+
+func _placement_index(instance_id: String) -> int:
+ if instance_id.is_empty():
+ return -1
+ for index: int in _placements.size():
+ if str(_placements[index].get("instance_id", "")) == instance_id:
+ return index
+ return -1
+
+
+func _overlaps_existing(
+ decor_id: StringName,
+ normalized: Dictionary,
+ ignored_instance_id: String,
+) -> bool:
+ var candidate: Dictionary = {
+ "decor_id": String(decor_id),
+ "position": _vector3_to_array(normalized["position"]),
+ "yaw": float(normalized["yaw"]),
+ }
+ for placement: Dictionary in _placements:
+ if str(placement.get("instance_id", "")) == ignored_instance_id:
+ continue
+ if placements_overlap(candidate, placement):
+ return true
+ return false
+
+
+func _touch() -> void:
+ _revision = mini(_revision + 1, MAX_REVISION)
+ changed.emit()
+
+
+static func _valid_mode(value: int) -> bool:
+ return value >= PlacementMode.GRID and value <= PlacementMode.FREE
+
+
+static func _vector3_to_array(value: Vector3) -> Array[float]:
+ return [value.x, value.y, value.z]
+
+
+static func _array_to_vector3(value: Variant) -> Vector3:
+ if typeof(value) != TYPE_ARRAY or (value as Array).size() != 3:
+ return Vector3.INF
+ var components: Array = value
+ return Vector3(
+ float(components[0]),
+ float(components[1]),
+ float(components[2]),
+ )
+
+
+static func _placement_axes(yaw: float) -> Array[Vector2]:
+ var cosine: float = cos(yaw)
+ var sine: float = sin(yaw)
+ return [Vector2(cosine, sine), Vector2(-sine, cosine)]
diff --git a/homes/player_home_state.gd.uid b/homes/player_home_state.gd.uid
new file mode 100644
index 0000000..37836c6
--- /dev/null
+++ b/homes/player_home_state.gd.uid
@@ -0,0 +1 @@
+uid://ggfdoak2ixxx
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/inventory/fish_inventory.gd b/inventory/fish_inventory.gd
index a4052f5..9318457 100644
--- a/inventory/fish_inventory.gd
+++ b/inventory/fish_inventory.gd
@@ -9,6 +9,7 @@ signal catches_changed
var _catches: Array[FishCatchType] = []
var _next_catch_sequence: int = 1
var _reservation_service: PlayerAssetReservationService
+var _inventory_layout: PlayerInventoryLayout
func set_reservation_service(
@@ -17,11 +18,24 @@ func set_reservation_service(
_reservation_service = reservation_service
-func add_catch(fish_catch: FishCatchType) -> void:
+func set_inventory_layout(layout: PlayerInventoryLayout) -> void:
+ _inventory_layout = layout
+
+
+func can_accept_catch(catch_id: StringName = StringName()) -> bool:
+ return (
+ _inventory_layout == null
+ or _inventory_layout.can_accept_catch(catch_id)
+ )
+
+
+func add_catch(fish_catch: FishCatchType) -> bool:
if fish_catch == null or not fish_catch.is_valid():
- return
+ return false
if get_catch_by_id(fish_catch.catch_id) != null:
- return
+ return false
+ if not can_accept_catch(fish_catch.catch_id):
+ return false
if fish_catch.catch_sequence <= 0:
fish_catch.catch_sequence = _next_catch_sequence
_next_catch_sequence = maxi(
@@ -34,6 +48,7 @@ func add_catch(fish_catch: FishCatchType) -> void:
get_count(fish_catch.fish_id)
)
catches_changed.emit()
+ return true
func get_count(fish_id: StringName) -> int:
diff --git a/inventory/player_bag.gd b/inventory/player_bag.gd
index 4fc12b6..d94845d 100644
--- a/inventory/player_bag.gd
+++ b/inventory/player_bag.gd
@@ -6,10 +6,21 @@ const ItemDataType = preload("res://items/item_data.gd")
const OwnedItemType = preload("res://items/owned_item.gd")
const DEFAULT_UNLOCKED_BAIT_IDS: Array[StringName] = [&"worms"]
+const MIN_STORAGE_SLOT_COUNT: int = 15
+const MAX_STORAGE_SLOT_INDEX: int = 254
+
+enum StorageGroup {
+ NONE,
+ EQUIPMENT,
+ ITEMS,
+ BAIT,
+ LURES,
+}
signal contents_changed
var _catalog: ItemCatalogType
+var _inventory_layout: PlayerInventoryLayout
var _items: Array[OwnedItemType] = []
var _unlocked_bait_ids: Array[StringName] = (
DEFAULT_UNLOCKED_BAIT_IDS.duplicate()
@@ -20,17 +31,28 @@ func setup(catalog: ItemCatalogType) -> void:
_catalog = catalog
+func set_inventory_layout(layout: PlayerInventoryLayout) -> void:
+ _inventory_layout = layout
+
+
func add_item(item_id: StringName, quantity: int = 1) -> bool:
if not can_add_item(item_id, quantity):
return false
var item: ItemDataType = _resolve_valid_item(item_id)
var existing: OwnedItemType = get_owned_item(item_id)
+ if (
+ existing == null
+ and _inventory_layout != null
+ and not _inventory_layout.can_accept_item(item_id)
+ ):
+ return false
if existing != null:
existing.quantity += quantity
else:
var owned := OwnedItemType.new()
owned.item_id = item_id
owned.quantity = quantity
+ owned.storage_slot = _first_available_storage_slot(item, _items)
_items.append(owned)
if item.is_bait() and not _unlocked_bait_ids.has(item_id):
_unlocked_bait_ids.append(item_id)
@@ -38,8 +60,30 @@ func add_item(item_id: StringName, quantity: int = 1) -> bool:
return true
+func add_item_to_storage(item_id: StringName, quantity: int = 1) -> bool:
+ var existing: OwnedItemType = get_owned_item(item_id)
+ if existing != null:
+ return add_item(item_id, quantity)
+ var item: ItemDataType = _resolve_available_item(item_id)
+ if (
+ item == null
+ or quantity <= 0
+ or quantity > (item.max_stack if item.stackable else 1)
+ or _inventory_layout == null
+ or not _inventory_layout.can_accept_item_in_storage(item_id)
+ ):
+ return false
+ var owned := OwnedItemType.new()
+ owned.item_id = item_id
+ owned.quantity = quantity
+ owned.storage_slot = _first_available_storage_slot(item, _items)
+ _items.append(owned)
+ contents_changed.emit()
+ return true
+
+
func can_add_item(item_id: StringName, quantity: int = 1) -> bool:
- var item: ItemDataType = _resolve_valid_item(item_id)
+ var item: ItemDataType = _resolve_available_item(item_id)
if item == null or quantity <= 0:
return false
var existing: OwnedItemType = get_owned_item(item_id)
@@ -48,6 +92,11 @@ func can_add_item(item_id: StringName, quantity: int = 1) -> bool:
item.stackable
and existing.quantity + quantity <= item.max_stack
)
+ if (
+ _inventory_layout != null
+ and not _inventory_layout.can_accept_item(item_id)
+ ):
+ return false
return quantity <= (item.max_stack if item.stackable else 1)
@@ -92,6 +141,42 @@ func get_all_items() -> Array[OwnedItemType]:
return result
+func get_storage_slot(item_id: StringName) -> int:
+ var owned: OwnedItemType = get_owned_item(item_id)
+ return owned.storage_slot if owned != null else -1
+
+
+func move_item_to_storage_slot(item_id: StringName, target_slot: int) -> bool:
+ if target_slot < 0 or target_slot > MAX_STORAGE_SLOT_INDEX:
+ return false
+ var owned: OwnedItemType = get_owned_item(item_id)
+ var item: ItemDataType = _resolve_valid_item(item_id)
+ var group: StorageGroup = _storage_group_for_item(item)
+ if owned == null or group == StorageGroup.NONE:
+ return false
+ if owned.storage_slot == target_slot:
+ return true
+ var displaced: OwnedItemType
+ for candidate: OwnedItemType in _items:
+ if candidate == null or candidate == owned:
+ continue
+ var candidate_item: ItemDataType = _resolve_valid_item(
+ candidate.item_id
+ )
+ if (
+ _storage_group_for_item(candidate_item) == group
+ and candidate.storage_slot == target_slot
+ ):
+ displaced = candidate
+ break
+ var previous_slot: int = owned.storage_slot
+ owned.storage_slot = target_slot
+ if displaced != null:
+ displaced.storage_slot = previous_slot
+ contents_changed.emit()
+ return true
+
+
func get_unlocked_bait_ids() -> Array[StringName]:
return _unlocked_bait_ids.duplicate()
@@ -146,13 +231,91 @@ func replace_all_items(items: Array[OwnedItemType]) -> bool:
return false
seen[owned.item_id] = true
validated.append(owned.duplicate_record())
+ _normalize_storage_slots(validated)
_items = validated
contents_changed.emit()
return true
+func _normalize_storage_slots(items: Array[OwnedItemType]) -> void:
+ var occupied: Dictionary[String, bool] = {}
+ for owned: OwnedItemType in items:
+ var item: ItemDataType = _resolve_valid_item(owned.item_id)
+ var group: StorageGroup = _storage_group_for_item(item)
+ if group == StorageGroup.NONE:
+ owned.storage_slot = -1
+ continue
+ var key := _storage_slot_key(group, owned.storage_slot)
+ if (
+ owned.storage_slot < 0
+ or owned.storage_slot > MAX_STORAGE_SLOT_INDEX
+ or occupied.has(key)
+ ):
+ owned.storage_slot = -1
+ continue
+ occupied[key] = true
+ for owned: OwnedItemType in items:
+ if owned.storage_slot >= 0:
+ continue
+ var item: ItemDataType = _resolve_valid_item(owned.item_id)
+ var group: StorageGroup = _storage_group_for_item(item)
+ if group == StorageGroup.NONE:
+ continue
+ for slot_index: int in range(MAX_STORAGE_SLOT_INDEX + 1):
+ var key := _storage_slot_key(group, slot_index)
+ if occupied.has(key):
+ continue
+ owned.storage_slot = slot_index
+ occupied[key] = true
+ break
+
+
+func _first_available_storage_slot(
+ item: ItemDataType,
+ items: Array[OwnedItemType],
+) -> int:
+ var group: StorageGroup = _storage_group_for_item(item)
+ if group == StorageGroup.NONE:
+ return -1
+ var occupied: Dictionary[int, bool] = {}
+ for owned: OwnedItemType in items:
+ var owned_item: ItemDataType = _resolve_valid_item(owned.item_id)
+ if (
+ _storage_group_for_item(owned_item) == group
+ and owned.storage_slot >= 0
+ ):
+ occupied[owned.storage_slot] = true
+ for slot_index: int in range(MAX_STORAGE_SLOT_INDEX + 1):
+ if not occupied.has(slot_index):
+ return slot_index
+ return -1
+
+
+func _storage_group_for_item(item: ItemDataType) -> StorageGroup:
+ if item == null:
+ return StorageGroup.NONE
+ if item.is_bait():
+ return StorageGroup.BAIT
+ if item.is_lure():
+ return StorageGroup.LURES
+ return (
+ StorageGroup.ITEMS
+ if item.category == ItemDataType.Category.CONSUMABLE
+ else StorageGroup.EQUIPMENT
+ )
+
+
+func _storage_slot_key(group: StorageGroup, slot_index: int) -> String:
+ return "%d:%d" % [int(group), slot_index]
+
+
func _resolve_valid_item(item_id: StringName) -> ItemDataType:
if _catalog == null:
return null
var item: ItemDataType = _catalog.get_item_by_id(item_id)
return item if item != null and item.is_valid() else null
+
+
+func _resolve_available_item(item_id: StringName) -> ItemDataType:
+ var item: ItemDataType = _resolve_valid_item(item_id)
+ return item if item != null and item.active else null
diff --git a/inventory/player_hotbar.gd b/inventory/player_hotbar.gd
index e9dbcec..36068ea 100644
--- a/inventory/player_hotbar.gd
+++ b/inventory/player_hotbar.gd
@@ -24,6 +24,7 @@ signal selected_assignment_changed(
var _bag: PlayerBagType
var _catalog: ItemCatalogType
var _fish_inventory: FishInventoryType
+var _inventory_layout: PlayerInventoryLayout
var _slots: Array[StringName] = []
var _fish_slots: Array[StringName] = []
var _selected_slot: int = 0
@@ -40,10 +41,12 @@ func setup(
bag: PlayerBagType,
catalog: ItemCatalogType,
fish_inventory: FishInventoryType = null,
+ inventory_layout: PlayerInventoryLayout = null,
) -> void:
_bag = bag
_catalog = catalog
_fish_inventory = fish_inventory
+ _inventory_layout = inventory_layout
if _bag != null and not _bag.contents_changed.is_connected(
_on_bag_contents_changed
):
@@ -55,6 +58,11 @@ func setup(
)
):
_fish_inventory.catches_changed.connect(_on_fish_inventory_changed)
+ if (
+ _inventory_layout != null
+ and not _inventory_layout.layout_changed.is_connected(_validate_assignments)
+ ):
+ _inventory_layout.layout_changed.connect(_validate_assignments)
_validate_assignments()
@@ -63,13 +71,26 @@ func assign_item(slot_index: int, item_id: StringName) -> bool:
return false
if _slots[slot_index] == item_id:
return true
+ var source_index: int = _slots.find(item_id)
+ var displaced_item: StringName = _slots[slot_index]
+ var displaced_fish: StringName = _fish_slots[slot_index]
+ if (
+ _inventory_layout != null
+ and not _inventory_layout.move_entry(
+ PlayerInventoryLayout.EntryKind.ITEM,
+ item_id,
+ PlayerInventoryLayout.InventoryContainer.HOTBAR,
+ slot_index,
+ )
+ ):
+ return false
var selected_slot_was_affected: bool = slot_index == _selected_slot
- for index: int in range(SLOT_COUNT):
- if index != slot_index and _slots[index] == item_id:
- _slots[index] = StringName()
- selected_slot_was_affected = (
- selected_slot_was_affected or index == _selected_slot
- )
+ if source_index >= 0 and source_index != slot_index:
+ _slots[source_index] = displaced_item
+ _fish_slots[source_index] = displaced_fish
+ selected_slot_was_affected = (
+ selected_slot_was_affected or source_index == _selected_slot
+ )
_slots[slot_index] = item_id
_fish_slots[slot_index] = StringName()
slots_changed.emit()
@@ -83,13 +104,26 @@ func assign_fish(slot_index: int, catch_id: StringName) -> bool:
return false
if _fish_slots[slot_index] == catch_id:
return true
+ var source_index: int = _fish_slots.find(catch_id)
+ var displaced_item: StringName = _slots[slot_index]
+ var displaced_fish: StringName = _fish_slots[slot_index]
+ if (
+ _inventory_layout != null
+ and not _inventory_layout.move_entry(
+ PlayerInventoryLayout.EntryKind.CATCH,
+ catch_id,
+ PlayerInventoryLayout.InventoryContainer.HOTBAR,
+ slot_index,
+ )
+ ):
+ return false
var selected_slot_was_affected: bool = slot_index == _selected_slot
- for index: int in range(SLOT_COUNT):
- if index != slot_index and _fish_slots[index] == catch_id:
- _fish_slots[index] = StringName()
- selected_slot_was_affected = (
- selected_slot_was_affected or index == _selected_slot
- )
+ if source_index >= 0 and source_index != slot_index:
+ _slots[source_index] = displaced_item
+ _fish_slots[source_index] = displaced_fish
+ selected_slot_was_affected = (
+ selected_slot_was_affected or source_index == _selected_slot
+ )
_slots[slot_index] = StringName()
_fish_slots[slot_index] = catch_id
slots_changed.emit()
@@ -107,6 +141,11 @@ func clear_slot(slot_index: int) -> bool:
)
):
return false
+ if (
+ _inventory_layout != null
+ and not _inventory_layout.return_hotbar_entry_to_inventory(slot_index)
+ ):
+ return false
_slots[slot_index] = StringName()
_fish_slots[slot_index] = StringName()
slots_changed.emit()
@@ -122,6 +161,39 @@ func swap_slots(from_index: int, to_index: int) -> bool:
or from_index == to_index
):
return false
+ if _inventory_layout != null:
+ var kind: PlayerInventoryLayout.EntryKind
+ var identity: StringName
+ if not _fish_slots[from_index].is_empty():
+ kind = PlayerInventoryLayout.EntryKind.CATCH
+ identity = _fish_slots[from_index]
+ elif not _slots[from_index].is_empty():
+ kind = PlayerInventoryLayout.EntryKind.ITEM
+ identity = _slots[from_index]
+ else:
+ kind = (
+ PlayerInventoryLayout.EntryKind.CATCH
+ if not _fish_slots[to_index].is_empty()
+ else PlayerInventoryLayout.EntryKind.ITEM
+ )
+ identity = (
+ _fish_slots[to_index]
+ if not _fish_slots[to_index].is_empty()
+ else _slots[to_index]
+ )
+ var temporary_index: int = from_index
+ from_index = to_index
+ to_index = temporary_index
+ if (
+ identity.is_empty()
+ or not _inventory_layout.move_entry(
+ kind,
+ identity,
+ PlayerInventoryLayout.InventoryContainer.HOTBAR,
+ to_index,
+ )
+ ):
+ return false
var temporary: StringName = _slots[from_index]
_slots[from_index] = _slots[to_index]
_slots[to_index] = temporary
@@ -134,6 +206,50 @@ func swap_slots(from_index: int, to_index: int) -> bool:
return true
+func move_slot_to_container(
+ slot_index: int,
+ target_container: int,
+ target_slot: int,
+) -> bool:
+ if not _is_slot_valid(slot_index) or _inventory_layout == null:
+ return false
+ var kind: PlayerInventoryLayout.EntryKind
+ var identity: StringName
+ if not _fish_slots[slot_index].is_empty():
+ kind = PlayerInventoryLayout.EntryKind.CATCH
+ identity = _fish_slots[slot_index]
+ elif not _slots[slot_index].is_empty():
+ kind = PlayerInventoryLayout.EntryKind.ITEM
+ identity = _slots[slot_index]
+ else:
+ return false
+ if not _inventory_layout.move_entry(
+ kind,
+ identity,
+ target_container,
+ target_slot,
+ ):
+ return false
+ _slots[slot_index] = StringName()
+ _fish_slots[slot_index] = StringName()
+ var replacement := _inventory_layout.get_entry_at(
+ PlayerInventoryLayout.InventoryContainer.HOTBAR,
+ slot_index,
+ )
+ if not replacement.is_empty():
+ var replacement_identity := StringName(
+ str(replacement.get("identity", ""))
+ )
+ if int(replacement.get("kind", -1)) == PlayerInventoryLayout.EntryKind.CATCH:
+ _fish_slots[slot_index] = replacement_identity
+ else:
+ _slots[slot_index] = replacement_identity
+ slots_changed.emit()
+ if slot_index == _selected_slot:
+ _emit_selected_assignment()
+ return true
+
+
func select_slot(slot_index: int) -> bool:
if not _is_slot_valid(slot_index):
return false
@@ -198,11 +314,36 @@ func get_fish_slots() -> Array[StringName]:
return _fish_slots.duplicate()
+func find_item_slot(item_id: StringName) -> int:
+ return _slots.find(item_id)
+
+
+func find_first_empty_slot() -> int:
+ for slot_index: int in SLOT_COUNT:
+ if get_assignment_kind(slot_index) != AssignmentKind.EMPTY:
+ continue
+ if (
+ _inventory_layout == null
+ or _inventory_layout.get_key_at(
+ PlayerInventoryLayout.InventoryContainer.HOTBAR,
+ slot_index,
+ ).is_empty()
+ ):
+ return slot_index
+ return -1
+
+
func replace_state(
slots: Array[StringName],
selected_slot: int,
fish_slots: Array[StringName] = [],
+ synchronize_layout: bool = true,
) -> bool:
+ if (
+ synchronize_layout and _inventory_layout != null
+ and not _inventory_layout.return_all_hotbar_entries_to_inventory()
+ ):
+ return false
var normalized: Array[StringName] = []
normalized.resize(SLOT_COUNT)
normalized.fill(StringName())
@@ -227,6 +368,22 @@ func replace_state(
seen_fish[catch_id] = true
_slots = normalized
_fish_slots = normalized_fish
+ if _inventory_layout != null and synchronize_layout:
+ for index: int in SLOT_COUNT:
+ if not _slots[index].is_empty():
+ _inventory_layout.move_entry(
+ PlayerInventoryLayout.EntryKind.ITEM,
+ _slots[index],
+ PlayerInventoryLayout.InventoryContainer.HOTBAR,
+ index,
+ )
+ elif not _fish_slots[index].is_empty():
+ _inventory_layout.move_entry(
+ PlayerInventoryLayout.EntryKind.CATCH,
+ _fish_slots[index],
+ PlayerInventoryLayout.InventoryContainer.HOTBAR,
+ index,
+ )
_selected_slot = clampi(selected_slot, 0, SLOT_COUNT - 1)
slots_changed.emit()
_emit_selected_assignment()
@@ -249,7 +406,17 @@ func _can_assign(item_id: StringName) -> bool:
if _catalog == null:
return false
var item: ItemDataType = _catalog.get_item_by_id(item_id)
- return item != null and item.is_valid() and item.hotbar_allowed
+ return (
+ item != null
+ and item.is_available()
+ and item.hotbar_allowed
+ and (
+ _inventory_layout == null
+ or _inventory_layout.get_container(
+ PlayerInventoryLayout.EntryKind.ITEM, item_id
+ ) >= 0
+ )
+ )
func _can_assign_fish(catch_id: StringName) -> bool:
@@ -257,6 +424,12 @@ func _can_assign_fish(catch_id: StringName) -> bool:
not catch_id.is_empty()
and _fish_inventory != null
and _fish_inventory.contains_catch_id(catch_id)
+ and (
+ _inventory_layout == null
+ or _inventory_layout.get_container(
+ PlayerInventoryLayout.EntryKind.CATCH, catch_id
+ ) >= 0
+ )
)
diff --git a/inventory/player_inventory_layout.gd b/inventory/player_inventory_layout.gd
new file mode 100644
index 0000000..bce726e
--- /dev/null
+++ b/inventory/player_inventory_layout.gd
@@ -0,0 +1,599 @@
+class_name PlayerInventoryLayout
+extends Node
+
+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 FishInventoryType = preload("res://inventory/fish_inventory.gd")
+const PlayerCoolerCapacityType = preload(
+ "res://progression/player_cooler_capacity.gd"
+)
+const PlayerWalletType = preload("res://economy/player_wallet.gd")
+
+const INVENTORY_COLUMNS: int = 9
+const INVENTORY_CAPACITIES: Array[int] = [9, 18, 27, 36]
+const BACKPACK_EXPANSION_COSTS: Array[int] = [1500, 4500, 9000]
+const MAX_BACKPACK_LEVEL: int = 3
+const MAX_INVENTORY_SLOT_COUNT: int = 36
+const MAX_STORAGE_SLOT_COUNT: int = PlayerCoolerCapacityType.MAX_CAPACITY
+const HOTBAR_SLOT_COUNT: int = 9
+
+enum EntryKind {
+ ITEM,
+ CATCH,
+}
+
+enum InventoryContainer {
+ INVENTORY,
+ STORAGE,
+ HOTBAR,
+}
+
+signal layout_changed
+signal backpack_capacity_changed(level: int, capacity: int)
+
+var _bag: PlayerBagType
+var _fish_inventory: FishInventoryType
+var _item_catalog: ItemCatalogType
+var _storage_capacity: PlayerCoolerCapacityType
+var _placements: Dictionary[String, Dictionary] = {}
+var _prepared_item_placements: Dictionary[String, Dictionary] = {}
+var _reconciling: bool = false
+var _backpack_level: int = 0
+
+
+func setup(
+ bag: PlayerBagType,
+ fish_inventory: FishInventoryType,
+ item_catalog: ItemCatalogType,
+ storage_capacity: PlayerCoolerCapacityType,
+) -> void:
+ _bag = bag
+ _fish_inventory = fish_inventory
+ _item_catalog = item_catalog
+ _storage_capacity = storage_capacity
+ if _bag != null and not _bag.contents_changed.is_connected(_on_contents_changed):
+ _bag.contents_changed.connect(_on_contents_changed)
+ if (
+ _fish_inventory != null
+ and not _fish_inventory.catches_changed.is_connected(_on_contents_changed)
+ ):
+ _fish_inventory.catches_changed.connect(_on_contents_changed)
+ if (
+ _storage_capacity != null
+ and not _storage_capacity.capacity_changed.is_connected(
+ _on_storage_capacity_changed
+ )
+ ):
+ _storage_capacity.capacity_changed.connect(_on_storage_capacity_changed)
+ _reconcile(true)
+
+
+func can_accept_item(item_id: StringName) -> bool:
+ if not is_managed_item(item_id):
+ return true
+ var key := item_key(item_id)
+ return (
+ _placements.has(key)
+ or _is_prepared_item_placement_available(key)
+ or _first_free_slot(InventoryContainer.INVENTORY) >= 0
+ )
+
+
+func prepare_new_item_placement(
+ item_id: StringName,
+ target_container: InventoryContainer,
+ target_slot: int,
+) -> bool:
+ if not is_managed_item(item_id):
+ return false
+ var key := item_key(item_id)
+ if (
+ _placements.has(key)
+ or not _is_slot_valid(target_container, target_slot)
+ or not _key_at(target_container, target_slot).is_empty()
+ or _prepared_slot_is_occupied(target_container, target_slot)
+ ):
+ return false
+ _prepared_item_placements[key] = {
+ "container": int(target_container),
+ "slot": target_slot,
+ }
+ return true
+
+
+func cancel_prepared_item_placement(item_id: StringName) -> void:
+ _prepared_item_placements.erase(item_key(item_id))
+
+
+func can_accept_item_in_storage(item_id: StringName) -> bool:
+ if not is_managed_item(item_id):
+ return true
+ var key := item_key(item_id)
+ return _placements.has(key) or _first_free_slot(InventoryContainer.STORAGE) >= 0
+
+
+func can_accept_catch(catch_id: StringName = StringName()) -> bool:
+ if not catch_id.is_empty() and _placements.has(catch_key(catch_id)):
+ return true
+ return _first_free_slot(InventoryContainer.INVENTORY) >= 0
+
+
+func is_managed_item(item_id: StringName) -> bool:
+ var item: ItemDataType = (
+ _item_catalog.get_item_by_id(item_id)
+ if _item_catalog != null else null
+ )
+ return item != null and not item.is_bait() and not item.is_lure()
+
+
+func is_item_in_inventory(item_id: StringName) -> bool:
+ return _is_entry_in_container(item_key(item_id), InventoryContainer.INVENTORY)
+
+
+func is_catch_in_inventory(catch_id: StringName) -> bool:
+ return _is_entry_in_container(catch_key(catch_id), InventoryContainer.INVENTORY)
+
+
+func is_item_carried(item_id: StringName) -> bool:
+ return _is_entry_carried(item_key(item_id))
+
+
+func is_catch_carried(catch_id: StringName) -> bool:
+ return _is_entry_carried(catch_key(catch_id))
+
+
+func get_inventory_count() -> int:
+ return get_entries(InventoryContainer.INVENTORY).size()
+
+
+func get_inventory_capacity() -> int:
+ return INVENTORY_CAPACITIES[_backpack_level]
+
+
+func get_backpack_level() -> int:
+ return _backpack_level
+
+
+func get_next_inventory_capacity() -> int:
+ if _backpack_level >= MAX_BACKPACK_LEVEL:
+ return -1
+ return INVENTORY_CAPACITIES[_backpack_level + 1]
+
+
+func get_next_backpack_cost() -> int:
+ if _backpack_level >= MAX_BACKPACK_LEVEL:
+ return -1
+ return BACKPACK_EXPANSION_COSTS[_backpack_level]
+
+
+func can_purchase_backpack(wallet: PlayerWalletType) -> bool:
+ var cost := get_next_backpack_cost()
+ return wallet != null and cost >= 0 and wallet.can_afford(cost)
+
+
+func purchase_backpack(wallet: PlayerWalletType) -> bool:
+ if not can_purchase_backpack(wallet):
+ return false
+ var cost := get_next_backpack_cost()
+ if not wallet.debit(cost):
+ return false
+ _backpack_level += 1
+ backpack_capacity_changed.emit(_backpack_level, get_inventory_capacity())
+ layout_changed.emit()
+ return true
+
+
+func restore_backpack_level(level: int) -> bool:
+ var validated := clampi(level, 0, MAX_BACKPACK_LEVEL)
+ var changed := _backpack_level != validated
+ _backpack_level = validated
+ if changed:
+ backpack_capacity_changed.emit(_backpack_level, get_inventory_capacity())
+ _reconcile(changed)
+ return true
+
+
+func get_storage_count() -> int:
+ return get_entries(InventoryContainer.STORAGE).size()
+
+
+func get_hotbar_count() -> int:
+ return get_entries(InventoryContainer.HOTBAR).size()
+
+
+func get_storage_capacity() -> int:
+ return _storage_capacity.get_capacity() if _storage_capacity != null else 0
+
+
+func get_entries(container: InventoryContainer) -> Array[Dictionary]:
+ var entries: Array[Dictionary] = []
+ for key: String in _placements:
+ var placement: Dictionary = _placements[key]
+ if int(placement.get("container", -1)) != int(container):
+ continue
+ var entry: Dictionary = placement.duplicate(true)
+ entry["key"] = key
+ entries.append(entry)
+ entries.sort_custom(
+ func(left: Dictionary, right: Dictionary) -> bool:
+ return int(left.get("slot", -1)) < int(right.get("slot", -1))
+ )
+ return entries
+
+
+func get_entry(kind: EntryKind, identity: StringName) -> Dictionary:
+ var key := _entry_key(kind, identity)
+ return _placements.get(key, {}).duplicate(true)
+
+
+func get_container(kind: EntryKind, identity: StringName) -> int:
+ return int(
+ get_entry(kind, identity).get("container", -1)
+ )
+
+
+func get_key_at(container: InventoryContainer, slot: int) -> String:
+ return _key_at(container, slot)
+
+
+func get_entry_at(container: InventoryContainer, slot: int) -> Dictionary:
+ var key := _key_at(container, slot)
+ if key.is_empty():
+ return {}
+ var entry: Dictionary = _placements[key].duplicate(true)
+ entry["key"] = key
+ return entry
+
+
+func move_entry(
+ kind: EntryKind,
+ identity: StringName,
+ target_container: InventoryContainer,
+ target_slot: int,
+) -> bool:
+ var key := _entry_key(kind, identity)
+ if not _placements.has(key) or not _is_slot_valid(target_container, target_slot):
+ return false
+ var source: Dictionary = _placements[key]
+ if (
+ int(source.get("container", -1)) == int(target_container)
+ and int(source.get("slot", -1)) == target_slot
+ ):
+ return true
+ var displaced_key := _key_at(target_container, target_slot)
+ var source_container: int = int(source.get("container", InventoryContainer.INVENTORY))
+ var source_slot: int = int(source.get("slot", -1))
+ source["container"] = int(target_container)
+ source["slot"] = target_slot
+ _placements[key] = source
+ if not displaced_key.is_empty():
+ var displaced: Dictionary = _placements[displaced_key]
+ displaced["container"] = source_container
+ displaced["slot"] = source_slot
+ _placements[displaced_key] = displaced
+ layout_changed.emit()
+ return true
+
+
+func move_entry_to_first_free(
+ kind: EntryKind,
+ identity: StringName,
+ target_container: InventoryContainer,
+) -> bool:
+ var slot := _first_free_slot(target_container)
+ return slot >= 0 and move_entry(kind, identity, target_container, slot)
+
+
+func return_hotbar_entry_to_inventory(slot: int) -> bool:
+ var key := _key_at(InventoryContainer.HOTBAR, slot)
+ if key.is_empty():
+ return true
+ var placement: Dictionary = _placements[key]
+ return move_entry_to_first_free(
+ int(placement.get("kind", -1)) as EntryKind,
+ StringName(str(placement.get("identity", ""))),
+ InventoryContainer.INVENTORY,
+ )
+
+
+func return_all_hotbar_entries_to_inventory() -> bool:
+ var entries := get_entries(InventoryContainer.HOTBAR)
+ if get_inventory_count() + entries.size() > get_inventory_capacity():
+ return false
+ for entry: Dictionary in entries:
+ if not move_entry_to_first_free(
+ int(entry.get("kind", -1)) as EntryKind,
+ StringName(str(entry.get("identity", ""))),
+ InventoryContainer.INVENTORY,
+ ):
+ return false
+ return true
+
+
+func to_save_data() -> Dictionary:
+ var serialized: Array[Dictionary] = []
+ var keys: Array[String] = []
+ keys.assign(_placements.keys())
+ keys.sort()
+ for key: String in keys:
+ var placement: Dictionary = _placements[key]
+ serialized.append({
+ "kind": int(placement.get("kind", EntryKind.ITEM)),
+ "identity": str(placement.get("identity", "")),
+ "container": int(placement.get("container", InventoryContainer.INVENTORY)),
+ "slot": int(placement.get("slot", -1)),
+ })
+ return {
+ "backpack_level": _backpack_level,
+ "placements": serialized,
+ }
+
+
+func restore_from_save_data(data: Dictionary) -> bool:
+ var level_value: Variant = data.get("backpack_level", 0)
+ if (
+ typeof(level_value) not in [TYPE_INT, TYPE_FLOAT]
+ or not is_equal_approx(float(level_value), floorf(float(level_value)))
+ ):
+ return false
+ _backpack_level = clampi(int(level_value), 0, MAX_BACKPACK_LEVEL)
+ var restored: Dictionary[String, Dictionary] = {}
+ var values: Variant = data.get("placements", [])
+ if typeof(values) != TYPE_ARRAY:
+ return false
+ for value: Variant in values as Array:
+ if typeof(value) != TYPE_DICTIONARY:
+ continue
+ var record := value as Dictionary
+ var kind: int = int(record.get("kind", -1))
+ var identity := StringName(str(record.get("identity", "")))
+ var container: int = int(record.get("container", -1))
+ var slot: int = int(record.get("slot", -1))
+ if (
+ kind not in [EntryKind.ITEM, EntryKind.CATCH]
+ or identity.is_empty()
+ or container not in [
+ InventoryContainer.INVENTORY,
+ InventoryContainer.STORAGE,
+ InventoryContainer.HOTBAR,
+ ]
+ or not _is_slot_valid(container as InventoryContainer, slot)
+ ):
+ continue
+ var key := _entry_key(kind as EntryKind, identity)
+ if restored.has(key) or _placement_slot_occupied(restored, container, slot):
+ continue
+ restored[key] = {
+ "kind": kind,
+ "identity": identity,
+ "container": container,
+ "slot": slot,
+ }
+ _prepared_item_placements.clear()
+ _placements = restored
+ _reconcile(true)
+ return true
+
+
+func reset_to_defaults() -> void:
+ var capacity_changed := _backpack_level != 0
+ _backpack_level = 0
+ _prepared_item_placements.clear()
+ _placements.clear()
+ _reconcile(true)
+ if capacity_changed:
+ backpack_capacity_changed.emit(_backpack_level, get_inventory_capacity())
+
+
+static func item_key(item_id: StringName) -> String:
+ return "item:%s" % String(item_id)
+
+
+static func catch_key(catch_id: StringName) -> String:
+ return "catch:%s" % String(catch_id)
+
+
+func _entry_key(kind: EntryKind, identity: StringName) -> String:
+ return item_key(identity) if kind == EntryKind.ITEM else catch_key(identity)
+
+
+func _is_entry_in_container(key: String, container: InventoryContainer) -> bool:
+ var placement: Dictionary = _placements.get(key, {})
+ return int(placement.get("container", -1)) == int(container)
+
+
+func _is_entry_carried(key: String) -> bool:
+ var placement: Dictionary = _placements.get(key, {})
+ var container: int = int(placement.get("container", -1))
+ return container in [InventoryContainer.INVENTORY, InventoryContainer.HOTBAR]
+
+
+func _on_contents_changed() -> void:
+ _reconcile(false)
+
+
+func _on_storage_capacity_changed(_level: int, _capacity: int) -> void:
+ _reconcile(true)
+
+
+func _reconcile(force_signal: bool) -> void:
+ if _reconciling or _bag == null or _fish_inventory == null:
+ return
+ _reconciling = true
+ var expected: Dictionary[String, Dictionary] = {}
+ for owned in _bag.get_all_items():
+ if owned == null or not is_managed_item(owned.item_id):
+ continue
+ var key := item_key(owned.item_id)
+ expected[key] = {"kind": EntryKind.ITEM, "identity": owned.item_id}
+ for fish_catch in _fish_inventory.get_all_catches():
+ if fish_catch == null or not fish_catch.is_valid():
+ continue
+ var key := catch_key(fish_catch.catch_id)
+ expected[key] = {"kind": EntryKind.CATCH, "identity": fish_catch.catch_id}
+ var changed: bool = false
+ for key: String in _placements.keys():
+ if expected.has(key):
+ continue
+ _placements.erase(key)
+ changed = true
+ _normalize_existing_placements()
+ for key: String in expected:
+ if _placements.has(key):
+ continue
+ var prepared: Dictionary = _prepared_item_placements.get(key, {})
+ var target_container := InventoryContainer.INVENTORY
+ var target_slot: int = -1
+ if _is_prepared_item_placement_available(key):
+ target_container = int(
+ prepared["container"]
+ ) as InventoryContainer
+ target_slot = int(prepared["slot"])
+ _prepared_item_placements.erase(key)
+ if target_slot < 0:
+ target_slot = _first_free_slot(target_container)
+ if target_slot < 0:
+ target_container = InventoryContainer.STORAGE
+ target_slot = _first_free_slot(target_container)
+ if target_slot < 0:
+ push_error("Inventory and storage cannot fit owned entry '%s'." % key)
+ continue
+ var placement: Dictionary = expected[key].duplicate(true)
+ placement["container"] = int(target_container)
+ placement["slot"] = target_slot
+ _placements[key] = placement
+ changed = true
+ _reconciling = false
+ if changed or force_signal:
+ layout_changed.emit()
+
+
+func _normalize_existing_placements() -> void:
+ var occupied_inventory: Dictionary[int, bool] = {}
+ var occupied_storage: Dictionary[int, bool] = {}
+ var occupied_hotbar: Dictionary[int, bool] = {}
+ var invalid_keys: Array[String] = []
+ var keys: Array[String] = []
+ keys.assign(_placements.keys())
+ keys.sort()
+ for key: String in keys:
+ var placement: Dictionary = _placements[key]
+ var container: int = int(placement.get("container", -1))
+ var slot: int = int(placement.get("slot", -1))
+ var occupied: Dictionary[int, bool]
+ match container:
+ InventoryContainer.INVENTORY:
+ occupied = occupied_inventory
+ InventoryContainer.STORAGE:
+ occupied = occupied_storage
+ InventoryContainer.HOTBAR:
+ occupied = occupied_hotbar
+ _:
+ occupied = {}
+ if (
+ container not in [
+ InventoryContainer.INVENTORY,
+ InventoryContainer.STORAGE,
+ InventoryContainer.HOTBAR,
+ ]
+ or not _is_slot_valid(container as InventoryContainer, slot)
+ or occupied.has(slot)
+ ):
+ invalid_keys.append(key)
+ continue
+ occupied[slot] = true
+ for key: String in invalid_keys:
+ # Invalid records must stop occupying their old slot before free-space
+ # lookup. Otherwise a duplicate can incorrectly reserve a valid slot.
+ var placement: Dictionary = _placements[key]
+ _placements.erase(key)
+ var target := InventoryContainer.INVENTORY
+ var slot := _first_free_slot(target)
+ if slot < 0:
+ target = InventoryContainer.STORAGE
+ slot = _first_free_slot(target)
+ if slot < 0:
+ _placements.erase(key)
+ continue
+ placement["container"] = int(target)
+ placement["slot"] = slot
+ _placements[key] = placement
+
+
+func _first_free_slot(container: InventoryContainer) -> int:
+ var capacity := (
+ get_inventory_capacity() if container == InventoryContainer.INVENTORY
+ else HOTBAR_SLOT_COUNT if container == InventoryContainer.HOTBAR
+ else get_storage_capacity()
+ )
+ for slot: int in capacity:
+ if _key_at(container, slot).is_empty():
+ return slot
+ return -1
+
+
+func _is_prepared_item_placement_available(key: String) -> bool:
+ var placement: Dictionary = _prepared_item_placements.get(key, {})
+ if placement.is_empty():
+ return false
+ var container: int = int(placement.get("container", -1))
+ var slot: int = int(placement.get("slot", -1))
+ if (
+ container not in [
+ InventoryContainer.INVENTORY,
+ InventoryContainer.STORAGE,
+ InventoryContainer.HOTBAR,
+ ]
+ or not _is_slot_valid(container as InventoryContainer, slot)
+ ):
+ return false
+ return _key_at(container as InventoryContainer, slot).is_empty()
+
+
+func _prepared_slot_is_occupied(
+ container: InventoryContainer,
+ slot: int,
+) -> bool:
+ for placement: Dictionary in _prepared_item_placements.values():
+ if (
+ int(placement.get("container", -1)) == int(container)
+ and int(placement.get("slot", -1)) == slot
+ ):
+ return true
+ return false
+
+
+func _key_at(container: InventoryContainer, slot: int) -> String:
+ for key: String in _placements:
+ var placement: Dictionary = _placements[key]
+ if (
+ int(placement.get("container", -1)) == int(container)
+ and int(placement.get("slot", -1)) == slot
+ ):
+ return key
+ return ""
+
+
+func _is_slot_valid(container: InventoryContainer, slot: int) -> bool:
+ if slot < 0:
+ return false
+ return slot < (
+ get_inventory_capacity() if container == InventoryContainer.INVENTORY
+ else HOTBAR_SLOT_COUNT if container == InventoryContainer.HOTBAR
+ else get_storage_capacity()
+ )
+
+
+func _placement_slot_occupied(
+ placements: Dictionary[String, Dictionary],
+ container: int,
+ slot: int,
+) -> bool:
+ for placement: Dictionary in placements.values():
+ if (
+ int(placement.get("container", -1)) == container
+ and int(placement.get("slot", -1)) == slot
+ ):
+ return true
+ return false
diff --git a/inventory/player_inventory_layout.gd.uid b/inventory/player_inventory_layout.gd.uid
new file mode 100644
index 0000000..debd549
--- /dev/null
+++ b/inventory/player_inventory_layout.gd.uid
@@ -0,0 +1 @@
+uid://c5cku54c6qgmw
diff --git a/items/catalog/art_kit.tres b/items/catalog/art_kit.tres
index 3bbc0f6..e59705d 100644
--- a/items/catalog/art_kit.tres
+++ b/items/catalog/art_kit.tres
@@ -1,13 +1,13 @@
[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")
item_id = &"art_kit"
display_name = "Art Kit"
-description = "Press P in the game world to paint!"
+description = "Select the Art Kit in the Hotbar to paint."
category = 1
icon = ExtResource("2_icon")
stackable = false
diff --git a/items/catalog/basic_cube.tres b/items/catalog/basic_cube.tres
new file mode 100644
index 0000000..86a2cf9
--- /dev/null
+++ b/items/catalog/basic_cube.tres
@@ -0,0 +1,17 @@
+[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://ui/icons/pictograms/x_light.png" id="2_icon"]
+
+[resource]
+script = ExtResource("1_item")
+item_id = &"basic_cube"
+display_name = "basic cube"
+description = "a simple piece of decor for testing your RV layout."
+category = 6
+icon = ExtResource("2_icon")
+stackable = true
+max_stack = 32
+usable = false
+equippable = true
+hotbar_allowed = true
diff --git a/items/catalog/basic_fishing_rod.tres b/items/catalog/basic_fishing_rod.tres
index f43f9ca..9849b89 100644
--- a/items/catalog/basic_fishing_rod.tres
+++ b/items/catalog/basic_fishing_rod.tres
@@ -1,6 +1,6 @@
-[gd_resource type="Resource" script_class="ItemData" load_steps=3 format=3]
+[gd_resource type="Resource" script_class="FishingRodData" load_steps=3 format=3]
-[ext_resource type="Script" path="res://items/item_data.gd" id="1_script"]
+[ext_resource type="Script" path="res://items/fishing_rod_data.gd" id="1_script"]
[ext_resource type="Texture2D" path="res://items/icons/equipment/64_rod_base_thick.png" id="2_icon"]
[resource]
@@ -8,6 +8,7 @@ script = ExtResource("1_script")
item_id = &"basic_fishing_rod"
display_name = "basic fishing rod"
description = "a dependable starter rod. select it to cast."
+active = true
category = 0
icon = ExtResource("2_icon")
stackable = false
@@ -15,3 +16,8 @@ max_stack = 1
usable = true
equippable = true
hotbar_allowed = true
+shop_order = 1
+shop_price = 0
+unlock_level = 1
+effect_summary = "neutral baseline with no catch-selection or handling modifiers."
+tradeoff = "none."
diff --git a/items/catalog/cooler_expansion.tres b/items/catalog/cooler_expansion.tres
deleted file mode 100644
index 2e9ed29..0000000
--- a/items/catalog/cooler_expansion.tres
+++ /dev/null
@@ -1,17 +0,0 @@
-[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/placeholder/cooler_expansion.svg" id="2"]
-
-[resource]
-script = ExtResource("1")
-item_id = &"cooler_expansion"
-display_name = "cooler expansion"
-description = "permanently increases the number of fish the cooler can hold."
-category = 4
-icon = ExtResource("2")
-stackable = false
-max_stack = 1
-usable = false
-equippable = false
-hotbar_allowed = false
diff --git a/items/catalog/crab_net.tres b/items/catalog/crab_net.tres
new file mode 100644
index 0000000..a6e26b0
--- /dev/null
+++ b/items/catalog/crab_net.tres
@@ -0,0 +1,18 @@
+[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/equipment/net.png" id="2_icon"]
+
+[resource]
+script = ExtResource("1_item")
+item_id = &"crab_net"
+display_name = "crab net"
+description = "a hand net for catching crabs and other small shore animals."
+active = true
+category = 1
+icon = ExtResource("2_icon")
+stackable = false
+max_stack = 1
+usable = false
+equippable = true
+hotbar_allowed = true
diff --git a/items/catalog/fishing_net.tres b/items/catalog/fishing_net.tres
new file mode 100644
index 0000000..04b1919
--- /dev/null
+++ b/items/catalog/fishing_net.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 = &"fishing_net"
+display_name = "fishing net"
+description = "a placeable water net reserved for a future passive fishing system."
+active = true
+category = 1
+stackable = false
+max_stack = 1
+usable = false
+equippable = false
+hotbar_allowed = false
diff --git a/items/catalog/item_catalog.tres b/items/catalog/item_catalog.tres
index f8c0f5d..2f15e0a 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=18 format=3]
+[gd_resource type="Resource" script_class="ItemCatalog" load_steps=42 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"]
@@ -6,7 +6,6 @@
[ext_resource type="Resource" path="res://items/catalog/energy_drink.tres" id="4_energy"]
[ext_resource type="Resource" path="res://items/catalog/snack.tres" id="5_snack"]
[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"]
@@ -17,7 +16,32 @@
[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"]
+[ext_resource type="Resource" path="res://items/catalog/rods/cardboard_classic_rod.tres" id="18_cardboard"]
+[ext_resource type="Resource" path="res://items/catalog/rods/pond_skipper_rod.tres" id="19_pond"]
+[ext_resource type="Resource" path="res://items/catalog/rods/river_runner_rod.tres" id="20_river"]
+[ext_resource type="Resource" path="res://items/catalog/rods/lake_lunker_rod.tres" id="21_lake"]
+[ext_resource type="Resource" path="res://items/catalog/rods/salt_spray_rod.tres" id="22_salt"]
+[ext_resource type="Resource" path="res://items/catalog/rods/whisker_stick_rod.tres" id="23_whisker"]
+[ext_resource type="Resource" path="res://items/catalog/rods/reef_raker_rod.tres" id="24_reef"]
+[ext_resource type="Resource" path="res://items/catalog/rods/moonbeam_rod.tres" id="25_moonbeam"]
+[ext_resource type="Resource" path="res://items/catalog/rods/sun_chaser_rod.tres" id="26_sun"]
+[ext_resource type="Resource" path="res://items/catalog/rods/rainmaker_rod.tres" id="27_rain"]
+[ext_resource type="Resource" path="res://items/catalog/rods/fogcatcher_rod.tres" id="28_fog"]
+[ext_resource type="Resource" path="res://items/catalog/rods/field_guide_rod.tres" id="29_guide"]
+[ext_resource type="Resource" path="res://items/catalog/rods/small_fry_rod.tres" id="30_small"]
+[ext_resource type="Resource" path="res://items/catalog/rods/heavy_hauler_rod.tres" id="31_heavy"]
+[ext_resource type="Resource" path="res://items/catalog/rods/pocket_rocket_rod.tres" id="32_rocket"]
+[ext_resource type="Resource" path="res://items/catalog/rods/lucky_twig_rod.tres" id="33_lucky"]
+[ext_resource type="Resource" path="res://items/catalog/rods/showboat_rod.tres" id="34_showboat"]
+[ext_resource type="Resource" path="res://items/catalog/rods/deep_blue_rod.tres" id="35_deep"]
+[ext_resource type="Resource" path="res://items/catalog/rods/oddity_string_rod.tres" id="36_oddity"]
+[ext_resource type="Resource" path="res://items/catalog/rods/aurora_rod.tres" id="37_aurora"]
+[ext_resource type="Resource" path="res://items/catalog/magnet.tres" id="38_magnet"]
+[ext_resource type="Resource" path="res://items/catalog/crab_net.tres" id="39_crab_net"]
+[ext_resource type="Resource" path="res://items/catalog/standard_shovel.tres" id="40_shovel"]
+[ext_resource type="Resource" path="res://items/catalog/fishing_net.tres" id="41_fishing_net"]
+[ext_resource type="Resource" path="res://items/catalog/basic_cube.tres" id="42_basic_cube"]
[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"), ExtResource("9_worms"), ExtResource("10_snails"), ExtResource("11_shrimp"), ExtResource("12_squid"), ExtResource("13_anchovy"), ExtResource("14_sardine"), ExtResource("15_roe"), ExtResource("16_standby"), ExtResource("17_batteries")]
+items = [ExtResource("2_rod"), ExtResource("3_coffee"), ExtResource("4_energy"), ExtResource("5_snack"), ExtResource("6_finder"), 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"), ExtResource("18_cardboard"), ExtResource("19_pond"), ExtResource("20_river"), ExtResource("21_lake"), ExtResource("22_salt"), ExtResource("23_whisker"), ExtResource("24_reef"), ExtResource("25_moonbeam"), ExtResource("26_sun"), ExtResource("27_rain"), ExtResource("28_fog"), ExtResource("29_guide"), ExtResource("30_small"), ExtResource("31_heavy"), ExtResource("32_rocket"), ExtResource("33_lucky"), ExtResource("34_showboat"), ExtResource("35_deep"), ExtResource("36_oddity"), ExtResource("37_aurora"), ExtResource("38_magnet"), ExtResource("39_crab_net"), ExtResource("40_shovel"), ExtResource("41_fishing_net"), ExtResource("42_basic_cube")]
diff --git a/items/catalog/luminous_roe.tres b/items/catalog/luminous_roe.tres
index aae9fc6..677563e 100644
--- a/items/catalog/luminous_roe.tres
+++ b/items/catalog/luminous_roe.tres
@@ -1,12 +1,14 @@
-[gd_resource type="Resource" script_class="ItemData" load_steps=2 format=3]
+[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."
+description = "rare glowing roe with the strongest rarity and quality odds."
+icon = ExtResource("2_icon")
category = 2
bait_tags = Array[StringName]([&"bait_rarity_6"])
stackable = true
diff --git a/items/catalog/magnet.tres b/items/catalog/magnet.tres
new file mode 100644
index 0000000..eb2e25c
--- /dev/null
+++ b/items/catalog/magnet.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"]
+
+[resource]
+script = ExtResource("1")
+item_id = &"magnet"
+display_name = "magnet"
+description = "a heavy magnet ready for future magnet fishing."
+active = true
+category = 1
+stackable = false
+max_stack = 1
+usable = false
+equippable = false
+hotbar_allowed = false
diff --git a/items/catalog/rods/aurora_rod.tres b/items/catalog/rods/aurora_rod.tres
new file mode 100644
index 0000000..303ce95
--- /dev/null
+++ b/items/catalog/rods/aurora_rod.tres
@@ -0,0 +1,25 @@
+[gd_resource type="Resource" script_class="FishingRodData" load_steps=2 format=3]
+
+[ext_resource type="Script" path="res://items/fishing_rod_data.gd" id="1_script"]
+
+[resource]
+script = ExtResource("1_script")
+item_id = &"aurora_rod"
+display_name = "aurora rod"
+description = "a shifting ribbon of color for anglers who have tried nearly everything else."
+active = false
+category = 0
+stackable = false
+max_stack = 1
+usable = true
+equippable = true
+hotbar_allowed = true
+shop_order = 21
+shop_price = 5000
+unlock_level = 15
+bite_time_multiplier = 1.35
+reel_speed_multiplier = 0.9
+rarity_weight_multipliers = Array[float]([0.85, 1.00, 1.15, 1.30, 1.45])
+quality_weight_multipliers = Array[float]([0.80, 0.95, 1.15, 1.30, 1.45])
+effect_summary = "modestly improves both species rarity and catch quality across all eligible fish."
+tradeoff = "the slowest bite cycle in the set; intended as an expensive late-game sidegrade."
diff --git a/items/catalog/rods/cardboard_classic_rod.tres b/items/catalog/rods/cardboard_classic_rod.tres
new file mode 100644
index 0000000..97a304f
--- /dev/null
+++ b/items/catalog/rods/cardboard_classic_rod.tres
@@ -0,0 +1,26 @@
+[gd_resource type="Resource" script_class="FishingRodData" load_steps=2 format=3]
+
+[ext_resource type="Script" path="res://items/fishing_rod_data.gd" id="1_script"]
+
+[resource]
+script = ExtResource("1_script")
+item_id = &"cardboard_classic_rod"
+display_name = "cardboard classic"
+description = "a cheerful folded rod that gets bites quickly, if not impressively."
+active = false
+category = 0
+stackable = false
+max_stack = 1
+usable = true
+equippable = true
+hotbar_allowed = true
+shop_order = 2
+shop_price = 40
+unlock_level = 1
+bite_time_multiplier = 0.85
+reel_speed_multiplier = 0.95
+barrier_power_multiplier = 0.85
+rarity_weight_multipliers = Array[float]([1.20, 1.00, 0.85, 0.70, 0.55])
+quality_weight_multipliers = Array[float]([1.20, 1.05, 0.85, 0.70, 0.55])
+effect_summary = "shorter bite waits and a low-cost early sidegrade."
+tradeoff = "leans toward common, lower-quality fish and has weaker handling."
diff --git a/items/catalog/rods/deep_blue_rod.tres b/items/catalog/rods/deep_blue_rod.tres
new file mode 100644
index 0000000..2d9b03f
--- /dev/null
+++ b/items/catalog/rods/deep_blue_rod.tres
@@ -0,0 +1,27 @@
+[gd_resource type="Resource" script_class="FishingRodData" load_steps=2 format=3]
+
+[ext_resource type="Script" path="res://items/fishing_rod_data.gd" id="1_script"]
+
+[resource]
+script = ExtResource("1_script")
+item_id = &"deep_blue_rod"
+display_name = "deep blue"
+description = "a heavy navy rod for enormous shapes moving far offshore."
+active = false
+category = 0
+stackable = false
+max_stack = 1
+usable = true
+equippable = true
+hotbar_allowed = true
+shop_order = 19
+shop_price = 2500
+unlock_level = 12
+preferred_collection_groups = Array[StringName]([&"Coastal sharks", &"Large coastal predators", &"Oceanic billfish", &"Oceanic pelagic fish", &"Oceanic tuna", &"Open-ocean giants"])
+affinity_weight_multiplier = 1.75
+weight_roll_bias = 0.32
+bite_time_multiplier = 1.28
+reel_speed_multiplier = 0.82
+barrier_power_multiplier = 1.25
+effect_summary = "favors large ocean predator and pelagic collection groups and biases weight upward."
+tradeoff = "very long bite waits and slower reeling."
diff --git a/items/catalog/rods/field_guide_rod.tres b/items/catalog/rods/field_guide_rod.tres
new file mode 100644
index 0000000..2b19f30
--- /dev/null
+++ b/items/catalog/rods/field_guide_rod.tres
@@ -0,0 +1,23 @@
+[gd_resource type="Resource" script_class="FishingRodData" load_steps=2 format=3]
+
+[ext_resource type="Script" path="res://items/fishing_rod_data.gd" id="1_script"]
+
+[resource]
+script = ExtResource("1_script")
+item_id = &"field_guide_rod"
+display_name = "field guide rod"
+description = "a note-covered rod that keeps tugging toward something new."
+active = false
+category = 0
+stackable = false
+max_stack = 1
+usable = true
+equippable = true
+hotbar_allowed = true
+shop_order = 13
+shop_price = 1350
+unlock_level = 8
+undiscovered_weight_multiplier = 1.9
+bite_time_multiplier = 1.15
+effect_summary = "substantially increases the relative weight of eligible undiscovered species."
+tradeoff = "longer bite waits and no bonus after a species is discovered."
diff --git a/items/catalog/rods/fogcatcher_rod.tres b/items/catalog/rods/fogcatcher_rod.tres
new file mode 100644
index 0000000..c0fdd25
--- /dev/null
+++ b/items/catalog/rods/fogcatcher_rod.tres
@@ -0,0 +1,23 @@
+[gd_resource type="Resource" script_class="FishingRodData" load_steps=2 format=3]
+
+[ext_resource type="Script" path="res://items/fishing_rod_data.gd" id="1_script"]
+
+[resource]
+script = ExtResource("1_script")
+item_id = &"fogcatcher_rod"
+display_name = "fogcatcher"
+description = "a hazy blue rod for fish that prefer not to be seen."
+active = false
+category = 0
+stackable = false
+max_stack = 1
+usable = true
+equippable = true
+hotbar_allowed = true
+shop_order = 12
+shop_price = 1100
+unlock_level = 7
+weather_affinity = 2
+affinity_weight_multiplier = 1.65
+effect_summary = "strongly favors eligible fog-active fish."
+tradeoff = "its affinity provides no benefit outside foggy weather."
diff --git a/items/catalog/rods/heavy_hauler_rod.tres b/items/catalog/rods/heavy_hauler_rod.tres
new file mode 100644
index 0000000..bc68c78
--- /dev/null
+++ b/items/catalog/rods/heavy_hauler_rod.tres
@@ -0,0 +1,25 @@
+[gd_resource type="Resource" script_class="FishingRodData" load_steps=2 format=3]
+
+[ext_resource type="Script" path="res://items/fishing_rod_data.gd" id="1_script"]
+
+[resource]
+script = ExtResource("1_script")
+item_id = &"heavy_hauler_rod"
+display_name = "heavy hauler"
+description = "a thick rod that would rather bring home one enormous fish."
+active = false
+category = 0
+stackable = false
+max_stack = 1
+usable = true
+equippable = true
+hotbar_allowed = true
+shop_order = 15
+shop_price = 1500
+unlock_level = 9
+weight_roll_bias = 0.28
+bite_time_multiplier = 1.18
+reel_speed_multiplier = 0.88
+barrier_power_multiplier = 1.2
+effect_summary = "biases fish weight upward and adds barrier power."
+tradeoff = "longer bite waits and slower reeling."
diff --git a/items/catalog/rods/lake_lunker_rod.tres b/items/catalog/rods/lake_lunker_rod.tres
new file mode 100644
index 0000000..4d30f56
--- /dev/null
+++ b/items/catalog/rods/lake_lunker_rod.tres
@@ -0,0 +1,25 @@
+[gd_resource type="Resource" script_class="FishingRodData" load_steps=2 format=3]
+
+[ext_resource type="Script" path="res://items/fishing_rod_data.gd" id="1_script"]
+
+[resource]
+script = ExtResource("1_script")
+item_id = &"lake_lunker_rod"
+display_name = "lake lunker"
+description = "a broad, patient rod that favors hefty catches from open lakes."
+active = false
+category = 0
+stackable = false
+max_stack = 1
+usable = true
+equippable = true
+hotbar_allowed = true
+shop_order = 5
+shop_price = 400
+unlock_level = 4
+preferred_location_tags = Array[StringName]([&"lake"])
+affinity_weight_multiplier = 1.45
+weight_roll_bias = 0.12
+bite_time_multiplier = 1.05
+effect_summary = "favors eligible lake fish and gently biases weight rolls upward."
+tradeoff = "slightly longer bite waits."
diff --git a/items/catalog/rods/lucky_twig_rod.tres b/items/catalog/rods/lucky_twig_rod.tres
new file mode 100644
index 0000000..8c26b19
--- /dev/null
+++ b/items/catalog/rods/lucky_twig_rod.tres
@@ -0,0 +1,25 @@
+[gd_resource type="Resource" script_class="FishingRodData" load_steps=2 format=3]
+
+[ext_resource type="Script" path="res://items/fishing_rod_data.gd" id="1_script"]
+
+[resource]
+script = ExtResource("1_script")
+item_id = &"lucky_twig_rod"
+display_name = "lucky twig"
+description = "an ordinary-looking branch that insists it is extremely lucky."
+active = false
+category = 0
+stackable = false
+max_stack = 1
+usable = true
+equippable = true
+hotbar_allowed = true
+shop_order = 17
+shop_price = 1800
+unlock_level = 10
+bite_time_multiplier = 1.2
+reel_speed_multiplier = 0.9
+barrier_power_multiplier = 0.85
+rarity_weight_multipliers = Array[float]([0.65, 1.00, 1.35, 1.65, 2.00])
+effect_summary = "strongly shifts species rarity toward rare, epic, and legendary fish."
+tradeoff = "longer bite waits, slower reeling, and weaker barrier power."
diff --git a/items/catalog/rods/moonbeam_rod.tres b/items/catalog/rods/moonbeam_rod.tres
new file mode 100644
index 0000000..df83634
--- /dev/null
+++ b/items/catalog/rods/moonbeam_rod.tres
@@ -0,0 +1,25 @@
+[gd_resource type="Resource" script_class="FishingRodData" load_steps=2 format=3]
+
+[ext_resource type="Script" path="res://items/fishing_rod_data.gd" id="1_script"]
+
+[resource]
+script = ExtResource("1_script")
+item_id = &"moonbeam_rod"
+display_name = "moonbeam rod"
+description = "a pale rod that wakes up when the island goes quiet."
+active = false
+category = 0
+stackable = false
+max_stack = 1
+usable = true
+equippable = true
+hotbar_allowed = true
+shop_order = 9
+shop_price = 950
+unlock_level = 6
+time_affinity = 2
+affinity_weight_multiplier = 1.5
+bite_time_multiplier = 1.08
+quality_weight_multipliers = Array[float]([0.95, 0.98, 1.00, 1.08, 1.25])
+effect_summary = "favors eligible night fish and gives shiny quality a modest lift."
+tradeoff = "slightly longer bite waits."
diff --git a/items/catalog/rods/oddity_string_rod.tres b/items/catalog/rods/oddity_string_rod.tres
new file mode 100644
index 0000000..8c51739
--- /dev/null
+++ b/items/catalog/rods/oddity_string_rod.tres
@@ -0,0 +1,25 @@
+[gd_resource type="Resource" script_class="FishingRodData" load_steps=2 format=3]
+
+[ext_resource type="Script" path="res://items/fishing_rod_data.gd" id="1_script"]
+
+[resource]
+script = ExtResource("1_script")
+item_id = &"oddity_string_rod"
+display_name = "oddity string"
+description = "a crooked spool of string that attracts fish with questionable silhouettes."
+active = false
+category = 0
+stackable = false
+max_stack = 1
+usable = true
+equippable = true
+hotbar_allowed = true
+shop_order = 20
+shop_price = 2600
+unlock_level = 12
+preferred_collection_groups = Array[StringName]([&"Brackish oddities", &"Coastal oddities", &"Deep-sea oddities", &"Freshwater oddities", &"Reef oddities"])
+affinity_weight_multiplier = 1.8
+bite_time_multiplier = 1.18
+quality_weight_multipliers = Array[float]([1.10, 1.05, 0.95, 0.90, 0.90])
+effect_summary = "favors eligible fish from authored oddity collection groups."
+tradeoff = "longer bite waits and a slight average-quality penalty."
diff --git a/items/catalog/rods/pocket_rocket_rod.tres b/items/catalog/rods/pocket_rocket_rod.tres
new file mode 100644
index 0000000..b93d348
--- /dev/null
+++ b/items/catalog/rods/pocket_rocket_rod.tres
@@ -0,0 +1,25 @@
+[gd_resource type="Resource" script_class="FishingRodData" load_steps=2 format=3]
+
+[ext_resource type="Script" path="res://items/fishing_rod_data.gd" id="1_script"]
+
+[resource]
+script = ExtResource("1_script")
+item_id = &"pocket_rocket_rod"
+display_name = "pocket rocket"
+description = "a buzzing pocket-sized rod for anglers in a hurry."
+active = false
+category = 0
+stackable = false
+max_stack = 1
+usable = true
+equippable = true
+hotbar_allowed = true
+shop_order = 16
+shop_price = 1400
+unlock_level = 9
+bite_time_multiplier = 0.7
+reel_speed_multiplier = 1.25
+barrier_power_multiplier = 0.8
+rarity_weight_multipliers = Array[float]([1.15, 1.00, 0.85, 0.72, 0.60])
+effect_summary = "greatly shortens bite waits and reels faster."
+tradeoff = "reduces higher-rarity odds and barrier power."
diff --git a/items/catalog/rods/pond_skipper_rod.tres b/items/catalog/rods/pond_skipper_rod.tres
new file mode 100644
index 0000000..adb18e4
--- /dev/null
+++ b/items/catalog/rods/pond_skipper_rod.tres
@@ -0,0 +1,24 @@
+[gd_resource type="Resource" script_class="FishingRodData" load_steps=2 format=3]
+
+[ext_resource type="Script" path="res://items/fishing_rod_data.gd" id="1_script"]
+
+[resource]
+script = ExtResource("1_script")
+item_id = &"pond_skipper_rod"
+display_name = "pond skipper"
+description = "a springy little rod that feels at home beside quiet ponds."
+active = false
+category = 0
+stackable = false
+max_stack = 1
+usable = true
+equippable = true
+hotbar_allowed = true
+shop_order = 3
+shop_price = 150
+unlock_level = 2
+preferred_location_tags = Array[StringName]([&"starter_pond", &"pond"])
+affinity_weight_multiplier = 1.5
+bite_time_multiplier = 0.95
+effect_summary = "increases the relative selection weight of eligible pond fish."
+tradeoff = "none; a focused sidegrade rather than a direct upgrade."
diff --git a/items/catalog/rods/rainmaker_rod.tres b/items/catalog/rods/rainmaker_rod.tres
new file mode 100644
index 0000000..3a407fd
--- /dev/null
+++ b/items/catalog/rods/rainmaker_rod.tres
@@ -0,0 +1,23 @@
+[gd_resource type="Resource" script_class="FishingRodData" load_steps=2 format=3]
+
+[ext_resource type="Script" path="res://items/fishing_rod_data.gd" id="1_script"]
+
+[resource]
+script = ExtResource("1_script")
+item_id = &"rainmaker_rod"
+display_name = "rainmaker"
+description = "a drip-shaped rod that becomes unusually lucky in the rain."
+active = false
+category = 0
+stackable = false
+max_stack = 1
+usable = true
+equippable = true
+hotbar_allowed = true
+shop_order = 11
+shop_price = 1100
+unlock_level = 7
+weather_affinity = 1
+affinity_weight_multiplier = 1.65
+effect_summary = "strongly favors eligible rain-active fish."
+tradeoff = "its affinity provides no benefit outside rainy weather."
diff --git a/items/catalog/rods/reef_raker_rod.tres b/items/catalog/rods/reef_raker_rod.tres
new file mode 100644
index 0000000..2b3ff91
--- /dev/null
+++ b/items/catalog/rods/reef_raker_rod.tres
@@ -0,0 +1,24 @@
+[gd_resource type="Resource" script_class="FishingRodData" load_steps=2 format=3]
+
+[ext_resource type="Script" path="res://items/fishing_rod_data.gd" id="1_script"]
+
+[resource]
+script = ExtResource("1_script")
+item_id = &"reef_raker_rod"
+display_name = "reef raker"
+description = "a bright hooked rod tuned for the crowded life around reefs."
+active = false
+category = 0
+stackable = false
+max_stack = 1
+usable = true
+equippable = true
+hotbar_allowed = true
+shop_order = 8
+shop_price = 900
+unlock_level = 6
+preferred_collection_groups = Array[StringName]([&"Cold reef fish", &"Reef ambush fish", &"Reef eels", &"Reef flatfish", &"Reef grazers", &"Reef oddities", &"Reef ornamentals", &"Reef predators", &"Reef schooling fish", &"Reef snapper", &"Reef wrasses", &"Temperate reef fish", &"Temperate reef predators"])
+affinity_weight_multiplier = 1.45
+quality_weight_multipliers = Array[float]([0.95, 1.00, 1.12, 1.05, 1.00])
+effect_summary = "favors eligible reef collection groups and modestly favors impressive quality."
+tradeoff = "no advantage away from reef-associated fish."
diff --git a/items/catalog/rods/river_runner_rod.tres b/items/catalog/rods/river_runner_rod.tres
new file mode 100644
index 0000000..7f0a464
--- /dev/null
+++ b/items/catalog/rods/river_runner_rod.tres
@@ -0,0 +1,24 @@
+[gd_resource type="Resource" script_class="FishingRodData" load_steps=2 format=3]
+
+[ext_resource type="Script" path="res://items/fishing_rod_data.gd" id="1_script"]
+
+[resource]
+script = ExtResource("1_script")
+item_id = &"river_runner_rod"
+display_name = "river runner"
+description = "a lively rod made to keep pace with moving river water."
+active = false
+category = 0
+stackable = false
+max_stack = 1
+usable = true
+equippable = true
+hotbar_allowed = true
+shop_order = 4
+shop_price = 250
+unlock_level = 3
+preferred_location_tags = Array[StringName]([&"river"])
+affinity_weight_multiplier = 1.5
+reel_speed_multiplier = 1.05
+effect_summary = "increases the relative selection weight of eligible river fish."
+tradeoff = "none; a focused sidegrade rather than a direct upgrade."
diff --git a/items/catalog/rods/salt_spray_rod.tres b/items/catalog/rods/salt_spray_rod.tres
new file mode 100644
index 0000000..f3e6102
--- /dev/null
+++ b/items/catalog/rods/salt_spray_rod.tres
@@ -0,0 +1,24 @@
+[gd_resource type="Resource" script_class="FishingRodData" load_steps=2 format=3]
+
+[ext_resource type="Script" path="res://items/fishing_rod_data.gd" id="1_script"]
+
+[resource]
+script = ExtResource("1_script")
+item_id = &"salt_spray_rod"
+display_name = "salt spray"
+description = "a sea-green rod that seems happiest with salt on the line."
+active = false
+category = 0
+stackable = false
+max_stack = 1
+usable = true
+equippable = true
+hotbar_allowed = true
+shop_order = 6
+shop_price = 600
+unlock_level = 5
+preferred_location_tags = Array[StringName]([&"ocean"])
+affinity_weight_multiplier = 1.5
+rarity_weight_multipliers = Array[float]([0.95, 1.00, 1.05, 1.05, 1.05])
+effect_summary = "increases the relative selection weight of eligible ocean fish."
+tradeoff = "none; a focused sidegrade rather than a direct upgrade."
diff --git a/items/catalog/rods/showboat_rod.tres b/items/catalog/rods/showboat_rod.tres
new file mode 100644
index 0000000..99bef41
--- /dev/null
+++ b/items/catalog/rods/showboat_rod.tres
@@ -0,0 +1,24 @@
+[gd_resource type="Resource" script_class="FishingRodData" load_steps=2 format=3]
+
+[ext_resource type="Script" path="res://items/fishing_rod_data.gd" id="1_script"]
+
+[resource]
+script = ExtResource("1_script")
+item_id = &"showboat_rod"
+display_name = "showboat"
+description = "a theatrical rod that refuses to land an unimpressive fish quietly."
+active = false
+category = 0
+stackable = false
+max_stack = 1
+usable = true
+equippable = true
+hotbar_allowed = true
+shop_order = 18
+shop_price = 2000
+unlock_level = 11
+bite_time_multiplier = 1.18
+reel_speed_multiplier = 0.9
+quality_weight_multipliers = Array[float]([0.50, 0.75, 1.35, 1.70, 2.10])
+effect_summary = "strongly shifts catch quality toward impressive, exceptional, and shiny."
+tradeoff = "longer bite waits and slower reeling; species rarity is unchanged."
diff --git a/items/catalog/rods/small_fry_rod.tres b/items/catalog/rods/small_fry_rod.tres
new file mode 100644
index 0000000..a1631f0
--- /dev/null
+++ b/items/catalog/rods/small_fry_rod.tres
@@ -0,0 +1,23 @@
+[gd_resource type="Resource" script_class="FishingRodData" load_steps=2 format=3]
+
+[ext_resource type="Script" path="res://items/fishing_rod_data.gd" id="1_script"]
+
+[resource]
+script = ExtResource("1_script")
+item_id = &"small_fry_rod"
+display_name = "small fry"
+description = "a tiny rod for anglers who value character over size."
+active = false
+category = 0
+stackable = false
+max_stack = 1
+usable = true
+equippable = true
+hotbar_allowed = true
+shop_order = 14
+shop_price = 1200
+unlock_level = 8
+weight_roll_bias = -0.25
+quality_weight_multipliers = Array[float]([0.75, 0.90, 1.25, 1.40, 1.55])
+effect_summary = "biases fish weight downward while improving high-quality outcomes."
+tradeoff = "produces smaller catches on average."
diff --git a/items/catalog/rods/sun_chaser_rod.tres b/items/catalog/rods/sun_chaser_rod.tres
new file mode 100644
index 0000000..22c0803
--- /dev/null
+++ b/items/catalog/rods/sun_chaser_rod.tres
@@ -0,0 +1,24 @@
+[gd_resource type="Resource" script_class="FishingRodData" load_steps=2 format=3]
+
+[ext_resource type="Script" path="res://items/fishing_rod_data.gd" id="1_script"]
+
+[resource]
+script = ExtResource("1_script")
+item_id = &"sun_chaser_rod"
+display_name = "sun chaser"
+description = "a warm cream rod that likes bright mornings and busy afternoons."
+active = false
+category = 0
+stackable = false
+max_stack = 1
+usable = true
+equippable = true
+hotbar_allowed = true
+shop_order = 10
+shop_price = 950
+unlock_level = 6
+time_affinity = 1
+affinity_weight_multiplier = 1.4
+rarity_weight_multipliers = Array[float]([0.95, 1.00, 1.12, 1.08, 1.00])
+effect_summary = "favors eligible daytime fish and modestly improves rare-species odds."
+tradeoff = "none; a focused sidegrade rather than a direct upgrade."
diff --git a/items/catalog/rods/whisker_stick_rod.tres b/items/catalog/rods/whisker_stick_rod.tres
new file mode 100644
index 0000000..a49ff63
--- /dev/null
+++ b/items/catalog/rods/whisker_stick_rod.tres
@@ -0,0 +1,24 @@
+[gd_resource type="Resource" script_class="FishingRodData" load_steps=2 format=3]
+
+[ext_resource type="Script" path="res://items/fishing_rod_data.gd" id="1_script"]
+
+[resource]
+script = ExtResource("1_script")
+item_id = &"whisker_stick_rod"
+display_name = "whisker stick"
+description = "a bendy rod with a suspicious talent for finding catfish."
+active = false
+category = 0
+stackable = false
+max_stack = 1
+usable = true
+equippable = true
+hotbar_allowed = true
+shop_order = 7
+shop_price = 750
+unlock_level = 5
+preferred_collection_groups = Array[StringName]([&"Freshwater catfish"])
+affinity_weight_multiplier = 1.7
+bite_time_multiplier = 1.08
+effect_summary = "strongly favors eligible fish in the Freshwater catfish collection group."
+tradeoff = "slightly longer bite waits while it searches for whiskers."
diff --git a/items/catalog/shrimp.tres b/items/catalog/shrimp.tres
index a9a63d3..6864265 100644
--- a/items/catalog/shrimp.tres
+++ b/items/catalog/shrimp.tres
@@ -1,12 +1,14 @@
-[gd_resource type="Resource" script_class="ItemData" load_steps=2 format=3]
+[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."
+description = "lively bait that noticeably improves rarity and quality odds."
+icon = ExtResource("2_icon")
category = 2
bait_tags = Array[StringName]([&"bait_rarity_2"])
stackable = true
diff --git a/items/catalog/snails.tres b/items/catalog/snails.tres
index 6bddbd4..adddefb 100644
--- a/items/catalog/snails.tres
+++ b/items/catalog/snails.tres
@@ -1,12 +1,14 @@
-[gd_resource type="Resource" script_class="ItemData" load_steps=2 format=3]
+[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."
+description = "slow-moving bait with better rarity and quality odds."
+icon = ExtResource("2_icon")
category = 2
bait_tags = Array[StringName]([&"bait_rarity_1"])
stackable = true
diff --git a/items/catalog/squid_chunks.tres b/items/catalog/squid_chunks.tres
index 108387b..bf0da93 100644
--- a/items/catalog/squid_chunks.tres
+++ b/items/catalog/squid_chunks.tres
@@ -1,12 +1,14 @@
-[gd_resource type="Resource" script_class="ItemData" load_steps=2 format=3]
+[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."
+description = "rich cut bait that strongly favors rarer, higher-quality catches."
+icon = ExtResource("2_icon")
category = 2
bait_tags = Array[StringName]([&"bait_rarity_3"])
stackable = true
diff --git a/items/catalog/standard_shovel.tres b/items/catalog/standard_shovel.tres
new file mode 100644
index 0000000..b30589c
--- /dev/null
+++ b/items/catalog/standard_shovel.tres
@@ -0,0 +1,18 @@
+[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/equipment/shovel.png" id="2_icon"]
+
+[resource]
+script = ExtResource("1_item")
+item_id = &"standard_shovel"
+display_name = "standard shovel"
+description = "a sturdy shovel for digging up buried finds on the beach."
+active = true
+category = 1
+icon = ExtResource("2_icon")
+stackable = false
+max_stack = 1
+usable = false
+equippable = true
+hotbar_allowed = true
diff --git a/items/catalog/whole_anchovy.tres b/items/catalog/whole_anchovy.tres
index 0aee243..b52018b 100644
--- a/items/catalog/whole_anchovy.tres
+++ b/items/catalog/whole_anchovy.tres
@@ -1,14 +1,16 @@
-[gd_resource type="Resource" script_class="ItemData" load_steps=2 format=3]
+[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 = "oily schooling bait with high rare catch potential."
+description = "valuable oily schooling bait tuned for exceptional rarity and quality odds."
+icon = ExtResource("2_icon")
category = 2
-bait_tags = Array[StringName]([&"bait_rarity_4"])
+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
index d03ac81..78a0e0e 100644
--- a/items/catalog/whole_sardine.tres
+++ b/items/catalog/whole_sardine.tres
@@ -1,14 +1,16 @@
-[gd_resource type="Resource" script_class="ItemData" load_steps=2 format=3]
+[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 = "valuable live bait tuned for exceptional catches."
+description = "small live bait with strong rarity and quality potential."
+icon = ExtResource("2_icon")
category = 2
-bait_tags = Array[StringName]([&"bait_rarity_5"])
+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
index f01258e..71bd744 100644
--- a/items/catalog/worms.tres
+++ b/items/catalog/worms.tres
@@ -7,7 +7,7 @@
script = ExtResource("1")
item_id = &"worms"
display_name = "worms"
-description = "basic bait that opens a small chance for rarer catches."
+description = "basic bait that opens a small chance for rarer, higher-quality catches."
icon = ExtResource("2_icon")
category = 2
bait_tags = Array[StringName]([&"worm", &"bait_rarity_0"])
diff --git a/items/fishing_rod_data.gd b/items/fishing_rod_data.gd
new file mode 100644
index 0000000..d1c8e7c
--- /dev/null
+++ b/items/fishing_rod_data.gd
@@ -0,0 +1,151 @@
+class_name FishingRodData
+extends ItemData
+
+const FishDataType = preload("res://fish/fish_data.gd")
+const FishingContextType = preload("res://fishing/fishing_context.gd")
+
+enum TimeAffinity {
+ ANY,
+ DAY,
+ NIGHT,
+}
+
+enum WeatherAffinity {
+ ANY,
+ RAIN,
+ FOG,
+}
+
+const MULTIPLIER_COUNT: int = 5
+
+@export_category("Rod Shop")
+@export var shop_enabled: bool = true
+@export_range(1, 999, 1) var shop_order: int = 1
+@export_range(0, 1000000, 1) var shop_price: int = 0
+@export_range(1, 100000, 1) var unlock_level: int = 1
+
+@export_category("Rod Presentation")
+## Optional authored visual. A null scene deliberately uses the current
+## temporary rod mesh so new rod data never requires a model to function.
+@export var rod_model_scene: PackedScene
+@export var rod_model_transform: Transform3D = Transform3D.IDENTITY
+@export var rod_tip_position := Vector3(0.0, 1.1, 0.0)
+
+@export_category("Rod Affinity")
+@export var preferred_location_tags: Array[StringName] = []
+@export var preferred_collection_groups: Array[StringName] = []
+@export var time_affinity: TimeAffinity = TimeAffinity.ANY
+@export var weather_affinity: WeatherAffinity = WeatherAffinity.ANY
+@export_range(0.0, 10.0, 0.01) var affinity_weight_multiplier: float = 1.0
+@export_range(0.0, 10.0, 0.01) var undiscovered_weight_multiplier: float = 1.0
+
+@export_category("Rod Catch Modifiers")
+@export_range(-0.5, 0.5, 0.01) var weight_roll_bias: float = 0.0
+@export_range(0.05, 5.0, 0.01) var bite_time_multiplier: float = 1.0
+@export_range(0.05, 5.0, 0.01) var reel_speed_multiplier: float = 1.0
+@export_range(0.05, 5.0, 0.01) var barrier_power_multiplier: float = 1.0
+@export var rarity_weight_multipliers: Array[float] = [
+ 1.0, 1.0, 1.0, 1.0, 1.0,
+]
+@export var quality_weight_multipliers: Array[float] = [
+ 1.0, 1.0, 1.0, 1.0, 1.0,
+]
+@export_multiline var effect_summary: String
+@export_multiline var tradeoff: String
+
+
+func is_valid() -> bool:
+ return (
+ super.is_valid()
+ and category == Category.ROD
+ and shop_order >= 1
+ and shop_price >= 0
+ and unlock_level >= 1
+ and rarity_weight_multipliers.size() == MULTIPLIER_COUNT
+ and quality_weight_multipliers.size() == MULTIPLIER_COUNT
+ and _multipliers_are_non_negative(rarity_weight_multipliers)
+ and _multipliers_are_non_negative(quality_weight_multipliers)
+ and affinity_weight_multiplier >= 0.0
+ and undiscovered_weight_multiplier >= 0.0
+ and bite_time_multiplier > 0.0
+ and reel_speed_multiplier > 0.0
+ and barrier_power_multiplier > 0.0
+ )
+
+
+func is_shop_available() -> bool:
+ return is_available() and shop_enabled and icon != null
+
+
+func get_affinity_multiplier(
+ fish: FishDataType,
+ context: FishingContextType,
+) -> float:
+ if fish == null or context == null or not _has_authored_affinity():
+ return 1.0
+ if (
+ not preferred_location_tags.is_empty()
+ and not _has_any_match(
+ preferred_location_tags,
+ context.location_tags,
+ )
+ ):
+ return 1.0
+ if (
+ not preferred_collection_groups.is_empty()
+ and fish.collection_group not in preferred_collection_groups
+ ):
+ return 1.0
+ match time_affinity:
+ TimeAffinity.DAY:
+ if context.is_night or context.is_day_night_transition:
+ return 1.0
+ TimeAffinity.NIGHT:
+ if not context.is_night or context.is_day_night_transition:
+ return 1.0
+ match weather_affinity:
+ WeatherAffinity.RAIN:
+ if not context.is_raining:
+ return 1.0
+ WeatherAffinity.FOG:
+ if not context.is_foggy:
+ return 1.0
+ return maxf(affinity_weight_multiplier, 0.0)
+
+
+func get_rarity_multiplier(rarity: int) -> float:
+ if rarity < 0 or rarity >= rarity_weight_multipliers.size():
+ return 1.0
+ return maxf(rarity_weight_multipliers[rarity], 0.0)
+
+
+func get_quality_multiplier(quality: int) -> float:
+ if quality < 0 or quality >= quality_weight_multipliers.size():
+ return 1.0
+ return maxf(quality_weight_multipliers[quality], 0.0)
+
+
+func _has_authored_affinity() -> bool:
+ return (
+ not preferred_location_tags.is_empty()
+ or not preferred_collection_groups.is_empty()
+ or time_affinity != TimeAffinity.ANY
+ or weather_affinity != WeatherAffinity.ANY
+ )
+
+
+func _has_any_match(
+ expected: Array[StringName],
+ actual: Array[StringName],
+) -> bool:
+ for value: StringName in expected:
+ if value in actual:
+ return true
+ return false
+
+
+func _multipliers_are_non_negative(values: Array[float]) -> bool:
+ for value: float in values:
+ if not is_finite(value) or value < 0.0:
+ return false
+ return true
diff --git a/items/fishing_rod_data.gd.uid b/items/fishing_rod_data.gd.uid
new file mode 100644
index 0000000..324cfd7
--- /dev/null
+++ b/items/fishing_rod_data.gd.uid
@@ -0,0 +1 @@
+uid://car3ne3q6dhln
diff --git a/items/icons/art/art_kit.png b/items/icons/art/art_kit.png
new file mode 100644
index 0000000..92adfa7
Binary files /dev/null and b/items/icons/art/art_kit.png differ
diff --git a/items/icons/art/art_kit.png.import b/items/icons/art/art_kit.png.import
new file mode 100644
index 0000000..053fd1f
--- /dev/null
+++ b/items/icons/art/art_kit.png.import
@@ -0,0 +1,40 @@
+[remap]
+
+importer="texture"
+type="CompressedTexture2D"
+uid="uid://datu7j7tq2k3e"
+path="res://.godot/imported/art_kit.png-b54a66a446203c844f97c37241065423.ctex"
+metadata={
+"vram_texture": false
+}
+
+[deps]
+
+source_file="res://items/icons/art/art_kit.png"
+dest_files=["res://.godot/imported/art_kit.png-b54a66a446203c844f97c37241065423.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_eraser.png b/items/icons/art/art_kit_eraser.png
new file mode 100644
index 0000000..08e7691
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_dark.png b/items/icons/art/art_kit_grid_dark.png
new file mode 100644
index 0000000..ba2ec42
Binary files /dev/null and b/items/icons/art/art_kit_grid_dark.png differ
diff --git a/items/icons/art/art_kit_grid_dark.png.import b/items/icons/art/art_kit_grid_dark.png.import
new file mode 100644
index 0000000..2e573da
--- /dev/null
+++ b/items/icons/art/art_kit_grid_dark.png.import
@@ -0,0 +1,40 @@
+[remap]
+
+importer="texture"
+type="CompressedTexture2D"
+uid="uid://ti3njllquh6r"
+path="res://.godot/imported/art_kit_grid_dark.png-a2b68e7c7a0946c1d4bbb0762a0fe05e.ctex"
+metadata={
+"vram_texture": false
+}
+
+[deps]
+
+source_file="res://items/icons/art/art_kit_grid_dark.png"
+dest_files=["res://.godot/imported/art_kit_grid_dark.png-a2b68e7c7a0946c1d4bbb0762a0fe05e.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_dark.png b/items/icons/art/art_kit_grid_finish_dark.png
new file mode 100644
index 0000000..0fe0922
Binary files /dev/null and b/items/icons/art/art_kit_grid_finish_dark.png differ
diff --git a/items/icons/art/art_kit_grid_finish_dark.png.import b/items/icons/art/art_kit_grid_finish_dark.png.import
new file mode 100644
index 0000000..1d91f6e
--- /dev/null
+++ b/items/icons/art/art_kit_grid_finish_dark.png.import
@@ -0,0 +1,40 @@
+[remap]
+
+importer="texture"
+type="CompressedTexture2D"
+uid="uid://br0d1nm0xof6g"
+path="res://.godot/imported/art_kit_grid_finish_dark.png-1658b5ecf5ce8df3dfa03b5f0e4a299c.ctex"
+metadata={
+"vram_texture": false
+}
+
+[deps]
+
+source_file="res://items/icons/art/art_kit_grid_finish_dark.png"
+dest_files=["res://.godot/imported/art_kit_grid_finish_dark.png-1658b5ecf5ce8df3dfa03b5f0e4a299c.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..20cafce
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_finish_mid.png b/items/icons/art/art_kit_grid_finish_mid.png
new file mode 100644
index 0000000..b2ff80b
Binary files /dev/null and b/items/icons/art/art_kit_grid_finish_mid.png differ
diff --git a/items/icons/art/art_kit_grid_finish_mid.png.import b/items/icons/art/art_kit_grid_finish_mid.png.import
new file mode 100644
index 0000000..40fd76d
--- /dev/null
+++ b/items/icons/art/art_kit_grid_finish_mid.png.import
@@ -0,0 +1,40 @@
+[remap]
+
+importer="texture"
+type="CompressedTexture2D"
+uid="uid://bdfey7xm6rp7k"
+path="res://.godot/imported/art_kit_grid_finish_mid.png-9ea2ca237871e37c0ecf862375f58498.ctex"
+metadata={
+"vram_texture": false
+}
+
+[deps]
+
+source_file="res://items/icons/art/art_kit_grid_finish_mid.png"
+dest_files=["res://.godot/imported/art_kit_grid_finish_mid.png-9ea2ca237871e37c0ecf862375f58498.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_dark.png b/items/icons/art/art_kit_grid_hidden_dark.png
new file mode 100644
index 0000000..e1e9e1e
Binary files /dev/null and b/items/icons/art/art_kit_grid_hidden_dark.png differ
diff --git a/items/icons/art/art_kit_grid_hidden_dark.png.import b/items/icons/art/art_kit_grid_hidden_dark.png.import
new file mode 100644
index 0000000..06ef0d9
--- /dev/null
+++ b/items/icons/art/art_kit_grid_hidden_dark.png.import
@@ -0,0 +1,40 @@
+[remap]
+
+importer="texture"
+type="CompressedTexture2D"
+uid="uid://cr1mpyrrykxhs"
+path="res://.godot/imported/art_kit_grid_hidden_dark.png-d8995a8a7bfc3f9bcd1ce726ac962ed5.ctex"
+metadata={
+"vram_texture": false
+}
+
+[deps]
+
+source_file="res://items/icons/art/art_kit_grid_hidden_dark.png"
+dest_files=["res://.godot/imported/art_kit_grid_hidden_dark.png-d8995a8a7bfc3f9bcd1ce726ac962ed5.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..191e852
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_hidden_mid.png b/items/icons/art/art_kit_grid_hidden_mid.png
new file mode 100644
index 0000000..f9cbfe4
Binary files /dev/null and b/items/icons/art/art_kit_grid_hidden_mid.png differ
diff --git a/items/icons/art/art_kit_grid_hidden_mid.png.import b/items/icons/art/art_kit_grid_hidden_mid.png.import
new file mode 100644
index 0000000..7080836
--- /dev/null
+++ b/items/icons/art/art_kit_grid_hidden_mid.png.import
@@ -0,0 +1,40 @@
+[remap]
+
+importer="texture"
+type="CompressedTexture2D"
+uid="uid://d3dcmab3w4ycy"
+path="res://.godot/imported/art_kit_grid_hidden_mid.png-ed65b27edb04579b9bd1b1eda42667db.ctex"
+metadata={
+"vram_texture": false
+}
+
+[deps]
+
+source_file="res://items/icons/art/art_kit_grid_hidden_mid.png"
+dest_files=["res://.godot/imported/art_kit_grid_hidden_mid.png-ed65b27edb04579b9bd1b1eda42667db.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_large_dark.png b/items/icons/art/art_kit_grid_large_dark.png
new file mode 100644
index 0000000..1a26888
Binary files /dev/null and b/items/icons/art/art_kit_grid_large_dark.png differ
diff --git a/items/icons/art/art_kit_grid_large_dark.png.import b/items/icons/art/art_kit_grid_large_dark.png.import
new file mode 100644
index 0000000..799918e
--- /dev/null
+++ b/items/icons/art/art_kit_grid_large_dark.png.import
@@ -0,0 +1,40 @@
+[remap]
+
+importer="texture"
+type="CompressedTexture2D"
+uid="uid://6vqwncasappw"
+path="res://.godot/imported/art_kit_grid_large_dark.png-2327b60999e01476872d201de06f9f50.ctex"
+metadata={
+"vram_texture": false
+}
+
+[deps]
+
+source_file="res://items/icons/art/art_kit_grid_large_dark.png"
+dest_files=["res://.godot/imported/art_kit_grid_large_dark.png-2327b60999e01476872d201de06f9f50.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_large_light.png b/items/icons/art/art_kit_grid_large_light.png
new file mode 100644
index 0000000..53ed358
Binary files /dev/null and b/items/icons/art/art_kit_grid_large_light.png differ
diff --git a/items/icons/art/art_kit_grid_large_light.png.import b/items/icons/art/art_kit_grid_large_light.png.import
new file mode 100644
index 0000000..1f8447c
--- /dev/null
+++ b/items/icons/art/art_kit_grid_large_light.png.import
@@ -0,0 +1,40 @@
+[remap]
+
+importer="texture"
+type="CompressedTexture2D"
+uid="uid://b75kbv3pib0qj"
+path="res://.godot/imported/art_kit_grid_large_light.png-54205faa1c3601a058feb891cd59af61.ctex"
+metadata={
+"vram_texture": false
+}
+
+[deps]
+
+source_file="res://items/icons/art/art_kit_grid_large_light.png"
+dest_files=["res://.godot/imported/art_kit_grid_large_light.png-54205faa1c3601a058feb891cd59af61.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_large_mid.png b/items/icons/art/art_kit_grid_large_mid.png
new file mode 100644
index 0000000..8f30fce
Binary files /dev/null and b/items/icons/art/art_kit_grid_large_mid.png differ
diff --git a/items/icons/art/art_kit_grid_large_mid.png.import b/items/icons/art/art_kit_grid_large_mid.png.import
new file mode 100644
index 0000000..436fbd3
--- /dev/null
+++ b/items/icons/art/art_kit_grid_large_mid.png.import
@@ -0,0 +1,40 @@
+[remap]
+
+importer="texture"
+type="CompressedTexture2D"
+uid="uid://eicec6lsxldy"
+path="res://.godot/imported/art_kit_grid_large_mid.png-a1bee2fda05cd0af5076e2d10d5d155d.ctex"
+metadata={
+"vram_texture": false
+}
+
+[deps]
+
+source_file="res://items/icons/art/art_kit_grid_large_mid.png"
+dest_files=["res://.godot/imported/art_kit_grid_large_mid.png-a1bee2fda05cd0af5076e2d10d5d155d.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..8dbc12e
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_medium_dark.png b/items/icons/art/art_kit_grid_medium_dark.png
new file mode 100644
index 0000000..5487617
Binary files /dev/null and b/items/icons/art/art_kit_grid_medium_dark.png differ
diff --git a/items/icons/art/art_kit_grid_medium_dark.png.import b/items/icons/art/art_kit_grid_medium_dark.png.import
new file mode 100644
index 0000000..a9a313f
--- /dev/null
+++ b/items/icons/art/art_kit_grid_medium_dark.png.import
@@ -0,0 +1,40 @@
+[remap]
+
+importer="texture"
+type="CompressedTexture2D"
+uid="uid://b8odlp8s63t84"
+path="res://.godot/imported/art_kit_grid_medium_dark.png-83db4c9a8e6d53a644fd16865cb7bd28.ctex"
+metadata={
+"vram_texture": false
+}
+
+[deps]
+
+source_file="res://items/icons/art/art_kit_grid_medium_dark.png"
+dest_files=["res://.godot/imported/art_kit_grid_medium_dark.png-83db4c9a8e6d53a644fd16865cb7bd28.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_medium_light.png b/items/icons/art/art_kit_grid_medium_light.png
new file mode 100644
index 0000000..2b7365f
Binary files /dev/null and b/items/icons/art/art_kit_grid_medium_light.png differ
diff --git a/items/icons/art/art_kit_grid_medium_light.png.import b/items/icons/art/art_kit_grid_medium_light.png.import
new file mode 100644
index 0000000..2bec287
--- /dev/null
+++ b/items/icons/art/art_kit_grid_medium_light.png.import
@@ -0,0 +1,40 @@
+[remap]
+
+importer="texture"
+type="CompressedTexture2D"
+uid="uid://b5r1wq3u63r7"
+path="res://.godot/imported/art_kit_grid_medium_light.png-136caf56e94ccc972f0fa220397c6194.ctex"
+metadata={
+"vram_texture": false
+}
+
+[deps]
+
+source_file="res://items/icons/art/art_kit_grid_medium_light.png"
+dest_files=["res://.godot/imported/art_kit_grid_medium_light.png-136caf56e94ccc972f0fa220397c6194.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_medium_mid.png b/items/icons/art/art_kit_grid_medium_mid.png
new file mode 100644
index 0000000..eaa71c4
Binary files /dev/null and b/items/icons/art/art_kit_grid_medium_mid.png differ
diff --git a/items/icons/art/art_kit_grid_medium_mid.png.import b/items/icons/art/art_kit_grid_medium_mid.png.import
new file mode 100644
index 0000000..ac3d78e
--- /dev/null
+++ b/items/icons/art/art_kit_grid_medium_mid.png.import
@@ -0,0 +1,40 @@
+[remap]
+
+importer="texture"
+type="CompressedTexture2D"
+uid="uid://c24yrduwv2xmg"
+path="res://.godot/imported/art_kit_grid_medium_mid.png-f3b0bb2b799a61b19f22164b6b718cb5.ctex"
+metadata={
+"vram_texture": false
+}
+
+[deps]
+
+source_file="res://items/icons/art/art_kit_grid_medium_mid.png"
+dest_files=["res://.godot/imported/art_kit_grid_medium_mid.png-f3b0bb2b799a61b19f22164b6b718cb5.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_mid.png b/items/icons/art/art_kit_grid_mid.png
new file mode 100644
index 0000000..304a551
Binary files /dev/null and b/items/icons/art/art_kit_grid_mid.png differ
diff --git a/items/icons/art/art_kit_grid_mid.png.import b/items/icons/art/art_kit_grid_mid.png.import
new file mode 100644
index 0000000..f946bfc
--- /dev/null
+++ b/items/icons/art/art_kit_grid_mid.png.import
@@ -0,0 +1,40 @@
+[remap]
+
+importer="texture"
+type="CompressedTexture2D"
+uid="uid://ddo2kmjtrke6y"
+path="res://.godot/imported/art_kit_grid_mid.png-f9c988d6d2c3fb83633590231f752436.ctex"
+metadata={
+"vram_texture": false
+}
+
+[deps]
+
+source_file="res://items/icons/art/art_kit_grid_mid.png"
+dest_files=["res://.godot/imported/art_kit_grid_mid.png-f9c988d6d2c3fb83633590231f752436.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_dark.png b/items/icons/art/art_kit_grid_restore_dark.png
new file mode 100644
index 0000000..1268809
Binary files /dev/null and b/items/icons/art/art_kit_grid_restore_dark.png differ
diff --git a/items/icons/art/art_kit_grid_restore_dark.png.import b/items/icons/art/art_kit_grid_restore_dark.png.import
new file mode 100644
index 0000000..bc9936b
--- /dev/null
+++ b/items/icons/art/art_kit_grid_restore_dark.png.import
@@ -0,0 +1,40 @@
+[remap]
+
+importer="texture"
+type="CompressedTexture2D"
+uid="uid://cchyevpvrcrtb"
+path="res://.godot/imported/art_kit_grid_restore_dark.png-57a9e7866f9136b53631d8c686343955.ctex"
+metadata={
+"vram_texture": false
+}
+
+[deps]
+
+source_file="res://items/icons/art/art_kit_grid_restore_dark.png"
+dest_files=["res://.godot/imported/art_kit_grid_restore_dark.png-57a9e7866f9136b53631d8c686343955.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..dd7cb01
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_grid_restore_mid.png b/items/icons/art/art_kit_grid_restore_mid.png
new file mode 100644
index 0000000..50b4d9a
Binary files /dev/null and b/items/icons/art/art_kit_grid_restore_mid.png differ
diff --git a/items/icons/art/art_kit_grid_restore_mid.png.import b/items/icons/art/art_kit_grid_restore_mid.png.import
new file mode 100644
index 0000000..b994da2
--- /dev/null
+++ b/items/icons/art/art_kit_grid_restore_mid.png.import
@@ -0,0 +1,40 @@
+[remap]
+
+importer="texture"
+type="CompressedTexture2D"
+uid="uid://cjaqmwiamywp7"
+path="res://.godot/imported/art_kit_grid_restore_mid.png-c4cb540f51a2d6b7fd328f089dc3cd52.ctex"
+metadata={
+"vram_texture": false
+}
+
+[deps]
+
+source_file="res://items/icons/art/art_kit_grid_restore_mid.png"
+dest_files=["res://.godot/imported/art_kit_grid_restore_mid.png-c4cb540f51a2d6b7fd328f089dc3cd52.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_small_dark.png b/items/icons/art/art_kit_grid_small_dark.png
new file mode 100644
index 0000000..7280808
Binary files /dev/null and b/items/icons/art/art_kit_grid_small_dark.png differ
diff --git a/items/icons/art/art_kit_grid_small_dark.png.import b/items/icons/art/art_kit_grid_small_dark.png.import
new file mode 100644
index 0000000..ddcf831
--- /dev/null
+++ b/items/icons/art/art_kit_grid_small_dark.png.import
@@ -0,0 +1,40 @@
+[remap]
+
+importer="texture"
+type="CompressedTexture2D"
+uid="uid://bnosm2ylhkpbp"
+path="res://.godot/imported/art_kit_grid_small_dark.png-19f946c2afe9de146a9c5277988e42eb.ctex"
+metadata={
+"vram_texture": false
+}
+
+[deps]
+
+source_file="res://items/icons/art/art_kit_grid_small_dark.png"
+dest_files=["res://.godot/imported/art_kit_grid_small_dark.png-19f946c2afe9de146a9c5277988e42eb.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_small_light.png b/items/icons/art/art_kit_grid_small_light.png
new file mode 100644
index 0000000..72a1ef2
Binary files /dev/null and b/items/icons/art/art_kit_grid_small_light.png differ
diff --git a/items/icons/art/art_kit_grid_small_light.png.import b/items/icons/art/art_kit_grid_small_light.png.import
new file mode 100644
index 0000000..3a9e7a1
--- /dev/null
+++ b/items/icons/art/art_kit_grid_small_light.png.import
@@ -0,0 +1,40 @@
+[remap]
+
+importer="texture"
+type="CompressedTexture2D"
+uid="uid://c5h70o3l0xov8"
+path="res://.godot/imported/art_kit_grid_small_light.png-04b6521c5f1923d20f59baa8674be0a5.ctex"
+metadata={
+"vram_texture": false
+}
+
+[deps]
+
+source_file="res://items/icons/art/art_kit_grid_small_light.png"
+dest_files=["res://.godot/imported/art_kit_grid_small_light.png-04b6521c5f1923d20f59baa8674be0a5.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_small_mid.png b/items/icons/art/art_kit_grid_small_mid.png
new file mode 100644
index 0000000..35b00a4
Binary files /dev/null and b/items/icons/art/art_kit_grid_small_mid.png differ
diff --git a/items/icons/art/art_kit_grid_small_mid.png.import b/items/icons/art/art_kit_grid_small_mid.png.import
new file mode 100644
index 0000000..1ea038c
--- /dev/null
+++ b/items/icons/art/art_kit_grid_small_mid.png.import
@@ -0,0 +1,40 @@
+[remap]
+
+importer="texture"
+type="CompressedTexture2D"
+uid="uid://dhevyq3kafoim"
+path="res://.godot/imported/art_kit_grid_small_mid.png-07dcaf91769645ebd9fe713ecd116ebe.ctex"
+metadata={
+"vram_texture": false
+}
+
+[deps]
+
+source_file="res://items/icons/art/art_kit_grid_small_mid.png"
+dest_files=["res://.godot/imported/art_kit_grid_small_mid.png-07dcaf91769645ebd9fe713ecd116ebe.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_xl_dark.png b/items/icons/art/art_kit_grid_xl_dark.png
new file mode 100644
index 0000000..d07ad1b
Binary files /dev/null and b/items/icons/art/art_kit_grid_xl_dark.png differ
diff --git a/items/icons/art/art_kit_grid_xl_dark.png.import b/items/icons/art/art_kit_grid_xl_dark.png.import
new file mode 100644
index 0000000..02c33b3
--- /dev/null
+++ b/items/icons/art/art_kit_grid_xl_dark.png.import
@@ -0,0 +1,40 @@
+[remap]
+
+importer="texture"
+type="CompressedTexture2D"
+uid="uid://d27djk1eme6bn"
+path="res://.godot/imported/art_kit_grid_xl_dark.png-add0fb303950020a41288746fc69713d.ctex"
+metadata={
+"vram_texture": false
+}
+
+[deps]
+
+source_file="res://items/icons/art/art_kit_grid_xl_dark.png"
+dest_files=["res://.godot/imported/art_kit_grid_xl_dark.png-add0fb303950020a41288746fc69713d.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_xl_light.png b/items/icons/art/art_kit_grid_xl_light.png
new file mode 100644
index 0000000..92e4d43
Binary files /dev/null and b/items/icons/art/art_kit_grid_xl_light.png differ
diff --git a/items/icons/art/art_kit_grid_xl_light.png.import b/items/icons/art/art_kit_grid_xl_light.png.import
new file mode 100644
index 0000000..19d9027
--- /dev/null
+++ b/items/icons/art/art_kit_grid_xl_light.png.import
@@ -0,0 +1,40 @@
+[remap]
+
+importer="texture"
+type="CompressedTexture2D"
+uid="uid://5axhxgpgvihn"
+path="res://.godot/imported/art_kit_grid_xl_light.png-f85f9418139b6a98f711d5a065adbcbf.ctex"
+metadata={
+"vram_texture": false
+}
+
+[deps]
+
+source_file="res://items/icons/art/art_kit_grid_xl_light.png"
+dest_files=["res://.godot/imported/art_kit_grid_xl_light.png-f85f9418139b6a98f711d5a065adbcbf.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_xl_mid.png b/items/icons/art/art_kit_grid_xl_mid.png
new file mode 100644
index 0000000..868f427
Binary files /dev/null and b/items/icons/art/art_kit_grid_xl_mid.png differ
diff --git a/items/icons/art/art_kit_grid_xl_mid.png.import b/items/icons/art/art_kit_grid_xl_mid.png.import
new file mode 100644
index 0000000..5123218
--- /dev/null
+++ b/items/icons/art/art_kit_grid_xl_mid.png.import
@@ -0,0 +1,40 @@
+[remap]
+
+importer="texture"
+type="CompressedTexture2D"
+uid="uid://sqvdn521pr00"
+path="res://.godot/imported/art_kit_grid_xl_mid.png-b3445636e6690b581c7eb543adf00733.ctex"
+metadata={
+"vram_texture": false
+}
+
+[deps]
+
+source_file="res://items/icons/art/art_kit_grid_xl_mid.png"
+dest_files=["res://.godot/imported/art_kit_grid_xl_mid.png-b3445636e6690b581c7eb543adf00733.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..fd7bfb6
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/art/art_kit_marker_tip.png b/items/icons/art/art_kit_marker_tip.png
new file mode 100644
index 0000000..7a2de39
Binary files /dev/null and b/items/icons/art/art_kit_marker_tip.png differ
diff --git a/items/icons/art/art_kit_marker_tip.png.import b/items/icons/art/art_kit_marker_tip.png.import
new file mode 100644
index 0000000..079288e
--- /dev/null
+++ b/items/icons/art/art_kit_marker_tip.png.import
@@ -0,0 +1,40 @@
+[remap]
+
+importer="texture"
+type="CompressedTexture2D"
+uid="uid://bg3ex075neqrm"
+path="res://.godot/imported/art_kit_marker_tip.png-a2b8d12e98fcc15518b673bdbdd0e1db.ctex"
+metadata={
+"vram_texture": false
+}
+
+[deps]
+
+source_file="res://items/icons/art/art_kit_marker_tip.png"
+dest_files=["res://.godot/imported/art_kit_marker_tip.png-a2b8d12e98fcc15518b673bdbdd0e1db.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_tip_fine.png b/items/icons/art/art_kit_marker_tip_fine.png
new file mode 100644
index 0000000..29cc8b7
Binary files /dev/null and b/items/icons/art/art_kit_marker_tip_fine.png differ
diff --git a/items/icons/art/art_kit_marker_tip_fine.png.import b/items/icons/art/art_kit_marker_tip_fine.png.import
new file mode 100644
index 0000000..a096332
--- /dev/null
+++ b/items/icons/art/art_kit_marker_tip_fine.png.import
@@ -0,0 +1,40 @@
+[remap]
+
+importer="texture"
+type="CompressedTexture2D"
+uid="uid://dja323uypy8p2"
+path="res://.godot/imported/art_kit_marker_tip_fine.png-1f54142bba82ca92ca8381ad4e1bc2e7.ctex"
+metadata={
+"vram_texture": false
+}
+
+[deps]
+
+source_file="res://items/icons/art/art_kit_marker_tip_fine.png"
+dest_files=["res://.godot/imported/art_kit_marker_tip_fine.png-1f54142bba82ca92ca8381ad4e1bc2e7.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_tip_mid.png b/items/icons/art/art_kit_marker_tip_mid.png
new file mode 100644
index 0000000..74c8498
Binary files /dev/null and b/items/icons/art/art_kit_marker_tip_mid.png differ
diff --git a/items/icons/art/art_kit_marker_tip_mid.png.import b/items/icons/art/art_kit_marker_tip_mid.png.import
new file mode 100644
index 0000000..b0e26d3
--- /dev/null
+++ b/items/icons/art/art_kit_marker_tip_mid.png.import
@@ -0,0 +1,40 @@
+[remap]
+
+importer="texture"
+type="CompressedTexture2D"
+uid="uid://bbdnn6chdfekq"
+path="res://.godot/imported/art_kit_marker_tip_mid.png-cc02669fb5d11d78cd31670752d6f8b9.ctex"
+metadata={
+"vram_texture": false
+}
+
+[deps]
+
+source_file="res://items/icons/art/art_kit_marker_tip_mid.png"
+dest_files=["res://.godot/imported/art_kit_marker_tip_mid.png-cc02669fb5d11d78cd31670752d6f8b9.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_tip_thick.png b/items/icons/art/art_kit_marker_tip_thick.png
new file mode 100644
index 0000000..f7d3c19
Binary files /dev/null and b/items/icons/art/art_kit_marker_tip_thick.png differ
diff --git a/items/icons/art/art_kit_marker_tip_thick.png.import b/items/icons/art/art_kit_marker_tip_thick.png.import
new file mode 100644
index 0000000..b47355d
--- /dev/null
+++ b/items/icons/art/art_kit_marker_tip_thick.png.import
@@ -0,0 +1,40 @@
+[remap]
+
+importer="texture"
+type="CompressedTexture2D"
+uid="uid://638sqeuw5n3g"
+path="res://.godot/imported/art_kit_marker_tip_thick.png-dae24cedd2ec60def334af28f261d280.ctex"
+metadata={
+"vram_texture": false
+}
+
+[deps]
+
+source_file="res://items/icons/art/art_kit_marker_tip_thick.png"
+dest_files=["res://.godot/imported/art_kit_marker_tip_thick.png-dae24cedd2ec60def334af28f261d280.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_tip_thin.png b/items/icons/art/art_kit_marker_tip_thin.png
new file mode 100644
index 0000000..54a4798
Binary files /dev/null and b/items/icons/art/art_kit_marker_tip_thin.png differ
diff --git a/items/icons/art/art_kit_marker_tip_thin.png.import b/items/icons/art/art_kit_marker_tip_thin.png.import
new file mode 100644
index 0000000..94ced56
--- /dev/null
+++ b/items/icons/art/art_kit_marker_tip_thin.png.import
@@ -0,0 +1,40 @@
+[remap]
+
+importer="texture"
+type="CompressedTexture2D"
+uid="uid://bwagkcgl5yjqm"
+path="res://.godot/imported/art_kit_marker_tip_thin.png-d3a317db15da4d5333192c86ef3bf227.ctex"
+metadata={
+"vram_texture": false
+}
+
+[deps]
+
+source_file="res://items/icons/art/art_kit_marker_tip_thin.png"
+dest_files=["res://.godot/imported/art_kit_marker_tip_thin.png-d3a317db15da4d5333192c86ef3bf227.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/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_coffee_plus.png b/items/icons/consumables/64_consumable_coffee_plus.png
index 621f796..61b3849 100644
Binary files a/items/icons/consumables/64_consumable_coffee_plus.png and b/items/icons/consumables/64_consumable_coffee_plus.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_fish.png b/items/icons/consumables/64_consumable_fish.png
index 3738c28..7b408ec 100644
Binary files a/items/icons/consumables/64_consumable_fish.png and b/items/icons/consumables/64_consumable_fish.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
deleted file mode 100644
index acbd889..0000000
Binary files a/items/icons/equipment/64_cooler_plus.png and /dev/null differ
diff --git a/items/icons/equipment/net.png b/items/icons/equipment/net.png
new file mode 100644
index 0000000..522dd65
Binary files /dev/null and b/items/icons/equipment/net.png differ
diff --git a/items/icons/equipment/net.png.import b/items/icons/equipment/net.png.import
new file mode 100644
index 0000000..e32d295
--- /dev/null
+++ b/items/icons/equipment/net.png.import
@@ -0,0 +1,40 @@
+[remap]
+
+importer="texture"
+type="CompressedTexture2D"
+uid="uid://c026rqygllmst"
+path="res://.godot/imported/net.png-695e6b1511b4d1d2f6c885df7ccf0412.ctex"
+metadata={
+"vram_texture": false
+}
+
+[deps]
+
+source_file="res://items/icons/equipment/net.png"
+dest_files=["res://.godot/imported/net.png-695e6b1511b4d1d2f6c885df7ccf0412.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/equipment/shovel.png b/items/icons/equipment/shovel.png
new file mode 100644
index 0000000..089f30c
Binary files /dev/null and b/items/icons/equipment/shovel.png differ
diff --git a/items/icons/equipment/shovel.png.import b/items/icons/equipment/shovel.png.import
new file mode 100644
index 0000000..093a093
--- /dev/null
+++ b/items/icons/equipment/shovel.png.import
@@ -0,0 +1,40 @@
+[remap]
+
+importer="texture"
+type="CompressedTexture2D"
+uid="uid://cabeaw2e0glu"
+path="res://.godot/imported/shovel.png-cdfb898e33414742110b8f36a05aceb3.ctex"
+metadata={
+"vram_texture": false
+}
+
+[deps]
+
+source_file="res://items/icons/equipment/shovel.png"
+dest_files=["res://.godot/imported/shovel.png-cdfb898e33414742110b8f36a05aceb3.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/equipment/temp_net.png b/items/icons/equipment/temp_net.png
new file mode 100644
index 0000000..56ba4de
Binary files /dev/null and b/items/icons/equipment/temp_net.png differ
diff --git a/items/icons/equipment/temp_net.png.import b/items/icons/equipment/temp_net.png.import
new file mode 100644
index 0000000..698ae2b
--- /dev/null
+++ b/items/icons/equipment/temp_net.png.import
@@ -0,0 +1,40 @@
+[remap]
+
+importer="texture"
+type="CompressedTexture2D"
+uid="uid://bokpob3mu1n8o"
+path="res://.godot/imported/temp_net.png-f80e317caaf9c9bd6739a7bb5e49e7b6.ctex"
+metadata={
+"vram_texture": false
+}
+
+[deps]
+
+source_file="res://items/icons/equipment/temp_net.png"
+dest_files=["res://.godot/imported/temp_net.png-f80e317caaf9c9bd6739a7bb5e49e7b6.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/cooler_expansion.svg b/items/icons/placeholder/cooler_expansion.svg
deleted file mode 100644
index 105bff7..0000000
--- a/items/icons/placeholder/cooler_expansion.svg
+++ /dev/null
@@ -1,5 +0,0 @@
-
diff --git a/items/icons/placeholder/cooler_expansion.svg.import b/items/icons/placeholder/cooler_expansion.svg.import
deleted file mode 100644
index fbcc85c..0000000
--- a/items/icons/placeholder/cooler_expansion.svg.import
+++ /dev/null
@@ -1,43 +0,0 @@
-[remap]
-
-importer="texture"
-type="CompressedTexture2D"
-uid="uid://d115jiqughf40"
-path="res://.godot/imported/cooler_expansion.svg-a3d63bc21ba230007f1039372b518c8f.ctex"
-metadata={
-"vram_texture": false
-}
-
-[deps]
-
-source_file="res://items/icons/placeholder/cooler_expansion.svg"
-dest_files=["res://.godot/imported/cooler_expansion.svg-a3d63bc21ba230007f1039372b518c8f.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
deleted file mode 100644
index 8aacdbc..0000000
Binary files a/items/icons/shop/64_power_plus.png and /dev/null differ
diff --git a/items/item_catalog.gd b/items/item_catalog.gd
index b4245a0..511604b 100644
--- a/items/item_catalog.gd
+++ b/items/item_catalog.gd
@@ -28,3 +28,16 @@ func get_valid_items() -> Array[ItemDataType]:
seen[item.item_id] = true
result.append(item)
return result
+
+
+func get_available_item_by_id(item_id: StringName) -> ItemDataType:
+ var item: ItemDataType = get_item_by_id(item_id)
+ return item if item != null and item.is_available() else null
+
+
+func get_available_items() -> Array[ItemDataType]:
+ var result: Array[ItemDataType] = []
+ for item: ItemDataType in get_valid_items():
+ if item.active:
+ result.append(item)
+ return result
diff --git a/items/item_data.gd b/items/item_data.gd
index a34cec0..72ac0d6 100644
--- a/items/item_data.gd
+++ b/items/item_data.gd
@@ -15,6 +15,11 @@ enum Category {
@export var item_id: StringName
@export var display_name: String
@export_multiline var description: String
+@export_category("Developer Catalog")
+## Inactive items remain resolvable for existing saves, but cannot be newly
+## acquired, equipped, used, or presented in shops.
+@export var active: bool = true
+@export_category("Item")
@export var category: Category = Category.UTILITY
@export var bait_tags: Array[StringName] = []
@export var lure_effects: Array[StringName] = []
@@ -35,6 +40,10 @@ func is_valid() -> bool:
)
+func is_available() -> bool:
+ return active and is_valid()
+
+
func get_category_name() -> String:
return Category.keys()[category].to_lower()
diff --git a/items/owned_item.gd b/items/owned_item.gd
index ead5b34..ade8cd1 100644
--- a/items/owned_item.gd
+++ b/items/owned_item.gd
@@ -1,12 +1,20 @@
class_name OwnedItem
extends Resource
+const MAX_STORAGE_SLOT_INDEX: int = 254
+
@export var item_id: StringName
@export var quantity: int = 1
+@export var storage_slot: int = -1
func is_valid() -> bool:
- return not item_id.is_empty() and quantity > 0
+ return (
+ not item_id.is_empty()
+ and quantity > 0
+ and storage_slot >= -1
+ and storage_slot <= MAX_STORAGE_SLOT_INDEX
+ )
func duplicate_record():
@@ -17,4 +25,5 @@ func to_save_dict() -> Dictionary:
return {
"item_id": String(item_id),
"quantity": quantity,
+ "storage_slot": storage_slot,
}
diff --git a/jobs/job_catalog.gd b/jobs/job_catalog.gd
index 4429e89..eb3ea02 100644
--- a/jobs/job_catalog.gd
+++ b/jobs/job_catalog.gd
@@ -14,6 +14,9 @@ enum Kind {
REACH_LEVEL,
DISCOVER_SPECIES,
MASTER_QUALITIES,
+ CATCH_CREATURE_GROUP,
+ DISCOVER_CREATURE_GROUP,
+ MASTER_CREATURE_GROUP,
}
const DAILY_JOB_COUNT: int = 4
@@ -23,6 +26,8 @@ const WEATHER_SEGMENT_HOURS: float = (
const WEATHER_SEGMENT_COUNT: int = (
WorldWeatherService.DAILY_PLAN_SEGMENT_COUNT
)
+const LEGACY_WEATHER_SEGMENT_HOURS: float = 2.0
+const LEGACY_WEATHER_SEGMENT_COUNT: int = 12
const MAX_JOB_REWARD_COINS: int = 100000
const MAX_JOB_REWARD_EXPERIENCE: int = 10000
@@ -90,7 +95,7 @@ static func generate_daily_jobs(
))
var species: Array[FishDataType] = []
for fish: FishDataType in candidates:
- if fish != null and fish.is_selectable():
+ if fish != null and fish.is_fishable():
species.append(fish)
if not species.is_empty():
species.sort_custom(
@@ -109,6 +114,28 @@ static func generate_daily_jobs(
50,
{"fish_id": String(selected.id)},
))
+ if _has_creature_group(candidates, FishDataType.CreatureGroup.INSECT):
+ optional.append(_job(
+ "insect_roundup",
+ "bug hunt",
+ "catch 3 insects",
+ Kind.CATCH_CREATURE_GROUP,
+ 3,
+ 40,
+ 50,
+ {"creature_group": int(FishDataType.CreatureGroup.INSECT)},
+ ))
+ if _has_creature_group(candidates, FishDataType.CreatureGroup.SHELLFISH):
+ optional.append(_job(
+ "shellfish_search",
+ "shore patrol",
+ "catch 3 shellfish",
+ Kind.CATCH_CREATURE_GROUP,
+ 3,
+ 45,
+ 50,
+ {"creature_group": int(FishDataType.CreatureGroup.SHELLFISH)},
+ ))
_shuffle(optional, rng)
while jobs.size() < DAILY_JOB_COUNT and not optional.is_empty():
jobs.append(optional.pop_back())
@@ -170,6 +197,8 @@ static func generate_weather_schedule(
static func lifetime_chains(
registered_species_count: int,
+ registered_insect_count: int = 0,
+ registered_shellfish_count: int = 0,
) -> Array[Dictionary]:
var chains: Array[Dictionary] = [
_chain("catch", Kind.CATCH_TOTAL, [100, 1000, 5000], [
@@ -195,15 +224,33 @@ static func lifetime_chains(
[registered_species_count],
[[1500, 2000]],
))
+ _append_creature_lifetime_chains(
+ chains,
+ "insect",
+ FishDataType.CreatureGroup.INSECT,
+ registered_insect_count,
+ )
+ _append_creature_lifetime_chains(
+ chains,
+ "shellfish",
+ FishDataType.CreatureGroup.SHELLFISH,
+ registered_shellfish_count,
+ )
return chains
static func visible_lifetime_jobs(
registered_species_count: int,
claimed_ids: Array[String],
+ registered_insect_count: int = 0,
+ registered_shellfish_count: int = 0,
) -> Array[Dictionary]:
var visible: Array[Dictionary] = []
- for chain: Dictionary in lifetime_chains(registered_species_count):
+ for chain: Dictionary in lifetime_chains(
+ registered_species_count,
+ registered_insect_count,
+ registered_shellfish_count,
+ ):
var targets: Array = chain.get("targets", [])
var rewards: Array = chain.get("rewards", [])
for tier_index: int in targets.size():
@@ -214,6 +261,9 @@ static func visible_lifetime_jobs(
if job_id in claimed_ids:
continue
var reward: Array = rewards[tier_index]
+ var extra: Dictionary = {}
+ if chain.has("creature_group"):
+ extra["creature_group"] = int(chain["creature_group"])
visible.append(_job(
job_id,
_lifetime_title(str(chain.get("id", "")), int(targets[tier_index])),
@@ -224,6 +274,7 @@ static func visible_lifetime_jobs(
int(targets[tier_index]),
int(reward[0]),
int(reward[1]),
+ extra,
))
break
return visible
@@ -254,10 +305,10 @@ static func is_valid_job(value: Variant) -> bool:
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
+ job.get("kind"), Kind.CATCH_TOTAL, Kind.MASTER_CREATURE_GROUP
)
and kind >= Kind.CATCH_TOTAL
- and kind <= Kind.MASTER_QUALITIES
+ and kind <= Kind.MASTER_CREATURE_GROUP
and is_bounded_integer(job.get("target"), 1, 1000000000)
and is_bounded_integer(
job.get("fish_coin"), 0, MAX_JOB_REWARD_COINS
@@ -316,6 +367,12 @@ static func is_valid_job(value: Variant) -> bool:
and not str(job.get("fish_id", "")).is_empty()
and str(job.get("fish_id", "")).length() <= 96
)
+ Kind.CATCH_CREATURE_GROUP, Kind.DISCOVER_CREATURE_GROUP, Kind.MASTER_CREATURE_GROUP:
+ return is_bounded_integer(
+ job.get("creature_group"),
+ FishDataType.CreatureGroup.FISH,
+ FishDataType.CreatureGroup.SHELLFISH,
+ )
return true
@@ -341,6 +398,36 @@ static func is_valid_weather_schedule(value: Variant) -> bool:
return WorldWeatherService.is_valid_daily_plan_schedule(value)
+static func is_valid_legacy_weather_schedule(value: Variant) -> bool:
+ if typeof(value) != TYPE_ARRAY:
+ return false
+ var schedule: Array = value
+ if schedule.size() != LEGACY_WEATHER_SEGMENT_COUNT:
+ return false
+ for index: int in schedule.size():
+ if typeof(schedule[index]) != TYPE_DICTIONARY:
+ return false
+ var entry: Dictionary = schedule[index]
+ var expected_hour: float = fposmod(
+ WorldTimeService.DAY_START_HOUR
+ + float(index) * LEGACY_WEATHER_SEGMENT_HOURS,
+ WorldTimeService.HOURS_PER_DAY,
+ )
+ if (
+ typeof(entry.get("start_hour")) not in [TYPE_FLOAT, TYPE_INT]
+ or not is_equal_approx(
+ float(entry.get("start_hour", -1.0)), expected_hour
+ )
+ or not is_bounded_integer(
+ entry.get("weather"),
+ WorldWeatherService.Weather.SUNNY,
+ WorldWeatherService.Weather.FOGGY,
+ )
+ ):
+ return false
+ return true
+
+
static func _job(
id: String,
title: String,
@@ -369,13 +456,16 @@ static func _chain(
kind: Kind,
targets: Array[int],
rewards: Array,
+ extra: Dictionary = {},
) -> Dictionary:
- return {
+ var result: Dictionary = {
"id": id,
"kind": int(kind),
"targets": targets,
"rewards": rewards,
}
+ result.merge(extra, true)
+ return result
static func _lifetime_title(chain_id: String, target: int) -> String:
@@ -383,13 +473,25 @@ static func _lifetime_title(chain_id: String, target: int) -> String:
"catch":
return "seasoned angler"
"sell":
- return "fish coin regular"
+ return "market regular"
"level":
return "reach level %d" % target
"discover":
return "complete the catalog"
"master":
return "quality master"
+ "insect_catch":
+ return "seasoned bug catcher"
+ "insect_discover":
+ return "complete the insect catalog"
+ "insect_master":
+ return "insect quality master"
+ "shellfish_catch":
+ return "seasoned beachcomber"
+ "shellfish_discover":
+ return "complete the shellfish catalog"
+ "shellfish_master":
+ return "shellfish quality master"
return "long-term job"
@@ -405,9 +507,67 @@ static func _lifetime_description(chain_id: String, target: int) -> String:
return "discover all %d cataloged fish" % target
"master":
return "collect every quality of all %d fish" % target
+ "insect_catch":
+ return "catch %d insects" % target
+ "insect_discover":
+ return "discover all %d cataloged insects" % target
+ "insect_master":
+ return "collect every quality of all %d insects" % target
+ "shellfish_catch":
+ return "catch %d shellfish" % target
+ "shellfish_discover":
+ return "discover all %d cataloged shellfish" % target
+ "shellfish_master":
+ return "collect every quality of all %d shellfish" % target
return "keep fishing"
+static func _has_creature_group(
+ candidates: Array[FishDataType],
+ creature_group: FishDataType.CreatureGroup,
+) -> bool:
+ for fish: FishDataType in candidates:
+ if (
+ fish != null
+ and fish.is_selectable()
+ and fish.get_creature_group() == creature_group
+ ):
+ return true
+ return false
+
+
+static func _append_creature_lifetime_chains(
+ chains: Array[Dictionary],
+ id_prefix: String,
+ creature_group: FishDataType.CreatureGroup,
+ registered_count: int,
+) -> void:
+ if registered_count <= 0:
+ return
+ var group_data := {"creature_group": int(creature_group)}
+ chains.append(_chain(
+ "%s_catch" % id_prefix,
+ Kind.CATCH_CREATURE_GROUP,
+ [10, 50, 250],
+ [[75, 100], [250, 350], [900, 1100]],
+ group_data,
+ ))
+ chains.append(_chain(
+ "%s_discover" % id_prefix,
+ Kind.DISCOVER_CREATURE_GROUP,
+ [registered_count],
+ [[250, 350]],
+ group_data,
+ ))
+ chains.append(_chain(
+ "%s_master" % id_prefix,
+ Kind.MASTER_CREATURE_GROUP,
+ [registered_count],
+ [[750, 1000]],
+ group_data,
+ ))
+
+
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)
diff --git a/jobs/player_job_service.gd b/jobs/player_job_service.gd
index 9955ccb..7ede866 100644
--- a/jobs/player_job_service.gd
+++ b/jobs/player_job_service.gd
@@ -25,6 +25,7 @@ var _session: NetworkSession
var _save_manager: PlayerSaveManager
var _network_fishing: NetworkFishingService
var _network_sale: NetworkSaleService
+var _network_world_spawns: NetworkWorldSpawnService
var _progression_ready: bool = false
var _host_board: Dictionary = {}
@@ -35,6 +36,8 @@ var _daily_completions: Dictionary[String, int] = {}
var _pending_rewards: Array[Dictionary] = []
var _lifetime_claimed: Array[String] = []
var _total_catches: int = 0
+var _total_insects: int = 0
+var _total_shellfish: int = 0
var _total_sold: int = 0
var _daily_clock_hour: float = WorldTimeService.DEFAULT_START_HOUR
@@ -65,9 +68,11 @@ func setup(
func bind_authoritative_services(
network_fishing: NetworkFishingService,
network_sale: NetworkSaleService,
+ network_world_spawns: NetworkWorldSpawnService,
) -> void:
_network_fishing = network_fishing
_network_sale = network_sale
+ _network_world_spawns = network_world_spawns
if not _network_fishing.local_catch_received.is_connected(
_on_authoritative_catch
):
@@ -78,6 +83,12 @@ func bind_authoritative_services(
_network_sale.local_sale_finished.connect(
_on_authoritative_sale_finished
)
+ if not _network_world_spawns.local_capture_received.is_connected(
+ _on_authoritative_catch
+ ):
+ _network_world_spawns.local_capture_received.connect(
+ _on_authoritative_catch
+ )
func set_save_manager(save_manager: PlayerSaveManager) -> void:
@@ -123,7 +134,10 @@ func get_daily_jobs() -> Array[Dictionary]:
func get_lifetime_jobs() -> Array[Dictionary]:
var result: Array[Dictionary] = JobCatalog.visible_lifetime_jobs(
- _registered_species_count(), _lifetime_claimed
+ _registered_species_count(),
+ _lifetime_claimed,
+ _registered_creature_count(FishDataType.CreatureGroup.INSECT),
+ _registered_creature_count(FishDataType.CreatureGroup.SHELLFISH),
)
for job: Dictionary in result:
var progress: int = _lifetime_progress(job)
@@ -150,6 +164,10 @@ 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"
@@ -232,6 +250,8 @@ func to_save_data() -> Dictionary:
"lifetime_claimed": _lifetime_claimed.duplicate(),
"statistics": {
"fish_caught": _total_catches,
+ "insects_caught": _total_insects,
+ "shellfish_caught": _total_shellfish,
"fish_sold": _total_sold,
},
}
@@ -241,16 +261,30 @@ 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", ""))
+ var saved_host_board: Dictionary = data.get("host_board", {})
+ var legacy_weather_board: bool = (
+ not saved_host_board.is_empty()
+ and not validate_board(saved_host_board)
+ and _validate_legacy_board(saved_host_board)
+ )
+ _host_board = (
+ {}
+ if legacy_weather_board
+ else saved_host_board.duplicate(true)
+ )
+ _active_plan_id = (
+ "" if legacy_weather_board else 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])
+ if not legacy_weather_board:
+ 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)
+ if not legacy_weather_board:
+ 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()
@@ -276,6 +310,8 @@ func restore_from_save_data(data: Dictionary) -> bool:
_lifetime_claimed.append(str(value))
var statistics: Dictionary = data.get("statistics", {})
_total_catches = int(statistics.get("fish_caught", 0))
+ _total_insects = int(statistics.get("insects_caught", 0))
+ _total_shellfish = int(statistics.get("shellfish_caught", 0))
_total_sold = int(statistics.get("fish_sold", 0))
if _session != null and _session.is_host():
_active_board = _host_board.duplicate(true)
@@ -300,6 +336,8 @@ func reset_to_defaults() -> void:
_pending_rewards.clear()
_lifetime_claimed.clear()
_total_catches = 0
+ _total_insects = 0
+ _total_shellfish = 0
_total_sold = 0
_daily_clock_hour = WorldTimeService.DEFAULT_START_HOUR
changed.emit()
@@ -315,7 +353,12 @@ static func default_save_data() -> Dictionary:
"daily_clock_hour": WorldTimeService.DEFAULT_START_HOUR,
"pending_rewards": [],
"lifetime_claimed": [],
- "statistics": {"fish_caught": 0, "fish_sold": 0},
+ "statistics": {
+ "fish_caught": 0,
+ "insects_caught": 0,
+ "shellfish_caught": 0,
+ "fish_sold": 0,
+ },
}
@@ -341,6 +384,14 @@ static func validate_board(value: Variant) -> bool:
)
):
return false
+ if (
+ board.has("calendar_cycle_id")
+ and (
+ typeof(board["calendar_cycle_id"]) != TYPE_STRING
+ or str(board["calendar_cycle_id"]).length() != 10
+ )
+ ):
+ return false
var jobs: Array = board.get("jobs", [])
if jobs.is_empty() or jobs.size() > 8:
return false
@@ -374,7 +425,11 @@ static func validate_save_data(value: Variant) -> bool:
):
return false
var host_board: Dictionary = data.get("host_board", {})
- if not host_board.is_empty() and not validate_board(host_board):
+ if (
+ not host_board.is_empty()
+ and not validate_board(host_board)
+ and not _validate_legacy_board(host_board)
+ ):
return false
if (
data.has("daily_clock_hour")
@@ -426,15 +481,55 @@ static func validate_save_data(value: Variant) -> bool:
JobCatalog.is_bounded_integer(
statistics.get("fish_caught"), 0, MAX_PROGRESS_VALUE
)
+ and JobCatalog.is_bounded_integer(
+ statistics.get("insects_caught", 0), 0, MAX_PROGRESS_VALUE
+ )
+ and JobCatalog.is_bounded_integer(
+ statistics.get("shellfish_caught", 0), 0, MAX_PROGRESS_VALUE
+ )
and JobCatalog.is_bounded_integer(
statistics.get("fish_sold"), 0, MAX_PROGRESS_VALUE
)
)
+static func _validate_legacy_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.LEGACY_WEATHER_SEGMENT_COUNT - 1,
+ )
+ or typeof(board.get("jobs")) != TYPE_ARRAY
+ or not JobCatalog.is_valid_legacy_weather_schedule(
+ board.get("weather_schedule", [])
+ )
+ ):
+ return false
+ var jobs: Array = board.get("jobs", [])
+ if jobs.is_empty() or jobs.size() > 8:
+ return false
+ for job: Variant in jobs:
+ if not JobCatalog.is_valid_job(job):
+ return false
+ return true
+
+
func _activate_host_board() -> void:
- if _host_board.is_empty():
- _generate_host_board(0)
+ if _world_time != null:
+ _daily_clock_hour = _world_time.get_time_hours()
+ if _host_board.is_empty() or _host_board_is_stale():
+ var next_cycle: int = int(_host_board.get("cycle", -1)) + 1
+ _generate_host_board(maxi(next_cycle, 0))
else:
_active_board = _host_board.duplicate(true)
_switch_plan(str(_host_board.get("plan_id", "")))
@@ -450,7 +545,7 @@ func _generate_host_board(cycle: int) -> void:
var candidates: Array[FishDataType] = []
if _catalog != null:
for fish: FishDataType in _catalog.candidates:
- if fish != null:
+ if fish != null and fish.is_selectable():
candidates.append(fish)
var schedule_anchor_index: int = _current_weather_segment()
var allow_weather_jobs: bool = (
@@ -468,6 +563,11 @@ func _generate_host_board(cycle: int) -> void:
plan_id, jobs, schedule_anchor_index
),
}
+ var calendar_cycle_id: String = _world_time.get_calendar_cycle_id(
+ DAILY_REFRESH_HOUR
+ )
+ if not calendar_cycle_id.is_empty():
+ _host_board["calendar_cycle_id"] = calendar_cycle_id
_active_board = _host_board.duplicate(true)
_switch_plan(plan_id)
_apply_host_weather_plan()
@@ -510,7 +610,10 @@ func _on_natural_time_advanced(hours: float) -> void:
_progression_ready
and _session != null
and _session.is_host()
- and _crossed_daily_refresh(previous_hour, _daily_clock_hour)
+ and (
+ hours >= WorldTimeService.HOURS_PER_DAY
+ or _crossed_daily_refresh(previous_hour, _daily_clock_hour)
+ )
):
var cycle: int = int(_host_board.get("cycle", -1)) + 1
_generate_host_board(maxi(cycle, 0))
@@ -536,7 +639,13 @@ func _on_session_state_changed(state: NetworkSession.State) -> void:
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)
+ match fish_catch.fish.get_creature_group():
+ FishDataType.CreatureGroup.FISH:
+ _total_catches = mini(_total_catches + 1, MAX_PROGRESS_VALUE)
+ FishDataType.CreatureGroup.INSECT:
+ _total_insects = mini(_total_insects + 1, MAX_PROGRESS_VALUE)
+ FishDataType.CreatureGroup.SHELLFISH:
+ _total_shellfish = mini(_total_shellfish + 1, MAX_PROGRESS_VALUE)
_update_daily_for_catch(fish_catch)
changed.emit()
@@ -558,23 +667,40 @@ func _on_authoritative_sale_finished(
func _update_daily_for_catch(fish_catch: FishCatchType) -> void:
+ var creature_group: int = int(fish_catch.fish.get_creature_group())
+ var is_fish: bool = creature_group == FishDataType.CreatureGroup.FISH
for job: Dictionary in get_daily_jobs():
var matches: bool = false
match int(job.get("kind", -1)):
JobCatalog.Kind.CATCH_TOTAL:
- matches = true
+ matches = is_fish
JobCatalog.Kind.CATCH_WATER:
- matches = fish_catch.fish.is_allowed_in_water(
+ matches = is_fish and 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))
+ matches = (
+ is_fish
+ and fish_catch.quality >= int(job.get("minimum_quality", 0))
+ )
JobCatalog.Kind.CATCH_PHASE:
- matches = int(_world_time.get_phase()) == int(job.get("phase", -1))
+ matches = (
+ is_fish
+ and 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))
+ matches = (
+ is_fish
+ and 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", ""))
+ matches = (
+ is_fish
+ and String(fish_catch.fish_id) == str(job.get("fish_id", ""))
+ )
+ JobCatalog.Kind.CATCH_CREATURE_GROUP:
+ matches = creature_group == int(job.get("creature_group", -1))
if matches:
_advance_daily(job, 1)
@@ -656,9 +782,7 @@ func _reward_claimed(reward: Dictionary) -> void:
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 and %d xp" % [
- fish_coin, experience,
- ])
+ status_changed.emit("payment received — %d xp" % experience)
changed.emit()
@@ -671,26 +795,74 @@ func _lifetime_progress(job: Dictionary) -> int:
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
+ return _collection_progress(
+ FishDataType.CreatureGroup.FISH, false
+ )
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 _collection_progress(
+ FishDataType.CreatureGroup.FISH, true
+ )
+ JobCatalog.Kind.CATCH_CREATURE_GROUP:
+ match int(job.get("creature_group", -1)):
+ FishDataType.CreatureGroup.INSECT:
+ return _total_insects
+ FishDataType.CreatureGroup.SHELLFISH:
+ return _total_shellfish
+ JobCatalog.Kind.DISCOVER_CREATURE_GROUP:
+ return _collection_progress(
+ int(job.get("creature_group", -1)) as FishDataType.CreatureGroup,
+ false,
+ )
+ JobCatalog.Kind.MASTER_CREATURE_GROUP:
+ return _collection_progress(
+ int(job.get("creature_group", -1)) as FishDataType.CreatureGroup,
+ true,
+ )
return 0
func _registered_species_count() -> int:
+ return _registered_creature_count(FishDataType.CreatureGroup.FISH)
+
+
+func _registered_creature_count(
+ creature_group: FishDataType.CreatureGroup,
+) -> int:
var count: int = 0
if _catalog != null:
for fish: FishDataType in _catalog.candidates:
- if fish != null and fish.is_selectable():
+ if (
+ fish != null
+ and fish.is_selectable()
+ and fish.get_creature_group() == creature_group
+ ):
count += 1
return count
+func _collection_progress(
+ creature_group: FishDataType.CreatureGroup,
+ require_mastery: bool,
+) -> int:
+ var progress: int = 0
+ if _collection == null or _catalog == null:
+ return progress
+ for fish: FishDataType in _catalog.candidates:
+ if (
+ fish == null
+ or not fish.is_selectable()
+ or fish.get_creature_group() != creature_group
+ ):
+ continue
+ if (
+ _collection.has_mastered(fish.id)
+ if require_mastery
+ else _collection.has_discovered(fish.id)
+ ):
+ progress += 1
+ return progress
+
+
func _expire_incomplete_daily_jobs() -> void:
_daily_progress.clear()
_daily_completions.clear()
@@ -725,7 +897,7 @@ func _matches_canonical_board(board: Dictionary) -> bool:
var candidates: Array[FishDataType] = []
if _catalog != null:
for fish: FishDataType in _catalog.candidates:
- if fish != null:
+ if fish != null and fish.is_selectable():
candidates.append(fish)
var anchor_index: int = int(board.get("schedule_anchor_index", -1))
var allow_weather_jobs: bool = (
@@ -757,6 +929,19 @@ func _current_weather_segment() -> int:
)
+func _host_board_is_stale() -> bool:
+ if _world_time == null:
+ return false
+ var current_cycle_id: String = _world_time.get_calendar_cycle_id(
+ DAILY_REFRESH_HOUR
+ )
+ return (
+ not current_cycle_id.is_empty()
+ and str(_host_board.get("calendar_cycle_id", ""))
+ != current_cycle_id
+ )
+
+
static func _daily_claim_id(
plan_id: String,
job_id: String,
diff --git a/main/main.gd b/main/main.gd
index 4647005..324c268 100644
--- a/main/main.gd
+++ b/main/main.gd
@@ -6,12 +6,16 @@ const FishPoolType = preload("res://fish/fish_pool.gd")
const GameUIType = preload("res://ui/game_ui.gd")
const PlayerType = preload("res://player/player.gd")
const TestWorldType = preload("res://world/test_world.gd")
+const WorldLayoutType = preload("res://world/world_layout.gd")
const WaterRecoveryControllerType = preload(
"res://world/water_recovery_controller.gd"
)
const PlayerSaveManagerType = preload(
"res://save/player_save_manager.gd"
)
+const PlayerSaveSlotCatalogType = preload(
+ "res://save/player_save_slot_catalog.gd"
+)
const PlayerSettingsManagerType = preload(
"res://settings/player_settings_manager.gd"
)
@@ -19,15 +23,29 @@ const PlayerSettingsType = preload("res://settings/player_settings.gd")
const ControllerMappingManagerType = preload(
"res://settings/controller_mapping_manager.gd"
)
+const KeyboardMouseMappingManagerType = preload(
+ "res://settings/keyboard_mouse_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")
const ArtShopStockType = preload("res://economy/art_shop_stock.gd")
const ItemDataType = preload("res://items/item_data.gd")
+const FishingRodDataType = preload("res://items/fishing_rod_data.gd")
+const FishingShopStockType = preload("res://economy/fishing_shop_stock.gd")
const FishingShopType = preload("res://ui/fishing_shop.gd")
const FishingShopInteractionType = preload(
"res://world/fishing_shop_interaction.gd"
)
+const DecorShopInteractionType = preload(
+ "res://world/decor_shop_interaction.gd"
+)
+const RVUpgradeInteractionType = preload(
+ "res://world/rv_upgrade_interaction.gd"
+)
+const PlayerStorageInteractionType = preload(
+ "res://world/player_storage_interaction.gd"
+)
const UIPixelationPresenterType = preload(
"res://ui/ui_pixelation_presenter.gd"
)
@@ -37,6 +55,9 @@ const PixelationResetOverlayType = preload(
const WorldPixelationPostprocessType = preload(
"res://main/world_pixelation_postprocess.gd"
)
+const RuntimePerformanceProfileType = preload(
+ "res://main/runtime_performance_profile.gd"
+)
const NetworkSessionType = preload("res://network/network_session.gd")
const DiscoveryClientType = preload("res://network/discovery_client.gd")
const DedicatedServerConfigType = preload(
@@ -55,6 +76,7 @@ const NetworkFishingServiceType = preload(
"res://network/network_fishing_service.gd"
)
const PlayerHotbarType = preload("res://inventory/player_hotbar.gd")
+const DecorCatalogType = preload("res://homes/decor_catalog.gd")
const NetworkSaleServiceType = preload(
"res://network/network_sale_service.gd"
)
@@ -99,20 +121,53 @@ const NetworkWorldWeatherServiceType = preload(
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 GatherableCatalogType = preload(
+ "res://gathering/gatherable_catalog.gd"
+)
+const GatheringControllerType = preload(
+ "res://gathering/gathering_controller.gd"
+)
+const NetworkWorldSpawnServiceType = preload(
+ "res://network/network_world_spawn_service.gd"
+)
+const PlayerHomeStateType = preload("res://homes/player_home_state.gd")
+const HomeWorldServiceType = preload("res://homes/home_world_service.gd")
+const NetworkHomeServiceType = preload(
+ "res://network/network_home_service.gd"
+)
+const HomeDecorControllerType = preload(
+ "res://homes/home_decor_controller.gd"
+)
const TITLE_MUSIC_SILENCE_DB: float = -80.0
+const TITLE_MUSIC_PATH: String = (
+ "res://audio/music/title/as_in_four_wolves.ogg"
+)
+const NEW_GAME_MUSIC_PATH: String = "res://audio/music/world/tulips.wav"
+const DUSK_MUSIC_PATH: String = "res://audio/music/world/craft.mp3"
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
+const DEDICATED_IDLE_PHYSICS_TICKS_PER_SECOND: int = 10
+const DEDICATED_ACTIVE_PHYSICS_TICKS_PER_SECOND: int = 30
+const HOME_ENTRY_INPUT_OWNER: StringName = &"home_entry_transition"
+const HOME_EXIT_INPUT_OWNER: StringName = &"home_exit_transition"
+const HOME_EXIT_FADE_GENERATION: int = -104729
+const HOME_ENTRY_WALK_DISTANCE: float = 1.05
+const HOME_ENTRY_WALK_DURATION: float = 0.55
+const HOME_EXIT_WALK_DISTANCE: float = 1.25
+const HOME_EXIT_WALK_DURATION: float = 0.62
@export var fish_catalog: FishPoolType
@export var pelican_buyer_profile: FishBuyerProfileType
@export var main_shop_buyer_profile: FishBuyerProfileType
@export var item_catalog: ItemCatalogType
+@export var gatherable_catalog: GatherableCatalogType
@export_category("Title Music")
@export_range(-40.0, 0.0, 0.5) var title_music_volume_db: float = -6.0
@export_range(0.0, 10.0, 0.05) var title_music_fade_out_seconds: float = 5.0
+@export_range(-40.0, 0.0, 0.5) var new_game_music_volume_db: float = -6.0
@onready var _test_world: TestWorldType = $TestWorld
@onready var _player: PlayerType = %Player
@@ -120,12 +175,17 @@ const SHOP_PATTERN_SCALE: float = 1.75
@onready var _game_ui: GameUIType = %GameUI
@onready var _water_recovery: WaterRecoveryControllerType = %WaterRecovery
@onready var _save_manager: PlayerSaveManagerType = %PlayerSaveManager
+@onready var _home_state: PlayerHomeStateType = %PlayerHomeState
@onready var _settings_manager: PlayerSettingsManagerType = %PlayerSettingsManager
@onready var _controller_mapping_manager: ControllerMappingManagerType = (
%ControllerMappingManager
)
+@onready var _keyboard_mouse_mapping_manager: KeyboardMouseMappingManagerType = (
+ %KeyboardMouseMappingManager
+)
@onready var _interface_fonts: InterfaceFontController = %InterfaceFontController
@onready var _title_music: AudioStreamPlayer = %TitleMusic
+@onready var _new_game_music: AudioStreamPlayer = %NewGameMusic
@onready var _dusk_music: AudioStreamPlayer = %DuskMusic
@onready var _ui_pixelation: UIPixelationPresenterType = %UIPresentation
@onready var _pixelation_reset: PixelationResetOverlayType = (
@@ -149,6 +209,12 @@ const SHOP_PATTERN_SCALE: float = 1.75
)
@onready var _player_jobs: PlayerJobServiceType = %PlayerJobService
@onready var _network_jobs: NetworkJobServiceType = %NetworkJobService
+@onready var _network_world_spawns: NetworkWorldSpawnServiceType = (
+ %NetworkWorldSpawnService
+)
+@onready var _gathering_controller: GatheringControllerType = (
+ %GatheringController
+)
@onready var _data_root: PlayerDataRoot = %PlayerDataRoot
@onready var _identity_backups: IdentityBackupService = %IdentityBackupService
@onready var _network_profile: NetworkProfilePreferencesType = (
@@ -169,6 +235,10 @@ const SHOP_PATTERN_SCALE: float = 1.75
)
@onready var _network_sale: NetworkSaleServiceType = %NetworkSaleService
@onready var _network_shop: NetworkShopServiceType = %NetworkShopService
+@onready var _network_home: NetworkHomeServiceType = %NetworkHomeService
+@onready var _home_decor_controller: HomeDecorControllerType = (
+ %HomeDecorController
+)
@onready var _network_item_use: NetworkItemUseServiceType = (
%NetworkItemUseService
)
@@ -190,12 +260,18 @@ const SHOP_PATTERN_SCALE: float = 1.75
)
@onready var _players_root: Node3D = $Players
@onready var _surface_drawings_root: Node3D = $SurfaceDrawings
+@onready var _world_gatherables_root: Node3D = $WorldGatherables
+@onready var _home_world: HomeWorldServiceType = %HomeWorldService
@onready var _title_background: ColorRect = %TitleBackground
@onready var _player_menu_backdrop: ColorRect = %PlayerMenuBackdrop
@onready var _shop_backdrop: ColorRect = %ShopBackdrop
var _gameplay_started: bool = false
+var _save_slots := PlayerSaveSlotCatalogType.new()
var _shop_interaction: FishingShopInteractionType
+var _decor_shop_interaction: DecorShopInteractionType
+var _rv_upgrade_shop_interaction: RVUpgradeInteractionType
+var _storage_interaction: PlayerStorageInteractionType
var _title_music_tween: Tween
var _title_music_transition_generation: int = 0
var _title_music_requested: bool = false
@@ -206,6 +282,10 @@ var _join_requested_from_pause: bool = false
var _player_menu_backdrop_tween: Tween
var _shop_backdrop_tween: Tween
var _pending_join_endpoint: String = ""
+var _social_prompt_queue: Array[Dictionary] = []
+var _active_social_prompt: Dictionary = {}
+var _pending_social_invite_join: bool = false
+var _pending_social_invite_gameplay: bool = false
var _server_trust_dialog: ConfirmationDialog
var _pending_trust_changed: bool = false
var _identity_notice_dialog: AcceptDialog
@@ -216,50 +296,51 @@ 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 _performance_profile: RuntimePerformanceProfileType
var _pending_existing_root_path := ""
var _local_recovery_attempt_id: String = ""
var _dedicated_runtime: bool = false
+var _dedicated_metrics_accumulator: float = 0.0
+var _home_exit_transition_active: bool = false
+var _home_exit_waiting_for_network: bool = false
+var _home_exit_start_position: Vector3 = Vector3.ZERO
+var _home_exit_walk_tween: Tween
+var _home_entry_transition_active: bool = false
+var _home_entry_walk_tween: Tween
@onready var _shoreline_ambience: ShorelineAmbience = %ShorelineAmbience
var _rain_ambience: RainAmbienceType
func _ready() -> void:
+ _data_root.prepare_legacy_user_data()
+ _performance_profile = RuntimePerformanceProfileType.from_environment()
_dedicated_runtime = _is_dedicated_server_runtime()
if _dedicated_runtime:
+ Engine.max_fps = 30
+ Engine.physics_ticks_per_second = 30
+ _disable_dedicated_presentation_tree()
call_deferred("_start_dedicated_server")
return
- DisplayServer.window_set_title("NETfishing")
+ _world_pixelation.set_light_performance_profile(
+ _performance_profile.is_light()
+ )
+ _test_world.set_light_performance_profile(
+ _performance_profile.is_light()
+ )
+ _configure_presentation_performance_profile(
+ _performance_profile.is_light()
+ )
+ DisplayServer.window_set_title("straywild")
_rain_ambience = RainAmbienceType.new()
_rain_ambience.name = "RainAmbience"
add_child(_rain_ambience)
_rain_ambience.configure(_world_weather)
+ _configure_audio_performance_profile(_performance_profile.is_light())
_shoreline_ambience.configure(
_player,
_test_world.get_saltwater_shoreline_mesh(),
)
- var menu_pattern_material := (
- _player_menu_backdrop.material as ShaderMaterial
- )
- if menu_pattern_material != null:
- menu_pattern_material.set_shader_parameter(
- "display_scale",
- PLAYER_MENU_PATTERN_SCALE,
- )
- menu_pattern_material.set_shader_parameter(
- "scroll_velocity_pixels",
- PLAYER_MENU_PATTERN_SCROLL_VELOCITY,
- )
- var shop_pattern_material := _shop_backdrop.material as ShaderMaterial
- if shop_pattern_material != null:
- shop_pattern_material.set_shader_parameter(
- "display_scale",
- SHOP_PATTERN_SCALE,
- )
- shop_pattern_material.set_shader_parameter(
- "scroll_velocity_pixels",
- PLAYER_MENU_PATTERN_SCROLL_VELOCITY,
- )
get_window().size_changed.connect(_resize_native_overlays)
_resize_native_overlays()
if not _settings_manager.settings_changed.is_connected(
@@ -268,6 +349,10 @@ func _ready() -> void:
_settings_manager.settings_changed.connect(_apply_runtime_settings)
_settings_manager.load_settings()
_interface_fonts.enforce_standard_font()
+ _interface_fonts.set_controller_text_entry_request(
+ Callable(_game_ui, "request_controller_text_entry_for"),
+ Callable(_game_ui, "is_controller_text_entry_open"),
+ )
if _data_root.resolve():
_configure_portable_stores()
_initialize_after_data_root()
@@ -275,22 +360,155 @@ func _ready() -> void:
_show_data_root_setup()
+func _configure_presentation_performance_profile(light_profile: bool) -> void:
+ _game_ui.set_light_performance_profile(light_profile)
+ var title_background_material := (
+ _title_background.material as ShaderMaterial
+ )
+ if title_background_material != null:
+ title_background_material.set_shader_parameter(
+ "animation_enabled",
+ not light_profile,
+ )
+ var menu_pattern_material := (
+ _player_menu_backdrop.material as ShaderMaterial
+ )
+ if menu_pattern_material != null:
+ menu_pattern_material.set_shader_parameter(
+ "animation_enabled",
+ not light_profile,
+ )
+ menu_pattern_material.set_shader_parameter(
+ "display_scale",
+ PLAYER_MENU_PATTERN_SCALE,
+ )
+ menu_pattern_material.set_shader_parameter(
+ "scroll_velocity_pixels",
+ (
+ Vector2.ZERO
+ if light_profile
+ else PLAYER_MENU_PATTERN_SCROLL_VELOCITY
+ ),
+ )
+ var shop_pattern_material := _shop_backdrop.material as ShaderMaterial
+ if shop_pattern_material != null:
+ shop_pattern_material.set_shader_parameter(
+ "animation_enabled",
+ not light_profile,
+ )
+ shop_pattern_material.set_shader_parameter(
+ "display_scale",
+ SHOP_PATTERN_SCALE,
+ )
+ shop_pattern_material.set_shader_parameter(
+ "scroll_velocity_pixels",
+ (
+ Vector2.ZERO
+ if light_profile
+ else PLAYER_MENU_PATTERN_SCROLL_VELOCITY
+ ),
+ )
+
+
+func _configure_audio_performance_profile(light_profile: bool) -> void:
+ _title_music.stream = _load_optional_audio_stream(TITLE_MUSIC_PATH)
+ _new_game_music.stream = _load_optional_audio_stream(NEW_GAME_MUSIC_PATH)
+ _dusk_music.stream = _load_optional_audio_stream(DUSK_MUSIC_PATH)
+ if light_profile:
+ _shoreline_ambience.set_audio_enabled(false)
+ _rain_ambience.set_audio_enabled(false)
+ return
+ _shoreline_ambience.set_audio_enabled(true)
+ _rain_ambience.set_audio_enabled(true)
+
+
+func _load_optional_audio_stream(path: String) -> AudioStream:
+ if not ResourceLoader.exists(path, "AudioStream"):
+ return null
+ return load(path) as AudioStream
+
+
func _is_dedicated_server_runtime() -> bool:
if OS.has_feature("dedicated_server"):
return true
return "--dedicated-server" in OS.get_cmdline_user_args()
+func _disable_dedicated_presentation_tree() -> void:
+ for path: NodePath in [
+ NodePath("TitleBackgroundLayer"),
+ NodePath("PlayerMenuBackdrop"),
+ NodePath("ShopBackdrop"),
+ NodePath("WorldPixelationPostprocess"),
+ NodePath("UIPresentation"),
+ NodePath("PixelationResetOverlay"),
+ NodePath("WorldTimeVisualController"),
+ NodePath("GatheringController"),
+ NodePath("FishingSpot"),
+ NodePath("WaterRecovery"),
+ NodePath("ControllerMappingManager"),
+ NodePath("KeyboardMouseMappingManager"),
+ ]:
+ var presentation_node: Node = get_node_or_null(path)
+ if presentation_node == null:
+ continue
+ presentation_node.process_mode = Node.PROCESS_MODE_DISABLED
+ if presentation_node is CanvasItem:
+ (presentation_node as CanvasItem).visible = false
+ elif presentation_node is CanvasLayer:
+ (presentation_node as CanvasLayer).visible = false
+ for audio_path: NodePath in [
+ NodePath("TitleMusic"),
+ NodePath("NewGameMusic"),
+ NodePath("DuskMusic"),
+ NodePath("ShorelineAmbience"),
+ ]:
+ var audio_node: Node = get_node_or_null(audio_path)
+ if audio_node == null:
+ continue
+ if audio_node.has_method("stop"):
+ audio_node.call("stop")
+ audio_node.process_mode = Node.PROCESS_MODE_DISABLED
+
+
+func _print_dedicated_metrics() -> void:
+ var metrics: Dictionary = {
+ "players": _network_session.get_player_count(),
+ "fps": Engine.get_frames_per_second(),
+ "frame_process_ms": Performance.get_monitor(
+ Performance.TIME_PROCESS
+ ) * 1000.0,
+ "frame_physics_ms": Performance.get_monitor(
+ Performance.TIME_PHYSICS_PROCESS
+ ) * 1000.0,
+ "memory_mib": Performance.get_monitor(
+ Performance.MEMORY_STATIC
+ ) / (1024.0 * 1024.0),
+ "nodes": int(Performance.get_monitor(Performance.OBJECT_NODE_COUNT)),
+ "physics_active_objects": int(Performance.get_monitor(
+ Performance.PHYSICS_3D_ACTIVE_OBJECTS
+ )),
+ "physics_collision_pairs": int(Performance.get_monitor(
+ Performance.PHYSICS_3D_COLLISION_PAIRS
+ )),
+ "movement": _network_session.get_movement_metrics(),
+ }
+ if _network_fishing.has_method("get_network_metrics"):
+ metrics["fishing"] = _network_fishing.call("get_network_metrics")
+ if _network_world_spawns.has_method("get_network_metrics"):
+ metrics["gatherables"] = _network_world_spawns.call(
+ "get_network_metrics"
+ )
+ print("straywild server metrics: %s" % JSON.stringify(metrics))
+
+
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):
+ if not _data_root.has_data_manifest(data_path):
var created: Dictionary = _data_root.create_unbound_root(data_path)
if not bool(created.get("ok", false)):
_fail_dedicated_server(str(created.get(
@@ -308,13 +526,50 @@ func _start_dedicated_server() -> void:
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
+ if not _network_chat.configure_dedicated_history(
+ config.chat_logging,
+ config.chat_log_path,
+ ):
+ _fail_dedicated_server(
+ "The dedicated-server chat privacy configuration is invalid."
+ )
+ return
+ if not _apply_world(
+ WorldLayoutType.GENERATED,
+ config.world_seed,
+ false,
+ ):
+ _fail_dedicated_server("The configured world seed could not be generated.")
+ return
+ _test_world.set_dedicated_simulation(true)
+ if not _network_session.set_host_world(
+ WorldLayoutType.GENERATED,
+ config.world_seed,
+ ):
+ _fail_dedicated_server("The configured world seed 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.set_network_simulation_only(true)
_player.visible = false
_player.collision_layer = 0
_player.collision_mask = 0
_player.set_physics_process(false)
+ if not _network_session.peer_count_changed.is_connected(
+ _on_dedicated_peer_count_changed
+ ):
+ _network_session.peer_count_changed.connect(
+ _on_dedicated_peer_count_changed
+ )
if not _network_session.start_dedicated_host(
config.port,
config.max_players,
@@ -323,22 +578,27 @@ func _start_dedicated_server() -> void:
_fail_dedicated_server("Could not start the dedicated server.")
return
_network_session.set_session_display_name(config.server_name)
- _discovery.set_room_name(config.server_name)
if not _network_session.set_host_open(true):
_fail_dedicated_server("Could not open the dedicated server.")
return
+ _on_dedicated_peer_count_changed(
+ _network_session.get_player_count(),
+ config.max_players,
+ )
+ _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)"
+ "straywild dedicated server ready: %s on %s:%d (%d players, %s, timezone %s)"
% [
config.server_name,
config.bind_address,
config.port,
config.max_players,
"public" if config.public_listing else "unlisted",
+ str(Time.get_time_zone_from_system().get("name", "local")),
]
)
print(
@@ -347,15 +607,34 @@ func _start_dedicated_server() -> void:
)
+func _on_dedicated_peer_count_changed(
+ player_count: int,
+ _max_players: int,
+) -> void:
+ if not _dedicated_runtime:
+ return
+ Engine.physics_ticks_per_second = (
+ DEDICATED_ACTIVE_PHYSICS_TICKS_PER_SECOND
+ if player_count > 0
+ else DEDICATED_IDLE_PHYSICS_TICKS_PER_SECOND
+ )
+
+
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
- )
+ if _dedicated_runtime:
+ _save_manager.configure_storage(
+ _data_root.path_for(&"player_save"), _data_root
+ )
+ elif not _save_slots.configure(_data_root, _save_manager):
+ push_error(
+ "Save-slot catalog setup failed: %s"
+ % _save_slots.get_error_message()
+ )
_network_profile.configure_storage(
_data_root.path_for(&"network_profile"), _data_root
)
@@ -392,7 +671,8 @@ func _initialize_application(dedicated: bool) -> void:
_player_spawn_service.setup(
_players_root,
_player,
- _test_world.get_player_spawn_transform()
+ _test_world.get_player_spawn_transform(),
+ dedicated,
)
_network_session.setup(
_network_profile,
@@ -405,7 +685,7 @@ func _initialize_application(dedicated: bool) -> void:
_host_bans,
dedicated,
)
- _discovery.setup(_network_session)
+ _discovery.setup(_network_session, _relationships)
if not dedicated:
_world_time_visuals.setup(
_world_time,
@@ -413,6 +693,8 @@ func _initialize_application(dedicated: bool) -> void:
_test_world.get_sun(),
_world_weather,
_player,
+ Callable(_player, "get_active_gameplay_camera"),
+ _performance_profile.is_light(),
)
if not _world_time.natural_time_advanced.is_connected(
_on_natural_time_advanced
@@ -478,16 +760,45 @@ func _initialize_application(dedicated: bool) -> void:
_player.wallet,
_asset_reservations
)
+ _player.cooler_capacity.bind_home_state(_home_state)
_player.bag.setup(item_catalog)
- _player.hotbar.setup(_player.bag, item_catalog, _player.inventory)
+ _player.inventory_layout.setup(
+ _player.bag,
+ _player.inventory,
+ item_catalog,
+ _player.cooler_capacity,
+ )
+ _player.bag.set_inventory_layout(_player.inventory_layout)
+ _player.inventory.set_inventory_layout(_player.inventory_layout)
+ _player.hotbar.setup(
+ _player.bag,
+ item_catalog,
+ _player.inventory,
+ _player.inventory_layout,
+ )
_shop_interaction = _test_world.get_fishing_shop()
+ _decor_shop_interaction = _test_world.get_decor_shop()
+ _rv_upgrade_shop_interaction = _test_world.get_rv_upgrade_shop()
+ _storage_interaction = _test_world.get_player_storage()
if not dedicated:
+ if not _home_state.changed.is_connected(_on_home_state_changed):
+ _home_state.changed.connect(_on_home_state_changed)
_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()
+ _refresh_shop_npc_speech_state()
+ _decor_shop_interaction.setup_local_player(_player)
+ _decor_shop_interaction.local_player_range_changed.connect(
+ _on_decor_shop_range_changed
+ )
+ _rv_upgrade_shop_interaction.setup_local_player(_player)
+ _rv_upgrade_shop_interaction.local_player_range_changed.connect(
+ _on_rv_upgrade_shop_range_changed
+ )
+ _storage_interaction.setup_local_player(_player)
+ _storage_interaction.local_player_range_changed.connect(
+ _on_storage_range_changed
)
_save_manager.setup(
_player.inventory,
@@ -496,6 +807,7 @@ func _initialize_application(dedicated: bool) -> void:
fish_catalog,
_player.bag,
_player.hotbar,
+ _player.inventory_layout,
item_catalog,
_player.fishing_upgrades,
_player.cooler_capacity,
@@ -504,6 +816,24 @@ func _initialize_application(dedicated: bool) -> void:
_world_time,
_world_weather,
_player_jobs,
+ _player,
+ _home_state,
+ )
+ _home_world.setup(
+ _test_world,
+ _player_spawn_service,
+ _network_session,
+ dedicated,
+ )
+ _network_home.setup(
+ _network_session,
+ _player_spawn_service,
+ _home_state,
+ _save_manager,
+ _home_world,
+ _player.bag,
+ _player.inventory_layout,
+ _player.hotbar,
)
_player_jobs.set_save_manager(_save_manager)
_save_manager.set_autosave_enabled(false)
@@ -532,6 +862,35 @@ func _initialize_application(dedicated: bool) -> void:
_save_manager,
_asset_reservations
)
+ _network_world_spawns.setup(
+ _network_session,
+ _player_spawn_service,
+ _test_world,
+ _world_gatherables_root,
+ gatherable_catalog,
+ fish_catalog,
+ _player.inventory,
+ _player.collection_log,
+ _player.cooler_capacity,
+ _player.experience,
+ _save_manager,
+ _network_item_use,
+ _world_time,
+ not dedicated,
+ )
+ _gathering_controller.setup(
+ _player,
+ _player.bag,
+ _player.hotbar,
+ _fishing_spot,
+ _network_world_spawns,
+ )
+ _network_world_spawns.local_capture_received.connect(
+ _fishing_spot.present_external_catch
+ )
+ _gathering_controller.status_changed.connect(
+ _fishing_spot.report_external_status
+ )
_network_fish_showcase.setup(
_network_session,
_player_spawn_service,
@@ -546,7 +905,9 @@ func _initialize_application(dedicated: bool) -> void:
_player,
_surface_drawings_root,
_player.bag,
+ _player.hotbar,
_player.art_unlocks,
+ _data_root,
)
_network_player_list.set_surface_drawing_service(
_network_surface_drawing
@@ -564,7 +925,8 @@ func _initialize_application(dedicated: bool) -> void:
_save_manager,
item_catalog,
fish_catalog,
- _network_item_use
+ _network_item_use,
+ not dedicated,
)
var sale_buyers: Array[FishBuyerProfileType] = [
pelican_buyer_profile,
@@ -576,14 +938,21 @@ func _initialize_application(dedicated: bool) -> void:
_network_fishing,
_shop_interaction,
_player.inventory,
+ _player.bag,
+ item_catalog,
_player.wallet,
_player.fish_sale_service,
_save_manager,
fish_catalog,
sale_buyers,
- _asset_reservations
+ _asset_reservations,
+ _player.inventory_layout,
+ )
+ _player_jobs.bind_authoritative_services(
+ _network_fishing,
+ _network_sale,
+ _network_world_spawns,
)
- _player_jobs.bind_authoritative_services(_network_fishing, _network_sale)
_network_shop.setup(
_network_session,
_player_spawn_service,
@@ -596,7 +965,12 @@ func _initialize_application(dedicated: bool) -> void:
_player.cooler_capacity,
_player.art_unlocks,
_save_manager,
- _asset_reservations
+ _asset_reservations,
+ _player.inventory_layout,
+ _home_state,
+ _network_home,
+ _decor_shop_interaction,
+ _rv_upgrade_shop_interaction,
)
_fishing_spot.setup(
_player,
@@ -614,6 +988,7 @@ func _initialize_application(dedicated: bool) -> void:
_network_fishing,
_world_time,
_world_weather,
+ Callable(_test_world, "get_fishable_water_regions"),
)
if dedicated:
return
@@ -629,15 +1004,20 @@ func _initialize_application(dedicated: bool) -> void:
_fishing_spot,
_player.bag,
_player.hotbar,
+ _player.inventory_layout,
item_catalog,
main_shop_buyer_profile,
_player.fishing_upgrades,
_shop_interaction,
+ _storage_interaction,
+ _decor_shop_interaction,
+ _rv_upgrade_shop_interaction,
_player.item_effects,
_player.cooler_capacity,
_network_session,
_network_sale,
_network_shop,
+ _home_state,
_network_chat,
_network_profile,
_player_spawn_service,
@@ -655,6 +1035,27 @@ func _initialize_application(dedicated: bool) -> void:
_test_world.get_world_environment(),
_test_world.get_sun(),
)
+ _home_decor_controller.setup(
+ _player,
+ _player.hotbar,
+ _player.bag,
+ _home_state,
+ _network_home,
+ _home_world,
+ _fishing_spot,
+ _game_ui.get_home_decor_toolbar(),
+ )
+ _game_ui.get_player_storage().decorate_requested.connect(
+ _home_decor_controller.begin_decorating
+ )
+ _network_home.local_space_changed.connect(_on_local_home_space_changed)
+ _network_home.local_transition_finished.connect(
+ _on_local_home_transition_finished
+ )
+ _game_ui.get_screen_fade().transition_completed.connect(
+ _on_home_exit_fade_transition_completed
+ )
+ _game_ui.rv_storage_requested.connect(_on_rv_storage_requested)
_game_ui.setup_data_and_identity(
_data_root,
_identity_backups,
@@ -663,9 +1064,23 @@ func _initialize_application(dedicated: bool) -> void:
_network_session,
_interface_fonts,
)
+ _game_ui.data_root_changed.connect(_on_data_root_changed)
_game_ui.setup_controller_mapping(_controller_mapping_manager)
+ _ui_pixelation.setup_controller_mapping(_controller_mapping_manager)
+ _game_ui.setup_keyboard_mouse_mapping(_keyboard_mouse_mapping_manager)
+ _network_player_list.friend_request_received.connect(
+ _on_friend_request_received
+ )
+ _discovery.friend_invite_received.connect(_on_friend_invite_received)
+ _discovery.public_join_prepared.connect(
+ _on_social_public_join_prepared
+ )
+ _discovery.public_join_status_changed.connect(
+ _on_social_public_join_status_changed
+ )
+ _game_ui.social_prompt_accepted.connect(_on_social_prompt_accepted)
+ _game_ui.social_prompt_declined.connect(_on_social_prompt_declined)
_data_root.conflict_detected.connect(_on_portable_conflict)
- _data_root.status_changed.connect(_on_data_root_status)
if (
PortableFileGuard.has_syncthing_conflict(
_data_root.root_path.path_join("player")
@@ -684,7 +1099,8 @@ func _initialize_application(dedicated: bool) -> void:
_game_ui,
_game_ui.get_screen_fade(),
_test_world.get_player_water_triggers(),
- _test_world.get_safe_respawn_points()
+ _test_world.get_safe_respawn_points(),
+ Callable(_test_world, "get_water_recovery_position"),
)
_ui_pixelation.effective_pixel_size_changed.connect(
_on_effective_ui_pixel_size_changed
@@ -714,16 +1130,19 @@ func _initialize_application(dedicated: bool) -> void:
_apply_runtime_settings(_settings_manager.current_settings)
_set_gameplay_active(false)
var title_screen: TitleScreenType = _game_ui.get_title_screen()
- title_screen.new_game_requested.connect(_on_new_game_requested)
- title_screen.continue_game_requested.connect(_on_continue_game_requested)
+ title_screen.slot_play_requested.connect(_on_slot_play_requested)
+ title_screen.new_slot_requested.connect(_on_new_slot_requested)
title_screen.quit_requested.connect(_on_quit_requested)
title_screen.setup(
_save_manager,
+ _save_slots,
_settings_manager,
_network_session,
_saved_servers,
_server_trust,
_discovery,
+ _data_root,
+ _interface_fonts,
)
var pause_menu: PauseMenuType = _game_ui.get_pause_menu()
pause_menu.setup(
@@ -771,7 +1190,7 @@ func _initialize_application(dedicated: bool) -> void:
func _show_data_root_setup() -> void:
if _data_setup_dialog == null:
_data_setup_dialog = ConfirmationDialog.new()
- _data_setup_dialog.title = "NETfishing player data"
+ _data_setup_dialog.title = "straywild player data"
_data_setup_dialog.ok_button_text = "Use This Folder"
_data_setup_dialog.cancel_button_text = "Quit"
_data_setup_dialog.confirmed.connect(_use_default_data_root)
@@ -788,14 +1207,14 @@ func _show_data_root_setup() -> void:
var default_path: String = _data_root.default_visible_path()
var legacy: bool = PortableDataMigration.legacy_files_present()
_data_setup_dialog.dialog_text = (
- ("Move your existing NETfishing data to an easy-to-find folder."
- if legacy else "Choose where NETfishing stores your player data.")
+ ("Move your existing straywild data to an easy-to-find folder."
+ if legacy else "Choose where straywild stores your player data.")
+ "\n\n%s\n\nYou can choose a Syncthing folder."
% (default_path if not default_path.is_empty() else "Choose a folder")
)
_data_setup_dialog.ok_button_text = (
(
- "Move to Documents/NETFISHING"
+ "Move to Documents/straywild"
if legacy else "Use This Folder"
)
if not default_path.is_empty()
@@ -804,7 +1223,7 @@ func _show_data_root_setup() -> void:
_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"
+ "Your straywild data folder is unavailable.\n\n%s"
% _data_root.error_message
)
_data_setup_dialog.popup_centered(Vector2i(640, 360))
@@ -823,13 +1242,13 @@ func _focus_data_setup_actions() -> void:
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()]
+ var previous: Button = actions[maxi(index - 1, 0)]
+ var next: Button = actions[mini(index + 1, actions.size() - 1)]
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
+ action.focus_neighbor_top = action.get_path_to(action)
+ action.focus_neighbor_bottom = action.get_path_to(action)
_data_setup_dialog.get_ok_button().grab_focus()
@@ -940,11 +1359,11 @@ func _activate_selected_data_path(path: String, app_data: bool = false) -> void:
func _show_existing_root_choice(path: String) -> void:
_pending_existing_root_path = path
var dialog: ConfirmationDialog = ConfirmationDialog.new()
- dialog.title = "Existing NETfishing data"
+ dialog.title = "Existing straywild data"
dialog.ok_button_text = "Use Data Already in Selected Folder"
dialog.cancel_button_text = "Cancel"
dialog.dialog_text = (
- "This folder already contains NETfishing player data.\n\n"
+ "This folder already contains straywild player data.\n\n"
+ "Choose which complete data set to use. Data is never merged automatically."
)
dialog.add_button(
@@ -976,12 +1395,16 @@ func _show_existing_root_choice(path: String) -> void:
_interface_fonts.apply_utility_theme(dialog)
add_child(dialog)
dialog.popup_centered(Vector2i(680, 360))
+ _configure_popup_dialog.call_deferred(
+ dialog, dialog.get_cancel_button()
+ )
func _show_data_error(message: String) -> void:
if _data_setup_dialog != null:
_data_setup_dialog.dialog_text = message
_data_setup_dialog.popup_centered(Vector2i(640, 300))
+ _focus_data_setup_actions.call_deferred()
func _on_portable_conflict(message: String, _path: String) -> void:
@@ -991,15 +1414,12 @@ func _on_portable_conflict(message: String, _path: String) -> void:
dialog.dialog_text = (
message
+ "\n\nDo not play the same profile on two devices at the same time."
- )
- dialog.add_button("Open Data Folder", false, "open")
- dialog.custom_action.connect(func(action: StringName) -> void:
- if action == &"open":
- _data_root.open_folder()
+ + "\n\nThe data folder can be opened from Settings > Data."
)
dialog.confirmed.connect(dialog.queue_free)
_game_ui.add_child(dialog)
dialog.popup_centered(Vector2i(560, 300))
+ _configure_popup_dialog.call_deferred(dialog, dialog.get_ok_button())
func _on_data_root_status(message: String) -> void:
@@ -1008,15 +1428,25 @@ func _on_data_root_status(message: String) -> void:
var dialog: AcceptDialog = AcceptDialog.new()
_interface_fonts.apply_utility_theme(dialog)
dialog.title = "Synced data needs review"
- dialog.dialog_text = message
- dialog.add_button("Open Data Folder", false, "open")
- dialog.custom_action.connect(func(action: StringName) -> void:
- if action == &"open":
- _data_root.open_folder()
+ dialog.dialog_text = (
+ message + "\n\nThe data folder can be opened from Settings > Data."
)
dialog.confirmed.connect(dialog.queue_free)
_game_ui.add_child(dialog)
dialog.popup_centered(Vector2i(560, 260))
+ _configure_popup_dialog.call_deferred(dialog, dialog.get_ok_button())
+
+
+func _on_data_root_changed() -> void:
+ _save_manager.set_autosave_enabled(false)
+ _save_manager.cancel_pending_autosave()
+ _configure_portable_stores()
+ if not _network_profile_service.reload_persisted_profile():
+ push_warning("The selected data folder's player profile could not be loaded.")
+ _network_session.set_local_appearance_snapshot(
+ _appearance_store.get_snapshot()
+ )
+ _game_ui.get_title_screen().refresh_after_data_root_change()
func _on_server_trust_required(
@@ -1057,6 +1487,10 @@ func _on_server_trust_required(
)
_server_trust_dialog.ok_button_text = "Trust & Connect"
_server_trust_dialog.popup_centered(Vector2i(560, 360))
+ _configure_popup_dialog.call_deferred(
+ _server_trust_dialog,
+ _server_trust_dialog.get_cancel_button(),
+ )
func _confirm_server_trust() -> void:
@@ -1068,7 +1502,13 @@ func _confirm_server_trust() -> void:
)
_server_trust_dialog.ok_button_text = "Replace Pin"
_server_trust_dialog.popup_centered(Vector2i(520, 260))
+ _configure_popup_dialog.call_deferred(
+ _server_trust_dialog,
+ _server_trust_dialog.get_cancel_button(),
+ )
return
+ if _server_trust_dialog != null:
+ _server_trust_dialog.hide()
_network_session.resolve_server_trust(true)
@@ -1084,13 +1524,31 @@ func _on_peer_identity_observed(_peer_id: int, status: String) -> void:
+ "This may be a different player using the same display name."
)
_identity_notice_dialog.popup_centered(Vector2i(480, 240))
+ _configure_popup_dialog.call_deferred(
+ _identity_notice_dialog,
+ _identity_notice_dialog.get_ok_button(),
+ )
+
+
+func _configure_popup_dialog(
+ dialog: Window,
+ preferred_control: Control = null,
+) -> void:
+ if dialog != null and is_instance_valid(dialog) and dialog.visible:
+ FileDialogControllerNavigation.configure_scope(
+ dialog, preferred_control
+ )
func _input(event: InputEvent) -> void:
+ # Do not let controller aliases close or manipulate the world/menu behind
+ # the modal on-screen keyboard. The keyboard consumes the event itself.
+ if _game_ui.is_controller_text_entry_open():
+ return
if _handle_data_root_controller_input(event):
get_viewport().set_input_as_handled()
return
- if _game_ui.is_controller_mapping_capturing():
+ if _game_ui.is_input_mapping_capturing():
return
if (
not (
@@ -1106,6 +1564,15 @@ func _input(event: InputEvent) -> void:
return
var pause_menu: PauseMenuType = _game_ui.get_pause_menu()
var fishing_shop: FishingShopType = _game_ui.get_fishing_shop()
+ if _game_ui.get_player_storage().consume_escape():
+ get_viewport().set_input_as_handled()
+ return
+ if _game_ui.get_decor_shop().consume_escape():
+ get_viewport().set_input_as_handled()
+ return
+ if _game_ui.get_rv_upgrade_shop().consume_escape():
+ get_viewport().set_input_as_handled()
+ return
if fishing_shop.consume_escape():
get_viewport().set_input_as_handled()
return
@@ -1129,6 +1596,8 @@ func _input(event: InputEvent) -> void:
func _handle_data_root_controller_input(event: InputEvent) -> bool:
+ if _game_ui.is_controller_text_entry_open():
+ return false
var button_event := event as InputEventJoypadButton
if button_event == null or not button_event.pressed:
return false
@@ -1140,6 +1609,11 @@ func _handle_data_root_controller_input(event: InputEvent) -> bool:
)
if not setup_visible and not picker_visible:
return false
+ var picker_scope: Window = (
+ FileDialogControllerNavigation.active_scope(_data_folder_dialog)
+ if picker_visible
+ else null
+ )
var use_mapping: bool = _controller_mapping_manager != null
var accept_pressed: bool = (
_controller_mapping_manager.event_matches_role(
@@ -1165,24 +1639,27 @@ func _handle_data_root_controller_input(event: InputEvent) -> bool:
)
if cancel_pressed:
if picker_visible:
- _on_data_folder_picker_canceled()
+ if picker_scope != null and picker_scope != _data_folder_dialog:
+ picker_scope.hide()
+ else:
+ _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()
+ picker_scope.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()
+ if (
+ (focused is LineEdit or focused is TextEdit)
+ and _game_ui.request_controller_text_entry_for(focused)
+ ):
return true
+ # Buttons are activated once through Godot's native ui_accept path. This
+ # handler only owns modal back behavior and controller text entry.
return false
@@ -1194,12 +1671,56 @@ func _is_pause_open_request(event: InputEvent) -> bool:
func _unhandled_input(event: InputEvent) -> void:
+ if (
+ _gameplay_started
+ and event.is_action_pressed("fish_primary")
+ and not (event is InputEventKey and event.echo)
+ and _fishing_spot.try_use_active_non_fishing_item()
+ ):
+ get_viewport().set_input_as_handled()
+ return
if (
not _gameplay_started
or not event.is_action_pressed("interact")
or (event is InputEventKey and event.echo)
):
return
+ var home_owner: String = _network_home.get_local_home_owner_fingerprint()
+ if not home_owner.is_empty():
+ if (
+ _network_home.can_local_decorate()
+ and _fishing_spot.can_open_fishing_shop()
+ and _game_ui.get_player_storage().open_storage_from_home(true)
+ ):
+ _refresh_home_door_prompt({})
+ get_viewport().set_input_as_handled()
+ return
+ var nearby_home_owner: String = _home_world.find_nearby_exterior_owner(
+ _player
+ )
+ if not nearby_home_owner.is_empty():
+ if _network_home.request_enter(nearby_home_owner):
+ _refresh_home_door_prompt({})
+ get_viewport().set_input_as_handled()
+ return
+ if (
+ _rv_upgrade_shop_interaction != null
+ and _rv_upgrade_shop_interaction.is_local_player_in_range()
+ and _fishing_spot.can_open_fishing_shop()
+ and _game_ui.get_rv_upgrade_shop().open_shop()
+ ):
+ _game_ui.set_shop_prompt_visible(false)
+ get_viewport().set_input_as_handled()
+ return
+ if (
+ _decor_shop_interaction != null
+ and _decor_shop_interaction.is_local_player_in_range()
+ and _fishing_spot.can_open_fishing_shop()
+ and _game_ui.get_decor_shop().open_shop()
+ ):
+ _game_ui.set_shop_prompt_visible(false)
+ get_viewport().set_input_as_handled()
+ return
if (
_shop_interaction != null
and _shop_interaction.is_local_player_in_range()
@@ -1208,35 +1729,60 @@ 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:
+func _process(delta: float) -> void:
if _dedicated_runtime:
+ _dedicated_metrics_accumulator += delta
+ if _dedicated_metrics_accumulator >= 60.0:
+ _dedicated_metrics_accumulator = fmod(
+ _dedicated_metrics_accumulator,
+ 60.0,
+ )
+ _print_dedicated_metrics()
return
- var show_shop_prompt := _can_show_shop_prompt()
+ _maybe_begin_home_exit_transition()
+ var show_rv_upgrade_prompt := _can_show_rv_upgrade_shop_prompt()
+ var show_decor_shop_prompt := _can_show_decor_shop_prompt()
+ var show_shop_prompt := (
+ show_rv_upgrade_prompt
+ or show_decor_shop_prompt
+ or _can_show_shop_prompt()
+ )
var shop_prompt_anchor := (
- _shop_interaction.get_prompt_anchor_position()
- if _shop_interaction != null
- else Vector3.ZERO
+ _rv_upgrade_shop_interaction.get_prompt_anchor_position()
+ if show_rv_upgrade_prompt
+ else (
+ _decor_shop_interaction.get_prompt_anchor_position()
+ if show_decor_shop_prompt
+ else (
+ _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,
)
+ var home_prompt := _get_home_prompt()
+ _refresh_home_door_prompt(home_prompt)
+ _game_ui.set_rv_storage_button_visible(
+ _can_show_rv_storage_button()
+ )
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
+ var current_window_mode: DisplayServer.WindowMode = (
+ DisplayServer.window_get_mode()
)
- if DisplayServer.window_get_mode() != requested_window_mode:
+ var requested_window_mode: DisplayServer.WindowMode = resolve_window_mode(
+ settings.fullscreen_enabled,
+ current_window_mode,
+ )
+ if current_window_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)
@@ -1249,7 +1795,8 @@ func _apply_runtime_settings(settings: PlayerSettingsType) -> void:
_player.apply_camera_settings(
settings.mouse_camera_sensitivity,
settings.controller_camera_sensitivity,
- settings.invert_camera_y
+ settings.invert_camera_y,
+ settings.swap_hotbar_camera_scroll,
)
_fishing_spot.configure_accessibility_auto_click(
settings.auto_click_enabled,
@@ -1262,13 +1809,30 @@ func _apply_runtime_settings(settings: PlayerSettingsType) -> void:
)
+static func resolve_window_mode(
+ fullscreen_enabled: bool,
+ current_mode: DisplayServer.WindowMode,
+) -> DisplayServer.WindowMode:
+ var is_fullscreen: bool = current_mode in [
+ DisplayServer.WINDOW_MODE_FULLSCREEN,
+ DisplayServer.WINDOW_MODE_EXCLUSIVE_FULLSCREEN,
+ ]
+ if fullscreen_enabled:
+ return (
+ current_mode
+ if is_fullscreen
+ else DisplayServer.WINDOW_MODE_FULLSCREEN
+ )
+ if is_fullscreen:
+ return DisplayServer.WINDOW_MODE_WINDOWED
+ return current_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 = (
- 0.75
- if OS.get_environment("NETFISHING_LOW_END") == "1"
- else 1.0
+ _performance_profile.get_world_render_scale()
)
root_viewport.msaa_3d = Viewport.MSAA_DISABLED
root_viewport.screen_space_aa = Viewport.SCREEN_SPACE_AA_DISABLED
@@ -1313,6 +1877,7 @@ func _set_gameplay_active(active: bool) -> void:
_player.set_camera_input_enabled(active)
_player.set_camera_active(active)
_fishing_spot.set_gameplay_input_enabled(active)
+ _gathering_controller.set_gameplay_input_enabled(active)
_water_recovery.set_recovery_enabled(active)
_game_ui.set_gameplay_ui_enabled(active)
_save_manager.set_autosave_enabled(active)
@@ -1348,7 +1913,11 @@ func _on_natural_time_advanced(advanced_hours: float) -> void:
)
):
_dusk_music_played_for_natural_day = true
- _dusk_music.play(0.0)
+ if (
+ _dusk_music.stream != null
+ and not _new_game_music.playing
+ ):
+ _dusk_music.play(0.0)
static func _natural_interval_crosses_hour(
@@ -1448,16 +2017,34 @@ func _resize_native_overlays() -> void:
_shop_backdrop.size = Vector2(get_window().size)
-func _on_new_game_requested() -> void:
+func _on_new_game_requested(
+ world_layout: StringName,
+ world_seed: int,
+) -> void:
if _gameplay_started or _quit_in_progress:
return
+ _start_new_game_music()
+ if not _apply_world(world_layout, world_seed, true):
+ _show_title_music(true)
+ _game_ui.get_title_screen().report_network_error(
+ "Could not prepare that world. Try another seed or the starter island."
+ )
+ return
+ if not _prepare_host_world(world_layout, world_seed):
+ _show_title_music(true)
+ _game_ui.get_title_screen().report_network_error(
+ "Could not prepare that world for hosting."
+ )
+ return
if not _prepare_private_host():
+ _show_title_music(true)
return
if (
not _save_manager.delete_progression_save()
- or not _save_manager.initialize_new_game()
+ or not _save_manager.initialize_new_game(world_seed, world_layout)
):
_network_session.disconnect_session("New Game setup failed.")
+ _show_title_music(true)
_game_ui.get_title_screen().report_network_error(
"Could not initialize local progression. Existing data was preserved where possible."
)
@@ -1468,14 +2055,114 @@ func _on_new_game_requested() -> void:
func _on_continue_game_requested() -> void:
if _gameplay_started or _quit_in_progress:
return
- if not _prepare_private_host():
- return
if not _save_manager.load_player_data():
- _network_session.disconnect_session("Continue failed.")
_game_ui.get_title_screen().report_network_error(
"Failed to load save. The original was preserved."
)
return
+ var world_layout: StringName = _save_manager.get_world_layout()
+ var world_seed: int = _save_manager.get_world_seed()
+ if (
+ not _apply_world(world_layout, world_seed, true)
+ or not _prepare_host_world(world_layout, world_seed)
+ ):
+ _game_ui.get_title_screen().report_network_error(
+ "The saved world could not be loaded. The save was preserved."
+ )
+ return
+ if not _prepare_private_host():
+ return
+ _enter_gameplay()
+
+
+func _on_slot_play_requested(slot_id: String) -> void:
+ if _gameplay_started or _quit_in_progress:
+ return
+ if not _save_slots.activate_slot(slot_id):
+ _game_ui.get_title_screen().report_network_error(
+ "The selected save slot could not be opened."
+ )
+ return
+ if not _save_manager.load_player_data():
+ _game_ui.get_title_screen().report_network_error(
+ "Failed to load that save slot. The original was preserved."
+ )
+ return
+ var world_layout: StringName = _save_manager.get_world_layout()
+ var world_seed: int = _save_manager.get_world_seed()
+ if (
+ not _apply_world(world_layout, world_seed, true)
+ or not _prepare_host_world(world_layout, world_seed)
+ ):
+ _game_ui.get_title_screen().report_network_error(
+ "The saved world could not be loaded. The save was preserved."
+ )
+ return
+ if not _prepare_private_host():
+ return
+ _save_slots.mark_played(slot_id)
+ _enter_gameplay()
+
+
+func _on_new_slot_requested(
+ display_name: String,
+ world_layout: StringName,
+ world_seed: int,
+ duplicate_source_slot_id: String,
+) -> void:
+ if _gameplay_started or _quit_in_progress:
+ return
+ var created: Dictionary = (
+ _save_slots.duplicate_slot(
+ duplicate_source_slot_id,
+ display_name,
+ world_layout,
+ world_seed,
+ )
+ if not duplicate_source_slot_id.is_empty()
+ else _save_slots.create_empty_slot(display_name)
+ )
+ if not bool(created.get("ok", false)):
+ _game_ui.get_title_screen().report_network_error(
+ str(created.get("message", "The save slot could not be created."))
+ )
+ return
+ var slot_id: String = str(created.get("slot_id", ""))
+ if not _save_slots.activate_slot(slot_id):
+ _game_ui.get_title_screen().report_network_error(
+ "The new save slot could not be selected."
+ )
+ return
+ if not duplicate_source_slot_id.is_empty():
+ _on_slot_play_requested(slot_id)
+ return
+ _start_new_game_music()
+ if not _apply_world(world_layout, world_seed, true):
+ _show_title_music(true)
+ _game_ui.get_title_screen().report_network_error(
+ "Could not prepare that world. Try another seed or the starter island."
+ )
+ return
+ if not _prepare_host_world(world_layout, world_seed):
+ _show_title_music(true)
+ _game_ui.get_title_screen().report_network_error(
+ "Could not prepare that world for hosting."
+ )
+ return
+ if not _prepare_private_host():
+ _show_title_music(true)
+ return
+ if (
+ not _save_manager.delete_progression_save()
+ or not _save_manager.initialize_new_game(world_seed, world_layout)
+ ):
+ _network_session.disconnect_session("New Game setup failed.")
+ _show_title_music(true)
+ _game_ui.get_title_screen().report_network_error(
+ "Could not initialize local progression."
+ )
+ return
+ _save_slots.mark_played(slot_id)
_enter_gameplay()
@@ -1498,6 +2185,18 @@ func _prepare_private_host() -> bool:
return true
+func _prepare_host_world(
+ world_layout: StringName,
+ world_seed: int,
+) -> bool:
+ if _network_session.state in [
+ NetworkSessionType.State.CONNECTION_FAILED,
+ NetworkSessionType.State.SERVER_LOST,
+ ]:
+ _network_session.reset_failure()
+ return _network_session.set_host_world(world_layout, world_seed)
+
+
func _enter_gameplay() -> void:
_fade_out_title_music()
_game_ui.get_title_screen().hide()
@@ -1523,6 +2222,12 @@ func _on_return_to_title_requested() -> void:
func _on_title_join_game_requested(endpoint: String) -> void:
if _quit_in_progress or _gameplay_started:
return
+ var active_slot: Dictionary = _save_slots.ensure_active_slot()
+ if not bool(active_slot.get("ok", false)):
+ _game_ui.get_title_screen().report_network_error(
+ str(active_slot.get("message", "Local progression is unavailable."))
+ )
+ return
if _network_session.state != NetworkSessionType.State.INACTIVE:
_network_session.disconnect_session("Preparing direct connection.")
_join_requested_from_title = true
@@ -1544,13 +2249,147 @@ 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."
)
+func _on_friend_request_received(
+ request_id: String,
+ fingerprint: String,
+ display_name: String,
+) -> void:
+ _queue_social_prompt({
+ "kind": "friend_request",
+ "request_id": request_id,
+ "fingerprint": fingerprint,
+ "display_name": display_name,
+ })
+
+
+func _on_friend_invite_received(
+ fingerprint: String,
+ display_name: String,
+ room: Dictionary,
+) -> void:
+ if (
+ not _relationships.is_friend(fingerprint)
+ or _relationships.is_blocked(fingerprint)
+ ):
+ return
+ _queue_social_prompt({
+ "kind": "friend_invite",
+ "fingerprint": fingerprint,
+ "display_name": display_name,
+ "room": room.duplicate(true),
+ })
+
+
+func _queue_social_prompt(details: Dictionary) -> void:
+ _social_prompt_queue.append(details.duplicate(true))
+ _show_next_social_prompt()
+
+
+func _show_next_social_prompt() -> void:
+ if (
+ not _active_social_prompt.is_empty()
+ or _game_ui.is_social_prompt_open()
+ or _social_prompt_queue.is_empty()
+ ):
+ return
+ _active_social_prompt = _social_prompt_queue.pop_front()
+ var kind := str(_active_social_prompt.get("kind", ""))
+ var display_name := str(
+ _active_social_prompt.get("display_name", "Player")
+ )
+ var shown: bool = false
+ if kind == "friend_request":
+ shown = _game_ui.show_social_prompt(
+ "%s wants to add you as a friend.\n" % display_name
+ + "Friendships are saved on this device.",
+ "accept",
+ "decline",
+ )
+ elif kind == "friend_invite":
+ var room: Dictionary = _active_social_prompt.get("room", {})
+ var room_name := str(room.get("room_name", "their room"))
+ shown = _game_ui.show_social_prompt(
+ "%s invited you to join %s." % [display_name, room_name],
+ "join",
+ "not now",
+ )
+ if not shown:
+ _active_social_prompt.clear()
+ call_deferred("_show_next_social_prompt")
+
+
+func _on_social_prompt_accepted() -> void:
+ var details: Dictionary = _active_social_prompt.duplicate(true)
+ _active_social_prompt.clear()
+ match str(details.get("kind", "")):
+ "friend_request":
+ _network_player_list.respond_friend_request(
+ str(details.get("request_id", "")), true
+ )
+ "friend_invite":
+ var room: Dictionary = details.get("room", {})
+ _pending_social_invite_join = true
+ _pending_social_invite_gameplay = _gameplay_started
+ if room.is_empty() or not _discovery.prepare_public_join(room):
+ _pending_social_invite_join = false
+ _report_social_join_error(
+ "This person needs to be online to do this."
+ )
+ call_deferred("_show_next_social_prompt")
+
+
+func _on_social_prompt_declined() -> void:
+ var details: Dictionary = _active_social_prompt.duplicate(true)
+ _active_social_prompt.clear()
+ if str(details.get("kind", "")) == "friend_request":
+ _network_player_list.respond_friend_request(
+ str(details.get("request_id", "")), false
+ )
+ call_deferred("_show_next_social_prompt")
+
+
+func _on_social_public_join_prepared(endpoint: String) -> void:
+ if not _pending_social_invite_join:
+ return
+ var from_gameplay := _pending_social_invite_gameplay
+ _pending_social_invite_join = false
+ _pending_social_invite_gameplay = false
+ if from_gameplay:
+ _on_pause_join_game_requested(endpoint)
+ else:
+ _on_title_join_game_requested(endpoint)
+
+
+func _on_social_public_join_status_changed(
+ message: String,
+ is_error: bool,
+) -> void:
+ if not _pending_social_invite_join or not is_error:
+ return
+ _pending_social_invite_join = false
+ _pending_social_invite_gameplay = false
+ _report_social_join_error(message)
+
+
+func _report_social_join_error(message: String) -> void:
+ if _gameplay_started:
+ _game_ui.get_pause_menu().report_network_error(message)
+ else:
+ _game_ui.get_title_screen().report_network_error(message)
+
+
func _on_network_join_authenticated() -> void:
var connected_endpoint: ConnectionEndpoint = (
_network_session.get_current_endpoint()
@@ -1582,10 +2421,15 @@ func _on_network_join_authenticated() -> void:
)
_join_requested_from_title = false
return
+ _save_slots.mark_played(_save_slots.get_active_slot_id())
+ if not _apply_joined_world(server_metadata):
+ return
_fade_out_title_music()
_game_ui.get_title_screen().hide()
_set_gameplay_active(true)
elif _join_requested_from_pause:
+ if not _apply_joined_world(server_metadata):
+ return
_set_gameplay_active(true)
_join_requested_from_title = false
_join_requested_from_pause = false
@@ -1630,14 +2474,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)
@@ -1663,17 +2509,9 @@ func _on_remote_recovery_requested(
peer_id,
"Fishing attempt ended."
)
- var target_position: Vector3 = _test_world.get_player_spawn_transform().origin
- var nearest_distance: float = INF
- for point: SafeRespawnPoint in _test_world.get_safe_respawn_points():
- if point == null or not point.enabled:
- continue
- var distance: float = point.get_horizontal_distance_squared(
- entry_position
- )
- if distance < nearest_distance:
- nearest_distance = distance
- target_position = point.global_position
+ var target_position := _test_world.get_water_recovery_position(
+ entry_position
+ )
target_position.y += _water_recovery.respawn_height_offset
avatar.global_position = target_position
avatar.velocity = Vector3.ZERO
@@ -1687,7 +2525,15 @@ func _on_reset_progress_requested() -> void:
if not _save_manager.delete_progression_save():
pause_menu.report_reset_failure()
return
- if not _save_manager.initialize_new_game():
+ var world_seed: int = PlayerSaveManager.roll_world_seed()
+ if not _save_manager.initialize_new_game(
+ world_seed,
+ WorldLayoutType.GENERATED,
+ ):
+ pause_menu.report_reset_failure()
+ return
+ _network_session.disconnect_session("Progress reset.")
+ if not _apply_world(WorldLayoutType.GENERATED, world_seed, true):
pause_menu.report_reset_failure()
return
pause_menu.close_for_title_transition()
@@ -1696,6 +2542,148 @@ func _on_reset_progress_requested() -> void:
_show_title_music(true)
+func _apply_joined_world(server_metadata: Dictionary) -> bool:
+ var world_layout: StringName = WorldLayoutType.normalized(
+ server_metadata.get(
+ "world_layout",
+ String(WorldLayoutType.GENERATED),
+ ),
+ &"",
+ )
+ var world_seed: int = int(server_metadata.get("world_seed", 0))
+ if (
+ not world_layout.is_empty()
+ and _apply_world(world_layout, world_seed, true)
+ ):
+ return true
+ _network_session.disconnect_session("World generation failed.")
+ _join_requested_from_title = false
+ _join_requested_from_pause = false
+ _set_gameplay_active(false)
+ var title_screen: TitleScreenType = _game_ui.get_title_screen()
+ title_screen.reopen_to_menu()
+ title_screen.report_network_error(
+ "The host's world could not be built locally."
+ )
+ _show_title_music(true)
+ return false
+
+
+func _apply_world(
+ world_layout: StringName,
+ world_seed: int,
+ reposition_player: bool,
+) -> bool:
+ if not _test_world.activate_world(world_layout, world_seed):
+ return false
+ var spawn_transform: Transform3D = _test_world.get_player_spawn_transform()
+ _player_spawn_service.set_spawn_transform(spawn_transform)
+ if reposition_player:
+ _player.global_transform = spawn_transform
+ _player.velocity = Vector3.ZERO
+ if _application_initialized:
+ _refresh_active_world_bindings()
+ _network_world_spawns.refresh_world_context()
+ _network_home.refresh_world_presentations()
+ if _application_initialized and not _dedicated_runtime:
+ _water_recovery.update_world_context(
+ spawn_transform,
+ _test_world.get_player_water_triggers(),
+ _test_world.get_safe_respawn_points(),
+ Callable(_test_world, "get_water_recovery_position"),
+ )
+ _shoreline_ambience.configure(
+ _player,
+ _test_world.get_saltwater_shoreline_mesh(),
+ )
+ return true
+
+
+func _refresh_active_world_bindings() -> void:
+ if is_instance_valid(_shop_interaction):
+ if _shop_interaction.local_player_range_changed.is_connected(
+ _on_shop_range_changed
+ ):
+ _shop_interaction.local_player_range_changed.disconnect(
+ _on_shop_range_changed
+ )
+ if is_instance_valid(_storage_interaction):
+ if _storage_interaction.local_player_range_changed.is_connected(
+ _on_storage_range_changed
+ ):
+ _storage_interaction.local_player_range_changed.disconnect(
+ _on_storage_range_changed
+ )
+ if is_instance_valid(_decor_shop_interaction):
+ if _decor_shop_interaction.local_player_range_changed.is_connected(
+ _on_decor_shop_range_changed
+ ):
+ _decor_shop_interaction.local_player_range_changed.disconnect(
+ _on_decor_shop_range_changed
+ )
+ if is_instance_valid(_rv_upgrade_shop_interaction):
+ if _rv_upgrade_shop_interaction.local_player_range_changed.is_connected(
+ _on_rv_upgrade_shop_range_changed
+ ):
+ _rv_upgrade_shop_interaction.local_player_range_changed.disconnect(
+ _on_rv_upgrade_shop_range_changed
+ )
+ _shop_interaction = _test_world.get_fishing_shop()
+ _decor_shop_interaction = _test_world.get_decor_shop()
+ _rv_upgrade_shop_interaction = _test_world.get_rv_upgrade_shop()
+ _storage_interaction = _test_world.get_player_storage()
+ _network_sale.set_shop_interaction(_shop_interaction)
+ _network_shop.set_shop_interaction(_shop_interaction)
+ _network_shop.set_decor_shop_interaction(_decor_shop_interaction)
+ _network_shop.set_rv_upgrade_shop_interaction(
+ _rv_upgrade_shop_interaction
+ )
+ if _dedicated_runtime:
+ return
+ if _shop_interaction != null:
+ _shop_interaction.setup_local_player(_player)
+ _shop_interaction.local_player_range_changed.connect(
+ _on_shop_range_changed
+ )
+ if _storage_interaction != null:
+ _storage_interaction.setup_local_player(_player)
+ _storage_interaction.local_player_range_changed.connect(
+ _on_storage_range_changed
+ )
+ if _decor_shop_interaction != null:
+ _decor_shop_interaction.setup_local_player(_player)
+ _decor_shop_interaction.local_player_range_changed.connect(
+ _on_decor_shop_range_changed
+ )
+ if _rv_upgrade_shop_interaction != null:
+ _rv_upgrade_shop_interaction.setup_local_player(_player)
+ _rv_upgrade_shop_interaction.local_player_range_changed.connect(
+ _on_rv_upgrade_shop_range_changed
+ )
+ _game_ui.set_world_interactions(
+ _shop_interaction,
+ _storage_interaction,
+ _decor_shop_interaction,
+ _rv_upgrade_shop_interaction,
+ )
+ _on_shop_range_changed(
+ _shop_interaction != null
+ and _shop_interaction.is_local_player_in_range()
+ )
+ _on_storage_range_changed(
+ _storage_interaction != null
+ and _storage_interaction.is_local_player_in_range()
+ )
+ _on_decor_shop_range_changed(
+ _decor_shop_interaction != null
+ and _decor_shop_interaction.is_local_player_in_range()
+ )
+ _on_rv_upgrade_shop_range_changed(
+ _rv_upgrade_shop_interaction != null
+ and _rv_upgrade_shop_interaction.is_local_player_in_range()
+ )
+
+
func _on_water_recovery_starting() -> void:
_local_recovery_attempt_id = (
"recovery:%s"
@@ -1707,6 +2695,9 @@ func _on_water_recovery_starting() -> void:
_game_ui.close_player_menu_for_water_recovery()
_game_ui.get_pause_menu().close_for_water_recovery()
_game_ui.get_fishing_shop().close_for_water_recovery()
+ _game_ui.get_decor_shop().close_for_water_recovery()
+ _game_ui.get_rv_upgrade_shop().close_for_water_recovery()
+ _game_ui.get_player_storage().close_for_water_recovery()
func _on_water_recovery_finished() -> void:
@@ -1745,19 +2736,46 @@ func _on_active_hotbar_item_changed(
var item = item_catalog.get_item_by_id(item_id)
var active_is_rod: bool = (
item != null
+ and item.is_available()
and item.category == ItemDataType.Category.ROD
and _player.bag.owns_item(item_id)
)
var active_is_art_kit: bool = (
item_id == ArtShopStockType.ART_KIT_ITEM_ID
and item != null
+ and item.is_available()
+ and _player.bag.owns_item(item_id)
+ and not String(_network_home.get_local_space()).begins_with("rv:")
+ )
+ var active_is_catching_net: bool = (
+ item_id == FishingShopStockType.CRAB_NET_ID
+ and item != null
+ and item.is_available()
and _player.bag.owns_item(item_id)
)
- _player.set_active_item_is_rod(active_is_rod, true)
+ var active_is_shovel: bool = (
+ item_id == FishingShopStockType.STANDARD_SHOVEL_ID
+ and item != null
+ and item.is_available()
+ and _player.bag.owns_item(item_id)
+ )
+ var active_is_decor: bool = (
+ DecorCatalogType.has_product(item_id)
+ and item != null
+ and item.is_available()
+ and _player.bag.owns_item(item_id)
+ )
+ _player.set_active_fishing_rod(
+ item as FishingRodDataType if active_is_rod else null,
+ true,
+ )
_player.set_active_art_kit(
item.icon if item != null else null,
active_is_art_kit,
)
+ _player.set_active_catching_net(active_is_catching_net)
+ _player.set_active_shovel(active_is_shovel)
+ _player.set_active_decor_package(active_is_decor)
_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)
@@ -1795,8 +2813,11 @@ func _on_quit_requested() -> void:
func _show_title_music(restart_from_beginning: bool = false) -> void:
+ _new_game_music.stop()
_title_music_requested = true
_replace_title_music_transition()
+ if _title_music.stream == null:
+ return
if restart_from_beginning:
_title_music.stop()
_title_music.volume_db = title_music_volume_db
@@ -1804,6 +2825,19 @@ func _show_title_music(restart_from_beginning: bool = false) -> void:
_title_music.play(0.0)
+func _start_new_game_music() -> void:
+ if _new_game_music.stream == null:
+ return
+ _title_music_requested = false
+ _replace_title_music_transition()
+ _title_music.stop()
+ _title_music.volume_db = title_music_volume_db
+ _dusk_music.stop()
+ _new_game_music.stop()
+ _new_game_music.volume_db = new_game_music_volume_db
+ _new_game_music.play(0.0)
+
+
func _fade_out_title_music(on_complete: Callable = Callable()) -> void:
_title_music_requested = false
var generation: int = _replace_title_music_transition()
@@ -1861,6 +2895,8 @@ func _finish_quit() -> void:
func _exit_tree() -> void:
+ _cancel_home_entry_transition()
+ _cancel_home_exit_transition(true)
if _dedicated_runtime and _network_session != null:
_network_session.disconnect_session("Dedicated server stopping.")
if _title_music_tween != null:
@@ -1869,16 +2905,233 @@ func _exit_tree() -> void:
if is_instance_valid(_title_music):
_title_music.stop()
_title_music.stream = null
+ if is_instance_valid(_new_game_music):
+ _new_game_music.stop()
+ _new_game_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)
+ _refresh_shop_npc_speech_state()
if not in_range:
_game_ui.get_fishing_shop().close_for_range_exit()
- _game_ui.set_shop_prompt_visible(_can_show_shop_prompt())
+ _game_ui.set_shop_prompt_visible(_can_show_any_shop_prompt())
+
+
+func _on_storage_range_changed(in_range: bool) -> void:
+ if not in_range:
+ _game_ui.get_player_storage().close_for_range_exit()
+ var prompt := _get_home_prompt()
+ _refresh_home_door_prompt(prompt)
+ _game_ui.set_rv_storage_button_visible(
+ _can_show_rv_storage_button()
+ )
+
+
+func _on_decor_shop_range_changed(in_range: bool) -> void:
+ _refresh_shop_npc_speech_state()
+ if not in_range:
+ _game_ui.get_decor_shop().close_for_range_exit()
+ _game_ui.set_shop_prompt_visible(_can_show_any_shop_prompt())
+
+
+func _on_rv_upgrade_shop_range_changed(in_range: bool) -> void:
+ _refresh_shop_npc_speech_state()
+ if not in_range:
+ _game_ui.get_rv_upgrade_shop().close_for_range_exit()
+ _game_ui.set_shop_prompt_visible(_can_show_any_shop_prompt())
+
+
+func _on_home_state_changed() -> void:
+ _refresh_shop_npc_speech_state()
+
+
+func _on_local_home_space_changed(
+ space_id: StringName,
+ owner_fingerprint: String,
+) -> void:
+ var inside_rv: bool = String(space_id).begins_with("rv:")
+ _refresh_active_hotbar_item()
+ _fishing_spot.set_fishing_environment_enabled(not inside_rv)
+ _game_ui.set_rv_interior_camera_mode(
+ inside_rv
+ )
+ _game_ui.set_rv_storage_button_visible(
+ _can_show_rv_storage_button()
+ )
+ if inside_rv and not _home_exit_transition_active:
+ _begin_home_entry_transition(owner_fingerprint)
+ elif not inside_rv and _home_entry_transition_active:
+ _cancel_home_entry_transition()
+
+
+func _begin_home_entry_transition(owner_fingerprint: String) -> void:
+ if owner_fingerprint.is_empty() or _home_entry_transition_active:
+ return
+ _home_entry_transition_active = true
+ _refresh_home_door_prompt({})
+ _player.set_local_input_suppressed(HOME_ENTRY_INPUT_OWNER, true)
+ _player.set_camera_input_suppressed(HOME_ENTRY_INPUT_OWNER, true)
+ var inward_direction: Vector3 = (
+ -_home_world.get_interior_exit_direction(owner_fingerprint)
+ )
+ _player.face_world_direction(inward_direction)
+ _player.begin_animation_action(PlayerType.CHARACTER_WALKING_ANIMATION)
+ _home_entry_walk_tween = create_tween()
+ _home_entry_walk_tween.set_trans(Tween.TRANS_SINE)
+ _home_entry_walk_tween.set_ease(Tween.EASE_IN_OUT)
+ _home_entry_walk_tween.tween_property(
+ _player,
+ "global_position",
+ _player.global_position + inward_direction * HOME_ENTRY_WALK_DISTANCE,
+ HOME_ENTRY_WALK_DURATION,
+ )
+ _home_entry_walk_tween.finished.connect(
+ _cancel_home_entry_transition,
+ CONNECT_ONE_SHOT,
+ )
+
+
+func _cancel_home_entry_transition() -> void:
+ if _home_entry_walk_tween != null and _home_entry_walk_tween.is_valid():
+ _home_entry_walk_tween.kill()
+ _home_entry_walk_tween = null
+ if is_instance_valid(_player):
+ _player.end_animation_action()
+ _player.set_local_input_suppressed(HOME_ENTRY_INPUT_OWNER, false)
+ _player.set_camera_input_suppressed(HOME_ENTRY_INPUT_OWNER, false)
+ _home_entry_transition_active = false
+
+
+func _maybe_begin_home_exit_transition() -> void:
+ if (
+ _home_exit_transition_active
+ or _home_entry_transition_active
+ or not _gameplay_started
+ or _water_recovery.is_recovery_active()
+ or not _fishing_spot.can_open_fishing_shop()
+ or _game_ui.get_player_storage().visible
+ or _game_ui.get_fishing_shop().visible
+ or _game_ui.get_decor_shop().visible
+ or _game_ui.get_rv_upgrade_shop().visible
+ ):
+ return
+ var owner_fingerprint: String = (
+ _network_home.get_local_home_owner_fingerprint()
+ )
+ if (
+ owner_fingerprint.is_empty()
+ or not _home_world.has_avatar_crossed_interior_exit_threshold(
+ _player,
+ owner_fingerprint,
+ )
+ ):
+ return
+ _begin_home_exit_transition(owner_fingerprint)
+
+
+func _begin_home_exit_transition(owner_fingerprint: String) -> void:
+ _home_exit_transition_active = true
+ _home_exit_waiting_for_network = false
+ _home_exit_start_position = _player.global_position
+ _refresh_home_door_prompt({})
+ _player.set_local_input_suppressed(HOME_EXIT_INPUT_OWNER, true)
+ _player.set_camera_input_suppressed(HOME_EXIT_INPUT_OWNER, true)
+ var exit_direction: Vector3 = _home_world.get_interior_exit_direction(
+ owner_fingerprint
+ )
+ _player.face_world_direction(exit_direction)
+ _player.begin_animation_action(PlayerType.CHARACTER_WALKING_ANIMATION)
+ _home_exit_walk_tween = create_tween()
+ _home_exit_walk_tween.set_trans(Tween.TRANS_SINE)
+ _home_exit_walk_tween.set_ease(Tween.EASE_IN_OUT)
+ _home_exit_walk_tween.tween_property(
+ _player,
+ "global_position",
+ _player.global_position + exit_direction * HOME_EXIT_WALK_DISTANCE,
+ HOME_EXIT_WALK_DURATION,
+ )
+ _home_exit_walk_tween.finished.connect(
+ _on_home_exit_walk_finished,
+ CONNECT_ONE_SHOT,
+ )
+
+
+func _on_home_exit_walk_finished() -> void:
+ _home_exit_walk_tween = null
+ if not _home_exit_transition_active:
+ return
+ # End the walking action on the same frame translation ends. The fade and
+ # authoritative relocation may take longer, but a stationary avatar must
+ # never keep cycling its walk animation during that wait.
+ _player.end_animation_action()
+ _game_ui.get_screen_fade().fade_to_black(HOME_EXIT_FADE_GENERATION)
+
+
+func _on_home_exit_fade_transition_completed(
+ generation: int,
+ faded_to_black: bool,
+) -> void:
+ if (
+ generation != HOME_EXIT_FADE_GENERATION
+ or not _home_exit_transition_active
+ ):
+ return
+ if faded_to_black:
+ # A local host can apply the authoritative transition synchronously from
+ # request_exit(). Mark this first so its completion signal is not lost.
+ _home_exit_waiting_for_network = true
+ var request_started: bool = _network_home.request_exit()
+ if not request_started:
+ _home_exit_waiting_for_network = false
+ _player.global_position = _home_exit_start_position
+ _game_ui.get_screen_fade().fade_from_black(
+ HOME_EXIT_FADE_GENERATION
+ )
+ return
+ _cancel_home_exit_transition(false)
+
+
+func _on_local_home_transition_finished(
+ accepted: bool,
+ _message: String,
+) -> void:
+ if (
+ not _home_exit_transition_active
+ or not _home_exit_waiting_for_network
+ ):
+ return
+ _home_exit_waiting_for_network = false
+ if not accepted:
+ _player.global_position = _home_exit_start_position
+ _game_ui.get_screen_fade().fade_from_black(HOME_EXIT_FADE_GENERATION)
+
+
+func _cancel_home_exit_transition(reset_fade: bool) -> void:
+ if _home_exit_walk_tween != null and _home_exit_walk_tween.is_valid():
+ _home_exit_walk_tween.kill()
+ _home_exit_walk_tween = null
+ if is_instance_valid(_player):
+ _player.end_animation_action()
+ _player.set_local_input_suppressed(HOME_EXIT_INPUT_OWNER, false)
+ _player.set_camera_input_suppressed(HOME_EXIT_INPUT_OWNER, false)
+ if (
+ reset_fade
+ and is_instance_valid(_game_ui)
+ and is_instance_valid(_game_ui.get_screen_fade())
+ ):
+ _game_ui.get_screen_fade().reset_immediately()
+ _home_exit_transition_active = false
+ _home_exit_waiting_for_network = false
+ _home_exit_start_position = Vector3.ZERO
+
+
+func _on_rv_storage_requested() -> void:
+ if not _can_show_rv_storage_button():
+ return
+ _game_ui.get_player_storage().open_storage_from_home(true)
func _can_show_shop_prompt() -> bool:
@@ -1887,6 +3140,141 @@ func _can_show_shop_prompt() -> bool:
and _shop_interaction != null
and _shop_interaction.is_local_player_in_range()
and not _game_ui.get_fishing_shop().visible
+ and not _game_ui.get_decor_shop().visible
+ and not _game_ui.get_rv_upgrade_shop().visible
and not _water_recovery.is_recovery_active()
and _fishing_spot.can_open_fishing_shop()
)
+
+
+func _can_show_decor_shop_prompt() -> bool:
+ return (
+ _gameplay_started
+ and _decor_shop_interaction != null
+ and _decor_shop_interaction.is_local_player_in_range()
+ and not _game_ui.get_decor_shop().visible
+ and not _game_ui.get_fishing_shop().visible
+ and not _game_ui.get_rv_upgrade_shop().visible
+ and not _water_recovery.is_recovery_active()
+ and _fishing_spot.can_open_fishing_shop()
+ )
+
+
+func _can_show_rv_upgrade_shop_prompt() -> bool:
+ return (
+ _gameplay_started
+ and _rv_upgrade_shop_interaction != null
+ and _rv_upgrade_shop_interaction.is_local_player_in_range()
+ and not _game_ui.get_rv_upgrade_shop().visible
+ and not _game_ui.get_fishing_shop().visible
+ and not _game_ui.get_decor_shop().visible
+ and not _water_recovery.is_recovery_active()
+ and _fishing_spot.can_open_fishing_shop()
+ )
+
+
+func _can_show_any_shop_prompt() -> bool:
+ return (
+ _can_show_shop_prompt()
+ or _can_show_decor_shop_prompt()
+ or _can_show_rv_upgrade_shop_prompt()
+ )
+
+
+func _is_any_shop_npc_in_range() -> bool:
+ return (
+ (
+ _shop_interaction != null
+ and _shop_interaction.is_local_player_in_range()
+ )
+ or (
+ _decor_shop_interaction != null
+ and _decor_shop_interaction.is_local_player_in_range()
+ )
+ or (
+ _rv_upgrade_shop_interaction != null
+ and _rv_upgrade_shop_interaction.is_local_player_in_range()
+ )
+ )
+
+
+func _refresh_shop_npc_speech_state() -> void:
+ var interaction := _get_active_shop_npc_interaction()
+ var dialogue_text: String = ""
+ if interaction != null:
+ dialogue_text = interaction.get_dialogue_text(_home_state.get_tier())
+ _game_ui.set_shop_npc_player_in_range(
+ interaction != null,
+ dialogue_text,
+ )
+
+
+func _get_active_shop_npc_interaction() -> FishingShopInteractionType:
+ # Match the interaction and prompt priority used above: RV, decor, fishing.
+ if (
+ _rv_upgrade_shop_interaction != null
+ and _rv_upgrade_shop_interaction.is_local_player_in_range()
+ ):
+ return _rv_upgrade_shop_interaction
+ if (
+ _decor_shop_interaction != null
+ and _decor_shop_interaction.is_local_player_in_range()
+ ):
+ return _decor_shop_interaction
+ if (
+ _shop_interaction != null
+ and _shop_interaction.is_local_player_in_range()
+ ):
+ return _shop_interaction
+ return null
+
+
+func _can_show_storage_prompt() -> bool:
+ return not _get_home_prompt().is_empty()
+
+
+func _refresh_home_door_prompt(prompt: Dictionary) -> void:
+ # Home entry belongs to the doorway in world space. Keep the retired HUD
+ # prompt hidden so it cannot overlap the compact door-mounted sign.
+ _game_ui.set_storage_prompt_visible(false)
+ _home_world.set_exterior_prompt_visible(
+ str(prompt.get("owner_fingerprint", "")),
+ not prompt.is_empty(),
+ str(prompt.get("label", "enter home")),
+ )
+
+
+func _can_show_rv_storage_button() -> bool:
+ return (
+ _gameplay_started
+ and not _network_home.get_local_home_owner_fingerprint().is_empty()
+ and _network_home.can_local_decorate()
+ and not _water_recovery.is_recovery_active()
+ and _fishing_spot.can_open_fishing_shop()
+ )
+
+
+func _get_home_prompt() -> Dictionary:
+ if (
+ not _gameplay_started
+ or _game_ui.get_player_storage().visible
+ or _game_ui.get_fishing_shop().visible
+ or _game_ui.get_decor_shop().visible
+ or _game_ui.get_rv_upgrade_shop().visible
+ or _water_recovery.is_recovery_active()
+ or not _fishing_spot.can_open_fishing_shop()
+ ):
+ return {}
+ var owner_fingerprint: String = (
+ _network_home.get_local_home_owner_fingerprint()
+ )
+ if not owner_fingerprint.is_empty():
+ return {}
+ var nearby_owner: String = _home_world.find_nearby_exterior_owner(_player)
+ if nearby_owner.is_empty():
+ return {}
+ return {
+ "label": "enter home",
+ "anchor": _home_world.get_exterior_prompt_position(nearby_owner),
+ "owner_fingerprint": nearby_owner,
+ }
diff --git a/main/main.tscn b/main/main.tscn
index 70eaa5d..778a1d4 100644
--- a/main/main.tscn
+++ b/main/main.tscn
@@ -12,8 +12,6 @@
[ext_resource type="Script" uid="uid://dwv3nvfke5xtu" path="res://settings/player_settings_manager.gd" id="10_settings"]
[ext_resource type="Resource" path="res://items/catalog/item_catalog.tres" id="11_items"]
[ext_resource type="Resource" path="res://economy/buyers/main_fishing_shop.tres" id="12_main_shop"]
-[ext_resource type="AudioStream" uid="uid://bgkfluumuen4m" path="res://audio/music/title/as_in_four_wolves.ogg" id="13_title_music"]
-[ext_resource type="AudioStream" path="res://audio/music/world/craft.mp3" id="dusk_music"]
[ext_resource type="Script" uid="uid://bdtb05vkiicd" path="res://ui/ui_pixelation_presenter.gd" id="14_pixelation"]
[ext_resource type="PackedScene" path="res://ui/pixelation_reset_overlay.tscn" id="15_reset"]
[ext_resource type="PackedScene" path="res://main/world_pixelation_postprocess.tscn" id="16_world_pixelation"]
@@ -40,7 +38,7 @@
[ext_resource type="Script" uid="uid://dpiv6wttry0j4" path="res://network/player_data_root.gd" id="37_data_root"]
[ext_resource type="Script" uid="uid://bu5q4a8jfvxkf" path="res://network/identity_backup_service.gd" id="38_identity_backup"]
[ext_resource type="Script" uid="uid://djmxy6jibi5y3" path="res://ui/interface_font_controller.gd" id="39_fonts"]
-[ext_resource type="Shader" uid="uid://c8s1g7tn771du" path="res://ui/title_water_background.gdshader" id="40_title_water"]
+[ext_resource type="Texture2D" uid="uid://doju2lbj2ghwa" path="res://world/generation/chunks/assets/chunk_0000_grass_lite.png" id="40_title_grass"]
[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"]
@@ -54,22 +52,23 @@
[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" path="res://settings/keyboard_mouse_mapping_manager.gd" id="keyboard_mouse_mapping"]
[ext_resource type="Script" uid="uid://d0jv8n2nfqia0" path="res://network/discovery_client.gd" id="57_discovery"]
+[ext_resource type="Resource" path="res://gathering/catalog/gatherable_catalog.tres" id="58_gatherable_catalog"]
+[ext_resource type="Script" path="res://network/network_world_spawn_service.gd" id="59_world_spawns"]
+[ext_resource type="Script" path="res://gathering/gathering_controller.gd" id="60_gathering_controller"]
+[ext_resource type="Script" path="res://homes/player_home_state.gd" id="61_home_state"]
+[ext_resource type="Script" path="res://homes/home_world_service.gd" id="62_home_world"]
+[ext_resource type="Script" path="res://network/network_home_service.gd" id="63_network_home"]
+[ext_resource type="Script" path="res://homes/home_decor_controller.gd" id="64_home_decor_controller"]
-[sub_resource type="ShaderMaterial" id="ShaderMaterial_title_water_native"]
-shader = ExtResource("40_title_water")
-shader_parameter/surface_color = Color(0.1, 0.45, 0.62, 1)
-shader_parameter/middle_color = Color(0.055, 0.285, 0.4, 1)
-shader_parameter/depth_color = Color(0.02, 0.16, 0.27, 1)
-shader_parameter/virtual_pixel_density = Vector2(320, 180)
-shader_parameter/color_step_count = 14.0
-shader_parameter/continuous_depth_blend = 0.12
-shader_parameter/wave_speed = 0.1
-shader_parameter/wave_scale = 2.4
-shader_parameter/wave_strength = 0.012
-shader_parameter/caustic_strength = 0.018
+[sub_resource type="ShaderMaterial" id="ShaderMaterial_title_grass_native"]
+shader = ExtResource("42_menu_pattern")
+shader_parameter/pattern_texture = ExtResource("40_title_grass")
+shader_parameter/source_tile_size = Vector2(128, 128)
+shader_parameter/display_scale = 1.0
+shader_parameter/scroll_velocity_pixels = Vector2(-7.5, -7.5)
[sub_resource type="ShaderMaterial" id="ShaderMaterial_player_menu_pattern"]
shader = ExtResource("42_menu_pattern")
@@ -85,54 +84,42 @@ 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_umo15"]
-data = PackedVector3Array(-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, -9.960857, -0.43518662, 25.698242, -7.448795, -0.21135795, 25.584726, -9.960857, -0.21135795, 25.698242, -9.960857, -0.43518662, 25.698242, -7.448795, -0.43518662, 25.584726, -7.448795, -0.21135795, 25.584726, -12.553318, -0.21135795, 25.584726, -9.960857, -0.43518662, 25.698242, -9.960857, -0.21135795, 25.698242, -12.553318, -0.21135795, 25.584726, -12.553318, -0.43518662, 25.584726, -9.960857, -0.43518662, 25.698242, -9.960857, -0.4351864, 26.297613, -7.448795, -0.43518662, 25.584726, -9.960857, -0.43518662, 25.698242, -9.960857, -0.4351864, 26.297613, -7.448795, -0.4351864, 26.092749, -7.448795, -0.43518662, 25.584726, -9.960857, -0.43518662, 25.698242, -12.553318, -0.4351864, 26.278648, -9.960857, -0.4351864, 26.297613, -9.960857, -0.43518662, 25.698242, -12.553318, -0.4351865, 25.925812, -12.553318, -0.4351864, 26.278648, -12.553318, -0.4351865, 25.925812, -9.960857, -0.43518662, 25.698242, -12.553318, -0.43518662, 25.584726, -7.448795, -0.4351864, 26.092749, -7.448795, -0.21135795, 25.584726, -7.448795, -0.43518662, 25.584726, -7.448795, -0.4351864, 26.092749, -7.448795, -0.21135771, 26.092747, -7.448795, -0.21135795, 25.584726, -12.553318, -0.21135783, 25.884945, -12.455294, -0.2783996, 25.944508, -12.553318, -0.4351865, 25.925812, -9.960857, -0.21135771, 26.14777, -9.960857, -0.26744843, 26.297613, -10.608839, -0.21135771, 26.292805, -9.960857, -0.21135771, 26.14777, -9.332909, -0.21135771, 26.24633, -9.960857, -0.26744843, 26.297613, -9.960857, -0.21135771, 26.14777, -7.448795, -0.21135771, 26.092747, -9.332909, -0.21135771, 26.24633, -9.960857, -0.21135771, 26.14777, -7.448795, -0.21135795, 25.584726, -7.448795, -0.21135771, 26.092747, -7.448795, -0.21135795, 25.584726, -9.960857, -0.21135771, 26.14777, -9.960857, -0.21135795, 25.698242, -10.608839, -0.21135771, 26.292805, -9.960857, -0.21135795, 25.698242, -9.960857, -0.21135771, 26.14777, -9.960857, -0.21135795, 25.698242, -12.553318, -0.21135783, 25.884945, -12.553318, -0.21135795, 25.584726, -9.960857, -0.21135795, 25.698242, -12.107531, -0.21135783, 25.946377, -12.553318, -0.21135783, 25.884945, -10.608839, -0.21135771, 26.292805, -12.107531, -0.21135783, 25.946377, -9.960857, -0.21135795, 25.698242, -12.455294, -0.2783996, 25.944508, -12.553318, -0.21135783, 25.884945, -12.107531, -0.21135783, 25.946377, -12.553318, -0.21135771, 26.054285, -12.455294, -0.2783996, 25.944508, -12.107531, -0.21135783, 25.946377, -12.107531, -0.21135783, 25.946377, -10.608839, -0.21135771, 26.292805, -12.553318, -0.21135771, 26.054285, -12.553318, -0.21135771, 26.054285, -10.608839, -0.21135771, 26.292805, -12.553318, -0.21135771, 26.278648, -12.553318, -0.43518662, 25.584726, -12.553318, -0.21135783, 25.884945, -12.553318, -0.4351865, 25.925812, -12.553318, -0.43518662, 25.584726, -12.553318, -0.21135795, 25.584726, -12.553318, -0.21135783, 25.884945, -12.553318, -0.21135771, 26.054285, -12.553318, -0.4351864, 26.278648, -12.553318, -0.4351865, 25.925812, -12.553318, -0.21135771, 26.054285, -12.553318, -0.21135771, 26.278648, -12.553318, -0.4351864, 26.278648, -12.455294, -0.2783996, 25.944508, -12.553318, -0.21135771, 26.054285, -12.553318, -0.4351865, 25.925812, -12.553318, -0.21135771, 26.278648, -9.960857, -0.4351864, 26.297613, -12.553318, -0.4351864, 26.278648, -9.960857, -0.4351864, 26.297613, -12.553318, -0.21135771, 26.278648, -10.608839, -0.21135771, 26.292805, -9.960857, -0.4351864, 26.297613, -10.608839, -0.21135771, 26.292805, -9.960857, -0.26744843, 26.297613, -9.960857, -0.26744843, 26.297613, -7.448795, -0.4351864, 26.092749, -9.960857, -0.4351864, 26.297613, -9.960857, -0.26744843, 26.297613, -7.448795, -0.21135771, 26.092747, -7.448795, -0.4351864, 26.092749, -7.448795, -0.21135771, 26.092747, -9.960857, -0.26744843, 26.297613, -9.332909, -0.21135771, 26.24633, -8.00062, -0.21135712, 27.768526, -7.448795, -0.4351858, 27.780544, -8.00062, -0.4351858, 27.768526, -8.00062, -0.21135712, 27.768526, -7.448795, -0.21135712, 27.780544, -7.448795, -0.4351858, 27.780544, -8.00062, -0.4351858, 27.768526, -11.415211, -0.21135712, 27.777607, -8.00062, -0.21135712, 27.768526, -8.00062, -0.4351858, 27.768526, -12.553318, -0.26744783, 27.780544, -11.415211, -0.21135712, 27.777607, -12.553318, -0.26744783, 27.780544, -8.00062, -0.4351858, 27.768526, -12.553318, -0.4351858, 27.780544, -7.448795, -0.21135736, 27.178772, -8.00062, -0.21135712, 27.768526, -8.00062, -0.21135736, 27.164883, -11.415211, -0.21135712, 27.777607, -8.00062, -0.21135736, 27.164883, -8.00062, -0.21135712, 27.768526, -11.415211, -0.21135712, 27.777607, -12.553318, -0.21135736, 27.272522, -8.00062, -0.21135736, 27.164883, -12.553318, -0.21135736, 27.272522, -11.415211, -0.21135712, 27.777607, -12.553318, -0.21135724, 27.653406, -12.553318, -0.21135724, 27.653406, -11.415211, -0.21135712, 27.777607, -12.553318, -0.26744783, 27.780544, -8.00062, -0.21135712, 27.768526, -7.448795, -0.21135736, 27.178772, -7.74287, -0.21135724, 27.495817, -7.74287, -0.21135724, 27.495817, -7.448795, -0.21135736, 27.178772, -7.448795, -0.21135724, 27.421297, -7.448795, -0.21135724, 27.421297, -7.4990096, -0.29682887, 27.488071, -7.74287, -0.21135724, 27.495817, -7.4990096, -0.29682887, 27.488071, -7.448795, -0.21135724, 27.589035, -7.74287, -0.21135724, 27.495817, -8.00062, -0.21135712, 27.768526, -7.74287, -0.21135724, 27.495817, -7.448795, -0.21135724, 27.589035, -8.00062, -0.21135712, 27.768526, -7.448795, -0.21135724, 27.589035, -7.448795, -0.21135712, 27.780544, -8.00062, -0.43518603, 27.164883, -7.448795, -0.21135736, 27.178772, -8.00062, -0.21135736, 27.164883, -8.00062, -0.43518603, 27.164883, -7.448795, -0.43518603, 27.178772, -7.448795, -0.21135736, 27.178772, -12.553318, -0.21135736, 27.272522, -8.00062, -0.43518603, 27.164883, -8.00062, -0.21135736, 27.164883, -12.553318, -0.21135736, 27.272522, -12.553318, -0.43518603, 27.272522, -8.00062, -0.43518603, 27.164883, -7.448795, -0.4351858, 27.780544, -8.00062, -0.43518603, 27.164883, -8.00062, -0.4351858, 27.768526, -8.00062, -0.43518603, 27.164883, -7.448795, -0.4351858, 27.780544, -7.448795, -0.4351859, 27.45522, -8.00062, -0.43518603, 27.164883, -7.448795, -0.4351859, 27.45522, -7.448795, -0.43518603, 27.178772, -12.553318, -0.43518603, 27.272522, -8.00062, -0.4351858, 27.768526, -8.00062, -0.43518603, 27.164883, -12.553318, -0.43518603, 27.272522, -12.553318, -0.4351858, 27.780544, -8.00062, -0.4351858, 27.768526, -7.448795, -0.21135724, 27.589035, -7.4990096, -0.29682887, 27.488071, -7.448795, -0.4351859, 27.45522, -7.4990096, -0.29682887, 27.488071, -7.448795, -0.21135724, 27.421297, -7.448795, -0.4351859, 27.45522, -7.448795, -0.4351858, 27.780544, -7.448795, -0.21135724, 27.589035, -7.448795, -0.4351859, 27.45522, -7.448795, -0.4351858, 27.780544, -7.448795, -0.21135712, 27.780544, -7.448795, -0.21135724, 27.589035, -7.448795, -0.21135724, 27.421297, -7.448795, -0.43518603, 27.178772, -7.448795, -0.4351859, 27.45522, -7.448795, -0.21135724, 27.421297, -7.448795, -0.21135736, 27.178772, -7.448795, -0.43518603, 27.178772, -12.553318, -0.21135736, 27.272522, -12.553318, -0.4351858, 27.780544, -12.553318, -0.43518603, 27.272522, -12.553318, -0.4351858, 27.780544, -12.553318, -0.21135736, 27.272522, -12.553318, -0.21135724, 27.653406, -12.553318, -0.4351858, 27.780544, -12.553318, -0.21135724, 27.653406, -12.553318, -0.26744783, 27.780544, -7.91141, -0.4506787, 24.580168, -7.8849664, -0.46884167, 23.938063, -7.8849664, -0.4752518, 24.59085, -7.91141, -0.4506787, 24.580168, -7.91141, -0.44480276, 23.94928, -7.8849664, -0.46884167, 23.938063, -7.91141, -0.44480276, 23.94928, -7.8849664, -0.31258893, 23.91349, -7.8849664, -0.46884167, 23.938063, -7.91141, -0.44480276, 23.94928, -7.91141, -0.3189993, 23.939398, -7.8849664, -0.31258893, 23.91349, -7.8849664, -0.4752518, 24.59085, -7.91141, -0.49314773, 23.926844, -7.91141, -0.49982488, 24.601803, -7.8849664, -0.4752518, 24.59085, -7.8849664, -0.46884167, 23.938063, -7.91141, -0.49314773, 23.926844, -7.8849664, -0.46884167, 23.938063, -7.91141, -0.30617857, 23.88758, -7.91141, -0.49314773, 23.926844, -7.8849664, -0.46884167, 23.938063, -7.8849664, -0.31258893, 23.91349, -7.91141, -0.30617857, 23.88758, -7.91141, -0.49982488, 24.601803, -7.9383864, -0.46884167, 23.938063, -7.9383864, -0.4752518, 24.59085, -7.91141, -0.49982488, 24.601803, -7.91141, -0.49314773, 23.926844, -7.9383864, -0.46884167, 23.938063, -7.91141, -0.49314773, 23.926844, -7.9383864, -0.31258893, 23.91349, -7.9383864, -0.46884167, 23.938063, -7.91141, -0.49314773, 23.926844, -7.91141, -0.30617857, 23.88758, -7.9383864, -0.31258893, 23.91349, -7.9383864, -0.4752518, 24.59085, -7.91141, -0.44480276, 23.94928, -7.91141, -0.4506787, 24.580168, -7.9383864, -0.4752518, 24.59085, -7.9383864, -0.46884167, 23.938063, -7.91141, -0.44480276, 23.94928, -7.9383864, -0.46884167, 23.938063, -7.91141, -0.3189993, 23.939398, -7.91141, -0.44480276, 23.94928, -7.9383864, -0.46884167, 23.938063, -7.9383864, -0.31258893, 23.91349, -7.91141, -0.3189993, 23.939398, -7.91141, -0.3189993, 23.939398, -7.8849664, -0.18144345, 23.953821, -7.8849664, -0.31258893, 23.91349, -7.91141, -0.3189993, 23.939398, -7.91141, -0.20761919, 23.95943, -7.8849664, -0.18144345, 23.953821, -7.91141, -0.20761919, 23.95943, -7.8849664, -0.17903936, 24.585777, -7.8849664, -0.18144345, 23.953821, -7.91141, -0.20761919, 23.95943, -7.91141, -0.20494795, 24.579367, -7.8849664, -0.17903936, 24.585777, -7.91141, -0.20494795, 24.579367, -7.8849664, -0.32434106, 24.644806, -7.8849664, -0.17903936, 24.585777, -7.91141, -0.20494795, 24.579367, -7.91141, -0.32968295, 24.61863, -7.8849664, -0.32434106, 24.644806, -7.8849664, -0.31258893, 23.91349, -7.91141, -0.15553498, 23.947945, -7.91141, -0.30617857, 23.88758, -7.8849664, -0.31258893, 23.91349, -7.8849664, -0.18144345, 23.953821, -7.91141, -0.15553498, 23.947945, -7.8849664, -0.18144345, 23.953821, -7.91141, -0.15286362, 24.592188, -7.91141, -0.15553498, 23.947945, -7.8849664, -0.18144345, 23.953821, -7.8849664, -0.17903936, 24.585777, -7.91141, -0.15286362, 24.592188, -7.8849664, -0.17903936, 24.585777, -7.91141, -0.31899905, 24.67098, -7.91141, -0.15286362, 24.592188, -7.8849664, -0.17903936, 24.585777, -7.8849664, -0.32434106, 24.644806, -7.91141, -0.31899905, 24.67098, -7.91141, -0.30617857, 23.88758, -7.9383864, -0.18144345, 23.953821, -7.9383864, -0.31258893, 23.91349, -7.91141, -0.30617857, 23.88758, -7.91141, -0.15553498, 23.947945, -7.9383864, -0.18144345, 23.953821, -7.9383864, -0.31258893, 23.91349, -7.91141, -0.20761919, 23.95943, -7.91141, -0.3189993, 23.939398, -7.9383864, -0.31258893, 23.91349, -7.9383864, -0.18144345, 23.953821, -7.91141, -0.20761919, 23.95943, -7.9383864, -0.18144345, 23.953821, -7.91141, -0.20494795, 24.579367, -7.91141, -0.20761919, 23.95943, -7.9383864, -0.18144345, 23.953821, -7.9383864, -0.17903936, 24.585777, -7.91141, -0.20494795, 24.579367, -7.9383864, -0.17903936, 24.585777, -7.91141, -0.32968295, 24.61863, -7.91141, -0.20494795, 24.579367, -7.9383864, -0.17903936, 24.585777, -7.9383864, -0.32434106, 24.644806, -7.91141, -0.32968295, 24.61863, -7.91141, -0.15553498, 23.947945, -7.9383864, -0.17903936, 24.585777, -7.9383864, -0.18144345, 23.953821, -7.91141, -0.15553498, 23.947945, -7.91141, -0.15286362, 24.592188, -7.9383864, -0.17903936, 24.585777, -7.91141, -0.15286362, 24.592188, -7.9383864, -0.32434106, 24.644806, -7.9383864, -0.17903936, 24.585777, -7.91141, -0.15286362, 24.592188, -7.91141, -0.31899905, 24.67098, -7.9383864, -0.32434106, 24.644806, -7.91141, -0.32968295, 24.61863, -7.8849664, -0.4752518, 24.59085, -7.8849664, -0.32434106, 24.644806, -7.91141, -0.32968295, 24.61863, -7.91141, -0.4506787, 24.580168, -7.8849664, -0.4752518, 24.59085, -7.8849664, -0.32434106, 24.644806, -7.91141, -0.49982488, 24.601803, -7.91141, -0.31899905, 24.67098, -7.8849664, -0.32434106, 24.644806, -7.8849664, -0.4752518, 24.59085, -7.91141, -0.49982488, 24.601803, -7.91141, -0.31899905, 24.67098, -7.9383864, -0.4752518, 24.59085, -7.9383864, -0.32434106, 24.644806, -7.91141, -0.31899905, 24.67098, -7.91141, -0.49982488, 24.601803, -7.9383864, -0.4752518, 24.59085, -7.9383864, -0.32434106, 24.644806, -7.91141, -0.4506787, 24.580168, -7.91141, -0.32968295, 24.61863, -7.9383864, -0.32434106, 24.644806, -7.9383864, -0.4752518, 24.59085, -7.91141, -0.4506787, 24.580168, -12.347652, -0.55564594, 31.95423, -12.205824, -0.4987539, 31.95423, -12.205824, -0.55564594, 31.95423, -12.347652, -0.55564594, 31.95423, -12.347652, -0.4987539, 31.95423, -12.205824, -0.4987539, 31.95423, -12.347652, -0.4987539, 31.95423, -12.205824, -0.49875677, 23.85259, -12.205824, -0.4987539, 31.95423, -12.347652, -0.4987539, 31.95423, -12.347652, -0.49875677, 23.85259, -12.205824, -0.49875677, 23.85259, -12.347652, -0.49875677, 23.85259, -12.205824, -0.5556488, 23.85259, -12.205824, -0.49875677, 23.85259, -12.347652, -0.49875677, 23.85259, -12.347652, -0.5556488, 23.85259, -12.205824, -0.5556488, 23.85259, -12.347652, -0.5556488, 23.85259, -12.205824, -0.55564594, 31.95423, -12.205824, -0.5556488, 23.85259, -12.347652, -0.5556488, 23.85259, -12.347652, -0.55564594, 31.95423, -12.205824, -0.55564594, 31.95423, -12.205824, -0.55564594, 31.95423, -12.205824, -0.49875677, 23.85259, -12.205824, -0.5556488, 23.85259, -12.205824, -0.55564594, 31.95423, -12.205824, -0.4987539, 31.95423, -12.205824, -0.49875677, 23.85259, -12.347652, -0.5556488, 23.85259, -12.347652, -0.4987539, 31.95423, -12.347652, -0.55564594, 31.95423, -12.347652, -0.5556488, 23.85259, -12.347652, -0.49875677, 23.85259, -12.347652, -0.4987539, 31.95423, -11.712225, -0.43518686, 24.871305, -7.27064, -0.21135819, 24.773548, -11.712225, -0.21135819, 24.871305, -11.712225, -0.43518686, 24.871305, -7.27064, -0.43518686, 24.773548, -7.27064, -0.21135819, 24.773548, -12.731207, -0.21135819, 24.867031, -11.712225, -0.43518686, 24.871305, -11.712225, -0.21135819, 24.871305, -12.731207, -0.21135819, 24.867031, -12.731207, -0.43518686, 24.867031, -11.712225, -0.43518686, 24.871305, -11.712225, -0.43518662, 25.488571, -7.27064, -0.43518686, 24.773548, -11.712225, -0.43518686, 24.871305, -11.712225, -0.43518662, 25.488571, -7.27064, -0.43518674, 25.375053, -7.27064, -0.43518686, 24.773548, -12.731207, -0.43518686, 24.867031, -11.712225, -0.43518662, 25.488571, -11.712225, -0.43518686, 24.871305, -12.731207, -0.43518686, 24.867031, -12.731207, -0.43518674, 25.375053, -11.712225, -0.43518662, 25.488571, -12.731207, -0.43518686, 24.867031, -12.731207, -0.21135807, 25.375053, -12.731207, -0.43518674, 25.375053, -12.731207, -0.43518686, 24.867031, -12.731207, -0.21135819, 24.867031, -12.731207, -0.21135807, 25.375053, -11.712225, -0.21135807, 25.334187, -11.712225, -0.26744866, 25.488571, -11.967037, -0.21135795, 25.460258, -11.712225, -0.21135807, 25.334187, -10.601894, -0.21135795, 25.460258, -11.712225, -0.26744866, 25.488571, -11.712225, -0.21135807, 25.334187, -8.103455, -0.21135795, 25.396154, -10.601894, -0.21135795, 25.460258, -11.967037, -0.21135795, 25.460258, -11.712225, -0.21135819, 24.871305, -11.712225, -0.21135807, 25.334187, -11.967037, -0.21135795, 25.460258, -12.731207, -0.21135819, 24.867031, -11.712225, -0.21135819, 24.871305, -12.731207, -0.21135819, 24.867031, -11.967037, -0.21135795, 25.460258, -12.731207, -0.21135807, 25.375053, -7.27064, -0.21135819, 24.773548, -11.712225, -0.21135807, 25.334187, -11.712225, -0.21135819, 24.871305, -11.712225, -0.21135807, 25.334187, -7.27064, -0.21135819, 24.773548, -8.103455, -0.21135795, 25.396154, -8.103455, -0.21135795, 25.396154, -7.27064, -0.21135819, 24.773548, -7.27064, -0.21135807, 25.224676, -8.103455, -0.21135795, 25.396154, -7.27064, -0.21135807, 25.224676, -7.27064, -0.26744878, 25.375053, -12.731207, -0.21135807, 25.375053, -11.712225, -0.43518662, 25.488571, -12.731207, -0.43518674, 25.375053, -11.712225, -0.43518662, 25.488571, -12.731207, -0.21135807, 25.375053, -11.967037, -0.21135795, 25.460258, -11.712225, -0.43518662, 25.488571, -11.967037, -0.21135795, 25.460258, -11.712225, -0.26744866, 25.488571, -11.712225, -0.26744866, 25.488571, -7.27064, -0.43518674, 25.375053, -11.712225, -0.43518662, 25.488571, -11.712225, -0.26744866, 25.488571, -8.103455, -0.21135795, 25.396154, -7.27064, -0.43518674, 25.375053, -7.27064, -0.43518674, 25.375053, -8.103455, -0.21135795, 25.396154, -7.27064, -0.26744878, 25.375053, -8.103455, -0.21135795, 25.396154, -11.712225, -0.26744866, 25.488571, -10.601894, -0.21135795, 25.460258, -7.27064, -0.26744878, 25.375053, -7.27064, -0.43518686, 24.773548, -7.27064, -0.43518674, 25.375053, -7.27064, -0.43518686, 24.773548, -7.27064, -0.26744878, 25.375053, -7.27064, -0.21135807, 25.224676, -7.27064, -0.43518686, 24.773548, -7.27064, -0.21135807, 25.224676, -7.27064, -0.21135819, 24.773548, -12.68206, -0.54656744, 23.846447, -12.206091, 2.3477144, 23.952217, -12.311861, -0.54656744, 23.846447, -12.68206, -0.54656744, 23.846447, -12.78783, 2.3477144, 23.952217, -12.206091, 2.3477144, 23.952217, -12.78783, 2.3477144, 23.952217, -12.206091, 2.3477142, 23.370743, -12.206091, 2.3477144, 23.952217, -12.78783, 2.3477144, 23.952217, -12.78783, 2.3477142, 23.370743, -12.206091, 2.3477142, 23.370743, -12.78783, 2.3477142, 23.370743, -12.311861, -0.54656756, 23.476517, -12.206091, 2.3477142, 23.370743, -12.78783, 2.3477142, 23.370743, -12.68206, -0.54656756, 23.476517, -12.311861, -0.54656756, 23.476517, -12.68206, -0.54656756, 23.476517, -12.311861, -0.54656744, 23.846447, -12.311861, -0.54656756, 23.476517, -12.68206, -0.54656756, 23.476517, -12.68206, -0.54656744, 23.846447, -12.311861, -0.54656744, 23.846447, -12.311861, -0.54656744, 23.846447, -12.206091, 2.3477142, 23.370743, -12.311861, -0.54656756, 23.476517, -12.311861, -0.54656744, 23.846447, -12.206091, 2.3477144, 23.952217, -12.206091, 2.3477142, 23.370743, -12.68206, -0.54656756, 23.476517, -12.78783, 2.3477144, 23.952217, -12.68206, -0.54656744, 23.846447, -12.68206, -0.54656756, 23.476517, -12.78783, 2.3477142, 23.370743, -12.78783, 2.3477144, 23.952217, -8.00062, -0.21135831, 24.566545, -7.448795, -0.43518698, 24.578299, -8.00062, -0.43518698, 24.566545, -8.00062, -0.21135831, 24.566545, -7.448795, -0.21135831, 24.578299, -7.448795, -0.43518698, 24.578299, -8.00062, -0.43518698, 24.566545, -11.415211, -0.21135831, 24.57536, -8.00062, -0.21135831, 24.566545, -8.00062, -0.43518698, 24.566545, -12.553318, -0.26744902, 24.578299, -11.415211, -0.21135831, 24.57536, -12.553318, -0.26744902, 24.578299, -8.00062, -0.43518698, 24.566545, -12.553318, -0.43518698, 24.578299, -7.448795, -0.21135855, 23.976791, -8.00062, -0.21135831, 24.566545, -8.00062, -0.21135855, 23.962635, -11.415211, -0.21135831, 24.57536, -8.00062, -0.21135855, 23.962635, -8.00062, -0.21135831, 24.566545, -11.415211, -0.21135831, 24.57536, -12.553318, -0.21135843, 24.070276, -8.00062, -0.21135855, 23.962635, -12.553318, -0.21135843, 24.070276, -11.415211, -0.21135831, 24.57536, -12.553318, -0.21135831, 24.451426, -12.553318, -0.21135831, 24.451426, -11.415211, -0.21135831, 24.57536, -12.553318, -0.26744902, 24.578299, -8.00062, -0.21135831, 24.566545, -7.448795, -0.21135855, 23.976791, -7.74287, -0.21135843, 24.293571, -7.74287, -0.21135843, 24.293571, -7.448795, -0.21135855, 23.976791, -7.448795, -0.21135843, 24.219051, -7.448795, -0.21135843, 24.219051, -7.4990096, -0.29683006, 24.285826, -7.74287, -0.21135843, 24.293571, -7.4990096, -0.29683006, 24.285826, -7.448795, -0.21135831, 24.386787, -7.74287, -0.21135843, 24.293571, -8.00062, -0.21135831, 24.566545, -7.74287, -0.21135843, 24.293571, -7.448795, -0.21135831, 24.386787, -8.00062, -0.21135831, 24.566545, -7.448795, -0.21135831, 24.386787, -7.448795, -0.21135831, 24.578299, -8.00062, -0.43518722, 23.962635, -7.448795, -0.21135855, 23.976791, -8.00062, -0.21135855, 23.962635, -8.00062, -0.43518722, 23.962635, -7.448795, -0.43518722, 23.976791, -7.448795, -0.21135855, 23.976791, -12.553318, -0.21135843, 24.070276, -8.00062, -0.43518722, 23.962635, -8.00062, -0.21135855, 23.962635, -12.553318, -0.21135843, 24.070276, -12.553318, -0.4351871, 24.070276, -8.00062, -0.43518722, 23.962635, -7.448795, -0.43518698, 24.578299, -8.00062, -0.43518722, 23.962635, -8.00062, -0.43518698, 24.566545, -8.00062, -0.43518722, 23.962635, -7.448795, -0.43518698, 24.578299, -7.448795, -0.4351871, 24.252972, -8.00062, -0.43518722, 23.962635, -7.448795, -0.4351871, 24.252972, -7.448795, -0.43518722, 23.976791, -12.553318, -0.4351871, 24.070276, -8.00062, -0.43518698, 24.566545, -8.00062, -0.43518722, 23.962635, -12.553318, -0.4351871, 24.070276, -12.553318, -0.43518698, 24.578299, -8.00062, -0.43518698, 24.566545, -7.448795, -0.21135831, 24.386787, -7.4990096, -0.29683006, 24.285826, -7.448795, -0.4351871, 24.252972, -7.4990096, -0.29683006, 24.285826, -7.448795, -0.21135843, 24.219051, -7.448795, -0.4351871, 24.252972, -7.448795, -0.43518698, 24.578299, -7.448795, -0.21135831, 24.386787, -7.448795, -0.4351871, 24.252972, -7.448795, -0.43518698, 24.578299, -7.448795, -0.21135831, 24.578299, -7.448795, -0.21135831, 24.386787, -7.448795, -0.21135843, 24.219051, -7.448795, -0.43518722, 23.976791, -7.448795, -0.4351871, 24.252972, -7.448795, -0.21135843, 24.219051, -7.448795, -0.21135855, 23.976791, -7.448795, -0.43518722, 23.976791, -12.553318, -0.21135843, 24.070276, -12.553318, -0.43518698, 24.578299, -12.553318, -0.4351871, 24.070276, -12.553318, -0.43518698, 24.578299, -12.553318, -0.21135843, 24.070276, -12.553318, -0.21135831, 24.451426, -12.553318, -0.43518698, 24.578299, -12.553318, -0.21135831, 24.451426, -12.553318, -0.26744902, 24.578299, -7.860928, -0.21135688, 28.491028, -7.27064, -0.43518555, 28.502779, -7.860928, -0.43518555, 28.491028, -7.860928, -0.21135688, 28.491028, -7.27064, -0.21135688, 28.502779, -7.27064, -0.43518555, 28.502779, -7.860928, -0.43518555, 28.491028, -11.513769, -0.21135688, 28.499842, -7.860928, -0.21135688, 28.491028, -7.860928, -0.43518555, 28.491028, -12.731207, -0.2674476, 28.502779, -11.513769, -0.21135688, 28.499842, -12.731207, -0.2674476, 28.502779, -7.860928, -0.43518555, 28.491028, -12.731207, -0.43518555, 28.502779, -7.27064, -0.21135712, 27.901274, -7.860928, -0.21135688, 28.491028, -7.860928, -0.21135712, 27.887384, -11.513769, -0.21135688, 28.499842, -7.860928, -0.21135712, 27.887384, -7.860928, -0.21135688, 28.491028, -11.513769, -0.21135688, 28.499842, -12.731207, -0.21135712, 27.994759, -7.860928, -0.21135712, 27.887384, -12.731207, -0.21135712, 27.994759, -11.513769, -0.21135688, 28.499842, -12.731207, -0.211357, 28.375908, -12.731207, -0.211357, 28.375908, -11.513769, -0.21135688, 28.499842, -12.731207, -0.2674476, 28.502779, -7.860928, -0.21135688, 28.491028, -7.27064, -0.21135712, 27.901274, -7.585282, -0.211357, 28.218319, -7.585282, -0.211357, 28.218319, -7.27064, -0.21135712, 27.901274, -7.27064, -0.211357, 28.143532, -7.27064, -0.211357, 28.143532, -7.324594, -0.29682863, 28.210306, -7.585282, -0.211357, 28.218319, -7.324594, -0.29682863, 28.210306, -7.27064, -0.211357, 28.311537, -7.585282, -0.211357, 28.218319, -7.860928, -0.21135688, 28.491028, -7.585282, -0.211357, 28.218319, -7.27064, -0.211357, 28.311537, -7.860928, -0.21135688, 28.491028, -7.27064, -0.211357, 28.311537, -7.27064, -0.21135688, 28.502779, -7.860928, -0.4351858, 27.887384, -7.27064, -0.21135712, 27.901274, -7.860928, -0.21135712, 27.887384, -7.860928, -0.4351858, 27.887384, -7.27064, -0.4351858, 27.901274, -7.27064, -0.21135712, 27.901274, -12.731207, -0.21135712, 27.994759, -7.860928, -0.4351858, 27.887384, -7.860928, -0.21135712, 27.887384, -12.731207, -0.21135712, 27.994759, -12.731207, -0.4351858, 27.994759, -7.860928, -0.4351858, 27.887384, -7.27064, -0.43518555, 28.502779, -7.860928, -0.4351858, 27.887384, -7.860928, -0.43518555, 28.491028, -7.860928, -0.4351858, 27.887384, -7.27064, -0.43518555, 28.502779, -7.27064, -0.43518567, 28.177721, -7.860928, -0.4351858, 27.887384, -7.27064, -0.43518567, 28.177721, -7.27064, -0.4351858, 27.901274, -12.731207, -0.4351858, 27.994759, -7.860928, -0.43518555, 28.491028, -7.860928, -0.4351858, 27.887384, -12.731207, -0.4351858, 27.994759, -12.731207, -0.43518555, 28.502779, -7.860928, -0.43518555, 28.491028, -7.27064, -0.211357, 28.311537, -7.324594, -0.29682863, 28.210306, -7.27064, -0.43518567, 28.177721, -7.324594, -0.29682863, 28.210306, -7.27064, -0.211357, 28.143532, -7.27064, -0.43518567, 28.177721, -7.27064, -0.43518555, 28.502779, -7.27064, -0.211357, 28.311537, -7.27064, -0.43518567, 28.177721, -7.27064, -0.43518555, 28.502779, -7.27064, -0.21135688, 28.502779, -7.27064, -0.211357, 28.311537, -7.27064, -0.211357, 28.143532, -7.27064, -0.4351858, 27.901274, -7.27064, -0.43518567, 28.177721, -7.27064, -0.211357, 28.143532, -7.27064, -0.21135712, 27.901274, -7.27064, -0.4351858, 27.901274, -12.731207, -0.21135712, 27.994759, -12.731207, -0.43518555, 28.502779, -12.731207, -0.4351858, 27.994759, -12.731207, -0.43518555, 28.502779, -12.731207, -0.21135712, 27.994759, -12.731207, -0.211357, 28.375908, -12.731207, -0.43518555, 28.502779, -12.731207, -0.211357, 28.375908, -12.731207, -0.2674476, 28.502779, -9.958187, -0.43518543, 28.820093, -7.27064, -0.21135688, 28.706842, -9.958187, -0.21135676, 28.820093, -9.958187, -0.43518543, 28.820093, -7.27064, -0.43518555, 28.706842, -7.27064, -0.21135688, 28.706842, -12.731207, -0.21135688, 28.706842, -9.958187, -0.43518543, 28.820093, -9.958187, -0.21135676, 28.820093, -12.731207, -0.21135688, 28.706842, -12.731207, -0.43518555, 28.706842, -9.958187, -0.43518543, 28.820093, -9.958187, -0.4351852, 29.419462, -7.27064, -0.43518555, 28.706842, -9.958187, -0.43518543, 28.820093, -9.958187, -0.4351852, 29.419462, -7.27064, -0.4351853, 29.214865, -7.27064, -0.43518555, 28.706842, -9.958187, -0.43518543, 28.820093, -12.731207, -0.4351852, 29.4005, -9.958187, -0.4351852, 29.419462, -9.958187, -0.43518543, 28.820093, -12.731207, -0.43518543, 29.047928, -12.731207, -0.4351852, 29.4005, -12.731207, -0.43518543, 29.047928, -9.958187, -0.43518543, 28.820093, -12.731207, -0.43518555, 28.706842, -7.27064, -0.4351853, 29.214865, -7.27064, -0.21135688, 28.706842, -7.27064, -0.43518555, 28.706842, -7.27064, -0.4351853, 29.214865, -7.27064, -0.21135664, 29.214865, -7.27064, -0.21135688, 28.706842, -12.731207, -0.21135676, 29.007061, -12.626503, -0.2783984, 29.066626, -12.731207, -0.43518543, 29.047928, -9.958187, -0.21135664, 29.269619, -9.958187, -0.26744723, 29.419462, -10.651308, -0.21135652, 29.414654, -9.958187, -0.21135664, 29.269619, -9.286167, -0.21135664, 29.368446, -9.958187, -0.26744723, 29.419462, -9.958187, -0.21135664, 29.269619, -7.27064, -0.21135664, 29.214865, -9.286167, -0.21135664, 29.368446, -9.958187, -0.21135664, 29.269619, -7.27064, -0.21135688, 28.706842, -7.27064, -0.21135664, 29.214865, -7.27064, -0.21135688, 28.706842, -9.958187, -0.21135664, 29.269619, -9.958187, -0.21135676, 28.820093, -10.651308, -0.21135652, 29.414654, -9.958187, -0.21135676, 28.820093, -9.958187, -0.21135664, 29.269619, -9.958187, -0.21135676, 28.820093, -12.731207, -0.21135676, 29.007061, -12.731207, -0.21135688, 28.706842, -9.958187, -0.21135676, 28.820093, -12.254702, -0.21135664, 29.068228, -12.731207, -0.21135676, 29.007061, -10.651308, -0.21135652, 29.414654, -12.254702, -0.21135664, 29.068228, -9.958187, -0.21135676, 28.820093, -12.626503, -0.2783984, 29.066626, -12.731207, -0.21135676, 29.007061, -12.254702, -0.21135664, 29.068228, -12.731207, -0.21135664, 29.176136, -12.626503, -0.2783984, 29.066626, -12.254702, -0.21135664, 29.068228, -12.254702, -0.21135664, 29.068228, -10.651308, -0.21135652, 29.414654, -12.731207, -0.21135664, 29.176136, -12.731207, -0.21135664, 29.176136, -10.651308, -0.21135652, 29.414654, -12.731207, -0.21135652, 29.400497, -12.731207, -0.43518555, 28.706842, -12.731207, -0.21135676, 29.007061, -12.731207, -0.43518543, 29.047928, -12.731207, -0.43518555, 28.706842, -12.731207, -0.21135688, 28.706842, -12.731207, -0.21135676, 29.007061, -12.731207, -0.21135664, 29.176136, -12.731207, -0.4351852, 29.4005, -12.731207, -0.43518543, 29.047928, -12.731207, -0.21135664, 29.176136, -12.731207, -0.21135652, 29.400497, -12.731207, -0.4351852, 29.4005, -12.626503, -0.2783984, 29.066626, -12.731207, -0.21135664, 29.176136, -12.731207, -0.43518543, 29.047928, -12.731207, -0.21135652, 29.400497, -9.958187, -0.4351852, 29.419462, -12.731207, -0.4351852, 29.4005, -9.958187, -0.4351852, 29.419462, -12.731207, -0.21135652, 29.400497, -10.651308, -0.21135652, 29.414654, -9.958187, -0.4351852, 29.419462, -10.651308, -0.21135652, 29.414654, -9.958187, -0.26744723, 29.419462, -9.958187, -0.26744723, 29.419462, -7.27064, -0.4351853, 29.214865, -9.958187, -0.4351852, 29.419462, -9.958187, -0.26744723, 29.419462, -7.27064, -0.21135664, 29.214865, -7.27064, -0.4351853, 29.214865, -7.27064, -0.21135664, 29.214865, -9.958187, -0.26744723, 29.419462, -9.286167, -0.21135664, 29.368446, -8.00062, -0.21135628, 30.203398, -7.448795, -0.43518496, 30.215149, -8.00062, -0.43518496, 30.203398, -8.00062, -0.21135628, 30.203398, -7.448795, -0.21135628, 30.215149, -7.448795, -0.43518496, 30.215149, -8.00062, -0.43518496, 30.203398, -11.415211, -0.21135628, 30.212212, -8.00062, -0.21135628, 30.203398, -8.00062, -0.43518496, 30.203398, -12.553318, -0.267447, 30.215149, -11.415211, -0.21135628, 30.212212, -12.553318, -0.267447, 30.215149, -8.00062, -0.43518496, 30.203398, -12.553318, -0.43518496, 30.215149, -7.448795, -0.21135652, 29.613644, -8.00062, -0.21135628, 30.203398, -8.00062, -0.21135652, 29.599754, -11.415211, -0.21135628, 30.212212, -8.00062, -0.21135652, 29.599754, -8.00062, -0.21135628, 30.203398, -11.415211, -0.21135628, 30.212212, -12.553318, -0.21135652, 29.707129, -8.00062, -0.21135652, 29.599754, -12.553318, -0.21135652, 29.707129, -11.415211, -0.21135628, 30.212212, -12.553318, -0.21135628, 30.088278, -12.553318, -0.21135628, 30.088278, -11.415211, -0.21135628, 30.212212, -12.553318, -0.267447, 30.215149, -8.00062, -0.21135628, 30.203398, -7.448795, -0.21135652, 29.613644, -7.74287, -0.2113564, 29.930422, -7.74287, -0.2113564, 29.930422, -7.448795, -0.21135652, 29.613644, -7.448795, -0.2113564, 29.855902, -7.448795, -0.2113564, 29.855902, -7.4990096, -0.29682803, 29.922676, -7.74287, -0.2113564, 29.930422, -7.4990096, -0.29682803, 29.922676, -7.448795, -0.2113564, 30.023907, -7.74287, -0.2113564, 29.930422, -8.00062, -0.21135628, 30.203398, -7.74287, -0.2113564, 29.930422, -7.448795, -0.2113564, 30.023907, -8.00062, -0.21135628, 30.203398, -7.448795, -0.2113564, 30.023907, -7.448795, -0.21135628, 30.215149, -8.00062, -0.4351852, 29.599754, -7.448795, -0.21135652, 29.613644, -8.00062, -0.21135652, 29.599754, -8.00062, -0.4351852, 29.599754, -7.448795, -0.4351852, 29.613644, -7.448795, -0.21135652, 29.613644, -12.553318, -0.21135652, 29.707129, -8.00062, -0.4351852, 29.599754, -8.00062, -0.21135652, 29.599754, -12.553318, -0.21135652, 29.707129, -12.553318, -0.4351852, 29.707129, -8.00062, -0.4351852, 29.599754, -7.448795, -0.43518496, 30.215149, -8.00062, -0.4351852, 29.599754, -8.00062, -0.43518496, 30.203398, -8.00062, -0.4351852, 29.599754, -7.448795, -0.43518496, 30.215149, -7.448795, -0.43518507, 29.889824, -8.00062, -0.4351852, 29.599754, -7.448795, -0.43518507, 29.889824, -7.448795, -0.4351852, 29.613644, -12.553318, -0.4351852, 29.707129, -8.00062, -0.43518496, 30.203398, -8.00062, -0.4351852, 29.599754, -12.553318, -0.4351852, 29.707129, -12.553318, -0.43518496, 30.215149, -8.00062, -0.43518496, 30.203398, -7.448795, -0.2113564, 30.023907, -7.4990096, -0.29682803, 29.922676, -7.448795, -0.43518507, 29.889824, -7.4990096, -0.29682803, 29.922676, -7.448795, -0.2113564, 29.855902, -7.448795, -0.43518507, 29.889824, -7.448795, -0.43518496, 30.215149, -7.448795, -0.2113564, 30.023907, -7.448795, -0.43518507, 29.889824, -7.448795, -0.43518496, 30.215149, -7.448795, -0.21135628, 30.215149, -7.448795, -0.2113564, 30.023907, -7.448795, -0.2113564, 29.855902, -7.448795, -0.4351852, 29.613644, -7.448795, -0.43518507, 29.889824, -7.448795, -0.2113564, 29.855902, -7.448795, -0.21135652, 29.613644, -7.448795, -0.4351852, 29.613644, -12.553318, -0.21135652, 29.707129, -12.553318, -0.43518496, 30.215149, -12.553318, -0.4351852, 29.707129, -12.553318, -0.43518496, 30.215149, -12.553318, -0.21135652, 29.707129, -12.553318, -0.21135628, 30.088278, -12.553318, -0.43518496, 30.215149, -12.553318, -0.21135628, 30.088278, -12.553318, -0.267447, 30.215149, -7.953344, -0.54656744, 23.88491, -7.5551, 2.3477144, 23.836563, -7.5067554, -0.54656744, 23.88491, -7.953344, -0.54656744, 23.88491, -7.9049993, 2.3477144, 23.836563, -7.5551, 2.3477144, 23.836563, -7.9049993, 2.3477144, 23.836563, -7.5551, 2.3477144, 23.486397, -7.5551, 2.3477144, 23.836563, -7.9049993, 2.3477144, 23.836563, -7.9049993, 2.3477144, 23.486397, -7.5551, 2.3477144, 23.486397, -7.9049993, 2.3477144, 23.486397, -7.5067554, -0.54656756, 23.438053, -7.5551, 2.3477144, 23.486397, -7.9049993, 2.3477144, 23.486397, -7.953344, -0.54656756, 23.438053, -7.5067554, -0.54656756, 23.438053, -7.953344, -0.54656756, 23.438053, -7.5067554, -0.54656744, 23.88491, -7.5067554, -0.54656756, 23.438053, -7.953344, -0.54656756, 23.438053, -7.953344, -0.54656744, 23.88491, -7.5067554, -0.54656744, 23.88491, -7.5067554, -0.54656744, 23.88491, -7.5551, 2.3477144, 23.486397, -7.5067554, -0.54656756, 23.438053, -7.5067554, -0.54656744, 23.88491, -7.5551, 2.3477144, 23.836563, -7.5551, 2.3477144, 23.486397, -7.953344, -0.54656756, 23.438053, -7.9049993, 2.3477144, 23.836563, -7.953344, -0.54656744, 23.88491, -7.953344, -0.54656756, 23.438053, -7.9049993, 2.3477144, 23.486397, -7.9049993, 2.3477144, 23.836563, -12.768333, -0.54656434, 32.394943, -12.321743, 2.3477175, 32.39494, -12.321743, -0.54656434, 32.394943, -12.768333, -0.54656434, 32.394943, -12.768333, 2.3477175, 32.39494, -12.321743, 2.3477175, 32.39494, -12.768333, -0.5465646, 31.948353, -12.321743, -0.54656434, 32.394943, -12.321743, -0.5465646, 31.948353, -12.768333, -0.5465646, 31.948353, -12.768333, -0.54656434, 32.394943, -12.321743, -0.54656434, 32.394943, -12.321743, -0.54656434, 32.394943, -12.321743, 2.3477173, 31.948353, -12.321743, -0.5465646, 31.948353, -12.321743, -0.54656434, 32.394943, -12.321743, 2.3477175, 32.39494, -12.321743, 2.3477173, 31.948353, -12.768333, 1.6241468, 31.948353, -12.656685, 2.3477173, 31.948353, -12.768333, 2.3477173, 32.059998, -12.768333, -0.54656434, 32.394943, -12.768333, 1.6241468, 31.948353, -12.768333, 2.3477173, 32.059998, -12.768333, 1.6241468, 31.948353, -12.768333, -0.54656434, 32.394943, -12.768333, -0.5465646, 31.948353, -12.768333, -0.54656434, 32.394943, -12.768333, 2.3477173, 32.059998, -12.768333, 2.3477175, 32.39494, -12.768333, 2.3477173, 32.059998, -12.321743, 2.3477175, 32.39494, -12.768333, 2.3477175, 32.39494, -12.321743, 2.3477175, 32.39494, -12.768333, 2.3477173, 32.059998, -12.656685, 2.3477173, 31.948353, -12.321743, 2.3477175, 32.39494, -12.656685, 2.3477173, 31.948353, -12.321743, 2.3477173, 31.948353, -12.768333, -0.5465646, 31.948353, -12.656685, 2.3477173, 31.948353, -12.768333, 1.6241468, 31.948353, -12.656685, 2.3477173, 31.948353, -12.768333, -0.5465646, 31.948353, -12.321743, -0.5465646, 31.948353, -12.656685, 2.3477173, 31.948353, -12.321743, -0.5465646, 31.948353, -12.321743, 2.3477173, 31.948353, -9.960857, -0.4351846, 31.335094, -7.448795, -0.21135592, 31.221577, -9.960857, -0.21135592, 31.335094, -9.960857, -0.4351846, 31.335094, -7.448795, -0.4351846, 31.221577, -7.448795, -0.21135592, 31.221577, -12.553318, -0.21135592, 31.221577, -9.960857, -0.4351846, 31.335094, -9.960857, -0.21135592, 31.335094, -12.553318, -0.21135592, 31.221577, -12.553318, -0.4351846, 31.221577, -9.960857, -0.4351846, 31.335094, -9.960857, -0.43518436, 31.934464, -7.448795, -0.4351846, 31.221577, -9.960857, -0.4351846, 31.335094, -9.960857, -0.43518436, 31.934464, -7.448795, -0.43518436, 31.729599, -7.448795, -0.4351846, 31.221577, -9.960857, -0.4351846, 31.335094, -12.553318, -0.43518436, 31.9155, -9.960857, -0.43518436, 31.934464, -9.960857, -0.4351846, 31.335094, -12.553318, -0.43518448, 31.562662, -12.553318, -0.43518436, 31.9155, -12.553318, -0.43518448, 31.562662, -9.960857, -0.4351846, 31.335094, -12.553318, -0.4351846, 31.221577, -7.448795, -0.43518436, 31.729599, -7.448795, -0.21135592, 31.221577, -7.448795, -0.4351846, 31.221577, -7.448795, -0.43518436, 31.729599, -7.448795, -0.21135569, 31.729599, -7.448795, -0.21135592, 31.221577, -12.553318, -0.2113558, 31.521797, -12.455294, -0.27839756, 31.58136, -12.553318, -0.43518448, 31.562662, -9.960857, -0.21135569, 31.784622, -9.960857, -0.2674464, 31.934464, -10.608839, -0.21135569, 31.929657, -9.960857, -0.21135569, 31.784622, -9.332909, -0.21135569, 31.883448, -9.960857, -0.2674464, 31.934464, -9.960857, -0.21135569, 31.784622, -7.448795, -0.21135569, 31.729599, -9.332909, -0.21135569, 31.883448, -9.960857, -0.21135569, 31.784622, -7.448795, -0.21135592, 31.221577, -7.448795, -0.21135569, 31.729599, -7.448795, -0.21135592, 31.221577, -9.960857, -0.21135569, 31.784622, -9.960857, -0.21135592, 31.335094, -10.608839, -0.21135569, 31.929657, -9.960857, -0.21135592, 31.335094, -9.960857, -0.21135569, 31.784622, -9.960857, -0.21135592, 31.335094, -12.553318, -0.2113558, 31.521797, -12.553318, -0.21135592, 31.221577, -9.960857, -0.21135592, 31.335094, -12.107531, -0.2113558, 31.58323, -12.553318, -0.2113558, 31.521797, -10.608839, -0.21135569, 31.929657, -12.107531, -0.2113558, 31.58323, -9.960857, -0.21135592, 31.335094, -12.455294, -0.27839756, 31.58136, -12.553318, -0.2113558, 31.521797, -12.107531, -0.2113558, 31.58323, -12.553318, -0.2113558, 31.691137, -12.455294, -0.27839756, 31.58136, -12.107531, -0.2113558, 31.58323, -12.107531, -0.2113558, 31.58323, -10.608839, -0.21135569, 31.929657, -12.553318, -0.2113558, 31.691137, -12.553318, -0.2113558, 31.691137, -10.608839, -0.21135569, 31.929657, -12.553318, -0.21135569, 31.9155, -12.553318, -0.4351846, 31.221577, -12.553318, -0.2113558, 31.521797, -12.553318, -0.43518448, 31.562662, -12.553318, -0.4351846, 31.221577, -12.553318, -0.21135592, 31.221577, -12.553318, -0.2113558, 31.521797, -12.553318, -0.2113558, 31.691137, -12.553318, -0.43518436, 31.9155, -12.553318, -0.43518448, 31.562662, -12.553318, -0.2113558, 31.691137, -12.553318, -0.21135569, 31.9155, -12.553318, -0.43518436, 31.9155, -12.455294, -0.27839756, 31.58136, -12.553318, -0.2113558, 31.691137, -12.553318, -0.43518448, 31.562662, -12.553318, -0.21135569, 31.9155, -9.960857, -0.43518436, 31.934464, -12.553318, -0.43518436, 31.9155, -9.960857, -0.43518436, 31.934464, -12.553318, -0.21135569, 31.9155, -10.608839, -0.21135569, 31.929657, -9.960857, -0.43518436, 31.934464, -10.608839, -0.21135569, 31.929657, -9.960857, -0.2674464, 31.934464, -9.960857, -0.2674464, 31.934464, -7.448795, -0.43518436, 31.729599, -9.960857, -0.43518436, 31.934464, -9.960857, -0.2674464, 31.934464, -7.448795, -0.21135569, 31.729599, -7.448795, -0.43518436, 31.729599, -7.448795, -0.21135569, 31.729599, -9.960857, -0.2674464, 31.934464, -9.332909, -0.21135569, 31.883448, -11.675098, -0.43518627, 26.44131, -7.3299356, -0.21135771, 26.343554, -11.675098, -0.2113576, 26.44131, -11.675098, -0.43518627, 26.44131, -7.3299356, -0.4351864, 26.343554, -7.3299356, -0.21135771, 26.343554, -12.671911, -0.2113576, 26.437038, -11.675098, -0.43518627, 26.44131, -11.675098, -0.2113576, 26.44131, -12.671911, -0.2113576, 26.437038, -12.671911, -0.43518627, 26.437038, -11.675098, -0.43518627, 26.44131, -11.675098, -0.43518603, 27.058844, -7.3299356, -0.4351864, 26.343554, -11.675098, -0.43518627, 26.44131, -11.675098, -0.43518603, 27.058844, -7.3299356, -0.43518615, 26.94506, -7.3299356, -0.4351864, 26.343554, -12.671911, -0.43518627, 26.437038, -11.675098, -0.43518603, 27.058844, -11.675098, -0.43518627, 26.44131, -12.671911, -0.43518627, 26.437038, -12.671911, -0.43518615, 26.94506, -11.675098, -0.43518603, 27.058844, -12.671911, -0.43518627, 26.437038, -12.671911, -0.21135747, 26.94506, -12.671911, -0.43518615, 26.94506, -12.671911, -0.43518627, 26.437038, -12.671911, -0.2113576, 26.437038, -12.671911, -0.21135747, 26.94506, -11.675098, -0.21135747, 26.90446, -11.675098, -0.26744807, 27.058844, -11.924301, -0.21135747, 27.030264, -11.675098, -0.21135747, 26.90446, -10.588808, -0.21135747, 27.030264, -11.675098, -0.26744807, 27.058844, -11.675098, -0.21135747, 26.90446, -8.144854, -0.21135747, 26.966427, -10.588808, -0.21135747, 27.030264, -11.924301, -0.21135747, 27.030264, -11.675098, -0.2113576, 26.44131, -11.675098, -0.21135747, 26.90446, -11.924301, -0.21135747, 27.030264, -12.671911, -0.2113576, 26.437038, -11.675098, -0.2113576, 26.44131, -12.671911, -0.2113576, 26.437038, -11.924301, -0.21135747, 27.030264, -12.671911, -0.21135747, 26.94506, -7.3299356, -0.21135771, 26.343554, -11.675098, -0.21135747, 26.90446, -11.675098, -0.2113576, 26.44131, -11.675098, -0.21135747, 26.90446, -7.3299356, -0.21135771, 26.343554, -8.144854, -0.21135747, 26.966427, -8.144854, -0.21135747, 26.966427, -7.3299356, -0.21135771, 26.343554, -7.3299356, -0.21135747, 26.794683, -8.144854, -0.21135747, 26.966427, -7.3299356, -0.21135747, 26.794683, -7.3299356, -0.2674482, 26.94506, -12.671911, -0.21135747, 26.94506, -11.675098, -0.43518603, 27.058844, -12.671911, -0.43518615, 26.94506, -11.675098, -0.43518603, 27.058844, -12.671911, -0.21135747, 26.94506, -11.924301, -0.21135747, 27.030264, -11.675098, -0.43518603, 27.058844, -11.924301, -0.21135747, 27.030264, -11.675098, -0.26744807, 27.058844, -11.675098, -0.26744807, 27.058844, -7.3299356, -0.43518615, 26.94506, -11.675098, -0.43518603, 27.058844, -11.675098, -0.26744807, 27.058844, -8.144854, -0.21135747, 26.966427, -7.3299356, -0.43518615, 26.94506, -7.3299356, -0.43518615, 26.94506, -8.144854, -0.21135747, 26.966427, -7.3299356, -0.2674482, 26.94506, -8.144854, -0.21135747, 26.966427, -11.675098, -0.26744807, 27.058844, -10.588808, -0.21135747, 27.030264, -7.3299356, -0.2674482, 26.94506, -7.3299356, -0.4351864, 26.343554, -7.3299356, -0.43518615, 26.94506, -7.3299356, -0.4351864, 26.343554, -7.3299356, -0.2674482, 26.94506, -7.3299356, -0.21135747, 26.794683, -7.3299356, -0.4351864, 26.343554, -7.3299356, -0.21135747, 26.794683, -7.3299356, -0.21135771, 26.343554, -11.712225, -0.43518484, 30.508156, -7.27064, -0.21135616, 30.410398, -11.712225, -0.21135616, 30.508156, -11.712225, -0.43518484, 30.508156, -7.27064, -0.43518484, 30.410398, -7.27064, -0.21135616, 30.410398, -12.731207, -0.21135616, 30.503883, -11.712225, -0.43518484, 30.508156, -11.712225, -0.21135616, 30.508156, -12.731207, -0.21135616, 30.503883, -12.731207, -0.43518484, 30.503883, -11.712225, -0.43518484, 30.508156, -11.712225, -0.4351846, 31.125422, -7.27064, -0.43518484, 30.410398, -11.712225, -0.43518484, 30.508156, -11.712225, -0.4351846, 31.125422, -7.27064, -0.43518472, 31.011906, -7.27064, -0.43518484, 30.410398, -12.731207, -0.43518484, 30.503883, -11.712225, -0.4351846, 31.125422, -11.712225, -0.43518484, 30.508156, -12.731207, -0.43518484, 30.503883, -12.731207, -0.43518472, 31.011906, -11.712225, -0.4351846, 31.125422, -12.731207, -0.43518484, 30.503883, -12.731207, -0.21135604, 31.011906, -12.731207, -0.43518472, 31.011906, -12.731207, -0.43518484, 30.503883, -12.731207, -0.21135616, 30.503883, -12.731207, -0.21135604, 31.011906, -11.712225, -0.21135604, 30.971306, -11.712225, -0.26744664, 31.125422, -11.967037, -0.21135592, 31.09711, -11.712225, -0.21135604, 30.971306, -10.601894, -0.21135592, 31.09711, -11.712225, -0.26744664, 31.125422, -11.712225, -0.21135604, 30.971306, -8.103455, -0.21135604, 31.033274, -10.601894, -0.21135592, 31.09711, -11.967037, -0.21135592, 31.09711, -11.712225, -0.21135616, 30.508156, -11.712225, -0.21135604, 30.971306, -11.967037, -0.21135592, 31.09711, -12.731207, -0.21135616, 30.503883, -11.712225, -0.21135616, 30.508156, -12.731207, -0.21135616, 30.503883, -11.967037, -0.21135592, 31.09711, -12.731207, -0.21135604, 31.011906, -7.27064, -0.21135616, 30.410398, -11.712225, -0.21135604, 30.971306, -11.712225, -0.21135616, 30.508156, -11.712225, -0.21135604, 30.971306, -7.27064, -0.21135616, 30.410398, -8.103455, -0.21135604, 31.033274, -8.103455, -0.21135604, 31.033274, -7.27064, -0.21135616, 30.410398, -7.27064, -0.21135604, 30.861528, -8.103455, -0.21135604, 31.033274, -7.27064, -0.21135604, 30.861528, -7.27064, -0.26744676, 31.011906, -12.731207, -0.21135604, 31.011906, -11.712225, -0.4351846, 31.125422, -12.731207, -0.43518472, 31.011906, -11.712225, -0.4351846, 31.125422, -12.731207, -0.21135604, 31.011906, -11.967037, -0.21135592, 31.09711, -11.712225, -0.4351846, 31.125422, -11.967037, -0.21135592, 31.09711, -11.712225, -0.26744664, 31.125422, -11.712225, -0.26744664, 31.125422, -7.27064, -0.43518472, 31.011906, -11.712225, -0.4351846, 31.125422, -11.712225, -0.26744664, 31.125422, -8.103455, -0.21135604, 31.033274, -7.27064, -0.43518472, 31.011906, -7.27064, -0.43518472, 31.011906, -8.103455, -0.21135604, 31.033274, -7.27064, -0.26744676, 31.011906, -8.103455, -0.21135604, 31.033274, -11.712225, -0.26744664, 31.125422, -10.601894, -0.21135592, 31.09711, -7.27064, -0.26744676, 31.011906, -7.27064, -0.43518484, 30.410398, -7.27064, -0.43518472, 31.011906, -7.27064, -0.43518484, 30.410398, -7.27064, -0.26744676, 31.011906, -7.27064, -0.21135604, 30.861528, -7.27064, -0.43518484, 30.410398, -7.27064, -0.21135604, 30.861528, -7.27064, -0.21135616, 30.410398, -7.871612, -0.54656446, 32.32149, -7.4250226, 2.3477173, 32.321487, -7.4250226, -0.54656446, 32.32149, -7.871612, -0.54656446, 32.32149, -7.871612, 2.0119743, 32.321487, -7.4250226, 2.3477173, 32.321487, -7.871612, 2.0119743, 32.321487, -7.4250226, 2.3477173, 31.874634, -7.4250226, 2.3477173, 32.321487, -7.871612, 2.0119743, 32.321487, -7.871612, 2.3477173, 31.874634, -7.4250226, 2.3477173, 31.874634, -7.871612, 2.3477173, 31.874634, -7.4250226, -0.5465646, 31.874634, -7.4250226, 2.3477173, 31.874634, -7.871612, 2.3477173, 31.874634, -7.871612, -0.5465646, 31.874634, -7.4250226, -0.5465646, 31.874634, -7.871612, -0.5465646, 31.874634, -7.4250226, -0.54656446, 32.32149, -7.4250226, -0.5465646, 31.874634, -7.871612, -0.5465646, 31.874634, -7.871612, -0.54656446, 32.32149, -7.4250226, -0.54656446, 32.32149, -7.4250226, -0.54656446, 32.32149, -7.4250226, 2.3477173, 31.874634, -7.4250226, -0.5465646, 31.874634, -7.4250226, -0.54656446, 32.32149, -7.4250226, 2.3477173, 32.321487, -7.4250226, 2.3477173, 31.874634, -7.871612, -0.5465646, 31.874634, -7.871612, 2.0119743, 32.321487, -7.871612, -0.54656446, 32.32149, -7.871612, -0.5465646, 31.874634, -7.871612, 2.3477173, 31.874634, -7.871612, 2.0119743, 32.321487, -7.7832026, -0.55564594, 31.95423, -7.641372, -0.4987539, 31.95423, -7.641372, -0.55564594, 31.95423, -7.7832026, -0.55564594, 31.95423, -7.7832026, -0.4987539, 31.95423, -7.641372, -0.4987539, 31.95423, -7.7832026, -0.4987539, 31.95423, -7.641372, -0.49875677, 23.85259, -7.641372, -0.4987539, 31.95423, -7.7832026, -0.4987539, 31.95423, -7.7832026, -0.49875677, 23.85259, -7.641372, -0.49875677, 23.85259, -7.7832026, -0.49875677, 23.85259, -7.641372, -0.5556488, 23.85259, -7.641372, -0.49875677, 23.85259, -7.7832026, -0.49875677, 23.85259, -7.7832026, -0.5556488, 23.85259, -7.641372, -0.5556488, 23.85259, -7.7832026, -0.5556488, 23.85259, -7.641372, -0.55564594, 31.95423, -7.641372, -0.5556488, 23.85259, -7.7832026, -0.5556488, 23.85259, -7.7832026, -0.55564594, 31.95423, -7.641372, -0.55564594, 31.95423, -7.641372, -0.55564594, 31.95423, -7.641372, -0.49875677, 23.85259, -7.641372, -0.5556488, 23.85259, -7.641372, -0.55564594, 31.95423, -7.641372, -0.4987539, 31.95423, -7.641372, -0.49875677, 23.85259, -7.7832026, -0.5556488, 23.85259, -7.7832026, -0.4987539, 31.95423, -7.7832026, -0.55564594, 31.95423, -7.7832026, -0.5556488, 23.85259, -7.7832026, -0.49875677, 23.85259, -7.7832026, -0.4987539, 31.95423, -12.288357, -0.46670425, 25.416721, -12.258175, -0.4872712, 24.820557, -12.258175, -0.4942155, 25.426603, -12.288357, -0.46670425, 25.416721, -12.288357, -0.46002698, 24.831242, -12.258175, -0.4872712, 24.820557, -12.288357, -0.46002698, 24.831242, -12.258175, -0.3107189, 24.797853, -12.258175, -0.4872712, 24.820557, -12.288357, -0.46002698, 24.831242, -12.288357, -0.31793058, 24.822159, -12.258175, -0.3107189, 24.797853, -12.258175, -0.4942155, 25.426603, -12.288357, -0.5145153, 24.81014, -12.288357, -0.52199376, 25.436485, -12.258175, -0.4942155, 25.426603, -12.258175, -0.4872712, 24.820557, -12.288357, -0.5145153, 24.81014, -12.258175, -0.4872712, 24.820557, -12.288357, -0.30377424, 24.774082, -12.288357, -0.5145153, 24.81014, -12.258175, -0.4872712, 24.820557, -12.258175, -0.3107189, 24.797853, -12.288357, -0.30377424, 24.774082, -12.288357, -0.52199376, 25.436485, -12.318539, -0.4872712, 24.820557, -12.318539, -0.4942155, 25.426603, -12.288357, -0.52199376, 25.436485, -12.288357, -0.5145153, 24.81014, -12.318539, -0.4872712, 24.820557, -12.288357, -0.5145153, 24.81014, -12.318539, -0.3107189, 24.797853, -12.318539, -0.4872712, 24.820557, -12.288357, -0.5145153, 24.81014, -12.288357, -0.30377424, 24.774082, -12.318539, -0.3107189, 24.797853, -12.318539, -0.4942155, 25.426603, -12.288357, -0.46002698, 24.831242, -12.288357, -0.46670425, 25.416721, -12.318539, -0.4942155, 25.426603, -12.318539, -0.4872712, 24.820557, -12.288357, -0.46002698, 24.831242, -12.318539, -0.4872712, 24.820557, -12.288357, -0.31793058, 24.822159, -12.288357, -0.46002698, 24.831242, -12.318539, -0.4872712, 24.820557, -12.318539, -0.3107189, 24.797853, -12.288357, -0.31793058, 24.822159, -12.288357, -0.31793058, 24.822159, -12.258175, -0.16301334, 24.835247, -12.258175, -0.3107189, 24.797853, -12.288357, -0.31793058, 24.822159, -12.288357, -0.19239414, 24.84059, -12.258175, -0.16301334, 24.835247, -12.288357, -0.19239414, 24.84059, -12.258175, -0.16007495, 25.421795, -12.258175, -0.16301334, 24.835247, -12.288357, -0.19239414, 24.84059, -12.288357, -0.18918872, 25.41592, -12.258175, -0.16007495, 25.421795, -12.288357, -0.18918872, 25.41592, -12.258175, -0.32407355, 25.476551, -12.258175, -0.16007495, 25.421795, -12.288357, -0.18918872, 25.41592, -12.288357, -0.33021688, 25.452246, -12.258175, -0.32407355, 25.476551, -12.258175, -0.3107189, 24.797853, -12.288357, -0.13363242, 24.829905, -12.288357, -0.30377424, 24.774082, -12.258175, -0.3107189, 24.797853, -12.258175, -0.16301334, 24.835247, -12.288357, -0.13363242, 24.829905, -12.258175, -0.16301334, 24.835247, -12.288357, -0.13069403, 25.427671, -12.288357, -0.13363242, 24.829905, -12.258175, -0.16301334, 24.835247, -12.258175, -0.16007495, 25.421795, -12.288357, -0.13069403, 25.427671, -12.258175, -0.16007495, 25.421795, -12.288357, -0.31819737, 25.500856, -12.288357, -0.13069403, 25.427671, -12.258175, -0.16007495, 25.421795, -12.258175, -0.32407355, 25.476551, -12.288357, -0.31819737, 25.500856, -12.288357, -0.30377424, 24.774082, -12.318539, -0.16301334, 24.835247, -12.318539, -0.3107189, 24.797853, -12.288357, -0.30377424, 24.774082, -12.288357, -0.13363242, 24.829905, -12.318539, -0.16301334, 24.835247, -12.318539, -0.3107189, 24.797853, -12.288357, -0.19239414, 24.84059, -12.288357, -0.31793058, 24.822159, -12.318539, -0.3107189, 24.797853, -12.318539, -0.16301334, 24.835247, -12.288357, -0.19239414, 24.84059, -12.318539, -0.16301334, 24.835247, -12.288357, -0.18918872, 25.41592, -12.288357, -0.19239414, 24.84059, -12.318539, -0.16301334, 24.835247, -12.318539, -0.16007495, 25.421795, -12.288357, -0.18918872, 25.41592, -12.318539, -0.16007495, 25.421795, -12.288357, -0.33021688, 25.452246, -12.288357, -0.18918872, 25.41592, -12.318539, -0.16007495, 25.421795, -12.318539, -0.32407355, 25.476551, -12.288357, -0.33021688, 25.452246, -12.288357, -0.13363242, 24.829905, -12.318539, -0.16007495, 25.421795, -12.318539, -0.16301334, 24.835247, -12.288357, -0.13363242, 24.829905, -12.288357, -0.13069403, 25.427671, -12.318539, -0.16007495, 25.421795, -12.288357, -0.13069403, 25.427671, -12.318539, -0.32407355, 25.476551, -12.318539, -0.16007495, 25.421795, -12.288357, -0.13069403, 25.427671, -12.288357, -0.31819737, 25.500856, -12.318539, -0.32407355, 25.476551, -12.288357, -0.33021688, 25.452246, -12.258175, -0.4942155, 25.426603, -12.258175, -0.32407355, 25.476551, -12.288357, -0.33021688, 25.452246, -12.288357, -0.46670425, 25.416721, -12.258175, -0.4942155, 25.426603, -12.258175, -0.32407355, 25.476551, -12.288357, -0.52199376, 25.436485, -12.288357, -0.31819737, 25.500856, -12.258175, -0.32407355, 25.476551, -12.258175, -0.4942155, 25.426603, -12.288357, -0.52199376, 25.436485, -12.288357, -0.31819737, 25.500856, -12.318539, -0.4942155, 25.426603, -12.318539, -0.32407355, 25.476551, -12.288357, -0.31819737, 25.500856, -12.288357, -0.52199376, 25.436485, -12.318539, -0.4942155, 25.426603, -12.318539, -0.32407355, 25.476551, -12.288357, -0.46670425, 25.416721, -12.288357, -0.33021688, 25.452246, -12.318539, -0.32407355, 25.476551, -12.318539, -0.4942155, 25.426603, -12.288357, -0.46670425, 25.416721, -7.861996, -0.45067835, 25.399893, -7.8352866, -0.4688413, 24.75752, -7.8352866, -0.47525144, 25.410578, -7.861996, -0.45067835, 25.399893, -7.861996, -0.4448024, 24.769007, -7.8352866, -0.4688413, 24.75752, -7.861996, -0.4448024, 24.769007, -7.8352866, -0.31258857, 24.733215, -7.8352866, -0.4688413, 24.75752, -7.861996, -0.4448024, 24.769007, -7.861996, -0.31899893, 24.759125, -7.8352866, -0.31258857, 24.733215, -7.8352866, -0.47525144, 25.410578, -7.861996, -0.49314737, 24.74657, -7.861996, -0.49982452, 25.42126, -7.8352866, -0.47525144, 25.410578, -7.8352866, -0.4688413, 24.75752, -7.861996, -0.49314737, 24.74657, -7.8352866, -0.4688413, 24.75752, -7.861996, -0.30617833, 24.707306, -7.861996, -0.49314737, 24.74657, -7.8352866, -0.4688413, 24.75752, -7.8352866, -0.31258857, 24.733215, -7.861996, -0.30617833, 24.707306, -7.861996, -0.49982452, 25.42126, -7.8887057, -0.4688413, 24.75752, -7.8887057, -0.47525144, 25.410578, -7.861996, -0.49982452, 25.42126, -7.861996, -0.49314737, 24.74657, -7.8887057, -0.4688413, 24.75752, -7.861996, -0.49314737, 24.74657, -7.8887057, -0.31258857, 24.733215, -7.8887057, -0.4688413, 24.75752, -7.861996, -0.49314737, 24.74657, -7.861996, -0.30617833, 24.707306, -7.8887057, -0.31258857, 24.733215, -7.8887057, -0.47525144, 25.410578, -7.861996, -0.4448024, 24.769007, -7.861996, -0.45067835, 25.399893, -7.8887057, -0.47525144, 25.410578, -7.8887057, -0.4688413, 24.75752, -7.861996, -0.4448024, 24.769007, -7.8887057, -0.4688413, 24.75752, -7.861996, -0.31899893, 24.759125, -7.861996, -0.4448024, 24.769007, -7.8887057, -0.4688413, 24.75752, -7.8887057, -0.31258857, 24.733215, -7.861996, -0.31899893, 24.759125, -7.861996, -0.31899893, 24.759125, -7.8352866, -0.1814431, 24.773548, -7.8352866, -0.31258857, 24.733215, -7.861996, -0.31899893, 24.759125, -7.861996, -0.20761883, 24.779156, -7.8352866, -0.1814431, 24.773548, -7.861996, -0.20761883, 24.779156, -7.8352866, -0.179039, 25.405235, -7.8352866, -0.1814431, 24.773548, -7.861996, -0.20761883, 24.779156, -7.861996, -0.20494759, 25.399092, -7.8352866, -0.179039, 25.405235, -7.861996, -0.20494759, 25.399092, -7.8352866, -0.3243407, 25.464264, -7.8352866, -0.179039, 25.405235, -7.861996, -0.20494759, 25.399092, -7.861996, -0.3296826, 25.43809, -7.8352866, -0.3243407, 25.464264, -7.8352866, -0.31258857, 24.733215, -7.861996, -0.15553463, 24.767672, -7.861996, -0.30617833, 24.707306, -7.8352866, -0.31258857, 24.733215, -7.8352866, -0.1814431, 24.773548, -7.861996, -0.15553463, 24.767672, -7.8352866, -0.1814431, 24.773548, -7.861996, -0.15286326, 25.411646, -7.861996, -0.15553463, 24.767672, -7.8352866, -0.1814431, 24.773548, -7.8352866, -0.179039, 25.405235, -7.861996, -0.15286326, 25.411646, -7.8352866, -0.179039, 25.405235, -7.861996, -0.3189987, 25.49044, -7.861996, -0.15286326, 25.411646, -7.8352866, -0.179039, 25.405235, -7.8352866, -0.3243407, 25.464264, -7.861996, -0.3189987, 25.49044, -7.861996, -0.30617833, 24.707306, -7.8887057, -0.1814431, 24.773548, -7.8887057, -0.31258857, 24.733215, -7.861996, -0.30617833, 24.707306, -7.861996, -0.15553463, 24.767672, -7.8887057, -0.1814431, 24.773548, -7.8887057, -0.31258857, 24.733215, -7.861996, -0.20761883, 24.779156, -7.861996, -0.31899893, 24.759125, -7.8887057, -0.31258857, 24.733215, -7.8887057, -0.1814431, 24.773548, -7.861996, -0.20761883, 24.779156, -7.8887057, -0.1814431, 24.773548, -7.861996, -0.20494759, 25.399092, -7.861996, -0.20761883, 24.779156, -7.8887057, -0.1814431, 24.773548, -7.8887057, -0.179039, 25.405235, -7.861996, -0.20494759, 25.399092, -7.8887057, -0.179039, 25.405235, -7.861996, -0.3296826, 25.43809, -7.861996, -0.20494759, 25.399092, -7.8887057, -0.179039, 25.405235, -7.8887057, -0.3243407, 25.464264, -7.861996, -0.3296826, 25.43809, -7.861996, -0.15553463, 24.767672, -7.8887057, -0.179039, 25.405235, -7.8887057, -0.1814431, 24.773548, -7.861996, -0.15553463, 24.767672, -7.861996, -0.15286326, 25.411646, -7.8887057, -0.179039, 25.405235, -7.861996, -0.15286326, 25.411646, -7.8887057, -0.3243407, 25.464264, -7.8887057, -0.179039, 25.405235, -7.861996, -0.15286326, 25.411646, -7.861996, -0.3189987, 25.49044, -7.8887057, -0.3243407, 25.464264, -7.861996, -0.3296826, 25.43809, -7.8352866, -0.47525144, 25.410578, -7.8352866, -0.3243407, 25.464264, -7.861996, -0.3296826, 25.43809, -7.861996, -0.45067835, 25.399893, -7.8352866, -0.47525144, 25.410578, -7.8352866, -0.3243407, 25.464264, -7.861996, -0.49982452, 25.42126, -7.861996, -0.3189987, 25.49044, -7.8352866, -0.3243407, 25.464264, -7.8352866, -0.47525144, 25.410578, -7.861996, -0.49982452, 25.42126, -7.861996, -0.3189987, 25.49044, -7.8887057, -0.47525144, 25.410578, -7.8887057, -0.3243407, 25.464264, -7.861996, -0.3189987, 25.49044, -7.861996, -0.49982452, 25.42126, -7.8887057, -0.47525144, 25.410578, -7.8887057, -0.3243407, 25.464264, -7.861996, -0.45067835, 25.399893, -7.861996, -0.3296826, 25.43809, -7.8887057, -0.3243407, 25.464264, -7.8887057, -0.47525144, 25.410578, -7.861996, -0.45067835, 25.399893, -12.19968, -0.466704, 26.275177, -12.169498, -0.48727095, 25.533176, -12.169498, -0.49421525, 26.287462, -12.19968, -0.466704, 26.275177, -12.19968, -0.46002674, 25.545998, -12.169498, -0.48727095, 25.533176, -12.19968, -0.46002674, 25.545998, -12.169498, -0.31071866, 25.504864, -12.169498, -0.48727095, 25.533176, -12.19968, -0.46002674, 25.545998, -12.19968, -0.31793034, 25.534779, -12.169498, -0.31071866, 25.504864, -12.169498, -0.49421525, 26.287462, -12.19968, -0.51451504, 25.520088, -12.19968, -0.5219935, 26.29975, -12.169498, -0.49421525, 26.287462, -12.169498, -0.48727095, 25.533176, -12.19968, -0.51451504, 25.520088, -12.169498, -0.48727095, 25.533176, -12.19968, -0.303774, 25.474949, -12.19968, -0.51451504, 25.520088, -12.169498, -0.48727095, 25.533176, -12.169498, -0.31071866, 25.504864, -12.19968, -0.303774, 25.474949, -12.19968, -0.5219935, 26.29975, -12.229862, -0.48727095, 25.533176, -12.229862, -0.49421525, 26.287462, -12.19968, -0.5219935, 26.29975, -12.19968, -0.51451504, 25.520088, -12.229862, -0.48727095, 25.533176, -12.19968, -0.51451504, 25.520088, -12.229862, -0.31071866, 25.504864, -12.229862, -0.48727095, 25.533176, -12.19968, -0.51451504, 25.520088, -12.19968, -0.303774, 25.474949, -12.229862, -0.31071866, 25.504864, -12.229862, -0.49421525, 26.287462, -12.19968, -0.46002674, 25.545998, -12.19968, -0.466704, 26.275177, -12.229862, -0.49421525, 26.287462, -12.229862, -0.48727095, 25.533176, -12.19968, -0.46002674, 25.545998, -12.229862, -0.48727095, 25.533176, -12.19968, -0.31793034, 25.534779, -12.19968, -0.46002674, 25.545998, -12.229862, -0.48727095, 25.533176, -12.229862, -0.31071866, 25.504864, -12.19968, -0.31793034, 25.534779, -12.19968, -0.31793034, 25.534779, -12.169498, -0.1630131, 25.551338, -12.169498, -0.31071866, 25.504864, -12.19968, -0.31793034, 25.534779, -12.19968, -0.1923939, 25.558016, -12.169498, -0.1630131, 25.551338, -12.19968, -0.1923939, 25.558016, -12.169498, -0.16007471, 26.281319, -12.169498, -0.1630131, 25.551338, -12.19968, -0.1923939, 25.558016, -12.19968, -0.18918848, 26.274107, -12.169498, -0.16007471, 26.281319, -12.19968, -0.18918848, 26.274107, -12.169498, -0.3240733, 26.349697, -12.169498, -0.16007471, 26.281319, -12.19968, -0.18918848, 26.274107, -12.19968, -0.33021665, 26.319248, -12.169498, -0.3240733, 26.349697, -12.169498, -0.31071866, 25.504864, -12.19968, -0.13363218, 25.54466, -12.19968, -0.303774, 25.474949, -12.169498, -0.31071866, 25.504864, -12.169498, -0.1630131, 25.551338, -12.19968, -0.13363218, 25.54466, -12.169498, -0.1630131, 25.551338, -12.19968, -0.1306938, 26.288797, -12.19968, -0.13363218, 25.54466, -12.169498, -0.1630131, 25.551338, -12.169498, -0.16007471, 26.281319, -12.19968, -0.1306938, 26.288797, -12.169498, -0.16007471, 26.281319, -12.19968, -0.31819713, 26.379879, -12.19968, -0.1306938, 26.288797, -12.169498, -0.16007471, 26.281319, -12.169498, -0.3240733, 26.349697, -12.19968, -0.31819713, 26.379879, -12.19968, -0.303774, 25.474949, -12.229862, -0.1630131, 25.551338, -12.229862, -0.31071866, 25.504864, -12.19968, -0.303774, 25.474949, -12.19968, -0.13363218, 25.54466, -12.229862, -0.1630131, 25.551338, -12.229862, -0.31071866, 25.504864, -12.19968, -0.1923939, 25.558016, -12.19968, -0.31793034, 25.534779, -12.229862, -0.31071866, 25.504864, -12.229862, -0.1630131, 25.551338, -12.19968, -0.1923939, 25.558016, -12.229862, -0.1630131, 25.551338, -12.19968, -0.18918848, 26.274107, -12.19968, -0.1923939, 25.558016, -12.229862, -0.1630131, 25.551338, -12.229862, -0.16007471, 26.281319, -12.19968, -0.18918848, 26.274107, -12.229862, -0.16007471, 26.281319, -12.19968, -0.33021665, 26.319248, -12.19968, -0.18918848, 26.274107, -12.229862, -0.16007471, 26.281319, -12.229862, -0.3240733, 26.349697, -12.19968, -0.33021665, 26.319248, -12.19968, -0.13363218, 25.54466, -12.229862, -0.16007471, 26.281319, -12.229862, -0.1630131, 25.551338, -12.19968, -0.13363218, 25.54466, -12.19968, -0.1306938, 26.288797, -12.229862, -0.16007471, 26.281319, -12.19968, -0.1306938, 26.288797, -12.229862, -0.3240733, 26.349697, -12.229862, -0.16007471, 26.281319, -12.19968, -0.1306938, 26.288797, -12.19968, -0.31819713, 26.379879, -12.229862, -0.3240733, 26.349697, -12.19968, -0.33021665, 26.319248, -12.169498, -0.49421525, 26.287462, -12.169498, -0.3240733, 26.349697, -12.19968, -0.33021665, 26.319248, -12.19968, -0.466704, 26.275177, -12.169498, -0.49421525, 26.287462, -12.169498, -0.3240733, 26.349697, -12.19968, -0.5219935, 26.29975, -12.19968, -0.31819713, 26.379879, -12.169498, -0.3240733, 26.349697, -12.169498, -0.49421525, 26.287462, -12.19968, -0.5219935, 26.29975, -12.19968, -0.31819713, 26.379879, -12.229862, -0.49421525, 26.287462, -12.229862, -0.3240733, 26.349697, -12.19968, -0.31819713, 26.379879, -12.19968, -0.5219935, 26.29975, -12.229862, -0.49421525, 26.287462, -12.229862, -0.3240733, 26.349697, -12.19968, -0.466704, 26.275177, -12.19968, -0.33021665, 26.319248, -12.229862, -0.3240733, 26.349697, -12.229862, -0.49421525, 26.287462, -12.19968, -0.466704, 26.275177, -12.30839, -0.46670341, 27.781612, -12.278473, -0.48727036, 27.23406, -12.278473, -0.49421465, 27.790695, -12.30839, -0.46670341, 27.781612, -12.30839, -0.46002614, 27.243944, -12.278473, -0.48727036, 27.23406, -12.278473, -0.49421465, 27.790695, -12.30839, -0.51451445, 27.224445, -12.30839, -0.5219929, 27.799776, -12.278473, -0.49421465, 27.790695, -12.278473, -0.48727036, 27.23406, -12.30839, -0.51451445, 27.224445, -12.30839, -0.5219929, 27.799776, -12.338572, -0.48727036, 27.23406, -12.338572, -0.49421465, 27.790695, -12.30839, -0.5219929, 27.799776, -12.30839, -0.51451445, 27.224445, -12.338572, -0.48727036, 27.23406, -12.338572, -0.49421465, 27.790695, -12.30839, -0.46002614, 27.243944, -12.30839, -0.46670341, 27.781612, -12.338572, -0.49421465, 27.790695, -12.338572, -0.48727036, 27.23406, -12.30839, -0.46002614, 27.243944, -12.30839, -0.46002614, 27.243944, -12.278473, -0.31071806, 27.213493, -12.278473, -0.48727036, 27.23406, -12.30839, -0.46002614, 27.243944, -12.30839, -0.31792974, 27.235395, -12.278473, -0.31071806, 27.213493, -12.278473, -0.48727036, 27.23406, -12.30839, -0.3037734, 27.191324, -12.30839, -0.51451445, 27.224445, -12.278473, -0.48727036, 27.23406, -12.278473, -0.31071806, 27.213493, -12.30839, -0.3037734, 27.191324, -12.30839, -0.51451445, 27.224445, -12.338572, -0.31071806, 27.213493, -12.338572, -0.48727036, 27.23406, -12.30839, -0.51451445, 27.224445, -12.30839, -0.3037734, 27.191324, -12.338572, -0.31071806, 27.213493, -12.338572, -0.48727036, 27.23406, -12.30839, -0.31792974, 27.235395, -12.30839, -0.46002614, 27.243944, -12.338572, -0.48727036, 27.23406, -12.338572, -0.31071806, 27.213493, -12.30839, -0.31792974, 27.235395, -12.30839, -0.31792974, 27.235395, -12.278473, -0.1630125, 27.247683, -12.278473, -0.31071806, 27.213493, -12.30839, -0.31792974, 27.235395, -12.30839, -0.1923933, 27.252491, -12.278473, -0.1630125, 27.247683, -12.278473, -0.31071806, 27.213493, -12.30839, -0.13363159, 27.242607, -12.30839, -0.3037734, 27.191324, -12.278473, -0.31071806, 27.213493, -12.278473, -0.1630125, 27.247683, -12.30839, -0.13363159, 27.242607, -12.30839, -0.3037734, 27.191324, -12.338572, -0.1630125, 27.247683, -12.338572, -0.31071806, 27.213493, -12.30839, -0.3037734, 27.191324, -12.30839, -0.13363159, 27.242607, -12.338572, -0.1630125, 27.247683, -12.338572, -0.31071806, 27.213493, -12.30839, -0.1923933, 27.252491, -12.30839, -0.31792974, 27.235395, -12.338572, -0.31071806, 27.213493, -12.338572, -0.1630125, 27.247683, -12.30839, -0.1923933, 27.252491, -12.30839, -0.1923933, 27.252491, -12.278473, -0.16007411, 27.786154, -12.278473, -0.1630125, 27.247683, -12.30839, -0.1923933, 27.252491, -12.30839, -0.18918788, 27.780811, -12.278473, -0.16007411, 27.786154, -12.30839, -0.18918788, 27.780811, -12.278473, -0.32407272, 27.836636, -12.278473, -0.16007411, 27.786154, -12.30839, -0.18918788, 27.780811, -12.30839, -0.33021605, 27.8142, -12.278473, -0.32407272, 27.836636, -12.278473, -0.1630125, 27.247683, -12.30839, -0.1306932, 27.791496, -12.30839, -0.13363159, 27.242607, -12.278473, -0.1630125, 27.247683, -12.278473, -0.16007411, 27.786154, -12.30839, -0.1306932, 27.791496, -12.278473, -0.16007411, 27.786154, -12.30839, -0.31819654, 27.858805, -12.30839, -0.1306932, 27.791496, -12.278473, -0.16007411, 27.786154, -12.278473, -0.32407272, 27.836636, -12.30839, -0.31819654, 27.858805, -12.30839, -0.13363159, 27.242607, -12.338572, -0.16007411, 27.786154, -12.338572, -0.1630125, 27.247683, -12.30839, -0.13363159, 27.242607, -12.30839, -0.1306932, 27.791496, -12.338572, -0.16007411, 27.786154, -12.30839, -0.1306932, 27.791496, -12.338572, -0.32407272, 27.836636, -12.338572, -0.16007411, 27.786154, -12.30839, -0.1306932, 27.791496, -12.30839, -0.31819654, 27.858805, -12.338572, -0.32407272, 27.836636, -12.338572, -0.1630125, 27.247683, -12.30839, -0.18918788, 27.780811, -12.30839, -0.1923933, 27.252491, -12.338572, -0.1630125, 27.247683, -12.338572, -0.16007411, 27.786154, -12.30839, -0.18918788, 27.780811, -12.338572, -0.16007411, 27.786154, -12.30839, -0.33021605, 27.8142, -12.30839, -0.18918788, 27.780811, -12.338572, -0.16007411, 27.786154, -12.338572, -0.32407272, 27.836636, -12.30839, -0.33021605, 27.8142, -12.30839, -0.33021605, 27.8142, -12.278473, -0.49421465, 27.790695, -12.278473, -0.32407272, 27.836636, -12.30839, -0.33021605, 27.8142, -12.30839, -0.46670341, 27.781612, -12.278473, -0.49421465, 27.790695, -12.278473, -0.32407272, 27.836636, -12.30839, -0.5219929, 27.799776, -12.30839, -0.31819654, 27.858805, -12.278473, -0.32407272, 27.836636, -12.278473, -0.49421465, 27.790695, -12.30839, -0.5219929, 27.799776, -12.30839, -0.31819654, 27.858805, -12.338572, -0.49421465, 27.790695, -12.338572, -0.32407272, 27.836636, -12.30839, -0.31819654, 27.858805, -12.30839, -0.5219929, 27.799776, -12.338572, -0.49421465, 27.790695, -12.338572, -0.32407272, 27.836636, -12.30839, -0.46670341, 27.781612, -12.30839, -0.33021605, 27.8142, -12.338572, -0.32407272, 27.836636, -12.338572, -0.49421465, 27.790695, -12.30839, -0.46670341, 27.781612, -12.30839, -0.46670282, 29.40023, -12.278473, -0.48726988, 28.684673, -12.278473, -0.49421406, 29.411983, -12.30839, -0.46670282, 29.40023, -12.30839, -0.46002567, 28.697227, -12.278473, -0.48726988, 28.684673, -12.30839, -0.46002567, 28.697227, -12.278473, -0.31071758, 28.65743, -12.278473, -0.48726988, 28.684673, -12.30839, -0.46002567, 28.697227, -12.30839, -0.31792927, 28.686543, -12.278473, -0.31071758, 28.65743, -12.278473, -0.49421406, 29.411983, -12.30839, -0.51451397, 28.672121, -12.30839, -0.5219923, 29.424004, -12.278473, -0.49421406, 29.411983, -12.278473, -0.48726988, 28.684673, -12.30839, -0.51451397, 28.672121, -12.278473, -0.48726988, 28.684673, -12.30839, -0.30377293, 28.628584, -12.30839, -0.51451397, 28.672121, -12.278473, -0.48726988, 28.684673, -12.278473, -0.31071758, 28.65743, -12.30839, -0.30377293, 28.628584, -12.30839, -0.5219923, 29.424004, -12.338572, -0.48726988, 28.684673, -12.338572, -0.49421406, 29.411983, -12.30839, -0.5219923, 29.424004, -12.30839, -0.51451397, 28.672121, -12.338572, -0.48726988, 28.684673, -12.30839, -0.51451397, 28.672121, -12.338572, -0.31071758, 28.65743, -12.338572, -0.48726988, 28.684673, -12.30839, -0.51451397, 28.672121, -12.30839, -0.30377293, 28.628584, -12.338572, -0.31071758, 28.65743, -12.338572, -0.49421406, 29.411983, -12.30839, -0.46002567, 28.697227, -12.30839, -0.46670282, 29.40023, -12.338572, -0.49421406, 29.411983, -12.338572, -0.48726988, 28.684673, -12.30839, -0.46002567, 28.697227, -12.338572, -0.48726988, 28.684673, -12.30839, -0.31792927, 28.686543, -12.30839, -0.46002567, 28.697227, -12.338572, -0.48726988, 28.684673, -12.338572, -0.31071758, 28.65743, -12.30839, -0.31792927, 28.686543, -12.30839, -0.31792927, 28.686543, -12.278473, -0.16301203, 28.702303, -12.278473, -0.31071758, 28.65743, -12.30839, -0.31792927, 28.686543, -12.30839, -0.19239283, 28.708714, -12.278473, -0.16301203, 28.702303, -12.30839, -0.19239283, 28.708714, -12.278473, -0.16007352, 29.406107, -12.278473, -0.16301203, 28.702303, -12.30839, -0.19239283, 28.708714, -12.30839, -0.18918729, 29.398895, -12.278473, -0.16007352, 29.406107, -12.30839, -0.18918729, 29.398895, -12.278473, -0.32407212, 29.471813, -12.278473, -0.16007352, 29.406107, -12.30839, -0.18918729, 29.398895, -12.30839, -0.33021545, 29.4427, -12.278473, -0.32407212, 29.471813, -12.278473, -0.31071758, 28.65743, -12.30839, -0.13363111, 28.695892, -12.30839, -0.30377293, 28.628584, -12.278473, -0.31071758, 28.65743, -12.278473, -0.16301203, 28.702303, -12.30839, -0.13363111, 28.695892, -12.278473, -0.16301203, 28.702303, -12.30839, -0.1306926, 29.413319, -12.30839, -0.13363111, 28.695892, -12.278473, -0.16301203, 28.702303, -12.278473, -0.16007352, 29.406107, -12.30839, -0.1306926, 29.413319, -12.278473, -0.16007352, 29.406107, -12.30839, -0.31819594, 29.500927, -12.30839, -0.1306926, 29.413319, -12.278473, -0.16007352, 29.406107, -12.278473, -0.32407212, 29.471813, -12.30839, -0.31819594, 29.500927, -12.30839, -0.30377293, 28.628584, -12.338572, -0.16301203, 28.702303, -12.338572, -0.31071758, 28.65743, -12.30839, -0.30377293, 28.628584, -12.30839, -0.13363111, 28.695892, -12.338572, -0.16301203, 28.702303, -12.338572, -0.31071758, 28.65743, -12.30839, -0.19239283, 28.708714, -12.30839, -0.31792927, 28.686543, -12.338572, -0.31071758, 28.65743, -12.338572, -0.16301203, 28.702303, -12.30839, -0.19239283, 28.708714, -12.338572, -0.16301203, 28.702303, -12.30839, -0.18918729, 29.398895, -12.30839, -0.19239283, 28.708714, -12.338572, -0.16301203, 28.702303, -12.338572, -0.16007352, 29.406107, -12.30839, -0.18918729, 29.398895, -12.338572, -0.16007352, 29.406107, -12.30839, -0.33021545, 29.4427, -12.30839, -0.18918729, 29.398895, -12.338572, -0.16007352, 29.406107, -12.338572, -0.32407212, 29.471813, -12.30839, -0.33021545, 29.4427, -12.30839, -0.13363111, 28.695892, -12.338572, -0.16007352, 29.406107, -12.338572, -0.16301203, 28.702303, -12.30839, -0.13363111, 28.695892, -12.30839, -0.1306926, 29.413319, -12.338572, -0.16007352, 29.406107, -12.30839, -0.1306926, 29.413319, -12.338572, -0.32407212, 29.471813, -12.338572, -0.16007352, 29.406107, -12.30839, -0.1306926, 29.413319, -12.30839, -0.31819594, 29.500927, -12.338572, -0.32407212, 29.471813, -12.30839, -0.33021545, 29.4427, -12.278473, -0.49421406, 29.411983, -12.278473, -0.32407212, 29.471813, -12.30839, -0.33021545, 29.4427, -12.30839, -0.46670282, 29.40023, -12.278473, -0.49421406, 29.411983, -12.278473, -0.32407212, 29.471813, -12.30839, -0.5219923, 29.424004, -12.30839, -0.31819594, 29.500927, -12.278473, -0.32407212, 29.471813, -12.278473, -0.49421406, 29.411983, -12.30839, -0.5219923, 29.424004, -12.30839, -0.31819594, 29.500927, -12.338572, -0.49421406, 29.411983, -12.338572, -0.32407212, 29.471813, -12.30839, -0.31819594, 29.500927, -12.30839, -0.5219923, 29.424004, -12.338572, -0.49421406, 29.411983, -12.338572, -0.32407212, 29.471813, -12.30839, -0.46670282, 29.40023, -12.30839, -0.33021545, 29.4427, -12.338572, -0.32407212, 29.471813, -12.338572, -0.49421406, 29.411983, -12.30839, -0.46670282, 29.40023, -7.713756, -0.45067668, 30.185501, -7.6870465, -0.46883965, 29.543129, -7.6870465, -0.47524977, 30.196186, -7.713756, -0.45067668, 30.185501, -7.713756, -0.44480073, 29.554615, -7.6870465, -0.46883965, 29.543129, -7.713756, -0.44480073, 29.554615, -7.6870465, -0.3125869, 29.518824, -7.6870465, -0.46883965, 29.543129, -7.713756, -0.44480073, 29.554615, -7.713756, -0.31899726, 29.544731, -7.6870465, -0.3125869, 29.518824, -7.6870465, -0.47524977, 30.196186, -7.713756, -0.4931457, 29.532179, -7.713756, -0.49982285, 30.20687, -7.6870465, -0.47524977, 30.196186, -7.6870465, -0.46883965, 29.543129, -7.713756, -0.4931457, 29.532179, -7.6870465, -0.46883965, 29.543129, -7.713756, -0.30617654, 29.492914, -7.713756, -0.4931457, 29.532179, -7.6870465, -0.46883965, 29.543129, -7.6870465, -0.3125869, 29.518824, -7.713756, -0.30617654, 29.492914, -7.713756, -0.49982285, 30.20687, -7.7404666, -0.46883965, 29.543129, -7.7404666, -0.47524977, 30.196186, -7.713756, -0.49982285, 30.20687, -7.713756, -0.4931457, 29.532179, -7.7404666, -0.46883965, 29.543129, -7.713756, -0.4931457, 29.532179, -7.7404666, -0.3125869, 29.518824, -7.7404666, -0.46883965, 29.543129, -7.713756, -0.4931457, 29.532179, -7.713756, -0.30617654, 29.492914, -7.7404666, -0.3125869, 29.518824, -7.7404666, -0.47524977, 30.196186, -7.713756, -0.44480073, 29.554615, -7.713756, -0.45067668, 30.185501, -7.7404666, -0.47524977, 30.196186, -7.7404666, -0.46883965, 29.543129, -7.713756, -0.44480073, 29.554615, -7.7404666, -0.46883965, 29.543129, -7.713756, -0.31899726, 29.544731, -7.713756, -0.44480073, 29.554615, -7.7404666, -0.46883965, 29.543129, -7.7404666, -0.3125869, 29.518824, -7.713756, -0.31899726, 29.544731, -7.713756, -0.31899726, 29.544731, -7.6870465, -0.18144143, 29.559155, -7.6870465, -0.3125869, 29.518824, -7.713756, -0.31899726, 29.544731, -7.713756, -0.20761716, 29.564764, -7.6870465, -0.18144143, 29.559155, -7.713756, -0.20761716, 29.564764, -7.6870465, -0.17903733, 30.190844, -7.6870465, -0.18144143, 29.559155, -7.713756, -0.20761716, 29.564764, -7.713756, -0.20494592, 30.1847, -7.6870465, -0.17903733, 30.190844, -7.713756, -0.20494592, 30.1847, -7.6870465, -0.32433903, 30.249872, -7.6870465, -0.17903733, 30.190844, -7.713756, -0.20494592, 30.1847, -7.713756, -0.32968092, 30.223698, -7.6870465, -0.32433903, 30.249872, -7.6870465, -0.3125869, 29.518824, -7.713756, -0.15553296, 29.55328, -7.713756, -0.30617654, 29.492914, -7.6870465, -0.3125869, 29.518824, -7.6870465, -0.18144143, 29.559155, -7.713756, -0.15553296, 29.55328, -7.6870465, -0.18144143, 29.559155, -7.713756, -0.1528616, 30.197254, -7.713756, -0.15553296, 29.55328, -7.6870465, -0.18144143, 29.559155, -7.6870465, -0.17903733, 30.190844, -7.713756, -0.1528616, 30.197254, -7.6870465, -0.17903733, 30.190844, -7.713756, -0.31899703, 30.276049, -7.713756, -0.1528616, 30.197254, -7.6870465, -0.17903733, 30.190844, -7.6870465, -0.32433903, 30.249872, -7.713756, -0.31899703, 30.276049, -7.713756, -0.30617654, 29.492914, -7.7404666, -0.18144143, 29.559155, -7.7404666, -0.3125869, 29.518824, -7.713756, -0.30617654, 29.492914, -7.713756, -0.15553296, 29.55328, -7.7404666, -0.18144143, 29.559155, -7.7404666, -0.3125869, 29.518824, -7.713756, -0.20761716, 29.564764, -7.713756, -0.31899726, 29.544731, -7.7404666, -0.3125869, 29.518824, -7.7404666, -0.18144143, 29.559155, -7.713756, -0.20761716, 29.564764, -7.7404666, -0.18144143, 29.559155, -7.713756, -0.20494592, 30.1847, -7.713756, -0.20761716, 29.564764, -7.7404666, -0.18144143, 29.559155, -7.7404666, -0.17903733, 30.190844, -7.713756, -0.20494592, 30.1847, -7.7404666, -0.17903733, 30.190844, -7.713756, -0.32968092, 30.223698, -7.713756, -0.20494592, 30.1847, -7.7404666, -0.17903733, 30.190844, -7.7404666, -0.32433903, 30.249872, -7.713756, -0.32968092, 30.223698, -7.713756, -0.15553296, 29.55328, -7.7404666, -0.17903733, 30.190844, -7.7404666, -0.18144143, 29.559155, -7.713756, -0.15553296, 29.55328, -7.713756, -0.1528616, 30.197254, -7.7404666, -0.17903733, 30.190844, -7.713756, -0.1528616, 30.197254, -7.7404666, -0.32433903, 30.249872, -7.7404666, -0.17903733, 30.190844, -7.713756, -0.1528616, 30.197254, -7.713756, -0.31899703, 30.276049, -7.7404666, -0.32433903, 30.249872, -7.713756, -0.32968092, 30.223698, -7.6870465, -0.47524977, 30.196186, -7.6870465, -0.32433903, 30.249872, -7.713756, -0.32968092, 30.223698, -7.713756, -0.45067668, 30.185501, -7.6870465, -0.47524977, 30.196186, -7.6870465, -0.32433903, 30.249872, -7.713756, -0.49982285, 30.20687, -7.713756, -0.31899703, 30.276049, -7.6870465, -0.32433903, 30.249872, -7.6870465, -0.47524977, 30.196186, -7.713756, -0.49982285, 30.20687, -7.713756, -0.31899703, 30.276049, -7.7404666, -0.47524977, 30.196186, -7.7404666, -0.32433903, 30.249872, -7.713756, -0.31899703, 30.276049, -7.713756, -0.49982285, 30.20687, -7.7404666, -0.47524977, 30.196186, -7.7404666, -0.32433903, 30.249872, -7.713756, -0.45067668, 30.185501, -7.713756, -0.32968092, 30.223698, -7.7404666, -0.32433903, 30.249872, -7.7404666, -0.47524977, 30.196186, -7.713756, -0.45067668, 30.185501, -12.381041, -0.46670377, 26.957613, -12.350857, -0.4872706, 26.434635, -12.350857, -0.494215, 26.966427, -12.381041, -0.46670377, 26.957613, -12.381041, -0.46002638, 26.443716, -12.350857, -0.4872706, 26.434635, -12.350857, -0.494215, 26.966427, -12.381041, -0.5145147, 26.425285, -12.381041, -0.5219933, 26.974976, -12.350857, -0.494215, 26.966427, -12.350857, -0.4872706, 26.434635, -12.381041, -0.5145147, 26.425285, -12.381041, -0.5219933, 26.974976, -12.411222, -0.4872706, 26.434635, -12.411222, -0.494215, 26.966427, -12.381041, -0.5219933, 26.974976, -12.381041, -0.5145147, 26.425285, -12.411222, -0.4872706, 26.434635, -12.411222, -0.494215, 26.966427, -12.381041, -0.46002638, 26.443716, -12.381041, -0.46670377, 26.957613, -12.411222, -0.494215, 26.966427, -12.411222, -0.4872706, 26.434635, -12.381041, -0.46002638, 26.443716, -12.381041, -0.46002638, 26.443716, -12.350857, -0.3107183, 26.414602, -12.350857, -0.4872706, 26.434635, -12.381041, -0.46002638, 26.443716, -12.381041, -0.31792998, 26.435703, -12.350857, -0.3107183, 26.414602, -12.350857, -0.4872706, 26.434635, -12.381041, -0.30377364, 26.393501, -12.381041, -0.5145147, 26.425285, -12.350857, -0.4872706, 26.434635, -12.350857, -0.3107183, 26.414602, -12.381041, -0.30377364, 26.393501, -12.381041, -0.5145147, 26.425285, -12.411222, -0.3107183, 26.414602, -12.411222, -0.4872706, 26.434635, -12.381041, -0.5145147, 26.425285, -12.381041, -0.30377364, 26.393501, -12.411222, -0.3107183, 26.414602, -12.411222, -0.4872706, 26.434635, -12.381041, -0.31792998, 26.435703, -12.381041, -0.46002638, 26.443716, -12.411222, -0.4872706, 26.434635, -12.411222, -0.3107183, 26.414602, -12.381041, -0.31792998, 26.435703, -12.381041, -0.31792998, 26.435703, -12.350857, -0.16301274, 26.447454, -12.350857, -0.3107183, 26.414602, -12.381041, -0.31792998, 26.435703, -12.381041, -0.19239354, 26.451996, -12.350857, -0.16301274, 26.447454, -12.350857, -0.3107183, 26.414602, -12.381041, -0.13363183, 26.442646, -12.381041, -0.30377364, 26.393501, -12.350857, -0.3107183, 26.414602, -12.350857, -0.16301274, 26.447454, -12.381041, -0.13363183, 26.442646, -12.381041, -0.30377364, 26.393501, -12.411222, -0.16301274, 26.447454, -12.411222, -0.3107183, 26.414602, -12.381041, -0.30377364, 26.393501, -12.381041, -0.13363183, 26.442646, -12.411222, -0.16301274, 26.447454, -12.411222, -0.3107183, 26.414602, -12.381041, -0.19239354, 26.451996, -12.381041, -0.31792998, 26.435703, -12.411222, -0.3107183, 26.414602, -12.411222, -0.16301274, 26.447454, -12.381041, -0.19239354, 26.451996, -12.381041, -0.19239354, 26.451996, -12.350857, -0.16007447, 26.962154, -12.350857, -0.16301274, 26.447454, -12.381041, -0.19239354, 26.451996, -12.381041, -0.18918824, 26.956812, -12.350857, -0.16007447, 26.962154, -12.350857, -0.16301274, 26.447454, -12.381041, -0.13069355, 26.96723, -12.381041, -0.13363183, 26.442646, -12.350857, -0.16301274, 26.447454, -12.350857, -0.16007447, 26.962154, -12.381041, -0.13069355, 26.96723, -12.381041, -0.13363183, 26.442646, -12.411222, -0.16007447, 26.962154, -12.411222, -0.16301274, 26.447454, -12.381041, -0.13363183, 26.442646, -12.381041, -0.13069355, 26.96723, -12.411222, -0.16007447, 26.962154, -12.411222, -0.16301274, 26.447454, -12.381041, -0.18918824, 26.956812, -12.381041, -0.19239354, 26.451996, -12.411222, -0.16301274, 26.447454, -12.411222, -0.16007447, 26.962154, -12.381041, -0.18918824, 26.956812, -12.381041, -0.18918824, 26.956812, -12.350857, -0.32407308, 27.010231, -12.350857, -0.16007447, 26.962154, -12.381041, -0.18918824, 26.956812, -12.381041, -0.3302164, 26.988865, -12.350857, -0.32407308, 27.010231, -12.350857, -0.16007447, 26.962154, -12.381041, -0.3181969, 27.031332, -12.381041, -0.13069355, 26.96723, -12.350857, -0.16007447, 26.962154, -12.350857, -0.32407308, 27.010231, -12.381041, -0.3181969, 27.031332, -12.381041, -0.13069355, 26.96723, -12.411222, -0.32407308, 27.010231, -12.411222, -0.16007447, 26.962154, -12.381041, -0.13069355, 26.96723, -12.381041, -0.3181969, 27.031332, -12.411222, -0.32407308, 27.010231, -12.411222, -0.16007447, 26.962154, -12.381041, -0.3302164, 26.988865, -12.381041, -0.18918824, 26.956812, -12.411222, -0.16007447, 26.962154, -12.411222, -0.32407308, 27.010231, -12.381041, -0.3302164, 26.988865, -12.381041, -0.3302164, 26.988865, -12.350857, -0.494215, 26.966427, -12.350857, -0.32407308, 27.010231, -12.381041, -0.3302164, 26.988865, -12.381041, -0.46670377, 26.957613, -12.350857, -0.494215, 26.966427, -12.350857, -0.32407308, 27.010231, -12.381041, -0.5219933, 26.974976, -12.381041, -0.3181969, 27.031332, -12.350857, -0.32407308, 27.010231, -12.350857, -0.494215, 26.966427, -12.381041, -0.5219933, 26.974976, -12.381041, -0.3181969, 27.031332, -12.411222, -0.494215, 26.966427, -12.411222, -0.32407308, 27.010231, -12.381041, -0.3181969, 27.031332, -12.381041, -0.5219933, 26.974976, -12.411222, -0.494215, 26.966427, -12.411222, -0.32407308, 27.010231, -12.381041, -0.46670377, 26.957613, -12.381041, -0.3302164, 26.988865, -12.411222, -0.32407308, 27.010231, -12.411222, -0.494215, 26.966427, -12.381041, -0.46670377, 26.957613, -12.377033, -0.46670318, 28.47527, -12.346851, -0.48727012, 27.95229, -12.346851, -0.49421442, 28.484083, -12.377033, -0.46670318, 28.47527, -12.377033, -0.4600259, 27.96137, -12.346851, -0.48727012, 27.95229, -12.346851, -0.49421442, 28.484083, -12.377033, -0.5145142, 27.942942, -12.377033, -0.5219927, 28.49263, -12.346851, -0.49421442, 28.484083, -12.346851, -0.48727012, 27.95229, -12.377033, -0.5145142, 27.942942, -12.377033, -0.5219927, 28.49263, -12.407215, -0.48727012, 27.95229, -12.407215, -0.49421442, 28.484083, -12.377033, -0.5219927, 28.49263, -12.377033, -0.5145142, 27.942942, -12.407215, -0.48727012, 27.95229, -12.407215, -0.49421442, 28.484083, -12.377033, -0.4600259, 27.96137, -12.377033, -0.46670318, 28.47527, -12.407215, -0.49421442, 28.484083, -12.407215, -0.48727012, 27.95229, -12.377033, -0.4600259, 27.96137, -12.377033, -0.4600259, 27.96137, -12.346851, -0.31071782, 27.932257, -12.346851, -0.48727012, 27.95229, -12.377033, -0.4600259, 27.96137, -12.377033, -0.3179295, 27.953358, -12.346851, -0.31071782, 27.932257, -12.346851, -0.48727012, 27.95229, -12.377033, -0.30377316, 27.911156, -12.377033, -0.5145142, 27.942942, -12.346851, -0.48727012, 27.95229, -12.346851, -0.31071782, 27.932257, -12.377033, -0.30377316, 27.911156, -12.377033, -0.5145142, 27.942942, -12.407215, -0.31071782, 27.932257, -12.407215, -0.48727012, 27.95229, -12.377033, -0.5145142, 27.942942, -12.377033, -0.30377316, 27.911156, -12.407215, -0.31071782, 27.932257, -12.407215, -0.48727012, 27.95229, -12.377033, -0.3179295, 27.953358, -12.377033, -0.4600259, 27.96137, -12.407215, -0.48727012, 27.95229, -12.407215, -0.31071782, 27.932257, -12.377033, -0.3179295, 27.953358, -12.377033, -0.3179295, 27.953358, -12.346851, -0.16301227, 27.965109, -12.346851, -0.31071782, 27.932257, -12.377033, -0.3179295, 27.953358, -12.377033, -0.19239306, 27.96965, -12.346851, -0.16301227, 27.965109, -12.346851, -0.31071782, 27.932257, -12.377033, -0.13363135, 27.960302, -12.377033, -0.30377316, 27.911156, -12.346851, -0.31071782, 27.932257, -12.346851, -0.16301227, 27.965109, -12.377033, -0.13363135, 27.960302, -12.377033, -0.30377316, 27.911156, -12.407215, -0.16301227, 27.965109, -12.407215, -0.31071782, 27.932257, -12.377033, -0.30377316, 27.911156, -12.377033, -0.13363135, 27.960302, -12.407215, -0.16301227, 27.965109, -12.407215, -0.31071782, 27.932257, -12.377033, -0.19239306, 27.96965, -12.377033, -0.3179295, 27.953358, -12.407215, -0.31071782, 27.932257, -12.407215, -0.16301227, 27.965109, -12.377033, -0.19239306, 27.96965, -12.377033, -0.19239306, 27.96965, -12.346851, -0.16007388, 28.479809, -12.346851, -0.16301227, 27.965109, -12.377033, -0.19239306, 27.96965, -12.377033, -0.18918765, 28.474466, -12.346851, -0.16007388, 28.479809, -12.346851, -0.16301227, 27.965109, -12.377033, -0.13069296, 28.484884, -12.377033, -0.13363135, 27.960302, -12.346851, -0.16301227, 27.965109, -12.346851, -0.16007388, 28.479809, -12.377033, -0.13069296, 28.484884, -12.377033, -0.13363135, 27.960302, -12.407215, -0.16007388, 28.479809, -12.407215, -0.16301227, 27.965109, -12.377033, -0.13363135, 27.960302, -12.377033, -0.13069296, 28.484884, -12.407215, -0.16007388, 28.479809, -12.407215, -0.16301227, 27.965109, -12.377033, -0.18918765, 28.474466, -12.377033, -0.19239306, 27.96965, -12.407215, -0.16301227, 27.965109, -12.407215, -0.16007388, 28.479809, -12.377033, -0.18918765, 28.474466, -12.377033, -0.18918765, 28.474466, -12.346851, -0.32407248, 28.527887, -12.346851, -0.16007388, 28.479809, -12.377033, -0.18918765, 28.474466, -12.377033, -0.3302158, 28.50652, -12.346851, -0.32407248, 28.527887, -12.346851, -0.16007388, 28.479809, -12.377033, -0.3181963, 28.548988, -12.377033, -0.13069296, 28.484884, -12.346851, -0.16007388, 28.479809, -12.346851, -0.32407248, 28.527887, -12.377033, -0.3181963, 28.548988, -12.377033, -0.13069296, 28.484884, -12.407215, -0.32407248, 28.527887, -12.407215, -0.16007388, 28.479809, -12.377033, -0.13069296, 28.484884, -12.377033, -0.3181963, 28.548988, -12.407215, -0.32407248, 28.527887, -12.407215, -0.16007388, 28.479809, -12.377033, -0.3302158, 28.50652, -12.377033, -0.18918765, 28.474466, -12.407215, -0.16007388, 28.479809, -12.407215, -0.32407248, 28.527887, -12.377033, -0.3302158, 28.50652, -12.377033, -0.3302158, 28.50652, -12.346851, -0.49421442, 28.484083, -12.346851, -0.32407248, 28.527887, -12.377033, -0.3302158, 28.50652, -12.377033, -0.46670318, 28.47527, -12.346851, -0.49421442, 28.484083, -12.346851, -0.32407248, 28.527887, -12.377033, -0.5219927, 28.49263, -12.377033, -0.3181963, 28.548988, -12.346851, -0.32407248, 28.527887, -12.346851, -0.49421442, 28.484083, -12.377033, -0.5219927, 28.49263, -12.377033, -0.3181963, 28.548988, -12.407215, -0.49421442, 28.484083, -12.407215, -0.32407248, 28.527887, -12.377033, -0.3181963, 28.548988, -12.377033, -0.5219927, 28.49263, -12.407215, -0.49421442, 28.484083, -12.407215, -0.32407248, 28.527887, -12.377033, -0.46670318, 28.47527, -12.377033, -0.3302158, 28.50652, -12.407215, -0.32407248, 28.527887, -12.407215, -0.49421442, 28.484083, -12.377033, -0.46670318, 28.47527, -7.753287, -0.45067787, 26.995274, -7.7265773, -0.46884084, 26.352901, -7.7265773, -0.47525096, 27.005692, -7.753287, -0.45067787, 26.995274, -7.753287, -0.44480193, 26.36412, -7.7265773, -0.46884084, 26.352901, -7.753287, -0.44480193, 26.36412, -7.7265773, -0.3125881, 26.32833, -7.7265773, -0.46884084, 26.352901, -7.753287, -0.44480193, 26.36412, -7.753287, -0.31899846, 26.354237, -7.7265773, -0.3125881, 26.32833, -7.7265773, -0.47525096, 27.005692, -7.753287, -0.4931469, 26.341684, -7.753287, -0.49982405, 27.016375, -7.7265773, -0.47525096, 27.005692, -7.7265773, -0.46884084, 26.352901, -7.753287, -0.4931469, 26.341684, -7.7265773, -0.46884084, 26.352901, -7.753287, -0.30617774, 26.30242, -7.753287, -0.4931469, 26.341684, -7.7265773, -0.46884084, 26.352901, -7.7265773, -0.3125881, 26.32833, -7.753287, -0.30617774, 26.30242, -7.753287, -0.49982405, 27.016375, -7.7799973, -0.46884084, 26.352901, -7.7799973, -0.47525096, 27.005692, -7.753287, -0.49982405, 27.016375, -7.753287, -0.4931469, 26.341684, -7.7799973, -0.46884084, 26.352901, -7.753287, -0.4931469, 26.341684, -7.7799973, -0.3125881, 26.32833, -7.7799973, -0.46884084, 26.352901, -7.753287, -0.4931469, 26.341684, -7.753287, -0.30617774, 26.30242, -7.7799973, -0.3125881, 26.32833, -7.7799973, -0.47525096, 27.005692, -7.753287, -0.44480193, 26.36412, -7.753287, -0.45067787, 26.995274, -7.7799973, -0.47525096, 27.005692, -7.7799973, -0.46884084, 26.352901, -7.753287, -0.44480193, 26.36412, -7.7799973, -0.46884084, 26.352901, -7.753287, -0.31899846, 26.354237, -7.753287, -0.44480193, 26.36412, -7.7799973, -0.46884084, 26.352901, -7.7799973, -0.3125881, 26.32833, -7.753287, -0.31899846, 26.354237, -7.753287, -0.31899846, 26.354237, -7.7265773, -0.18144262, 26.36866, -7.7265773, -0.3125881, 26.32833, -7.753287, -0.31899846, 26.354237, -7.753287, -0.20761836, 26.37427, -7.7265773, -0.18144262, 26.36866, -7.753287, -0.20761836, 26.37427, -7.7265773, -0.17903852, 27.000616, -7.7265773, -0.18144262, 26.36866, -7.753287, -0.20761836, 26.37427, -7.753287, -0.20494711, 26.994205, -7.7265773, -0.17903852, 27.000616, -7.753287, -0.20494711, 26.994205, -7.7265773, -0.3243401, 27.059378, -7.7265773, -0.17903852, 27.000616, -7.753287, -0.20494711, 26.994205, -7.753287, -0.3296821, 27.033203, -7.7265773, -0.3243401, 27.059378, -7.7265773, -0.3125881, 26.32833, -7.753287, -0.15553415, 26.362785, -7.753287, -0.30617774, 26.30242, -7.7265773, -0.3125881, 26.32833, -7.7265773, -0.18144262, 26.36866, -7.753287, -0.15553415, 26.362785, -7.7265773, -0.18144262, 26.36866, -7.753287, -0.15286279, 27.00676, -7.753287, -0.15553415, 26.362785, -7.7265773, -0.18144262, 26.36866, -7.7265773, -0.17903852, 27.000616, -7.753287, -0.15286279, 27.00676, -7.7265773, -0.17903852, 27.000616, -7.753287, -0.3189981, 27.085821, -7.753287, -0.15286279, 27.00676, -7.7265773, -0.17903852, 27.000616, -7.7265773, -0.3243401, 27.059378, -7.753287, -0.3189981, 27.085821, -7.753287, -0.30617774, 26.30242, -7.7799973, -0.18144262, 26.36866, -7.7799973, -0.3125881, 26.32833, -7.753287, -0.30617774, 26.30242, -7.753287, -0.15553415, 26.362785, -7.7799973, -0.18144262, 26.36866, -7.7799973, -0.3125881, 26.32833, -7.753287, -0.20761836, 26.37427, -7.753287, -0.31899846, 26.354237, -7.7799973, -0.3125881, 26.32833, -7.7799973, -0.18144262, 26.36866, -7.753287, -0.20761836, 26.37427, -7.7799973, -0.18144262, 26.36866, -7.753287, -0.20494711, 26.994205, -7.753287, -0.20761836, 26.37427, -7.7799973, -0.18144262, 26.36866, -7.7799973, -0.17903852, 27.000616, -7.753287, -0.20494711, 26.994205, -7.7799973, -0.17903852, 27.000616, -7.753287, -0.3296821, 27.033203, -7.753287, -0.20494711, 26.994205, -7.7799973, -0.17903852, 27.000616, -7.7799973, -0.3243401, 27.059378, -7.753287, -0.3296821, 27.033203, -7.753287, -0.15553415, 26.362785, -7.7799973, -0.17903852, 27.000616, -7.7799973, -0.18144262, 26.36866, -7.753287, -0.15553415, 26.362785, -7.753287, -0.15286279, 27.00676, -7.7799973, -0.17903852, 27.000616, -7.753287, -0.15286279, 27.00676, -7.7799973, -0.3243401, 27.059378, -7.7799973, -0.17903852, 27.000616, -7.753287, -0.15286279, 27.00676, -7.753287, -0.3189981, 27.085821, -7.7799973, -0.3243401, 27.059378, -7.753287, -0.3296821, 27.033203, -7.7265773, -0.47525096, 27.005692, -7.7265773, -0.3243401, 27.059378, -7.753287, -0.3296821, 27.033203, -7.753287, -0.45067787, 26.995274, -7.7265773, -0.47525096, 27.005692, -7.7265773, -0.3243401, 27.059378, -7.753287, -0.49982405, 27.016375, -7.753287, -0.3189981, 27.085821, -7.7265773, -0.3243401, 27.059378, -7.7265773, -0.47525096, 27.005692, -7.753287, -0.49982405, 27.016375, -7.753287, -0.3189981, 27.085821, -7.7799973, -0.47525096, 27.005692, -7.7799973, -0.3243401, 27.059378, -7.753287, -0.3189981, 27.085821, -7.753287, -0.49982405, 27.016375, -7.7799973, -0.47525096, 27.005692, -7.7799973, -0.3243401, 27.059378, -7.753287, -0.45067787, 26.995274, -7.753287, -0.3296821, 27.033203, -7.7799973, -0.3243401, 27.059378, -7.7799973, -0.47525096, 27.005692, -7.753287, -0.45067787, 26.995274, -7.8224654, -0.45067728, 28.55513, -7.795756, -0.46884024, 27.912758, -7.795756, -0.47525036, 28.565815, -7.8224654, -0.45067728, 28.55513, -7.8224654, -0.44480133, 27.923977, -7.795756, -0.46884024, 27.912758, -7.8224654, -0.44480133, 27.923977, -7.795756, -0.3125875, 27.888186, -7.795756, -0.46884024, 27.912758, -7.8224654, -0.44480133, 27.923977, -7.8224654, -0.31899786, 27.914362, -7.795756, -0.3125875, 27.888186, -7.795756, -0.47525036, 28.565815, -7.8224654, -0.4931463, 27.90154, -7.8224654, -0.49982345, 28.576498, -7.795756, -0.47525036, 28.565815, -7.795756, -0.46884024, 27.912758, -7.8224654, -0.4931463, 27.90154, -7.795756, -0.46884024, 27.912758, -7.8224654, -0.30617714, 27.862276, -7.8224654, -0.4931463, 27.90154, -7.795756, -0.46884024, 27.912758, -7.795756, -0.3125875, 27.888186, -7.8224654, -0.30617714, 27.862276, -7.8224654, -0.49982345, 28.576498, -7.849176, -0.46884024, 27.912758, -7.849176, -0.47525036, 28.565815, -7.8224654, -0.49982345, 28.576498, -7.8224654, -0.4931463, 27.90154, -7.849176, -0.46884024, 27.912758, -7.8224654, -0.4931463, 27.90154, -7.849176, -0.3125875, 27.888186, -7.849176, -0.46884024, 27.912758, -7.8224654, -0.4931463, 27.90154, -7.8224654, -0.30617714, 27.862276, -7.849176, -0.3125875, 27.888186, -7.849176, -0.47525036, 28.565815, -7.8224654, -0.44480133, 27.923977, -7.8224654, -0.45067728, 28.55513, -7.849176, -0.47525036, 28.565815, -7.849176, -0.46884024, 27.912758, -7.8224654, -0.44480133, 27.923977, -7.849176, -0.46884024, 27.912758, -7.8224654, -0.31899786, 27.914362, -7.8224654, -0.44480133, 27.923977, -7.849176, -0.46884024, 27.912758, -7.849176, -0.3125875, 27.888186, -7.8224654, -0.31899786, 27.914362, -7.8224654, -0.31899786, 27.914362, -7.795756, -0.18144202, 27.928516, -7.795756, -0.3125875, 27.888186, -7.8224654, -0.31899786, 27.914362, -7.8224654, -0.20761776, 27.934393, -7.795756, -0.18144202, 27.928516, -7.8224654, -0.20761776, 27.934393, -7.795756, -0.17903793, 28.560472, -7.795756, -0.18144202, 27.928516, -7.8224654, -0.20761776, 27.934393, -7.8224654, -0.20494652, 28.554062, -7.795756, -0.17903793, 28.560472, -7.8224654, -0.20494652, 28.554062, -7.795756, -0.32433963, 28.619501, -7.795756, -0.17903793, 28.560472, -7.8224654, -0.20494652, 28.554062, -7.8224654, -0.32968152, 28.593327, -7.795756, -0.32433963, 28.619501, -7.795756, -0.3125875, 27.888186, -7.8224654, -0.15553355, 27.922909, -7.8224654, -0.30617714, 27.862276, -7.795756, -0.3125875, 27.888186, -7.795756, -0.18144202, 27.928516, -7.8224654, -0.15553355, 27.922909, -7.795756, -0.18144202, 27.928516, -7.8224654, -0.15286219, 28.566883, -7.8224654, -0.15553355, 27.922909, -7.795756, -0.18144202, 27.928516, -7.795756, -0.17903793, 28.560472, -7.8224654, -0.15286219, 28.566883, -7.795756, -0.17903793, 28.560472, -7.8224654, -0.31899762, 28.645678, -7.8224654, -0.15286219, 28.566883, -7.795756, -0.17903793, 28.560472, -7.795756, -0.32433963, 28.619501, -7.8224654, -0.31899762, 28.645678, -7.8224654, -0.30617714, 27.862276, -7.849176, -0.18144202, 27.928516, -7.849176, -0.3125875, 27.888186, -7.8224654, -0.30617714, 27.862276, -7.8224654, -0.15553355, 27.922909, -7.849176, -0.18144202, 27.928516, -7.849176, -0.3125875, 27.888186, -7.8224654, -0.20761776, 27.934393, -7.8224654, -0.31899786, 27.914362, -7.849176, -0.3125875, 27.888186, -7.849176, -0.18144202, 27.928516, -7.8224654, -0.20761776, 27.934393, -7.849176, -0.18144202, 27.928516, -7.8224654, -0.20494652, 28.554062, -7.8224654, -0.20761776, 27.934393, -7.849176, -0.18144202, 27.928516, -7.849176, -0.17903793, 28.560472, -7.8224654, -0.20494652, 28.554062, -7.849176, -0.17903793, 28.560472, -7.8224654, -0.32968152, 28.593327, -7.8224654, -0.20494652, 28.554062, -7.849176, -0.17903793, 28.560472, -7.849176, -0.32433963, 28.619501, -7.8224654, -0.32968152, 28.593327, -7.8224654, -0.15553355, 27.922909, -7.849176, -0.17903793, 28.560472, -7.849176, -0.18144202, 27.928516, -7.8224654, -0.15553355, 27.922909, -7.8224654, -0.15286219, 28.566883, -7.849176, -0.17903793, 28.560472, -7.8224654, -0.15286219, 28.566883, -7.849176, -0.32433963, 28.619501, -7.849176, -0.17903793, 28.560472, -7.8224654, -0.15286219, 28.566883, -7.8224654, -0.31899762, 28.645678, -7.849176, -0.32433963, 28.619501, -7.8224654, -0.32968152, 28.593327, -7.795756, -0.47525036, 28.565815, -7.795756, -0.32433963, 28.619501, -7.8224654, -0.32968152, 28.593327, -7.8224654, -0.45067728, 28.55513, -7.795756, -0.47525036, 28.565815, -7.795756, -0.32433963, 28.619501, -7.8224654, -0.49982345, 28.576498, -7.8224654, -0.31899762, 28.645678, -7.795756, -0.32433963, 28.619501, -7.795756, -0.47525036, 28.565815, -7.8224654, -0.49982345, 28.576498, -7.8224654, -0.31899762, 28.645678, -7.849176, -0.47525036, 28.565815, -7.849176, -0.32433963, 28.619501, -7.8224654, -0.31899762, 28.645678, -7.8224654, -0.49982345, 28.576498, -7.849176, -0.47525036, 28.565815, -7.849176, -0.32433963, 28.619501, -7.8224654, -0.45067728, 28.55513, -7.8224654, -0.32968152, 28.593327, -7.849176, -0.32433963, 28.619501, -7.849176, -0.47525036, 28.565815, -7.8224654, -0.45067728, 28.55513, -7.733522, -0.4506775, 27.814734, -7.7068114, -0.46884048, 27.172628, -7.7068114, -0.4752506, 27.825417, -7.733522, -0.4506775, 27.814734, -7.733522, -0.44480157, 27.183846, -7.7068114, -0.46884048, 27.172628, -7.733522, -0.44480157, 27.183846, -7.7068114, -0.31258774, 27.148054, -7.7068114, -0.46884048, 27.172628, -7.733522, -0.44480157, 27.183846, -7.733522, -0.3189981, 27.173964, -7.7068114, -0.31258774, 27.148054, -7.7068114, -0.4752506, 27.825417, -7.733522, -0.49314654, 27.161142, -7.733522, -0.4998237, 27.836102, -7.7068114, -0.4752506, 27.825417, -7.7068114, -0.46884048, 27.172628, -7.733522, -0.49314654, 27.161142, -7.7068114, -0.46884048, 27.172628, -7.733522, -0.30617738, 27.122147, -7.733522, -0.49314654, 27.161142, -7.7068114, -0.46884048, 27.172628, -7.7068114, -0.31258774, 27.148054, -7.733522, -0.30617738, 27.122147, -7.733522, -0.4998237, 27.836102, -7.7602315, -0.46884048, 27.172628, -7.7602315, -0.4752506, 27.825417, -7.733522, -0.4998237, 27.836102, -7.733522, -0.49314654, 27.161142, -7.7602315, -0.46884048, 27.172628, -7.733522, -0.49314654, 27.161142, -7.7602315, -0.31258774, 27.148054, -7.7602315, -0.46884048, 27.172628, -7.733522, -0.49314654, 27.161142, -7.733522, -0.30617738, 27.122147, -7.7602315, -0.31258774, 27.148054, -7.7602315, -0.4752506, 27.825417, -7.733522, -0.44480157, 27.183846, -7.733522, -0.4506775, 27.814734, -7.7602315, -0.4752506, 27.825417, -7.7602315, -0.46884048, 27.172628, -7.733522, -0.44480157, 27.183846, -7.7602315, -0.46884048, 27.172628, -7.733522, -0.3189981, 27.173964, -7.733522, -0.44480157, 27.183846, -7.7602315, -0.46884048, 27.172628, -7.7602315, -0.31258774, 27.148054, -7.733522, -0.3189981, 27.173964, -7.733522, -0.3189981, 27.173964, -7.7068114, -0.18144226, 27.18812, -7.7068114, -0.31258774, 27.148054, -7.733522, -0.3189981, 27.173964, -7.733522, -0.207618, 27.193996, -7.7068114, -0.18144226, 27.18812, -7.733522, -0.207618, 27.193996, -7.7068114, -0.17903817, 27.820076, -7.7068114, -0.18144226, 27.18812, -7.733522, -0.207618, 27.193996, -7.733522, -0.20494676, 27.813665, -7.7068114, -0.17903817, 27.820076, -7.733522, -0.20494676, 27.813665, -7.7068114, -0.32433987, 27.879105, -7.7068114, -0.17903817, 27.820076, -7.733522, -0.20494676, 27.813665, -7.733522, -0.32968175, 27.852928, -7.7068114, -0.32433987, 27.879105, -7.7068114, -0.31258774, 27.148054, -7.733522, -0.15553379, 27.18251, -7.733522, -0.30617738, 27.122147, -7.7068114, -0.31258774, 27.148054, -7.7068114, -0.18144226, 27.18812, -7.733522, -0.15553379, 27.18251, -7.7068114, -0.18144226, 27.18812, -7.733522, -0.15286243, 27.826485, -7.733522, -0.15553379, 27.18251, -7.7068114, -0.18144226, 27.18812, -7.7068114, -0.17903817, 27.820076, -7.733522, -0.15286243, 27.826485, -7.7068114, -0.17903817, 27.820076, -7.733522, -0.31899786, 27.90528, -7.733522, -0.15286243, 27.826485, -7.7068114, -0.17903817, 27.820076, -7.7068114, -0.32433987, 27.879105, -7.733522, -0.31899786, 27.90528, -7.733522, -0.30617738, 27.122147, -7.7602315, -0.18144226, 27.18812, -7.7602315, -0.31258774, 27.148054, -7.733522, -0.30617738, 27.122147, -7.733522, -0.15553379, 27.18251, -7.7602315, -0.18144226, 27.18812, -7.7602315, -0.31258774, 27.148054, -7.733522, -0.207618, 27.193996, -7.733522, -0.3189981, 27.173964, -7.7602315, -0.31258774, 27.148054, -7.7602315, -0.18144226, 27.18812, -7.733522, -0.207618, 27.193996, -7.7602315, -0.18144226, 27.18812, -7.733522, -0.20494676, 27.813665, -7.733522, -0.207618, 27.193996, -7.7602315, -0.18144226, 27.18812, -7.7602315, -0.17903817, 27.820076, -7.733522, -0.20494676, 27.813665, -7.7602315, -0.17903817, 27.820076, -7.733522, -0.32968175, 27.852928, -7.733522, -0.20494676, 27.813665, -7.7602315, -0.17903817, 27.820076, -7.7602315, -0.32433987, 27.879105, -7.733522, -0.32968175, 27.852928, -7.733522, -0.15553379, 27.18251, -7.7602315, -0.17903817, 27.820076, -7.7602315, -0.18144226, 27.18812, -7.733522, -0.15553379, 27.18251, -7.733522, -0.15286243, 27.826485, -7.7602315, -0.17903817, 27.820076, -7.733522, -0.15286243, 27.826485, -7.7602315, -0.32433987, 27.879105, -7.7602315, -0.17903817, 27.820076, -7.733522, -0.15286243, 27.826485, -7.733522, -0.31899786, 27.90528, -7.7602315, -0.32433987, 27.879105, -7.733522, -0.32968175, 27.852928, -7.7068114, -0.4752506, 27.825417, -7.7068114, -0.32433987, 27.879105, -7.733522, -0.32968175, 27.852928, -7.733522, -0.4506775, 27.814734, -7.7068114, -0.4752506, 27.825417, -7.7068114, -0.32433987, 27.879105, -7.733522, -0.4998237, 27.836102, -7.733522, -0.31899786, 27.90528, -7.7068114, -0.32433987, 27.879105, -7.7068114, -0.4752506, 27.825417, -7.733522, -0.4998237, 27.836102, -7.733522, -0.31899786, 27.90528, -7.7602315, -0.4752506, 27.825417, -7.7602315, -0.32433987, 27.879105, -7.733522, -0.31899786, 27.90528, -7.733522, -0.4998237, 27.836102, -7.7602315, -0.4752506, 27.825417, -7.7602315, -0.32433987, 27.879105, -7.733522, -0.4506775, 27.814734, -7.733522, -0.32968175, 27.852928, -7.7602315, -0.32433987, 27.879105, -7.7602315, -0.4752506, 27.825417, -7.733522, -0.4506775, 27.814734, -12.336702, -0.4667046, 24.562271, -12.306519, -0.48727155, 24.014719, -12.306519, -0.49421585, 24.571354, -12.336702, -0.4667046, 24.562271, -12.336702, -0.46002734, 24.024603, -12.306519, -0.48727155, 24.014719, -12.306519, -0.49421585, 24.571354, -12.336702, -0.51451564, 24.005104, -12.336702, -0.5219941, 24.580435, -12.306519, -0.49421585, 24.571354, -12.306519, -0.48727155, 24.014719, -12.336702, -0.51451564, 24.005104, -12.336702, -0.5219941, 24.580435, -12.366884, -0.48727155, 24.014719, -12.366884, -0.49421585, 24.571354, -12.336702, -0.5219941, 24.580435, -12.336702, -0.51451564, 24.005104, -12.366884, -0.48727155, 24.014719, -12.366884, -0.49421585, 24.571354, -12.336702, -0.46002734, 24.024603, -12.336702, -0.4667046, 24.562271, -12.366884, -0.49421585, 24.571354, -12.366884, -0.48727155, 24.014719, -12.336702, -0.46002734, 24.024603, -12.336702, -0.46002734, 24.024603, -12.306519, -0.31071925, 23.994152, -12.306519, -0.48727155, 24.014719, -12.336702, -0.46002734, 24.024603, -12.336702, -0.31793094, 24.016056, -12.306519, -0.31071925, 23.994152, -12.306519, -0.48727155, 24.014719, -12.336702, -0.3037746, 23.971983, -12.336702, -0.51451564, 24.005104, -12.306519, -0.48727155, 24.014719, -12.306519, -0.31071925, 23.994152, -12.336702, -0.3037746, 23.971983, -12.336702, -0.51451564, 24.005104, -12.366884, -0.31071925, 23.994152, -12.366884, -0.48727155, 24.014719, -12.336702, -0.51451564, 24.005104, -12.336702, -0.3037746, 23.971983, -12.366884, -0.31071925, 23.994152, -12.366884, -0.48727155, 24.014719, -12.336702, -0.31793094, 24.016056, -12.336702, -0.46002734, 24.024603, -12.366884, -0.48727155, 24.014719, -12.366884, -0.31071925, 23.994152, -12.336702, -0.31793094, 24.016056, -12.336702, -0.31793094, 24.016056, -12.306519, -0.1630137, 24.028341, -12.306519, -0.31071925, 23.994152, -12.336702, -0.31793094, 24.016056, -12.336702, -0.1923945, 24.03315, -12.306519, -0.1630137, 24.028341, -12.306519, -0.31071925, 23.994152, -12.336702, -0.13363278, 24.023268, -12.336702, -0.3037746, 23.971983, -12.306519, -0.31071925, 23.994152, -12.306519, -0.1630137, 24.028341, -12.336702, -0.13363278, 24.023268, -12.336702, -0.3037746, 23.971983, -12.366884, -0.1630137, 24.028341, -12.366884, -0.31071925, 23.994152, -12.336702, -0.3037746, 23.971983, -12.336702, -0.13363278, 24.023268, -12.366884, -0.1630137, 24.028341, -12.366884, -0.31071925, 23.994152, -12.336702, -0.1923945, 24.03315, -12.336702, -0.31793094, 24.016056, -12.366884, -0.31071925, 23.994152, -12.366884, -0.1630137, 24.028341, -12.336702, -0.1923945, 24.03315, -12.336702, -0.1923945, 24.03315, -12.306519, -0.1600753, 24.566813, -12.306519, -0.1630137, 24.028341, -12.336702, -0.1923945, 24.03315, -12.336702, -0.18918908, 24.56147, -12.306519, -0.1600753, 24.566813, -12.336702, -0.18918908, 24.56147, -12.306519, -0.3240739, 24.617027, -12.306519, -0.1600753, 24.566813, -12.336702, -0.18918908, 24.56147, -12.336702, -0.33021724, 24.594858, -12.306519, -0.3240739, 24.617027, -12.306519, -0.1630137, 24.028341, -12.336702, -0.13069439, 24.572422, -12.336702, -0.13363278, 24.023268, -12.306519, -0.1630137, 24.028341, -12.306519, -0.1600753, 24.566813, -12.336702, -0.13069439, 24.572422, -12.306519, -0.1600753, 24.566813, -12.336702, -0.31819773, 24.639463, -12.336702, -0.13069439, 24.572422, -12.306519, -0.1600753, 24.566813, -12.306519, -0.3240739, 24.617027, -12.336702, -0.31819773, 24.639463, -12.336702, -0.13363278, 24.023268, -12.366884, -0.1600753, 24.566813, -12.366884, -0.1630137, 24.028341, -12.336702, -0.13363278, 24.023268, -12.336702, -0.13069439, 24.572422, -12.366884, -0.1600753, 24.566813, -12.336702, -0.13069439, 24.572422, -12.366884, -0.3240739, 24.617027, -12.366884, -0.1600753, 24.566813, -12.336702, -0.13069439, 24.572422, -12.336702, -0.31819773, 24.639463, -12.366884, -0.3240739, 24.617027, -12.366884, -0.1630137, 24.028341, -12.336702, -0.18918908, 24.56147, -12.336702, -0.1923945, 24.03315, -12.366884, -0.1630137, 24.028341, -12.366884, -0.1600753, 24.566813, -12.336702, -0.18918908, 24.56147, -12.366884, -0.1600753, 24.566813, -12.336702, -0.33021724, 24.594858, -12.336702, -0.18918908, 24.56147, -12.366884, -0.1600753, 24.566813, -12.366884, -0.3240739, 24.617027, -12.336702, -0.33021724, 24.594858, -12.336702, -0.33021724, 24.594858, -12.306519, -0.49421585, 24.571354, -12.306519, -0.3240739, 24.617027, -12.336702, -0.33021724, 24.594858, -12.336702, -0.4667046, 24.562271, -12.306519, -0.49421585, 24.571354, -12.306519, -0.3240739, 24.617027, -12.336702, -0.5219941, 24.580435, -12.336702, -0.31819773, 24.639463, -12.306519, -0.3240739, 24.617027, -12.306519, -0.49421585, 24.571354, -12.336702, -0.5219941, 24.580435, -12.336702, -0.31819773, 24.639463, -12.366884, -0.49421585, 24.571354, -12.366884, -0.3240739, 24.617027, -12.336702, -0.31819773, 24.639463, -12.336702, -0.5219941, 24.580435, -12.366884, -0.49421585, 24.571354, -12.366884, -0.3240739, 24.617027, -12.336702, -0.4667046, 24.562271, -12.336702, -0.33021724, 24.594858, -12.366884, -0.3240739, 24.617027, -12.366884, -0.49421585, 24.571354, -12.336702, -0.4667046, 24.562271, -7.861996, -0.4506781, 26.201992, -7.8352866, -0.46884108, 25.559618, -7.8352866, -0.4752512, 26.212675, -7.861996, -0.4506781, 26.201992, -7.861996, -0.44480217, 25.570837, -7.8352866, -0.46884108, 25.559618, -7.861996, -0.44480217, 25.570837, -7.8352866, -0.31258833, 25.535313, -7.8352866, -0.46884108, 25.559618, -7.861996, -0.44480217, 25.570837, -7.861996, -0.3189987, 25.561222, -7.8352866, -0.31258833, 25.535313, -7.8352866, -0.4752512, 26.212675, -7.861996, -0.49314713, 25.5484, -7.861996, -0.4998243, 26.223358, -7.8352866, -0.4752512, 26.212675, -7.8352866, -0.46884108, 25.559618, -7.861996, -0.49314713, 25.5484, -7.8352866, -0.46884108, 25.559618, -7.861996, -0.30617797, 25.509403, -7.861996, -0.49314713, 25.5484, -7.8352866, -0.46884108, 25.559618, -7.8352866, -0.31258833, 25.535313, -7.861996, -0.30617797, 25.509403, -7.861996, -0.4998243, 26.223358, -7.8887057, -0.46884108, 25.559618, -7.8887057, -0.4752512, 26.212675, -7.861996, -0.4998243, 26.223358, -7.861996, -0.49314713, 25.5484, -7.8887057, -0.46884108, 25.559618, -7.861996, -0.49314713, 25.5484, -7.8887057, -0.31258833, 25.535313, -7.8887057, -0.46884108, 25.559618, -7.861996, -0.49314713, 25.5484, -7.861996, -0.30617797, 25.509403, -7.8887057, -0.31258833, 25.535313, -7.8887057, -0.4752512, 26.212675, -7.861996, -0.44480217, 25.570837, -7.861996, -0.4506781, 26.201992, -7.8887057, -0.4752512, 26.212675, -7.8887057, -0.46884108, 25.559618, -7.861996, -0.44480217, 25.570837, -7.8887057, -0.46884108, 25.559618, -7.861996, -0.3189987, 25.561222, -7.861996, -0.44480217, 25.570837, -7.8887057, -0.46884108, 25.559618, -7.8887057, -0.31258833, 25.535313, -7.861996, -0.3189987, 25.561222, -7.861996, -0.3189987, 25.561222, -7.8352866, -0.18144286, 25.575378, -7.8352866, -0.31258833, 25.535313, -7.861996, -0.3189987, 25.561222, -7.861996, -0.2076186, 25.580986, -7.8352866, -0.18144286, 25.575378, -7.861996, -0.2076186, 25.580986, -7.8352866, -0.17903876, 26.207333, -7.8352866, -0.18144286, 25.575378, -7.861996, -0.2076186, 25.580986, -7.861996, -0.20494735, 26.200922, -7.8352866, -0.17903876, 26.207333, -7.861996, -0.20494735, 26.200922, -7.8352866, -0.32434046, 26.266361, -7.8352866, -0.17903876, 26.207333, -7.861996, -0.20494735, 26.200922, -7.861996, -0.32968235, 26.240187, -7.8352866, -0.32434046, 26.266361, -7.8352866, -0.31258833, 25.535313, -7.861996, -0.15553439, 25.569769, -7.861996, -0.30617797, 25.509403, -7.8352866, -0.31258833, 25.535313, -7.8352866, -0.18144286, 25.575378, -7.861996, -0.15553439, 25.569769, -7.8352866, -0.18144286, 25.575378, -7.861996, -0.15286303, 26.213743, -7.861996, -0.15553439, 25.569769, -7.8352866, -0.18144286, 25.575378, -7.8352866, -0.17903876, 26.207333, -7.861996, -0.15286303, 26.213743, -7.8352866, -0.17903876, 26.207333, -7.861996, -0.31899846, 26.292538, -7.861996, -0.15286303, 26.213743, -7.8352866, -0.17903876, 26.207333, -7.8352866, -0.32434046, 26.266361, -7.861996, -0.31899846, 26.292538, -7.861996, -0.30617797, 25.509403, -7.8887057, -0.18144286, 25.575378, -7.8887057, -0.31258833, 25.535313, -7.861996, -0.30617797, 25.509403, -7.861996, -0.15553439, 25.569769, -7.8887057, -0.18144286, 25.575378, -7.8887057, -0.31258833, 25.535313, -7.861996, -0.2076186, 25.580986, -7.861996, -0.3189987, 25.561222, -7.8887057, -0.31258833, 25.535313, -7.8887057, -0.18144286, 25.575378, -7.861996, -0.2076186, 25.580986, -7.8887057, -0.18144286, 25.575378, -7.861996, -0.20494735, 26.200922, -7.861996, -0.2076186, 25.580986, -7.8887057, -0.18144286, 25.575378, -7.8887057, -0.17903876, 26.207333, -7.861996, -0.20494735, 26.200922, -7.8887057, -0.17903876, 26.207333, -7.861996, -0.32968235, 26.240187, -7.861996, -0.20494735, 26.200922, -7.8887057, -0.17903876, 26.207333, -7.8887057, -0.32434046, 26.266361, -7.861996, -0.32968235, 26.240187, -7.861996, -0.15553439, 25.569769, -7.8887057, -0.17903876, 26.207333, -7.8887057, -0.18144286, 25.575378, -7.861996, -0.15553439, 25.569769, -7.861996, -0.15286303, 26.213743, -7.8887057, -0.17903876, 26.207333, -7.861996, -0.15286303, 26.213743, -7.8887057, -0.32434046, 26.266361, -7.8887057, -0.17903876, 26.207333, -7.861996, -0.15286303, 26.213743, -7.861996, -0.31899846, 26.292538, -7.8887057, -0.32434046, 26.266361, -7.861996, -0.32968235, 26.240187, -7.8352866, -0.4752512, 26.212675, -7.8352866, -0.32434046, 26.266361, -7.861996, -0.32968235, 26.240187, -7.861996, -0.4506781, 26.201992, -7.8352866, -0.4752512, 26.212675, -7.8352866, -0.32434046, 26.266361, -7.861996, -0.4998243, 26.223358, -7.861996, -0.31899846, 26.292538, -7.8352866, -0.32434046, 26.266361, -7.8352866, -0.4752512, 26.212675, -7.861996, -0.4998243, 26.223358, -7.861996, -0.31899846, 26.292538, -7.8887057, -0.4752512, 26.212675, -7.8887057, -0.32434046, 26.266361, -7.861996, -0.31899846, 26.292538, -7.861996, -0.4998243, 26.223358, -7.8887057, -0.4752512, 26.212675, -7.8887057, -0.32434046, 26.266361, -7.861996, -0.4506781, 26.201992, -7.861996, -0.32968235, 26.240187, -7.8887057, -0.32434046, 26.266361, -7.8887057, -0.4752512, 26.212675, -7.861996, -0.4506781, 26.201992, -7.6643434, -0.45067704, 29.339333, -7.637634, -0.46884, 28.697227, -7.637634, -0.47525012, 29.350018, -7.6643434, -0.45067704, 29.339333, -7.6643434, -0.4448011, 28.708447, -7.637634, -0.46884, 28.697227, -7.6643434, -0.4448011, 28.708447, -7.637634, -0.31258726, 28.672655, -7.637634, -0.46884, 28.697227, -7.6643434, -0.4448011, 28.708447, -7.6643434, -0.31899762, 28.698563, -7.637634, -0.31258726, 28.672655, -7.637634, -0.47525012, 29.350018, -7.6643434, -0.49314606, 28.685741, -7.6643434, -0.4998232, 29.360968, -7.637634, -0.47525012, 29.350018, -7.637634, -0.46884, 28.697227, -7.6643434, -0.49314606, 28.685741, -7.637634, -0.46884, 28.697227, -7.6643434, -0.3061769, 28.646746, -7.6643434, -0.49314606, 28.685741, -7.637634, -0.46884, 28.697227, -7.637634, -0.31258726, 28.672655, -7.6643434, -0.3061769, 28.646746, -7.6643434, -0.4998232, 29.360968, -7.691053, -0.46884, 28.697227, -7.691053, -0.47525012, 29.350018, -7.6643434, -0.4998232, 29.360968, -7.6643434, -0.49314606, 28.685741, -7.691053, -0.46884, 28.697227, -7.6643434, -0.49314606, 28.685741, -7.691053, -0.31258726, 28.672655, -7.691053, -0.46884, 28.697227, -7.6643434, -0.49314606, 28.685741, -7.6643434, -0.3061769, 28.646746, -7.691053, -0.31258726, 28.672655, -7.691053, -0.47525012, 29.350018, -7.6643434, -0.4448011, 28.708447, -7.6643434, -0.45067704, 29.339333, -7.691053, -0.47525012, 29.350018, -7.691053, -0.46884, 28.697227, -7.6643434, -0.4448011, 28.708447, -7.691053, -0.46884, 28.697227, -7.6643434, -0.31899762, 28.698563, -7.6643434, -0.4448011, 28.708447, -7.691053, -0.46884, 28.697227, -7.691053, -0.31258726, 28.672655, -7.6643434, -0.31899762, 28.698563, -7.6643434, -0.31899762, 28.698563, -7.637634, -0.18144178, 28.712986, -7.637634, -0.31258726, 28.672655, -7.6643434, -0.31899762, 28.698563, -7.6643434, -0.2076174, 28.718596, -7.637634, -0.18144178, 28.712986, -7.6643434, -0.2076174, 28.718596, -7.637634, -0.17903769, 29.344942, -7.637634, -0.18144178, 28.712986, -7.6643434, -0.2076174, 28.718596, -7.6643434, -0.20494628, 29.338531, -7.637634, -0.17903769, 29.344942, -7.6643434, -0.20494628, 29.338531, -7.637634, -0.32433927, 29.40397, -7.637634, -0.17903769, 29.344942, -7.6643434, -0.20494628, 29.338531, -7.6643434, -0.32968128, 29.377794, -7.637634, -0.32433927, 29.40397, -7.637634, -0.31258726, 28.672655, -7.6643434, -0.15553331, 28.70711, -7.6643434, -0.3061769, 28.646746, -7.637634, -0.31258726, 28.672655, -7.637634, -0.18144178, 28.712986, -7.6643434, -0.15553331, 28.70711, -7.637634, -0.18144178, 28.712986, -7.6643434, -0.15286195, 29.351353, -7.6643434, -0.15553331, 28.70711, -7.637634, -0.18144178, 28.712986, -7.637634, -0.17903769, 29.344942, -7.6643434, -0.15286195, 29.351353, -7.637634, -0.17903769, 29.344942, -7.6643434, -0.31899726, 29.42988, -7.6643434, -0.15286195, 29.351353, -7.637634, -0.17903769, 29.344942, -7.637634, -0.32433927, 29.40397, -7.6643434, -0.31899726, 29.42988, -7.6643434, -0.3061769, 28.646746, -7.691053, -0.18144178, 28.712986, -7.691053, -0.31258726, 28.672655, -7.6643434, -0.3061769, 28.646746, -7.6643434, -0.15553331, 28.70711, -7.691053, -0.18144178, 28.712986, -7.691053, -0.31258726, 28.672655, -7.6643434, -0.2076174, 28.718596, -7.6643434, -0.31899762, 28.698563, -7.691053, -0.31258726, 28.672655, -7.691053, -0.18144178, 28.712986, -7.6643434, -0.2076174, 28.718596, -7.691053, -0.18144178, 28.712986, -7.6643434, -0.20494628, 29.338531, -7.6643434, -0.2076174, 28.718596, -7.691053, -0.18144178, 28.712986, -7.691053, -0.17903769, 29.344942, -7.6643434, -0.20494628, 29.338531, -7.691053, -0.17903769, 29.344942, -7.6643434, -0.32968128, 29.377794, -7.6643434, -0.20494628, 29.338531, -7.691053, -0.17903769, 29.344942, -7.691053, -0.32433927, 29.40397, -7.6643434, -0.32968128, 29.377794, -7.6643434, -0.15553331, 28.70711, -7.691053, -0.17903769, 29.344942, -7.691053, -0.18144178, 28.712986, -7.6643434, -0.15553331, 28.70711, -7.6643434, -0.15286195, 29.351353, -7.691053, -0.17903769, 29.344942, -7.6643434, -0.15286195, 29.351353, -7.691053, -0.32433927, 29.40397, -7.691053, -0.17903769, 29.344942, -7.6643434, -0.15286195, 29.351353, -7.6643434, -0.31899726, 29.42988, -7.691053, -0.32433927, 29.40397, -7.6643434, -0.32968128, 29.377794, -7.637634, -0.47525012, 29.350018, -7.637634, -0.32433927, 29.40397, -7.6643434, -0.32968128, 29.377794, -7.6643434, -0.45067704, 29.339333, -7.637634, -0.47525012, 29.350018, -7.637634, -0.32433927, 29.40397, -7.6643434, -0.4998232, 29.360968, -7.6643434, -0.31899726, 29.42988, -7.637634, -0.32433927, 29.40397, -7.637634, -0.47525012, 29.350018, -7.6643434, -0.4998232, 29.360968, -7.6643434, -0.31899726, 29.42988, -7.691053, -0.47525012, 29.350018, -7.691053, -0.32433927, 29.40397, -7.6643434, -0.31899726, 29.42988, -7.6643434, -0.4998232, 29.360968, -7.691053, -0.47525012, 29.350018, -7.691053, -0.32433927, 29.40397, -7.6643434, -0.45067704, 29.339333, -7.6643434, -0.32968128, 29.377794, -7.691053, -0.32433927, 29.40397, -7.691053, -0.47525012, 29.350018, -7.6643434, -0.45067704, 29.339333, -12.288357, -0.46670258, 30.184967, -12.258175, -0.48726952, 29.686295, -12.258175, -0.49421382, 30.193247, -12.288357, -0.46670258, 30.184967, -12.288357, -0.4600253, 29.694841, -12.258175, -0.48726952, 29.686295, -12.258175, -0.49421382, 30.193247, -12.288357, -0.5145136, 29.67748, -12.288357, -0.5219921, 30.201527, -12.258175, -0.49421382, 30.193247, -12.258175, -0.48726952, 29.686295, -12.288357, -0.5145136, 29.67748, -12.288357, -0.5219921, 30.201527, -12.318539, -0.48726952, 29.686295, -12.318539, -0.49421382, 30.193247, -12.288357, -0.5219921, 30.201527, -12.288357, -0.5145136, 29.67748, -12.318539, -0.48726952, 29.686295, -12.318539, -0.49421382, 30.193247, -12.288357, -0.4600253, 29.694841, -12.288357, -0.46670258, 30.184967, -12.318539, -0.49421382, 30.193247, -12.318539, -0.48726952, 29.686295, -12.288357, -0.4600253, 29.694841, -12.288357, -0.4600253, 29.694841, -12.258175, -0.31071723, 29.66733, -12.258175, -0.48726952, 29.686295, -12.288357, -0.4600253, 29.694841, -12.288357, -0.3179289, 29.687363, -12.258175, -0.31071723, 29.66733, -12.258175, -0.48726952, 29.686295, -12.288357, -0.30377257, 29.64703, -12.288357, -0.5145136, 29.67748, -12.258175, -0.48726952, 29.686295, -12.258175, -0.31071723, 29.66733, -12.288357, -0.30377257, 29.64703, -12.288357, -0.5145136, 29.67748, -12.318539, -0.31071723, 29.66733, -12.318539, -0.48726952, 29.686295, -12.288357, -0.5145136, 29.67748, -12.288357, -0.30377257, 29.64703, -12.318539, -0.31071723, 29.66733, -12.318539, -0.48726952, 29.686295, -12.288357, -0.3179289, 29.687363, -12.288357, -0.4600253, 29.694841, -12.318539, -0.48726952, 29.686295, -12.318539, -0.31071723, 29.66733, -12.288357, -0.3179289, 29.687363, -12.288357, -0.3179289, 29.687363, -12.258175, -0.16301167, 29.698313, -12.258175, -0.31071723, 29.66733, -12.288357, -0.3179289, 29.687363, -12.288357, -0.19239247, 29.702854, -12.258175, -0.16301167, 29.698313, -12.258175, -0.31071723, 29.66733, -12.288357, -0.13363075, 29.69404, -12.288357, -0.30377257, 29.64703, -12.258175, -0.31071723, 29.66733, -12.258175, -0.16301167, 29.698313, -12.288357, -0.13363075, 29.69404, -12.288357, -0.30377257, 29.64703, -12.318539, -0.16301167, 29.698313, -12.318539, -0.31071723, 29.66733, -12.288357, -0.30377257, 29.64703, -12.288357, -0.13363075, 29.69404, -12.318539, -0.16301167, 29.698313, -12.318539, -0.31071723, 29.66733, -12.288357, -0.19239247, 29.702854, -12.288357, -0.3179289, 29.687363, -12.318539, -0.31071723, 29.66733, -12.318539, -0.16301167, 29.698313, -12.288357, -0.19239247, 29.702854, -12.288357, -0.19239247, 29.702854, -12.258175, -0.16007328, 30.189241, -12.258175, -0.16301167, 29.698313, -12.288357, -0.19239247, 29.702854, -12.288357, -0.18918705, 30.184166, -12.258175, -0.16007328, 30.189241, -12.258175, -0.16301167, 29.698313, -12.288357, -0.13069236, 30.19405, -12.288357, -0.13363075, 29.69404, -12.258175, -0.16301167, 29.698313, -12.258175, -0.16007328, 30.189241, -12.288357, -0.13069236, 30.19405, -12.288357, -0.13363075, 29.69404, -12.318539, -0.16007328, 30.189241, -12.318539, -0.16301167, 29.698313, -12.288357, -0.13363075, 29.69404, -12.288357, -0.13069236, 30.19405, -12.318539, -0.16007328, 30.189241, -12.318539, -0.16301167, 29.698313, -12.288357, -0.18918705, 30.184166, -12.288357, -0.19239247, 29.702854, -12.318539, -0.16301167, 29.698313, -12.318539, -0.16007328, 30.189241, -12.288357, -0.18918705, 30.184166, -12.288357, -0.18918705, 30.184166, -12.258175, -0.32407188, 30.234915, -12.258175, -0.16007328, 30.189241, -12.288357, -0.18918705, 30.184166, -12.288357, -0.33021522, 30.214615, -12.258175, -0.32407188, 30.234915, -12.258175, -0.16007328, 30.189241, -12.288357, -0.3181957, 30.255215, -12.288357, -0.13069236, 30.19405, -12.258175, -0.16007328, 30.189241, -12.258175, -0.32407188, 30.234915, -12.288357, -0.3181957, 30.255215, -12.288357, -0.13069236, 30.19405, -12.318539, -0.32407188, 30.234915, -12.318539, -0.16007328, 30.189241, -12.288357, -0.13069236, 30.19405, -12.288357, -0.3181957, 30.255215, -12.318539, -0.32407188, 30.234915, -12.318539, -0.16007328, 30.189241, -12.288357, -0.33021522, 30.214615, -12.288357, -0.18918705, 30.184166, -12.318539, -0.16007328, 30.189241, -12.318539, -0.32407188, 30.234915, -12.288357, -0.33021522, 30.214615, -12.288357, -0.33021522, 30.214615, -12.258175, -0.49421382, 30.193247, -12.258175, -0.32407188, 30.234915, -12.288357, -0.33021522, 30.214615, -12.288357, -0.46670258, 30.184967, -12.258175, -0.49421382, 30.193247, -12.258175, -0.32407188, 30.234915, -12.288357, -0.5219921, 30.201527, -12.288357, -0.3181957, 30.255215, -12.258175, -0.32407188, 30.234915, -12.258175, -0.49421382, 30.193247, -12.288357, -0.5219921, 30.201527, -12.288357, -0.3181957, 30.255215, -12.318539, -0.49421382, 30.193247, -12.318539, -0.32407188, 30.234915, -12.288357, -0.3181957, 30.255215, -12.288357, -0.5219921, 30.201527, -12.318539, -0.49421382, 30.193247, -12.318539, -0.32407188, 30.234915, -12.288357, -0.46670258, 30.184967, -12.288357, -0.33021522, 30.214615, -12.318539, -0.32407188, 30.234915, -12.318539, -0.49421382, 30.193247, -12.288357, -0.46670258, 30.184967, -12.336702, -0.46670234, 31.048765, -12.306519, -0.48726916, 30.501213, -12.306519, -0.49421346, 31.057846, -12.336702, -0.46670234, 31.048765, -12.336702, -0.46002495, 30.511095, -12.306519, -0.48726916, 30.501213, -12.306519, -0.49421346, 31.057846, -12.336702, -0.51451325, 30.491863, -12.336702, -0.5219917, 31.066927, -12.306519, -0.49421346, 31.057846, -12.306519, -0.48726916, 30.501213, -12.336702, -0.51451325, 30.491863, -12.336702, -0.5219917, 31.066927, -12.366884, -0.48726916, 30.501213, -12.366884, -0.49421346, 31.057846, -12.336702, -0.5219917, 31.066927, -12.336702, -0.51451325, 30.491863, -12.366884, -0.48726916, 30.501213, -12.366884, -0.49421346, 31.057846, -12.336702, -0.46002495, 30.511095, -12.336702, -0.46670234, 31.048765, -12.366884, -0.49421346, 31.057846, -12.366884, -0.48726916, 30.501213, -12.336702, -0.46002495, 30.511095, -12.336702, -0.46002495, 30.511095, -12.306519, -0.31071687, 30.480646, -12.306519, -0.48726916, 30.501213, -12.336702, -0.46002495, 30.511095, -12.336702, -0.31792855, 30.502815, -12.306519, -0.31071687, 30.480646, -12.306519, -0.48726916, 30.501213, -12.336702, -0.3037722, 30.458477, -12.336702, -0.51451325, 30.491863, -12.306519, -0.48726916, 30.501213, -12.306519, -0.31071687, 30.480646, -12.336702, -0.3037722, 30.458477, -12.336702, -0.51451325, 30.491863, -12.366884, -0.31071687, 30.480646, -12.366884, -0.48726916, 30.501213, -12.336702, -0.51451325, 30.491863, -12.336702, -0.3037722, 30.458477, -12.366884, -0.31071687, 30.480646, -12.366884, -0.48726916, 30.501213, -12.336702, -0.31792855, 30.502815, -12.336702, -0.46002495, 30.511095, -12.366884, -0.48726916, 30.501213, -12.366884, -0.31071687, 30.480646, -12.336702, -0.31792855, 30.502815, -12.336702, -0.31792855, 30.502815, -12.306519, -0.16301131, 30.514835, -12.306519, -0.31071687, 30.480646, -12.336702, -0.31792855, 30.502815, -12.336702, -0.19239211, 30.519642, -12.306519, -0.16301131, 30.514835, -12.306519, -0.31071687, 30.480646, -12.336702, -0.1336304, 30.50976, -12.336702, -0.3037722, 30.458477, -12.306519, -0.31071687, 30.480646, -12.306519, -0.16301131, 30.514835, -12.336702, -0.1336304, 30.50976, -12.336702, -0.3037722, 30.458477, -12.366884, -0.16301131, 30.514835, -12.366884, -0.31071687, 30.480646, -12.336702, -0.3037722, 30.458477, -12.336702, -0.1336304, 30.50976, -12.366884, -0.16301131, 30.514835, -12.366884, -0.31071687, 30.480646, -12.336702, -0.19239211, 30.519642, -12.336702, -0.31792855, 30.502815, -12.366884, -0.31071687, 30.480646, -12.366884, -0.16301131, 30.514835, -12.336702, -0.19239211, 30.519642, -12.336702, -0.19239211, 30.519642, -12.306519, -0.16007292, 31.053305, -12.306519, -0.16301131, 30.514835, -12.336702, -0.19239211, 30.519642, -12.336702, -0.18918681, 31.047964, -12.306519, -0.16007292, 31.053305, -12.336702, -0.18918681, 31.047964, -12.306519, -0.32407153, 31.10352, -12.306519, -0.16007292, 31.053305, -12.336702, -0.18918681, 31.047964, -12.336702, -0.33021486, 31.08135, -12.306519, -0.32407153, 31.10352, -12.306519, -0.16301131, 30.514835, -12.336702, -0.130692, 31.058914, -12.336702, -0.1336304, 30.50976, -12.306519, -0.16301131, 30.514835, -12.306519, -0.16007292, 31.053305, -12.336702, -0.130692, 31.058914, -12.306519, -0.16007292, 31.053305, -12.336702, -0.31819534, 31.125956, -12.336702, -0.130692, 31.058914, -12.306519, -0.16007292, 31.053305, -12.306519, -0.32407153, 31.10352, -12.336702, -0.31819534, 31.125956, -12.336702, -0.1336304, 30.50976, -12.366884, -0.16007292, 31.053305, -12.366884, -0.16301131, 30.514835, -12.336702, -0.1336304, 30.50976, -12.336702, -0.130692, 31.058914, -12.366884, -0.16007292, 31.053305, -12.336702, -0.130692, 31.058914, -12.366884, -0.32407153, 31.10352, -12.366884, -0.16007292, 31.053305, -12.336702, -0.130692, 31.058914, -12.336702, -0.31819534, 31.125956, -12.366884, -0.32407153, 31.10352, -12.366884, -0.16301131, 30.514835, -12.336702, -0.18918681, 31.047964, -12.336702, -0.19239211, 30.519642, -12.366884, -0.16301131, 30.514835, -12.366884, -0.16007292, 31.053305, -12.336702, -0.18918681, 31.047964, -12.366884, -0.16007292, 31.053305, -12.336702, -0.33021486, 31.08135, -12.336702, -0.18918681, 31.047964, -12.366884, -0.16007292, 31.053305, -12.366884, -0.32407153, 31.10352, -12.336702, -0.33021486, 31.08135, -12.336702, -0.33021486, 31.08135, -12.306519, -0.49421346, 31.057846, -12.306519, -0.32407153, 31.10352, -12.336702, -0.33021486, 31.08135, -12.336702, -0.46670234, 31.048765, -12.306519, -0.49421346, 31.057846, -12.306519, -0.32407153, 31.10352, -12.336702, -0.5219917, 31.066927, -12.336702, -0.31819534, 31.125956, -12.306519, -0.32407153, 31.10352, -12.306519, -0.49421346, 31.057846, -12.336702, -0.5219917, 31.066927, -12.336702, -0.31819534, 31.125956, -12.366884, -0.49421346, 31.057846, -12.366884, -0.32407153, 31.10352, -12.336702, -0.31819534, 31.125956, -12.336702, -0.5219917, 31.066927, -12.366884, -0.49421346, 31.057846, -12.366884, -0.32407153, 31.10352, -12.336702, -0.46670234, 31.048765, -12.336702, -0.33021486, 31.08135, -12.366884, -0.32407153, 31.10352, -12.366884, -0.49421346, 31.057846, -12.336702, -0.46670234, 31.048765, -7.713756, -0.45067632, 31.066927, -7.6870465, -0.4688393, 30.424555, -7.6870465, -0.4752494, 31.077612, -7.713756, -0.45067632, 31.066927, -7.713756, -0.44480038, 30.435774, -7.6870465, -0.4688393, 30.424555, -7.713756, -0.44480038, 30.435774, -7.6870465, -0.31258655, 30.40025, -7.6870465, -0.4688393, 30.424555, -7.713756, -0.44480038, 30.435774, -7.713756, -0.3189969, 30.42589, -7.6870465, -0.31258655, 30.40025, -7.6870465, -0.4752494, 31.077612, -7.713756, -0.49314535, 30.413338, -7.713756, -0.4998225, 31.088295, -7.6870465, -0.4752494, 31.077612, -7.6870465, -0.4688393, 30.424555, -7.713756, -0.49314535, 30.413338, -7.6870465, -0.4688393, 30.424555, -7.713756, -0.3061763, 30.374073, -7.713756, -0.49314535, 30.413338, -7.6870465, -0.4688393, 30.424555, -7.6870465, -0.31258655, 30.40025, -7.713756, -0.3061763, 30.374073, -7.713756, -0.4998225, 31.088295, -7.7404666, -0.4688393, 30.424555, -7.7404666, -0.4752494, 31.077612, -7.713756, -0.4998225, 31.088295, -7.713756, -0.49314535, 30.413338, -7.7404666, -0.4688393, 30.424555, -7.713756, -0.49314535, 30.413338, -7.7404666, -0.31258655, 30.40025, -7.7404666, -0.4688393, 30.424555, -7.713756, -0.49314535, 30.413338, -7.713756, -0.3061763, 30.374073, -7.7404666, -0.31258655, 30.40025, -7.7404666, -0.4752494, 31.077612, -7.713756, -0.44480038, 30.435774, -7.713756, -0.45067632, 31.066927, -7.7404666, -0.4752494, 31.077612, -7.7404666, -0.4688393, 30.424555, -7.713756, -0.44480038, 30.435774, -7.7404666, -0.4688393, 30.424555, -7.713756, -0.3189969, 30.42589, -7.713756, -0.44480038, 30.435774, -7.7404666, -0.4688393, 30.424555, -7.7404666, -0.31258655, 30.40025, -7.713756, -0.3189969, 30.42589, -7.713756, -0.3189969, 30.42589, -7.6870465, -0.18144107, 30.440313, -7.6870465, -0.31258655, 30.40025, -7.713756, -0.3189969, 30.42589, -7.713756, -0.2076168, 30.445923, -7.6870465, -0.18144107, 30.440313, -7.713756, -0.2076168, 30.445923, -7.6870465, -0.17903697, 31.07227, -7.6870465, -0.18144107, 30.440313, -7.713756, -0.2076168, 30.445923, -7.713756, -0.20494556, 31.065859, -7.6870465, -0.17903697, 31.07227, -7.713756, -0.20494556, 31.065859, -7.6870465, -0.32433867, 31.131298, -7.6870465, -0.17903697, 31.07227, -7.713756, -0.20494556, 31.065859, -7.713756, -0.32968056, 31.105122, -7.6870465, -0.32433867, 31.131298, -7.6870465, -0.31258655, 30.40025, -7.713756, -0.1555326, 30.434437, -7.713756, -0.3061763, 30.374073, -7.6870465, -0.31258655, 30.40025, -7.6870465, -0.18144107, 30.440313, -7.713756, -0.1555326, 30.434437, -7.6870465, -0.18144107, 30.440313, -7.713756, -0.15286124, 31.07868, -7.713756, -0.1555326, 30.434437, -7.6870465, -0.18144107, 30.440313, -7.6870465, -0.17903697, 31.07227, -7.713756, -0.15286124, 31.07868, -7.6870465, -0.17903697, 31.07227, -7.713756, -0.31899667, 31.157475, -7.713756, -0.15286124, 31.07868, -7.6870465, -0.17903697, 31.07227, -7.6870465, -0.32433867, 31.131298, -7.713756, -0.31899667, 31.157475, -7.713756, -0.3061763, 30.374073, -7.7404666, -0.18144107, 30.440313, -7.7404666, -0.31258655, 30.40025, -7.713756, -0.3061763, 30.374073, -7.713756, -0.1555326, 30.434437, -7.7404666, -0.18144107, 30.440313, -7.7404666, -0.31258655, 30.40025, -7.713756, -0.2076168, 30.445923, -7.713756, -0.3189969, 30.42589, -7.7404666, -0.31258655, 30.40025, -7.7404666, -0.18144107, 30.440313, -7.713756, -0.2076168, 30.445923, -7.7404666, -0.18144107, 30.440313, -7.713756, -0.20494556, 31.065859, -7.713756, -0.2076168, 30.445923, -7.7404666, -0.18144107, 30.440313, -7.7404666, -0.17903697, 31.07227, -7.713756, -0.20494556, 31.065859, -7.7404666, -0.17903697, 31.07227, -7.713756, -0.32968056, 31.105122, -7.713756, -0.20494556, 31.065859, -7.7404666, -0.17903697, 31.07227, -7.7404666, -0.32433867, 31.131298, -7.713756, -0.32968056, 31.105122, -7.713756, -0.1555326, 30.434437, -7.7404666, -0.17903697, 31.07227, -7.7404666, -0.18144107, 30.440313, -7.713756, -0.1555326, 30.434437, -7.713756, -0.15286124, 31.07868, -7.7404666, -0.17903697, 31.07227, -7.713756, -0.15286124, 31.07868, -7.7404666, -0.32433867, 31.131298, -7.7404666, -0.17903697, 31.07227, -7.713756, -0.15286124, 31.07868, -7.713756, -0.31899667, 31.157475, -7.7404666, -0.32433867, 31.131298, -7.713756, -0.32968056, 31.105122, -7.6870465, -0.4752494, 31.077612, -7.6870465, -0.32433867, 31.131298, -7.713756, -0.32968056, 31.105122, -7.713756, -0.45067632, 31.066927, -7.6870465, -0.4752494, 31.077612, -7.6870465, -0.32433867, 31.131298, -7.713756, -0.4998225, 31.088295, -7.713756, -0.31899667, 31.157475, -7.6870465, -0.32433867, 31.131298, -7.6870465, -0.4752494, 31.077612, -7.713756, -0.4998225, 31.088295, -7.713756, -0.31899667, 31.157475, -7.7404666, -0.4752494, 31.077612, -7.7404666, -0.32433867, 31.131298, -7.713756, -0.31899667, 31.157475, -7.713756, -0.4998225, 31.088295, -7.7404666, -0.4752494, 31.077612, -7.7404666, -0.32433867, 31.131298, -7.713756, -0.45067632, 31.066927, -7.713756, -0.32968056, 31.105122, -7.7404666, -0.32433867, 31.131298, -7.7404666, -0.4752494, 31.077612, -7.713756, -0.45067632, 31.066927, -7.713756, -0.45067608, 31.842316, -7.6870465, -0.46883905, 31.20021, -7.6870465, -0.47524917, 31.852999, -7.713756, -0.45067608, 31.842316, -7.713756, -0.44480014, 31.211428, -7.6870465, -0.46883905, 31.20021, -7.713756, -0.44480014, 31.211428, -7.6870465, -0.3125863, 31.175636, -7.6870465, -0.46883905, 31.20021, -7.713756, -0.44480014, 31.211428, -7.713756, -0.31899667, 31.201546, -7.6870465, -0.3125863, 31.175636, -7.6870465, -0.47524917, 31.852999, -7.713756, -0.4931451, 31.188992, -7.713756, -0.49982226, 31.863684, -7.6870465, -0.47524917, 31.852999, -7.6870465, -0.46883905, 31.20021, -7.713756, -0.4931451, 31.188992, -7.6870465, -0.46883905, 31.20021, -7.713756, -0.30617595, 31.149729, -7.713756, -0.4931451, 31.188992, -7.6870465, -0.46883905, 31.20021, -7.6870465, -0.3125863, 31.175636, -7.713756, -0.30617595, 31.149729, -7.713756, -0.49982226, 31.863684, -7.7404666, -0.46883905, 31.20021, -7.7404666, -0.47524917, 31.852999, -7.713756, -0.49982226, 31.863684, -7.713756, -0.4931451, 31.188992, -7.7404666, -0.46883905, 31.20021, -7.713756, -0.4931451, 31.188992, -7.7404666, -0.3125863, 31.175636, -7.7404666, -0.46883905, 31.20021, -7.713756, -0.4931451, 31.188992, -7.713756, -0.30617595, 31.149729, -7.7404666, -0.3125863, 31.175636, -7.7404666, -0.47524917, 31.852999, -7.713756, -0.44480014, 31.211428, -7.713756, -0.45067608, 31.842316, -7.7404666, -0.47524917, 31.852999, -7.7404666, -0.46883905, 31.20021, -7.713756, -0.44480014, 31.211428, -7.7404666, -0.46883905, 31.20021, -7.713756, -0.31899667, 31.201546, -7.713756, -0.44480014, 31.211428, -7.7404666, -0.46883905, 31.20021, -7.7404666, -0.3125863, 31.175636, -7.713756, -0.31899667, 31.201546, -7.713756, -0.31899667, 31.201546, -7.6870465, -0.18144083, 31.21597, -7.6870465, -0.3125863, 31.175636, -7.713756, -0.31899667, 31.201546, -7.713756, -0.20761657, 31.221577, -7.6870465, -0.18144083, 31.21597, -7.713756, -0.20761657, 31.221577, -7.6870465, -0.17903674, 31.847923, -7.6870465, -0.18144083, 31.21597, -7.713756, -0.20761657, 31.221577, -7.713756, -0.20494533, 31.841515, -7.6870465, -0.17903674, 31.847923, -7.713756, -0.20494533, 31.841515, -7.6870465, -0.32433844, 31.906685, -7.6870465, -0.17903674, 31.847923, -7.713756, -0.20494533, 31.841515, -7.713756, -0.32968032, 31.88051, -7.6870465, -0.32433844, 31.906685, -7.6870465, -0.3125863, 31.175636, -7.713756, -0.15553236, 31.210093, -7.713756, -0.30617595, 31.149729, -7.6870465, -0.3125863, 31.175636, -7.6870465, -0.18144083, 31.21597, -7.713756, -0.15553236, 31.210093, -7.6870465, -0.18144083, 31.21597, -7.713756, -0.152861, 31.854067, -7.713756, -0.15553236, 31.210093, -7.6870465, -0.18144083, 31.21597, -7.6870465, -0.17903674, 31.847923, -7.713756, -0.152861, 31.854067, -7.6870465, -0.17903674, 31.847923, -7.713756, -0.31899643, 31.932861, -7.713756, -0.152861, 31.854067, -7.6870465, -0.17903674, 31.847923, -7.6870465, -0.32433844, 31.906685, -7.713756, -0.31899643, 31.932861, -7.713756, -0.30617595, 31.149729, -7.7404666, -0.18144083, 31.21597, -7.7404666, -0.3125863, 31.175636, -7.713756, -0.30617595, 31.149729, -7.713756, -0.15553236, 31.210093, -7.7404666, -0.18144083, 31.21597, -7.7404666, -0.3125863, 31.175636, -7.713756, -0.20761657, 31.221577, -7.713756, -0.31899667, 31.201546, -7.7404666, -0.3125863, 31.175636, -7.7404666, -0.18144083, 31.21597, -7.713756, -0.20761657, 31.221577, -7.7404666, -0.18144083, 31.21597, -7.713756, -0.20494533, 31.841515, -7.713756, -0.20761657, 31.221577, -7.7404666, -0.18144083, 31.21597, -7.7404666, -0.17903674, 31.847923, -7.713756, -0.20494533, 31.841515, -7.7404666, -0.17903674, 31.847923, -7.713756, -0.32968032, 31.88051, -7.713756, -0.20494533, 31.841515, -7.7404666, -0.17903674, 31.847923, -7.7404666, -0.32433844, 31.906685, -7.713756, -0.32968032, 31.88051, -7.713756, -0.15553236, 31.210093, -7.7404666, -0.17903674, 31.847923, -7.7404666, -0.18144083, 31.21597, -7.713756, -0.15553236, 31.210093, -7.713756, -0.152861, 31.854067, -7.7404666, -0.17903674, 31.847923, -7.713756, -0.152861, 31.854067, -7.7404666, -0.32433844, 31.906685, -7.7404666, -0.17903674, 31.847923, -7.713756, -0.152861, 31.854067, -7.713756, -0.31899643, 31.932861, -7.7404666, -0.32433844, 31.906685, -7.713756, -0.32968032, 31.88051, -7.6870465, -0.47524917, 31.852999, -7.6870465, -0.32433844, 31.906685, -7.713756, -0.32968032, 31.88051, -7.713756, -0.45067608, 31.842316, -7.6870465, -0.47524917, 31.852999, -7.6870465, -0.32433844, 31.906685, -7.713756, -0.49982226, 31.863684, -7.713756, -0.31899643, 31.932861, -7.6870465, -0.32433844, 31.906685, -7.6870465, -0.47524917, 31.852999, -7.713756, -0.49982226, 31.863684, -7.713756, -0.31899643, 31.932861, -7.7404666, -0.47524917, 31.852999, -7.7404666, -0.32433844, 31.906685, -7.713756, -0.31899643, 31.932861, -7.713756, -0.49982226, 31.863684, -7.7404666, -0.47524917, 31.852999, -7.7404666, -0.32433844, 31.906685, -7.713756, -0.45067608, 31.842316, -7.713756, -0.32968032, 31.88051, -7.7404666, -0.32433844, 31.906685, -7.7404666, -0.47524917, 31.852999, -7.713756, -0.45067608, 31.842316, -12.244019, -0.46670198, 31.902145, -12.213837, -0.48726892, 31.221043, -12.213837, -0.49421322, 31.913631, -12.244019, -0.46670198, 31.902145, -12.244019, -0.4600247, 31.233063, -12.213837, -0.48726892, 31.221043, -12.244019, -0.4600247, 31.233063, -12.213837, -0.31071663, 31.195135, -12.213837, -0.48726892, 31.221043, -12.244019, -0.4600247, 31.233063, -12.244019, -0.3179283, 31.222378, -12.213837, -0.31071663, 31.195135, -12.213837, -0.49421322, 31.913631, -12.244019, -0.514513, 31.209024, -12.244019, -0.5219915, 31.924849, -12.213837, -0.49421322, 31.913631, -12.213837, -0.48726892, 31.221043, -12.244019, -0.514513, 31.209024, -12.213837, -0.48726892, 31.221043, -12.244019, -0.30377197, 31.167624, -12.244019, -0.514513, 31.209024, -12.213837, -0.48726892, 31.221043, -12.213837, -0.31071663, 31.195135, -12.244019, -0.30377197, 31.167624, -12.244019, -0.5219915, 31.924849, -12.2742, -0.48726892, 31.221043, -12.2742, -0.49421322, 31.913631, -12.244019, -0.5219915, 31.924849, -12.244019, -0.514513, 31.209024, -12.2742, -0.48726892, 31.221043, -12.244019, -0.514513, 31.209024, -12.2742, -0.31071663, 31.195135, -12.2742, -0.48726892, 31.221043, -12.244019, -0.514513, 31.209024, -12.244019, -0.30377197, 31.167624, -12.2742, -0.31071663, 31.195135, -12.2742, -0.49421322, 31.913631, -12.244019, -0.4600247, 31.233063, -12.244019, -0.46670198, 31.902145, -12.2742, -0.49421322, 31.913631, -12.2742, -0.48726892, 31.221043, -12.244019, -0.4600247, 31.233063, -12.2742, -0.48726892, 31.221043, -12.244019, -0.3179283, 31.222378, -12.244019, -0.4600247, 31.233063, -12.2742, -0.48726892, 31.221043, -12.2742, -0.31071663, 31.195135, -12.244019, -0.3179283, 31.222378, -12.244019, -0.3179283, 31.222378, -12.213837, -0.16301107, 31.237604, -12.213837, -0.31071663, 31.195135, -12.244019, -0.3179283, 31.222378, -12.244019, -0.19239187, 31.243748, -12.213837, -0.16301107, 31.237604, -12.244019, -0.19239187, 31.243748, -12.213837, -0.16007268, 31.908022, -12.213837, -0.16301107, 31.237604, -12.244019, -0.19239187, 31.243748, -12.244019, -0.18918645, 31.901344, -12.213837, -0.16007268, 31.908022, -12.244019, -0.18918645, 31.901344, -12.213837, -0.3240713, 31.970522, -12.213837, -0.16007268, 31.908022, -12.244019, -0.18918645, 31.901344, -12.244019, -0.33021462, 31.942745, -12.213837, -0.3240713, 31.970522, -12.213837, -0.31071663, 31.195135, -12.244019, -0.13363016, 31.23146, -12.244019, -0.30377197, 31.167624, -12.213837, -0.31071663, 31.195135, -12.213837, -0.16301107, 31.237604, -12.244019, -0.13363016, 31.23146, -12.213837, -0.16301107, 31.237604, -12.244019, -0.13069177, 31.9147, -12.244019, -0.13363016, 31.23146, -12.213837, -0.16301107, 31.237604, -12.213837, -0.16007268, 31.908022, -12.244019, -0.13069177, 31.9147, -12.213837, -0.16007268, 31.908022, -12.244019, -0.3181951, 31.9983, -12.244019, -0.13069177, 31.9147, -12.213837, -0.16007268, 31.908022, -12.213837, -0.3240713, 31.970522, -12.244019, -0.3181951, 31.9983, -12.244019, -0.30377197, 31.167624, -12.2742, -0.16301107, 31.237604, -12.2742, -0.31071663, 31.195135, -12.244019, -0.30377197, 31.167624, -12.244019, -0.13363016, 31.23146, -12.2742, -0.16301107, 31.237604, -12.2742, -0.31071663, 31.195135, -12.244019, -0.19239187, 31.243748, -12.244019, -0.3179283, 31.222378, -12.2742, -0.31071663, 31.195135, -12.2742, -0.16301107, 31.237604, -12.244019, -0.19239187, 31.243748, -12.2742, -0.16301107, 31.237604, -12.244019, -0.18918645, 31.901344, -12.244019, -0.19239187, 31.243748, -12.2742, -0.16301107, 31.237604, -12.2742, -0.16007268, 31.908022, -12.244019, -0.18918645, 31.901344, -12.2742, -0.16007268, 31.908022, -12.244019, -0.33021462, 31.942745, -12.244019, -0.18918645, 31.901344, -12.2742, -0.16007268, 31.908022, -12.2742, -0.3240713, 31.970522, -12.244019, -0.33021462, 31.942745, -12.244019, -0.13363016, 31.23146, -12.2742, -0.16007268, 31.908022, -12.2742, -0.16301107, 31.237604, -12.244019, -0.13363016, 31.23146, -12.244019, -0.13069177, 31.9147, -12.2742, -0.16007268, 31.908022, -12.244019, -0.13069177, 31.9147, -12.2742, -0.3240713, 31.970522, -12.2742, -0.16007268, 31.908022, -12.244019, -0.13069177, 31.9147, -12.244019, -0.3181951, 31.9983, -12.2742, -0.3240713, 31.970522, -12.244019, -0.33021462, 31.942745, -12.213837, -0.49421322, 31.913631, -12.213837, -0.3240713, 31.970522, -12.244019, -0.33021462, 31.942745, -12.244019, -0.46670198, 31.902145, -12.213837, -0.49421322, 31.913631, -12.213837, -0.3240713, 31.970522, -12.244019, -0.5219915, 31.924849, -12.244019, -0.3181951, 31.9983, -12.213837, -0.3240713, 31.970522, -12.213837, -0.49421322, 31.913631, -12.244019, -0.5219915, 31.924849, -12.244019, -0.3181951, 31.9983, -12.2742, -0.49421322, 31.913631, -12.2742, -0.3240713, 31.970522, -12.244019, -0.3181951, 31.9983, -12.244019, -0.5219915, 31.924849, -12.2742, -0.49421322, 31.913631, -12.2742, -0.3240713, 31.970522, -12.244019, -0.46670198, 31.902145, -12.244019, -0.33021462, 31.942745, -12.2742, -0.3240713, 31.970522, -12.2742, -0.49421322, 31.913631, -12.244019, -0.46670198, 31.902145, -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, -13.598646, -21.527376, 3.4402225, -13.598646, -21.049309, 3.7162285, -13.64679, -21.34583, 3.1714063, -13.64679, -21.34583, 3.1714063, -13.598646, -21.049309, 3.7162285, -13.598646, -21.049309, 3.1642163, -13.598646, -21.60132, 3.7162287, -13.598646, -21.049309, 3.7162285, -13.598646, -21.527376, 3.4402225, -13.598646, -21.049309, 3.1642163, -13.598646, -21.049309, 3.7162285, -13.598646, -20.773302, 3.2381606, -13.604641, -21.549007, 3.9200876, -13.598646, -21.049309, 3.7162285, -13.598646, -21.60132, 3.7162287, -13.598646, -20.773302, 3.2381606, -13.598646, -21.049309, 3.7162285, -13.598646, -20.571241, 3.4402223, -13.598646, -21.325315, 4.194296, -13.598646, -21.049309, 3.7162285, -13.604641, -21.549007, 3.9200876, -13.598646, -20.571241, 3.4402223, -13.598646, -21.049309, 3.7162285, -13.598646, -20.497297, 3.7162282, -13.602205, -21.078316, 4.268241, -13.598646, -21.049309, 3.7162285, -13.598646, -21.325315, 4.194296, -13.598646, -20.497297, 3.7162282, -13.598646, -21.049309, 3.7162285, -13.598646, -20.571241, 3.9922345, -13.598646, -20.773302, 4.194296, -13.598646, -21.049309, 3.7162285, -13.602205, -21.078316, 4.268241, -13.598646, -20.571241, 3.9922345, -13.598646, -21.049309, 3.7162285, -13.598646, -20.773302, 4.194296, -19.042458, -21.325315, 3.238161, -19.042458, -21.049309, 3.7162285, -19.042458, -21.527376, 3.4402225, -19.042458, -21.527376, 3.4402225, -19.042458, -21.049309, 3.7162285, -19.042458, -21.60132, 3.7162287, -19.042458, -21.049309, 3.1642163, -19.042458, -21.049309, 3.7162285, -19.042458, -21.325315, 3.238161, -19.042458, -21.60132, 3.7162287, -19.042458, -21.049309, 3.7162285, -19.042458, -21.527376, 3.9922347, -19.042458, -20.773302, 3.2381606, -19.042458, -21.049309, 3.7162285, -19.042458, -21.049309, 3.1642163, -19.042458, -21.527376, 3.9922347, -19.042458, -21.049309, 3.7162285, -19.042458, -21.325315, 4.194296, -18.784325, -20.601488, 3.451131, -19.042458, -21.049309, 3.7162285, -19.042458, -20.773302, 3.2381606, -19.042458, -21.325315, 4.194296, -19.042458, -21.049309, 3.7162285, -19.042458, -21.049309, 4.268241, -19.042458, -20.497297, 3.7162282, -19.042458, -21.049309, 3.7162285, -18.784325, -20.601488, 3.451131, -19.042458, -21.049309, 4.268241, -19.042458, -21.049309, 3.7162285, -19.042458, -20.773302, 4.194296, -19.042458, -20.571241, 3.9922345, -19.042458, -21.049309, 3.7162285, -19.042458, -20.497297, 3.7162282, -19.042458, -20.773302, 4.194296, -19.042458, -21.049309, 3.7162285, -19.042458, -20.571241, 3.9922345, -14.534702, -21.325129, 3.2385328, -17.467253, -21.049309, 3.164526, -17.467253, -21.325129, 3.2384708, -17.467253, -21.325129, 3.2384708, -19.084044, -21.586073, 3.4063182, -17.467253, -21.527067, 3.4404085, -17.467253, -21.325129, 3.2384708, -19.084044, -21.359219, 3.1794639, -19.084044, -21.586073, 3.4063182, -17.467253, -21.049309, 3.164526, -19.084044, -21.359219, 3.1794639, -17.467253, -21.325129, 3.2384708, -17.467253, -21.325129, 3.2384708, -17.467253, -21.527067, 3.4404085, -14.516907, -21.527004, 3.4404085, -17.467253, -21.325129, 3.2384708, -14.516907, -21.527004, 3.4404085, -14.534702, -21.325129, 3.2385328, -17.467253, -21.049309, 3.164526, -19.084044, -21.049309, 3.096408, -19.084044, -21.359219, 3.1794639, -13.605204, -21.379736, 3.1126473, -14.534702, -21.325129, 3.2385328, -14.516907, -21.527004, 3.4404085, -13.605204, -21.379736, 3.1126473, -14.516907, -21.527004, 3.4404085, -13.557062, -21.586073, 3.4063182, -13.557062, -21.049309, 3.096408, -14.534702, -21.325129, 3.2385328, -13.605204, -21.379736, 3.1126473, -13.557062, -21.049309, 3.096408, -14.519905, -21.049309, 3.164588, -14.534702, -21.325129, 3.2385328, -14.534702, -21.325129, 3.2385328, -14.519905, -21.049309, 3.164588, -17.467253, -21.049309, 3.164526, -13.557062, -20.739399, 3.1794636, -14.519905, -21.049309, 3.164588, -13.557062, -21.049309, 3.096408, -17.467253, -20.773489, 3.2384706, -19.084044, -21.049309, 3.096408, -17.467253, -21.049309, 3.164526, -17.467253, -20.773489, 3.2384706, -17.467253, -21.049309, 3.164526, -14.519905, -21.049309, 3.164588, -17.467253, -20.773489, 3.2384706, -19.084044, -20.739399, 3.1794636, -19.084044, -21.049309, 3.096408, -17.467253, -20.57155, 3.4404082, -19.084044, -20.739399, 3.1794636, -17.467253, -20.773489, 3.2384706, -17.467253, -20.57155, 3.4404082, -18.82591, -20.542791, 3.4172268, -19.084044, -20.739399, 3.1794636, -17.467253, -20.773489, 3.2384706, -14.519905, -21.049309, 3.164588, -14.509039, -20.773489, 3.2385325, -17.467253, -20.57155, 3.4404082, -17.467253, -20.773489, 3.2384706, -14.509039, -20.773489, 3.2385325, -13.557062, -20.739399, 3.1794636, -14.509039, -20.773489, 3.2385325, -14.519905, -21.049309, 3.164588, -17.467253, -20.57155, 3.4404082, -14.509039, -20.773489, 3.2385325, -14.509039, -20.571613, 3.4404082, -13.557062, -20.512545, 3.406318, -14.509039, -20.773489, 3.2385325, -13.557062, -20.739399, 3.1794636, -13.557062, -20.512545, 3.406318, -14.509039, -20.571613, 3.4404082, -14.509039, -20.773489, 3.2385325, -17.467253, -21.527067, 3.4404085, -14.509039, -21.600948, 3.7162287, -14.516907, -21.527004, 3.4404085, -13.557062, -21.586073, 3.4063182, -14.516907, -21.527004, 3.4404085, -14.509039, -21.600948, 3.7162287, -13.557062, -21.586073, 3.4063182, -14.509039, -21.600948, 3.7162287, -13.557062, -21.66913, 3.7162287, -13.557062, -21.66913, 3.7162287, -14.509039, -21.600948, 3.7162287, -14.511475, -21.527004, 3.9920487, -13.557062, -21.66913, 3.7162287, -14.511475, -21.527004, 3.9920487, -13.563242, -21.607706, 3.953992, -17.467253, -21.601011, 3.7162287, -14.511475, -21.527004, 3.9920487, -14.509039, -21.600948, 3.7162287, -17.467253, -21.527067, 3.4404085, -17.467253, -21.601011, 3.7162287, -14.509039, -21.600948, 3.7162287, -17.467253, -21.601011, 3.7162287, -17.467253, -21.527067, 3.9920487, -14.511475, -21.527004, 3.9920487, -17.467253, -21.601011, 3.7162287, -19.084044, -21.586073, 4.0261393, -17.467253, -21.527067, 3.9920487, -17.467253, -21.601011, 3.7162287, -19.084044, -21.66913, 3.7162287, -19.084044, -21.586073, 4.0261393, -17.467253, -21.527067, 3.4404085, -19.084044, -21.66913, 3.7162287, -17.467253, -21.601011, 3.7162287, -17.467253, -21.527067, 3.4404085, -19.084044, -21.586073, 3.4063182, -19.084044, -21.66913, 3.7162287, -17.467253, -21.527067, 3.9920487, -14.509039, -21.325129, 4.1939244, -14.511475, -21.527004, 3.9920487, -13.563242, -21.607706, 3.953992, -14.511475, -21.527004, 3.9920487, -14.509039, -21.325129, 4.1939244, -13.563242, -21.607706, 3.953992, -14.509039, -21.325129, 4.1939244, -13.557062, -21.359219, 4.2529936, -13.557062, -21.359219, 4.2529936, -14.509039, -21.325129, 4.1939244, -14.510912, -21.049309, 4.267869, -13.557062, -21.359219, 4.2529936, -14.510912, -21.049309, 4.267869, -13.560621, -21.078316, 4.336049, -17.467253, -21.527067, 3.9920487, -17.467253, -21.325129, 4.1939864, -14.509039, -21.325129, 4.1939244, -17.467253, -21.325129, 4.1939864, -14.510912, -21.049309, 4.267869, -14.509039, -21.325129, 4.1939244, -17.467253, -21.527067, 3.9920487, -19.084044, -21.359219, 4.2529936, -17.467253, -21.325129, 4.1939864, -17.467253, -21.527067, 3.9920487, -19.084044, -21.586073, 4.0261393, -19.084044, -21.359219, 4.2529936, -17.467253, -21.325129, 4.1939864, -19.084044, -21.359219, 4.2529936, -19.084044, -21.049309, 4.336049, -17.467253, -21.325129, 4.1939864, -19.084044, -21.049309, 4.336049, -17.467253, -21.049309, 4.267931, -17.467253, -21.325129, 4.1939864, -17.467253, -21.049309, 4.267931, -14.510912, -21.049309, 4.267869, -17.467253, -21.049309, 4.267931, -19.084044, -21.049309, 4.336049, -19.084044, -20.739399, 4.252993, -13.560621, -21.078316, 4.336049, -14.510912, -21.049309, 4.267869, -14.510912, -20.79512, 4.121777, -17.467253, -21.049309, 4.267931, -14.510912, -20.79512, 4.121777, -14.510912, -21.049309, 4.267869, -13.560621, -21.078316, 4.336049, -14.510912, -20.79512, 4.121777, -13.557062, -20.739399, 4.252993, -17.467253, -21.049309, 4.267931, -19.084044, -20.739399, 4.252993, -17.467253, -20.773489, 4.193986, -17.467253, -21.049309, 4.267931, -17.467253, -20.773489, 4.193986, -14.510912, -20.79512, 4.121777, -17.467253, -20.773489, 4.193986, -19.084044, -20.739399, 4.252993, -19.084044, -20.512545, 4.0261383, -17.467253, -20.773489, 4.193986, -19.084044, -20.512545, 4.0261383, -17.467253, -20.57155, 3.9920485, -17.467253, -20.773489, 4.193986, -17.467253, -20.57155, 3.9920485, -14.509039, -20.571613, 3.9920485, -17.467253, -20.773489, 4.193986, -14.509039, -20.571613, 3.9920485, -14.510912, -20.79512, 4.121777, -13.557062, -20.739399, 4.252993, -14.510912, -20.79512, 4.121777, -14.509039, -20.571613, 3.9920485, -13.557062, -20.739399, 4.252993, -14.509039, -20.571613, 3.9920485, -13.557062, -20.512545, 4.0261383, -17.467253, -20.57155, 3.9920485, -14.509039, -20.49773, 3.7162282, -14.509039, -20.571613, 3.9920485, -13.557062, -20.512545, 4.0261383, -14.509039, -20.571613, 3.9920485, -14.509039, -20.49773, 3.7162282, -13.557062, -20.512545, 4.0261383, -14.509039, -20.49773, 3.7162282, -13.557062, -20.429487, 3.7162282, -13.557062, -20.429487, 3.7162282, -14.509039, -20.49773, 3.7162282, -14.509039, -20.571613, 3.4404082, -13.557062, -20.429487, 3.7162282, -14.509039, -20.571613, 3.4404082, -13.557062, -20.512545, 3.406318, -17.467253, -20.497606, 3.7162282, -14.509039, -20.571613, 3.4404082, -14.509039, -20.49773, 3.7162282, -17.467253, -20.57155, 3.9920485, -17.467253, -20.497606, 3.7162282, -14.509039, -20.49773, 3.7162282, -17.467253, -20.497606, 3.7162282, -17.467253, -20.57155, 3.4404082, -14.509039, -20.571613, 3.4404082, -17.467253, -20.497606, 3.7162282, -18.82591, -20.542791, 3.4172268, -17.467253, -20.57155, 3.4404082, -17.467253, -20.497606, 3.7162282, -19.084044, -20.429487, 3.7162282, -18.82591, -20.542791, 3.4172268, -17.467253, -20.57155, 3.9920485, -19.084044, -20.429487, 3.7162282, -17.467253, -20.497606, 3.7162282, -17.467253, -20.57155, 3.9920485, -19.084044, -20.512545, 4.0261383, -19.084044, -20.429487, 3.7162282, -19.084044, -21.359219, 3.1794639, -19.084044, -21.586073, 3.4063182, -19.084044, -21.586073, 3.4063182, -19.084044, -21.586073, 3.4063182, -19.084044, -21.586073, 3.4063182, -19.084044, -21.66913, 3.7162287, -19.084044, -21.359219, 3.1794639, -19.084044, -21.359219, 3.1794639, -19.084044, -21.586073, 3.4063182, -19.084044, -21.049309, 3.096408, -19.084044, -21.359219, 3.1794639, -19.084044, -21.359219, 3.1794639, -19.084044, -21.049309, 3.096408, -19.084044, -21.049309, 3.096408, -19.084044, -21.359219, 3.1794639, -19.084044, -21.586073, 3.4063182, -19.084044, -21.66913, 3.7162287, -19.084044, -21.66913, 3.7162287, -19.084044, -21.66913, 3.7162287, -19.084044, -21.66913, 3.7162287, -19.084044, -21.586073, 4.0261393, -19.084044, -21.66913, 3.7162287, -19.084044, -21.586073, 4.0261393, -19.084044, -21.586073, 4.0261393, -19.084044, -21.586073, 4.0261393, -19.084044, -21.586073, 4.0261393, -19.084044, -21.359219, 4.2529936, -19.084044, -21.586073, 4.0261393, -19.084044, -21.359219, 4.2529936, -19.084044, -21.359219, 4.2529936, -19.084044, -21.359219, 4.2529936, -19.084044, -21.359219, 4.2529936, -19.084044, -21.049309, 4.336049, -19.084044, -21.359219, 4.2529936, -19.084044, -21.049309, 4.336049, -19.084044, -21.049309, 4.336049, -19.084044, -21.049309, 4.336049, -19.084044, -21.049309, 4.336049, -19.084044, -20.739399, 4.252993, -19.084044, -21.049309, 4.336049, -19.084044, -20.739399, 4.252993, -19.084044, -20.739399, 4.252993, -19.084044, -20.739399, 4.252993, -19.084044, -20.739399, 4.252993, -19.084044, -20.512545, 4.0261383, -19.084044, -20.739399, 4.252993, -19.084044, -20.512545, 4.0261383, -19.084044, -20.512545, 4.0261383, -19.084044, -20.512545, 4.0261383, -19.084044, -20.512545, 4.0261383, -19.084044, -20.429487, 3.7162282, -19.084044, -20.512545, 4.0261383, -19.084044, -20.429487, 3.7162282, -19.084044, -20.429487, 3.7162282, -19.084044, -20.429487, 3.7162282, -19.084044, -20.429487, 3.7162282, -18.82591, -20.542791, 3.4172268, -19.084044, -20.429487, 3.7162282, -18.82591, -20.542791, 3.4172268, -18.82591, -20.542791, 3.4172268, -18.82591, -20.542791, 3.4172268, -19.084044, -20.739399, 3.1794636, -19.084044, -20.739399, 3.1794636, -18.82591, -20.542791, 3.4172268, -18.82591, -20.542791, 3.4172268, -19.084044, -20.739399, 3.1794636, -19.084044, -20.739399, 3.1794636, -19.084044, -21.049309, 3.096408, -19.084044, -21.049309, 3.096408, -19.084044, -20.739399, 3.1794636, -19.084044, -20.739399, 3.1794636, -19.084044, -21.049309, 3.096408, -13.557062, -21.586073, 3.4063182, -13.605204, -21.34583, 3.1714063, -13.605204, -21.379736, 3.1126473, -13.605204, -21.379736, 3.1126473, -13.605204, -21.34583, 3.1714063, -13.557062, -21.049309, 3.1642163, -13.557062, -21.586073, 3.4063182, -13.557062, -21.527376, 3.4402225, -13.605204, -21.34583, 3.1714063, -13.605204, -21.379736, 3.1126473, -13.557062, -21.049309, 3.1642163, -13.557062, -21.049309, 3.096408, -13.557062, -21.66913, 3.7162287, -13.557062, -21.527376, 3.4402225, -13.557062, -21.586073, 3.4063182, -13.557062, -21.049309, 3.096408, -13.557062, -21.049309, 3.1642163, -13.557062, -20.773302, 3.2381606, -13.557062, -21.66913, 3.7162287, -13.557062, -21.60132, 3.7162287, -13.557062, -21.527376, 3.4402225, -13.557062, -21.049309, 3.096408, -13.557062, -20.773302, 3.2381606, -13.557062, -20.739399, 3.1794636, -13.563242, -21.607706, 3.953992, -13.557062, -21.60132, 3.7162287, -13.557062, -21.66913, 3.7162287, -13.557062, -20.739399, 3.1794636, -13.557062, -20.773302, 3.2381606, -13.557062, -20.571241, 3.4402223, -13.563242, -21.607706, 3.953992, -13.563242, -21.549007, 3.9200876, -13.557062, -21.60132, 3.7162287, -13.557062, -20.739399, 3.1794636, -13.557062, -20.571241, 3.4402223, -13.557062, -20.512545, 3.406318, -13.557062, -21.359219, 4.2529936, -13.563242, -21.549007, 3.9200876, -13.563242, -21.607706, 3.953992, -13.557062, -20.512545, 3.406318, -13.557062, -20.571241, 3.4402223, -13.557062, -20.497297, 3.7162282, -13.557062, -21.359219, 4.2529936, -13.557062, -21.325315, 4.194296, -13.563242, -21.549007, 3.9200876, -13.557062, -20.512545, 3.406318, -13.557062, -20.497297, 3.7162282, -13.557062, -20.429487, 3.7162282, -13.560621, -21.078316, 4.336049, -13.557062, -21.325315, 4.194296, -13.557062, -21.359219, 4.2529936, -13.557062, -20.429487, 3.7162282, -13.557062, -20.497297, 3.7162282, -13.557062, -20.571241, 3.9922345, -13.560621, -21.078316, 4.336049, -13.560621, -21.078316, 4.268241, -13.557062, -21.325315, 4.194296, -13.557062, -20.429487, 3.7162282, -13.557062, -20.571241, 3.9922345, -13.557062, -20.512545, 4.0261383, -13.557062, -20.739399, 4.252993, -13.560621, -21.078316, 4.268241, -13.560621, -21.078316, 4.336049, -13.557062, -20.512545, 4.0261383, -13.557062, -20.571241, 3.9922345, -13.557062, -20.773302, 4.194296, -13.557062, -20.739399, 4.252993, -13.557062, -20.773302, 4.194296, -13.560621, -21.078316, 4.268241, -13.557062, -20.512545, 4.0261383, -13.557062, -20.773302, 4.194296, -13.557062, -20.739399, 4.252993, -19.084044, -21.359219, 3.1794639, -19.084044, -21.527376, 3.4402225, -19.084044, -21.586073, 3.4063182, -19.084044, -21.586073, 3.4063182, -19.084044, -21.527376, 3.4402225, -19.084044, -21.60132, 3.7162287, -19.084044, -21.359219, 3.1794639, -19.084044, -21.325315, 3.238161, -19.084044, -21.527376, 3.4402225, -19.084044, -21.586073, 3.4063182, -19.084044, -21.60132, 3.7162287, -19.084044, -21.66913, 3.7162287, -19.084044, -21.049309, 3.096408, -19.084044, -21.325315, 3.238161, -19.084044, -21.359219, 3.1794639, -19.084044, -21.66913, 3.7162287, -19.084044, -21.60132, 3.7162287, -19.084044, -21.527376, 3.9922347, -19.084044, -21.049309, 3.096408, -19.084044, -21.049309, 3.1642163, -19.084044, -21.325315, 3.238161, -19.084044, -21.66913, 3.7162287, -19.084044, -21.527376, 3.9922347, -19.084044, -21.586073, 4.0261393, -19.084044, -20.739399, 3.1794636, -19.084044, -21.049309, 3.1642163, -19.084044, -21.049309, 3.096408, -19.084044, -21.586073, 4.0261393, -19.084044, -21.527376, 3.9922347, -19.084044, -21.325315, 4.194296, -19.084044, -20.739399, 3.1794636, -19.084044, -20.773302, 3.2381606, -19.084044, -21.049309, 3.1642163, -19.084044, -21.586073, 4.0261393, -19.084044, -21.325315, 4.194296, -19.084044, -21.359219, 4.2529936, -18.82591, -20.542791, 3.4172268, -19.084044, -20.773302, 3.2381606, -19.084044, -20.739399, 3.1794636, -19.084044, -21.359219, 4.2529936, -19.084044, -21.325315, 4.194296, -19.084044, -21.049309, 4.268241, -18.82591, -20.542791, 3.4172268, -18.82591, -20.601488, 3.451131, -19.084044, -20.773302, 3.2381606, -19.084044, -21.359219, 4.2529936, -19.084044, -21.049309, 4.268241, -19.084044, -21.049309, 4.336049, -19.084044, -20.429487, 3.7162282, -18.82591, -20.601488, 3.451131, -18.82591, -20.542791, 3.4172268, -19.084044, -21.049309, 4.336049, -19.084044, -21.049309, 4.268241, -19.084044, -20.773302, 4.194296, -19.084044, -20.429487, 3.7162282, -19.084044, -20.497297, 3.7162282, -18.82591, -20.601488, 3.451131, -19.084044, -21.049309, 4.336049, -19.084044, -20.773302, 4.194296, -19.084044, -20.739399, 4.252993, -19.084044, -20.512545, 4.0261383, -19.084044, -20.497297, 3.7162282, -19.084044, -20.429487, 3.7162282, -19.084044, -20.739399, 4.252993, -19.084044, -20.773302, 4.194296, -19.084044, -20.571241, 3.9922345, -19.084044, -20.512545, 4.0261383, -19.084044, -20.571241, 3.9922345, -19.084044, -20.497297, 3.7162282, -19.084044, -20.739399, 4.252993, -19.084044, -20.571241, 3.9922345, -19.084044, -20.512545, 4.0261383, -13.557062, -21.527376, 3.4402225, -13.64679, -21.34583, 3.1714063, -13.605204, -21.34583, 3.1714063, -13.557062, -21.527376, 3.4402225, -13.598646, -21.527376, 3.4402225, -13.64679, -21.34583, 3.1714063, -13.557062, -21.60132, 3.7162287, -13.598646, -21.527376, 3.4402225, -13.557062, -21.527376, 3.4402225, -13.557062, -21.60132, 3.7162287, -13.598646, -21.60132, 3.7162287, -13.598646, -21.527376, 3.4402225, -13.563242, -21.549007, 3.9200876, -13.598646, -21.60132, 3.7162287, -13.557062, -21.60132, 3.7162287, -13.563242, -21.549007, 3.9200876, -13.604641, -21.549007, 3.9200876, -13.598646, -21.60132, 3.7162287, -13.557062, -21.325315, 4.194296, -13.604641, -21.549007, 3.9200876, -13.563242, -21.549007, 3.9200876, -13.557062, -21.325315, 4.194296, -13.598646, -21.325315, 4.194296, -13.604641, -21.549007, 3.9200876, -13.560621, -21.078316, 4.268241, -13.598646, -21.325315, 4.194296, -13.557062, -21.325315, 4.194296, -13.560621, -21.078316, 4.268241, -13.602205, -21.078316, 4.268241, -13.598646, -21.325315, 4.194296, -13.557062, -20.773302, 4.194296, -13.602205, -21.078316, 4.268241, -13.560621, -21.078316, 4.268241, -13.557062, -20.773302, 4.194296, -13.598646, -20.773302, 4.194296, -13.602205, -21.078316, 4.268241, -13.557062, -20.571241, 3.9922345, -13.598646, -20.773302, 4.194296, -13.557062, -20.773302, 4.194296, -13.557062, -20.571241, 3.9922345, -13.598646, -20.571241, 3.9922345, -13.598646, -20.773302, 4.194296, -13.557062, -20.497297, 3.7162282, -13.598646, -20.571241, 3.9922345, -13.557062, -20.571241, 3.9922345, -13.557062, -20.497297, 3.7162282, -13.598646, -20.497297, 3.7162282, -13.598646, -20.571241, 3.9922345, -13.557062, -20.571241, 3.4402223, -13.598646, -20.497297, 3.7162282, -13.557062, -20.497297, 3.7162282, -13.557062, -20.571241, 3.4402223, -13.598646, -20.571241, 3.4402223, -13.598646, -20.497297, 3.7162282, -13.557062, -20.773302, 3.2381606, -13.598646, -20.571241, 3.4402223, -13.557062, -20.571241, 3.4402223, -13.557062, -20.773302, 3.2381606, -13.598646, -20.773302, 3.2381606, -13.598646, -20.571241, 3.4402223, -13.557062, -21.049309, 3.1642163, -13.598646, -20.773302, 3.2381606, -13.557062, -20.773302, 3.2381606, -13.557062, -21.049309, 3.1642163, -13.598646, -21.049309, 3.1642163, -13.598646, -20.773302, 3.2381606, -13.605204, -21.34583, 3.1714063, -13.598646, -21.049309, 3.1642163, -13.557062, -21.049309, 3.1642163, -13.605204, -21.34583, 3.1714063, -13.64679, -21.34583, 3.1714063, -13.598646, -21.049309, 3.1642163, -19.084044, -21.325315, 3.238161, -19.042458, -21.527376, 3.4402225, -19.084044, -21.527376, 3.4402225, -19.084044, -21.325315, 3.238161, -19.042458, -21.325315, 3.238161, -19.042458, -21.527376, 3.4402225, -19.084044, -21.527376, 3.4402225, -19.042458, -21.60132, 3.7162287, -19.084044, -21.60132, 3.7162287, -19.084044, -21.527376, 3.4402225, -19.042458, -21.527376, 3.4402225, -19.042458, -21.60132, 3.7162287, -19.084044, -21.60132, 3.7162287, -19.042458, -21.527376, 3.9922347, -19.084044, -21.527376, 3.9922347, -19.084044, -21.60132, 3.7162287, -19.042458, -21.60132, 3.7162287, -19.042458, -21.527376, 3.9922347, -19.084044, -21.527376, 3.9922347, -19.042458, -21.325315, 4.194296, -19.084044, -21.325315, 4.194296, -19.084044, -21.527376, 3.9922347, -19.042458, -21.527376, 3.9922347, -19.042458, -21.325315, 4.194296, -19.084044, -21.325315, 4.194296, -19.042458, -21.049309, 4.268241, -19.084044, -21.049309, 4.268241, -19.084044, -21.325315, 4.194296, -19.042458, -21.325315, 4.194296, -19.042458, -21.049309, 4.268241, -19.084044, -21.049309, 4.268241, -19.042458, -21.049309, 4.268241, -19.042458, -20.773302, 4.194296, -19.084044, -21.049309, 4.268241, -19.042458, -20.773302, 4.194296, -19.084044, -20.773302, 4.194296, -19.084044, -20.773302, 4.194296, -19.042458, -20.571241, 3.9922345, -19.084044, -20.571241, 3.9922345, -19.084044, -20.773302, 4.194296, -19.042458, -20.773302, 4.194296, -19.042458, -20.571241, 3.9922345, -19.084044, -20.571241, 3.9922345, -19.042458, -20.497297, 3.7162282, -19.084044, -20.497297, 3.7162282, -19.084044, -20.571241, 3.9922345, -19.042458, -20.571241, 3.9922345, -19.042458, -20.497297, 3.7162282, -19.084044, -20.497297, 3.7162282, -18.784325, -20.601488, 3.451131, -18.82591, -20.601488, 3.451131, -19.084044, -20.497297, 3.7162282, -19.042458, -20.497297, 3.7162282, -18.784325, -20.601488, 3.451131, -18.82591, -20.601488, 3.451131, -19.042458, -20.773302, 3.2381606, -19.084044, -20.773302, 3.2381606, -18.82591, -20.601488, 3.451131, -18.784325, -20.601488, 3.451131, -19.042458, -20.773302, 3.2381606, -19.084044, -20.773302, 3.2381606, -19.042458, -21.049309, 3.1642163, -19.084044, -21.049309, 3.1642163, -19.084044, -20.773302, 3.2381606, -19.042458, -20.773302, 3.2381606, -19.042458, -21.049309, 3.1642163, -19.084044, -21.049309, 3.1642163, -19.042458, -21.049309, 3.1642163, -19.042458, -21.325315, 3.238161, -19.084044, -21.049309, 3.1642163, -19.042458, -21.325315, 3.238161, -19.084044, -21.325315, 3.238161, -5.555944, -21.308428, 0.4478172, -3.5470695, -21.129433, 0.35703874, -5.555944, -21.129433, 0.4478172, -5.555944, -21.308428, 0.4478172, -3.5470695, -21.308428, 0.35703874, -3.5470695, -21.129433, 0.35703874, -7.629111, -21.129433, 0.35703874, -5.555944, -21.308428, 0.4478172, -5.555944, -21.129433, 0.4478172, -7.629111, -21.129433, 0.35703874, -7.629111, -21.308428, 0.35703874, -5.555944, -21.308428, 0.4478172, -5.555944, -21.308428, 0.9271276, -3.5470695, -21.308428, 0.35703874, -5.555944, -21.308428, 0.4478172, -5.555944, -21.308428, 0.9271276, -3.5470695, -21.308428, 0.7632991, -3.5470695, -21.308428, 0.35703874, -5.555944, -21.308428, 0.4478172, -7.629111, -21.308428, 0.91196215, -5.555944, -21.308428, 0.9271276, -5.555944, -21.308428, 0.4478172, -7.629111, -21.308428, 0.6298014, -7.629111, -21.308428, 0.91196215, -7.629111, -21.308428, 0.6298014, -5.555944, -21.308428, 0.4478172, -7.629111, -21.308428, 0.35703874, -3.5470695, -21.308428, 0.7632991, -3.5470695, -21.129433, 0.35703874, -3.5470695, -21.308428, 0.35703874, -3.5470695, -21.308428, 0.7632991, -3.5470695, -21.129433, 0.7632991, -3.5470695, -21.129433, 0.35703874, -7.629111, -21.129433, 0.5971211, -7.5507216, -21.183046, 0.6447531, -7.629111, -21.308428, 0.6298014, -5.555944, -21.129433, 0.8073, -5.555944, -21.174288, 0.9271276, -6.0741286, -21.129433, 0.92328286, -5.555944, -21.129433, 0.8073, -5.053779, -21.129433, 0.8861171, -5.555944, -21.174288, 0.9271276, -5.555944, -21.129433, 0.8073, -3.5470695, -21.129433, 0.7632991, -5.053779, -21.129433, 0.8861171, -5.555944, -21.129433, 0.8073, -3.5470695, -21.129433, 0.35703874, -3.5470695, -21.129433, 0.7632991, -3.5470695, -21.129433, 0.35703874, -5.555944, -21.129433, 0.8073, -5.555944, -21.129433, 0.4478172, -6.0741286, -21.129433, 0.92328286, -5.555944, -21.129433, 0.4478172, -5.555944, -21.129433, 0.8073, -5.555944, -21.129433, 0.4478172, -7.629111, -21.129433, 0.5971211, -7.629111, -21.129433, 0.35703874, -5.555944, -21.129433, 0.4478172, -7.272619, -21.129433, 0.64624834, -7.629111, -21.129433, 0.5971211, -6.0741286, -21.129433, 0.92328286, -7.272619, -21.129433, 0.64624834, -5.555944, -21.129433, 0.4478172, -7.5507216, -21.183046, 0.6447531, -7.629111, -21.129433, 0.5971211, -7.272619, -21.129433, 0.64624834, -7.629111, -21.129433, 0.7325412, -7.5507216, -21.183046, 0.6447531, -7.272619, -21.129433, 0.64624834, -7.272619, -21.129433, 0.64624834, -6.0741286, -21.129433, 0.92328286, -7.629111, -21.129433, 0.7325412, -7.629111, -21.129433, 0.7325412, -6.0741286, -21.129433, 0.92328286, -7.629111, -21.129433, 0.91196215, -7.629111, -21.308428, 0.35703874, -7.629111, -21.129433, 0.5971211, -7.629111, -21.308428, 0.6298014, -7.629111, -21.308428, 0.35703874, -7.629111, -21.129433, 0.35703874, -7.629111, -21.129433, 0.5971211, -7.629111, -21.129433, 0.7325412, -7.629111, -21.308428, 0.91196215, -7.629111, -21.308428, 0.6298014, -7.629111, -21.129433, 0.7325412, -7.629111, -21.129433, 0.91196215, -7.629111, -21.308428, 0.91196215, -7.5507216, -21.183046, 0.6447531, -7.629111, -21.129433, 0.7325412, -7.629111, -21.308428, 0.6298014, -7.629111, -21.129433, 0.91196215, -5.555944, -21.308428, 0.9271276, -7.629111, -21.308428, 0.91196215, -5.555944, -21.308428, 0.9271276, -7.629111, -21.129433, 0.91196215, -6.0741286, -21.129433, 0.92328286, -5.555944, -21.308428, 0.9271276, -6.0741286, -21.129433, 0.92328286, -5.555944, -21.174288, 0.9271276, -5.555944, -21.174288, 0.9271276, -3.5470695, -21.308428, 0.7632991, -5.555944, -21.308428, 0.9271276, -5.555944, -21.174288, 0.9271276, -3.5470695, -21.129433, 0.7632991, -3.5470695, -21.308428, 0.7632991, -3.5470695, -21.129433, 0.7632991, -5.555944, -21.174288, 0.9271276, -5.053779, -21.129433, 0.8861171, -3.98836, -21.129433, 2.103403, -3.5470695, -21.308426, 2.113015, -3.98836, -21.308426, 2.103403, -3.98836, -21.129433, 2.103403, -3.5470695, -21.129433, 2.113015, -3.5470695, -21.308426, 2.113015, -3.98836, -21.308426, 2.103403, -6.7189765, -21.129433, 2.1106653, -3.98836, -21.129433, 2.103403, -3.98836, -21.308426, 2.103403, -7.629111, -21.174288, 2.113015, -6.7189765, -21.129433, 2.1106653, -7.629111, -21.174288, 2.113015, -3.98836, -21.308426, 2.103403, -7.629111, -21.308426, 2.113015, -3.5470695, -21.129433, 1.6317822, -3.98836, -21.129433, 2.103403, -3.98836, -21.129433, 1.6206751, -6.7189765, -21.129433, 2.1106653, -3.98836, -21.129433, 1.6206751, -3.98836, -21.129433, 2.103403, -6.7189765, -21.129433, 2.1106653, -7.629111, -21.129433, 1.7067546, -3.98836, -21.129433, 1.6206751, -7.629111, -21.129433, 1.7067546, -6.7189765, -21.129433, 2.1106653, -7.629111, -21.129433, 2.011343, -7.629111, -21.129433, 2.011343, -6.7189765, -21.129433, 2.1106653, -7.629111, -21.174288, 2.113015, -3.98836, -21.129433, 2.103403, -3.5470695, -21.129433, 1.6317822, -3.7822394, -21.129433, 1.8853211, -3.7822394, -21.129433, 1.8853211, -3.5470695, -21.129433, 1.6317822, -3.5470695, -21.129433, 1.8257278, -3.5470695, -21.129433, 1.8257278, -3.587226, -21.197784, 1.8791269, -3.7822394, -21.129433, 1.8853211, -3.587226, -21.197784, 1.8791269, -3.5470695, -21.129433, 1.9598663, -3.7822394, -21.129433, 1.8853211, -3.98836, -21.129433, 2.103403, -3.7822394, -21.129433, 1.8853211, -3.5470695, -21.129433, 1.9598663, -3.98836, -21.129433, 2.103403, -3.5470695, -21.129433, 1.9598663, -3.5470695, -21.129433, 2.113015, -3.98836, -21.308428, 1.6206752, -3.5470695, -21.129433, 1.6317822, -3.98836, -21.129433, 1.6206751, -3.98836, -21.308428, 1.6206752, -3.5470695, -21.308428, 1.6317823, -3.5470695, -21.129433, 1.6317822, -7.629111, -21.129433, 1.7067546, -3.98836, -21.308428, 1.6206752, -3.98836, -21.129433, 1.6206751, -7.629111, -21.129433, 1.7067546, -7.629111, -21.308426, 1.7067547, -3.98836, -21.308428, 1.6206752, -3.5470695, -21.308426, 2.113015, -3.98836, -21.308428, 1.6206752, -3.98836, -21.308426, 2.103403, -3.98836, -21.308428, 1.6206752, -3.5470695, -21.308426, 2.113015, -3.5470695, -21.308426, 1.8528546, -3.98836, -21.308428, 1.6206752, -3.5470695, -21.308426, 1.8528546, -3.5470695, -21.308428, 1.6317823, -7.629111, -21.308426, 1.7067547, -3.98836, -21.308426, 2.103403, -3.98836, -21.308428, 1.6206752, -7.629111, -21.308426, 1.7067547, -7.629111, -21.308426, 2.113015, -3.98836, -21.308426, 2.103403, -3.5470695, -21.129433, 1.9598663, -3.587226, -21.197784, 1.8791269, -3.5470695, -21.308426, 1.8528546, -3.587226, -21.197784, 1.8791269, -3.5470695, -21.129433, 1.8257278, -3.5470695, -21.308426, 1.8528546, -3.5470695, -21.308426, 2.113015, -3.5470695, -21.129433, 1.9598663, -3.5470695, -21.308426, 1.8528546, -3.5470695, -21.308426, 2.113015, -3.5470695, -21.129433, 2.113015, -3.5470695, -21.129433, 1.9598663, -3.5470695, -21.129433, 1.8257278, -3.5470695, -21.308428, 1.6317823, -3.5470695, -21.308426, 1.8528546, -3.5470695, -21.129433, 1.8257278, -3.5470695, -21.129433, 1.6317822, -3.5470695, -21.308428, 1.6317823, -7.629111, -21.129433, 1.7067546, -7.629111, -21.308426, 2.113015, -7.629111, -21.308426, 1.7067547, -7.629111, -21.308426, 2.113015, -7.629111, -21.129433, 1.7067546, -7.629111, -21.129433, 2.011343, -7.629111, -21.308426, 2.113015, -7.629111, -21.129433, 2.011343, -7.629111, -21.174288, 2.113015, -3.9170184, -21.320816, -0.4462974, -3.8958726, -21.33534, -0.9597833, -3.8958726, -21.340467, -0.43775344, -3.9170184, -21.320816, -0.4462974, -3.9170184, -21.316116, -0.9508121, -3.8958726, -21.33534, -0.9597833, -3.9170184, -21.316116, -0.9508121, -3.8958726, -21.210386, -0.97943425, -3.8958726, -21.33534, -0.9597833, -3.9170184, -21.316116, -0.9508121, -3.9170184, -21.215513, -0.95871544, -3.8958726, -21.210386, -0.97943425, -3.8958726, -21.340467, -0.43775344, -3.9170184, -21.354778, -0.9687543, -3.9170184, -21.360119, -0.4289961, -3.8958726, -21.340467, -0.43775344, -3.8958726, -21.33534, -0.9597833, -3.9170184, -21.354778, -0.9687543, -3.8958726, -21.33534, -0.9597833, -3.9170184, -21.205261, -1.0001533, -3.9170184, -21.354778, -0.9687543, -3.8958726, -21.33534, -0.9597833, -3.8958726, -21.210386, -0.97943425, -3.9170184, -21.205261, -1.0001533, -3.9170184, -21.360119, -0.4289961, -3.938592, -21.33534, -0.9597833, -3.938592, -21.340467, -0.43775344, -3.9170184, -21.360119, -0.4289961, -3.9170184, -21.354778, -0.9687543, -3.938592, -21.33534, -0.9597833, -3.9170184, -21.354778, -0.9687543, -3.938592, -21.210386, -0.97943425, -3.938592, -21.33534, -0.9597833, -3.9170184, -21.354778, -0.9687543, -3.9170184, -21.205261, -1.0001533, -3.938592, -21.210386, -0.97943425, -3.938592, -21.340467, -0.43775344, -3.9170184, -21.316116, -0.9508121, -3.9170184, -21.320816, -0.4462974, -3.938592, -21.340467, -0.43775344, -3.938592, -21.33534, -0.9597833, -3.9170184, -21.316116, -0.9508121, -3.938592, -21.33534, -0.9597833, -3.9170184, -21.215513, -0.95871544, -3.9170184, -21.316116, -0.9508121, -3.938592, -21.33534, -0.9597833, -3.938592, -21.210386, -0.97943425, -3.9170184, -21.215513, -0.95871544, -3.9170184, -21.215513, -0.95871544, -3.8958726, -21.10551, -0.9471812, -3.8958726, -21.210386, -0.97943425, -3.9170184, -21.215513, -0.95871544, -3.9170184, -21.126444, -0.9426956, -3.8958726, -21.10551, -0.9471812, -3.9170184, -21.126444, -0.9426956, -3.8958726, -21.103588, -0.44181204, -3.8958726, -21.10551, -0.9471812, -3.9170184, -21.126444, -0.9426956, -3.9170184, -21.124308, -0.44693828, -3.8958726, -21.103588, -0.44181204, -3.9170184, -21.124308, -0.44693828, -3.8958726, -21.219786, -0.3946073, -3.8958726, -21.103588, -0.44181204, -3.9170184, -21.124308, -0.44693828, -3.9170184, -21.224056, -0.41553974, -3.8958726, -21.219786, -0.3946073, -3.8958726, -21.210386, -0.97943425, -3.9170184, -21.084793, -0.95188046, -3.9170184, -21.205261, -1.0001533, -3.8958726, -21.210386, -0.97943425, -3.8958726, -21.10551, -0.9471812, -3.9170184, -21.084793, -0.95188046, -3.8958726, -21.10551, -0.9471812, -3.9170184, -21.082657, -0.4366858, -3.9170184, -21.084793, -0.95188046, -3.8958726, -21.10551, -0.9471812, -3.8958726, -21.103588, -0.44181204, -3.9170184, -21.082657, -0.4366858, -3.8958726, -21.103588, -0.44181204, -3.9170184, -21.215513, -0.37367487, -3.9170184, -21.082657, -0.4366858, -3.8958726, -21.103588, -0.44181204, -3.8958726, -21.219786, -0.3946073, -3.9170184, -21.215513, -0.37367487, -3.9170184, -21.205261, -1.0001533, -3.938592, -21.10551, -0.9471812, -3.938592, -21.210386, -0.97943425, -3.9170184, -21.205261, -1.0001533, -3.9170184, -21.084793, -0.95188046, -3.938592, -21.10551, -0.9471812, -3.938592, -21.210386, -0.97943425, -3.9170184, -21.126444, -0.9426956, -3.9170184, -21.215513, -0.95871544, -3.938592, -21.210386, -0.97943425, -3.938592, -21.10551, -0.9471812, -3.9170184, -21.126444, -0.9426956, -3.938592, -21.10551, -0.9471812, -3.9170184, -21.124308, -0.44693828, -3.9170184, -21.126444, -0.9426956, -3.938592, -21.10551, -0.9471812, -3.938592, -21.103588, -0.44181204, -3.9170184, -21.124308, -0.44693828, -3.938592, -21.103588, -0.44181204, -3.9170184, -21.224056, -0.41553974, -3.9170184, -21.124308, -0.44693828, -3.938592, -21.103588, -0.44181204, -3.938592, -21.219786, -0.3946073, -3.9170184, -21.224056, -0.41553974, -3.9170184, -21.084793, -0.95188046, -3.938592, -21.103588, -0.44181204, -3.938592, -21.10551, -0.9471812, -3.9170184, -21.084793, -0.95188046, -3.9170184, -21.082657, -0.4366858, -3.938592, -21.103588, -0.44181204, -3.9170184, -21.082657, -0.4366858, -3.938592, -21.219786, -0.3946073, -3.938592, -21.103588, -0.44181204, -3.9170184, -21.082657, -0.4366858, -3.9170184, -21.215513, -0.37367487, -3.938592, -21.219786, -0.3946073, -3.9170184, -21.224056, -0.41553974, -3.8958726, -21.340467, -0.43775344, -3.8958726, -21.219786, -0.3946073, -3.9170184, -21.224056, -0.41553974, -3.9170184, -21.320816, -0.4462974, -3.8958726, -21.340467, -0.43775344, -3.8958726, -21.219786, -0.3946073, -3.9170184, -21.360119, -0.4289961, -3.9170184, -21.215513, -0.37367487, -3.8958726, -21.219786, -0.3946073, -3.8958726, -21.340467, -0.43775344, -3.9170184, -21.360119, -0.4289961, -3.9170184, -21.215513, -0.37367487, -3.938592, -21.340467, -0.43775344, -3.938592, -21.219786, -0.3946073, -3.9170184, -21.215513, -0.37367487, -3.9170184, -21.360119, -0.4289961, -3.938592, -21.340467, -0.43775344, -3.938592, -21.219786, -0.3946073, -3.9170184, -21.320816, -0.4462974, -3.9170184, -21.224056, -0.41553974, -3.938592, -21.219786, -0.3946073, -3.938592, -21.340467, -0.43775344, -3.9170184, -21.320816, -0.4462974, -7.464642, -21.404758, 5.450673, -7.3512225, -21.35926, 5.450673, -7.3512225, -21.404758, 5.450673, -7.464642, -21.404758, 5.450673, -7.464642, -21.35926, 5.450673, -7.3512225, -21.35926, 5.450673, -7.464642, -21.35926, 5.450673, -7.3512225, -21.359264, -1.0281339, -7.3512225, -21.35926, 5.450673, -7.464642, -21.35926, 5.450673, -7.464642, -21.359264, -1.0281339, -7.3512225, -21.359264, -1.0281339, -7.464642, -21.359264, -1.0281339, -7.3512225, -21.40476, -1.0281339, -7.3512225, -21.359264, -1.0281339, -7.464642, -21.359264, -1.0281339, -7.464642, -21.40476, -1.0281339, -7.3512225, -21.40476, -1.0281339, -7.464642, -21.40476, -1.0281339, -7.3512225, -21.404758, 5.450673, -7.3512225, -21.40476, -1.0281339, -7.464642, -21.40476, -1.0281339, -7.464642, -21.404758, 5.450673, -7.3512225, -21.404758, 5.450673, -7.3512225, -21.404758, 5.450673, -7.3512225, -21.359264, -1.0281339, -7.3512225, -21.40476, -1.0281339, -7.3512225, -21.404758, 5.450673, -7.3512225, -21.35926, 5.450673, -7.3512225, -21.359264, -1.0281339, -7.464642, -21.40476, -1.0281339, -7.464642, -21.35926, 5.450673, -7.464642, -21.404758, 5.450673, -7.464642, -21.40476, -1.0281339, -7.464642, -21.359264, -1.0281339, -7.464642, -21.35926, 5.450673, -6.956496, -21.308428, -0.21347713, -3.4046006, -21.129435, -0.29165363, -6.956496, -21.129435, -0.21347737, -6.956496, -21.308428, -0.21347713, -3.4046006, -21.308428, -0.2916534, -3.4046006, -21.129435, -0.29165363, -7.7713666, -21.129435, -0.2168951, -6.956496, -21.308428, -0.21347713, -6.956496, -21.129435, -0.21347737, -7.7713666, -21.129435, -0.2168951, -7.7713666, -21.308428, -0.21689487, -6.956496, -21.308428, -0.21347713, -6.956496, -21.308428, 0.28014398, -3.4046006, -21.308428, -0.2916534, -6.956496, -21.308428, -0.21347713, -6.956496, -21.308428, 0.28014398, -3.4046006, -21.308428, 0.18936563, -3.4046006, -21.308428, -0.2916534, -7.7713666, -21.308428, -0.21689487, -6.956496, -21.308428, 0.28014398, -6.956496, -21.308428, -0.21347713, -7.7713666, -21.308428, -0.21689487, -7.7713666, -21.308428, 0.18936563, -6.956496, -21.308428, 0.28014398, -7.7713666, -21.308428, -0.21689487, -7.7713666, -21.129435, 0.18936539, -7.7713666, -21.308428, 0.18936563, -7.7713666, -21.308428, -0.21689487, -7.7713666, -21.129435, -0.2168951, -7.7713666, -21.129435, 0.18936539, -6.956496, -21.129435, 0.15668511, -6.956496, -21.17429, 0.28014398, -7.1602664, -21.129435, 0.25750268, -6.956496, -21.129435, 0.15668511, -6.0685754, -21.129435, 0.25750268, -6.956496, -21.17429, 0.28014398, -6.956496, -21.129435, 0.15668511, -4.0705943, -21.129435, 0.20623958, -6.0685754, -21.129435, 0.25750268, -7.1602664, -21.129435, 0.25750268, -6.956496, -21.129435, -0.21347737, -6.956496, -21.129435, 0.15668511, -7.1602664, -21.129435, 0.25750268, -7.7713666, -21.129435, -0.2168951, -6.956496, -21.129435, -0.21347737, -7.7713666, -21.129435, -0.2168951, -7.1602664, -21.129435, 0.25750268, -7.7713666, -21.129435, 0.18936539, -3.4046006, -21.129435, -0.29165363, -6.956496, -21.129435, 0.15668511, -6.956496, -21.129435, -0.21347737, -6.956496, -21.129435, 0.15668511, -3.4046006, -21.129435, -0.29165363, -4.0705943, -21.129435, 0.20623958, -4.0705943, -21.129435, 0.20623958, -3.4046006, -21.129435, -0.29165363, -3.4046006, -21.129435, 0.06911063, -4.0705943, -21.129435, 0.20623958, -3.4046006, -21.129435, 0.06911063, -3.4046006, -21.17429, 0.18936539, -7.7713666, -21.129435, 0.18936539, -6.956496, -21.308428, 0.28014398, -7.7713666, -21.308428, 0.18936563, -6.956496, -21.308428, 0.28014398, -7.7713666, -21.129435, 0.18936539, -7.1602664, -21.129435, 0.25750268, -6.956496, -21.308428, 0.28014398, -7.1602664, -21.129435, 0.25750268, -6.956496, -21.17429, 0.28014398, -6.956496, -21.17429, 0.28014398, -3.4046006, -21.308428, 0.18936563, -6.956496, -21.308428, 0.28014398, -6.956496, -21.17429, 0.28014398, -4.0705943, -21.129435, 0.20623958, -3.4046006, -21.308428, 0.18936563, -3.4046006, -21.308428, 0.18936563, -4.0705943, -21.129435, 0.20623958, -3.4046006, -21.17429, 0.18936539, -4.0705943, -21.129435, 0.20623958, -6.956496, -21.17429, 0.28014398, -6.0685754, -21.129435, 0.25750268, -3.4046006, -21.17429, 0.18936539, -3.4046006, -21.308428, -0.2916534, -3.4046006, -21.308428, 0.18936563, -3.4046006, -21.308428, -0.2916534, -3.4046006, -21.17429, 0.18936539, -3.4046006, -21.129435, 0.06911063, -3.4046006, -21.308428, -0.2916534, -3.4046006, -21.129435, 0.06911063, -3.4046006, -21.129435, -0.29165363, -7.7320647, -21.397497, -1.0330467, -7.351436, -19.082966, -0.94846344, -7.4360194, -21.397497, -1.0330467, -7.7320647, -21.397497, -1.0330467, -7.816649, -19.082966, -0.94846344, -7.351436, -19.082966, -0.94846344, -7.816649, -19.082966, -0.94846344, -7.351436, -19.082966, -1.4134629, -7.351436, -19.082966, -0.94846344, -7.816649, -19.082966, -0.94846344, -7.816649, -19.082966, -1.4134629, -7.351436, -19.082966, -1.4134629, -7.816649, -19.082966, -1.4134629, -7.4360194, -21.397497, -1.3288777, -7.351436, -19.082966, -1.4134629, -7.816649, -19.082966, -1.4134629, -7.7320647, -21.397497, -1.3288777, -7.4360194, -21.397497, -1.3288777, -7.7320647, -21.397497, -1.3288777, -7.4360194, -21.397497, -1.0330467, -7.4360194, -21.397497, -1.3288777, -7.7320647, -21.397497, -1.3288777, -7.7320647, -21.397497, -1.0330467, -7.4360194, -21.397497, -1.0330467, -7.4360194, -21.397497, -1.0330467, -7.351436, -19.082966, -1.4134629, -7.4360194, -21.397497, -1.3288777, -7.4360194, -21.397497, -1.0330467, -7.351436, -19.082966, -0.94846344, -7.351436, -19.082966, -1.4134629, -7.7320647, -21.397497, -1.3288777, -7.816649, -19.082966, -0.94846344, -7.7320647, -21.397497, -1.0330467, -7.7320647, -21.397497, -1.3288777, -7.816649, -19.082966, -1.4134629, -7.816649, -19.082966, -0.94846344, -3.98836, -21.129435, -0.457191, -3.5470695, -21.308428, -0.44779253, -3.98836, -21.308428, -0.45719075, -3.98836, -21.129435, -0.457191, -3.5470695, -21.129435, -0.44779277, -3.5470695, -21.308428, -0.44779253, -3.98836, -21.308428, -0.45719075, -6.7189765, -21.129435, -0.45014238, -3.98836, -21.129435, -0.457191, -3.98836, -21.308428, -0.45719075, -7.629111, -21.17429, -0.44779277, -6.7189765, -21.129435, -0.45014238, -7.629111, -21.17429, -0.44779277, -3.98836, -21.308428, -0.45719075, -7.629111, -21.308428, -0.44779253, -3.5470695, -21.129435, -0.928812, -3.98836, -21.129435, -0.457191, -3.98836, -21.129435, -0.9401326, -6.7189765, -21.129435, -0.45014238, -3.98836, -21.129435, -0.9401326, -3.98836, -21.129435, -0.457191, -6.7189765, -21.129435, -0.45014238, -7.629111, -21.129435, -0.85405326, -3.98836, -21.129435, -0.9401326, -7.629111, -21.129435, -0.85405326, -6.7189765, -21.129435, -0.45014238, -7.629111, -21.129435, -0.5492511, -7.629111, -21.129435, -0.5492511, -6.7189765, -21.129435, -0.45014238, -7.629111, -21.17429, -0.44779277, -3.98836, -21.129435, -0.457191, -3.5470695, -21.129435, -0.928812, -3.7822394, -21.129435, -0.67548656, -3.7822394, -21.129435, -0.67548656, -3.5470695, -21.129435, -0.928812, -3.5470695, -21.129435, -0.73508, -3.5470695, -21.129435, -0.73508, -3.587226, -21.197784, -0.6816809, -3.7822394, -21.129435, -0.67548656, -3.587226, -21.197784, -0.6816809, -3.5470695, -21.129435, -0.6009414, -3.7822394, -21.129435, -0.67548656, -3.98836, -21.129435, -0.457191, -3.7822394, -21.129435, -0.67548656, -3.5470695, -21.129435, -0.6009414, -3.98836, -21.129435, -0.457191, -3.5470695, -21.129435, -0.6009414, -3.5470695, -21.129435, -0.44779277, -3.98836, -21.308428, -0.9401324, -3.5470695, -21.129435, -0.928812, -3.98836, -21.129435, -0.9401326, -3.98836, -21.308428, -0.9401324, -3.5470695, -21.308428, -0.9288118, -3.5470695, -21.129435, -0.928812, -7.629111, -21.129435, -0.85405326, -3.98836, -21.308428, -0.9401324, -3.98836, -21.129435, -0.9401326, -7.629111, -21.129435, -0.85405326, -7.629111, -21.308428, -0.854053, -3.98836, -21.308428, -0.9401324, -3.5470695, -21.308428, -0.44779253, -3.98836, -21.308428, -0.9401324, -3.98836, -21.308428, -0.45719075, -3.98836, -21.308428, -0.9401324, -3.5470695, -21.308428, -0.44779253, -3.5470695, -21.308428, -0.707953, -3.98836, -21.308428, -0.9401324, -3.5470695, -21.308428, -0.707953, -3.5470695, -21.308428, -0.9288118, -7.629111, -21.308428, -0.854053, -3.98836, -21.308428, -0.45719075, -3.98836, -21.308428, -0.9401324, -7.629111, -21.308428, -0.854053, -7.629111, -21.308428, -0.44779253, -3.98836, -21.308428, -0.45719075, -3.5470695, -21.129435, -0.6009414, -3.587226, -21.197784, -0.6816809, -3.5470695, -21.308428, -0.707953, -3.587226, -21.197784, -0.6816809, -3.5470695, -21.129435, -0.73508, -3.5470695, -21.308428, -0.707953, -3.5470695, -21.308428, -0.44779253, -3.5470695, -21.129435, -0.6009414, -3.5470695, -21.308428, -0.707953, -3.5470695, -21.308428, -0.44779253, -3.5470695, -21.129435, -0.44779277, -3.5470695, -21.129435, -0.6009414, -3.5470695, -21.129435, -0.73508, -3.5470695, -21.308428, -0.9288118, -3.5470695, -21.308428, -0.707953, -3.5470695, -21.129435, -0.73508, -3.5470695, -21.129435, -0.928812, -3.5470695, -21.308428, -0.9288118, -7.629111, -21.129435, -0.85405326, -7.629111, -21.308428, -0.44779253, -7.629111, -21.308428, -0.854053, -7.629111, -21.308428, -0.44779253, -7.629111, -21.129435, -0.85405326, -7.629111, -21.129435, -0.5492511, -7.629111, -21.308428, -0.44779253, -7.629111, -21.129435, -0.5492511, -7.629111, -21.17429, -0.44779277, -3.876649, -21.129433, 2.6811814, -3.4046006, -21.308426, 2.69058, -3.876649, -21.308426, 2.6811814, -3.876649, -21.129433, 2.6811814, -3.4046006, -21.129433, 2.6905797, -3.4046006, -21.308426, 2.69058, -3.876649, -21.308426, 2.6811814, -6.797794, -21.129433, 2.68823, -3.876649, -21.129433, 2.6811814, -3.876649, -21.308426, 2.6811814, -7.7713666, -21.174288, 2.6905797, -6.797794, -21.129433, 2.68823, -7.7713666, -21.174288, 2.6905797, -3.876649, -21.308426, 2.6811814, -7.7713666, -21.308426, 2.69058, -3.4046006, -21.129433, 2.2095606, -3.876649, -21.129433, 2.6811814, -3.876649, -21.129433, 2.1984534, -6.797794, -21.129433, 2.68823, -3.876649, -21.129433, 2.1984534, -3.876649, -21.129433, 2.6811814, -6.797794, -21.129433, 2.68823, -7.7713666, -21.129433, 2.2843194, -3.876649, -21.129433, 2.1984534, -7.7713666, -21.129433, 2.2843194, -6.797794, -21.129433, 2.68823, -7.7713666, -21.129433, 2.5891213, -7.7713666, -21.129433, 2.5891213, -6.797794, -21.129433, 2.68823, -7.7713666, -21.174288, 2.6905797, -3.876649, -21.129433, 2.6811814, -3.4046006, -21.129433, 2.2095606, -3.6562176, -21.129433, 2.4630995, -3.6562176, -21.129433, 2.4630995, -3.4046006, -21.129433, 2.2095606, -3.4046006, -21.129433, 2.4032927, -3.4046006, -21.129433, 2.4032927, -3.4477477, -21.197784, 2.4566917, -3.6562176, -21.129433, 2.4630995, -3.4477477, -21.197784, 2.4566917, -3.4046006, -21.129433, 2.5376446, -3.6562176, -21.129433, 2.4630995, -3.876649, -21.129433, 2.6811814, -3.6562176, -21.129433, 2.4630995, -3.4046006, -21.129433, 2.5376446, -3.876649, -21.129433, 2.6811814, -3.4046006, -21.129433, 2.5376446, -3.4046006, -21.129433, 2.6905797, -3.876649, -21.308426, 2.1984537, -3.4046006, -21.129433, 2.2095606, -3.876649, -21.129433, 2.1984534, -3.876649, -21.308426, 2.1984537, -3.4046006, -21.308426, 2.2095606, -3.4046006, -21.129433, 2.2095606, -7.7713666, -21.129433, 2.2843194, -3.876649, -21.308426, 2.1984537, -3.876649, -21.129433, 2.1984534, -7.7713666, -21.129433, 2.2843194, -7.7713666, -21.308426, 2.2843194, -3.876649, -21.308426, 2.1984537, -3.4046006, -21.308426, 2.69058, -3.876649, -21.308426, 2.1984537, -3.876649, -21.308426, 2.6811814, -3.876649, -21.308426, 2.1984537, -3.4046006, -21.308426, 2.69058, -3.4046006, -21.308426, 2.430633, -3.876649, -21.308426, 2.1984537, -3.4046006, -21.308426, 2.430633, -3.4046006, -21.308426, 2.2095606, -7.7713666, -21.308426, 2.2843194, -3.876649, -21.308426, 2.6811814, -3.876649, -21.308426, 2.1984537, -7.7713666, -21.308426, 2.2843194, -7.7713666, -21.308426, 2.69058, -3.876649, -21.308426, 2.6811814, -3.4046006, -21.129433, 2.5376446, -3.4477477, -21.197784, 2.4566917, -3.4046006, -21.308426, 2.430633, -3.4477477, -21.197784, 2.4566917, -3.4046006, -21.129433, 2.4032927, -3.4046006, -21.308426, 2.430633, -3.4046006, -21.308426, 2.69058, -3.4046006, -21.129433, 2.5376446, -3.4046006, -21.308426, 2.430633, -3.4046006, -21.308426, 2.69058, -3.4046006, -21.129433, 2.6905797, -3.4046006, -21.129433, 2.5376446, -3.4046006, -21.129433, 2.4032927, -3.4046006, -21.308426, 2.2095606, -3.4046006, -21.308426, 2.430633, -3.4046006, -21.129433, 2.4032927, -3.4046006, -21.129433, 2.2095606, -3.4046006, -21.308426, 2.2095606, -7.7713666, -21.129433, 2.2843194, -7.7713666, -21.308426, 2.69058, -7.7713666, -21.308426, 2.2843194, -7.7713666, -21.308426, 2.69058, -7.7713666, -21.129433, 2.2843194, -7.7713666, -21.129433, 2.5891213, -7.7713666, -21.308426, 2.69058, -7.7713666, -21.129433, 2.5891213, -7.7713666, -21.174288, 2.6905797, -5.5538077, -21.308426, 2.9443324, -3.4046006, -21.129433, 2.8537674, -5.5538077, -21.129433, 2.9443321, -5.5538077, -21.308426, 2.9443324, -3.4046006, -21.308426, 2.8537674, -3.4046006, -21.129433, 2.8537674, -7.7713666, -21.129433, 2.8537674, -5.5538077, -21.308426, 2.9443324, -5.5538077, -21.129433, 2.9443321, -7.7713666, -21.129433, 2.8537674, -7.7713666, -21.308426, 2.8537674, -5.5538077, -21.308426, 2.9443324, -5.5538077, -21.308426, 3.4236426, -3.4046006, -21.308426, 2.8537674, -5.5538077, -21.308426, 2.9443324, -5.5538077, -21.308426, 3.4236426, -3.4046006, -21.308426, 3.260028, -3.4046006, -21.308426, 2.8537674, -5.5538077, -21.308426, 2.9443324, -7.7713666, -21.308426, 3.4084773, -5.5538077, -21.308426, 3.4236426, -5.5538077, -21.308426, 2.9443324, -7.7713666, -21.308426, 3.1265302, -7.7713666, -21.308426, 3.4084773, -7.7713666, -21.308426, 3.1265302, -5.5538077, -21.308426, 2.9443324, -7.7713666, -21.308426, 2.8537674, -3.4046006, -21.308426, 3.260028, -3.4046006, -21.129433, 2.8537674, -3.4046006, -21.308426, 2.8537674, -3.4046006, -21.308426, 3.260028, -3.4046006, -21.129433, 3.260028, -3.4046006, -21.129433, 2.8537674, -7.7713666, -21.129433, 3.0938497, -7.687637, -21.183044, 3.1414819, -7.7713666, -21.308426, 3.1265302, -5.5538077, -21.129433, 3.3038151, -5.5538077, -21.174288, 3.4236426, -6.108091, -21.129433, 3.419798, -5.5538077, -21.129433, 3.3038151, -5.0163994, -21.129433, 3.3828459, -5.5538077, -21.174288, 3.4236426, -5.5538077, -21.129433, 3.3038151, -3.4046006, -21.129433, 3.260028, -5.0163994, -21.129433, 3.3828459, -5.5538077, -21.129433, 3.3038151, -3.4046006, -21.129433, 2.8537674, -3.4046006, -21.129433, 3.260028, -3.4046006, -21.129433, 2.8537674, -5.5538077, -21.129433, 3.3038151, -5.5538077, -21.129433, 2.9443321, -6.108091, -21.129433, 3.419798, -5.5538077, -21.129433, 2.9443321, -5.5538077, -21.129433, 3.3038151, -5.5538077, -21.129433, 2.9443321, -7.7713666, -21.129433, 3.0938497, -7.7713666, -21.129433, 2.8537674, -5.5538077, -21.129433, 2.9443321, -7.39031, -21.129433, 3.1427634, -7.7713666, -21.129433, 3.0938497, -6.108091, -21.129433, 3.419798, -7.39031, -21.129433, 3.1427634, -5.5538077, -21.129433, 2.9443321, -7.687637, -21.183044, 3.1414819, -7.7713666, -21.129433, 3.0938497, -7.39031, -21.129433, 3.1427634, -7.7713666, -21.129433, 3.2290564, -7.687637, -21.183044, 3.1414819, -7.39031, -21.129433, 3.1427634, -7.39031, -21.129433, 3.1427634, -6.108091, -21.129433, 3.419798, -7.7713666, -21.129433, 3.2290564, -7.7713666, -21.129433, 3.2290564, -6.108091, -21.129433, 3.419798, -7.7713666, -21.129433, 3.4084773, -7.7713666, -21.308426, 2.8537674, -7.7713666, -21.129433, 3.0938497, -7.7713666, -21.308426, 3.1265302, -7.7713666, -21.308426, 2.8537674, -7.7713666, -21.129433, 2.8537674, -7.7713666, -21.129433, 3.0938497, -7.7713666, -21.129433, 3.2290564, -7.7713666, -21.308426, 3.4084773, -7.7713666, -21.308426, 3.1265302, -7.7713666, -21.129433, 3.2290564, -7.7713666, -21.129433, 3.4084773, -7.7713666, -21.308426, 3.4084773, -7.687637, -21.183044, 3.1414819, -7.7713666, -21.129433, 3.2290564, -7.7713666, -21.308426, 3.1265302, -7.7713666, -21.129433, 3.4084773, -5.5538077, -21.308426, 3.4236426, -7.7713666, -21.308426, 3.4084773, -5.5538077, -21.308426, 3.4236426, -7.7713666, -21.129433, 3.4084773, -6.108091, -21.129433, 3.419798, -5.5538077, -21.308426, 3.4236426, -6.108091, -21.129433, 3.419798, -5.5538077, -21.174288, 3.4236426, -5.5538077, -21.174288, 3.4236426, -3.4046006, -21.308426, 3.260028, -5.5538077, -21.308426, 3.4236426, -5.5538077, -21.174288, 3.4236426, -3.4046006, -21.129433, 3.260028, -3.4046006, -21.308426, 3.260028, -3.4046006, -21.129433, 3.260028, -5.5538077, -21.174288, 3.4236426, -5.0163994, -21.129433, 3.3828459, -3.98836, -21.129433, 4.0505486, -3.5470695, -21.308426, 4.0599465, -3.98836, -21.308426, 4.0505486, -3.98836, -21.129433, 4.0505486, -3.5470695, -21.129433, 4.0599465, -3.5470695, -21.308426, 4.0599465, -3.98836, -21.308426, 4.0505486, -6.7189765, -21.129433, 4.057597, -3.98836, -21.129433, 4.0505486, -3.98836, -21.308426, 4.0505486, -7.629111, -21.174288, 4.0599465, -6.7189765, -21.129433, 4.057597, -7.629111, -21.174288, 4.0599465, -3.98836, -21.308426, 4.0505486, -7.629111, -21.308426, 4.0599465, -3.5470695, -21.129433, 3.5789275, -3.98836, -21.129433, 4.0505486, -3.98836, -21.129433, 3.5678203, -6.7189765, -21.129433, 4.057597, -3.98836, -21.129433, 3.5678203, -3.98836, -21.129433, 4.0505486, -6.7189765, -21.129433, 4.057597, -7.629111, -21.129433, 3.653686, -3.98836, -21.129433, 3.5678203, -7.629111, -21.129433, 3.653686, -6.7189765, -21.129433, 4.057597, -7.629111, -21.129433, 3.9584882, -7.629111, -21.129433, 3.9584882, -6.7189765, -21.129433, 4.057597, -7.629111, -21.174288, 4.0599465, -3.98836, -21.129433, 4.0505486, -3.5470695, -21.129433, 3.5789275, -3.7822394, -21.129433, 3.8322525, -3.7822394, -21.129433, 3.8322525, -3.5470695, -21.129433, 3.5789275, -3.5470695, -21.129433, 3.7726593, -3.5470695, -21.129433, 3.7726593, -3.587226, -21.197783, 3.8260584, -3.7822394, -21.129433, 3.8322525, -3.587226, -21.197783, 3.8260584, -3.5470695, -21.129433, 3.9070115, -3.7822394, -21.129433, 3.8322525, -3.98836, -21.129433, 4.0505486, -3.7822394, -21.129433, 3.8322525, -3.5470695, -21.129433, 3.9070115, -3.98836, -21.129433, 4.0505486, -3.5470695, -21.129433, 3.9070115, -3.5470695, -21.129433, 4.0599465, -3.98836, -21.308426, 3.5678203, -3.5470695, -21.129433, 3.5789275, -3.98836, -21.129433, 3.5678203, -3.98836, -21.308426, 3.5678203, -3.5470695, -21.308426, 3.5789275, -3.5470695, -21.129433, 3.5789275, -7.629111, -21.129433, 3.653686, -3.98836, -21.308426, 3.5678203, -3.98836, -21.129433, 3.5678203, -7.629111, -21.129433, 3.653686, -7.629111, -21.308426, 3.653686, -3.98836, -21.308426, 3.5678203, -3.5470695, -21.308426, 4.0599465, -3.98836, -21.308426, 3.5678203, -3.98836, -21.308426, 4.0505486, -3.98836, -21.308426, 3.5678203, -3.5470695, -21.308426, 4.0599465, -3.5470695, -21.308426, 3.799786, -3.98836, -21.308426, 3.5678203, -3.5470695, -21.308426, 3.799786, -3.5470695, -21.308426, 3.5789275, -7.629111, -21.308426, 3.653686, -3.98836, -21.308426, 4.0505486, -3.98836, -21.308426, 3.5678203, -7.629111, -21.308426, 3.653686, -7.629111, -21.308426, 4.0599465, -3.98836, -21.308426, 4.0505486, -3.5470695, -21.129433, 3.9070115, -3.587226, -21.197783, 3.8260584, -3.5470695, -21.308426, 3.799786, -3.587226, -21.197783, 3.8260584, -3.5470695, -21.129433, 3.7726593, -3.5470695, -21.308426, 3.799786, -3.5470695, -21.308426, 4.0599465, -3.5470695, -21.129433, 3.9070115, -3.5470695, -21.308426, 3.799786, -3.5470695, -21.308426, 4.0599465, -3.5470695, -21.129433, 4.0599465, -3.5470695, -21.129433, 3.9070115, -3.5470695, -21.129433, 3.7726593, -3.5470695, -21.308426, 3.5789275, -3.5470695, -21.308426, 3.799786, -3.5470695, -21.129433, 3.7726593, -3.5470695, -21.129433, 3.5789275, -3.5470695, -21.308426, 3.5789275, -7.629111, -21.129433, 3.653686, -7.629111, -21.308426, 4.0599465, -7.629111, -21.308426, 3.653686, -7.629111, -21.308426, 4.0599465, -7.629111, -21.129433, 3.653686, -7.629111, -21.129433, 3.9584882, -7.629111, -21.308426, 4.0599465, -7.629111, -21.129433, 3.9584882, -7.629111, -21.174288, 4.0599465, -3.9505534, -21.397497, -1.0022888, -3.632081, -19.082966, -1.0409508, -3.59342, -21.397497, -1.0022888, -3.9505534, -21.397497, -1.0022888, -3.9118924, -19.082966, -1.0409508, -3.632081, -19.082966, -1.0409508, -3.9118924, -19.082966, -1.0409508, -3.632081, -19.082966, -1.3209755, -3.632081, -19.082966, -1.0409508, -3.9118924, -19.082966, -1.0409508, -3.9118924, -19.082966, -1.3209755, -3.632081, -19.082966, -1.3209755, -3.9118924, -19.082966, -1.3209755, -3.59342, -21.397497, -1.3596356, -3.632081, -19.082966, -1.3209755, -3.9118924, -19.082966, -1.3209755, -3.9505534, -21.397497, -1.3596356, -3.59342, -21.397497, -1.3596356, -3.9505534, -21.397497, -1.3596356, -3.59342, -21.397497, -1.0022888, -3.59342, -21.397497, -1.3596356, -3.9505534, -21.397497, -1.3596356, -3.9505534, -21.397497, -1.0022888, -3.59342, -21.397497, -1.0022888, -3.59342, -21.397497, -1.0022888, -3.632081, -19.082966, -1.3209755, -3.59342, -21.397497, -1.3596356, -3.59342, -21.397497, -1.0022888, -3.632081, -19.082966, -1.0409508, -3.632081, -19.082966, -1.3209755, -3.9505534, -21.397497, -1.3596356, -3.9118924, -19.082966, -1.0409508, -3.9505534, -21.397497, -1.0022888, -3.9505534, -21.397497, -1.3596356, -3.9118924, -19.082966, -1.3209755, -3.9118924, -19.082966, -1.0409508, -7.8010564, -21.397495, 5.8031073, -7.4439235, -19.082964, 5.8031063, -7.4439235, -21.397495, 5.8031073, -7.8010564, -21.397495, 5.8031073, -7.8010564, -19.082964, 5.8031063, -7.4439235, -19.082964, 5.8031063, -7.8010564, -21.397495, 5.445974, -7.4439235, -21.397495, 5.8031073, -7.4439235, -21.397495, 5.445974, -7.8010564, -21.397495, 5.445974, -7.8010564, -21.397495, 5.8031073, -7.4439235, -21.397495, 5.8031073, -7.4439235, -21.397495, 5.8031073, -7.4439235, -19.082964, 5.445973, -7.4439235, -21.397495, 5.445974, -7.4439235, -21.397495, 5.8031073, -7.4439235, -19.082964, 5.8031063, -7.4439235, -19.082964, 5.445973, -7.8010564, -19.661596, 5.4459734, -7.7117724, -19.082964, 5.445973, -7.8010564, -19.082964, 5.5352564, -7.8010564, -21.397495, 5.8031073, -7.8010564, -19.661596, 5.4459734, -7.8010564, -19.082964, 5.5352564, -7.8010564, -19.661596, 5.4459734, -7.8010564, -21.397495, 5.8031073, -7.8010564, -21.397495, 5.445974, -7.8010564, -21.397495, 5.8031073, -7.8010564, -19.082964, 5.5352564, -7.8010564, -19.082964, 5.8031063, -7.8010564, -19.082964, 5.5352564, -7.4439235, -19.082964, 5.8031063, -7.8010564, -19.082964, 5.8031063, -7.4439235, -19.082964, 5.8031063, -7.8010564, -19.082964, 5.5352564, -7.7117724, -19.082964, 5.445973, -7.4439235, -19.082964, 5.8031063, -7.7117724, -19.082964, 5.445973, -7.4439235, -19.082964, 5.445973, -7.8010564, -21.397495, 5.445974, -7.7117724, -19.082964, 5.445973, -7.8010564, -19.661596, 5.4459734, -7.7117724, -19.082964, 5.445973, -7.8010564, -21.397495, 5.445974, -7.4439235, -21.397495, 5.445974, -7.7117724, -19.082964, 5.445973, -7.4439235, -21.397495, 5.445974, -7.4439235, -19.082964, 5.445973, -5.555944, -21.308426, 4.955557, -3.5470695, -21.129433, 4.8647776, -5.555944, -21.129433, 4.9555564, -5.555944, -21.308426, 4.955557, -3.5470695, -21.308426, 4.864778, -3.5470695, -21.129433, 4.8647776, -7.629111, -21.129433, 4.8647776, -5.555944, -21.308426, 4.955557, -5.555944, -21.129433, 4.9555564, -7.629111, -21.129433, 4.8647776, -7.629111, -21.308426, 4.864778, -5.555944, -21.308426, 4.955557, -5.555944, -21.308426, 5.434867, -3.5470695, -21.308426, 4.864778, -5.555944, -21.308426, 4.955557, -5.555944, -21.308426, 5.434867, -3.5470695, -21.308426, 5.271038, -3.5470695, -21.308426, 4.864778, -5.555944, -21.308426, 4.955557, -7.629111, -21.308426, 5.4197016, -5.555944, -21.308426, 5.434867, -5.555944, -21.308426, 4.955557, -7.629111, -21.308426, 5.137541, -7.629111, -21.308426, 5.4197016, -7.629111, -21.308426, 5.137541, -5.555944, -21.308426, 4.955557, -7.629111, -21.308426, 4.864778, -3.5470695, -21.308426, 5.271038, -3.5470695, -21.129433, 4.8647776, -3.5470695, -21.308426, 4.864778, -3.5470695, -21.308426, 5.271038, -3.5470695, -21.129433, 5.271038, -3.5470695, -21.129433, 4.8647776, -7.629111, -21.129433, 5.1048603, -7.5507216, -21.183044, 5.152492, -7.629111, -21.308426, 5.137541, -5.555944, -21.129433, 5.3150387, -5.555944, -21.174288, 5.434867, -6.0741286, -21.129433, 5.4310217, -5.555944, -21.129433, 5.3150387, -5.053779, -21.129433, 5.3940697, -5.555944, -21.174288, 5.434867, -5.555944, -21.129433, 5.3150387, -3.5470695, -21.129433, 5.271038, -5.053779, -21.129433, 5.3940697, -5.555944, -21.129433, 5.3150387, -3.5470695, -21.129433, 4.8647776, -3.5470695, -21.129433, 5.271038, -3.5470695, -21.129433, 4.8647776, -5.555944, -21.129433, 5.3150387, -5.555944, -21.129433, 4.9555564, -6.0741286, -21.129433, 5.4310217, -5.555944, -21.129433, 4.9555564, -5.555944, -21.129433, 5.3150387, -5.555944, -21.129433, 4.9555564, -7.629111, -21.129433, 5.1048603, -7.629111, -21.129433, 4.8647776, -5.555944, -21.129433, 4.9555564, -7.272619, -21.129433, 5.153987, -7.629111, -21.129433, 5.1048603, -6.0741286, -21.129433, 5.4310217, -7.272619, -21.129433, 5.153987, -5.555944, -21.129433, 4.9555564, -7.5507216, -21.183044, 5.152492, -7.629111, -21.129433, 5.1048603, -7.272619, -21.129433, 5.153987, -7.629111, -21.129433, 5.24028, -7.5507216, -21.183044, 5.152492, -7.272619, -21.129433, 5.153987, -7.272619, -21.129433, 5.153987, -6.0741286, -21.129433, 5.4310217, -7.629111, -21.129433, 5.24028, -7.629111, -21.129433, 5.24028, -6.0741286, -21.129433, 5.4310217, -7.629111, -21.129433, 5.4197016, -7.629111, -21.308426, 4.864778, -7.629111, -21.129433, 5.1048603, -7.629111, -21.308426, 5.137541, -7.629111, -21.308426, 4.864778, -7.629111, -21.129433, 4.8647776, -7.629111, -21.129433, 5.1048603, -7.629111, -21.129433, 5.24028, -7.629111, -21.308426, 5.4197016, -7.629111, -21.308426, 5.137541, -7.629111, -21.129433, 5.24028, -7.629111, -21.129433, 5.4197016, -7.629111, -21.308426, 5.4197016, -7.5507216, -21.183044, 5.152492, -7.629111, -21.129433, 5.24028, -7.629111, -21.308426, 5.137541, -7.629111, -21.129433, 5.4197016, -5.555944, -21.308426, 5.434867, -7.629111, -21.308426, 5.4197016, -5.555944, -21.308426, 5.434867, -7.629111, -21.129433, 5.4197016, -6.0741286, -21.129433, 5.4310217, -5.555944, -21.308426, 5.434867, -6.0741286, -21.129433, 5.4310217, -5.555944, -21.174288, 5.434867, -5.555944, -21.174288, 5.434867, -3.5470695, -21.308426, 5.271038, -5.555944, -21.308426, 5.434867, -5.555944, -21.174288, 5.434867, -3.5470695, -21.129433, 5.271038, -3.5470695, -21.308426, 5.271038, -3.5470695, -21.129433, 5.271038, -5.555944, -21.174288, 5.434867, -5.053779, -21.129433, 5.3940697, -6.926806, -21.308428, 1.0420425, -3.4520192, -21.129433, 0.96386623, -6.926806, -21.129433, 1.0420425, -6.926806, -21.308428, 1.0420425, -3.4520192, -21.308428, 0.96386623, -3.4520192, -21.129433, 0.96386623, -7.723948, -21.129433, 1.0386249, -6.926806, -21.308428, 1.0420425, -6.926806, -21.129433, 1.0420425, -7.723948, -21.129433, 1.0386249, -7.723948, -21.308428, 1.0386249, -6.926806, -21.308428, 1.0420425, -6.926806, -21.308428, 1.5358775, -3.4520192, -21.308428, 0.96386623, -6.926806, -21.308428, 1.0420425, -6.926806, -21.308428, 1.5358775, -3.4520192, -21.308428, 1.4448853, -3.4520192, -21.308428, 0.96386623, -7.723948, -21.308428, 1.0386249, -6.926806, -21.308428, 1.5358775, -6.926806, -21.308428, 1.0420425, -7.723948, -21.308428, 1.0386249, -7.723948, -21.308428, 1.4448853, -6.926806, -21.308428, 1.5358775, -7.723948, -21.308428, 1.0386249, -7.723948, -21.129433, 1.4448853, -7.723948, -21.308428, 1.4448853, -7.723948, -21.308428, 1.0386249, -7.723948, -21.129433, 1.0386249, -7.723948, -21.129433, 1.4448853, -6.926806, -21.129433, 1.4124186, -6.926806, -21.174288, 1.5358775, -7.1260915, -21.129433, 1.5130227, -6.926806, -21.129433, 1.4124186, -6.0581098, -21.129433, 1.5130227, -6.926806, -21.174288, 1.5358775, -6.926806, -21.129433, 1.4124186, -4.103702, -21.129433, 1.461973, -6.0581098, -21.129433, 1.5130227, -7.1260915, -21.129433, 1.5130227, -6.926806, -21.129433, 1.0420425, -6.926806, -21.129433, 1.4124186, -7.1260915, -21.129433, 1.5130227, -7.723948, -21.129433, 1.0386249, -6.926806, -21.129433, 1.0420425, -7.723948, -21.129433, 1.0386249, -7.1260915, -21.129433, 1.5130227, -7.723948, -21.129433, 1.4448853, -3.4520192, -21.129433, 0.96386623, -6.926806, -21.129433, 1.4124186, -6.926806, -21.129433, 1.0420425, -6.926806, -21.129433, 1.4124186, -3.4520192, -21.129433, 0.96386623, -4.103702, -21.129433, 1.461973, -4.103702, -21.129433, 1.461973, -3.4520192, -21.129433, 0.96386623, -3.4520192, -21.129433, 1.3246305, -4.103702, -21.129433, 1.461973, -3.4520192, -21.129433, 1.3246305, -3.4520192, -21.174288, 1.4448853, -7.723948, -21.129433, 1.4448853, -6.926806, -21.308428, 1.5358775, -7.723948, -21.308428, 1.4448853, -6.926806, -21.308428, 1.5358775, -7.723948, -21.129433, 1.4448853, -7.1260915, -21.129433, 1.5130227, -6.926806, -21.308428, 1.5358775, -7.1260915, -21.129433, 1.5130227, -6.926806, -21.174288, 1.5358775, -6.926806, -21.174288, 1.5358775, -3.4520192, -21.308428, 1.4448853, -6.926806, -21.308428, 1.5358775, -6.926806, -21.174288, 1.5358775, -4.103702, -21.129433, 1.461973, -3.4520192, -21.308428, 1.4448853, -3.4520192, -21.308428, 1.4448853, -4.103702, -21.129433, 1.461973, -3.4520192, -21.174288, 1.4448853, -4.103702, -21.129433, 1.461973, -6.926806, -21.174288, 1.5358775, -6.0581098, -21.129433, 1.5130227, -3.4520192, -21.174288, 1.4448853, -3.4520192, -21.308428, 0.96386623, -3.4520192, -21.308428, 1.4448853, -3.4520192, -21.308428, 0.96386623, -3.4520192, -21.174288, 1.4448853, -3.4520192, -21.129433, 1.3246305, -3.4520192, -21.308428, 0.96386623, -3.4520192, -21.129433, 1.3246305, -3.4520192, -21.129433, 0.96386623, -6.956496, -21.308426, 4.294262, -3.4046006, -21.129433, 4.2160854, -6.956496, -21.129433, 4.294262, -6.956496, -21.308426, 4.294262, -3.4046006, -21.308426, 4.2160854, -3.4046006, -21.129433, 4.2160854, -7.7713666, -21.129433, 4.290844, -6.956496, -21.308426, 4.294262, -6.956496, -21.129433, 4.294262, -7.7713666, -21.129433, 4.290844, -7.7713666, -21.308426, 4.2908444, -6.956496, -21.308426, 4.294262, -6.956496, -21.308426, 4.7878833, -3.4046006, -21.308426, 4.2160854, -6.956496, -21.308426, 4.294262, -6.956496, -21.308426, 4.7878833, -3.4046006, -21.308426, 4.6971045, -3.4046006, -21.308426, 4.2160854, -7.7713666, -21.308426, 4.2908444, -6.956496, -21.308426, 4.7878833, -6.956496, -21.308426, 4.294262, -7.7713666, -21.308426, 4.2908444, -7.7713666, -21.308426, 4.6971045, -6.956496, -21.308426, 4.7878833, -7.7713666, -21.308426, 4.2908444, -7.7713666, -21.129433, 4.6971045, -7.7713666, -21.308426, 4.6971045, -7.7713666, -21.308426, 4.2908444, -7.7713666, -21.129433, 4.290844, -7.7713666, -21.129433, 4.6971045, -6.956496, -21.129433, 4.6646376, -6.956496, -21.174288, 4.787883, -7.1602664, -21.129433, 4.7652416, -6.956496, -21.129433, 4.6646376, -6.0685754, -21.129433, 4.7652416, -6.956496, -21.174288, 4.787883, -6.956496, -21.129433, 4.6646376, -4.0705943, -21.129433, 4.7141924, -6.0685754, -21.129433, 4.7652416, -7.1602664, -21.129433, 4.7652416, -6.956496, -21.129433, 4.294262, -6.956496, -21.129433, 4.6646376, -7.1602664, -21.129433, 4.7652416, -7.7713666, -21.129433, 4.290844, -6.956496, -21.129433, 4.294262, -7.7713666, -21.129433, 4.290844, -7.1602664, -21.129433, 4.7652416, -7.7713666, -21.129433, 4.6971045, -3.4046006, -21.129433, 4.2160854, -6.956496, -21.129433, 4.6646376, -6.956496, -21.129433, 4.294262, -6.956496, -21.129433, 4.6646376, -3.4046006, -21.129433, 4.2160854, -4.0705943, -21.129433, 4.7141924, -4.0705943, -21.129433, 4.7141924, -3.4046006, -21.129433, 4.2160854, -3.4046006, -21.129433, 4.57685, -4.0705943, -21.129433, 4.7141924, -3.4046006, -21.129433, 4.57685, -3.4046006, -21.174288, 4.6971045, -7.7713666, -21.129433, 4.6971045, -6.956496, -21.308426, 4.7878833, -7.7713666, -21.308426, 4.6971045, -6.956496, -21.308426, 4.7878833, -7.7713666, -21.129433, 4.6971045, -7.1602664, -21.129433, 4.7652416, -6.956496, -21.308426, 4.7878833, -7.1602664, -21.129433, 4.7652416, -6.956496, -21.174288, 4.787883, -6.956496, -21.174288, 4.787883, -3.4046006, -21.308426, 4.6971045, -6.956496, -21.308426, 4.7878833, -6.956496, -21.174288, 4.787883, -4.0705943, -21.129433, 4.7141924, -3.4046006, -21.308426, 4.6971045, -3.4046006, -21.308426, 4.6971045, -4.0705943, -21.129433, 4.7141924, -3.4046006, -21.174288, 4.6971045, -4.0705943, -21.129433, 4.7141924, -6.956496, -21.174288, 4.787883, -6.0685754, -21.129433, 4.7652416, -3.4046006, -21.174288, 4.6971045, -3.4046006, -21.308426, 4.2160854, -3.4046006, -21.308426, 4.6971045, -3.4046006, -21.308426, 4.2160854, -3.4046006, -21.174288, 4.6971045, -3.4046006, -21.129433, 4.57685, -3.4046006, -21.308426, 4.2160854, -3.4046006, -21.129433, 4.57685, -3.4046006, -21.129433, 4.2160854, -3.8851929, -21.397495, 5.744368, -3.5280595, -19.082964, 5.744367, -3.5280595, -21.397495, 5.744368, -3.8851929, -21.397495, 5.744368, -3.8851929, -19.351456, 5.7443676, -3.5280595, -19.082964, 5.744367, -3.8851929, -19.351456, 5.7443676, -3.5280595, -19.082964, 5.38702, -3.5280595, -19.082964, 5.744367, -3.8851929, -19.351456, 5.7443676, -3.8851929, -19.082964, 5.38702, -3.5280595, -19.082964, 5.38702, -3.8851929, -19.082964, 5.38702, -3.5280595, -21.397495, 5.387021, -3.5280595, -19.082964, 5.38702, -3.8851929, -19.082964, 5.38702, -3.8851929, -21.397495, 5.387021, -3.5280595, -21.397495, 5.387021, -3.8851929, -21.397495, 5.387021, -3.5280595, -21.397495, 5.744368, -3.5280595, -21.397495, 5.387021, -3.8851929, -21.397495, 5.387021, -3.8851929, -21.397495, 5.744368, -3.5280595, -21.397495, 5.744368, -3.5280595, -21.397495, 5.744368, -3.5280595, -19.082964, 5.38702, -3.5280595, -21.397495, 5.387021, -3.5280595, -21.397495, 5.744368, -3.5280595, -19.082964, 5.744367, -3.5280595, -19.082964, 5.38702, -3.8851929, -21.397495, 5.387021, -3.8851929, -19.351456, 5.7443676, -3.8851929, -21.397495, 5.744368, -3.8851929, -21.397495, 5.387021, -3.8851929, -19.082964, 5.38702, -3.8851929, -19.351456, 5.7443676, -3.8144922, -21.404758, 5.450673, -3.7010727, -21.35926, 5.450673, -3.7010727, -21.404758, 5.450673, -3.8144922, -21.404758, 5.450673, -3.8144922, -21.35926, 5.450673, -3.7010727, -21.35926, 5.450673, -3.8144922, -21.35926, 5.450673, -3.7010727, -21.359264, -1.0281339, -3.7010727, -21.35926, 5.450673, -3.8144922, -21.35926, 5.450673, -3.8144922, -21.359264, -1.0281339, -3.7010727, -21.359264, -1.0281339, -3.8144922, -21.359264, -1.0281339, -3.7010727, -21.40476, -1.0281339, -3.7010727, -21.359264, -1.0281339, -3.8144922, -21.359264, -1.0281339, -3.8144922, -21.40476, -1.0281339, -3.7010727, -21.40476, -1.0281339, -3.8144922, -21.40476, -1.0281339, -3.7010727, -21.404758, 5.450673, -3.7010727, -21.40476, -1.0281339, -3.8144922, -21.40476, -1.0281339, -3.8144922, -21.404758, 5.450673, -3.7010727, -21.404758, 5.450673, -3.7010727, -21.404758, 5.450673, -3.7010727, -21.359264, -1.0281339, -3.7010727, -21.40476, -1.0281339, -3.7010727, -21.404758, 5.450673, -3.7010727, -21.35926, 5.450673, -3.7010727, -21.359264, -1.0281339, -3.8144922, -21.40476, -1.0281339, -3.8144922, -21.35926, 5.450673, -3.8144922, -21.404758, 5.450673, -3.8144922, -21.40476, -1.0281339, -3.8144922, -21.359264, -1.0281339, -3.8144922, -21.35926, 5.450673, -7.4172235, -21.333632, 0.22268653, -7.393087, -21.350079, -0.2540605, -7.393087, -21.355633, 0.23058963, -7.4172235, -21.333632, 0.22268653, -7.4172235, -21.328293, -0.24551654, -7.393087, -21.350079, -0.2540605, -7.4172235, -21.328293, -0.24551654, -7.393087, -21.20889, -0.27221656, -7.393087, -21.350079, -0.2540605, -7.4172235, -21.328293, -0.24551654, -7.4172235, -21.214659, -0.25277925, -7.393087, -21.20889, -0.27221656, -7.393087, -21.355633, 0.23058963, -7.4172235, -21.371866, -0.2623906, -7.4172235, -21.377846, 0.23849261, -7.393087, -21.355633, 0.23058963, -7.393087, -21.350079, -0.2540605, -7.4172235, -21.371866, -0.2623906, -7.393087, -21.350079, -0.2540605, -7.4172235, -21.203339, -0.29122663, -7.4172235, -21.371866, -0.2623906, -7.393087, -21.350079, -0.2540605, -7.393087, -21.20889, -0.27221656, -7.4172235, -21.203339, -0.29122663, -7.4172235, -21.377846, 0.23849261, -7.44136, -21.350079, -0.2540605, -7.44136, -21.355633, 0.23058963, -7.4172235, -21.377846, 0.23849261, -7.4172235, -21.371866, -0.2623906, -7.44136, -21.350079, -0.2540605, -7.4172235, -21.371866, -0.2623906, -7.44136, -21.20889, -0.27221656, -7.44136, -21.350079, -0.2540605, -7.4172235, -21.371866, -0.2623906, -7.4172235, -21.203339, -0.29122663, -7.44136, -21.20889, -0.27221656, -7.44136, -21.355633, 0.23058963, -7.4172235, -21.328293, -0.24551654, -7.4172235, -21.333632, 0.22268653, -7.44136, -21.355633, 0.23058963, -7.44136, -21.350079, -0.2540605, -7.4172235, -21.328293, -0.24551654, -7.44136, -21.350079, -0.2540605, -7.4172235, -21.214659, -0.25277925, -7.4172235, -21.328293, -0.24551654, -7.44136, -21.350079, -0.2540605, -7.44136, -21.20889, -0.27221656, -7.4172235, -21.214659, -0.25277925, -7.4172235, -21.214659, -0.25277925, -7.393087, -21.090773, -0.24231291, -7.393087, -21.20889, -0.27221656, -7.4172235, -21.214659, -0.25277925, -7.4172235, -21.11427, -0.23804092, -7.393087, -21.090773, -0.24231291, -7.4172235, -21.11427, -0.23804092, -7.393087, -21.088423, 0.22674489, -7.393087, -21.090773, -0.24231291, -7.4172235, -21.11427, -0.23804092, -7.4172235, -21.111706, 0.22204578, -7.393087, -21.088423, 0.22674489, -7.4172235, -21.111706, 0.22204578, -7.393087, -21.219572, 0.27053213, -7.393087, -21.088423, 0.22674489, -7.4172235, -21.111706, 0.22204578, -7.4172235, -21.224483, 0.25109494, -7.393087, -21.219572, 0.27053213, -7.393087, -21.20889, -0.27221656, -7.4172235, -21.067278, -0.24658489, -7.4172235, -21.203339, -0.29122663, -7.393087, -21.20889, -0.27221656, -7.393087, -21.090773, -0.24231291, -7.4172235, -21.067278, -0.24658489, -7.393087, -21.090773, -0.24231291, -7.4172235, -21.064928, 0.23144388, -7.4172235, -21.067278, -0.24658489, -7.393087, -21.090773, -0.24231291, -7.393087, -21.088423, 0.22674489, -7.4172235, -21.064928, 0.23144388, -7.393087, -21.088423, 0.22674489, -7.4172235, -21.214872, 0.28996944, -7.4172235, -21.064928, 0.23144388, -7.393087, -21.088423, 0.22674489, -7.393087, -21.219572, 0.27053213, -7.4172235, -21.214872, 0.28996944, -7.4172235, -21.203339, -0.29122663, -7.44136, -21.090773, -0.24231291, -7.44136, -21.20889, -0.27221656, -7.4172235, -21.203339, -0.29122663, -7.4172235, -21.067278, -0.24658489, -7.44136, -21.090773, -0.24231291, -7.44136, -21.20889, -0.27221656, -7.4172235, -21.11427, -0.23804092, -7.4172235, -21.214659, -0.25277925, -7.44136, -21.20889, -0.27221656, -7.44136, -21.090773, -0.24231291, -7.4172235, -21.11427, -0.23804092, -7.44136, -21.090773, -0.24231291, -7.4172235, -21.111706, 0.22204578, -7.4172235, -21.11427, -0.23804092, -7.44136, -21.090773, -0.24231291, -7.44136, -21.088423, 0.22674489, -7.4172235, -21.111706, 0.22204578, -7.44136, -21.088423, 0.22674489, -7.4172235, -21.224483, 0.25109494, -7.4172235, -21.111706, 0.22204578, -7.44136, -21.088423, 0.22674489, -7.44136, -21.219572, 0.27053213, -7.4172235, -21.224483, 0.25109494, -7.4172235, -21.067278, -0.24658489, -7.44136, -21.088423, 0.22674489, -7.44136, -21.090773, -0.24231291, -7.4172235, -21.067278, -0.24658489, -7.4172235, -21.064928, 0.23144388, -7.44136, -21.088423, 0.22674489, -7.4172235, -21.064928, 0.23144388, -7.44136, -21.219572, 0.27053213, -7.44136, -21.088423, 0.22674489, -7.4172235, -21.064928, 0.23144388, -7.4172235, -21.214872, 0.28996944, -7.44136, -21.219572, 0.27053213, -7.4172235, -21.224483, 0.25109494, -7.393087, -21.355633, 0.23058963, -7.393087, -21.219572, 0.27053213, -7.4172235, -21.224483, 0.25109494, -7.4172235, -21.333632, 0.22268653, -7.393087, -21.355633, 0.23058963, -7.393087, -21.219572, 0.27053213, -7.4172235, -21.377846, 0.23849261, -7.4172235, -21.214872, 0.28996944, -7.393087, -21.219572, 0.27053213, -7.393087, -21.355633, 0.23058963, -7.4172235, -21.377846, 0.23849261, -7.4172235, -21.214872, 0.28996944, -7.44136, -21.355633, 0.23058963, -7.44136, -21.219572, 0.27053213, -7.4172235, -21.214872, 0.28996944, -7.4172235, -21.377846, 0.23849261, -7.44136, -21.355633, 0.23058963, -7.44136, -21.219572, 0.27053213, -7.4172235, -21.333632, 0.22268653, -7.4172235, -21.224483, 0.25109494, -7.44136, -21.219572, 0.27053213, -7.44136, -21.355633, 0.23058963, -7.4172235, -21.333632, 0.22268653, -3.8775034, -21.320816, 0.20922995, -3.8561435, -21.33534, -0.30446935, -3.8561435, -21.340467, 0.2177738, -3.8775034, -21.320816, 0.20922995, -3.8775034, -21.316116, -0.29528475, -3.8561435, -21.33534, -0.30446935, -3.8775034, -21.316116, -0.29528475, -3.8561435, -21.210386, -0.32390666, -3.8561435, -21.33534, -0.30446935, -3.8775034, -21.316116, -0.29528475, -3.8775034, -21.215513, -0.3031881, -3.8561435, -21.210386, -0.32390666, -3.8561435, -21.340467, 0.2177738, -3.8775034, -21.354778, -0.3132267, -3.8775034, -21.360117, 0.22631764, -3.8561435, -21.340467, 0.2177738, -3.8561435, -21.33534, -0.30446935, -3.8775034, -21.354778, -0.3132267, -3.8561435, -21.33534, -0.30446935, -3.8775034, -21.205261, -0.3446257, -3.8775034, -21.354778, -0.3132267, -3.8561435, -21.33534, -0.30446935, -3.8561435, -21.210386, -0.32390666, -3.8775034, -21.205261, -0.3446257, -3.8775034, -21.360117, 0.22631764, -3.8988628, -21.33534, -0.30446935, -3.8988628, -21.340467, 0.2177738, -3.8775034, -21.360117, 0.22631764, -3.8775034, -21.354778, -0.3132267, -3.8988628, -21.33534, -0.30446935, -3.8775034, -21.354778, -0.3132267, -3.8988628, -21.210386, -0.32390666, -3.8988628, -21.33534, -0.30446935, -3.8775034, -21.354778, -0.3132267, -3.8775034, -21.205261, -0.3446257, -3.8988628, -21.210386, -0.32390666, -3.8988628, -21.340467, 0.2177738, -3.8775034, -21.316116, -0.29528475, -3.8775034, -21.320816, 0.20922995, -3.8988628, -21.340467, 0.2177738, -3.8988628, -21.33534, -0.30446935, -3.8775034, -21.316116, -0.29528475, -3.8988628, -21.33534, -0.30446935, -3.8775034, -21.215513, -0.3031881, -3.8775034, -21.316116, -0.29528475, -3.8988628, -21.33534, -0.30446935, -3.8988628, -21.210386, -0.32390666, -3.8775034, -21.215513, -0.3031881, -3.8775034, -21.215513, -0.3031881, -3.8561435, -21.10551, -0.29165363, -3.8561435, -21.210386, -0.32390666, -3.8775034, -21.215513, -0.3031881, -3.8775034, -21.126444, -0.28716826, -3.8561435, -21.10551, -0.29165363, -3.8775034, -21.126444, -0.28716826, -3.8561435, -21.103588, 0.21350193, -3.8561435, -21.10551, -0.29165363, -3.8775034, -21.126444, -0.28716826, -3.8775034, -21.124308, 0.2085892, -3.8561435, -21.103588, 0.21350193, -3.8775034, -21.124308, 0.2085892, -3.8561435, -21.219786, 0.26070666, -3.8561435, -21.103588, 0.21350193, -3.8775034, -21.124308, 0.2085892, -3.8775034, -21.224056, 0.23977435, -3.8561435, -21.219786, 0.26070666, -3.8561435, -21.210386, -0.32390666, -3.8775034, -21.084793, -0.29635286, -3.8775034, -21.205261, -0.3446257, -3.8561435, -21.210386, -0.32390666, -3.8561435, -21.10551, -0.29165363, -3.8775034, -21.084793, -0.29635286, -3.8561435, -21.10551, -0.29165363, -3.8775034, -21.082657, 0.21862817, -3.8775034, -21.084793, -0.29635286, -3.8561435, -21.10551, -0.29165363, -3.8561435, -21.103588, 0.21350193, -3.8775034, -21.082657, 0.21862817, -3.8561435, -21.103588, 0.21350193, -3.8775034, -21.215513, 0.28163922, -3.8775034, -21.082657, 0.21862817, -3.8561435, -21.103588, 0.21350193, -3.8561435, -21.219786, 0.26070666, -3.8775034, -21.215513, 0.28163922, -3.8775034, -21.205261, -0.3446257, -3.8988628, -21.10551, -0.29165363, -3.8988628, -21.210386, -0.32390666, -3.8775034, -21.205261, -0.3446257, -3.8775034, -21.084793, -0.29635286, -3.8988628, -21.10551, -0.29165363, -3.8988628, -21.210386, -0.32390666, -3.8775034, -21.126444, -0.28716826, -3.8775034, -21.215513, -0.3031881, -3.8988628, -21.210386, -0.32390666, -3.8988628, -21.10551, -0.29165363, -3.8775034, -21.126444, -0.28716826, -3.8988628, -21.10551, -0.29165363, -3.8775034, -21.124308, 0.2085892, -3.8775034, -21.126444, -0.28716826, -3.8988628, -21.10551, -0.29165363, -3.8988628, -21.103588, 0.21350193, -3.8775034, -21.124308, 0.2085892, -3.8988628, -21.103588, 0.21350193, -3.8775034, -21.224056, 0.23977435, -3.8775034, -21.124308, 0.2085892, -3.8988628, -21.103588, 0.21350193, -3.8988628, -21.219786, 0.26070666, -3.8775034, -21.224056, 0.23977435, -3.8775034, -21.084793, -0.29635286, -3.8988628, -21.103588, 0.21350193, -3.8988628, -21.10551, -0.29165363, -3.8775034, -21.084793, -0.29635286, -3.8775034, -21.082657, 0.21862817, -3.8988628, -21.103588, 0.21350193, -3.8775034, -21.082657, 0.21862817, -3.8988628, -21.219786, 0.26070666, -3.8988628, -21.103588, 0.21350193, -3.8775034, -21.082657, 0.21862817, -3.8775034, -21.215513, 0.28163922, -3.8988628, -21.219786, 0.26070666, -3.8775034, -21.224056, 0.23977435, -3.8561435, -21.340467, 0.2177738, -3.8561435, -21.219786, 0.26070666, -3.8775034, -21.224056, 0.23977435, -3.8775034, -21.320816, 0.20922995, -3.8561435, -21.340467, 0.2177738, -3.8561435, -21.219786, 0.26070666, -3.8775034, -21.360117, 0.22631764, -3.8775034, -21.215513, 0.28163922, -3.8561435, -21.219786, 0.26070666, -3.8561435, -21.340467, 0.2177738, -3.8775034, -21.360117, 0.22631764, -3.8775034, -21.215513, 0.28163922, -3.8988628, -21.340467, 0.2177738, -3.8988628, -21.219786, 0.26070666, -3.8775034, -21.215513, 0.28163922, -3.8775034, -21.360117, 0.22631764, -3.8988628, -21.340467, 0.2177738, -3.8988628, -21.219786, 0.26070666, -3.8775034, -21.320816, 0.20922995, -3.8775034, -21.224056, 0.23977435, -3.8988628, -21.219786, 0.26070666, -3.8988628, -21.340467, 0.2177738, -3.8775034, -21.320816, 0.20922995, -7.346309, -21.333632, 0.9091855, -7.3221726, -21.350079, 0.3158145, -7.3221726, -21.355633, 0.9190109, -7.346309, -21.333632, 0.9091855, -7.346309, -21.328293, 0.3260672, -7.3221726, -21.350079, 0.3158145, -7.346309, -21.328293, 0.3260672, -7.3221726, -21.20889, 0.29317343, -7.3221726, -21.350079, 0.3158145, -7.346309, -21.328293, 0.3260672, -7.346309, -21.214659, 0.31709623, -7.3221726, -21.20889, 0.29317343, -7.3221726, -21.355633, 0.9190109, -7.346309, -21.371866, 0.3053484, -7.346309, -21.377846, 0.92883635, -7.3221726, -21.355633, 0.9190109, -7.3221726, -21.350079, 0.3158145, -7.346309, -21.371866, 0.3053484, -7.3221726, -21.350079, 0.3158145, -7.346309, -21.203339, 0.2692505, -7.346309, -21.371866, 0.3053484, -7.3221726, -21.350079, 0.3158145, -7.3221726, -21.20889, 0.29317343, -7.346309, -21.203339, 0.2692505, -7.346309, -21.377846, 0.92883635, -7.3704457, -21.350079, 0.3158145, -7.3704457, -21.355633, 0.9190109, -7.346309, -21.377846, 0.92883635, -7.346309, -21.371866, 0.3053484, -7.3704457, -21.350079, 0.3158145, -7.346309, -21.371866, 0.3053484, -7.3704457, -21.20889, 0.29317343, -7.3704457, -21.350079, 0.3158145, -7.346309, -21.371866, 0.3053484, -7.346309, -21.203339, 0.2692505, -7.3704457, -21.20889, 0.29317343, -7.3704457, -21.355633, 0.9190109, -7.346309, -21.328293, 0.3260672, -7.346309, -21.333632, 0.9091855, -7.3704457, -21.355633, 0.9190109, -7.3704457, -21.350079, 0.3158145, -7.346309, -21.328293, 0.3260672, -7.3704457, -21.350079, 0.3158145, -7.346309, -21.214659, 0.31709623, -7.346309, -21.328293, 0.3260672, -7.3704457, -21.350079, 0.3158145, -7.3704457, -21.20889, 0.29317343, -7.346309, -21.214659, 0.31709623, -7.346309, -21.214659, 0.31709623, -7.3221726, -21.090773, 0.3303392, -7.3221726, -21.20889, 0.29317343, -7.346309, -21.214659, 0.31709623, -7.346309, -21.11427, 0.33567917, -7.3221726, -21.090773, 0.3303392, -7.346309, -21.11427, 0.33567917, -7.3221726, -21.088423, 0.91409826, -7.3221726, -21.090773, 0.3303392, -7.346309, -21.11427, 0.33567917, -7.346309, -21.111706, 0.90833116, -7.3221726, -21.088423, 0.91409826, -7.346309, -21.111706, 0.90833116, -7.3221726, -21.21957, 0.96877885, -7.3221726, -21.088423, 0.91409826, -7.346309, -21.111706, 0.90833116, -7.346309, -21.224483, 0.9444289, -7.3221726, -21.21957, 0.96877885, -7.3221726, -21.20889, 0.29317343, -7.346309, -21.067278, 0.3249991, -7.346309, -21.203339, 0.2692505, -7.3221726, -21.20889, 0.29317343, -7.3221726, -21.090773, 0.3303392, -7.346309, -21.067278, 0.3249991, -7.3221726, -21.090773, 0.3303392, -7.346309, -21.064928, 0.9200789, -7.346309, -21.067278, 0.3249991, -7.3221726, -21.090773, 0.3303392, -7.3221726, -21.088423, 0.91409826, -7.346309, -21.064928, 0.9200789, -7.3221726, -21.088423, 0.91409826, -7.346309, -21.214872, 0.9929153, -7.346309, -21.064928, 0.9200789, -7.3221726, -21.088423, 0.91409826, -7.3221726, -21.21957, 0.96877885, -7.346309, -21.214872, 0.9929153, -7.346309, -21.203339, 0.2692505, -7.3704457, -21.090773, 0.3303392, -7.3704457, -21.20889, 0.29317343, -7.346309, -21.203339, 0.2692505, -7.346309, -21.067278, 0.3249991, -7.3704457, -21.090773, 0.3303392, -7.3704457, -21.20889, 0.29317343, -7.346309, -21.11427, 0.33567917, -7.346309, -21.214659, 0.31709623, -7.3704457, -21.20889, 0.29317343, -7.3704457, -21.090773, 0.3303392, -7.346309, -21.11427, 0.33567917, -7.3704457, -21.090773, 0.3303392, -7.346309, -21.111706, 0.90833116, -7.346309, -21.11427, 0.33567917, -7.3704457, -21.090773, 0.3303392, -7.3704457, -21.088423, 0.91409826, -7.346309, -21.111706, 0.90833116, -7.3704457, -21.088423, 0.91409826, -7.346309, -21.224483, 0.9444289, -7.346309, -21.111706, 0.90833116, -7.3704457, -21.088423, 0.91409826, -7.3704457, -21.21957, 0.96877885, -7.346309, -21.224483, 0.9444289, -7.346309, -21.067278, 0.3249991, -7.3704457, -21.088423, 0.91409826, -7.3704457, -21.090773, 0.3303392, -7.346309, -21.067278, 0.3249991, -7.346309, -21.064928, 0.9200789, -7.3704457, -21.088423, 0.91409826, -7.346309, -21.064928, 0.9200789, -7.3704457, -21.21957, 0.96877885, -7.3704457, -21.088423, 0.91409826, -7.346309, -21.064928, 0.9200789, -7.346309, -21.214872, 0.9929153, -7.3704457, -21.21957, 0.96877885, -7.346309, -21.224483, 0.9444289, -7.3221726, -21.355633, 0.9190109, -7.3221726, -21.21957, 0.96877885, -7.346309, -21.224483, 0.9444289, -7.346309, -21.333632, 0.9091855, -7.3221726, -21.355633, 0.9190109, -7.3221726, -21.21957, 0.96877885, -7.346309, -21.377846, 0.92883635, -7.346309, -21.214872, 0.9929153, -7.3221726, -21.21957, 0.96877885, -7.3221726, -21.355633, 0.9190109, -7.346309, -21.377846, 0.92883635, -7.346309, -21.214872, 0.9929153, -7.3704457, -21.355633, 0.9190109, -7.3704457, -21.21957, 0.96877885, -7.346309, -21.214872, 0.9929153, -7.346309, -21.377846, 0.92883635, -7.3704457, -21.355633, 0.9190109, -7.3704457, -21.21957, 0.96877885, -7.346309, -21.333632, 0.9091855, -7.346309, -21.224483, 0.9444289, -7.3704457, -21.21957, 0.96877885, -7.3704457, -21.355633, 0.9190109, -7.346309, -21.333632, 0.9091855, -7.4332433, -21.333632, 2.1138694, -7.4093204, -21.350079, 1.6759968, -7.4093204, -21.35563, 2.1211317, -7.4332433, -21.333632, 2.1138694, -7.4332433, -21.32829, 1.6838999, -7.4093204, -21.350079, 1.6759968, -7.4093204, -21.35563, 2.1211317, -7.4332433, -21.371864, 1.6683073, -7.4332433, -21.377846, 2.128394, -7.4093204, -21.35563, 2.1211317, -7.4093204, -21.350079, 1.6759968, -7.4332433, -21.371864, 1.6683073, -7.4332433, -21.377846, 2.128394, -7.45738, -21.350079, 1.6759968, -7.45738, -21.35563, 2.1211317, -7.4332433, -21.377846, 2.128394, -7.4332433, -21.371864, 1.6683073, -7.45738, -21.350079, 1.6759968, -7.45738, -21.35563, 2.1211317, -7.4332433, -21.32829, 1.6838999, -7.4332433, -21.333632, 2.1138694, -7.45738, -21.35563, 2.1211317, -7.45738, -21.350079, 1.6759968, -7.4332433, -21.32829, 1.6838999, -7.4332433, -21.32829, 1.6838999, -7.4093204, -21.20889, 1.6595497, -7.4093204, -21.350079, 1.6759968, -7.4332433, -21.32829, 1.6838999, -7.4332433, -21.214659, 1.6770647, -7.4093204, -21.20889, 1.6595497, -7.4093204, -21.350079, 1.6759968, -7.4332433, -21.203337, 1.6418213, -7.4332433, -21.371864, 1.6683073, -7.4093204, -21.350079, 1.6759968, -7.4093204, -21.20889, 1.6595497, -7.4332433, -21.203337, 1.6418213, -7.4332433, -21.371864, 1.6683073, -7.45738, -21.20889, 1.6595497, -7.45738, -21.350079, 1.6759968, -7.4332433, -21.371864, 1.6683073, -7.4332433, -21.203337, 1.6418213, -7.45738, -21.20889, 1.6595497, -7.45738, -21.350079, 1.6759968, -7.4332433, -21.214659, 1.6770647, -7.4332433, -21.32829, 1.6838999, -7.45738, -21.350079, 1.6759968, -7.45738, -21.20889, 1.6595497, -7.4332433, -21.214659, 1.6770647, -7.4332433, -21.214659, 1.6770647, -7.4093204, -21.090773, 1.6868901, -7.4093204, -21.20889, 1.6595497, -7.4332433, -21.214659, 1.6770647, -7.4332433, -21.114267, 1.6907347, -7.4093204, -21.090773, 1.6868901, -7.4093204, -21.20889, 1.6595497, -7.4332433, -21.067276, 1.6828318, -7.4332433, -21.203337, 1.6418213, -7.4093204, -21.20889, 1.6595497, -7.4093204, -21.090773, 1.6868901, -7.4332433, -21.067276, 1.6828318, -7.4332433, -21.203337, 1.6418213, -7.45738, -21.090773, 1.6868901, -7.45738, -21.20889, 1.6595497, -7.4332433, -21.203337, 1.6418213, -7.4332433, -21.067276, 1.6828318, -7.45738, -21.090773, 1.6868901, -7.45738, -21.20889, 1.6595497, -7.4332433, -21.114267, 1.6907347, -7.4332433, -21.214659, 1.6770647, -7.45738, -21.20889, 1.6595497, -7.45738, -21.090773, 1.6868901, -7.4332433, -21.114267, 1.6907347, -7.4332433, -21.114267, 1.6907347, -7.4093204, -21.088423, 2.1175005, -7.4093204, -21.090773, 1.6868901, -7.4332433, -21.114267, 1.6907347, -7.4332433, -21.111704, 2.1132286, -7.4093204, -21.088423, 2.1175005, -7.4332433, -21.111704, 2.1132286, -7.4093204, -21.21957, 2.1578703, -7.4093204, -21.088423, 2.1175005, -7.4332433, -21.111704, 2.1132286, -7.4332433, -21.224483, 2.139928, -7.4093204, -21.21957, 2.1578703, -7.4093204, -21.090773, 1.6868901, -7.4332433, -21.064926, 2.1217723, -7.4332433, -21.067276, 1.6828318, -7.4093204, -21.090773, 1.6868901, -7.4093204, -21.088423, 2.1175005, -7.4332433, -21.064926, 2.1217723, -7.4093204, -21.088423, 2.1175005, -7.4332433, -21.214872, 2.1755986, -7.4332433, -21.064926, 2.1217723, -7.4093204, -21.088423, 2.1175005, -7.4093204, -21.21957, 2.1578703, -7.4332433, -21.214872, 2.1755986, -7.4332433, -21.067276, 1.6828318, -7.45738, -21.088423, 2.1175005, -7.45738, -21.090773, 1.6868901, -7.4332433, -21.067276, 1.6828318, -7.4332433, -21.064926, 2.1217723, -7.45738, -21.088423, 2.1175005, -7.4332433, -21.064926, 2.1217723, -7.45738, -21.21957, 2.1578703, -7.45738, -21.088423, 2.1175005, -7.4332433, -21.064926, 2.1217723, -7.4332433, -21.214872, 2.1755986, -7.45738, -21.21957, 2.1578703, -7.45738, -21.090773, 1.6868901, -7.4332433, -21.111704, 2.1132286, -7.4332433, -21.114267, 1.6907347, -7.45738, -21.090773, 1.6868901, -7.45738, -21.088423, 2.1175005, -7.4332433, -21.111704, 2.1132286, -7.45738, -21.088423, 2.1175005, -7.4332433, -21.224483, 2.139928, -7.4332433, -21.111704, 2.1132286, -7.45738, -21.088423, 2.1175005, -7.45738, -21.21957, 2.1578703, -7.4332433, -21.224483, 2.139928, -7.4332433, -21.224483, 2.139928, -7.4093204, -21.35563, 2.1211317, -7.4093204, -21.21957, 2.1578703, -7.4332433, -21.224483, 2.139928, -7.4332433, -21.333632, 2.1138694, -7.4093204, -21.35563, 2.1211317, -7.4093204, -21.21957, 2.1578703, -7.4332433, -21.377846, 2.128394, -7.4332433, -21.214872, 2.1755986, -7.4093204, -21.21957, 2.1578703, -7.4093204, -21.35563, 2.1211317, -7.4332433, -21.377846, 2.128394, -7.4332433, -21.214872, 2.1755986, -7.45738, -21.35563, 2.1211317, -7.45738, -21.21957, 2.1578703, -7.4332433, -21.214872, 2.1755986, -7.4332433, -21.377846, 2.128394, -7.45738, -21.35563, 2.1211317, -7.45738, -21.21957, 2.1578703, -7.4332433, -21.333632, 2.1138694, -7.4332433, -21.224483, 2.139928, -7.45738, -21.21957, 2.1578703, -7.45738, -21.35563, 2.1211317, -7.4332433, -21.333632, 2.1138694, -7.4332433, -21.333632, 3.4082637, -7.4093204, -21.350079, 2.836039, -7.4093204, -21.35563, 3.4176621, -7.4332433, -21.333632, 3.4082637, -7.4332433, -21.32829, 2.846078, -7.4093204, -21.350079, 2.836039, -7.4332433, -21.32829, 2.846078, -7.4093204, -21.20889, 2.8142521, -7.4093204, -21.350079, 2.836039, -7.4332433, -21.32829, 2.846078, -7.4332433, -21.214657, 2.837534, -7.4093204, -21.20889, 2.8142521, -7.4093204, -21.35563, 3.4176621, -7.4332433, -21.371864, 2.826, -7.4332433, -21.377846, 3.4272738, -7.4093204, -21.35563, 3.4176621, -7.4093204, -21.350079, 2.836039, -7.4332433, -21.371864, 2.826, -7.4093204, -21.350079, 2.836039, -7.4332433, -21.203337, 2.7911837, -7.4332433, -21.371864, 2.826, -7.4093204, -21.350079, 2.836039, -7.4093204, -21.20889, 2.8142521, -7.4332433, -21.203337, 2.7911837, -7.4332433, -21.377846, 3.4272738, -7.45738, -21.350079, 2.836039, -7.45738, -21.35563, 3.4176621, -7.4332433, -21.377846, 3.4272738, -7.4332433, -21.371864, 2.826, -7.45738, -21.350079, 2.836039, -7.4332433, -21.371864, 2.826, -7.45738, -21.20889, 2.8142521, -7.45738, -21.350079, 2.836039, -7.4332433, -21.371864, 2.826, -7.4332433, -21.203337, 2.7911837, -7.45738, -21.20889, 2.8142521, -7.45738, -21.35563, 3.4176621, -7.4332433, -21.32829, 2.846078, -7.4332433, -21.333632, 3.4082637, -7.45738, -21.35563, 3.4176621, -7.45738, -21.350079, 2.836039, -7.4332433, -21.32829, 2.846078, -7.45738, -21.350079, 2.836039, -7.4332433, -21.214657, 2.837534, -7.4332433, -21.32829, 2.846078, -7.45738, -21.350079, 2.836039, -7.45738, -21.20889, 2.8142521, -7.4332433, -21.214657, 2.837534, -7.4332433, -21.214657, 2.837534, -7.4093204, -21.090773, 2.8501363, -7.4093204, -21.20889, 2.8142521, -7.4332433, -21.214657, 2.837534, -7.4332433, -21.114267, 2.8552625, -7.4093204, -21.090773, 2.8501363, -7.4332433, -21.114267, 2.8552625, -7.4093204, -21.088423, 3.412963, -7.4093204, -21.090773, 2.8501363, -7.4332433, -21.114267, 2.8552625, -7.4332433, -21.111704, 3.4071958, -7.4093204, -21.088423, 3.412963, -7.4332433, -21.111704, 3.4071958, -7.4093204, -21.21957, 3.4655075, -7.4093204, -21.088423, 3.412963, -7.4332433, -21.111704, 3.4071958, -7.4332433, -21.224483, 3.4422255, -7.4093204, -21.21957, 3.4655075, -7.4093204, -21.20889, 2.8142521, -7.4332433, -21.067276, 2.8450098, -7.4332433, -21.203337, 2.7911837, -7.4093204, -21.20889, 2.8142521, -7.4093204, -21.090773, 2.8501363, -7.4332433, -21.067276, 2.8450098, -7.4093204, -21.090773, 2.8501363, -7.4332433, -21.064926, 3.4187298, -7.4332433, -21.067276, 2.8450098, -7.4093204, -21.090773, 2.8501363, -7.4093204, -21.088423, 3.412963, -7.4332433, -21.064926, 3.4187298, -7.4093204, -21.088423, 3.412963, -7.4332433, -21.21487, 3.4887896, -7.4332433, -21.064926, 3.4187298, -7.4093204, -21.088423, 3.412963, -7.4093204, -21.21957, 3.4655075, -7.4332433, -21.21487, 3.4887896, -7.4332433, -21.203337, 2.7911837, -7.45738, -21.090773, 2.8501363, -7.45738, -21.20889, 2.8142521, -7.4332433, -21.203337, 2.7911837, -7.4332433, -21.067276, 2.8450098, -7.45738, -21.090773, 2.8501363, -7.45738, -21.20889, 2.8142521, -7.4332433, -21.114267, 2.8552625, -7.4332433, -21.214657, 2.837534, -7.45738, -21.20889, 2.8142521, -7.45738, -21.090773, 2.8501363, -7.4332433, -21.114267, 2.8552625, -7.45738, -21.090773, 2.8501363, -7.4332433, -21.111704, 3.4071958, -7.4332433, -21.114267, 2.8552625, -7.45738, -21.090773, 2.8501363, -7.45738, -21.088423, 3.412963, -7.4332433, -21.111704, 3.4071958, -7.45738, -21.088423, 3.412963, -7.4332433, -21.224483, 3.4422255, -7.4332433, -21.111704, 3.4071958, -7.45738, -21.088423, 3.412963, -7.45738, -21.21957, 3.4655075, -7.4332433, -21.224483, 3.4422255, -7.4332433, -21.067276, 2.8450098, -7.45738, -21.088423, 3.412963, -7.45738, -21.090773, 2.8501363, -7.4332433, -21.067276, 2.8450098, -7.4332433, -21.064926, 3.4187298, -7.45738, -21.088423, 3.412963, -7.4332433, -21.064926, 3.4187298, -7.45738, -21.21957, 3.4655075, -7.45738, -21.088423, 3.412963, -7.4332433, -21.064926, 3.4187298, -7.4332433, -21.21487, 3.4887896, -7.45738, -21.21957, 3.4655075, -7.4332433, -21.224483, 3.4422255, -7.4093204, -21.35563, 3.4176621, -7.4093204, -21.21957, 3.4655075, -7.4332433, -21.224483, 3.4422255, -7.4332433, -21.333632, 3.4082637, -7.4093204, -21.35563, 3.4176621, -7.4093204, -21.21957, 3.4655075, -7.4332433, -21.377846, 3.4272738, -7.4332433, -21.21487, 3.4887896, -7.4093204, -21.21957, 3.4655075, -7.4093204, -21.35563, 3.4176621, -7.4332433, -21.377846, 3.4272738, -7.4332433, -21.21487, 3.4887896, -7.45738, -21.35563, 3.4176621, -7.45738, -21.21957, 3.4655075, -7.4332433, -21.21487, 3.4887896, -7.4332433, -21.377846, 3.4272738, -7.45738, -21.35563, 3.4176621, -7.45738, -21.21957, 3.4655075, -7.4332433, -21.333632, 3.4082637, -7.4332433, -21.224483, 3.4422255, -7.45738, -21.21957, 3.4655075, -7.45738, -21.35563, 3.4176621, -7.4332433, -21.333632, 3.4082637, -3.7589574, -21.320814, 4.0362372, -3.737598, -21.335339, 3.5225377, -3.737598, -21.340466, 4.044781, -3.7589574, -21.320814, 4.0362372, -3.7589574, -21.316116, 3.5317225, -3.737598, -21.335339, 3.5225377, -3.7589574, -21.316116, 3.5317225, -3.737598, -21.210386, 3.5031006, -3.737598, -21.335339, 3.5225377, -3.7589574, -21.316116, 3.5317225, -3.7589574, -21.215511, 3.5238194, -3.737598, -21.210386, 3.5031006, -3.737598, -21.340466, 4.044781, -3.7589574, -21.354776, 3.5137806, -3.7589574, -21.360117, 4.0533247, -3.737598, -21.340466, 4.044781, -3.737598, -21.335339, 3.5225377, -3.7589574, -21.354776, 3.5137806, -3.737598, -21.335339, 3.5225377, -3.7589574, -21.20526, 3.4823818, -3.7589574, -21.354776, 3.5137806, -3.737598, -21.335339, 3.5225377, -3.737598, -21.210386, 3.5031006, -3.7589574, -21.20526, 3.4823818, -3.7589574, -21.360117, 4.0533247, -3.7803168, -21.335339, 3.5225377, -3.7803168, -21.340466, 4.044781, -3.7589574, -21.360117, 4.0533247, -3.7589574, -21.354776, 3.5137806, -3.7803168, -21.335339, 3.5225377, -3.7589574, -21.354776, 3.5137806, -3.7803168, -21.210386, 3.5031006, -3.7803168, -21.335339, 3.5225377, -3.7589574, -21.354776, 3.5137806, -3.7589574, -21.20526, 3.4823818, -3.7803168, -21.210386, 3.5031006, -3.7803168, -21.340466, 4.044781, -3.7589574, -21.316116, 3.5317225, -3.7589574, -21.320814, 4.0362372, -3.7803168, -21.340466, 4.044781, -3.7803168, -21.335339, 3.5225377, -3.7589574, -21.316116, 3.5317225, -3.7803168, -21.335339, 3.5225377, -3.7589574, -21.215511, 3.5238194, -3.7589574, -21.316116, 3.5317225, -3.7803168, -21.335339, 3.5225377, -3.7803168, -21.210386, 3.5031006, -3.7589574, -21.215511, 3.5238194, -3.7589574, -21.215511, 3.5238194, -3.737598, -21.105509, 3.5353537, -3.737598, -21.210386, 3.5031006, -3.7589574, -21.215511, 3.5238194, -3.7589574, -21.126442, 3.5398393, -3.737598, -21.105509, 3.5353537, -3.7589574, -21.126442, 3.5398393, -3.737598, -21.103588, 4.040509, -3.737598, -21.105509, 3.5353537, -3.7589574, -21.126442, 3.5398393, -3.7589574, -21.124306, 4.0355964, -3.737598, -21.103588, 4.040509, -3.7589574, -21.124306, 4.0355964, -3.737598, -21.219784, 4.087714, -3.737598, -21.103588, 4.040509, -3.7589574, -21.124306, 4.0355964, -3.7589574, -21.224056, 4.0667815, -3.737598, -21.219784, 4.087714, -3.737598, -21.210386, 3.5031006, -3.7589574, -21.084791, 3.5306544, -3.7589574, -21.20526, 3.4823818, -3.737598, -21.210386, 3.5031006, -3.737598, -21.105509, 3.5353537, -3.7589574, -21.084791, 3.5306544, -3.737598, -21.105509, 3.5353537, -3.7589574, -21.082655, 4.045635, -3.7589574, -21.084791, 3.5306544, -3.737598, -21.105509, 3.5353537, -3.737598, -21.103588, 4.040509, -3.7589574, -21.082655, 4.045635, -3.737598, -21.103588, 4.040509, -3.7589574, -21.215511, 4.1086464, -3.7589574, -21.082655, 4.045635, -3.737598, -21.103588, 4.040509, -3.737598, -21.219784, 4.087714, -3.7589574, -21.215511, 4.1086464, -3.7589574, -21.20526, 3.4823818, -3.7803168, -21.105509, 3.5353537, -3.7803168, -21.210386, 3.5031006, -3.7589574, -21.20526, 3.4823818, -3.7589574, -21.084791, 3.5306544, -3.7803168, -21.105509, 3.5353537, -3.7803168, -21.210386, 3.5031006, -3.7589574, -21.126442, 3.5398393, -3.7589574, -21.215511, 3.5238194, -3.7803168, -21.210386, 3.5031006, -3.7803168, -21.105509, 3.5353537, -3.7589574, -21.126442, 3.5398393, -3.7803168, -21.105509, 3.5353537, -3.7589574, -21.124306, 4.0355964, -3.7589574, -21.126442, 3.5398393, -3.7803168, -21.105509, 3.5353537, -3.7803168, -21.103588, 4.040509, -3.7589574, -21.124306, 4.0355964, -3.7803168, -21.103588, 4.040509, -3.7589574, -21.224056, 4.0667815, -3.7589574, -21.124306, 4.0355964, -3.7803168, -21.103588, 4.040509, -3.7803168, -21.219784, 4.087714, -3.7589574, -21.224056, 4.0667815, -3.7589574, -21.084791, 3.5306544, -3.7803168, -21.103588, 4.040509, -3.7803168, -21.105509, 3.5353537, -3.7589574, -21.084791, 3.5306544, -3.7589574, -21.082655, 4.045635, -3.7803168, -21.103588, 4.040509, -3.7589574, -21.082655, 4.045635, -3.7803168, -21.219784, 4.087714, -3.7803168, -21.103588, 4.040509, -3.7589574, -21.082655, 4.045635, -3.7589574, -21.215511, 4.1086464, -3.7803168, -21.219784, 4.087714, -3.7589574, -21.224056, 4.0667815, -3.737598, -21.340466, 4.044781, -3.737598, -21.219784, 4.087714, -3.7589574, -21.224056, 4.0667815, -3.7589574, -21.320814, 4.0362372, -3.737598, -21.340466, 4.044781, -3.737598, -21.219784, 4.087714, -3.7589574, -21.360117, 4.0533247, -3.7589574, -21.215511, 4.1086464, -3.737598, -21.219784, 4.087714, -3.737598, -21.340466, 4.044781, -3.7589574, -21.360117, 4.0533247, -3.7589574, -21.215511, 4.1086464, -3.7803168, -21.340466, 4.044781, -3.7803168, -21.219784, 4.087714, -3.7589574, -21.215511, 4.1086464, -3.7589574, -21.360117, 4.0533247, -3.7803168, -21.340466, 4.044781, -3.7803168, -21.219784, 4.087714, -3.7589574, -21.320814, 4.0362372, -3.7589574, -21.224056, 4.0667815, -3.7803168, -21.219784, 4.087714, -3.7803168, -21.340466, 4.044781, -3.7589574, -21.320814, 4.0362372, -7.491341, -21.333632, 1.4549245, -7.4672046, -21.350079, 1.0367026, -7.4672046, -21.355633, 1.4619732, -7.491341, -21.333632, 1.4549245, -7.491341, -21.32829, 1.0439649, -7.4672046, -21.350079, 1.0367026, -7.4672046, -21.355633, 1.4619732, -7.491341, -21.371866, 1.0292267, -7.491341, -21.377846, 1.4688082, -7.4672046, -21.355633, 1.4619732, -7.4672046, -21.350079, 1.0367026, -7.491341, -21.371866, 1.0292267, -7.491341, -21.377846, 1.4688082, -7.5154777, -21.350079, 1.0367026, -7.5154777, -21.355633, 1.4619732, -7.491341, -21.377846, 1.4688082, -7.491341, -21.371866, 1.0292267, -7.5154777, -21.350079, 1.0367026, -7.5154777, -21.355633, 1.4619732, -7.491341, -21.32829, 1.0439649, -7.491341, -21.333632, 1.4549245, -7.5154777, -21.355633, 1.4619732, -7.5154777, -21.350079, 1.0367026, -7.491341, -21.32829, 1.0439649, -7.491341, -21.32829, 1.0439649, -7.4672046, -21.20889, 1.0206829, -7.4672046, -21.350079, 1.0367026, -7.491341, -21.32829, 1.0439649, -7.491341, -21.214659, 1.037557, -7.4672046, -21.20889, 1.0206829, -7.4672046, -21.350079, 1.0367026, -7.491341, -21.203339, 1.0038087, -7.491341, -21.371866, 1.0292267, -7.4672046, -21.350079, 1.0367026, -7.4672046, -21.20889, 1.0206829, -7.491341, -21.203339, 1.0038087, -7.491341, -21.371866, 1.0292267, -7.5154777, -21.20889, 1.0206829, -7.5154777, -21.350079, 1.0367026, -7.491341, -21.371866, 1.0292267, -7.491341, -21.203339, 1.0038087, -7.5154777, -21.20889, 1.0206829, -7.5154777, -21.350079, 1.0367026, -7.491341, -21.214659, 1.037557, -7.491341, -21.32829, 1.0439649, -7.5154777, -21.350079, 1.0367026, -7.5154777, -21.20889, 1.0206829, -7.491341, -21.214659, 1.037557, -7.491341, -21.214659, 1.037557, -7.4672046, -21.090773, 1.0469551, -7.4672046, -21.20889, 1.0206829, -7.491341, -21.214659, 1.037557, -7.491341, -21.114267, 1.0505863, -7.4672046, -21.090773, 1.0469551, -7.4672046, -21.20889, 1.0206829, -7.491341, -21.067276, 1.0431103, -7.491341, -21.203339, 1.0038087, -7.4672046, -21.20889, 1.0206829, -7.4672046, -21.090773, 1.0469551, -7.491341, -21.067276, 1.0431103, -7.491341, -21.203339, 1.0038087, -7.5154777, -21.090773, 1.0469551, -7.5154777, -21.20889, 1.0206829, -7.491341, -21.203339, 1.0038087, -7.491341, -21.067276, 1.0431103, -7.5154777, -21.090773, 1.0469551, -7.5154777, -21.20889, 1.0206829, -7.491341, -21.114267, 1.0505863, -7.491341, -21.214659, 1.037557, -7.5154777, -21.20889, 1.0206829, -7.5154777, -21.090773, 1.0469551, -7.491341, -21.114267, 1.0505863, -7.491341, -21.114267, 1.0505863, -7.4672046, -21.088423, 1.4585555, -7.4672046, -21.090773, 1.0469551, -7.491341, -21.114267, 1.0505863, -7.491341, -21.111704, 1.4542835, -7.4672046, -21.088423, 1.4585555, -7.4672046, -21.090773, 1.0469551, -7.491341, -21.064926, 1.4626138, -7.491341, -21.067276, 1.0431103, -7.4672046, -21.090773, 1.0469551, -7.4672046, -21.088423, 1.4585555, -7.491341, -21.064926, 1.4626138, -7.491341, -21.067276, 1.0431103, -7.5154777, -21.088423, 1.4585555, -7.5154777, -21.090773, 1.0469551, -7.491341, -21.067276, 1.0431103, -7.491341, -21.064926, 1.4626138, -7.5154777, -21.088423, 1.4585555, -7.5154777, -21.090773, 1.0469551, -7.491341, -21.111704, 1.4542835, -7.491341, -21.114267, 1.0505863, -7.5154777, -21.090773, 1.0469551, -7.5154777, -21.088423, 1.4585555, -7.491341, -21.111704, 1.4542835, -7.491341, -21.111704, 1.4542835, -7.4672046, -21.21957, 1.4970028, -7.4672046, -21.088423, 1.4585555, -7.491341, -21.111704, 1.4542835, -7.491341, -21.224483, 1.4799151, -7.4672046, -21.21957, 1.4970028, -7.4672046, -21.088423, 1.4585555, -7.491341, -21.214872, 1.5138769, -7.491341, -21.064926, 1.4626138, -7.4672046, -21.088423, 1.4585555, -7.4672046, -21.21957, 1.4970028, -7.491341, -21.214872, 1.5138769, -7.491341, -21.064926, 1.4626138, -7.5154777, -21.21957, 1.4970028, -7.5154777, -21.088423, 1.4585555, -7.491341, -21.064926, 1.4626138, -7.491341, -21.214872, 1.5138769, -7.5154777, -21.21957, 1.4970028, -7.5154777, -21.088423, 1.4585555, -7.491341, -21.224483, 1.4799151, -7.491341, -21.111704, 1.4542835, -7.5154777, -21.088423, 1.4585555, -7.5154777, -21.21957, 1.4970028, -7.491341, -21.224483, 1.4799151, -7.491341, -21.224483, 1.4799151, -7.4672046, -21.355633, 1.4619732, -7.4672046, -21.21957, 1.4970028, -7.491341, -21.224483, 1.4799151, -7.491341, -21.333632, 1.4549245, -7.4672046, -21.355633, 1.4619732, -7.4672046, -21.21957, 1.4970028, -7.491341, -21.377846, 1.4688082, -7.491341, -21.214872, 1.5138769, -7.4672046, -21.21957, 1.4970028, -7.4672046, -21.355633, 1.4619732, -7.491341, -21.377846, 1.4688082, -7.491341, -21.214872, 1.5138769, -7.5154777, -21.355633, 1.4619732, -7.5154777, -21.21957, 1.4970028, -7.491341, -21.214872, 1.5138769, -7.491341, -21.377846, 1.4688082, -7.5154777, -21.355633, 1.4619732, -7.5154777, -21.21957, 1.4970028, -7.491341, -21.333632, 1.4549245, -7.491341, -21.224483, 1.4799151, -7.5154777, -21.21957, 1.4970028, -7.5154777, -21.355633, 1.4619732, -7.491341, -21.333632, 1.4549245, -7.4881377, -21.333632, 2.6685793, -7.464001, -21.350079, 2.2503576, -7.464001, -21.35563, 2.6756282, -7.4881377, -21.333632, 2.6685793, -7.4881377, -21.32829, 2.2576199, -7.464001, -21.350079, 2.2503576, -7.464001, -21.35563, 2.6756282, -7.4881377, -21.371864, 2.2428818, -7.4881377, -21.377846, 2.6824632, -7.464001, -21.35563, 2.6756282, -7.464001, -21.350079, 2.2503576, -7.4881377, -21.371864, 2.2428818, -7.4881377, -21.377846, 2.6824632, -7.5122733, -21.350079, 2.2503576, -7.5122733, -21.35563, 2.6756282, -7.4881377, -21.377846, 2.6824632, -7.4881377, -21.371864, 2.2428818, -7.5122733, -21.350079, 2.2503576, -7.5122733, -21.35563, 2.6756282, -7.4881377, -21.32829, 2.2576199, -7.4881377, -21.333632, 2.6685793, -7.5122733, -21.35563, 2.6756282, -7.5122733, -21.350079, 2.2503576, -7.4881377, -21.32829, 2.2576199, -7.4881377, -21.32829, 2.2576199, -7.464001, -21.20889, 2.2343378, -7.464001, -21.350079, 2.2503576, -7.4881377, -21.32829, 2.2576199, -7.4881377, -21.214659, 2.251212, -7.464001, -21.20889, 2.2343378, -7.464001, -21.350079, 2.2503576, -7.4881377, -21.203337, 2.2174637, -7.4881377, -21.371864, 2.2428818, -7.464001, -21.350079, 2.2503576, -7.464001, -21.20889, 2.2343378, -7.4881377, -21.203337, 2.2174637, -7.4881377, -21.371864, 2.2428818, -7.5122733, -21.20889, 2.2343378, -7.5122733, -21.350079, 2.2503576, -7.4881377, -21.371864, 2.2428818, -7.4881377, -21.203337, 2.2174637, -7.5122733, -21.20889, 2.2343378, -7.5122733, -21.350079, 2.2503576, -7.4881377, -21.214659, 2.251212, -7.4881377, -21.32829, 2.2576199, -7.5122733, -21.350079, 2.2503576, -7.5122733, -21.20889, 2.2343378, -7.4881377, -21.214659, 2.251212, -7.4881377, -21.214659, 2.251212, -7.464001, -21.090773, 2.26061, -7.464001, -21.20889, 2.2343378, -7.4881377, -21.214659, 2.251212, -7.4881377, -21.114267, 2.2642412, -7.464001, -21.090773, 2.26061, -7.464001, -21.20889, 2.2343378, -7.4881377, -21.067276, 2.2567654, -7.4881377, -21.203337, 2.2174637, -7.464001, -21.20889, 2.2343378, -7.464001, -21.090773, 2.26061, -7.4881377, -21.067276, 2.2567654, -7.4881377, -21.203337, 2.2174637, -7.5122733, -21.090773, 2.26061, -7.5122733, -21.20889, 2.2343378, -7.4881377, -21.203337, 2.2174637, -7.4881377, -21.067276, 2.2567654, -7.5122733, -21.090773, 2.26061, -7.5122733, -21.20889, 2.2343378, -7.4881377, -21.114267, 2.2642412, -7.4881377, -21.214659, 2.251212, -7.5122733, -21.20889, 2.2343378, -7.5122733, -21.090773, 2.26061, -7.4881377, -21.114267, 2.2642412, -7.4881377, -21.114267, 2.2642412, -7.464001, -21.088423, 2.6722105, -7.464001, -21.090773, 2.26061, -7.4881377, -21.114267, 2.2642412, -7.4881377, -21.111704, 2.6679385, -7.464001, -21.088423, 2.6722105, -7.464001, -21.090773, 2.26061, -7.4881377, -21.064926, 2.6762688, -7.4881377, -21.067276, 2.2567654, -7.464001, -21.090773, 2.26061, -7.464001, -21.088423, 2.6722105, -7.4881377, -21.064926, 2.6762688, -7.4881377, -21.067276, 2.2567654, -7.5122733, -21.088423, 2.6722105, -7.5122733, -21.090773, 2.26061, -7.4881377, -21.067276, 2.2567654, -7.4881377, -21.064926, 2.6762688, -7.5122733, -21.088423, 2.6722105, -7.5122733, -21.090773, 2.26061, -7.4881377, -21.111704, 2.6679385, -7.4881377, -21.114267, 2.2642412, -7.5122733, -21.090773, 2.26061, -7.5122733, -21.088423, 2.6722105, -7.4881377, -21.111704, 2.6679385, -7.4881377, -21.111704, 2.6679385, -7.464001, -21.21957, 2.7106578, -7.464001, -21.088423, 2.6722105, -7.4881377, -21.111704, 2.6679385, -7.4881377, -21.224483, 2.6935701, -7.464001, -21.21957, 2.7106578, -7.464001, -21.088423, 2.6722105, -7.4881377, -21.21487, 2.727532, -7.4881377, -21.064926, 2.6762688, -7.464001, -21.088423, 2.6722105, -7.464001, -21.21957, 2.7106578, -7.4881377, -21.21487, 2.727532, -7.4881377, -21.064926, 2.6762688, -7.5122733, -21.21957, 2.7106578, -7.5122733, -21.088423, 2.6722105, -7.4881377, -21.064926, 2.6762688, -7.4881377, -21.21487, 2.727532, -7.5122733, -21.21957, 2.7106578, -7.5122733, -21.088423, 2.6722105, -7.4881377, -21.224483, 2.6935701, -7.4881377, -21.111704, 2.6679385, -7.5122733, -21.088423, 2.6722105, -7.5122733, -21.21957, 2.7106578, -7.4881377, -21.224483, 2.6935701, -7.4881377, -21.224483, 2.6935701, -7.464001, -21.35563, 2.6756282, -7.464001, -21.21957, 2.7106578, -7.4881377, -21.224483, 2.6935701, -7.4881377, -21.333632, 2.6685793, -7.464001, -21.35563, 2.6756282, -7.464001, -21.21957, 2.7106578, -7.4881377, -21.377846, 2.6824632, -7.4881377, -21.21487, 2.727532, -7.464001, -21.21957, 2.7106578, -7.464001, -21.35563, 2.6756282, -7.4881377, -21.377846, 2.6824632, -7.4881377, -21.21487, 2.727532, -7.5122733, -21.35563, 2.6756282, -7.5122733, -21.21957, 2.7106578, -7.4881377, -21.21487, 2.727532, -7.4881377, -21.377846, 2.6824632, -7.5122733, -21.35563, 2.6756282, -7.5122733, -21.21957, 2.7106578, -7.4881377, -21.333632, 2.6685793, -7.4881377, -21.224483, 2.6935701, -7.5122733, -21.21957, 2.7106578, -7.5122733, -21.35563, 2.6756282, -7.4881377, -21.333632, 2.6685793, -3.7905698, -21.320816, 1.4850415, -3.7692099, -21.33534, 0.9713421, -3.7692099, -21.340466, 1.4933717, -3.7905698, -21.320816, 1.4850415, -3.7905698, -21.316116, 0.98031306, -3.7692099, -21.33534, 0.9713421, -3.7905698, -21.316116, 0.98031306, -3.7692099, -21.210386, 0.95169127, -3.7692099, -21.33534, 0.9713421, -3.7905698, -21.316116, 0.98031306, -3.7905698, -21.215513, 0.9724101, -3.7692099, -21.210386, 0.95169127, -3.7692099, -21.340466, 1.4933717, -3.7905698, -21.354778, 0.9623711, -3.7905698, -21.360117, 1.5019157, -3.7692099, -21.340466, 1.4933717, -3.7692099, -21.33534, 0.9713421, -3.7905698, -21.354778, 0.9623711, -3.7692099, -21.33534, 0.9713421, -3.7905698, -21.20526, 0.93097234, -3.7905698, -21.354778, 0.9623711, -3.7692099, -21.33534, 0.9713421, -3.7692099, -21.210386, 0.95169127, -3.7905698, -21.20526, 0.93097234, -3.7905698, -21.360117, 1.5019157, -3.8119292, -21.33534, 0.9713421, -3.8119292, -21.340466, 1.4933717, -3.7905698, -21.360117, 1.5019157, -3.7905698, -21.354778, 0.9623711, -3.8119292, -21.33534, 0.9713421, -3.7905698, -21.354778, 0.9623711, -3.8119292, -21.210386, 0.95169127, -3.8119292, -21.33534, 0.9713421, -3.7905698, -21.354778, 0.9623711, -3.7905698, -21.20526, 0.93097234, -3.8119292, -21.210386, 0.95169127, -3.8119292, -21.340466, 1.4933717, -3.7905698, -21.316116, 0.98031306, -3.7905698, -21.320816, 1.4850415, -3.8119292, -21.340466, 1.4933717, -3.8119292, -21.33534, 0.9713421, -3.7905698, -21.316116, 0.98031306, -3.8119292, -21.33534, 0.9713421, -3.7905698, -21.215513, 0.9724101, -3.7905698, -21.316116, 0.98031306, -3.8119292, -21.33534, 0.9713421, -3.8119292, -21.210386, 0.95169127, -3.7905698, -21.215513, 0.9724101, -3.7905698, -21.215513, 0.9724101, -3.7692099, -21.10551, 0.9839442, -3.7692099, -21.210386, 0.95169127, -3.7905698, -21.215513, 0.9724101, -3.7905698, -21.126444, 0.9884298, -3.7692099, -21.10551, 0.9839442, -3.7905698, -21.126444, 0.9884298, -3.7692099, -21.103588, 1.4893134, -3.7692099, -21.10551, 0.9839442, -3.7905698, -21.126444, 0.9884298, -3.7905698, -21.124308, 1.4841871, -3.7692099, -21.103588, 1.4893134, -3.7905698, -21.124308, 1.4841871, -3.7692099, -21.219784, 1.5363047, -3.7692099, -21.103588, 1.4893134, -3.7905698, -21.124308, 1.4841871, -3.7905698, -21.224056, 1.5153722, -3.7692099, -21.219784, 1.5363047, -3.7692099, -21.210386, 0.95169127, -3.7905698, -21.084791, 0.9792452, -3.7905698, -21.20526, 0.93097234, -3.7692099, -21.210386, 0.95169127, -3.7692099, -21.10551, 0.9839442, -3.7905698, -21.084791, 0.9792452, -3.7692099, -21.10551, 0.9839442, -3.7905698, -21.082655, 1.4942261, -3.7905698, -21.084791, 0.9792452, -3.7692099, -21.10551, 0.9839442, -3.7692099, -21.103588, 1.4893134, -3.7905698, -21.082655, 1.4942261, -3.7692099, -21.103588, 1.4893134, -3.7905698, -21.215513, 1.5574508, -3.7905698, -21.082655, 1.4942261, -3.7692099, -21.103588, 1.4893134, -3.7692099, -21.219784, 1.5363047, -3.7905698, -21.215513, 1.5574508, -3.7905698, -21.20526, 0.93097234, -3.8119292, -21.10551, 0.9839442, -3.8119292, -21.210386, 0.95169127, -3.7905698, -21.20526, 0.93097234, -3.7905698, -21.084791, 0.9792452, -3.8119292, -21.10551, 0.9839442, -3.8119292, -21.210386, 0.95169127, -3.7905698, -21.126444, 0.9884298, -3.7905698, -21.215513, 0.9724101, -3.8119292, -21.210386, 0.95169127, -3.8119292, -21.10551, 0.9839442, -3.7905698, -21.126444, 0.9884298, -3.8119292, -21.10551, 0.9839442, -3.7905698, -21.124308, 1.4841871, -3.7905698, -21.126444, 0.9884298, -3.8119292, -21.10551, 0.9839442, -3.8119292, -21.103588, 1.4893134, -3.7905698, -21.124308, 1.4841871, -3.8119292, -21.103588, 1.4893134, -3.7905698, -21.224056, 1.5153722, -3.7905698, -21.124308, 1.4841871, -3.8119292, -21.103588, 1.4893134, -3.8119292, -21.219784, 1.5363047, -3.7905698, -21.224056, 1.5153722, -3.7905698, -21.084791, 0.9792452, -3.8119292, -21.103588, 1.4893134, -3.8119292, -21.10551, 0.9839442, -3.7905698, -21.084791, 0.9792452, -3.7905698, -21.082655, 1.4942261, -3.8119292, -21.103588, 1.4893134, -3.7905698, -21.082655, 1.4942261, -3.8119292, -21.219784, 1.5363047, -3.8119292, -21.103588, 1.4893134, -3.7905698, -21.082655, 1.4942261, -3.7905698, -21.215513, 1.5574508, -3.8119292, -21.219784, 1.5363047, -3.7905698, -21.224056, 1.5153722, -3.7692099, -21.340466, 1.4933717, -3.7692099, -21.219784, 1.5363047, -3.7905698, -21.224056, 1.5153722, -3.7905698, -21.320816, 1.4850415, -3.7692099, -21.340466, 1.4933717, -3.7692099, -21.219784, 1.5363047, -3.7905698, -21.360117, 1.5019157, -3.7905698, -21.215513, 1.5574508, -3.7692099, -21.219784, 1.5363047, -3.7692099, -21.340466, 1.4933717, -3.7905698, -21.360117, 1.5019157, -3.7905698, -21.215513, 1.5574508, -3.8119292, -21.340466, 1.4933717, -3.8119292, -21.219784, 1.5363047, -3.7905698, -21.215513, 1.5574508, -3.7905698, -21.360117, 1.5019157, -3.8119292, -21.340466, 1.4933717, -3.8119292, -21.219784, 1.5363047, -3.7905698, -21.320816, 1.4850415, -3.7905698, -21.224056, 1.5153722, -3.8119292, -21.219784, 1.5363047, -3.8119292, -21.340466, 1.4933717, -3.7905698, -21.320816, 1.4850415, -3.845891, -21.320816, 2.7324448, -3.824531, -21.33534, 2.2187452, -3.824531, -21.340466, 2.7409885, -3.845891, -21.320816, 2.7324448, -3.845891, -21.316116, 2.2277162, -3.824531, -21.33534, 2.2187452, -3.845891, -21.316116, 2.2277162, -3.824531, -21.210386, 2.1990943, -3.824531, -21.33534, 2.2187452, -3.845891, -21.316116, 2.2277162, -3.845891, -21.215513, 2.2200267, -3.824531, -21.210386, 2.1990943, -3.824531, -21.340466, 2.7409885, -3.845891, -21.354776, 2.2097743, -3.845891, -21.360117, 2.7495325, -3.824531, -21.340466, 2.7409885, -3.824531, -21.33534, 2.2187452, -3.845891, -21.354776, 2.2097743, -3.824531, -21.33534, 2.2187452, -3.845891, -21.20526, 2.1783755, -3.845891, -21.354776, 2.2097743, -3.824531, -21.33534, 2.2187452, -3.824531, -21.210386, 2.1990943, -3.845891, -21.20526, 2.1783755, -3.845891, -21.360117, 2.7495325, -3.867251, -21.33534, 2.2187452, -3.867251, -21.340466, 2.7409885, -3.845891, -21.360117, 2.7495325, -3.845891, -21.354776, 2.2097743, -3.867251, -21.33534, 2.2187452, -3.845891, -21.354776, 2.2097743, -3.867251, -21.210386, 2.1990943, -3.867251, -21.33534, 2.2187452, -3.845891, -21.354776, 2.2097743, -3.845891, -21.20526, 2.1783755, -3.867251, -21.210386, 2.1990943, -3.867251, -21.340466, 2.7409885, -3.845891, -21.316116, 2.2277162, -3.845891, -21.320816, 2.7324448, -3.867251, -21.340466, 2.7409885, -3.867251, -21.33534, 2.2187452, -3.845891, -21.316116, 2.2277162, -3.867251, -21.33534, 2.2187452, -3.845891, -21.215513, 2.2200267, -3.845891, -21.316116, 2.2277162, -3.867251, -21.33534, 2.2187452, -3.867251, -21.210386, 2.1990943, -3.845891, -21.215513, 2.2200267, -3.845891, -21.215513, 2.2200267, -3.824531, -21.10551, 2.2313473, -3.824531, -21.210386, 2.1990943, -3.845891, -21.215513, 2.2200267, -3.845891, -21.126442, 2.2360466, -3.824531, -21.10551, 2.2313473, -3.845891, -21.126442, 2.2360466, -3.824531, -21.103588, 2.7367165, -3.824531, -21.10551, 2.2313473, -3.845891, -21.126442, 2.2360466, -3.845891, -21.124306, 2.7315903, -3.824531, -21.103588, 2.7367165, -3.845891, -21.124306, 2.7315903, -3.824531, -21.219784, 2.7839212, -3.824531, -21.103588, 2.7367165, -3.845891, -21.124306, 2.7315903, -3.845891, -21.224056, 2.762989, -3.824531, -21.219784, 2.7839212, -3.824531, -21.210386, 2.1990943, -3.845891, -21.084791, 2.226862, -3.845891, -21.20526, 2.1783755, -3.824531, -21.210386, 2.1990943, -3.824531, -21.10551, 2.2313473, -3.845891, -21.084791, 2.226862, -3.824531, -21.10551, 2.2313473, -3.845891, -21.082655, 2.7418427, -3.845891, -21.084791, 2.226862, -3.824531, -21.10551, 2.2313473, -3.824531, -21.103588, 2.7367165, -3.845891, -21.082655, 2.7418427, -3.824531, -21.103588, 2.7367165, -3.845891, -21.215511, 2.804854, -3.845891, -21.082655, 2.7418427, -3.824531, -21.103588, 2.7367165, -3.824531, -21.219784, 2.7839212, -3.845891, -21.215511, 2.804854, -3.845891, -21.20526, 2.1783755, -3.867251, -21.10551, 2.2313473, -3.867251, -21.210386, 2.1990943, -3.845891, -21.20526, 2.1783755, -3.845891, -21.084791, 2.226862, -3.867251, -21.10551, 2.2313473, -3.867251, -21.210386, 2.1990943, -3.845891, -21.126442, 2.2360466, -3.845891, -21.215513, 2.2200267, -3.867251, -21.210386, 2.1990943, -3.867251, -21.10551, 2.2313473, -3.845891, -21.126442, 2.2360466, -3.867251, -21.10551, 2.2313473, -3.845891, -21.124306, 2.7315903, -3.845891, -21.126442, 2.2360466, -3.867251, -21.10551, 2.2313473, -3.867251, -21.103588, 2.7367165, -3.845891, -21.124306, 2.7315903, -3.867251, -21.103588, 2.7367165, -3.845891, -21.224056, 2.762989, -3.845891, -21.124306, 2.7315903, -3.867251, -21.103588, 2.7367165, -3.867251, -21.219784, 2.7839212, -3.845891, -21.224056, 2.762989, -3.845891, -21.084791, 2.226862, -3.867251, -21.103588, 2.7367165, -3.867251, -21.10551, 2.2313473, -3.845891, -21.084791, 2.226862, -3.845891, -21.082655, 2.7418427, -3.867251, -21.103588, 2.7367165, -3.845891, -21.082655, 2.7418427, -3.867251, -21.219784, 2.7839212, -3.867251, -21.103588, 2.7367165, -3.845891, -21.082655, 2.7418427, -3.845891, -21.215511, 2.804854, -3.867251, -21.219784, 2.7839212, -3.845891, -21.224056, 2.762989, -3.824531, -21.340466, 2.7409885, -3.824531, -21.219784, 2.7839212, -3.845891, -21.224056, 2.762989, -3.845891, -21.320816, 2.7324448, -3.824531, -21.340466, 2.7409885, -3.824531, -21.219784, 2.7839212, -3.845891, -21.360117, 2.7495325, -3.845891, -21.215511, 2.804854, -3.824531, -21.219784, 2.7839212, -3.824531, -21.340466, 2.7409885, -3.845891, -21.360117, 2.7495325, -3.845891, -21.215511, 2.804854, -3.867251, -21.340466, 2.7409885, -3.867251, -21.219784, 2.7839212, -3.845891, -21.215511, 2.804854, -3.845891, -21.360117, 2.7495325, -3.867251, -21.340466, 2.7409885, -3.867251, -21.219784, 2.7839212, -3.845891, -21.320816, 2.7324448, -3.845891, -21.224056, 2.762989, -3.867251, -21.219784, 2.7839212, -3.867251, -21.340466, 2.7409885, -3.845891, -21.320816, 2.7324448, -3.7747636, -21.320816, 2.1403553, -3.7534037, -21.33534, 1.6268696, -3.7534037, -21.340466, 2.1488993, -3.7747636, -21.320816, 2.1403553, -3.7747636, -21.316116, 1.6358405, -3.7534037, -21.33534, 1.6268696, -3.7747636, -21.316116, 1.6358405, -3.7534037, -21.210386, 1.6072186, -3.7534037, -21.33534, 1.6268696, -3.7747636, -21.316116, 1.6358405, -3.7747636, -21.215513, 1.6279376, -3.7534037, -21.210386, 1.6072186, -3.7534037, -21.340466, 2.1488993, -3.7747636, -21.354778, 1.617685, -3.7747636, -21.360117, 2.157443, -3.7534037, -21.340466, 2.1488993, -3.7534037, -21.33534, 1.6268696, -3.7747636, -21.354778, 1.617685, -3.7534037, -21.33534, 1.6268696, -3.7747636, -21.20526, 1.5864998, -3.7747636, -21.354778, 1.617685, -3.7534037, -21.33534, 1.6268696, -3.7534037, -21.210386, 1.6072186, -3.7747636, -21.20526, 1.5864998, -3.7747636, -21.360117, 2.157443, -3.796123, -21.33534, 1.6268696, -3.796123, -21.340466, 2.1488993, -3.7747636, -21.360117, 2.157443, -3.7747636, -21.354778, 1.617685, -3.796123, -21.33534, 1.6268696, -3.7747636, -21.354778, 1.617685, -3.796123, -21.210386, 1.6072186, -3.796123, -21.33534, 1.6268696, -3.7747636, -21.354778, 1.617685, -3.7747636, -21.20526, 1.5864998, -3.796123, -21.210386, 1.6072186, -3.796123, -21.340466, 2.1488993, -3.7747636, -21.316116, 1.6358405, -3.7747636, -21.320816, 2.1403553, -3.796123, -21.340466, 2.1488993, -3.796123, -21.33534, 1.6268696, -3.7747636, -21.316116, 1.6358405, -3.796123, -21.33534, 1.6268696, -3.7747636, -21.215513, 1.6279376, -3.7747636, -21.316116, 1.6358405, -3.796123, -21.33534, 1.6268696, -3.796123, -21.210386, 1.6072186, -3.7747636, -21.215513, 1.6279376, -3.7747636, -21.215513, 1.6279376, -3.7534037, -21.10551, 1.639258, -3.7534037, -21.210386, 1.6072186, -3.7747636, -21.215513, 1.6279376, -3.7747636, -21.126442, 1.6439571, -3.7534037, -21.10551, 1.639258, -3.7747636, -21.126442, 1.6439571, -3.7534037, -21.103588, 2.144627, -3.7534037, -21.10551, 1.639258, -3.7747636, -21.126442, 1.6439571, -3.7747636, -21.124306, 2.1395009, -3.7534037, -21.103588, 2.144627, -3.7747636, -21.124306, 2.1395009, -3.7534037, -21.219784, 2.191832, -3.7534037, -21.103588, 2.144627, -3.7747636, -21.124306, 2.1395009, -3.7747636, -21.224056, 2.1708996, -3.7534037, -21.219784, 2.191832, -3.7534037, -21.210386, 1.6072186, -3.7747636, -21.084791, 1.6347725, -3.7747636, -21.20526, 1.5864998, -3.7534037, -21.210386, 1.6072186, -3.7534037, -21.10551, 1.639258, -3.7747636, -21.084791, 1.6347725, -3.7534037, -21.10551, 1.639258, -3.7747636, -21.082655, 2.1497536, -3.7747636, -21.084791, 1.6347725, -3.7534037, -21.10551, 1.639258, -3.7534037, -21.103588, 2.144627, -3.7747636, -21.082655, 2.1497536, -3.7534037, -21.103588, 2.144627, -3.7747636, -21.215513, 2.2127645, -3.7747636, -21.082655, 2.1497536, -3.7534037, -21.103588, 2.144627, -3.7534037, -21.219784, 2.191832, -3.7747636, -21.215513, 2.2127645, -3.7747636, -21.20526, 1.5864998, -3.796123, -21.10551, 1.639258, -3.796123, -21.210386, 1.6072186, -3.7747636, -21.20526, 1.5864998, -3.7747636, -21.084791, 1.6347725, -3.796123, -21.10551, 1.639258, -3.796123, -21.210386, 1.6072186, -3.7747636, -21.126442, 1.6439571, -3.7747636, -21.215513, 1.6279376, -3.796123, -21.210386, 1.6072186, -3.796123, -21.10551, 1.639258, -3.7747636, -21.126442, 1.6439571, -3.796123, -21.10551, 1.639258, -3.7747636, -21.124306, 2.1395009, -3.7747636, -21.126442, 1.6439571, -3.796123, -21.10551, 1.639258, -3.796123, -21.103588, 2.144627, -3.7747636, -21.124306, 2.1395009, -3.796123, -21.103588, 2.144627, -3.7747636, -21.224056, 2.1708996, -3.7747636, -21.124306, 2.1395009, -3.796123, -21.103588, 2.144627, -3.796123, -21.219784, 2.191832, -3.7747636, -21.224056, 2.1708996, -3.7747636, -21.084791, 1.6347725, -3.796123, -21.103588, 2.144627, -3.796123, -21.10551, 1.639258, -3.7747636, -21.084791, 1.6347725, -3.7747636, -21.082655, 2.1497536, -3.796123, -21.103588, 2.144627, -3.7747636, -21.082655, 2.1497536, -3.796123, -21.219784, 2.191832, -3.796123, -21.103588, 2.144627, -3.7747636, -21.082655, 2.1497536, -3.7747636, -21.215513, 2.2127645, -3.796123, -21.219784, 2.191832, -3.7747636, -21.224056, 2.1708996, -3.7534037, -21.340466, 2.1488993, -3.7534037, -21.219784, 2.191832, -3.7747636, -21.224056, 2.1708996, -3.7747636, -21.320816, 2.1403553, -3.7534037, -21.340466, 2.1488993, -3.7534037, -21.219784, 2.191832, -3.7747636, -21.360117, 2.157443, -3.7747636, -21.215513, 2.2127645, -3.7534037, -21.219784, 2.191832, -3.7534037, -21.340466, 2.1488993, -3.7747636, -21.360117, 2.157443, -3.7747636, -21.215513, 2.2127645, -3.796123, -21.340466, 2.1488993, -3.796123, -21.219784, 2.191832, -3.7747636, -21.215513, 2.2127645, -3.7747636, -21.360117, 2.157443, -3.796123, -21.340466, 2.1488993, -3.796123, -21.219784, 2.191832, -3.7747636, -21.320816, 2.1403553, -3.7747636, -21.224056, 2.1708996, -3.796123, -21.219784, 2.191832, -3.796123, -21.340466, 2.1488993, -3.7747636, -21.320816, 2.1403553, -7.4558845, -21.333632, -0.46060824, -7.431748, -21.350079, -0.89848113, -7.431748, -21.355633, -0.453346, -7.4558845, -21.333632, -0.46060824, -7.4558845, -21.328293, -0.89057803, -7.431748, -21.350079, -0.89848113, -7.431748, -21.355633, -0.453346, -7.4558845, -21.371866, -0.9061706, -7.4558845, -21.377846, -0.44608378, -7.431748, -21.355633, -0.453346, -7.431748, -21.350079, -0.89848113, -7.4558845, -21.371866, -0.9061706, -7.4558845, -21.377846, -0.44608378, -7.480021, -21.350079, -0.89848113, -7.480021, -21.355633, -0.453346, -7.4558845, -21.377846, -0.44608378, -7.4558845, -21.371866, -0.9061706, -7.480021, -21.350079, -0.89848113, -7.480021, -21.355633, -0.453346, -7.4558845, -21.328293, -0.89057803, -7.4558845, -21.333632, -0.46060824, -7.480021, -21.355633, -0.453346, -7.480021, -21.350079, -0.89848113, -7.4558845, -21.328293, -0.89057803, -7.4558845, -21.328293, -0.89057803, -7.431748, -21.208893, -0.9149282, -7.431748, -21.350079, -0.89848113, -7.4558845, -21.328293, -0.89057803, -7.4558845, -21.214659, -0.89741325, -7.431748, -21.208893, -0.9149282, -7.431748, -21.350079, -0.89848113, -7.4558845, -21.203339, -0.93265676, -7.4558845, -21.371866, -0.9061706, -7.431748, -21.350079, -0.89848113, -7.431748, -21.208893, -0.9149282, -7.4558845, -21.203339, -0.93265676, -7.4558845, -21.371866, -0.9061706, -7.480021, -21.208893, -0.9149282, -7.480021, -21.350079, -0.89848113, -7.4558845, -21.371866, -0.9061706, -7.4558845, -21.203339, -0.93265676, -7.480021, -21.208893, -0.9149282, -7.480021, -21.350079, -0.89848113, -7.4558845, -21.214659, -0.89741325, -7.4558845, -21.328293, -0.89057803, -7.480021, -21.350079, -0.89848113, -7.480021, -21.208893, -0.9149282, -7.4558845, -21.214659, -0.89741325, -7.4558845, -21.214659, -0.89741325, -7.431748, -21.090773, -0.8875878, -7.431748, -21.208893, -0.9149282, -7.4558845, -21.214659, -0.89741325, -7.4558845, -21.11427, -0.88374305, -7.431748, -21.090773, -0.8875878, -7.431748, -21.208893, -0.9149282, -7.4558845, -21.067278, -0.89164615, -7.4558845, -21.203339, -0.93265676, -7.431748, -21.208893, -0.9149282, -7.431748, -21.090773, -0.8875878, -7.4558845, -21.067278, -0.89164615, -7.4558845, -21.203339, -0.93265676, -7.480021, -21.090773, -0.8875878, -7.480021, -21.208893, -0.9149282, -7.4558845, -21.203339, -0.93265676, -7.4558845, -21.067278, -0.89164615, -7.480021, -21.090773, -0.8875878, -7.480021, -21.208893, -0.9149282, -7.4558845, -21.11427, -0.88374305, -7.4558845, -21.214659, -0.89741325, -7.480021, -21.208893, -0.9149282, -7.480021, -21.090773, -0.8875878, -7.4558845, -21.11427, -0.88374305, -7.4558845, -21.11427, -0.88374305, -7.431748, -21.088423, -0.4569776, -7.431748, -21.090773, -0.8875878, -7.4558845, -21.11427, -0.88374305, -7.4558845, -21.111706, -0.46124935, -7.431748, -21.088423, -0.4569776, -7.4558845, -21.111706, -0.46124935, -7.431748, -21.219572, -0.41682124, -7.431748, -21.088423, -0.4569776, -7.4558845, -21.111706, -0.46124935, -7.4558845, -21.224483, -0.4345498, -7.431748, -21.219572, -0.41682124, -7.431748, -21.090773, -0.8875878, -7.4558845, -21.064928, -0.45249176, -7.4558845, -21.067278, -0.89164615, -7.431748, -21.090773, -0.8875878, -7.431748, -21.088423, -0.4569776, -7.4558845, -21.064928, -0.45249176, -7.431748, -21.088423, -0.4569776, -7.4558845, -21.214872, -0.39887905, -7.4558845, -21.064928, -0.45249176, -7.431748, -21.088423, -0.4569776, -7.431748, -21.219572, -0.41682124, -7.4558845, -21.214872, -0.39887905, -7.4558845, -21.067278, -0.89164615, -7.480021, -21.088423, -0.4569776, -7.480021, -21.090773, -0.8875878, -7.4558845, -21.067278, -0.89164615, -7.4558845, -21.064928, -0.45249176, -7.480021, -21.088423, -0.4569776, -7.4558845, -21.064928, -0.45249176, -7.480021, -21.219572, -0.41682124, -7.480021, -21.088423, -0.4569776, -7.4558845, -21.064928, -0.45249176, -7.4558845, -21.214872, -0.39887905, -7.480021, -21.219572, -0.41682124, -7.480021, -21.090773, -0.8875878, -7.4558845, -21.111706, -0.46124935, -7.4558845, -21.11427, -0.88374305, -7.480021, -21.090773, -0.8875878, -7.480021, -21.088423, -0.4569776, -7.4558845, -21.111706, -0.46124935, -7.480021, -21.088423, -0.4569776, -7.4558845, -21.224483, -0.4345498, -7.4558845, -21.111706, -0.46124935, -7.480021, -21.088423, -0.4569776, -7.480021, -21.219572, -0.41682124, -7.4558845, -21.224483, -0.4345498, -7.4558845, -21.224483, -0.4345498, -7.431748, -21.355633, -0.453346, -7.431748, -21.219572, -0.41682124, -7.4558845, -21.224483, -0.4345498, -7.4558845, -21.333632, -0.46060824, -7.431748, -21.355633, -0.453346, -7.431748, -21.219572, -0.41682124, -7.4558845, -21.377846, -0.44608378, -7.4558845, -21.214872, -0.39887905, -7.431748, -21.219572, -0.41682124, -7.431748, -21.355633, -0.453346, -7.4558845, -21.377846, -0.44608378, -7.4558845, -21.214872, -0.39887905, -7.480021, -21.355633, -0.453346, -7.480021, -21.219572, -0.41682124, -7.4558845, -21.214872, -0.39887905, -7.4558845, -21.377846, -0.44608378, -7.480021, -21.355633, -0.453346, -7.480021, -21.219572, -0.41682124, -7.4558845, -21.333632, -0.46060824, -7.4558845, -21.224483, -0.4345498, -7.480021, -21.219572, -0.41682124, -7.480021, -21.355633, -0.453346, -7.4558845, -21.333632, -0.46060824, -3.8775034, -21.320816, 0.8506601, -3.8561435, -21.33534, 0.33696067, -3.8561435, -21.340467, 0.85920393, -3.8775034, -21.320816, 0.8506601, -3.8775034, -21.316116, 0.34593165, -3.8561435, -21.33534, 0.33696067, -3.8775034, -21.316116, 0.34593165, -3.8561435, -21.210386, 0.31752336, -3.8561435, -21.33534, 0.33696067, -3.8775034, -21.316116, 0.34593165, -3.8775034, -21.215513, 0.33824217, -3.8561435, -21.210386, 0.31752336, -3.8561435, -21.340467, 0.85920393, -3.8775034, -21.354778, 0.3279897, -3.8775034, -21.360117, 0.8677478, -3.8561435, -21.340467, 0.85920393, -3.8561435, -21.33534, 0.33696067, -3.8775034, -21.354778, 0.3279897, -3.8561435, -21.33534, 0.33696067, -3.8775034, -21.205261, 0.29680455, -3.8775034, -21.354778, 0.3279897, -3.8561435, -21.33534, 0.33696067, -3.8561435, -21.210386, 0.31752336, -3.8775034, -21.205261, 0.29680455, -3.8775034, -21.360117, 0.8677478, -3.8988628, -21.33534, 0.33696067, -3.8988628, -21.340467, 0.85920393, -3.8775034, -21.360117, 0.8677478, -3.8775034, -21.354778, 0.3279897, -3.8988628, -21.33534, 0.33696067, -3.8775034, -21.354778, 0.3279897, -3.8988628, -21.210386, 0.31752336, -3.8988628, -21.33534, 0.33696067, -3.8775034, -21.354778, 0.3279897, -3.8775034, -21.205261, 0.29680455, -3.8988628, -21.210386, 0.31752336, -3.8988628, -21.340467, 0.85920393, -3.8775034, -21.316116, 0.34593165, -3.8775034, -21.320816, 0.8506601, -3.8988628, -21.340467, 0.85920393, -3.8988628, -21.33534, 0.33696067, -3.8775034, -21.316116, 0.34593165, -3.8988628, -21.33534, 0.33696067, -3.8775034, -21.215513, 0.33824217, -3.8775034, -21.316116, 0.34593165, -3.8988628, -21.33534, 0.33696067, -3.8988628, -21.210386, 0.31752336, -3.8775034, -21.215513, 0.33824217, -3.8775034, -21.215513, 0.33824217, -3.8561435, -21.10551, 0.34956276, -3.8561435, -21.210386, 0.31752336, -3.8775034, -21.215513, 0.33824217, -3.8775034, -21.126444, 0.35404837, -3.8561435, -21.10551, 0.34956276, -3.8775034, -21.126444, 0.35404837, -3.8561435, -21.103588, 0.85493207, -3.8561435, -21.10551, 0.34956276, -3.8775034, -21.126444, 0.35404837, -3.8775034, -21.124308, 0.8498056, -3.8561435, -21.103588, 0.85493207, -3.8775034, -21.124308, 0.8498056, -3.8561435, -21.219784, 0.9021368, -3.8561435, -21.103588, 0.85493207, -3.8775034, -21.124308, 0.8498056, -3.8775034, -21.224056, 0.88120437, -3.8561435, -21.219784, 0.9021368, -3.8561435, -21.210386, 0.31752336, -3.8775034, -21.084791, 0.34507728, -3.8775034, -21.205261, 0.29680455, -3.8561435, -21.210386, 0.31752336, -3.8561435, -21.10551, 0.34956276, -3.8775034, -21.084791, 0.34507728, -3.8561435, -21.10551, 0.34956276, -3.8775034, -21.082655, 0.8600583, -3.8775034, -21.084791, 0.34507728, -3.8561435, -21.10551, 0.34956276, -3.8561435, -21.103588, 0.85493207, -3.8775034, -21.082655, 0.8600583, -3.8561435, -21.103588, 0.85493207, -3.8775034, -21.215513, 0.92306924, -3.8775034, -21.082655, 0.8600583, -3.8561435, -21.103588, 0.85493207, -3.8561435, -21.219784, 0.9021368, -3.8775034, -21.215513, 0.92306924, -3.8775034, -21.205261, 0.29680455, -3.8988628, -21.10551, 0.34956276, -3.8988628, -21.210386, 0.31752336, -3.8775034, -21.205261, 0.29680455, -3.8775034, -21.084791, 0.34507728, -3.8988628, -21.10551, 0.34956276, -3.8988628, -21.210386, 0.31752336, -3.8775034, -21.126444, 0.35404837, -3.8775034, -21.215513, 0.33824217, -3.8988628, -21.210386, 0.31752336, -3.8988628, -21.10551, 0.34956276, -3.8775034, -21.126444, 0.35404837, -3.8988628, -21.10551, 0.34956276, -3.8775034, -21.124308, 0.8498056, -3.8775034, -21.126444, 0.35404837, -3.8988628, -21.10551, 0.34956276, -3.8988628, -21.103588, 0.85493207, -3.8775034, -21.124308, 0.8498056, -3.8988628, -21.103588, 0.85493207, -3.8775034, -21.224056, 0.88120437, -3.8775034, -21.124308, 0.8498056, -3.8988628, -21.103588, 0.85493207, -3.8988628, -21.219784, 0.9021368, -3.8775034, -21.224056, 0.88120437, -3.8775034, -21.084791, 0.34507728, -3.8988628, -21.103588, 0.85493207, -3.8988628, -21.10551, 0.34956276, -3.8775034, -21.084791, 0.34507728, -3.8775034, -21.082655, 0.8600583, -3.8988628, -21.103588, 0.85493207, -3.8775034, -21.082655, 0.8600583, -3.8988628, -21.219784, 0.9021368, -3.8988628, -21.103588, 0.85493207, -3.8775034, -21.082655, 0.8600583, -3.8775034, -21.215513, 0.92306924, -3.8988628, -21.219784, 0.9021368, -3.8775034, -21.224056, 0.88120437, -3.8561435, -21.340467, 0.85920393, -3.8561435, -21.219784, 0.9021368, -3.8775034, -21.224056, 0.88120437, -3.8775034, -21.320816, 0.8506601, -3.8561435, -21.340467, 0.85920393, -3.8561435, -21.219784, 0.9021368, -3.8775034, -21.360117, 0.8677478, -3.8775034, -21.215513, 0.92306924, -3.8561435, -21.219784, 0.9021368, -3.8561435, -21.340467, 0.85920393, -3.8775034, -21.360117, 0.8677478, -3.8775034, -21.215513, 0.92306924, -3.8988628, -21.340467, 0.85920393, -3.8988628, -21.219784, 0.9021368, -3.8775034, -21.215513, 0.92306924, -3.8775034, -21.360117, 0.8677478, -3.8988628, -21.340467, 0.85920393, -3.8988628, -21.219784, 0.9021368, -3.8775034, -21.320816, 0.8506601, -3.8775034, -21.224056, 0.88120437, -3.8988628, -21.219784, 0.9021368, -3.8988628, -21.340467, 0.85920393, -3.8775034, -21.320816, 0.8506601, -3.719442, -21.320814, 3.3595638, -3.6980824, -21.33534, 2.846078, -3.6980824, -21.340466, 3.3681078, -3.719442, -21.320814, 3.3595638, -3.719442, -21.316116, 2.8550491, -3.6980824, -21.33534, 2.846078, -3.719442, -21.316116, 2.8550491, -3.6980824, -21.210386, 2.826427, -3.6980824, -21.33534, 2.846078, -3.719442, -21.316116, 2.8550491, -3.719442, -21.215511, 2.847146, -3.6980824, -21.210386, 2.826427, -3.6980824, -21.340466, 3.3681078, -3.719442, -21.354776, 2.8368933, -3.719442, -21.360117, 3.376865, -3.6980824, -21.340466, 3.3681078, -3.6980824, -21.33534, 2.846078, -3.719442, -21.354776, 2.8368933, -3.6980824, -21.33534, 2.846078, -3.719442, -21.20526, 2.8057082, -3.719442, -21.354776, 2.8368933, -3.6980824, -21.33534, 2.846078, -3.6980824, -21.210386, 2.826427, -3.719442, -21.20526, 2.8057082, -3.719442, -21.360117, 3.376865, -3.7408013, -21.33534, 2.846078, -3.7408013, -21.340466, 3.3681078, -3.719442, -21.360117, 3.376865, -3.719442, -21.354776, 2.8368933, -3.7408013, -21.33534, 2.846078, -3.719442, -21.354776, 2.8368933, -3.7408013, -21.210386, 2.826427, -3.7408013, -21.33534, 2.846078, -3.719442, -21.354776, 2.8368933, -3.719442, -21.20526, 2.8057082, -3.7408013, -21.210386, 2.826427, -3.7408013, -21.340466, 3.3681078, -3.719442, -21.316116, 2.8550491, -3.719442, -21.320814, 3.3595638, -3.7408013, -21.340466, 3.3681078, -3.7408013, -21.33534, 2.846078, -3.719442, -21.316116, 2.8550491, -3.7408013, -21.33534, 2.846078, -3.719442, -21.215511, 2.847146, -3.719442, -21.316116, 2.8550491, -3.7408013, -21.33534, 2.846078, -3.7408013, -21.210386, 2.826427, -3.719442, -21.215511, 2.847146, -3.719442, -21.215511, 2.847146, -3.6980824, -21.10551, 2.85868, -3.6980824, -21.210386, 2.826427, -3.719442, -21.215511, 2.847146, -3.719442, -21.126442, 2.8631656, -3.6980824, -21.10551, 2.85868, -3.719442, -21.126442, 2.8631656, -3.6980824, -21.103588, 3.3640494, -3.6980824, -21.10551, 2.85868, -3.719442, -21.126442, 2.8631656, -3.719442, -21.124306, 3.358923, -3.6980824, -21.103588, 3.3640494, -3.719442, -21.124306, 3.358923, -3.6980824, -21.219784, 3.411254, -3.6980824, -21.103588, 3.3640494, -3.719442, -21.124306, 3.358923, -3.719442, -21.224056, 3.3903217, -3.6980824, -21.219784, 3.411254, -3.6980824, -21.210386, 2.826427, -3.719442, -21.084791, 2.853981, -3.719442, -21.20526, 2.8057082, -3.6980824, -21.210386, 2.826427, -3.6980824, -21.10551, 2.85868, -3.719442, -21.084791, 2.853981, -3.6980824, -21.10551, 2.85868, -3.719442, -21.082655, 3.3691754, -3.719442, -21.084791, 2.853981, -3.6980824, -21.10551, 2.85868, -3.6980824, -21.103588, 3.3640494, -3.719442, -21.082655, 3.3691754, -3.6980824, -21.103588, 3.3640494, -3.719442, -21.215511, 3.431973, -3.719442, -21.082655, 3.3691754, -3.6980824, -21.103588, 3.3640494, -3.6980824, -21.219784, 3.411254, -3.719442, -21.215511, 3.431973, -3.719442, -21.20526, 2.8057082, -3.7408013, -21.10551, 2.85868, -3.7408013, -21.210386, 2.826427, -3.719442, -21.20526, 2.8057082, -3.719442, -21.084791, 2.853981, -3.7408013, -21.10551, 2.85868, -3.7408013, -21.210386, 2.826427, -3.719442, -21.126442, 2.8631656, -3.719442, -21.215511, 2.847146, -3.7408013, -21.210386, 2.826427, -3.7408013, -21.10551, 2.85868, -3.719442, -21.126442, 2.8631656, -3.7408013, -21.10551, 2.85868, -3.719442, -21.124306, 3.358923, -3.719442, -21.126442, 2.8631656, -3.7408013, -21.10551, 2.85868, -3.7408013, -21.103588, 3.3640494, -3.719442, -21.124306, 3.358923, -3.7408013, -21.103588, 3.3640494, -3.719442, -21.224056, 3.3903217, -3.719442, -21.124306, 3.358923, -3.7408013, -21.103588, 3.3640494, -3.7408013, -21.219784, 3.411254, -3.719442, -21.224056, 3.3903217, -3.719442, -21.084791, 2.853981, -3.7408013, -21.103588, 3.3640494, -3.7408013, -21.10551, 2.85868, -3.719442, -21.084791, 2.853981, -3.719442, -21.082655, 3.3691754, -3.7408013, -21.103588, 3.3640494, -3.719442, -21.082655, 3.3691754, -3.7408013, -21.219784, 3.411254, -3.7408013, -21.103588, 3.3640494, -3.719442, -21.082655, 3.3691754, -3.719442, -21.215511, 3.431973, -3.7408013, -21.219784, 3.411254, -3.719442, -21.224056, 3.3903217, -3.6980824, -21.340466, 3.3681078, -3.6980824, -21.219784, 3.411254, -3.719442, -21.224056, 3.3903217, -3.719442, -21.320814, 3.3595638, -3.6980824, -21.340466, 3.3681078, -3.6980824, -21.219784, 3.411254, -3.719442, -21.360117, 3.376865, -3.719442, -21.215511, 3.431973, -3.6980824, -21.219784, 3.411254, -3.6980824, -21.340466, 3.3681078, -3.719442, -21.360117, 3.376865, -3.719442, -21.215511, 3.431973, -3.7408013, -21.340466, 3.3681078, -3.7408013, -21.219784, 3.411254, -3.719442, -21.215511, 3.431973, -3.719442, -21.360117, 3.376865, -3.7408013, -21.340466, 3.3681078, -3.7408013, -21.219784, 3.411254, -3.719442, -21.320814, 3.3595638, -3.719442, -21.224056, 3.3903217, -3.7408013, -21.219784, 3.411254, -3.7408013, -21.340466, 3.3681078, -3.719442, -21.320814, 3.3595638, -7.4172235, -21.33363, 4.03581, -7.393087, -21.350079, 3.6370256, -7.393087, -21.35563, 4.0424314, -7.4172235, -21.33363, 4.03581, -7.4172235, -21.32829, 3.6438606, -7.393087, -21.350079, 3.6370256, -7.393087, -21.35563, 4.0424314, -7.4172235, -21.371864, 3.6299767, -7.4172235, -21.377846, 4.049053, -7.393087, -21.35563, 4.0424314, -7.393087, -21.350079, 3.6370256, -7.4172235, -21.371864, 3.6299767, -7.4172235, -21.377846, 4.049053, -7.44136, -21.350079, 3.6370256, -7.44136, -21.35563, 4.0424314, -7.4172235, -21.377846, 4.049053, -7.4172235, -21.371864, 3.6299767, -7.44136, -21.350079, 3.6370256, -7.44136, -21.35563, 4.0424314, -7.4172235, -21.32829, 3.6438606, -7.4172235, -21.33363, 4.03581, -7.44136, -21.35563, 4.0424314, -7.44136, -21.350079, 3.6370256, -7.4172235, -21.32829, 3.6438606, -7.4172235, -21.32829, 3.6438606, -7.393087, -21.20889, 3.6218603, -7.393087, -21.350079, 3.6370256, -7.4172235, -21.32829, 3.6438606, -7.4172235, -21.214657, 3.6378798, -7.393087, -21.20889, 3.6218603, -7.393087, -21.350079, 3.6370256, -7.4172235, -21.203337, 3.605627, -7.4172235, -21.371864, 3.6299767, -7.393087, -21.350079, 3.6370256, -7.393087, -21.20889, 3.6218603, -7.4172235, -21.203337, 3.605627, -7.4172235, -21.371864, 3.6299767, -7.44136, -21.20889, 3.6218603, -7.44136, -21.350079, 3.6370256, -7.4172235, -21.371864, 3.6299767, -7.4172235, -21.203337, 3.605627, -7.44136, -21.20889, 3.6218603, -7.44136, -21.350079, 3.6370256, -7.4172235, -21.214657, 3.6378798, -7.4172235, -21.32829, 3.6438606, -7.44136, -21.350079, 3.6370256, -7.44136, -21.20889, 3.6218603, -7.4172235, -21.214657, 3.6378798, -7.4172235, -21.214657, 3.6378798, -7.393087, -21.09077, 3.6466374, -7.393087, -21.20889, 3.6218603, -7.4172235, -21.214657, 3.6378798, -7.4172235, -21.114267, 3.6502686, -7.393087, -21.09077, 3.6466374, -7.393087, -21.20889, 3.6218603, -7.4172235, -21.067276, 3.6432197, -7.4172235, -21.203337, 3.605627, -7.393087, -21.20889, 3.6218603, -7.393087, -21.09077, 3.6466374, -7.4172235, -21.067276, 3.6432197, -7.4172235, -21.203337, 3.605627, -7.44136, -21.09077, 3.6466374, -7.44136, -21.20889, 3.6218603, -7.4172235, -21.203337, 3.605627, -7.4172235, -21.067276, 3.6432197, -7.44136, -21.09077, 3.6466374, -7.44136, -21.20889, 3.6218603, -7.4172235, -21.114267, 3.6502686, -7.4172235, -21.214657, 3.6378798, -7.44136, -21.20889, 3.6218603, -7.44136, -21.09077, 3.6466374, -7.4172235, -21.114267, 3.6502686, -7.4172235, -21.114267, 3.6502686, -7.393087, -21.08842, 4.0392275, -7.393087, -21.09077, 3.6466374, -7.4172235, -21.114267, 3.6502686, -7.4172235, -21.111704, 4.0351696, -7.393087, -21.08842, 4.0392275, -7.393087, -21.09077, 3.6466374, -7.4172235, -21.064926, 4.043072, -7.4172235, -21.067276, 3.6432197, -7.393087, -21.09077, 3.6466374, -7.393087, -21.08842, 4.0392275, -7.4172235, -21.064926, 4.043072, -7.4172235, -21.067276, 3.6432197, -7.44136, -21.08842, 4.0392275, -7.44136, -21.09077, 3.6466374, -7.4172235, -21.067276, 3.6432197, -7.4172235, -21.064926, 4.043072, -7.44136, -21.08842, 4.0392275, -7.44136, -21.09077, 3.6466374, -7.4172235, -21.111704, 4.0351696, -7.4172235, -21.114267, 3.6502686, -7.44136, -21.09077, 3.6466374, -7.44136, -21.08842, 4.0392275, -7.4172235, -21.111704, 4.0351696, -7.4172235, -21.111704, 4.0351696, -7.393087, -21.21957, 4.0757527, -7.393087, -21.08842, 4.0392275, -7.4172235, -21.111704, 4.0351696, -7.4172235, -21.224483, 4.0595193, -7.393087, -21.21957, 4.0757527, -7.393087, -21.08842, 4.0392275, -7.4172235, -21.21487, 4.0919857, -7.4172235, -21.064926, 4.043072, -7.393087, -21.08842, 4.0392275, -7.393087, -21.21957, 4.0757527, -7.4172235, -21.21487, 4.0919857, -7.4172235, -21.064926, 4.043072, -7.44136, -21.21957, 4.0757527, -7.44136, -21.08842, 4.0392275, -7.4172235, -21.064926, 4.043072, -7.4172235, -21.21487, 4.0919857, -7.44136, -21.21957, 4.0757527, -7.44136, -21.08842, 4.0392275, -7.4172235, -21.224483, 4.0595193, -7.4172235, -21.111704, 4.0351696, -7.44136, -21.08842, 4.0392275, -7.44136, -21.21957, 4.0757527, -7.4172235, -21.224483, 4.0595193, -7.4172235, -21.224483, 4.0595193, -7.393087, -21.35563, 4.0424314, -7.393087, -21.21957, 4.0757527, -7.4172235, -21.224483, 4.0595193, -7.4172235, -21.33363, 4.03581, -7.393087, -21.35563, 4.0424314, -7.393087, -21.21957, 4.0757527, -7.4172235, -21.377846, 4.049053, -7.4172235, -21.21487, 4.0919857, -7.393087, -21.21957, 4.0757527, -7.393087, -21.35563, 4.0424314, -7.4172235, -21.377846, 4.049053, -7.4172235, -21.21487, 4.0919857, -7.44136, -21.35563, 4.0424314, -7.44136, -21.21957, 4.0757527, -7.4172235, -21.21487, 4.0919857, -7.4172235, -21.377846, 4.049053, -7.44136, -21.35563, 4.0424314, -7.44136, -21.21957, 4.0757527, -7.4172235, -21.33363, 4.03581, -7.4172235, -21.224483, 4.0595193, -7.44136, -21.21957, 4.0757527, -7.44136, -21.35563, 4.0424314, -7.4172235, -21.33363, 4.03581, -7.4558845, -21.33363, 4.726581, -7.431748, -21.350077, 4.2887087, -7.431748, -21.35563, 4.7338433, -7.4558845, -21.33363, 4.726581, -7.4558845, -21.32829, 4.296612, -7.431748, -21.350077, 4.2887087, -7.431748, -21.35563, 4.7338433, -7.4558845, -21.371864, 4.281233, -7.4558845, -21.377844, 4.7411056, -7.431748, -21.35563, 4.7338433, -7.431748, -21.350077, 4.2887087, -7.4558845, -21.371864, 4.281233, -7.4558845, -21.377844, 4.7411056, -7.480021, -21.350077, 4.2887087, -7.480021, -21.35563, 4.7338433, -7.4558845, -21.377844, 4.7411056, -7.4558845, -21.371864, 4.281233, -7.480021, -21.350077, 4.2887087, -7.480021, -21.35563, 4.7338433, -7.4558845, -21.32829, 4.296612, -7.4558845, -21.33363, 4.726581, -7.480021, -21.35563, 4.7338433, -7.480021, -21.350077, 4.2887087, -7.4558845, -21.32829, 4.296612, -7.4558845, -21.32829, 4.296612, -7.431748, -21.20889, 4.272261, -7.431748, -21.350077, 4.2887087, -7.4558845, -21.32829, 4.296612, -7.4558845, -21.214657, 4.2899895, -7.431748, -21.20889, 4.272261, -7.431748, -21.350077, 4.2887087, -7.4558845, -21.203337, 4.254533, -7.4558845, -21.371864, 4.281233, -7.431748, -21.350077, 4.2887087, -7.431748, -21.20889, 4.272261, -7.4558845, -21.203337, 4.254533, -7.4558845, -21.371864, 4.281233, -7.480021, -21.20889, 4.272261, -7.480021, -21.350077, 4.2887087, -7.4558845, -21.371864, 4.281233, -7.4558845, -21.203337, 4.254533, -7.480021, -21.20889, 4.272261, -7.480021, -21.350077, 4.2887087, -7.4558845, -21.214657, 4.2899895, -7.4558845, -21.32829, 4.296612, -7.480021, -21.350077, 4.2887087, -7.480021, -21.20889, 4.272261, -7.4558845, -21.214657, 4.2899895, -7.4558845, -21.214657, 4.2899895, -7.431748, -21.09077, 4.2996016, -7.431748, -21.20889, 4.272261, -7.4558845, -21.214657, 4.2899895, -7.4558845, -21.114267, 4.3034463, -7.431748, -21.09077, 4.2996016, -7.431748, -21.20889, 4.272261, -7.4558845, -21.067276, 4.295543, -7.4558845, -21.203337, 4.254533, -7.431748, -21.20889, 4.272261, -7.431748, -21.09077, 4.2996016, -7.4558845, -21.067276, 4.295543, -7.4558845, -21.203337, 4.254533, -7.480021, -21.09077, 4.2996016, -7.480021, -21.20889, 4.272261, -7.4558845, -21.203337, 4.254533, -7.4558845, -21.067276, 4.295543, -7.480021, -21.09077, 4.2996016, -7.480021, -21.20889, 4.272261, -7.4558845, -21.114267, 4.3034463, -7.4558845, -21.214657, 4.2899895, -7.480021, -21.20889, 4.272261, -7.480021, -21.09077, 4.2996016, -7.4558845, -21.114267, 4.3034463, -7.4558845, -21.114267, 4.3034463, -7.431748, -21.08842, 4.730212, -7.431748, -21.09077, 4.2996016, -7.4558845, -21.114267, 4.3034463, -7.4558845, -21.111704, 4.7259398, -7.431748, -21.08842, 4.730212, -7.4558845, -21.111704, 4.7259398, -7.431748, -21.21957, 4.770368, -7.431748, -21.08842, 4.730212, -7.4558845, -21.111704, 4.7259398, -7.4558845, -21.224482, 4.75264, -7.431748, -21.21957, 4.770368, -7.431748, -21.09077, 4.2996016, -7.4558845, -21.064926, 4.7346973, -7.4558845, -21.067276, 4.295543, -7.431748, -21.09077, 4.2996016, -7.431748, -21.08842, 4.730212, -7.4558845, -21.064926, 4.7346973, -7.431748, -21.08842, 4.730212, -7.4558845, -21.21487, 4.78831, -7.4558845, -21.064926, 4.7346973, -7.431748, -21.08842, 4.730212, -7.431748, -21.21957, 4.770368, -7.4558845, -21.21487, 4.78831, -7.4558845, -21.067276, 4.295543, -7.480021, -21.08842, 4.730212, -7.480021, -21.09077, 4.2996016, -7.4558845, -21.067276, 4.295543, -7.4558845, -21.064926, 4.7346973, -7.480021, -21.08842, 4.730212, -7.4558845, -21.064926, 4.7346973, -7.480021, -21.21957, 4.770368, -7.480021, -21.08842, 4.730212, -7.4558845, -21.064926, 4.7346973, -7.4558845, -21.21487, 4.78831, -7.480021, -21.21957, 4.770368, -7.480021, -21.09077, 4.2996016, -7.4558845, -21.111704, 4.7259398, -7.4558845, -21.114267, 4.3034463, -7.480021, -21.09077, 4.2996016, -7.480021, -21.08842, 4.730212, -7.4558845, -21.111704, 4.7259398, -7.480021, -21.08842, 4.730212, -7.4558845, -21.224482, 4.75264, -7.4558845, -21.111704, 4.7259398, -7.480021, -21.08842, 4.730212, -7.480021, -21.21957, 4.770368, -7.4558845, -21.224482, 4.75264, -7.4558845, -21.224482, 4.75264, -7.431748, -21.35563, 4.7338433, -7.431748, -21.21957, 4.770368, -7.4558845, -21.224482, 4.75264, -7.4558845, -21.33363, 4.726581, -7.431748, -21.35563, 4.7338433, -7.431748, -21.21957, 4.770368, -7.4558845, -21.377844, 4.7411056, -7.4558845, -21.21487, 4.78831, -7.431748, -21.21957, 4.770368, -7.431748, -21.35563, 4.7338433, -7.4558845, -21.377844, 4.7411056, -7.4558845, -21.21487, 4.78831, -7.480021, -21.35563, 4.7338433, -7.480021, -21.21957, 4.770368, -7.4558845, -21.21487, 4.78831, -7.4558845, -21.377844, 4.7411056, -7.480021, -21.35563, 4.7338433, -7.480021, -21.21957, 4.770368, -7.4558845, -21.33363, 4.726581, -7.4558845, -21.224482, 4.75264, -7.480021, -21.21957, 4.770368, -7.480021, -21.35563, 4.7338433, -7.4558845, -21.33363, 4.726581, -3.7589574, -21.320814, 4.7411056, -3.737598, -21.335339, 4.2274065, -3.737598, -21.340466, 4.7496495, -3.7589574, -21.320814, 4.7411056, -3.7589574, -21.316116, 4.2363772, -3.737598, -21.335339, 4.2274065, -3.7589574, -21.316116, 4.2363772, -3.737598, -21.210384, 4.2079687, -3.737598, -21.335339, 4.2274065, -3.7589574, -21.316116, 4.2363772, -3.7589574, -21.215511, 4.2284737, -3.737598, -21.210384, 4.2079687, -3.737598, -21.340466, 4.7496495, -3.7589574, -21.354776, 4.2184353, -3.7589574, -21.360117, 4.758193, -3.737598, -21.340466, 4.7496495, -3.737598, -21.335339, 4.2274065, -3.7589574, -21.354776, 4.2184353, -3.737598, -21.335339, 4.2274065, -3.7589574, -21.20526, 4.1870365, -3.7589574, -21.354776, 4.2184353, -3.737598, -21.335339, 4.2274065, -3.737598, -21.210384, 4.2079687, -3.7589574, -21.20526, 4.1870365, -3.7589574, -21.360117, 4.758193, -3.7803168, -21.335339, 4.2274065, -3.7803168, -21.340466, 4.7496495, -3.7589574, -21.360117, 4.758193, -3.7589574, -21.354776, 4.2184353, -3.7803168, -21.335339, 4.2274065, -3.7589574, -21.354776, 4.2184353, -3.7803168, -21.210384, 4.2079687, -3.7803168, -21.335339, 4.2274065, -3.7589574, -21.354776, 4.2184353, -3.7589574, -21.20526, 4.1870365, -3.7803168, -21.210384, 4.2079687, -3.7803168, -21.340466, 4.7496495, -3.7589574, -21.316116, 4.2363772, -3.7589574, -21.320814, 4.7411056, -3.7803168, -21.340466, 4.7496495, -3.7803168, -21.335339, 4.2274065, -3.7589574, -21.316116, 4.2363772, -3.7803168, -21.335339, 4.2274065, -3.7589574, -21.215511, 4.2284737, -3.7589574, -21.316116, 4.2363772, -3.7803168, -21.335339, 4.2274065, -3.7803168, -21.210384, 4.2079687, -3.7589574, -21.215511, 4.2284737, -3.7589574, -21.215511, 4.2284737, -3.737598, -21.105509, 4.2400084, -3.737598, -21.210384, 4.2079687, -3.7589574, -21.215511, 4.2284737, -3.7589574, -21.126442, 4.2444935, -3.737598, -21.105509, 4.2400084, -3.7589574, -21.126442, 4.2444935, -3.737598, -21.103586, 4.7453775, -3.737598, -21.105509, 4.2400084, -3.7589574, -21.126442, 4.2444935, -3.7589574, -21.124306, 4.740251, -3.737598, -21.103586, 4.7453775, -3.7589574, -21.124306, 4.740251, -3.737598, -21.219784, 4.7925825, -3.737598, -21.103586, 4.7453775, -3.7589574, -21.124306, 4.740251, -3.7589574, -21.224056, 4.7716494, -3.737598, -21.219784, 4.7925825, -3.737598, -21.210384, 4.2079687, -3.7589574, -21.084791, 4.235309, -3.7589574, -21.20526, 4.1870365, -3.737598, -21.210384, 4.2079687, -3.737598, -21.105509, 4.2400084, -3.7589574, -21.084791, 4.235309, -3.737598, -21.105509, 4.2400084, -3.7589574, -21.082655, 4.7505035, -3.7589574, -21.084791, 4.235309, -3.737598, -21.105509, 4.2400084, -3.737598, -21.103586, 4.7453775, -3.7589574, -21.082655, 4.7505035, -3.737598, -21.103586, 4.7453775, -3.7589574, -21.215511, 4.8135147, -3.7589574, -21.082655, 4.7505035, -3.737598, -21.103586, 4.7453775, -3.737598, -21.219784, 4.7925825, -3.7589574, -21.215511, 4.8135147, -3.7589574, -21.20526, 4.1870365, -3.7803168, -21.105509, 4.2400084, -3.7803168, -21.210384, 4.2079687, -3.7589574, -21.20526, 4.1870365, -3.7589574, -21.084791, 4.235309, -3.7803168, -21.105509, 4.2400084, -3.7803168, -21.210384, 4.2079687, -3.7589574, -21.126442, 4.2444935, -3.7589574, -21.215511, 4.2284737, -3.7803168, -21.210384, 4.2079687, -3.7803168, -21.105509, 4.2400084, -3.7589574, -21.126442, 4.2444935, -3.7803168, -21.105509, 4.2400084, -3.7589574, -21.124306, 4.740251, -3.7589574, -21.126442, 4.2444935, -3.7803168, -21.105509, 4.2400084, -3.7803168, -21.103586, 4.7453775, -3.7589574, -21.124306, 4.740251, -3.7803168, -21.103586, 4.7453775, -3.7589574, -21.224056, 4.7716494, -3.7589574, -21.124306, 4.740251, -3.7803168, -21.103586, 4.7453775, -3.7803168, -21.219784, 4.7925825, -3.7589574, -21.224056, 4.7716494, -3.7589574, -21.084791, 4.235309, -3.7803168, -21.103586, 4.7453775, -3.7803168, -21.105509, 4.2400084, -3.7589574, -21.084791, 4.235309, -3.7589574, -21.082655, 4.7505035, -3.7803168, -21.103586, 4.7453775, -3.7589574, -21.082655, 4.7505035, -3.7803168, -21.219784, 4.7925825, -3.7803168, -21.103586, 4.7453775, -3.7589574, -21.082655, 4.7505035, -3.7589574, -21.215511, 4.8135147, -3.7803168, -21.219784, 4.7925825, -3.7589574, -21.224056, 4.7716494, -3.737598, -21.340466, 4.7496495, -3.737598, -21.219784, 4.7925825, -3.7589574, -21.224056, 4.7716494, -3.7589574, -21.320814, 4.7411056, -3.737598, -21.340466, 4.7496495, -3.737598, -21.219784, 4.7925825, -3.7589574, -21.360117, 4.758193, -3.7589574, -21.215511, 4.8135147, -3.737598, -21.219784, 4.7925825, -3.737598, -21.340466, 4.7496495, -3.7589574, -21.360117, 4.758193, -3.7589574, -21.215511, 4.8135147, -3.7803168, -21.340466, 4.7496495, -3.7803168, -21.219784, 4.7925825, -3.7589574, -21.215511, 4.8135147, -3.7589574, -21.360117, 4.758193, -3.7803168, -21.340466, 4.7496495, -3.7803168, -21.219784, 4.7925825, -3.7589574, -21.320814, 4.7411056, -3.7589574, -21.224056, 4.7716494, -3.7803168, -21.219784, 4.7925825, -3.7803168, -21.340466, 4.7496495, -3.7589574, -21.320814, 4.7411056, -3.7589574, -21.320814, 5.361176, -3.737598, -21.335339, 4.8476906, -3.737598, -21.340466, 5.36972, -3.7589574, -21.320814, 5.361176, -3.7589574, -21.316114, 4.8566613, -3.737598, -21.335339, 4.8476906, -3.7589574, -21.316114, 4.8566613, -3.737598, -21.210384, 4.828039, -3.737598, -21.335339, 4.8476906, -3.7589574, -21.316114, 4.8566613, -3.7589574, -21.215511, 4.8487577, -3.737598, -21.210384, 4.828039, -3.737598, -21.340466, 5.36972, -3.7589574, -21.354776, 4.8387194, -3.7589574, -21.360115, 5.3782635, -3.737598, -21.340466, 5.36972, -3.737598, -21.335339, 4.8476906, -3.7589574, -21.354776, 4.8387194, -3.737598, -21.335339, 4.8476906, -3.7589574, -21.20526, 4.8073206, -3.7589574, -21.354776, 4.8387194, -3.737598, -21.335339, 4.8476906, -3.737598, -21.210384, 4.828039, -3.7589574, -21.20526, 4.8073206, -3.7589574, -21.360115, 5.3782635, -3.7803168, -21.335339, 4.8476906, -3.7803168, -21.340466, 5.36972, -3.7589574, -21.360115, 5.3782635, -3.7589574, -21.354776, 4.8387194, -3.7803168, -21.335339, 4.8476906, -3.7589574, -21.354776, 4.8387194, -3.7803168, -21.210384, 4.828039, -3.7803168, -21.335339, 4.8476906, -3.7589574, -21.354776, 4.8387194, -3.7589574, -21.20526, 4.8073206, -3.7803168, -21.210384, 4.828039, -3.7803168, -21.340466, 5.36972, -3.7589574, -21.316114, 4.8566613, -3.7589574, -21.320814, 5.361176, -3.7803168, -21.340466, 5.36972, -3.7803168, -21.335339, 4.8476906, -3.7589574, -21.316114, 4.8566613, -3.7803168, -21.335339, 4.8476906, -3.7589574, -21.215511, 4.8487577, -3.7589574, -21.316114, 4.8566613, -3.7803168, -21.335339, 4.8476906, -3.7803168, -21.210384, 4.828039, -3.7589574, -21.215511, 4.8487577, -3.7589574, -21.215511, 4.8487577, -3.737598, -21.105509, 4.8602924, -3.737598, -21.210384, 4.828039, -3.7589574, -21.215511, 4.8487577, -3.7589574, -21.126442, 4.8647776, -3.737598, -21.105509, 4.8602924, -3.7589574, -21.126442, 4.8647776, -3.737598, -21.103586, 5.3656616, -3.737598, -21.105509, 4.8602924, -3.7589574, -21.126442, 4.8647776, -3.7589574, -21.124306, 5.360535, -3.737598, -21.103586, 5.3656616, -3.7589574, -21.124306, 5.360535, -3.737598, -21.219784, 5.412653, -3.737598, -21.103586, 5.3656616, -3.7589574, -21.124306, 5.360535, -3.7589574, -21.224054, 5.39172, -3.737598, -21.219784, 5.412653, -3.737598, -21.210384, 4.828039, -3.7589574, -21.084791, 4.855593, -3.7589574, -21.20526, 4.8073206, -3.737598, -21.210384, 4.828039, -3.737598, -21.105509, 4.8602924, -3.7589574, -21.084791, 4.855593, -3.737598, -21.105509, 4.8602924, -3.7589574, -21.082655, 5.370574, -3.7589574, -21.084791, 4.855593, -3.737598, -21.105509, 4.8602924, -3.737598, -21.103586, 5.3656616, -3.7589574, -21.082655, 5.370574, -3.737598, -21.103586, 5.3656616, -3.7589574, -21.215511, 5.433585, -3.7589574, -21.082655, 5.370574, -3.737598, -21.103586, 5.3656616, -3.737598, -21.219784, 5.412653, -3.7589574, -21.215511, 5.433585, -3.7589574, -21.20526, 4.8073206, -3.7803168, -21.105509, 4.8602924, -3.7803168, -21.210384, 4.828039, -3.7589574, -21.20526, 4.8073206, -3.7589574, -21.084791, 4.855593, -3.7803168, -21.105509, 4.8602924, -3.7803168, -21.210384, 4.828039, -3.7589574, -21.126442, 4.8647776, -3.7589574, -21.215511, 4.8487577, -3.7803168, -21.210384, 4.828039, -3.7803168, -21.105509, 4.8602924, -3.7589574, -21.126442, 4.8647776, -3.7803168, -21.105509, 4.8602924, -3.7589574, -21.124306, 5.360535, -3.7589574, -21.126442, 4.8647776, -3.7803168, -21.105509, 4.8602924, -3.7803168, -21.103586, 5.3656616, -3.7589574, -21.124306, 5.360535, -3.7803168, -21.103586, 5.3656616, -3.7589574, -21.224054, 5.39172, -3.7589574, -21.124306, 5.360535, -3.7803168, -21.103586, 5.3656616, -3.7803168, -21.219784, 5.412653, -3.7589574, -21.224054, 5.39172, -3.7589574, -21.084791, 4.855593, -3.7803168, -21.103586, 5.3656616, -3.7803168, -21.105509, 4.8602924, -3.7589574, -21.084791, 4.855593, -3.7589574, -21.082655, 5.370574, -3.7803168, -21.103586, 5.3656616, -3.7589574, -21.082655, 5.370574, -3.7803168, -21.219784, 5.412653, -3.7803168, -21.103586, 5.3656616, -3.7589574, -21.082655, 5.370574, -3.7589574, -21.215511, 5.433585, -3.7803168, -21.219784, 5.412653, -3.7589574, -21.224054, 5.39172, -3.737598, -21.340466, 5.36972, -3.737598, -21.219784, 5.412653, -3.7589574, -21.224054, 5.39172, -3.7589574, -21.320814, 5.361176, -3.737598, -21.340466, 5.36972, -3.737598, -21.219784, 5.412653, -3.7589574, -21.360115, 5.3782635, -3.7589574, -21.215511, 5.433585, -3.737598, -21.219784, 5.412653, -3.737598, -21.340466, 5.36972, -3.7589574, -21.360115, 5.3782635, -3.7589574, -21.215511, 5.433585, -3.7803168, -21.340466, 5.36972, -3.7803168, -21.219784, 5.412653, -3.7589574, -21.215511, 5.433585, -3.7589574, -21.360115, 5.3782635, -3.7803168, -21.340466, 5.36972, -3.7803168, -21.219784, 5.412653, -3.7589574, -21.320814, 5.361176, -3.7589574, -21.224054, 5.39172, -3.7803168, -21.219784, 5.412653, -3.7803168, -21.340466, 5.36972, -3.7589574, -21.320814, 5.361176, -7.381767, -21.33363, 5.4090214, -7.3576303, -21.350077, 4.864351, -7.3576303, -21.35563, 5.418206, -7.381767, -21.33363, 5.4090214, -7.381767, -21.32829, 4.8739624, -7.3576303, -21.350077, 4.864351, -7.381767, -21.32829, 4.8739624, -7.3576303, -21.208889, 4.8436317, -7.3576303, -21.350077, 4.864351, -7.381767, -21.32829, 4.8739624, -7.381767, -21.214657, 4.8654184, -7.3576303, -21.208889, 4.8436317, -7.3576303, -21.35563, 5.418206, -7.381767, -21.371864, 4.854739, -7.381767, -21.377844, 5.4271774, -7.3576303, -21.35563, 5.418206, -7.3576303, -21.350077, 4.864351, -7.381767, -21.371864, 4.854739, -7.3576303, -21.350077, 4.864351, -7.381767, -21.203337, 4.8216314, -7.381767, -21.371864, 4.854739, -7.3576303, -21.350077, 4.864351, -7.3576303, -21.208889, 4.8436317, -7.381767, -21.203337, 4.8216314, -7.381767, -21.377844, 5.4271774, -7.4059024, -21.350077, 4.864351, -7.4059024, -21.35563, 5.418206, -7.381767, -21.377844, 5.4271774, -7.381767, -21.371864, 4.854739, -7.4059024, -21.350077, 4.864351, -7.381767, -21.371864, 4.854739, -7.4059024, -21.208889, 4.8436317, -7.4059024, -21.350077, 4.864351, -7.381767, -21.371864, 4.854739, -7.381767, -21.203337, 4.8216314, -7.4059024, -21.208889, 4.8436317, -7.4059024, -21.35563, 5.418206, -7.381767, -21.32829, 4.8739624, -7.381767, -21.33363, 5.4090214, -7.4059024, -21.35563, 5.418206, -7.4059024, -21.350077, 4.864351, -7.381767, -21.32829, 4.8739624, -7.4059024, -21.350077, 4.864351, -7.381767, -21.214657, 4.8654184, -7.381767, -21.32829, 4.8739624, -7.4059024, -21.350077, 4.864351, -7.4059024, -21.208889, 4.8436317, -7.381767, -21.214657, 4.8654184, -7.381767, -21.214657, 4.8654184, -7.3576303, -21.09077, 4.8775935, -7.3576303, -21.208889, 4.8436317, -7.381767, -21.214657, 4.8654184, -7.381767, -21.114267, 4.8825064, -7.3576303, -21.09077, 4.8775935, -7.381767, -21.114267, 4.8825064, -7.3576303, -21.08842, 5.4137206, -7.3576303, -21.09077, 4.8775935, -7.381767, -21.114267, 4.8825064, -7.381767, -21.111704, 5.4083805, -7.3576303, -21.08842, 5.4137206, -7.381767, -21.111704, 5.4083805, -7.3576303, -21.21957, 5.463702, -7.3576303, -21.08842, 5.4137206, -7.381767, -21.111704, 5.4083805, -7.381767, -21.224482, 5.4414883, -7.3576303, -21.21957, 5.463702, -7.3576303, -21.208889, 4.8436317, -7.381767, -21.067276, 4.8726807, -7.381767, -21.203337, 4.8216314, -7.3576303, -21.208889, 4.8436317, -7.3576303, -21.09077, 4.8775935, -7.381767, -21.067276, 4.8726807, -7.3576303, -21.09077, 4.8775935, -7.381767, -21.064926, 5.4190607, -7.381767, -21.067276, 4.8726807, -7.3576303, -21.09077, 4.8775935, -7.3576303, -21.08842, 5.4137206, -7.381767, -21.064926, 5.4190607, -7.3576303, -21.08842, 5.4137206, -7.381767, -21.21487, 5.485916, -7.381767, -21.064926, 5.4190607, -7.3576303, -21.08842, 5.4137206, -7.3576303, -21.21957, 5.463702, -7.381767, -21.21487, 5.485916, -7.381767, -21.203337, 4.8216314, -7.4059024, -21.09077, 4.8775935, -7.4059024, -21.208889, 4.8436317, -7.381767, -21.203337, 4.8216314, -7.381767, -21.067276, 4.8726807, -7.4059024, -21.09077, 4.8775935, -7.4059024, -21.208889, 4.8436317, -7.381767, -21.114267, 4.8825064, -7.381767, -21.214657, 4.8654184, -7.4059024, -21.208889, 4.8436317, -7.4059024, -21.09077, 4.8775935, -7.381767, -21.114267, 4.8825064, -7.4059024, -21.09077, 4.8775935, -7.381767, -21.111704, 5.4083805, -7.381767, -21.114267, 4.8825064, -7.4059024, -21.09077, 4.8775935, -7.4059024, -21.08842, 5.4137206, -7.381767, -21.111704, 5.4083805, -7.4059024, -21.08842, 5.4137206, -7.381767, -21.224482, 5.4414883, -7.381767, -21.111704, 5.4083805, -7.4059024, -21.08842, 5.4137206, -7.4059024, -21.21957, 5.463702, -7.381767, -21.224482, 5.4414883, -7.381767, -21.067276, 4.8726807, -7.4059024, -21.08842, 5.4137206, -7.4059024, -21.09077, 4.8775935, -7.381767, -21.067276, 4.8726807, -7.381767, -21.064926, 5.4190607, -7.4059024, -21.08842, 5.4137206, -7.381767, -21.064926, 5.4190607, -7.4059024, -21.21957, 5.463702, -7.4059024, -21.08842, 5.4137206, -7.381767, -21.064926, 5.4190607, -7.381767, -21.21487, 5.485916, -7.4059024, -21.21957, 5.463702, -7.381767, -21.224482, 5.4414883, -7.3576303, -21.35563, 5.418206, -7.3576303, -21.21957, 5.463702, -7.381767, -21.224482, 5.4414883, -7.381767, -21.33363, 5.4090214, -7.3576303, -21.35563, 5.418206, -7.3576303, -21.21957, 5.463702, -7.381767, -21.377844, 5.4271774, -7.381767, -21.21487, 5.485916, -7.3576303, -21.21957, 5.463702, -7.3576303, -21.35563, 5.418206, -7.381767, -21.377844, 5.4271774, -7.381767, -21.21487, 5.485916, -7.4059024, -21.35563, 5.418206, -7.4059024, -21.21957, 5.463702, -7.381767, -21.21487, 5.485916, -7.381767, -21.377844, 5.4271774, -7.4059024, -21.35563, 5.418206, -7.4059024, -21.21957, 5.463702, -7.381767, -21.33363, 5.4090214, -7.381767, -21.224482, 5.4414883, -7.4059024, -21.21957, 5.463702, -7.4059024, -21.35563, 5.418206, -7.381767, -21.33363, 5.4090214, -12.414185, -22.456993, 9.651152, -10.526909, -22.242653, 9.475411, -12.414185, -22.326994, 9.5211525, -12.414185, -22.456993, 9.651152, -10.526909, -22.391993, 9.624752, -10.526909, -22.242653, 9.475411, -19.07084, -22.391994, 9.624752, -12.414185, -22.456993, 9.651152, -12.414185, -22.326994, 9.5211525, -19.07084, -22.391994, 9.624752, -12.414185, -22.326994, 9.5211525, -19.07084, -22.242655, 9.475411, -12.414185, -22.326994, 9.5211525, -10.526909, -22.038635, 9.420734, -12.414185, -22.149376, 9.473576, -12.414185, -22.326994, 9.5211525, -10.526909, -22.242653, 9.475411, -10.526909, -22.038635, 9.420734, -19.07084, -22.242655, 9.475411, -12.414185, -22.326994, 9.5211525, -12.414185, -22.149376, 9.473576, -19.07084, -22.242655, 9.475411, -12.414185, -22.149376, 9.473576, -19.07084, -22.038637, 9.420734, -12.414185, -22.149376, 9.473576, -10.526909, -21.83462, 9.475411, -12.414185, -21.9718, 9.5211525, -12.414185, -22.149376, 9.473576, -10.526909, -22.038635, 9.420734, -10.526909, -21.83462, 9.475411, -19.07084, -22.038637, 9.420734, -12.414185, -22.149376, 9.473576, -12.414185, -21.9718, 9.5211525, -19.07084, -22.038637, 9.420734, -12.414185, -21.9718, 9.5211525, -19.07084, -21.834621, 9.475411, -12.414185, -21.9718, 9.5211525, -10.526909, -21.68528, 9.624752, -12.414185, -21.8418, 9.651152, -12.414185, -21.9718, 9.5211525, -10.526909, -21.83462, 9.475411, -10.526909, -21.68528, 9.624752, -19.07084, -21.834621, 9.475411, -12.414185, -21.9718, 9.5211525, -12.414185, -21.8418, 9.651152, -19.07084, -21.834621, 9.475411, -12.414185, -21.8418, 9.651152, -19.07084, -21.68528, 9.624752, -12.414185, -21.8418, 9.651152, -10.526909, -21.630602, 9.828768, -12.414185, -21.794184, 9.828768, -12.414185, -21.8418, 9.651152, -10.526909, -21.68528, 9.624752, -10.526909, -21.630602, 9.828768, -19.07084, -21.68528, 9.624752, -12.414185, -21.8418, 9.651152, -12.414185, -21.794184, 9.828768, -19.07084, -21.68528, 9.624752, -12.414185, -21.794184, 9.828768, -19.07084, -21.630604, 9.828768, -12.414185, -21.794184, 9.828768, -10.526909, -21.68528, 10.032785, -12.414185, -21.8418, 10.006344, -12.414185, -21.794184, 9.828768, -10.526909, -21.630602, 9.828768, -10.526909, -21.68528, 10.032785, -19.07084, -21.630604, 9.828768, -12.414185, -21.794184, 9.828768, -12.414185, -21.8418, 10.006344, -19.07084, -21.630604, 9.828768, -12.414185, -21.8418, 10.006344, -19.07084, -21.68528, 10.032785, -12.414185, -21.8418, 10.006344, -10.526909, -21.83462, 10.182125, -12.414185, -21.9718, 10.136343, -12.414185, -21.8418, 10.006344, -10.526909, -21.68528, 10.032785, -10.526909, -21.83462, 10.182125, -19.07084, -21.68528, 10.032785, -12.414185, -21.8418, 10.006344, -12.414185, -21.9718, 10.136343, -19.07084, -21.68528, 10.032785, -12.414185, -21.9718, 10.136343, -19.07084, -21.834621, 10.182125, -12.414185, -21.9718, 10.136343, -10.526909, -22.038635, 10.236801, -12.414185, -22.149376, 10.183962, -12.414185, -21.9718, 10.136343, -10.526909, -21.83462, 10.182125, -10.526909, -22.038635, 10.236801, -19.07084, -21.834621, 10.182125, -12.414185, -21.9718, 10.136343, -12.414185, -22.149376, 10.183962, -19.07084, -21.834621, 10.182125, -12.414185, -22.149376, 10.183962, -19.07084, -22.038637, 10.236801, -12.414185, -22.149376, 10.183962, -10.526909, -22.242653, 10.182125, -12.414185, -22.326994, 10.136343, -12.414185, -22.149376, 10.183962, -10.526909, -22.038635, 10.236801, -10.526909, -22.242653, 10.182125, -19.07084, -22.038637, 10.236801, -12.414185, -22.149376, 10.183962, -12.414185, -22.326994, 10.136343, -19.07084, -22.038637, 10.236801, -12.414185, -22.326994, 10.136343, -19.07084, -22.242655, 10.182125, -12.414185, -22.326994, 10.136343, -10.526909, -22.391993, 10.032785, -12.414185, -22.456993, 10.006344, -12.414185, -22.326994, 10.136343, -10.526909, -22.242653, 10.182125, -10.526909, -22.391993, 10.032785, -19.07084, -22.242655, 10.182125, -12.414185, -22.326994, 10.136343, -12.414185, -22.456993, 10.006344, -19.07084, -22.242655, 10.182125, -12.414185, -22.456993, 10.006344, -19.07084, -22.391994, 10.032785, -12.414185, -22.456993, 10.006344, -10.526909, -22.446669, 9.828768, -12.414185, -22.50457, 9.828768, -12.414185, -22.456993, 10.006344, -10.526909, -22.391993, 10.032785, -10.526909, -22.446669, 9.828768, -19.07084, -22.391994, 10.032785, -12.414185, -22.456993, 10.006344, -12.414185, -22.50457, 9.828768, -19.07084, -22.391994, 10.032785, -12.414185, -22.50457, 9.828768, -19.07084, -22.44667, 9.828768, -12.414185, -22.50457, 9.828768, -10.526909, -22.391993, 9.624752, -12.414185, -22.456993, 9.651152, -12.414185, -22.50457, 9.828768, -10.526909, -22.446669, 9.828768, -10.526909, -22.391993, 9.624752, -19.07084, -22.44667, 9.828768, -12.414185, -22.50457, 9.828768, -12.414185, -22.456993, 9.651152, -19.07084, -22.44667, 9.828768, -12.414185, -22.456993, 9.651152, -19.07084, -22.391994, 9.624752, -19.07084, -22.242655, 9.475411, -19.693947, -22.038637, 9.828768, -19.07084, -22.391994, 9.624752, -19.07084, -22.391994, 9.624752, -19.693947, -22.038637, 9.828768, -19.07084, -22.44667, 9.828768, -19.07084, -22.44667, 9.828768, -19.693947, -22.038637, 9.828768, -19.07084, -22.391994, 10.032785, -19.07084, -22.391994, 10.032785, -19.693947, -22.038637, 9.828768, -19.07084, -22.242655, 10.182125, -19.07084, -22.038637, 9.420734, -19.693947, -22.038637, 9.828768, -19.07084, -22.242655, 9.475411, -19.07084, -21.834621, 9.475411, -19.693947, -22.038637, 9.828768, -19.07084, -22.038637, 9.420734, -19.07084, -21.68528, 9.624752, -19.693947, -22.038637, 9.828768, -19.07084, -21.834621, 9.475411, -19.07084, -21.630604, 9.828768, -19.693947, -22.038637, 9.828768, -19.07084, -21.68528, 9.624752, -19.07084, -21.68528, 10.032785, -19.693947, -22.038637, 9.828768, -19.07084, -21.630604, 9.828768, -19.07084, -21.834621, 10.182125, -19.693947, -22.038637, 9.828768, -19.07084, -21.68528, 10.032785, -19.07084, -22.038637, 10.236801, -19.693947, -22.038637, 9.828768, -19.07084, -21.834621, 10.182125, -19.07084, -22.242655, 10.182125, -19.693947, -22.038637, 9.828768, -19.07084, -22.038637, 10.236801, -10.526909, -22.391993, 9.624752, -9.89662, -22.038635, 9.828768, -10.526909, -22.242653, 9.475411, -10.526909, -22.446669, 9.828768, -9.89662, -22.038635, 9.828768, -10.526909, -22.391993, 9.624752, -10.526909, -22.391993, 10.032785, -9.89662, -22.038635, 9.828768, -10.526909, -22.446669, 9.828768, -10.526909, -22.242653, 10.182125, -9.89662, -22.038635, 9.828768, -10.526909, -22.391993, 10.032785, -10.526909, -22.242653, 9.475411, -9.89662, -22.038635, 9.828768, -10.526909, -22.038635, 9.420734, -10.526909, -22.038635, 9.420734, -9.89662, -22.038635, 9.828768, -10.526909, -21.83462, 9.475411, -10.526909, -21.83462, 9.475411, -9.89662, -22.038635, 9.828768, -10.526909, -21.68528, 9.624752, -10.526909, -21.68528, 9.624752, -9.89662, -22.038635, 9.828768, -10.526909, -21.630602, 9.828768, -10.526909, -21.630602, 9.828768, -9.89662, -22.038635, 9.828768, -10.526909, -21.68528, 10.032785, -10.526909, -21.68528, 10.032785, -9.89662, -22.038635, 9.828768, -10.526909, -21.83462, 10.182125, -10.526909, -21.83462, 10.182125, -9.89662, -22.038635, 9.828768, -10.526909, -22.038635, 10.236801, -10.526909, -22.038635, 10.236801, -9.89662, -22.038635, 9.828768, -10.526909, -22.242653, 10.182125, -14.262087, -22.34866, 7.7697783, -10.526909, -22.242653, 7.634801, -14.262087, -22.188833, 7.609911, -14.262087, -22.34866, 7.7697783, -10.526909, -22.391993, 7.784141, -10.526909, -22.242653, 7.634801, -19.07084, -22.391994, 7.784141, -14.262087, -22.34866, 7.7697783, -14.262087, -22.188833, 7.609911, -19.07084, -22.391994, 7.784141, -14.262087, -22.188833, 7.609911, -19.07084, -22.242655, 7.634801, -14.262087, -22.188833, 7.609911, -10.526909, -22.038635, 7.5801244, -14.262087, -21.970455, 7.5513988, -14.262087, -22.188833, 7.609911, -10.526909, -22.242653, 7.634801, -10.526909, -22.038635, 7.5801244, -19.07084, -22.242655, 7.634801, -14.262087, -22.188833, 7.609911, -14.262087, -21.970455, 7.5513988, -19.07084, -22.242655, 7.634801, -14.262087, -21.970455, 7.5513988, -19.07084, -22.038637, 7.5801244, -14.262087, -21.970455, 7.5513988, -10.526909, -21.83462, 7.634801, -14.262087, -21.752075, 7.609911, -14.262087, -21.970455, 7.5513988, -10.526909, -22.038635, 7.5801244, -10.526909, -21.83462, 7.634801, -19.07084, -22.038637, 7.5801244, -14.262087, -21.970455, 7.5513988, -14.262087, -21.752075, 7.609911, -19.07084, -22.038637, 7.5801244, -14.262087, -21.752075, 7.609911, -19.07084, -21.834621, 7.634801, -14.262087, -21.752075, 7.609911, -10.526909, -21.68528, 7.784141, -14.262087, -21.592207, 7.7697783, -14.262087, -21.752075, 7.609911, -10.526909, -21.83462, 7.634801, -10.526909, -21.68528, 7.784141, -19.07084, -21.834621, 7.634801, -14.262087, -21.752075, 7.609911, -14.262087, -21.592207, 7.7697783, -19.07084, -21.834621, 7.634801, -14.262087, -21.592207, 7.7697783, -19.07084, -21.68528, 7.784141, -14.262087, -21.592207, 7.7697783, -10.526909, -21.630602, 7.9881577, -14.262087, -21.533695, 7.9881577, -14.262087, -21.592207, 7.7697783, -10.526909, -21.68528, 7.784141, -10.526909, -21.630602, 7.9881577, -19.07084, -21.68528, 7.784141, -14.262087, -21.592207, 7.7697783, -14.262087, -21.533695, 7.9881577, -19.07084, -21.68528, 7.784141, -14.262087, -21.533695, 7.9881577, -19.07084, -21.630604, 7.9881577, -14.262087, -21.533695, 7.9881577, -10.526909, -21.68528, 8.192175, -14.262087, -21.592207, 8.206537, -14.262087, -21.533695, 7.9881577, -10.526909, -21.630602, 7.9881577, -10.526909, -21.68528, 8.192175, -19.07084, -21.630604, 7.9881577, -14.262087, -21.533695, 7.9881577, -14.262087, -21.592207, 8.206537, -19.07084, -21.630604, 7.9881577, -14.262087, -21.592207, 8.206537, -19.07084, -21.68528, 8.192175, -14.262087, -21.592207, 8.206537, -10.526909, -21.83462, 8.341515, -14.262087, -21.752075, 8.366364, -14.262087, -21.592207, 8.206537, -10.526909, -21.68528, 8.192175, -10.526909, -21.83462, 8.341515, -19.07084, -21.68528, 8.192175, -14.262087, -21.592207, 8.206537, -14.262087, -21.752075, 8.366364, -19.07084, -21.68528, 8.192175, -14.262087, -21.752075, 8.366364, -19.07084, -21.834621, 8.341515, -14.262087, -21.752075, 8.366364, -10.526909, -22.038635, 8.396191, -14.262087, -21.970453, 8.424917, -14.262087, -21.752075, 8.366364, -10.526909, -21.83462, 8.341515, -10.526909, -22.038635, 8.396191, -19.07084, -21.834621, 8.341515, -14.262087, -21.752075, 8.366364, -14.262087, -21.970453, 8.424917, -19.07084, -21.834621, 8.341515, -14.262087, -21.970453, 8.424917, -19.07084, -22.038637, 8.396191, -14.262087, -21.970453, 8.424917, -10.526909, -22.242653, 8.341515, -14.262087, -22.188833, 8.366364, -14.262087, -21.970453, 8.424917, -10.526909, -22.038635, 8.396191, -10.526909, -22.242653, 8.341515, -19.07084, -22.038637, 8.396191, -14.262087, -21.970453, 8.424917, -14.262087, -22.188833, 8.366364, -19.07084, -22.038637, 8.396191, -14.262087, -22.188833, 8.366364, -19.07084, -22.242655, 8.341515, -14.262087, -22.188833, 8.366364, -10.526909, -22.391993, 8.192175, -14.262087, -22.34866, 8.206537, -14.262087, -22.188833, 8.366364, -10.526909, -22.242653, 8.341515, -10.526909, -22.391993, 8.192175, -19.07084, -22.242655, 8.341515, -14.262087, -22.188833, 8.366364, -14.262087, -22.34866, 8.206537, -19.07084, -22.242655, 8.341515, -14.262087, -22.34866, 8.206537, -19.07084, -22.391994, 8.192175, -14.262087, -22.34866, 8.206537, -10.526909, -22.446669, 7.9881577, -14.262087, -22.407173, 7.9881577, -14.262087, -22.34866, 8.206537, -10.526909, -22.391993, 8.192175, -10.526909, -22.446669, 7.9881577, -19.07084, -22.391994, 8.192175, -14.262087, -22.34866, 8.206537, -14.262087, -22.407173, 7.9881577, -19.07084, -22.391994, 8.192175, -14.262087, -22.407173, 7.9881577, -19.07084, -22.44667, 7.9881577, -14.262087, -22.407173, 7.9881577, -10.526909, -22.391993, 7.784141, -14.262087, -22.34866, 7.7697783, -14.262087, -22.407173, 7.9881577, -10.526909, -22.446669, 7.9881577, -10.526909, -22.391993, 7.784141, -19.07084, -22.44667, 7.9881577, -14.262087, -22.407173, 7.9881577, -14.262087, -22.34866, 7.7697783, -19.07084, -22.44667, 7.9881577, -14.262087, -22.34866, 7.7697783, -19.07084, -22.391994, 7.784141, -19.07084, -22.242655, 7.634801, -19.693947, -22.038637, 7.9881577, -19.07084, -22.391994, 7.784141, -19.07084, -22.391994, 7.784141, -19.693947, -22.038637, 7.9881577, -19.07084, -22.44667, 7.9881577, -19.07084, -22.44667, 7.9881577, -19.693947, -22.038637, 7.9881577, -19.07084, -22.391994, 8.192175, -19.07084, -22.391994, 8.192175, -19.693947, -22.038637, 7.9881577, -19.07084, -22.242655, 8.341515, -19.07084, -22.038637, 7.5801244, -19.693947, -22.038637, 7.9881577, -19.07084, -22.242655, 7.634801, -19.07084, -21.834621, 7.634801, -19.693947, -22.038637, 7.9881577, -19.07084, -22.038637, 7.5801244, -19.07084, -21.68528, 7.784141, -19.693947, -22.038637, 7.9881577, -19.07084, -21.834621, 7.634801, -19.07084, -21.630604, 7.9881577, -19.693947, -22.038637, 7.9881577, -19.07084, -21.68528, 7.784141, -19.07084, -21.68528, 8.192175, -19.693947, -22.038637, 7.9881577, -19.07084, -21.630604, 7.9881577, -19.07084, -21.834621, 8.341515, -19.693947, -22.038637, 7.9881577, -19.07084, -21.68528, 8.192175, -19.07084, -22.038637, 8.396191, -19.693947, -22.038637, 7.9881577, -19.07084, -21.834621, 8.341515, -19.07084, -22.242655, 8.341515, -19.693947, -22.038637, 7.9881577, -19.07084, -22.038637, 8.396191, -10.526909, -22.391993, 7.784141, -9.89662, -22.038635, 7.9881577, -10.526909, -22.242653, 7.634801, -10.526909, -22.446669, 7.9881577, -9.89662, -22.038635, 7.9881577, -10.526909, -22.391993, 7.784141, -10.526909, -22.391993, 8.192175, -9.89662, -22.038635, 7.9881577, -10.526909, -22.446669, 7.9881577, -10.526909, -22.242653, 8.341515, -9.89662, -22.038635, 7.9881577, -10.526909, -22.391993, 8.192175, -10.526909, -22.242653, 7.634801, -9.89662, -22.038635, 7.9881577, -10.526909, -22.038635, 7.5801244, -10.526909, -22.038635, 7.5801244, -9.89662, -22.038635, 7.9881577, -10.526909, -21.83462, 7.634801, -10.526909, -21.83462, 7.634801, -9.89662, -22.038635, 7.9881577, -10.526909, -21.68528, 7.784141, -10.526909, -21.68528, 7.784141, -9.89662, -22.038635, 7.9881577, -10.526909, -21.630602, 7.9881577, -10.526909, -21.630602, 7.9881577, -9.89662, -22.038635, 7.9881577, -10.526909, -21.68528, 8.192175, -10.526909, -21.68528, 8.192175, -9.89662, -22.038635, 7.9881577, -10.526909, -21.83462, 8.341515, -10.526909, -21.83462, 8.341515, -9.89662, -22.038635, 7.9881577, -10.526909, -22.038635, 8.396191, -10.526909, -22.038635, 8.396191, -9.89662, -22.038635, 7.9881577, -10.526909, -22.242653, 8.341515, -12.004164, -22.046722, 10.088959, -12.124954, -19.902674, 9.968166, -12.124954, -22.046722, 9.968168, -12.004164, -22.046722, 10.088959, -12.004164, -19.902674, 10.088957, -12.124954, -19.902674, 9.968166, -12.124954, -22.046722, 9.968168, -12.2899685, -19.902674, 9.923943, -12.2899685, -22.046722, 9.923944, -12.124954, -22.046722, 9.968168, -12.124954, -19.902674, 9.968166, -12.2899685, -19.902674, 9.923943, -12.2899685, -22.046722, 9.923944, -12.454983, -19.902674, 9.968166, -12.454983, -22.046722, 9.968168, -12.2899685, -22.046722, 9.923944, -12.2899685, -19.902674, 9.923943, -12.454983, -19.902674, 9.968166, -12.454983, -22.046722, 9.968168, -12.575771, -19.902674, 10.088957, -12.575771, -22.046722, 10.088959, -12.454983, -22.046722, 9.968168, -12.454983, -19.902674, 9.968166, -12.575771, -19.902674, 10.088957, -12.575771, -22.046722, 10.088959, -12.619995, -19.902674, 10.253971, -12.619995, -22.046722, 10.253971, -12.575771, -22.046722, 10.088959, -12.575771, -19.902674, 10.088957, -12.619995, -19.902674, 10.253971, -12.619995, -22.046722, 10.253971, -12.575771, -19.902674, 10.418985, -12.575771, -22.046722, 10.418985, -12.619995, -22.046722, 10.253971, -12.619995, -19.902674, 10.253971, -12.575771, -19.902674, 10.418985, -12.575771, -22.046722, 10.418985, -12.454983, -19.902674, 10.539776, -12.454983, -22.046722, 10.539776, -12.575771, -22.046722, 10.418985, -12.575771, -19.902674, 10.418985, -12.454983, -19.902674, 10.539776, -12.454983, -22.046722, 10.539776, -12.2899685, -19.902674, 10.584, -12.2899685, -22.046722, 10.584, -12.454983, -22.046722, 10.539776, -12.454983, -19.902674, 10.539776, -12.2899685, -19.902674, 10.584, -12.2899685, -22.046722, 10.584, -12.124954, -19.902674, 10.539776, -12.124954, -22.046722, 10.539776, -12.2899685, -22.046722, 10.584, -12.2899685, -19.902674, 10.584, -12.124954, -19.902674, 10.539776, -12.124954, -22.046722, 10.539776, -12.004164, -19.902674, 10.418985, -12.004164, -22.046722, 10.418985, -12.124954, -22.046722, 10.539776, -12.124954, -19.902674, 10.539776, -12.004164, -19.902674, 10.418985, -12.004164, -22.046722, 10.418985, -11.95994, -19.902674, 10.253971, -11.95994, -22.046722, 10.253971, -12.004164, -22.046722, 10.418985, -12.004164, -19.902674, 10.418985, -11.95994, -19.902674, 10.253971, -11.95994, -22.046722, 10.253971, -12.004164, -19.902674, 10.088957, -12.004164, -22.046722, 10.088959, -11.95994, -22.046722, 10.253971, -11.95994, -19.902674, 10.253971, -12.004164, -19.902674, 10.088957, -12.124954, -22.046722, 9.968168, -12.2899685, -22.046722, 10.253971, -12.004164, -22.046722, 10.088959, -12.004164, -22.046722, 10.088959, -12.2899685, -22.046722, 10.253971, -11.95994, -22.046722, 10.253971, -12.2899685, -22.046722, 9.923944, -12.2899685, -22.046722, 10.253971, -12.124954, -22.046722, 9.968168, -11.95994, -22.046722, 10.253971, -12.2899685, -22.046722, 10.253971, -12.004164, -22.046722, 10.418985, -12.454983, -22.046722, 9.968168, -12.2899685, -22.046722, 10.253971, -12.2899685, -22.046722, 9.923944, -12.004164, -22.046722, 10.418985, -12.2899685, -22.046722, 10.253971, -12.124954, -22.046722, 10.539776, -12.575771, -22.046722, 10.088959, -12.2899685, -22.046722, 10.253971, -12.454983, -22.046722, 9.968168, -12.124954, -22.046722, 10.539776, -12.2899685, -22.046722, 10.253971, -12.2899685, -22.046722, 10.584, -12.619995, -22.046722, 10.253971, -12.2899685, -22.046722, 10.253971, -12.575771, -22.046722, 10.088959, -12.2899685, -22.046722, 10.584, -12.2899685, -22.046722, 10.253971, -12.454983, -22.046722, 10.539776, -12.575771, -22.046722, 10.418985, -12.2899685, -22.046722, 10.253971, -12.619995, -22.046722, 10.253971, -12.454983, -22.046722, 10.539776, -12.2899685, -22.046722, 10.253971, -12.575771, -22.046722, 10.418985, -12.004164, -19.902674, 10.088957, -12.2899685, -19.456131, 10.253971, -12.124954, -19.902674, 9.968166, -12.124954, -19.902674, 9.968166, -12.2899685, -19.456131, 10.253971, -12.2899685, -19.902674, 9.923943, -11.95994, -19.902674, 10.253971, -12.2899685, -19.456131, 10.253971, -12.004164, -19.902674, 10.088957, -12.2899685, -19.902674, 9.923943, -12.2899685, -19.456131, 10.253971, -12.454983, -19.902674, 9.968166, -12.004164, -19.902674, 10.418985, -12.2899685, -19.456131, 10.253971, -11.95994, -19.902674, 10.253971, -12.454983, -19.902674, 9.968166, -12.2899685, -19.456131, 10.253971, -12.575771, -19.902674, 10.088957, -12.124954, -19.902674, 10.539776, -12.2899685, -19.456131, 10.253971, -12.004164, -19.902674, 10.418985, -12.575771, -19.902674, 10.088957, -12.2899685, -19.456131, 10.253971, -12.619995, -19.902674, 10.253971, -12.2899685, -19.902674, 10.584, -12.2899685, -19.456131, 10.253971, -12.124954, -19.902674, 10.539776, -12.619995, -19.902674, 10.253971, -12.2899685, -19.456131, 10.253971, -12.575771, -19.902674, 10.418985, -12.454983, -19.902674, 10.539776, -12.2899685, -19.456131, 10.253971, -12.2899685, -19.902674, 10.584, -12.575771, -19.902674, 10.418985, -12.2899685, -19.456131, 10.253971, -12.454983, -19.902674, 10.539776, -12.004164, -22.046722, 6.8597555, -12.124954, -19.902674, 6.7389646, -12.124954, -22.046722, 6.738965, -12.004164, -22.046722, 6.8597555, -12.004164, -19.902674, 6.8597546, -12.124954, -19.902674, 6.7389646, -12.124954, -22.046722, 6.738965, -12.2899685, -19.902674, 6.694741, -12.2899685, -22.046722, 6.6947412, -12.124954, -22.046722, 6.738965, -12.124954, -19.902674, 6.7389646, -12.2899685, -19.902674, 6.694741, -12.2899685, -22.046722, 6.6947412, -12.454983, -19.902674, 6.7389646, -12.454983, -22.046722, 6.738965, -12.2899685, -22.046722, 6.6947412, -12.2899685, -19.902674, 6.694741, -12.454983, -19.902674, 6.7389646, -12.454983, -22.046722, 6.738965, -12.575771, -19.902674, 6.8597546, -12.575771, -22.046722, 6.8597555, -12.454983, -22.046722, 6.738965, -12.454983, -19.902674, 6.7389646, -12.575771, -19.902674, 6.8597546, -12.575771, -22.046722, 6.8597555, -12.619995, -19.902674, 7.0247684, -12.619995, -22.046722, 7.0247693, -12.575771, -22.046722, 6.8597555, -12.575771, -19.902674, 6.8597546, -12.619995, -19.902674, 7.0247684, -12.619995, -22.046722, 7.0247693, -12.575771, -19.902674, 7.189782, -12.575771, -22.046722, 7.189783, -12.619995, -22.046722, 7.0247693, -12.619995, -19.902674, 7.0247684, -12.575771, -19.902674, 7.189782, -12.575771, -22.046722, 7.189783, -12.454983, -19.902674, 7.3105726, -12.454983, -22.046722, 7.310573, -12.575771, -22.046722, 7.189783, -12.575771, -19.902674, 7.189782, -12.454983, -19.902674, 7.3105726, -12.454983, -22.046722, 7.310573, -12.2899685, -19.902674, 7.3547964, -12.2899685, -22.046722, 7.354797, -12.454983, -22.046722, 7.310573, -12.454983, -19.902674, 7.3105726, -12.2899685, -19.902674, 7.3547964, -12.2899685, -22.046722, 7.354797, -12.124954, -19.902674, 7.3105726, -12.124954, -22.046722, 7.310573, -12.2899685, -22.046722, 7.354797, -12.2899685, -19.902674, 7.3547964, -12.124954, -19.902674, 7.3105726, -12.124954, -22.046722, 7.310573, -12.004164, -19.902674, 7.189782, -12.004164, -22.046722, 7.189783, -12.124954, -22.046722, 7.310573, -12.124954, -19.902674, 7.3105726, -12.004164, -19.902674, 7.189782, -12.004164, -22.046722, 7.189783, -11.95994, -19.902674, 7.0247684, -11.95994, -22.046722, 7.0247693, -12.004164, -22.046722, 7.189783, -12.004164, -19.902674, 7.189782, -11.95994, -19.902674, 7.0247684, -11.95994, -22.046722, 7.0247693, -12.004164, -19.902674, 6.8597546, -12.004164, -22.046722, 6.8597555, -11.95994, -22.046722, 7.0247693, -11.95994, -19.902674, 7.0247684, -12.004164, -19.902674, 6.8597546, -12.124954, -22.046722, 6.738965, -12.2899685, -22.046722, 7.0247693, -12.004164, -22.046722, 6.8597555, -12.004164, -22.046722, 6.8597555, -12.2899685, -22.046722, 7.0247693, -11.95994, -22.046722, 7.0247693, -12.2899685, -22.046722, 6.6947412, -12.2899685, -22.046722, 7.0247693, -12.124954, -22.046722, 6.738965, -11.95994, -22.046722, 7.0247693, -12.2899685, -22.046722, 7.0247693, -12.004164, -22.046722, 7.189783, -12.454983, -22.046722, 6.738965, -12.2899685, -22.046722, 7.0247693, -12.2899685, -22.046722, 6.6947412, -12.004164, -22.046722, 7.189783, -12.2899685, -22.046722, 7.0247693, -12.124954, -22.046722, 7.310573, -12.575771, -22.046722, 6.8597555, -12.2899685, -22.046722, 7.0247693, -12.454983, -22.046722, 6.738965, -12.124954, -22.046722, 7.310573, -12.2899685, -22.046722, 7.0247693, -12.2899685, -22.046722, 7.354797, -12.619995, -22.046722, 7.0247693, -12.2899685, -22.046722, 7.0247693, -12.575771, -22.046722, 6.8597555, -12.2899685, -22.046722, 7.354797, -12.2899685, -22.046722, 7.0247693, -12.454983, -22.046722, 7.310573, -12.575771, -22.046722, 7.189783, -12.2899685, -22.046722, 7.0247693, -12.619995, -22.046722, 7.0247693, -12.454983, -22.046722, 7.310573, -12.2899685, -22.046722, 7.0247693, -12.575771, -22.046722, 7.189783, -12.004164, -19.902674, 6.8597546, -12.2899685, -19.456131, 7.0247684, -12.124954, -19.902674, 6.7389646, -12.124954, -19.902674, 6.7389646, -12.2899685, -19.456131, 7.0247684, -12.2899685, -19.902674, 6.694741, -11.95994, -19.902674, 7.0247684, -12.2899685, -19.456131, 7.0247684, -12.004164, -19.902674, 6.8597546, -12.2899685, -19.902674, 6.694741, -12.2899685, -19.456131, 7.0247684, -12.454983, -19.902674, 6.7389646, -12.004164, -19.902674, 7.189782, -12.2899685, -19.456131, 7.0247684, -11.95994, -19.902674, 7.0247684, -12.454983, -19.902674, 6.7389646, -12.2899685, -19.456131, 7.0247684, -12.575771, -19.902674, 6.8597546, -12.124954, -19.902674, 7.3105726, -12.2899685, -19.456131, 7.0247684, -12.004164, -19.902674, 7.189782, -12.575771, -19.902674, 6.8597546, -12.2899685, -19.456131, 7.0247684, -12.619995, -19.902674, 7.0247684, -12.2899685, -19.902674, 7.3547964, -12.2899685, -19.456131, 7.0247684, -12.124954, -19.902674, 7.3105726, -12.619995, -19.902674, 7.0247684, -12.2899685, -19.456131, 7.0247684, -12.575771, -19.902674, 7.189782, -12.454983, -19.902674, 7.3105726, -12.2899685, -19.456131, 7.0247684, -12.2899685, -19.902674, 7.3547964, -12.575771, -19.902674, 7.189782, -12.2899685, -19.456131, 7.0247684, -12.454983, -19.902674, 7.3105726, -12.917406, -22.143435, 6.9334874, -12.989674, -20.442585, 6.866668, -12.989674, -22.143435, 6.866669, -12.917406, -22.143435, 6.9334874, -12.917406, -20.442585, 6.9334865, -12.989674, -20.442585, 6.866668, -12.989674, -22.143435, 6.866669, -13.088398, -20.442585, 6.842205, -13.088398, -22.143435, 6.8422055, -12.989674, -22.143435, 6.866669, -12.989674, -20.442585, 6.866668, -13.088398, -20.442585, 6.842205, -13.088398, -22.143435, 6.8422055, -13.187122, -20.442585, 6.866668, -13.187122, -22.143435, 6.866669, -13.088398, -22.143435, 6.8422055, -13.088398, -20.442585, 6.842205, -13.187122, -20.442585, 6.866668, -13.187122, -22.143435, 6.866669, -13.259388, -20.442585, 6.9334865, -13.259388, -22.143435, 6.9334874, -13.187122, -22.143435, 6.866669, -13.187122, -20.442585, 6.866668, -13.259388, -20.442585, 6.9334865, -13.259388, -22.143435, 6.9334874, -13.285847, -20.442583, 7.0247684, -13.285847, -22.143435, 7.0247693, -13.259388, -22.143435, 6.9334874, -13.259388, -20.442585, 6.9334865, -13.285847, -20.442583, 7.0247684, -13.285847, -22.143435, 7.0247693, -13.259388, -20.442583, 7.1160502, -13.259388, -22.143435, 7.1160507, -13.285847, -22.143435, 7.0247693, -13.285847, -20.442583, 7.0247684, -13.259388, -20.442583, 7.1160502, -13.259388, -22.143435, 7.1160507, -13.187122, -20.442583, 7.1828685, -13.187122, -22.143435, 7.1828694, -13.259388, -22.143435, 7.1160507, -13.259388, -20.442583, 7.1160502, -13.187122, -20.442583, 7.1828685, -13.187122, -22.143435, 7.1828694, -13.088398, -20.442583, 7.207332, -13.088398, -22.143435, 7.2073326, -13.187122, -22.143435, 7.1828694, -13.187122, -20.442583, 7.1828685, -13.088398, -20.442583, 7.207332, -13.088398, -22.143435, 7.2073326, -12.989674, -20.442583, 7.1828685, -12.989674, -22.143435, 7.1828694, -13.088398, -22.143435, 7.2073326, -13.088398, -20.442583, 7.207332, -12.989674, -20.442583, 7.1828685, -12.989674, -22.143435, 7.1828694, -12.917406, -20.442583, 7.1160502, -12.917406, -22.143435, 7.1160507, -12.989674, -22.143435, 7.1828694, -12.989674, -20.442583, 7.1828685, -12.917406, -20.442583, 7.1160502, -12.917406, -22.143435, 7.1160507, -12.890947, -20.442583, 7.0247684, -12.890947, -22.143435, 7.0247693, -12.917406, -22.143435, 7.1160507, -12.917406, -20.442583, 7.1160502, -12.890947, -20.442583, 7.0247684, -12.890947, -22.143435, 7.0247693, -12.917406, -20.442585, 6.9334865, -12.917406, -22.143435, 6.9334874, -12.890947, -22.143435, 7.0247693, -12.890947, -20.442583, 7.0247684, -12.917406, -20.442585, 6.9334865, -12.989674, -22.143435, 6.866669, -13.088398, -22.143435, 7.0247693, -12.917406, -22.143435, 6.9334874, -12.917406, -22.143435, 6.9334874, -13.088398, -22.143435, 7.0247693, -12.890947, -22.143435, 7.0247693, -13.088398, -22.143435, 6.8422055, -13.088398, -22.143435, 7.0247693, -12.989674, -22.143435, 6.866669, -12.890947, -22.143435, 7.0247693, -13.088398, -22.143435, 7.0247693, -12.917406, -22.143435, 7.1160507, -13.187122, -22.143435, 6.866669, -13.088398, -22.143435, 7.0247693, -13.088398, -22.143435, 6.8422055, -12.917406, -22.143435, 7.1160507, -13.088398, -22.143435, 7.0247693, -12.989674, -22.143435, 7.1828694, -13.259388, -22.143435, 6.9334874, -13.088398, -22.143435, 7.0247693, -13.187122, -22.143435, 6.866669, -12.989674, -22.143435, 7.1828694, -13.088398, -22.143435, 7.0247693, -13.088398, -22.143435, 7.2073326, -13.285847, -22.143435, 7.0247693, -13.088398, -22.143435, 7.0247693, -13.259388, -22.143435, 6.9334874, -13.088398, -22.143435, 7.2073326, -13.088398, -22.143435, 7.0247693, -13.187122, -22.143435, 7.1828694, -13.259388, -22.143435, 7.1160507, -13.088398, -22.143435, 7.0247693, -13.285847, -22.143435, 7.0247693, -13.187122, -22.143435, 7.1828694, -13.088398, -22.143435, 7.0247693, -13.259388, -22.143435, 7.1160507, -12.917406, -20.442585, 6.9334865, -13.088398, -20.088346, 7.0247684, -12.989674, -20.442585, 6.866668, -12.989674, -20.442585, 6.866668, -13.088398, -20.088346, 7.0247684, -13.088398, -20.442585, 6.842205, -12.890947, -20.442583, 7.0247684, -13.088398, -20.088346, 7.0247684, -12.917406, -20.442585, 6.9334865, -13.088398, -20.442585, 6.842205, -13.088398, -20.088346, 7.0247684, -13.187122, -20.442585, 6.866668, -12.917406, -20.442583, 7.1160502, -13.088398, -20.088346, 7.0247684, -12.890947, -20.442583, 7.0247684, -13.187122, -20.442585, 6.866668, -13.088398, -20.088346, 7.0247684, -13.259388, -20.442585, 6.9334865, -12.989674, -20.442583, 7.1828685, -13.088398, -20.088346, 7.0247684, -12.917406, -20.442583, 7.1160502, -13.259388, -20.442585, 6.9334865, -13.088398, -20.088346, 7.0247684, -13.285847, -20.442583, 7.0247684, -13.088398, -20.442583, 7.207332, -13.088398, -20.088346, 7.0247684, -12.989674, -20.442583, 7.1828685, -13.285847, -20.442583, 7.0247684, -13.088398, -20.088346, 7.0247684, -13.259388, -20.442583, 7.1160502, -13.187122, -20.442583, 7.1828685, -13.088398, -20.088346, 7.0247684, -13.088398, -20.442583, 7.207332, -13.259388, -20.442583, 7.1160502, -13.088398, -20.088346, 7.0247684, -13.187122, -20.442583, 7.1828685, 7.7560034, -21.843685, -1.5963855, 7.8594503, -21.712652, -1.5136316, 7.8594503, -21.889544, -1.5601864, 7.7560034, -21.843685, -1.5963855, 7.7198057, -21.712652, -1.5601866, 7.8594503, -21.712652, -1.5136316, 7.7198057, -21.889544, -1.6998296, 7.7560034, -21.843685, -1.5963855, 7.8594503, -21.889544, -1.5601864, 7.7198057, -21.889544, -1.6998296, 7.7198057, -21.712652, -1.5601866, 7.7560034, -21.843685, -1.5963855, 7.7198057, -21.712652, -1.5601866, 7.8594503, -21.392511, -1.5493045, 7.8594503, -21.712652, -1.5136316, 7.8594503, -21.712652, -1.5136316, 7.8594503, -21.392511, -1.5493045, 7.9628973, -21.392511, -1.5855265, 7.7198057, -21.712652, -1.5601866, 7.7560034, -21.392511, -1.5855265, 7.8594503, -21.392511, -1.5493045, 7.8594503, -21.712652, -1.5136316, 7.9628973, -21.392511, -1.5855265, 7.999095, -21.712652, -1.5601866, 7.8594503, -21.889544, -1.5601864, 7.8594503, -21.712652, -1.5136316, 7.999095, -21.712652, -1.5601866, 7.999095, -21.712652, -1.5601866, 7.9628973, -21.392511, -1.5855265, 7.999095, -21.392511, -1.6889691, 7.673258, -21.712654, -1.6998296, 7.7560034, -21.392511, -1.5855265, 7.7198057, -21.712652, -1.5601866, 7.7198057, -21.889544, -1.6998296, 7.673258, -21.712654, -1.6998296, 7.7198057, -21.712652, -1.5601866, 7.673258, -21.712654, -1.6998296, 7.7198057, -21.392511, -1.6889691, 7.7560034, -21.392511, -1.5855265, 7.999095, -21.712652, -1.5601866, 7.999095, -21.392511, -1.6889691, 8.045643, -21.712654, -1.6998296, 8.045643, -21.712654, -1.6998296, 7.999095, -21.392511, -1.6889691, 7.9628973, -21.392511, -1.7924135, 7.8594503, -21.889544, -1.5601864, 7.999095, -21.712652, -1.5601866, 7.9628973, -21.843685, -1.5963855, 7.9628973, -21.843685, -1.5963855, 7.999095, -21.712652, -1.5601866, 8.045643, -21.712654, -1.6998296, 7.7198057, -21.889544, -1.6998296, 7.8594503, -21.889544, -1.5601864, 7.8594503, -21.948503, -1.6998296, 7.8594503, -21.948503, -1.6998296, 7.8594503, -21.889544, -1.5601864, 7.9628973, -21.843685, -1.5963855, 7.7560034, -21.843685, -1.8032725, 7.673258, -21.712654, -1.6998296, 7.7198057, -21.889544, -1.6998296, 7.7560034, -21.843685, -1.8032725, 7.7198057, -21.889544, -1.6998296, 7.8594503, -21.948503, -1.6998296, 7.7198057, -21.712654, -1.8394728, 7.7198057, -21.392511, -1.6889691, 7.673258, -21.712654, -1.6998296, 7.7560034, -21.843685, -1.8032725, 7.7198057, -21.712654, -1.8394728, 7.673258, -21.712654, -1.6998296, 7.7198057, -21.712654, -1.8394728, 7.7560034, -21.392511, -1.7924135, 7.7198057, -21.392511, -1.6889691, 7.7560034, -21.843685, -1.8032725, 7.8594503, -21.948503, -1.6998296, 7.8594503, -21.889544, -1.8394728, 7.7198057, -21.712654, -1.8394728, 7.7560034, -21.843685, -1.8032725, 7.8594503, -21.889544, -1.8394728, 7.8594503, -21.948503, -1.6998296, 7.9628973, -21.843685, -1.5963855, 7.999095, -21.889544, -1.6998296, 7.8594503, -21.889544, -1.8394728, 7.8594503, -21.948503, -1.6998296, 7.999095, -21.889544, -1.6998296, 7.9628973, -21.843685, -1.5963855, 8.045643, -21.712654, -1.6998296, 7.999095, -21.889544, -1.6998296, 7.8594503, -21.889544, -1.8394728, 7.999095, -21.889544, -1.6998296, 7.9628973, -21.843685, -1.8032725, 7.7560034, -21.392511, -1.7924135, 7.7198057, -21.712654, -1.8394728, 7.8594503, -21.712654, -1.8860278, 7.7198057, -21.712654, -1.8394728, 7.8594503, -21.889544, -1.8394728, 7.8594503, -21.712654, -1.8860278, 7.8594503, -21.712654, -1.8860278, 7.8594503, -21.889544, -1.8394728, 7.9628973, -21.843685, -1.8032725, 7.7560034, -21.392511, -1.7924135, 7.8594503, -21.712654, -1.8860278, 7.8594503, -21.392511, -1.8286123, 7.999095, -21.889544, -1.6998296, 8.045643, -21.712654, -1.6998296, 7.999095, -21.712654, -1.8394728, 7.999095, -21.889544, -1.6998296, 7.999095, -21.712654, -1.8394728, 7.9628973, -21.843685, -1.8032725, 7.8594503, -21.712654, -1.8860278, 7.9628973, -21.843685, -1.8032725, 7.999095, -21.712654, -1.8394728, 7.8594503, -21.392511, -1.8286123, 7.8594503, -21.712654, -1.8860278, 7.999095, -21.712654, -1.8394728, 8.045643, -21.712654, -1.6998296, 7.9628973, -21.392511, -1.7924135, 7.999095, -21.712654, -1.8394728, 7.8594503, -21.392511, -1.8286123, 7.999095, -21.712654, -1.8394728, 7.9628973, -21.392511, -1.7924135, 7.597027, -21.442596, -1.34617, 7.8594503, -21.312296, -1.1490684, 7.8594503, -21.495977, -1.2661133, 7.597027, -21.442596, -1.34617, 7.505171, -21.314875, -1.2550128, 7.8594503, -21.312296, -1.1490684, 7.8594503, -21.495977, -1.2661133, 7.8594503, -21.312296, -1.1490684, 8.21373, -21.314875, -1.2550128, 7.505171, -21.409954, -1.6840336, 7.505171, -21.314875, -1.2550128, 7.597027, -21.442596, -1.34617, 7.505171, -21.409954, -1.6840336, 7.597027, -21.442596, -1.34617, 7.8594503, -21.495977, -1.2661133, 7.505171, -21.409954, -1.6840336, 7.387092, -21.227238, -1.6845381, 7.505171, -21.314875, -1.2550128, 7.8594503, -21.495977, -1.2661133, 8.21373, -21.314875, -1.2550128, 8.121872, -21.442596, -1.34617, 8.121872, -21.442596, -1.34617, 8.21373, -21.314875, -1.2550128, 8.331823, -21.227238, -1.6845381, 7.505171, -21.409954, -1.6840336, 7.8594503, -21.495977, -1.2661133, 7.8594503, -21.49225, -1.6844721, 7.8594503, -21.49225, -1.6844721, 7.8594503, -21.495977, -1.2661133, 8.121872, -21.442596, -1.34617, 7.597027, -21.339413, -1.9443204, 7.387092, -21.227238, -1.6845381, 7.505171, -21.409954, -1.6840336, 7.597027, -21.339413, -1.9443204, 7.505171, -21.409954, -1.6840336, 7.8594503, -21.49225, -1.6844721, 7.597027, -21.339413, -1.9443204, 7.505171, -21.222761, -2.0274482, 7.387092, -21.227238, -1.6845381, 8.121872, -21.442596, -1.34617, 8.331823, -21.227238, -1.6845381, 8.21373, -21.409954, -1.6840336, 7.8594503, -21.49225, -1.6844721, 8.121872, -21.442596, -1.34617, 8.21373, -21.409954, -1.6840336, 8.21373, -21.409954, -1.6840336, 8.331823, -21.227238, -1.6845381, 8.21373, -21.222761, -2.0274482, 7.597027, -21.339413, -1.9443204, 7.8594503, -21.49225, -1.6844721, 7.8594503, -21.403198, -2.0336347, 7.505171, -21.222761, -2.0274482, 7.597027, -21.339413, -1.9443204, 7.8594503, -21.403198, -2.0336347, 7.8594503, -21.403198, -2.0336347, 7.8594503, -21.49225, -1.6844721, 8.21373, -21.409954, -1.6840336, 7.505171, -21.222761, -2.0274482, 7.8594503, -21.403198, -2.0336347, 7.8594503, -21.227154, -2.1436162, 8.21373, -21.409954, -1.6840336, 8.21373, -21.222761, -2.0274482, 8.121872, -21.339413, -1.9443204, 7.8594503, -21.403198, -2.0336347, 8.21373, -21.409954, -1.6840336, 8.121872, -21.339413, -1.9443204, 7.8594503, -21.227154, -2.1436162, 8.121872, -21.339413, -1.9443204, 8.21373, -21.222761, -2.0274482, 7.8594503, -21.227154, -2.1436162, 7.8594503, -21.403198, -2.0336347, 8.121872, -21.339413, -1.9443204, 7.8594503, -21.312296, -1.1490684, 8.121872, -21.111135, -1.4137647, 8.21373, -21.314875, -1.2550128, 8.21373, -21.314875, -1.2550128, 8.121872, -21.111135, -1.4137647, 8.21373, -21.024746, -1.6911418, 7.8594503, -21.312296, -1.1490684, 7.8594503, -21.038862, -1.3319972, 8.121872, -21.111135, -1.4137647, 7.8594503, -21.038862, -1.3319972, 8.21373, -21.024746, -1.6911418, 8.121872, -21.111135, -1.4137647, 8.21373, -21.314875, -1.2550128, 8.21373, -21.024746, -1.6911418, 8.331823, -21.227238, -1.6845381, 7.505171, -21.314875, -1.2550128, 7.8594503, -21.038862, -1.3319972, 7.8594503, -21.312296, -1.1490684, 7.505171, -21.314875, -1.2550128, 7.597027, -21.111135, -1.4137647, 7.8594503, -21.038862, -1.3319972, 7.387092, -21.227238, -1.6845381, 7.597027, -21.111135, -1.4137647, 7.505171, -21.314875, -1.2550128, 7.8594503, -21.038862, -1.3319972, 7.8594503, -20.941702, -1.6914926, 8.21373, -21.024746, -1.6911418, 7.597027, -21.111135, -1.4137647, 7.8594503, -20.941702, -1.6914926, 7.8594503, -21.038862, -1.3319972, 8.331823, -21.227238, -1.6845381, 8.21373, -21.024746, -1.6911418, 8.121872, -21.09246, -1.9232578, 7.8594503, -20.941702, -1.6914926, 8.121872, -21.09246, -1.9232578, 8.21373, -21.024746, -1.6911418, 8.331823, -21.227238, -1.6845381, 8.121872, -21.09246, -1.9232578, 8.21373, -21.222761, -2.0274482, 7.387092, -21.227238, -1.6845381, 7.505171, -21.024746, -1.6911418, 7.597027, -21.111135, -1.4137647, 7.597027, -21.111135, -1.4137647, 7.505171, -21.024746, -1.6911418, 7.8594503, -20.941702, -1.6914926, 7.505171, -21.222761, -2.0274482, 7.505171, -21.024746, -1.6911418, 7.387092, -21.227238, -1.6845381, 7.8594503, -21.028408, -2.012529, 8.21373, -21.222761, -2.0274482, 8.121872, -21.09246, -1.9232578, 7.8594503, -20.941702, -1.6914926, 7.8594503, -21.028408, -2.012529, 8.121872, -21.09246, -1.9232578, 7.505171, -21.024746, -1.6911418, 7.8594503, -21.028408, -2.012529, 7.8594503, -20.941702, -1.6914926, 7.8594503, -21.028408, -2.012529, 7.8594503, -21.227154, -2.1436162, 8.21373, -21.222761, -2.0274482, 7.505171, -21.222761, -2.0274482, 7.597027, -21.09246, -1.9232578, 7.505171, -21.024746, -1.6911418, 7.505171, -21.024746, -1.6911418, 7.597027, -21.09246, -1.9232578, 7.8594503, -21.028408, -2.012529, 7.597027, -21.09246, -1.9232578, 7.505171, -21.222761, -2.0274482, 7.8594503, -21.227154, -2.1436162, 7.597027, -21.09246, -1.9232578, 7.8594503, -21.227154, -2.1436162, 7.8594503, -21.028408, -2.012529, 7.7984433, -21.953579, -2.1591706, 7.8594503, -21.864277, -2.1397543, 7.8594503, -21.97169, -2.1298366, 7.7984433, -21.953579, -2.1591706, 7.7770934, -21.873665, -2.165554, 7.8594503, -21.864277, -2.1397543, 7.7770934, -21.99987, -2.2072396, 7.7984433, -21.953579, -2.1591706, 7.8594503, -21.97169, -2.1298366, 7.7770934, -21.99987, -2.2072396, 7.7770934, -21.873665, -2.165554, 7.7984433, -21.953579, -2.1591706, 7.7770934, -21.873665, -2.165554, 7.8594503, -21.69406, -2.224133, 7.8594503, -21.864277, -2.1397543, 7.8594503, -21.864277, -2.1397543, 7.8594503, -21.69406, -2.224133, 7.9204574, -21.701366, -2.2442064, 7.7770934, -21.873665, -2.165554, 7.7984433, -21.701366, -2.2442064, 7.8594503, -21.69406, -2.224133, 7.8594503, -21.864277, -2.1397543, 7.9204574, -21.701366, -2.2442064, 7.9418073, -21.873665, -2.165554, 7.8594503, -21.97169, -2.1298366, 7.8594503, -21.864277, -2.1397543, 7.9418073, -21.873665, -2.165554, 7.9418073, -21.873665, -2.165554, 7.9204574, -21.701366, -2.2442064, 7.9418073, -21.72224, -2.3015342, 7.7496314, -21.901844, -2.2429338, 7.7984433, -21.701366, -2.2442064, 7.7770934, -21.873665, -2.165554, 7.7770934, -21.99987, -2.2072396, 7.7496314, -21.901844, -2.2429338, 7.7770934, -21.873665, -2.165554, 7.7496314, -21.901844, -2.2429338, 7.7770934, -21.72224, -2.3015342, 7.7984433, -21.701366, -2.2442064, 7.9418073, -21.873665, -2.165554, 7.9418073, -21.72224, -2.3015342, 7.9692693, -21.901844, -2.2429338, 7.9692693, -21.901844, -2.2429338, 7.9418073, -21.72224, -2.3015342, 7.9204574, -21.743114, -2.3588605, 7.8594503, -21.97169, -2.1298366, 7.9418073, -21.873665, -2.165554, 7.9204574, -21.953579, -2.1591706, 7.9204574, -21.953579, -2.1591706, 7.9418073, -21.873665, -2.165554, 7.9692693, -21.901844, -2.2429338, 7.7770934, -21.99987, -2.2072396, 7.8594503, -21.97169, -2.1298366, 7.8594503, -22.032562, -2.1953478, 7.8594503, -22.032562, -2.1953478, 7.8594503, -21.97169, -2.1298366, 7.9204574, -21.953579, -2.1591706, 7.7984433, -21.995342, -2.2738247, 7.7496314, -21.901844, -2.2429338, 7.7770934, -21.99987, -2.2072396, 7.7984433, -21.995342, -2.2738247, 7.7770934, -21.99987, -2.2072396, 7.8594503, -22.032562, -2.1953478, 7.7770934, -21.930042, -2.320335, 7.7770934, -21.72224, -2.3015342, 7.7496314, -21.901844, -2.2429338, 7.7984433, -21.995342, -2.2738247, 7.7770934, -21.930042, -2.320335, 7.7496314, -21.901844, -2.2429338, 7.7770934, -21.930042, -2.320335, 7.7984433, -21.743114, -2.3588605, 7.7770934, -21.72224, -2.3015342, 7.7984433, -21.995342, -2.2738247, 7.8594503, -22.032562, -2.1953478, 7.8594503, -22.028069, -2.2846189, 7.7770934, -21.930042, -2.320335, 7.7984433, -21.995342, -2.2738247, 7.8594503, -22.028069, -2.2846189, 7.8594503, -22.032562, -2.1953478, 7.9204574, -21.953579, -2.1591706, 7.9418073, -21.99987, -2.2072396, 7.8594503, -22.028069, -2.2846189, 7.8594503, -22.032562, -2.1953478, 7.9418073, -21.99987, -2.2072396, 7.9204574, -21.953579, -2.1591706, 7.9692693, -21.901844, -2.2429338, 7.9418073, -21.99987, -2.2072396, 7.8594503, -22.028069, -2.2846189, 7.9418073, -21.99987, -2.2072396, 7.9204574, -21.995342, -2.2738247, 7.7984433, -21.743114, -2.3588605, 7.7770934, -21.930042, -2.320335, 7.8594503, -21.939432, -2.3461366, 7.7770934, -21.930042, -2.320335, 7.8594503, -22.028069, -2.2846189, 7.8594503, -21.939432, -2.3461366, 7.8594503, -21.939432, -2.3461366, 7.8594503, -22.028069, -2.2846189, 7.9204574, -21.995342, -2.2738247, 7.7984433, -21.743114, -2.3588605, 7.8594503, -21.939432, -2.3461366, 7.8594503, -21.750422, -2.3789139, 7.9418073, -21.99987, -2.2072396, 7.9692693, -21.901844, -2.2429338, 7.9418073, -21.930042, -2.320335, 7.9418073, -21.99987, -2.2072396, 7.9418073, -21.930042, -2.320335, 7.9204574, -21.995342, -2.2738247, 7.8594503, -21.939432, -2.3461366, 7.9204574, -21.995342, -2.2738247, 7.9418073, -21.930042, -2.320335, 7.8594503, -21.750422, -2.3789139, 7.8594503, -21.939432, -2.3461366, 7.9418073, -21.930042, -2.320335, 7.9692693, -21.901844, -2.2429338, 7.9204574, -21.743114, -2.3588605, 7.9418073, -21.930042, -2.320335, 7.8594503, -21.750422, -2.3789139, 7.9418073, -21.930042, -2.320335, 7.9204574, -21.743114, -2.3588605, 7.7046695, -21.68081, -2.1014476, 7.8594503, -21.568819, -2.0185182, 7.8594503, -21.694242, -2.0463152, 7.7046695, -21.68081, -2.1014476, 7.6505103, -21.59164, -2.0767, 7.8594503, -21.568819, -2.0185182, 7.8594503, -21.694242, -2.0463152, 7.8594503, -21.568819, -2.0185182, 8.06839, -21.59164, -2.0767, 7.6505103, -21.730913, -2.2952814, 7.6505103, -21.59164, -2.0767, 7.7046695, -21.68081, -2.1014476, 7.6505103, -21.730913, -2.2952814, 7.7046695, -21.68081, -2.1014476, 7.8594503, -21.694242, -2.0463152, 7.6505103, -21.730913, -2.2952814, 7.5808525, -21.629759, -2.3324466, 7.6505103, -21.59164, -2.0767, 7.8594503, -21.694242, -2.0463152, 8.06839, -21.59164, -2.0767, 8.014231, -21.68081, -2.1014476, 8.014231, -21.68081, -2.1014476, 8.06839, -21.59164, -2.0767, 8.138046, -21.629759, -2.3324466, 7.6505103, -21.730913, -2.2952814, 7.8594503, -21.694242, -2.0463152, 7.8594503, -21.776606, -2.2788925, 7.8594503, -21.776606, -2.2788925, 7.8594503, -21.694242, -2.0463152, 8.014231, -21.68081, -2.1014476, 7.7046695, -21.744347, -2.4537702, 7.5808525, -21.629759, -2.3324466, 7.6505103, -21.730913, -2.2952814, 7.7046695, -21.744347, -2.4537702, 7.6505103, -21.730913, -2.2952814, 7.8594503, -21.776606, -2.2788925, 7.7046695, -21.744347, -2.4537702, 7.6505103, -21.696474, -2.5233836, 7.5808525, -21.629759, -2.3324466, 8.014231, -21.68081, -2.1014476, 8.138046, -21.629759, -2.3324466, 8.06839, -21.730913, -2.2952814, 7.8594503, -21.776606, -2.2788925, 8.014231, -21.68081, -2.1014476, 8.06839, -21.730913, -2.2952814, 8.06839, -21.730913, -2.2952814, 8.138046, -21.629759, -2.3324466, 8.06839, -21.696474, -2.5233836, 7.7046695, -21.744347, -2.4537702, 7.8594503, -21.776606, -2.2788925, 7.8594503, -21.797728, -2.490386, 7.6505103, -21.696474, -2.5233836, 7.7046695, -21.744347, -2.4537702, 7.8594503, -21.797728, -2.490386, 7.8594503, -21.797728, -2.490386, 7.8594503, -21.776606, -2.2788925, 8.06839, -21.730913, -2.2952814, 7.6505103, -21.696474, -2.5233836, 7.8594503, -21.797728, -2.490386, 7.8594503, -21.722357, -2.586853, 8.06839, -21.730913, -2.2952814, 8.06839, -21.696474, -2.5233836, 8.014231, -21.744347, -2.4537702, 7.8594503, -21.797728, -2.490386, 8.06839, -21.730913, -2.2952814, 8.014231, -21.744347, -2.4537702, 7.8594503, -21.722357, -2.586853, 8.014231, -21.744347, -2.4537702, 8.06839, -21.696474, -2.5233836, 7.8594503, -21.722357, -2.586853, 7.8594503, -21.797728, -2.490386, 8.014231, -21.744347, -2.4537702, 7.8594503, -21.568819, -2.0185182, 8.014231, -21.51076, -2.2058134, 8.06839, -21.59164, -2.0767, 8.06839, -21.59164, -2.0767, 8.014231, -21.51076, -2.2058134, 8.06839, -21.518867, -2.3769612, 7.8594503, -21.568819, -2.0185182, 7.8594503, -21.454199, -2.1750765, 8.014231, -21.51076, -2.2058134, 7.8594503, -21.454199, -2.1750765, 8.06839, -21.518867, -2.3769612, 8.014231, -21.51076, -2.2058134, 8.06839, -21.59164, -2.0767, 8.06839, -21.518867, -2.3769612, 8.138046, -21.629759, -2.3324466, 7.6505103, -21.59164, -2.0767, 7.8594503, -21.454199, -2.1750765, 7.8594503, -21.568819, -2.0185182, 7.6505103, -21.59164, -2.0767, 7.7046695, -21.51076, -2.2058134, 7.8594503, -21.454199, -2.1750765, 7.5808525, -21.629759, -2.3324466, 7.7046695, -21.51076, -2.2058134, 7.6505103, -21.59164, -2.0767, 7.8594503, -21.454199, -2.1750765, 7.8594503, -21.472908, -2.3939195, 8.06839, -21.518867, -2.3769612, 7.7046695, -21.51076, -2.2058134, 7.8594503, -21.472908, -2.3939195, 7.8594503, -21.454199, -2.1750765, 8.138046, -21.629759, -2.3324466, 8.06839, -21.518867, -2.3769612, 8.014231, -21.603243, -2.491922, 7.8594503, -21.472908, -2.3939195, 8.014231, -21.603243, -2.491922, 8.06839, -21.518867, -2.3769612, 8.138046, -21.629759, -2.3324466, 8.014231, -21.603243, -2.491922, 8.06839, -21.696474, -2.5233836, 7.5808525, -21.629759, -2.3324466, 7.6505103, -21.518867, -2.3769612, 7.7046695, -21.51076, -2.2058134, 7.7046695, -21.51076, -2.2058134, 7.6505103, -21.518867, -2.3769612, 7.8594503, -21.472908, -2.3939195, 7.6505103, -21.696474, -2.5233836, 7.6505103, -21.518867, -2.3769612, 7.5808525, -21.629759, -2.3324466, 7.8594503, -21.585764, -2.5543175, 8.06839, -21.696474, -2.5233836, 8.014231, -21.603243, -2.491922, 7.8594503, -21.472908, -2.3939195, 7.8594503, -21.585764, -2.5543175, 8.014231, -21.603243, -2.491922, 7.6505103, -21.518867, -2.3769612, 7.8594503, -21.585764, -2.5543175, 7.8594503, -21.472908, -2.3939195, 7.8594503, -21.585764, -2.5543175, 7.8594503, -21.722357, -2.586853, 8.06839, -21.696474, -2.5233836, 7.6505103, -21.696474, -2.5233836, 7.7046695, -21.603243, -2.491922, 7.6505103, -21.518867, -2.3769612, 7.6505103, -21.518867, -2.3769612, 7.7046695, -21.603243, -2.491922, 7.8594503, -21.585764, -2.5543175, 7.7046695, -21.603243, -2.491922, 7.6505103, -21.696474, -2.5233836, 7.8594503, -21.722357, -2.586853, 7.7046695, -21.603243, -2.491922, 7.8594503, -21.722357, -2.586853, 7.8594503, -21.585764, -2.5543175, -3.1935773, 3.8995895, -1.8595612, -2.9311552, 3.6870894, -1.6457095, -2.9311552, 3.8599355, -1.961901, -3.1935773, 3.8995895, -1.8595612, -3.2854233, 3.7667134, -1.6248901, -2.9311552, 3.6870894, -1.6457095, -2.9311552, 3.8599355, -1.961901, -2.9311552, 3.6870894, -1.6457095, -2.5768757, 3.7667134, -1.6248901, -2.9311552, 3.8599355, -1.961901, -2.5768757, 3.7667134, -1.6248901, -2.6687322, 3.8995895, -1.8595612, -2.9311552, 3.6870894, -1.6457095, -2.6687322, 3.6513314, -1.3056598, -2.5768757, 3.7667134, -1.6248901, -2.9311552, 3.6870894, -1.6457095, -2.9311552, 3.5797672, -1.2413263, -2.6687322, 3.6513314, -1.3056598, -3.2854233, 3.7667134, -1.6248901, -2.9311552, 3.5797672, -1.2413263, -2.9311552, 3.6870894, -1.6457095, -3.2854233, 3.7667134, -1.6248901, -3.1935773, 3.6513314, -1.3056598, -2.9311552, 3.5797672, -1.2413263, -3.085936, 3.6335888, -2.453485, -2.9311552, 3.5589857, -2.2920842, -2.9311552, 3.5909512, -2.5021906, -3.085936, 3.6335888, -2.453485, -3.1401024, 3.6073158, -2.2966275, -2.9311552, 3.5589857, -2.2920842, -2.9311552, 3.5909512, -2.5021906, -2.9311552, 3.5589857, -2.2920842, -2.7222066, 3.6073158, -2.2966275, -2.9311552, 3.5909512, -2.5021906, -2.7222066, 3.6073158, -2.2966275, -2.7763743, 3.6335888, -2.453485, -2.9311552, 3.5589857, -2.2920842, -2.7763743, 3.6077993, -2.0964208, -2.7222066, 3.6073158, -2.2966275, -2.9311552, 3.5589857, -2.2920842, -2.9311552, 3.5811186, -2.0463247, -2.7763743, 3.6077993, -2.0964208, -3.1401024, 3.6073158, -2.2966275, -2.9311552, 3.5811186, -2.0463247, -2.9311552, 3.5589857, -2.2920842, -3.1401024, 3.6073158, -2.2966275, -3.085936, 3.6077993, -2.0964208, -2.9311552, 3.5811186, -2.0463247, -3.1935802, 3.6513338, -1.3056636, -2.9311566, 3.7287047, -1.0824113, -2.9311566, 3.5797653, -1.2413311, -2.9311566, 3.5797653, -1.2413311, -2.9311566, 3.7287047, -1.0824113, -2.576877, 3.7524617, -1.1856871, -3.1935802, 3.6513338, -1.3056636, -3.2854362, 3.7524617, -1.1856871, -2.9311566, 3.7287047, -1.0824113, -2.9311566, 3.5797653, -1.2413311, -2.576877, 3.7524617, -1.1856871, -2.668735, 3.6513338, -1.3056636, -3.2854362, 3.766717, -1.624898, -3.2854362, 3.7524617, -1.1856871, -3.1935802, 3.6513338, -1.3056636, -2.668735, 3.6513338, -1.3056636, -2.576877, 3.7524617, -1.1856871, -2.458784, 3.94386, -1.5800836, -3.2854362, 3.766717, -1.624898, -3.4035149, 3.94386, -1.5800836, -3.2854362, 3.7524617, -1.1856871, -2.668735, 3.6513338, -1.3056636, -2.458784, 3.94386, -1.5800836, -2.576877, 3.766717, -1.624898, -3.1935802, 3.8995967, -1.8595593, -3.4035149, 3.94386, -1.5800836, -3.2854362, 3.766717, -1.624898, -2.576877, 3.766717, -1.624898, -2.458784, 3.94386, -1.5800836, -2.576877, 4.0332108, -1.9111636, -3.1935802, 3.8995967, -1.8595593, -3.2854362, 4.0332108, -1.9111636, -3.4035149, 3.94386, -1.5800836, -2.576877, 3.766717, -1.624898, -2.576877, 4.0332108, -1.9111636, -2.668735, 3.8995967, -1.8595593, -3.2854362, 4.0332108, -1.9111636, -3.1935802, 3.8995967, -1.8595593, -2.9311566, 3.8599317, -1.9618974, -2.9311566, 4.0577416, -2.0248003, -2.668735, 3.8995967, -1.8595593, -2.576877, 4.0332108, -1.9111636, -3.2854362, 4.0332108, -1.9111636, -2.9311566, 3.8599317, -1.9618974, -2.9311566, 4.0577416, -2.0248003, -2.9311566, 4.0577416, -2.0248003, -2.9311566, 3.8599317, -1.9618974, -2.668735, 3.8995967, -1.8595593, -2.9311566, 3.7287047, -1.0824113, -2.668735, 3.9892077, -1.2889729, -2.576877, 3.7524617, -1.1856871, -2.576877, 3.7524617, -1.1856871, -2.668735, 3.9892077, -1.2889729, -2.576877, 4.141674, -1.5362778, -2.9311566, 3.7287047, -1.0824113, -2.9311566, 4.0389485, -1.1918268, -2.668735, 3.9892077, -1.2889729, -2.9311566, 4.0389485, -1.1918268, -2.576877, 4.141674, -1.5362778, -2.668735, 3.9892077, -1.2889729, -2.576877, 3.7524617, -1.1856871, -2.576877, 4.141674, -1.5362778, -2.458784, 3.94386, -1.5800836, -3.2854362, 3.7524617, -1.1856871, -2.9311566, 4.0389485, -1.1918268, -2.9311566, 3.7287047, -1.0824113, -3.2854362, 3.7524617, -1.1856871, -3.1935802, 3.9892077, -1.2889729, -2.9311566, 4.0389485, -1.1918268, -3.4035149, 3.94386, -1.5800836, -3.1935802, 3.9892077, -1.2889729, -3.2854362, 3.7524617, -1.1856871, -2.9311566, 4.0389485, -1.1918268, -2.9311566, 4.222207, -1.5160284, -2.576877, 4.141674, -1.5362778, -3.1935802, 3.9892077, -1.2889729, -2.9311566, 4.222207, -1.5160284, -2.9311566, 4.0389485, -1.1918268, -2.458784, 3.94386, -1.5800836, -2.576877, 4.141674, -1.5362778, -2.668735, 4.1336055, -1.7779188, -2.9311566, 4.222207, -1.5160284, -2.668735, 4.1336055, -1.7779188, -2.576877, 4.141674, -1.5362778, -2.458784, 3.94386, -1.5800836, -2.668735, 4.1336055, -1.7779188, -2.576877, 4.0332108, -1.9111636, -3.4035149, 3.94386, -1.5800836, -3.2854362, 4.141674, -1.5362778, -3.1935802, 3.9892077, -1.2889729, -3.1935802, 3.9892077, -1.2889729, -3.2854362, 4.141674, -1.5362778, -2.9311566, 4.222207, -1.5160284, -3.2854362, 4.0332108, -1.9111636, -3.2854362, 4.141674, -1.5362778, -3.4035149, 3.94386, -1.5800836, -2.9311566, 4.2177896, -1.8485312, -2.576877, 4.0332108, -1.9111636, -2.668735, 4.1336055, -1.7779188, -2.9311566, 4.222207, -1.5160284, -2.9311566, 4.2177896, -1.8485312, -2.668735, 4.1336055, -1.7779188, -3.2854362, 4.141674, -1.5362778, -2.9311566, 4.2177896, -1.8485312, -2.9311566, 4.222207, -1.5160284, -2.9311566, 4.2177896, -1.8485312, -2.9311566, 4.0577416, -2.0248003, -2.576877, 4.0332108, -1.9111636, -3.2854362, 4.0332108, -1.9111636, -3.1935802, 4.1336055, -1.7779188, -3.2854362, 4.141674, -1.5362778, -3.2854362, 4.141674, -1.5362778, -3.1935802, 4.1336055, -1.7779188, -2.9311566, 4.2177896, -1.8485312, -3.1935802, 4.1336055, -1.7779188, -3.2854362, 4.0332108, -1.9111636, -2.9311566, 4.0577416, -2.0248003, -3.1935802, 4.1336055, -1.7779188, -2.9311566, 4.0577416, -2.0248003, -2.9311566, 4.2177896, -1.8485312, -3.0345936, 3.324797, -1.6475012, -2.9311523, 3.431215, -1.5348425, -2.9311523, 3.2713995, -1.6237943, -3.0345936, 3.324797, -1.6475012, -3.0708013, 3.4427567, -1.5799441, -2.9311523, 3.431215, -1.5348425, -3.0708013, 3.3060207, -1.7590866, -3.0345936, 3.324797, -1.6475012, -2.9311523, 3.2713995, -1.6237943, -3.0708013, 3.3060207, -1.7590866, -3.0708013, 3.4427567, -1.5799441, -3.0345936, 3.324797, -1.6475012, -3.0708013, 3.4427567, -1.5799441, -2.9311523, 3.7502108, -1.4900501, -2.9311523, 3.431215, -1.5348425, -2.9311523, 3.431215, -1.5348425, -2.9311523, 3.7502108, -1.4900501, -2.827711, 3.759186, -1.5251231, -3.0708013, 3.4427567, -1.5799441, -3.0345936, 3.759186, -1.5251231, -2.9311523, 3.7502108, -1.4900501, -2.9311523, 3.431215, -1.5348425, -2.827711, 3.759186, -1.5251231, -2.7915034, 3.4427567, -1.5799441, -2.9311523, 3.2713995, -1.6237943, -2.9311523, 3.431215, -1.5348425, -2.7915034, 3.4427567, -1.5799441, -2.7915034, 3.4427567, -1.5799441, -2.827711, 3.759186, -1.5251231, -2.7915034, 3.784829, -1.6253297, -3.1173463, 3.477378, -1.7152362, -3.0345936, 3.759186, -1.5251231, -3.0708013, 3.4427567, -1.5799441, -3.0708013, 3.3060207, -1.7590866, -3.1173463, 3.477378, -1.7152362, -3.0708013, 3.4427567, -1.5799441, -3.1173463, 3.477378, -1.7152362, -3.0708013, 3.784829, -1.6253297, -3.0345936, 3.759186, -1.5251231, -2.7915034, 3.4427567, -1.5799441, -2.7915034, 3.784829, -1.6253297, -2.7449527, 3.477378, -1.7152362, -2.7449527, 3.477378, -1.7152362, -2.7915034, 3.784829, -1.6253297, -2.827711, 3.810475, -1.7255487, -2.9311523, 3.2713995, -1.6237943, -2.7915034, 3.4427567, -1.5799441, -2.827711, 3.324797, -1.6475012, -2.827711, 3.324797, -1.6475012, -2.7915034, 3.4427567, -1.5799441, -2.7449527, 3.477378, -1.7152362, -3.0708013, 3.3060207, -1.7590866, -2.9311523, 3.2713995, -1.6237943, -2.9311523, 3.2488918, -1.773706, -2.9311523, 3.2488918, -1.773706, -2.9311523, 3.2713995, -1.6237943, -2.827711, 3.324797, -1.6475012, -3.0345936, 3.3760855, -1.8479257, -3.1173463, 3.477378, -1.7152362, -3.0708013, 3.3060207, -1.7590866, -3.0345936, 3.3760855, -1.8479257, -3.0708013, 3.3060207, -1.7590866, -2.9311523, 3.2488918, -1.773706, -3.0708013, 3.511996, -1.8505161, -3.0708013, 3.784829, -1.6253297, -3.1173463, 3.477378, -1.7152362, -3.0345936, 3.3760855, -1.8479257, -3.0708013, 3.511996, -1.8505161, -3.1173463, 3.477378, -1.7152362, -3.0708013, 3.511996, -1.8505161, -3.0345936, 3.810475, -1.7255487, -3.0708013, 3.784829, -1.6253297, -3.0345936, 3.3760855, -1.8479257, -2.9311523, 3.2488918, -1.773706, -2.9311523, 3.3406389, -1.8943663, -3.0708013, 3.511996, -1.8505161, -3.0345936, 3.3760855, -1.8479257, -2.9311523, 3.3406389, -1.8943663, -2.9311523, 3.2488918, -1.773706, -2.827711, 3.324797, -1.6475012, -2.7915034, 3.3060207, -1.7590866, -2.9311523, 3.3406389, -1.8943663, -2.9311523, 3.2488918, -1.773706, -2.7915034, 3.3060207, -1.7590866, -2.827711, 3.324797, -1.6475012, -2.7449527, 3.477378, -1.7152362, -2.7915034, 3.3060207, -1.7590866, -2.9311523, 3.3406389, -1.8943663, -2.7915034, 3.3060207, -1.7590866, -2.827711, 3.3760855, -1.8479257, -3.0345936, 3.810475, -1.7255487, -3.0708013, 3.511996, -1.8505161, -2.9311523, 3.5235374, -1.8956175, -3.0708013, 3.511996, -1.8505161, -2.9311523, 3.3406389, -1.8943663, -2.9311523, 3.5235374, -1.8956175, -2.9311523, 3.5235374, -1.8956175, -2.9311523, 3.3406389, -1.8943663, -2.827711, 3.3760855, -1.8479257, -3.0345936, 3.810475, -1.7255487, -2.9311523, 3.5235374, -1.8956175, -2.9311523, 3.8194501, -1.760622, -2.7915034, 3.3060207, -1.7590866, -2.7449527, 3.477378, -1.7152362, -2.7915034, 3.511996, -1.8505161, -2.7915034, 3.3060207, -1.7590866, -2.7915034, 3.511996, -1.8505161, -2.827711, 3.3760855, -1.8479257, -2.9311523, 3.5235374, -1.8956175, -2.827711, 3.3760855, -1.8479257, -2.7915034, 3.511996, -1.8505161, -2.9311523, 3.8194501, -1.760622, -2.9311523, 3.5235374, -1.8956175, -2.7915034, 3.511996, -1.8505161, -2.7449527, 3.477378, -1.7152362, -2.827711, 3.810475, -1.7255487, -2.7915034, 3.511996, -1.8505161, -2.9311523, 3.8194501, -1.760622, -2.7915034, 3.511996, -1.8505161, -2.827711, 3.810475, -1.7255487, -2.9921637, 3.3578458, -2.2199569, -2.9311523, 3.4395587, -2.1790118, -2.9311523, 3.3330348, -2.1960373, -2.9921637, 3.3578458, -2.2199569, -3.0135121, 3.436851, -2.2063227, -2.9311523, 3.4395587, -2.1790118, -3.0135121, 3.3249156, -2.2780056, -2.9921637, 3.3578458, -2.2199569, -2.9311523, 3.3330348, -2.1960373, -3.0135121, 3.3249156, -2.2780056, -3.0135121, 3.436851, -2.2063227, -2.9921637, 3.3578458, -2.2199569, -3.0135121, 3.436851, -2.2063227, -2.9311523, 3.6253839, -2.2185612, -2.9311523, 3.4395587, -2.1790118, -2.9311523, 3.4395587, -2.1790118, -2.9311523, 3.6253839, -2.2185612, -2.870141, 3.6232789, -2.239802, -3.0135121, 3.436851, -2.2063227, -2.9921637, 3.6232789, -2.239802, -2.9311523, 3.6253839, -2.2185612, -2.9311523, 3.4395587, -2.1790118, -2.870141, 3.6232789, -2.239802, -2.8487926, 3.436851, -2.2063227, -2.9311523, 3.3330348, -2.1960373, -2.9311523, 3.4395587, -2.1790118, -2.8487926, 3.436851, -2.2063227, -2.8487926, 3.436851, -2.2063227, -2.870141, 3.6232789, -2.239802, -2.8487926, 3.617262, -2.3005185, -3.0409698, 3.428742, -2.28829, -2.9921637, 3.6232789, -2.239802, -3.0135121, 3.436851, -2.2063227, -3.0135121, 3.3249156, -2.2780056, -3.0409698, 3.428742, -2.28829, -3.0135121, 3.436851, -2.2063227, -3.0409698, 3.428742, -2.28829, -3.0135121, 3.617262, -2.3005185, -2.9921637, 3.6232789, -2.239802, -2.8487926, 3.436851, -2.2063227, -2.8487926, 3.617262, -2.3005185, -2.8213348, 3.428742, -2.28829, -2.8213348, 3.428742, -2.28829, -2.8487926, 3.617262, -2.3005185, -2.870141, 3.611256, -2.3612318, -2.9311523, 3.3330348, -2.1960373, -2.8487926, 3.436851, -2.2063227, -2.870141, 3.3578458, -2.2199569, -2.870141, 3.3578458, -2.2199569, -2.8487926, 3.436851, -2.2063227, -2.8213348, 3.428742, -2.28829, -3.0135121, 3.3249156, -2.2780056, -2.9311523, 3.3330348, -2.1960373, -2.9311523, 3.2903087, -2.2745829, -2.9311523, 3.2903087, -2.2745829, -2.9311523, 3.3330348, -2.1960373, -2.870141, 3.3578458, -2.2199569, -2.9921637, 3.345823, -2.3413863, -3.0409698, 3.428742, -2.28829, -3.0135121, 3.3249156, -2.2780056, -2.9921637, 3.345823, -2.3413863, -3.0135121, 3.3249156, -2.2780056, -2.9311523, 3.2903087, -2.2745829, -3.0135121, 3.4206212, -2.370246, -3.0135121, 3.617262, -2.3005185, -3.0409698, 3.428742, -2.28829, -2.9921637, 3.345823, -2.3413863, -3.0135121, 3.4206212, -2.370246, -3.0409698, 3.428742, -2.28829, -3.0135121, 3.4206212, -2.370246, -2.9921637, 3.611256, -2.3612318, -3.0135121, 3.617262, -2.3005185, -2.9921637, 3.345823, -2.3413863, -2.9311523, 3.2903087, -2.2745829, -2.9311523, 3.316795, -2.359963, -3.0135121, 3.4206212, -2.370246, -2.9921637, 3.345823, -2.3413863, -2.9311523, 3.316795, -2.359963, -2.9311523, 3.2903087, -2.2745829, -2.870141, 3.3578458, -2.2199569, -2.8487926, 3.3249156, -2.2780056, -2.9311523, 3.316795, -2.359963, -2.9311523, 3.2903087, -2.2745829, -2.8487926, 3.3249156, -2.2780056, -2.870141, 3.3578458, -2.2199569, -2.8213348, 3.428742, -2.28829, -2.8487926, 3.3249156, -2.2780056, -2.9311523, 3.316795, -2.359963, -2.8487926, 3.3249156, -2.2780056, -2.870141, 3.345823, -2.3413863, -2.9921637, 3.611256, -2.3612318, -3.0135121, 3.4206212, -2.370246, -2.9311523, 3.4179149, -2.3975697, -3.0135121, 3.4206212, -2.370246, -2.9311523, 3.316795, -2.359963, -2.9311523, 3.4179149, -2.3975697, -2.9311523, 3.4179149, -2.3975697, -2.9311523, 3.316795, -2.359963, -2.870141, 3.345823, -2.3413863, -2.9921637, 3.611256, -2.3612318, -2.9311523, 3.4179149, -2.3975697, -2.9311523, 3.6091409, -2.382474, -2.8487926, 3.3249156, -2.2780056, -2.8213348, 3.428742, -2.28829, -2.8487926, 3.4206212, -2.370246, -2.8487926, 3.3249156, -2.2780056, -2.8487926, 3.4206212, -2.370246, -2.870141, 3.345823, -2.3413863, -2.9311523, 3.4179149, -2.3975697, -2.870141, 3.345823, -2.3413863, -2.8487926, 3.4206212, -2.370246, -2.9311523, 3.6091409, -2.382474, -2.9311523, 3.4179149, -2.3975697, -2.8487926, 3.4206212, -2.370246, -2.8213348, 3.428742, -2.28829, -2.870141, 3.611256, -2.3612318, -2.8487926, 3.4206212, -2.370246, -2.9311523, 3.6091409, -2.382474, -2.8487926, 3.4206212, -2.370246, -2.870141, 3.611256, -2.3612318, -3.0859275, 3.6077986, -2.0964181, -2.9311538, 3.695728, -1.9883108, -2.9311538, 3.5811217, -2.0463195, -2.9311538, 3.5811217, -2.0463195, -2.9311538, 3.695728, -1.9883108, -2.7222037, 3.6880543, -2.0503416, -3.0859275, 3.6077986, -2.0964181, -3.1401038, 3.6880543, -2.0503416, -2.9311538, 3.695728, -1.9883108, -2.9311538, 3.5811217, -2.0463195, -2.7222037, 3.6880543, -2.0503416, -2.77638, 3.6077986, -2.0964181, -3.1401038, 3.6073084, -2.296616, -3.1401038, 3.6880543, -2.0503416, -3.0859275, 3.6077986, -2.0964181, -2.77638, 3.6077986, -2.0964181, -2.7222037, 3.6880543, -2.0503416, -2.652553, 3.714524, -2.3075376, -3.1401038, 3.6073084, -2.296616, -3.209746, 3.714524, -2.3075376, -3.1401038, 3.6880543, -2.0503416, -2.77638, 3.6077986, -2.0964181, -2.652553, 3.714524, -2.3075376, -2.7222037, 3.6073084, -2.296616, -3.0859275, 3.6335883, -2.4534836, -3.209746, 3.714524, -2.3075376, -3.1401038, 3.6073084, -2.296616, -2.7222037, 3.6073084, -2.296616, -2.652553, 3.714524, -2.3075376, -2.7222037, 3.697219, -2.509048, -3.0859275, 3.6335883, -2.4534836, -3.1401038, 3.697219, -2.509048, -3.209746, 3.714524, -2.3075376, -2.7222037, 3.6073084, -2.296616, -2.7222037, 3.697219, -2.509048, -2.77638, 3.6335883, -2.4534836, -3.1401038, 3.697219, -2.509048, -3.0859275, 3.6335883, -2.4534836, -2.9311538, 3.5909529, -2.5021973, -2.9311538, 3.687878, -2.5769744, -2.77638, 3.6335883, -2.4534836, -2.7222037, 3.697219, -2.509048, -3.1401038, 3.697219, -2.509048, -2.9311538, 3.5909529, -2.5021973, -2.9311538, 3.687878, -2.5769744, -2.9311538, 3.687878, -2.5769744, -2.9311538, 3.5909529, -2.5021973, -2.77638, 3.6335883, -2.4534836, -2.9311538, 3.695728, -1.9883108, -2.77638, 3.7984102, -2.1553574, -2.7222037, 3.6880543, -2.0503416, -2.7222037, 3.6880543, -2.0503416, -2.77638, 3.7984102, -2.1553574, -2.7222037, 3.8329904, -2.3231802, -2.9311538, 3.695728, -1.9883108, -2.9311538, 3.845578, -2.1115613, -2.77638, 3.7984102, -2.1553574, -2.9311538, 3.845578, -2.1115613, -2.7222037, 3.8329904, -2.3231802, -2.77638, 3.7984102, -2.1553574, -2.7222037, 3.6880543, -2.0503416, -2.7222037, 3.8329904, -2.3231802, -2.652553, 3.714524, -2.3075376, -3.1401038, 3.6880543, -2.0503416, -2.9311538, 3.845578, -2.1115613, -2.9311538, 3.695728, -1.9883108, -3.1401038, 3.6880543, -2.0503416, -3.0859275, 3.7984102, -2.1553574, -2.9311538, 3.845578, -2.1115613, -3.209746, 3.714524, -2.3075376, -3.0859275, 3.7984102, -2.1553574, -3.1401038, 3.6880543, -2.0503416, -2.9311538, 3.845578, -2.1115613, -2.9311538, 3.8817086, -2.3282132, -2.7222037, 3.8329904, -2.3231802, -3.0859275, 3.7984102, -2.1553574, -2.9311538, 3.8817086, -2.3282132, -2.9311538, 3.845578, -2.1115613, -2.652553, 3.714524, -2.3075376, -2.7222037, 3.8329904, -2.3231802, -2.77638, 3.7797475, -2.4554696, -2.9311538, 3.8817086, -2.3282132, -2.77638, 3.7797475, -2.4554696, -2.7222037, 3.8329904, -2.3231802, -2.652553, 3.714524, -2.3075376, -2.77638, 3.7797475, -2.4554696, -2.7222037, 3.697219, -2.509048, -3.209746, 3.714524, -2.3075376, -3.1401038, 3.8329904, -2.3231802, -3.0859275, 3.7984102, -2.1553574, -3.0859275, 3.7984102, -2.1553574, -3.1401038, 3.8329904, -2.3231802, -2.9311538, 3.8817086, -2.3282132, -3.1401038, 3.697219, -2.509048, -3.1401038, 3.8329904, -2.3231802, -3.209746, 3.714524, -2.3075376, -2.9311538, 3.812152, -2.5115852, -2.7222037, 3.697219, -2.509048, -2.77638, 3.7797475, -2.4554696, -2.9311538, 3.8817086, -2.3282132, -2.9311538, 3.812152, -2.5115852, -2.77638, 3.7797475, -2.4554696, -3.1401038, 3.8329904, -2.3231802, -2.9311538, 3.812152, -2.5115852, -2.9311538, 3.8817086, -2.3282132, -2.9311538, 3.812152, -2.5115852, -2.9311538, 3.687878, -2.5769744, -2.7222037, 3.697219, -2.509048, -3.1401038, 3.697219, -2.509048, -3.0859275, 3.7797475, -2.4554696, -3.1401038, 3.8329904, -2.3231802, -3.1401038, 3.8329904, -2.3231802, -3.0859275, 3.7797475, -2.4554696, -2.9311538, 3.812152, -2.5115852, -3.0859275, 3.7797475, -2.4554696, -3.1401038, 3.697219, -2.509048, -2.9311538, 3.687878, -2.5769744, -3.0859275, 3.7797475, -2.4554696, -2.9311538, 3.687878, -2.5769744, -2.9311538, 3.812152, -2.5115852, -16.306217, 3.8995857, -8.693367, -16.639778, 3.6870856, -8.751112, -16.484772, 3.8599317, -8.475521, -16.306217, 3.8995857, -8.693367, -16.341208, 3.7667093, -8.942932, -16.639778, 3.6870856, -8.751112, -16.484772, 3.8599317, -8.475521, -16.639778, 3.6870856, -8.751112, -16.958773, 3.7667096, -8.595579, -16.484772, 3.8599317, -8.475521, -16.958773, 3.7667096, -8.595579, -16.763668, 3.8995857, -8.436071, -16.639778, 3.6870856, -8.751112, -17.035208, 3.6513274, -8.918848, -16.958773, 3.7667096, -8.595579, -16.639778, 3.6870856, -8.751112, -16.83802, 3.579763, -9.103569, -17.035208, 3.6513274, -8.918848, -16.341208, 3.7667093, -8.942932, -16.83802, 3.579763, -9.103569, -16.639778, 3.6870856, -8.751112, -16.341208, 3.7667093, -8.942932, -16.577757, 3.6513274, -9.176144, -16.83802, 3.579763, -9.103569, -16.108875, 3.6335852, -8.12294, -16.322905, 3.5589821, -8.1877365, -16.219904, 3.5909476, -8.00461, -16.108875, 3.6335852, -8.12294, -16.138561, 3.6073122, -8.286209, -16.322905, 3.5589821, -8.1877365, -16.219904, 3.5909476, -8.00461, -16.322905, 3.5589821, -8.1877365, -16.502796, 3.6073122, -8.081343, -16.219904, 3.5909476, -8.00461, -16.502796, 3.6073122, -8.081343, -16.378687, 3.6335852, -7.9711823, -16.322905, 3.5589821, -8.1877365, -16.553732, 3.6077955, -8.282396, -16.502796, 3.6073122, -8.081343, -16.322905, 3.5589821, -8.1877365, -16.443384, 3.5811148, -8.401939, -16.553732, 3.6077955, -8.282396, -16.138561, 3.6073122, -8.286209, -16.443384, 3.5811148, -8.401939, -16.322905, 3.5589821, -8.1877365, -16.138561, 3.6073122, -8.286209, -16.28392, 3.6077955, -8.4341545, -16.443384, 3.5811148, -8.401939, -16.577753, 3.6513298, -9.176142, -16.915924, 3.7287004, -9.242079, -16.838017, 3.5797613, -9.103565, -16.838017, 3.5797613, -9.103565, -16.915924, 3.7287004, -9.242079, -17.174082, 3.7524576, -8.978384, -16.577753, 3.6513298, -9.176142, -16.556509, 3.7524576, -9.325743, -16.915924, 3.7287004, -9.242079, -16.838017, 3.5797613, -9.103565, -17.174082, 3.7524576, -8.978384, -17.035204, 3.6513298, -8.918846, -16.341192, 3.766713, -8.94293, -16.556509, 3.7524576, -9.325743, -16.577753, 3.6513298, -9.176142, -17.035204, 3.6513298, -8.918846, -17.174082, 3.7524576, -8.978384, -17.083666, 3.943856, -8.576738, -16.341192, 3.766713, -8.94293, -16.260246, 3.943856, -9.039877, -16.556509, 3.7524576, -9.325743, -17.035204, 3.6513298, -8.918846, -17.083666, 3.943856, -8.576738, -16.958767, 3.766713, -8.5955715, -16.306215, 3.8995926, -8.693371, -16.260246, 3.943856, -9.039877, -16.341192, 3.766713, -8.94293, -16.958767, 3.766713, -8.5955715, -17.083666, 3.943856, -8.576738, -16.81843, 4.033207, -8.3460655, -16.306215, 3.8995926, -8.693371, -16.200857, 4.033207, -8.693424, -16.260246, 3.943856, -9.039877, -16.958767, 3.766713, -8.5955715, -16.81843, 4.033207, -8.3460655, -16.763666, 3.8995926, -8.436075, -16.200857, 4.033207, -8.693424, -16.306215, 3.8995926, -8.693371, -16.484772, 3.859928, -8.475525, -16.453936, 4.057738, -8.4207, -16.763666, 3.8995926, -8.436075, -16.81843, 4.033207, -8.3460655, -16.200857, 4.033207, -8.693424, -16.484772, 3.859928, -8.475525, -16.453936, 4.057738, -8.4207, -16.453936, 4.057738, -8.4207, -16.484772, 3.859928, -8.475525, -16.763666, 3.8995926, -8.436075, -16.915924, 3.7287004, -9.242079, -17.043386, 3.9892037, -8.9333935, -17.174082, 3.7524576, -8.978384, -17.174082, 3.7524576, -8.978384, -17.043386, 3.9892037, -8.9333935, -17.00221, 4.14167, -8.672813, -16.915924, 3.7287004, -9.242079, -16.862286, 4.0389442, -9.146713, -17.043386, 3.9892037, -8.9333935, -16.862286, 4.0389442, -9.146713, -17.00221, 4.14167, -8.672813, -17.043386, 3.9892037, -8.9333935, -17.174082, 3.7524576, -8.978384, -17.00221, 4.14167, -8.672813, -17.083666, 3.943856, -8.576738, -16.556509, 3.7524576, -9.325743, -16.862286, 4.0389442, -9.146713, -16.915924, 3.7287004, -9.242079, -16.556509, 3.7524576, -9.325743, -16.585936, 3.9892037, -9.190689, -16.862286, 4.0389442, -9.146713, -16.260246, 3.943856, -9.039877, -16.585936, 3.9892037, -9.190689, -16.556509, 3.7524576, -9.325743, -16.862286, 4.0389442, -9.146713, -16.703352, 4.222203, -8.864141, -17.00221, 4.14167, -8.672813, -16.585936, 3.9892037, -9.190689, -16.703352, 4.222203, -8.864141, -16.862286, 4.0389442, -9.146713, -17.083666, 3.943856, -8.576738, -17.00221, 4.14167, -8.672813, -16.803688, 4.1336017, -8.507233, -16.703352, 4.222203, -8.864141, -16.803688, 4.1336017, -8.507233, -17.00221, 4.14167, -8.672813, -17.083666, 3.943856, -8.576738, -16.803688, 4.1336017, -8.507233, -16.81843, 4.033207, -8.3460655, -16.260246, 3.943856, -9.039877, -16.384638, 4.14167, -9.020172, -16.585936, 3.9892037, -9.190689, -16.585936, 3.9892037, -9.190689, -16.384638, 4.14167, -9.020172, -16.703352, 4.222203, -8.864141, -16.200857, 4.033207, -8.693424, -16.384638, 4.14167, -9.020172, -16.260246, 3.943856, -9.039877, -16.540348, 4.217786, -8.574335, -16.81843, 4.033207, -8.3460655, -16.803688, 4.1336017, -8.507233, -16.703352, 4.222203, -8.864141, -16.540348, 4.217786, -8.574335, -16.803688, 4.1336017, -8.507233, -16.384638, 4.14167, -9.020172, -16.540348, 4.217786, -8.574335, -16.703352, 4.222203, -8.864141, -16.540348, 4.217786, -8.574335, -16.453936, 4.057738, -8.4207, -16.81843, 4.033207, -8.3460655, -16.200857, 4.033207, -8.693424, -16.34624, 4.1336017, -8.764528, -16.384638, 4.14167, -9.020172, -16.384638, 4.14167, -9.020172, -16.34624, 4.1336017, -8.764528, -16.540348, 4.217786, -8.574335, -16.34624, 4.1336017, -8.764528, -16.200857, 4.033207, -8.693424, -16.453936, 4.057738, -8.4207, -16.34624, 4.1336017, -8.764528, -16.453936, 4.057738, -8.4207, -16.540348, 4.217786, -8.574335, -16.548744, 3.324793, -8.800259, -16.694132, 3.4312112, -8.84774, -16.650524, 3.2713957, -8.77021, -16.548744, 3.324793, -8.800259, -16.550304, 3.4427526, -8.87689, -16.694132, 3.4312112, -8.84774, -16.462482, 3.306017, -8.720753, -16.548744, 3.324793, -8.800259, -16.650524, 3.2713957, -8.77021, -16.462482, 3.306017, -8.720753, -16.550304, 3.4427526, -8.87689, -16.548744, 3.324793, -8.800259, -16.550304, 3.4427526, -8.87689, -16.716091, 3.750207, -8.886782, -16.694132, 3.4312112, -8.84774, -16.694132, 3.4312112, -8.84774, -16.716091, 3.750207, -8.886782, -16.789055, 3.759182, -8.805502, -16.550304, 3.4427526, -8.87689, -16.608738, 3.759182, -8.906923, -16.716091, 3.750207, -8.886782, -16.694132, 3.4312112, -8.84774, -16.789055, 3.759182, -8.805502, -16.793737, 3.4427526, -8.739971, -16.650524, 3.2713957, -8.77021, -16.694132, 3.4312112, -8.84774, -16.793737, 3.4427526, -8.739971, -16.793737, 3.4427526, -8.739971, -16.789055, 3.759182, -8.805502, -16.771488, 3.784825, -8.700413, -16.443413, 3.477374, -8.78179, -16.608738, 3.759182, -8.906923, -16.550304, 3.4427526, -8.87689, -16.462482, 3.306017, -8.720753, -16.443413, 3.477374, -8.78179, -16.550304, 3.4427526, -8.87689, -16.443413, 3.477374, -8.78179, -16.528055, 3.7848248, -8.837334, -16.608738, 3.759182, -8.906923, -16.793737, 3.4427526, -8.739971, -16.771488, 3.784825, -8.700413, -16.767986, 3.477374, -8.59923, -16.767986, 3.477374, -8.59923, -16.771488, 3.784825, -8.700413, -16.6908, 3.810471, -8.630812, -16.650524, 3.2713957, -8.77021, -16.793737, 3.4427526, -8.739971, -16.729061, 3.324793, -8.698837, -16.729061, 3.324793, -8.698837, -16.793737, 3.4427526, -8.739971, -16.767986, 3.477374, -8.59923, -16.462482, 3.306017, -8.720753, -16.650524, 3.2713957, -8.77021, -16.577034, 3.2488883, -8.639549, -16.577034, 3.2488883, -8.639549, -16.650524, 3.2713957, -8.77021, -16.729061, 3.324793, -8.698837, -16.450489, 3.3760817, -8.62557, -16.443413, 3.477374, -8.78179, -16.462482, 3.306017, -8.720753, -16.450489, 3.3760817, -8.62557, -16.462482, 3.306017, -8.720753, -16.577034, 3.2488883, -8.639549, -16.417662, 3.5119922, -8.641064, -16.528055, 3.7848248, -8.837334, -16.443413, 3.477374, -8.78179, -16.450489, 3.3760817, -8.62557, -16.417662, 3.5119922, -8.641064, -16.443413, 3.477374, -8.78179, -16.417662, 3.5119922, -8.641064, -16.510483, 3.810471, -8.732233, -16.528055, 3.7848248, -8.837334, -16.450489, 3.3760817, -8.62557, -16.577034, 3.2488883, -8.639549, -16.517881, 3.340635, -8.534382, -16.417662, 3.5119922, -8.641064, -16.450489, 3.3760817, -8.62557, -16.517881, 3.340635, -8.534382, -16.577034, 3.2488883, -8.639549, -16.729061, 3.324793, -8.698837, -16.705917, 3.306017, -8.583832, -16.517881, 3.340635, -8.534382, -16.577034, 3.2488883, -8.639549, -16.705917, 3.306017, -8.583832, -16.729061, 3.324793, -8.698837, -16.767986, 3.477374, -8.59923, -16.705917, 3.306017, -8.583832, -16.517881, 3.340635, -8.534382, -16.705917, 3.306017, -8.583832, -16.630806, 3.3760817, -8.524149, -16.510483, 3.810471, -8.732233, -16.417662, 3.5119922, -8.641064, -16.517267, 3.5235336, -8.533293, -16.417662, 3.5119922, -8.641064, -16.517881, 3.340635, -8.534382, -16.517267, 3.5235336, -8.533293, -16.517267, 3.5235336, -8.533293, -16.517881, 3.340635, -8.534382, -16.630806, 3.3760817, -8.524149, -16.510483, 3.810471, -8.732233, -16.517267, 3.5235336, -8.533293, -16.583447, 3.8194463, -8.650953, -16.705917, 3.306017, -8.583832, -16.767986, 3.477374, -8.59923, -16.661095, 3.5119922, -8.504143, -16.705917, 3.306017, -8.583832, -16.661095, 3.5119922, -8.504143, -16.630806, 3.3760817, -8.524149, -16.517267, 3.5235336, -8.533293, -16.630806, 3.3760817, -8.524149, -16.661095, 3.5119922, -8.504143, -16.583447, 3.8194463, -8.650953, -16.517267, 3.5235336, -8.533293, -16.661095, 3.5119922, -8.504143, -16.767986, 3.477374, -8.59923, -16.6908, 3.810471, -8.630812, -16.661095, 3.5119922, -8.504143, -16.583447, 3.8194463, -8.650953, -16.661095, 3.5119922, -8.504143, -16.6908, 3.810471, -8.630812, -16.30509, 3.3578424, -8.28051, -16.37834, 3.4395552, -8.286289, -16.369993, 3.3330312, -8.271448, -16.30509, 3.3578424, -8.28051, -16.293167, 3.4368472, -8.30286, -16.37834, 3.4395552, -8.286289, -16.258024, 3.324912, -8.240381, -16.30509, 3.3578424, -8.28051, -16.369993, 3.3330312, -8.271448, -16.258024, 3.324912, -8.240381, -16.293167, 3.4368472, -8.30286, -16.30509, 3.3578424, -8.28051, -16.293167, 3.4368472, -8.30286, -16.358952, 3.62538, -8.251818, -16.37834, 3.4395552, -8.286289, -16.37834, 3.4395552, -8.286289, -16.358952, 3.62538, -8.251818, -16.401714, 3.6232753, -8.203394, -16.293167, 3.4368472, -8.30286, -16.29536, 3.6232753, -8.263214, -16.358952, 3.62538, -8.251818, -16.37834, 3.4395552, -8.286289, -16.401714, 3.6232753, -8.203394, -16.436735, 3.4368472, -8.222109, -16.369993, 3.3330312, -8.271448, -16.37834, 3.4395552, -8.286289, -16.436735, 3.4368472, -8.222109, -16.436735, 3.4368472, -8.222109, -16.401714, 3.6232753, -8.203394, -16.390556, 3.6172583, -8.140009, -16.229052, 3.4287384, -8.244879, -16.29536, 3.6232753, -8.263214, -16.293167, 3.4368472, -8.30286, -16.258024, 3.324912, -8.240381, -16.229052, 3.4287384, -8.244879, -16.293167, 3.4368472, -8.30286, -16.229052, 3.4287384, -8.244879, -16.246988, 3.6172583, -8.22076, -16.29536, 3.6232753, -8.263214, -16.436735, 3.4368472, -8.222109, -16.390556, 3.6172583, -8.140009, -16.420483, 3.4287384, -8.137205, -16.420483, 3.4287384, -8.137205, -16.390556, 3.6172583, -8.140009, -16.342186, 3.6112523, -8.097557, -16.369993, 3.3330312, -8.271448, -16.436735, 3.4368472, -8.222109, -16.411444, 3.3578424, -8.220692, -16.411444, 3.3578424, -8.220692, -16.436735, 3.4368472, -8.222109, -16.420483, 3.4287384, -8.137205, -16.258024, 3.324912, -8.240381, -16.369993, 3.3330312, -8.271448, -16.331488, 3.2903051, -8.20299, -16.331488, 3.2903051, -8.20299, -16.369993, 3.3330312, -8.271448, -16.411444, 3.3578424, -8.220692, -16.245562, 3.3458195, -8.174673, -16.229052, 3.4287384, -8.244879, -16.258024, 3.324912, -8.240381, -16.245562, 3.3458195, -8.174673, -16.258024, 3.324912, -8.240381, -16.331488, 3.2903051, -8.20299, -16.212807, 3.4206176, -8.1599865, -16.246988, 3.6172583, -8.22076, -16.229052, 3.4287384, -8.244879, -16.245562, 3.3458195, -8.174673, -16.212807, 3.4206176, -8.1599865, -16.229052, 3.4287384, -8.244879, -16.212807, 3.4206176, -8.1599865, -16.235832, 3.6112523, -8.157377, -16.246988, 3.6172583, -8.22076, -16.245562, 3.3458195, -8.174673, -16.331488, 3.2903051, -8.20299, -16.28963, 3.3167918, -8.128572, -16.212807, 3.4206176, -8.1599865, -16.245562, 3.3458195, -8.174673, -16.28963, 3.3167918, -8.128572, -16.331488, 3.2903051, -8.20299, -16.411444, 3.3578424, -8.220692, -16.401594, 3.324912, -8.15963, -16.28963, 3.3167918, -8.128572, -16.331488, 3.2903051, -8.20299, -16.401594, 3.324912, -8.15963, -16.411444, 3.3578424, -8.220692, -16.420483, 3.4287384, -8.137205, -16.401594, 3.324912, -8.15963, -16.28963, 3.3167918, -8.128572, -16.401594, 3.324912, -8.15963, -16.351915, 3.3458195, -8.114855, -16.235832, 3.6112523, -8.157377, -16.212807, 3.4206176, -8.1599865, -16.271194, 3.4179115, -8.095795, -16.212807, 3.4206176, -8.1599865, -16.28963, 3.3167918, -8.128572, -16.271194, 3.4179115, -8.095795, -16.271194, 3.4179115, -8.095795, -16.28963, 3.3167918, -8.128572, -16.351915, 3.3458195, -8.114855, -16.235832, 3.6112523, -8.157377, -16.271194, 3.4179115, -8.095795, -16.278595, 3.6091375, -8.108952, -16.401594, 3.324912, -8.15963, -16.420483, 3.4287384, -8.137205, -16.356375, 3.4206176, -8.079235, -16.401594, 3.324912, -8.15963, -16.356375, 3.4206176, -8.079235, -16.351915, 3.3458195, -8.114855, -16.271194, 3.4179115, -8.095795, -16.351915, 3.3458195, -8.114855, -16.356375, 3.4206176, -8.079235, -16.278595, 3.6091375, -8.108952, -16.271194, 3.4179115, -8.095795, -16.356375, 3.4206176, -8.079235, -16.420483, 3.4287384, -8.137205, -16.342186, 3.6112523, -8.097557, -16.356375, 3.4206176, -8.079235, -16.278595, 3.6091375, -8.108952, -16.356375, 3.4206176, -8.079235, -16.342186, 3.6112523, -8.097557, -16.283928, 3.6077948, -8.434153, -16.471827, 3.6957242, -8.452503, -16.443388, 3.5811179, -8.401943, -16.443388, 3.5811179, -8.401943, -16.471827, 3.6957242, -8.452503, -16.623535, 3.6880505, -8.296003, -16.283928, 3.6077948, -8.434153, -16.259296, 3.6880505, -8.500872, -16.471827, 3.6957242, -8.452503, -16.443388, 3.5811179, -8.401943, -16.623535, 3.6880505, -8.296003, -16.553728, 3.6077948, -8.282402, -16.138565, 3.6073048, -8.286221, -16.259296, 3.6880505, -8.500872, -16.283928, 3.6077948, -8.434153, -16.553728, 3.6077948, -8.282402, -16.623535, 3.6880505, -8.296003, -16.558157, 3.7145205, -8.037687, -16.138565, 3.6073048, -8.286221, -16.072512, 3.7145205, -8.3108425, -16.259296, 3.6880505, -8.500872, -16.553728, 3.6077948, -8.282402, -16.558157, 3.7145205, -8.037687, -16.502804, 3.6073048, -8.081352, -16.108883, 3.633585, -8.122936, -16.072512, 3.7145205, -8.3108425, -16.138565, 3.6073048, -8.286221, -16.502804, 3.6073048, -8.081352, -16.558157, 3.7145205, -8.037687, -16.398663, 3.6972153, -7.896198, -16.108883, 3.633585, -8.122936, -16.034424, 3.6972153, -8.101067, -16.072512, 3.7145205, -8.3108425, -16.502804, 3.6073048, -8.081352, -16.398663, 3.6972153, -7.896198, -16.378683, 3.633585, -7.971186, -16.034424, 3.6972153, -8.101067, -16.108883, 3.633585, -8.122936, -16.219902, 3.5909493, -8.004602, -16.183245, 3.6878743, -7.939428, -16.378683, 3.633585, -7.971186, -16.398663, 3.6972153, -7.896198, -16.034424, 3.6972153, -8.101067, -16.219902, 3.5909493, -8.004602, -16.183245, 3.6878743, -7.939428, -16.183245, 3.6878743, -7.939428, -16.219902, 3.5909493, -8.004602, -16.378683, 3.633585, -7.971186, -16.471827, 3.6957242, -8.452503, -16.524834, 3.7984066, -8.231031, -16.623535, 3.6880505, -8.296003, -16.623535, 3.6880505, -8.296003, -16.524834, 3.7984066, -8.231031, -16.48978, 3.8329868, -8.058199, -16.471827, 3.6957242, -8.452503, -16.411404, 3.8455741, -8.3450775, -16.524834, 3.7984066, -8.231031, -16.411404, 3.8455741, -8.3450775, -16.48978, 3.8329868, -8.058199, -16.524834, 3.7984066, -8.231031, -16.623535, 3.6880505, -8.296003, -16.48978, 3.8329868, -8.058199, -16.558157, 3.7145205, -8.037687, -16.259296, 3.6880505, -8.500872, -16.411404, 3.8455741, -8.3450775, -16.471827, 3.6957242, -8.452503, -16.259296, 3.6880505, -8.500872, -16.255035, 3.7984066, -8.382782, -16.411404, 3.8455741, -8.3450775, -16.072512, 3.7145205, -8.3108425, -16.255035, 3.7984066, -8.382782, -16.259296, 3.6880505, -8.500872, -16.411404, 3.8455741, -8.3450775, -16.305195, 3.881705, -8.156246, -16.48978, 3.8329868, -8.058199, -16.255035, 3.7984066, -8.382782, -16.305195, 3.881705, -8.156246, -16.411404, 3.8455741, -8.3450775, -16.558157, 3.7145205, -8.037687, -16.48978, 3.8329868, -8.058199, -16.377708, 3.779744, -7.9694552, -16.305195, 3.881705, -8.156246, -16.377708, 3.779744, -7.9694552, -16.48978, 3.8329868, -8.058199, -16.558157, 3.7145205, -8.037687, -16.377708, 3.779744, -7.9694552, -16.398663, 3.6972153, -7.896198, -16.072512, 3.7145205, -8.3108425, -16.125544, 3.8329868, -8.263067, -16.255035, 3.7984066, -8.382782, -16.255035, 3.7984066, -8.382782, -16.125544, 3.8329868, -8.263067, -16.305195, 3.881705, -8.156246, -16.034424, 3.6972153, -8.101067, -16.125544, 3.8329868, -8.263067, -16.072512, 3.7145205, -8.3108425, -16.2153, 3.8121483, -7.9964204, -16.398663, 3.6972153, -7.896198, -16.377708, 3.779744, -7.9694552, -16.305195, 3.881705, -8.156246, -16.2153, 3.8121483, -7.9964204, -16.377708, 3.779744, -7.9694552, -16.125544, 3.8329868, -8.263067, -16.2153, 3.8121483, -7.9964204, -16.305195, 3.881705, -8.156246, -16.2153, 3.8121483, -7.9964204, -16.183245, 3.6878743, -7.939428, -16.398663, 3.6972153, -7.896198, -16.034424, 3.6972153, -8.101067, -16.10791, 3.779744, -8.121206, -16.125544, 3.8329868, -8.263067, -16.125544, 3.8329868, -8.263067, -16.10791, 3.779744, -8.121206, -16.2153, 3.8121483, -7.9964204, -16.10791, 3.779744, -8.121206, -16.034424, 3.6972153, -8.101067, -16.183245, 3.6878743, -7.939428, -16.10791, 3.779744, -8.121206, -16.183245, 3.6878743, -7.939428, -16.2153, 3.8121483, -7.9964204, 17.25302, -20.0689, 7.978983, 17.227175, -19.73569, 9.261202, 17.026821, -20.231873, 8.073179, 17.25302, -20.0689, 7.978983, 17.434576, -19.60518, 9.127918, 17.227175, -19.73569, 9.261202, 17.434576, -19.60518, 9.127918, 17.571705, -18.471197, 10.415478, 17.227175, -19.73569, 9.261202, 17.434576, -19.60518, 9.127918, 17.629164, -18.471624, 10.358019, 17.571705, -18.471197, 10.415478, 17.227175, -19.73569, 9.261202, 17.571705, -18.471197, 10.415478, 17.608658, -18.470556, 10.487459, 17.026821, -20.231873, 8.073179, 17.227175, -19.73569, 9.261202, 17.33739, -19.946936, 9.406448, 17.227175, -19.73569, 9.261202, 17.608658, -18.470556, 10.487459, 17.33739, -19.946936, 9.406448, 17.026821, -20.231873, 8.073179, 17.33739, -19.946936, 9.406448, 17.13127, -20.495665, 8.1509285, 17.33739, -19.946936, 9.406448, 17.608658, -18.470556, 10.487459, 17.688543, -18.470556, 10.474857, 17.13127, -20.495665, 8.1509285, 17.33739, -19.946936, 9.406448, 17.61293, -19.946936, 9.362874, 17.33739, -19.946936, 9.406448, 17.688543, -18.470556, 10.474857, 17.61293, -19.946936, 9.362874, 17.13127, -20.495665, 8.1509285, 17.61293, -19.946936, 9.362874, 17.42176, -20.495665, 8.105005, 17.61293, -19.946936, 9.362874, 17.688543, -18.470556, 10.474857, 17.701359, -18.471197, 10.394972, 17.42176, -20.495665, 8.105005, 17.61293, -19.946936, 9.362874, 17.67295, -19.73569, 9.190716, 17.61293, -19.946936, 9.362874, 17.701359, -18.471197, 10.394972, 17.67295, -19.73569, 9.190716, 17.67295, -19.73569, 9.190716, 17.701359, -18.471197, 10.394972, 17.629164, -18.471624, 10.358019, 17.42176, -20.495665, 8.105005, 17.67295, -19.73569, 9.190716, 17.49716, -20.231873, 7.9988475, 17.67295, -19.73569, 9.190716, 17.629164, -18.471624, 10.358019, 17.434576, -19.60518, 9.127918, 17.49716, -20.231873, 7.9988475, 17.67295, -19.73569, 9.190716, 17.434576, -19.60518, 9.127918, 17.49716, -20.231873, 7.9988475, 17.434576, -19.60518, 9.127918, 17.25302, -20.0689, 7.978983, 16.28308, -22.030996, 7.9881682, 17.282068, -19.973848, 8.21394, 16.7107, -22.030996, 8.612511, 16.28308, -22.030996, 7.9881682, 17.120804, -19.944586, 7.988168, 17.282068, -19.973848, 8.21394, 17.120804, -19.944586, 7.988168, 17.36345, -17.90773, 8.087916, 17.282068, -19.973848, 8.21394, 17.120804, -19.944586, 7.988168, 17.291254, -17.900042, 7.988167, 17.36345, -17.90773, 8.087916, 17.282068, -19.973848, 8.21394, 17.36345, -17.90773, 8.087916, 17.480072, -17.920118, 8.049683, 16.7107, -22.030996, 8.612511, 17.282068, -19.973848, 8.21394, 17.543297, -20.021267, 8.1276455, 17.282068, -19.973848, 8.21394, 17.480072, -17.920118, 8.049683, 17.543297, -20.021267, 8.1276455, 16.7107, -22.030996, 8.612511, 17.543297, -20.021267, 8.1276455, 17.402325, -22.030996, 8.373923, 17.543297, -20.021267, 8.1276455, 17.480072, -17.920118, 8.049683, 17.480072, -17.920118, 7.9264374, 17.402325, -22.030996, 8.373923, 17.543297, -20.021267, 8.1276455, 17.543297, -20.021267, 7.848689, 17.543297, -20.021267, 8.1276455, 17.480072, -17.920118, 7.9264374, 17.543297, -20.021267, 7.848689, 17.402325, -22.030996, 8.373923, 17.543297, -20.021267, 7.848689, 17.402325, -22.030996, 7.6021996, 17.543297, -20.021267, 7.848689, 17.480072, -17.920118, 7.9264374, 17.36345, -17.90773, 7.8884172, 17.402325, -22.030996, 7.6021996, 17.543297, -20.021267, 7.848689, 17.282068, -19.973848, 7.7623963, 17.543297, -20.021267, 7.848689, 17.36345, -17.90773, 7.8884172, 17.282068, -19.973848, 7.7623963, 17.282068, -19.973848, 7.7623963, 17.36345, -17.90773, 7.8884172, 17.291254, -17.900042, 7.988167, 17.402325, -22.030996, 7.6021996, 17.282068, -19.973848, 7.7623963, 16.7107, -22.030996, 7.363826, 17.282068, -19.973848, 7.7623963, 17.291254, -17.900042, 7.988167, 17.120804, -19.944586, 7.988168, 16.7107, -22.030996, 7.363826, 17.282068, -19.973848, 7.7623963, 17.120804, -19.944586, 7.988168, 16.7107, -22.030996, 7.363826, 17.120804, -19.944586, 7.988168, 16.28308, -22.030996, 7.9881682, 17.19428, -19.80297, 7.6028395, 18.323992, -19.463995, 6.9496617, 17.395275, -19.968937, 7.7500076, 17.19428, -19.80297, 7.6028395, 18.098648, -19.331137, 6.8390183, 18.323992, -19.463995, 6.9496617, 18.098648, -19.331137, 6.8390183, 19.147192, -18.177076, 6.0412354, 18.323992, -19.463995, 6.9496617, 18.098648, -19.331137, 6.8390183, 19.067095, -18.177504, 6.0213714, 19.147192, -18.177076, 6.0412354, 18.323992, -19.463995, 6.9496617, 19.147192, -18.177076, 6.0412354, 19.190765, -18.176435, 5.9711757, 17.395275, -19.968937, 7.7500076, 18.323992, -19.463995, 6.9496617, 18.392344, -19.679087, 6.777076, 18.323992, -19.463995, 6.9496617, 19.190765, -18.176435, 5.9711757, 18.392344, -19.679087, 6.777076, 17.395275, -19.968937, 7.7500076, 18.392344, -19.679087, 6.777076, 17.40809, -20.237427, 7.6180053, 18.392344, -19.679087, 6.777076, 19.190765, -18.176435, 5.9711757, 19.137367, -18.176435, 5.9081645, 17.40809, -20.237427, 7.6180053, 18.392344, -19.679087, 6.777076, 18.209078, -19.679087, 6.560062, 18.392344, -19.679087, 6.777076, 19.137367, -18.176435, 5.9081645, 18.209078, -19.679087, 6.560062, 17.40809, -20.237427, 7.6180053, 18.209078, -19.679087, 6.560062, 17.214787, -20.237427, 7.3894567, 18.209078, -19.679087, 6.560062, 19.137367, -18.176435, 5.9081645, 19.061113, -18.177076, 5.939136, 17.214787, -20.237427, 7.3894567, 18.209078, -19.679087, 6.560062, 18.027735, -19.463995, 6.598509, 18.209078, -19.679087, 6.560062, 19.061113, -18.177076, 5.939136, 18.027735, -19.463995, 6.598509, 18.027735, -19.463995, 6.598509, 19.061113, -18.177076, 5.939136, 19.067095, -18.177504, 6.0213714, 17.214787, -20.237427, 7.3894567, 18.027735, -19.463995, 6.598509, 17.082783, -19.968937, 7.379845, 18.027735, -19.463995, 6.598509, 19.067095, -18.177504, 6.0213714, 18.098648, -19.331137, 6.8390183, 17.082783, -19.968937, 7.379845, 18.027735, -19.463995, 6.598509, 18.098648, -19.331137, 6.8390183, 17.082783, -19.968937, 7.379845, 18.098648, -19.331137, 6.8390183, 17.19428, -19.80297, 7.6028395, 19.916353, -18.107658, 4.509749, 18.669376, -17.361994, 4.59476, 18.997675, -18.380848, 4.7551713, 18.997675, -18.380848, 4.7551713, 18.669376, -17.361994, 4.59476, 17.938025, -17.356226, 5.7366467, 18.997675, -18.380848, 4.7551713, 17.938025, -17.356226, 5.7366467, 18.45151, -18.107658, 5.7464724, 19.916353, -18.107658, 4.509749, 19.861246, -17.356226, 4.112673, 18.669376, -17.361994, 4.59476, 20.759846, -18.352867, 5.1768107, 19.861246, -17.356226, 4.112673, 19.916353, -18.107658, 4.509749, 18.669376, -17.361994, 4.59476, 18.361372, -16.596249, 5.750744, 17.938025, -17.356226, 5.7366467, 20.759846, -18.352867, 5.1768107, 20.691708, -17.339565, 4.9839325, 19.861246, -17.356226, 4.112673, 20.916626, -18.072842, 6.2943473, 20.691708, -17.339565, 4.9839325, 20.759846, -18.352867, 5.1768107, 19.861246, -17.356226, 4.112673, 18.80138, -16.331818, 5.0939345, 18.669376, -17.361994, 4.59476, 18.669376, -17.361994, 4.59476, 18.80138, -16.331818, 5.0939345, 18.361372, -16.596249, 5.750744, 20.691708, -17.339565, 4.9839325, 19.58528, -16.59625, 4.7173643, 19.861246, -17.356226, 4.112673, 19.861246, -17.356226, 4.112673, 19.58528, -16.59625, 4.7173643, 18.80138, -16.331818, 5.0939345, 20.916626, -18.072842, 6.2943473, 20.97152, -17.322691, 6.320192, 20.691708, -17.339565, 4.9839325, 20.97152, -17.322691, 6.320192, 20.916626, -18.072842, 6.2943473, 20.598795, -18.324884, 7.222637, 20.691708, -17.339565, 4.9839325, 20.07292, -16.326263, 5.255841, 19.58528, -16.59625, 4.7173643, 20.97152, -17.322691, 6.320192, 20.07292, -16.326263, 5.255841, 20.691708, -17.339565, 4.9839325, 19.58528, -16.59625, 4.7173643, 19.312943, -15.988567, 5.8139677, 18.80138, -16.331818, 5.0939345, 19.58528, -16.59625, 4.7173643, 20.07292, -16.326263, 5.255841, 19.312943, -15.988567, 5.8139677, 20.97152, -17.322691, 6.320192, 20.390537, -16.58151, 6.067721, 20.07292, -16.326263, 5.255841, 20.97152, -17.322691, 6.320192, 20.598795, -18.324884, 7.222637, 20.511219, -17.317137, 7.2331033, 20.390537, -16.58151, 6.067721, 20.97152, -17.322691, 6.320192, 20.511219, -17.317137, 7.2331033, 20.511219, -17.317137, 7.2331033, 20.598795, -18.324884, 7.222637, 19.692719, -18.072842, 7.327727, 20.07292, -16.326263, 5.255841, 20.390537, -16.58151, 6.067721, 19.96313, -16.32071, 6.5839834, 20.390537, -16.58151, 6.067721, 20.511219, -17.317137, 7.2331033, 19.96313, -16.32071, 6.5839834, 20.07292, -16.326263, 5.255841, 19.96313, -16.32071, 6.5839834, 19.312943, -15.988567, 5.8139677, 20.511219, -17.317137, 7.2331033, 19.692719, -18.072842, 7.327727, 19.590405, -17.322691, 7.486215, 19.96313, -16.32071, 6.5839834, 20.511219, -17.317137, 7.2331033, 19.590405, -17.322691, 7.486215, 18.836624, -18.352865, 6.800998, 19.590405, -17.322691, 7.486215, 19.692719, -18.072842, 7.327727, 19.312943, -15.988567, 5.8139677, 19.96313, -16.32071, 6.5839834, 19.407352, -16.58151, 6.897543, 19.96313, -16.32071, 6.5839834, 19.590405, -17.322691, 7.486215, 19.407352, -16.58151, 6.897543, 18.836624, -18.352865, 6.800998, 18.488674, -17.339565, 6.8439307, 19.590405, -17.322691, 7.486215, 18.488674, -17.339565, 6.8439307, 19.407352, -16.58151, 6.897543, 19.590405, -17.322691, 7.486215, 18.45151, -18.107658, 5.7464724, 18.488674, -17.339565, 6.8439307, 18.836624, -18.352865, 6.800998, 18.45151, -18.107658, 5.7464724, 17.938025, -17.356226, 5.7366467, 18.488674, -17.339565, 6.8439307, 18.488674, -17.339565, 6.8439307, 18.691591, -16.326263, 6.422077, 19.407352, -16.58151, 6.897543, 19.312943, -15.988567, 5.8139677, 19.407352, -16.58151, 6.897543, 18.691591, -16.326263, 6.422077, 17.938025, -17.356226, 5.7366467, 18.691591, -16.326263, 6.422077, 18.488674, -17.339565, 6.8439307, 18.80138, -16.331818, 5.0939345, 19.312943, -15.988567, 5.8139677, 18.691591, -16.326263, 6.422077, 17.938025, -17.356226, 5.7366467, 18.361372, -16.596249, 5.750744, 18.691591, -16.326263, 6.422077, 18.80138, -16.331818, 5.0939345, 18.691591, -16.326263, 6.422077, 18.361372, -16.596249, 5.750744, 20.916626, -18.072842, 6.2943473, 19.867653, -18.690775, 6.0138955, 20.598795, -18.324884, 7.222637, 20.916626, -18.072842, 6.2943473, 20.759846, -18.352867, 5.1768107, 19.867653, -18.690775, 6.0138955, 20.598795, -18.324884, 7.222637, 19.867653, -18.690775, 6.0138955, 18.836624, -18.352865, 6.800998, 20.598795, -18.324884, 7.222637, 18.836624, -18.352865, 6.800998, 19.692719, -18.072842, 7.327727, 19.867653, -18.690775, 6.0138955, 18.45151, -18.107658, 5.7464724, 18.836624, -18.352865, 6.800998, 19.867653, -18.690775, 6.0138955, 18.997675, -18.380848, 4.7551713, 18.45151, -18.107658, 5.7464724, 20.759846, -18.352867, 5.1768107, 18.997675, -18.380848, 4.7551713, 19.867653, -18.690775, 6.0138955, 20.759846, -18.352867, 5.1768107, 19.916353, -18.107658, 4.509749, 18.997675, -18.380848, 4.7551713, 15.432323, -17.738775, 11.597305, 17.151134, -15.819397, 12.648413, 17.029385, -18.463293, 12.383127, 17.029385, -18.463293, 12.383127, 17.151134, -15.819397, 12.648413, 19.233913, -15.752114, 11.624859, 17.029385, -18.463293, 12.383127, 19.233913, -15.752114, 11.624859, 18.862041, -17.774445, 11.419594, 15.432323, -17.738775, 11.597305, 15.174299, -15.8606205, 12.197084, 17.151134, -15.819397, 12.648413, 14.790466, -18.280243, 9.459633, 15.174299, -15.8606205, 12.197084, 15.432323, -17.738775, 11.597305, 17.151134, -15.819397, 12.648413, 18.632425, -13.811804, 11.039604, 19.233913, -15.752114, 11.624859, 14.790466, -18.280243, 9.459633, 14.663162, -15.672871, 9.612781, 15.174299, -15.8606205, 12.197084, 15.765961, -17.546753, 7.4719043, 14.663162, -15.672871, 9.612781, 14.790466, -18.280243, 9.459633, 15.174299, -15.8606205, 12.197084, 17.378614, -13.149655, 11.438816, 17.151134, -15.819397, 12.648413, 17.151134, -15.819397, 12.648413, 17.378614, -13.149655, 11.438816, 18.632425, -13.811804, 11.039604, 14.663162, -15.672871, 9.612781, 16.053888, -13.860291, 11.293997, 15.174299, -15.8606205, 12.197084, 15.174299, -15.8606205, 12.197084, 16.053888, -13.860291, 11.293997, 17.378614, -13.149655, 11.438816, 15.765961, -17.546753, 7.4719043, 15.501957, -15.532538, 7.0716233, 14.663162, -15.672871, 9.612781, 15.501957, -15.532538, 7.0716233, 15.765961, -17.546753, 7.4719043, 17.558676, -18.333216, 6.7520843, 14.663162, -15.672871, 9.612781, 15.88579, -13.113131, 9.809074, 16.053888, -13.860291, 11.293997, 15.501957, -15.532538, 7.0716233, 15.88579, -13.113131, 9.809074, 14.663162, -15.672871, 9.612781, 16.053888, -13.860291, 11.293997, 17.454441, -12.259813, 9.874649, 17.378614, -13.149655, 11.438816, 16.053888, -13.860291, 11.293997, 15.88579, -13.113131, 9.809074, 17.454441, -12.259813, 9.874649, 15.501957, -15.532538, 7.0716233, 16.199562, -13.714191, 8.241919, 15.88579, -13.113131, 9.809074, 15.501957, -15.532538, 7.0716233, 17.558676, -18.333216, 6.7520843, 17.574482, -15.715377, 6.7499475, 16.199562, -13.714191, 8.241919, 15.501957, -15.532538, 7.0716233, 17.574482, -15.715377, 6.7499475, 17.574482, -15.715377, 6.7499475, 17.558676, -18.333216, 6.7520843, 19.186922, -17.804777, 7.918107, 15.88579, -13.113131, 9.809074, 16.199562, -13.714191, 8.241919, 17.484558, -13.123598, 8.222481, 16.199562, -13.714191, 8.241919, 17.574482, -15.715377, 6.7499475, 17.484558, -13.123598, 8.222481, 15.88579, -13.113131, 9.809074, 17.484558, -13.123598, 8.222481, 17.454441, -12.259813, 9.874649, 17.574482, -15.715377, 6.7499475, 19.186922, -17.804777, 7.918107, 19.541065, -15.924274, 7.902941, 17.484558, -13.123598, 8.222481, 17.574482, -15.715377, 6.7499475, 19.541065, -15.924274, 7.902941, 19.797594, -18.516266, 9.675579, 19.541065, -15.924274, 7.902941, 19.186922, -17.804777, 7.918107, 17.454441, -12.259813, 9.874649, 17.484558, -13.123598, 8.222481, 18.768913, -13.888486, 8.611226, 17.484558, -13.123598, 8.222481, 19.541065, -15.924274, 7.902941, 18.768913, -13.888486, 8.611226, 19.797594, -18.516266, 9.675579, 20.062452, -15.861903, 9.78558, 19.541065, -15.924274, 7.902941, 20.062452, -15.861903, 9.78558, 18.768913, -13.888486, 8.611226, 19.541065, -15.924274, 7.902941, 18.862041, -17.774445, 11.419594, 20.062452, -15.861903, 9.78558, 19.797594, -18.516266, 9.675579, 18.862041, -17.774445, 11.419594, 19.233913, -15.752114, 11.624859, 20.062452, -15.861903, 9.78558, 20.062452, -15.861903, 9.78558, 18.977383, -13.160122, 9.852434, 18.768913, -13.888486, 8.611226, 17.454441, -12.259813, 9.874649, 18.768913, -13.888486, 8.611226, 18.977383, -13.160122, 9.852434, 19.233913, -15.752114, 11.624859, 18.977383, -13.160122, 9.852434, 20.062452, -15.861903, 9.78558, 17.378614, -13.149655, 11.438816, 17.454441, -12.259813, 9.874649, 18.977383, -13.160122, 9.852434, 19.233913, -15.752114, 11.624859, 18.632425, -13.811804, 11.039604, 18.977383, -13.160122, 9.852434, 17.378614, -13.149655, 11.438816, 18.977383, -13.160122, 9.852434, 18.632425, -13.811804, 11.039604, 15.765961, -17.546753, 7.4719043, 17.271175, -19.275175, 9.523712, 17.558676, -18.333216, 6.7520843, 15.765961, -17.546753, 7.4719043, 14.790466, -18.280243, 9.459633, 17.271175, -19.275175, 9.523712, 17.558676, -18.333216, 6.7520843, 17.271175, -19.275175, 9.523712, 19.797594, -18.516266, 9.675579, 17.558676, -18.333216, 6.7520843, 19.797594, -18.516266, 9.675579, 19.186922, -17.804777, 7.918107, 17.271175, -19.275175, 9.523712, 18.862041, -17.774445, 11.419594, 19.797594, -18.516266, 9.675579, 17.271175, -19.275175, 9.523712, 17.029385, -18.463293, 12.383127, 18.862041, -17.774445, 11.419594, 14.790466, -18.280243, 9.459633, 17.029385, -18.463293, 12.383127, 17.271175, -19.275175, 9.523712, 14.790466, -18.280243, 9.459633, 15.432323, -17.738775, 11.597305, 17.029385, -18.463293, 12.383127, 0.9335377, -18.661974, -2.1739244, 1.8889831, -18.823336, -0.58917046, 1.5114222, -18.39982, -1.417089, 0.9335377, -18.661974, -2.1739244, 0.9335377, -18.661972, 1.0975087, 1.8889831, -18.823336, -0.58917046, 1.8889831, -18.823336, -0.58917046, 0.9335377, -18.661972, 1.0975087, 1.6508096, -18.476694, 0.38623178, -0.9279604, -18.475742, -2.1898947, 0.9335377, -18.661974, -2.1739244, -0.001098156, -18.845232, -2.413156, -1.7685199, -18.39982, -0.53820944, 0.9335377, -18.661974, -2.1739244, -0.9279604, -18.475742, -2.1898947, 0.9335377, -18.661972, 1.0975087, 0.9335377, -18.661974, -2.1739244, -1.7685199, -18.39982, -0.53820944, -1.7685199, -18.39982, -0.53820944, -0.9279604, -18.475742, -2.1898947, -1.6464877, -18.661974, -1.482532, 0.9335377, -18.661972, 1.0975087, -1.7685199, -18.39982, -0.53820944, -0.95520806, -18.661972, 1.0975087, -0.95520806, -18.661972, 1.0975087, -1.7685199, -18.39982, -0.53820944, -1.6464877, -18.661972, 0.40619373, 0.9335377, -18.661972, 1.0975087, -0.95520806, -18.661972, 1.0975087, -0.010835171, -18.39982, 1.2195504, 1.5114222, -18.39982, -1.417089, -0.010835171, -14.884384, -0.53821063, 0.9335377, -18.661974, -2.1739244, 0.9335377, -18.661974, -2.1739244, -0.010835171, -14.884384, -0.53821063, -0.001098156, -18.845232, -2.413156, -0.001098156, -18.845232, -2.413156, -0.010835171, -14.884384, -0.53821063, -0.9279604, -18.475742, -2.1898947, -0.9279604, -18.475742, -2.1898947, -0.010835171, -14.884384, -0.53821063, -1.6464877, -18.661974, -1.482532, -1.6464877, -18.661974, -1.482532, -0.010835171, -14.884384, -0.53821063, -1.7685199, -18.39982, -0.53820944, -1.7685199, -18.39982, -0.53820944, -0.010835171, -14.884384, -0.53821063, -1.6464877, -18.661972, 0.40619373, -1.6464877, -18.661972, 0.40619373, -0.010835171, -14.884384, -0.53821063, -0.95520806, -18.661972, 1.0975087, -0.95520806, -18.661972, 1.0975087, -0.010835171, -14.884384, -0.53821063, -0.010835171, -18.39982, 1.2195504, -0.010835171, -18.39982, 1.2195504, -0.010835171, -14.884384, -0.53821063, 0.9335377, -18.661972, 1.0975087, 0.9335377, -18.661972, 1.0975087, -0.010835171, -14.884384, -0.53821063, 1.6508096, -18.476694, 0.38623178, 1.6508096, -18.476694, 0.38623178, -0.010835171, -14.884384, -0.53821063, 1.8889831, -18.823336, -0.58917046, 1.8889831, -18.823336, -0.58917046, -0.010835171, -14.884384, -0.53821063, 1.5114222, -18.39982, -1.417089, 0.79053855, -17.297068, -2.016264, 1.2353985, -16.723497, -0.5382099, 1.198884, -17.02468, -1.2366481, 0.79053855, -17.297068, -2.016264, 0.4979446, -16.831308, 0.34912395, 1.2353985, -16.723497, -0.5382099, 1.2353985, -16.723497, -0.5382099, 0.4979446, -16.831308, 0.34912395, 1.198884, -17.02468, 0.16022837, 0.4979446, -16.831308, 0.34912395, -0.88492775, -17.226858, 1.0175031, -0.010835171, -17.02468, 0.85866666, 0.4979446, -16.831308, 0.34912395, -1.2205544, -17.02468, 0.16022837, -0.88492775, -17.226858, 1.0175031, 0.79053855, -17.297068, -2.016264, -1.2205544, -17.02468, 0.16022837, 0.4979446, -16.831308, 0.34912395, -1.2205544, -17.02468, 0.16022837, 0.79053855, -17.297068, -2.016264, -0.6996014, -16.880219, -1.4757221, -0.6996014, -16.880219, -1.4757221, 0.79053855, -17.297068, -2.016264, -0.010835171, -17.02468, -1.9350863, -1.2205544, -17.02468, 0.16022837, -0.6996014, -16.880219, -1.4757221, -1.2205544, -17.02468, -1.2366481, -1.2205544, -17.02468, 0.16022837, -1.2205544, -17.02468, -1.2366481, -1.5400834, -17.23483, -0.53750443, 1.198884, -17.02468, -1.2366481, -0.010835171, -14.231084, -0.53821087, 0.79053855, -17.297068, -2.016264, 0.79053855, -17.297068, -2.016264, -0.010835171, -14.231084, -0.53821087, -0.010835171, -17.02468, -1.9350863, -0.010835171, -17.02468, -1.9350863, -0.010835171, -14.231084, -0.53821087, -0.6996014, -16.880219, -1.4757221, -0.6996014, -16.880219, -1.4757221, -0.010835171, -14.231084, -0.53821087, -1.2205544, -17.02468, -1.2366481, -1.2205544, -17.02468, -1.2366481, -0.010835171, -14.231084, -0.53821087, -1.5400834, -17.23483, -0.53750443, -1.5400834, -17.23483, -0.53750443, -0.010835171, -14.231084, -0.53821087, -1.2205544, -17.02468, 0.16022837, -1.2205544, -17.02468, 0.16022837, -0.010835171, -14.231084, -0.53821087, -0.88492775, -17.226858, 1.0175031, -0.88492775, -17.226858, 1.0175031, -0.010835171, -14.231084, -0.53821087, -0.010835171, -17.02468, 0.85866666, -0.010835171, -17.02468, 0.85866666, -0.010835171, -14.231084, -0.53821087, 0.4979446, -16.831308, 0.34912395, 0.4979446, -16.831308, 0.34912395, -0.010835171, -14.231084, -0.53821087, 1.198884, -17.02468, 0.16022837, 1.198884, -17.02468, 0.16022837, -0.010835171, -14.231084, -0.53821087, 1.2353985, -16.723497, -0.5382099, 1.2353985, -16.723497, -0.5382099, -0.010835171, -14.231084, -0.53821087, 1.198884, -17.02468, -1.2366481, 1.3094504, -20.552732, -2.7694192, 2.5816762, -20.552732, -0.5104182, 2.2008963, -20.875694, -1.8151541, 1.3094504, -20.552732, -2.7694192, 1.2661028, -20.875692, 1.6735129, 2.5816762, -20.552732, -0.5104182, 2.5816762, -20.552732, -0.5104182, 1.2661028, -20.875692, 1.6735129, 2.2298722, -20.62413, 0.73239887, -1.2877731, -20.875694, -2.749927, 1.3094504, -20.552732, -2.7694192, -0.039105892, -21.061092, -3.116448, -2.5646338, -20.875692, -0.5382085, 1.3094504, -20.552732, -2.7694192, -1.2877731, -20.875694, -2.749927, 1.2661028, -20.875692, 1.6735129, 1.3094504, -20.552732, -2.7694192, -2.5646338, -20.875692, -0.5382085, -2.5646338, -20.875692, -0.5382085, -1.2877731, -20.875694, -2.749927, -2.1870723, -20.562727, -1.6952257, 1.2661028, -20.875692, 1.6735129, -2.5646338, -20.875692, -0.5382085, -1.2877731, -20.875692, 1.6735129, -1.2877731, -20.875692, 1.6735129, -2.5646338, -20.875692, -0.5382085, -2.1694045, -20.598309, 0.75627506, 1.2661028, -20.875692, 1.6735129, -1.2877731, -20.875692, 1.6735129, -0.033530712, -21.082869, 2.0138066, 2.2008963, -20.875694, -1.8151541, -0.010835171, -15.767943, -0.5382104, 1.3094504, -20.552732, -2.7694192, 1.3094504, -20.552732, -2.7694192, -0.010835171, -15.767943, -0.5382104, -0.039105892, -21.061092, -3.116448, -0.039105892, -21.061092, -3.116448, -0.010835171, -15.767943, -0.5382104, -1.2877731, -20.875694, -2.749927, -1.2877731, -20.875694, -2.749927, -0.010835171, -15.767943, -0.5382104, -2.1870723, -20.562727, -1.6952257, -2.1870723, -20.562727, -1.6952257, -0.010835171, -15.767943, -0.5382104, -2.5646338, -20.875692, -0.5382085, -2.5646338, -20.875692, -0.5382085, -0.010835171, -15.767943, -0.5382104, -2.1694045, -20.598309, 0.75627506, -2.1694045, -20.598309, 0.75627506, -0.010835171, -15.767943, -0.5382104, -1.2877731, -20.875692, 1.6735129, -1.2877731, -20.875692, 1.6735129, -0.010835171, -15.767943, -0.5382104, -0.033530712, -21.082869, 2.0138066, -0.033530712, -21.082869, 2.0138066, -0.010835171, -15.767943, -0.5382104, 1.2661028, -20.875692, 1.6735129, 1.2661028, -20.875692, 1.6735129, -0.010835171, -15.767943, -0.5382104, 2.2298722, -20.62413, 0.73239887, 2.2298722, -20.62413, 0.73239887, -0.010835171, -15.767943, -0.5382104, 2.5816762, -20.552732, -0.5104182, 2.5816762, -20.552732, -0.5104182, -0.010835171, -15.767943, -0.5382104, 2.2008963, -20.875694, -1.8151541, 0.5583327, -21.275526, -0.82769585, 0.37442207, -20.354483, -1.1662691, 0.31780314, -21.275528, -1.0682604, 0.5583327, -21.275526, -0.82769585, 0.65641344, -20.354483, -0.884295, 0.37442207, -20.354483, -1.1662691, 0.6890812, -22.029617, -0.9031608, 0.5583327, -21.275526, -0.82769585, 0.31780314, -21.275528, -1.0682604, 0.6890812, -22.029617, -0.9031608, 0.31780314, -21.275528, -1.0682604, 0.39326835, -22.029617, -1.1989913, 0.31780314, -21.275528, -1.0682604, -0.010835171, -20.354483, -1.2695246, -0.010835171, -21.275528, -1.1563265, 0.31780314, -21.275528, -1.0682604, 0.37442207, -20.354483, -1.1662691, -0.010835171, -20.354483, -1.2695246, 0.39326835, -22.029617, -1.1989913, 0.31780314, -21.275528, -1.0682604, -0.010835171, -21.275528, -1.1563265, 0.39326835, -22.029617, -1.1989913, -0.010835171, -21.275528, -1.1563265, -0.010835171, -22.029617, -1.3072567, -0.010835171, -21.275528, -1.1563265, -0.3960147, -20.354483, -1.1662691, -0.339396, -21.275528, -1.0682604, -0.010835171, -21.275528, -1.1563265, -0.010835171, -20.354483, -1.2695246, -0.3960147, -20.354483, -1.1662691, -0.010835171, -22.029617, -1.3072567, -0.010835171, -21.275528, -1.1563265, -0.339396, -21.275528, -1.0682604, -0.010835171, -22.029617, -1.3072567, -0.339396, -21.275528, -1.0682604, -0.4148612, -22.029617, -1.1989913, -0.339396, -21.275528, -1.0682604, -0.6780064, -20.354483, -0.884295, -0.580003, -21.275526, -0.82769585, -0.339396, -21.275528, -1.0682604, -0.3960147, -20.354483, -1.1662691, -0.6780064, -20.354483, -0.884295, -0.4148612, -22.029617, -1.1989913, -0.339396, -21.275528, -1.0682604, -0.580003, -21.275526, -0.82769585, -0.4148612, -22.029617, -1.1989913, -0.580003, -21.275526, -0.82769585, -0.71067405, -22.029617, -0.9031608, -0.580003, -21.275526, -0.82769585, -0.78126955, -20.354483, -0.4990654, -0.6680341, -21.275526, -0.49906516, -0.580003, -21.275526, -0.82769585, -0.6780064, -20.354483, -0.884295, -0.78126955, -20.354483, -0.4990654, -0.71067405, -22.029617, -0.9031608, -0.580003, -21.275526, -0.82769585, -0.6680341, -21.275526, -0.49906516, -0.71067405, -22.029617, -0.9031608, -0.6680341, -21.275526, -0.49906516, -0.8189645, -22.029615, -0.49906468, -0.6680341, -21.275526, -0.49906516, -0.6780064, -20.354483, -0.11391592, -0.580003, -21.275526, -0.17051435, -0.6680341, -21.275526, -0.49906516, -0.78126955, -20.354483, -0.4990654, -0.6780064, -20.354483, -0.11391592, -0.8189645, -22.029615, -0.49906468, -0.6680341, -21.275526, -0.49906516, -0.580003, -21.275526, -0.17051435, -0.8189645, -22.029615, -0.49906468, -0.580003, -21.275526, -0.17051435, -0.71067405, -22.029615, -0.095048904, -0.580003, -21.275526, -0.17051435, -0.3960147, -20.354483, 0.16813564, -0.339396, -21.275526, 0.070047855, -0.580003, -21.275526, -0.17051435, -0.6780064, -20.354483, -0.11391592, -0.3960147, -20.354483, 0.16813564, -0.71067405, -22.029615, -0.095048904, -0.580003, -21.275526, -0.17051435, -0.339396, -21.275526, 0.070047855, -0.71067405, -22.029615, -0.095048904, -0.339396, -21.275526, 0.070047855, -0.4148612, -22.029615, 0.20078158, -0.339396, -21.275526, 0.070047855, -0.010835171, -20.354483, 0.2713139, -0.010835171, -21.275526, 0.15811634, -0.339396, -21.275526, 0.070047855, -0.3960147, -20.354483, 0.16813564, -0.010835171, -20.354483, 0.2713139, -0.4148612, -22.029615, 0.20078158, -0.339396, -21.275526, 0.070047855, -0.010835171, -21.275526, 0.15811634, -0.4148612, -22.029615, 0.20078158, -0.010835171, -21.275526, 0.15811634, -0.010835171, -22.029615, 0.30904698, -0.010835171, -21.275526, 0.15811634, 0.37442207, -20.354483, 0.16813564, 0.31780314, -21.275526, 0.070047855, -0.010835171, -21.275526, 0.15811634, -0.010835171, -20.354483, 0.2713139, 0.37442207, -20.354483, 0.16813564, -0.010835171, -22.029615, 0.30904698, -0.010835171, -21.275526, 0.15811634, 0.31780314, -21.275526, 0.070047855, -0.010835171, -22.029615, 0.30904698, 0.31780314, -21.275526, 0.070047855, 0.39326835, -22.029615, 0.20078158, 0.31780314, -21.275526, 0.070047855, 0.65641344, -20.354483, -0.11391592, 0.5583327, -21.275526, -0.17051435, 0.31780314, -21.275526, 0.070047855, 0.37442207, -20.354483, 0.16813564, 0.65641344, -20.354483, -0.11391592, 0.39326835, -22.029615, 0.20078158, 0.31780314, -21.275526, 0.070047855, 0.5583327, -21.275526, -0.17051435, 0.39326835, -22.029615, 0.20078158, 0.5583327, -21.275526, -0.17051435, 0.6890812, -22.029615, -0.095048904, 0.5583327, -21.275526, -0.17051435, 0.759599, -20.354483, -0.4990654, 0.64644134, -21.275526, -0.49906516, 0.5583327, -21.275526, -0.17051435, 0.65641344, -20.354483, -0.11391592, 0.759599, -20.354483, -0.4990654, 0.6890812, -22.029615, -0.095048904, 0.5583327, -21.275526, -0.17051435, 0.64644134, -21.275526, -0.49906516, 0.6890812, -22.029615, -0.095048904, 0.64644134, -21.275526, -0.49906516, 0.7973716, -22.029615, -0.49906468, 0.64644134, -21.275526, -0.49906516, 0.65641344, -20.354483, -0.884295, 0.5583327, -21.275526, -0.82769585, 0.64644134, -21.275526, -0.49906516, 0.759599, -20.354483, -0.4990654, 0.65641344, -20.354483, -0.884295, 0.7973716, -22.029615, -0.49906468, 0.64644134, -21.275526, -0.49906516, 0.5583327, -21.275526, -0.82769585, 0.7973716, -22.029615, -0.49906468, 0.5583327, -21.275526, -0.82769585, 0.6890812, -22.029617, -0.9031608, 0.39326835, -22.029617, -1.1989913, -0.010835171, -22.029615, -0.49906468, 0.6890812, -22.029617, -0.9031608, 0.6890812, -22.029617, -0.9031608, -0.010835171, -22.029615, -0.49906468, 0.7973716, -22.029615, -0.49906468, -0.010835171, -22.029617, -1.3072567, -0.010835171, -22.029615, -0.49906468, 0.39326835, -22.029617, -1.1989913, 0.7973716, -22.029615, -0.49906468, -0.010835171, -22.029615, -0.49906468, 0.6890812, -22.029615, -0.095048904, -0.4148612, -22.029617, -1.1989913, -0.010835171, -22.029615, -0.49906468, -0.010835171, -22.029617, -1.3072567, 0.6890812, -22.029615, -0.095048904, -0.010835171, -22.029615, -0.49906468, 0.39326835, -22.029615, 0.20078158, -0.71067405, -22.029617, -0.9031608, -0.010835171, -22.029615, -0.49906468, -0.4148612, -22.029617, -1.1989913, 0.39326835, -22.029615, 0.20078158, -0.010835171, -22.029615, -0.49906468, -0.010835171, -22.029615, 0.30904698, -0.8189645, -22.029615, -0.49906468, -0.010835171, -22.029615, -0.49906468, -0.71067405, -22.029617, -0.9031608, -0.010835171, -22.029615, 0.30904698, -0.010835171, -22.029615, -0.49906468, -0.4148612, -22.029615, 0.20078158, -0.71067405, -22.029615, -0.095048904, -0.010835171, -22.029615, -0.49906468, -0.8189645, -22.029615, -0.49906468, -0.4148612, -22.029615, 0.20078158, -0.010835171, -22.029615, -0.49906468, -0.71067405, -22.029615, -0.095048904, 9.266796, -17.581045, 9.74091, 10.985607, -16.339836, 10.792017, 10.863857, -18.049463, 10.5267315, 10.863857, -18.049463, 10.5267315, 10.985607, -16.339836, 10.792017, 13.068386, -16.296263, 9.768463, 10.863857, -18.049463, 10.5267315, 13.068386, -16.296263, 9.768463, 12.696514, -17.604115, 9.563198, 9.266796, -17.581045, 9.74091, 9.008772, -16.366535, 10.340902, 10.985607, -16.339836, 10.792017, 8.624725, -17.931131, 7.6034503, 9.008772, -16.366535, 10.340902, 9.266796, -17.581045, 9.74091, 10.985607, -16.339836, 10.792017, 12.466898, -15.041597, 9.183208, 13.068386, -16.296263, 9.768463, 8.624725, -17.931131, 7.6034503, 8.497636, -16.245214, 7.756598, 9.008772, -16.366535, 10.340902, 9.600648, -17.456734, 5.615508, 8.497636, -16.245214, 7.756598, 8.624725, -17.931131, 7.6034503, 9.008772, -16.366535, 10.340902, 11.213087, -14.613337, 9.582634, 10.985607, -16.339836, 10.792017, 10.985607, -16.339836, 10.792017, 11.213087, -14.613337, 9.582634, 12.466898, -15.041597, 9.183208, 8.497636, -16.245214, 7.756598, 9.8885765, -15.0727825, 9.437601, 9.008772, -16.366535, 10.340902, 9.008772, -16.366535, 10.340902, 9.8885765, -15.0727825, 9.437601, 11.213087, -14.613337, 9.582634, 9.600648, -17.456734, 5.615508, 9.33643, -16.154436, 5.215441, 8.497636, -16.245214, 7.756598, 9.33643, -16.154436, 5.215441, 9.600648, -17.456734, 5.615508, 11.392935, -17.96552, 4.895688, 8.497636, -16.245214, 7.756598, 9.720263, -14.589842, 7.952893, 9.8885765, -15.0727825, 9.437601, 9.33643, -16.154436, 5.215441, 9.720263, -14.589842, 7.952893, 8.497636, -16.245214, 7.756598, 9.8885765, -15.0727825, 9.437601, 11.288914, -14.037909, 8.018253, 11.213087, -14.613337, 9.582634, 9.8885765, -15.0727825, 9.437601, 9.720263, -14.589842, 7.952893, 11.288914, -14.037909, 8.018253, 9.33643, -16.154436, 5.215441, 10.034035, -14.978588, 6.3855224, 9.720263, -14.589842, 7.952893, 9.33643, -16.154436, 5.215441, 11.392935, -17.96552, 4.895688, 11.408955, -16.272556, 4.8937654, 10.034035, -14.978588, 6.3855224, 9.33643, -16.154436, 5.215441, 11.408955, -16.272556, 4.8937654, 11.408955, -16.272556, 4.8937654, 11.392935, -17.96552, 4.895688, 13.021395, -17.623766, 6.0619245, 9.720263, -14.589842, 7.952893, 10.034035, -14.978588, 6.3855224, 11.318817, -14.596678, 6.366298, 10.034035, -14.978588, 6.3855224, 11.408955, -16.272556, 4.8937654, 11.318817, -14.596678, 6.366298, 9.720263, -14.589842, 7.952893, 11.318817, -14.596678, 6.366298, 11.288914, -14.037909, 8.018253, 11.408955, -16.272556, 4.8937654, 13.021395, -17.623766, 6.0619245, 13.375538, -16.407763, 6.0467587, 11.318817, -14.596678, 6.366298, 11.408955, -16.272556, 4.8937654, 13.375538, -16.407763, 6.0467587, 13.632067, -18.08364, 7.8191824, 13.375538, -16.407763, 6.0467587, 13.021395, -17.623766, 6.0619245, 11.288914, -14.037909, 8.018253, 11.318817, -14.596678, 6.366298, 12.603386, -15.091153, 6.755044, 11.318817, -14.596678, 6.366298, 13.375538, -16.407763, 6.0467587, 12.603386, -15.091153, 6.755044, 13.632067, -18.08364, 7.8191824, 13.896927, -16.367392, 7.929184, 13.375538, -16.407763, 6.0467587, 13.896927, -16.367392, 7.929184, 12.603386, -15.091153, 6.755044, 13.375538, -16.407763, 6.0467587, 12.696514, -17.604115, 9.563198, 13.896927, -16.367392, 7.929184, 13.632067, -18.08364, 7.8191824, 12.696514, -17.604115, 9.563198, 13.068386, -16.296263, 9.768463, 13.896927, -16.367392, 7.929184, 13.896927, -16.367392, 7.929184, 12.811857, -14.6201725, 7.9960394, 12.603386, -15.091153, 6.755044, 11.288914, -14.037909, 8.018253, 12.603386, -15.091153, 6.755044, 12.811857, -14.6201725, 7.9960394, 13.068386, -16.296263, 9.768463, 12.811857, -14.6201725, 7.9960394, 13.896927, -16.367392, 7.929184, 11.213087, -14.613337, 9.582634, 11.288914, -14.037909, 8.018253, 12.811857, -14.6201725, 7.9960394, 13.068386, -16.296263, 9.768463, 12.466898, -15.041597, 9.183208, 12.811857, -14.6201725, 7.9960394, 11.213087, -14.613337, 9.582634, 12.811857, -14.6201725, 7.9960394, 12.466898, -15.041597, 9.183208, 9.600648, -17.456734, 5.615508, 11.105434, -18.574484, 7.667529, 11.392935, -17.96552, 4.895688, 9.600648, -17.456734, 5.615508, 8.624725, -17.931131, 7.6034503, 11.105434, -18.574484, 7.667529, 11.392935, -17.96552, 4.895688, 11.105434, -18.574484, 7.667529, 13.632067, -18.08364, 7.8191824, 11.392935, -17.96552, 4.895688, 13.632067, -18.08364, 7.8191824, 13.021395, -17.623766, 6.0619245, 11.105434, -18.574484, 7.667529, 12.696514, -17.604115, 9.563198, 13.632067, -18.08364, 7.8191824, 11.105434, -18.574484, 7.667529, 10.863857, -18.049463, 10.5267315, 12.696514, -17.604115, 9.563198, 8.624725, -17.931131, 7.6034503, 10.863857, -18.049463, 10.5267315, 11.105434, -18.574484, 7.667529, 8.624725, -17.931131, 7.6034503, 9.266796, -17.581045, 9.74091, 10.863857, -18.049463, 10.5267315, 7.21563, -18.140028, 8.584927, 8.205863, -17.407393, 9.311367, 8.061045, -18.408518, 8.982857, 8.061045, -18.408518, 8.982857, 8.205863, -17.407393, 9.311367, 9.478684, -17.401838, 8.917923, 8.061045, -18.408518, 8.982857, 9.478684, -17.401838, 8.917923, 9.09955, -18.140028, 8.584927, 7.21563, -18.140028, 8.584927, 7.0052376, -17.401838, 8.917923, 8.205863, -17.407393, 9.311367, 7.0052376, -18.380964, 7.548983, 7.0052376, -17.401838, 8.917923, 7.21563, -18.140028, 8.584927, 8.205863, -17.407393, 9.311367, 9.169825, -16.655106, 8.638752, 9.478684, -17.401838, 8.917923, 7.0052376, -18.380964, 7.548983, 6.93411, -17.385178, 7.737161, 7.0052376, -17.401838, 8.917923, 7.595832, -18.10564, 6.6108675, 6.93411, -17.385178, 7.737161, 7.0052376, -18.380964, 7.548983, 7.0052376, -17.401838, 8.917923, 8.42309, -16.394945, 8.852989, 8.205863, -17.407393, 9.311367, 8.205863, -17.407393, 9.311367, 8.42309, -16.394945, 8.852989, 9.169825, -16.655106, 8.638752, 6.93411, -17.385178, 7.737161, 7.595832, -16.655106, 8.638752, 7.0052376, -17.401838, 8.917923, 7.0052376, -17.401838, 8.917923, 7.595832, -16.655106, 8.638752, 8.42309, -16.394945, 8.852989, 7.595832, -18.10564, 6.6108675, 7.5710545, -17.368732, 6.556614, 6.93411, -17.385178, 7.737161, 7.5710545, -17.368732, 6.556614, 7.595832, -18.10564, 6.6108675, 8.42309, -18.35341, 6.115324, 6.93411, -17.385178, 7.737161, 7.5710545, -16.389393, 7.925339, 7.595832, -16.655106, 8.638752, 7.5710545, -17.368732, 6.556614, 7.5710545, -16.389393, 7.925339, 6.93411, -17.385178, 7.737161, 7.595832, -16.655106, 8.638752, 8.495287, -16.057676, 7.988137, 8.42309, -16.394945, 8.852989, 7.595832, -16.655106, 8.638752, 7.5710545, -16.389393, 7.925339, 8.495287, -16.057676, 7.988137, 7.5710545, -17.368732, 6.556614, 7.8472347, -16.640368, 7.1145277, 7.5710545, -16.389393, 7.925339, 7.5710545, -17.368732, 6.556614, 8.42309, -18.35341, 6.115324, 8.495287, -17.363178, 6.1631694, 7.8472347, -16.640368, 7.1145277, 7.5710545, -17.368732, 6.556614, 8.495287, -17.363178, 6.1631694, 8.495287, -17.363178, 6.1631694, 8.42309, -18.35341, 6.115324, 9.169825, -18.10564, 6.6108675, 7.5710545, -16.389393, 7.925339, 7.8472347, -16.640368, 7.1145277, 8.495287, -16.384052, 6.997904, 7.8472347, -16.640368, 7.1145277, 8.495287, -17.363178, 6.1631694, 8.495287, -16.384052, 6.997904, 7.5710545, -16.389393, 7.925339, 8.495287, -16.384052, 6.997904, 8.495287, -16.057676, 7.988137, 8.495287, -17.363178, 6.1631694, 9.169825, -18.10564, 6.6108675, 9.347322, -17.368732, 6.556614, 8.495287, -16.384052, 6.997904, 8.495287, -17.363178, 6.1631694, 9.347322, -17.368732, 6.556614, 9.478684, -18.380964, 7.548983, 9.347322, -17.368732, 6.556614, 9.169825, -18.10564, 6.6108675, 8.495287, -16.057676, 7.988137, 8.495287, -16.384052, 6.997904, 9.111299, -16.640368, 7.1145277, 8.495287, -16.384052, 6.997904, 9.347322, -17.368732, 6.556614, 9.111299, -16.640368, 7.1145277, 9.478684, -18.380964, 7.548983, 9.767253, -17.385178, 7.737161, 9.347322, -17.368732, 6.556614, 9.767253, -17.385178, 7.737161, 9.111299, -16.640368, 7.1145277, 9.347322, -17.368732, 6.556614, 9.09955, -18.140028, 8.584927, 9.767253, -17.385178, 7.737161, 9.478684, -18.380964, 7.548983, 9.09955, -18.140028, 8.584927, 9.478684, -17.401838, 8.917923, 9.767253, -17.385178, 7.737161, 9.767253, -17.385178, 7.737161, 9.347322, -16.389393, 7.925339, 9.111299, -16.640368, 7.1145277, 8.495287, -16.057676, 7.988137, 9.111299, -16.640368, 7.1145277, 9.347322, -16.389393, 7.925339, 9.478684, -17.401838, 8.917923, 9.347322, -16.389393, 7.925339, 9.767253, -17.385178, 7.737161, 8.42309, -16.394945, 8.852989, 8.495287, -16.057676, 7.988137, 9.347322, -16.389393, 7.925339, 9.478684, -17.401838, 8.917923, 9.169825, -16.655106, 8.638752, 9.347322, -16.389393, 7.925339, 8.42309, -16.394945, 8.852989, 9.347322, -16.389393, 7.925339, 9.169825, -16.655106, 8.638752, 7.595832, -18.10564, 6.6108675, 8.205863, -18.712894, 7.4863997, 8.42309, -18.35341, 6.115324, 7.595832, -18.10564, 6.6108675, 7.0052376, -18.380964, 7.548983, 8.205863, -18.712894, 7.4863997, 8.42309, -18.35341, 6.115324, 8.205863, -18.712894, 7.4863997, 9.478684, -18.380964, 7.548983, 8.42309, -18.35341, 6.115324, 9.478684, -18.380964, 7.548983, 9.169825, -18.10564, 6.6108675, 8.205863, -18.712894, 7.4863997, 9.09955, -18.140028, 8.584927, 9.478684, -18.380964, 7.548983, 8.205863, -18.712894, 7.4863997, 8.061045, -18.408518, 8.982857, 9.09955, -18.140028, 8.584927, 7.0052376, -18.380964, 7.548983, 8.061045, -18.408518, 8.982857, 8.205863, -18.712894, 7.4863997, 7.0052376, -18.380964, 7.548983, 7.21563, -18.140028, 8.584927, 8.061045, -18.408518, 8.982857, 11.220137, -19.805866, 7.988138, 9.957781, -19.47287, 7.7623663, 11.162678, -19.969055, 7.749978, 11.220137, -19.805866, 7.988138, 10.0568905, -19.342148, 7.9881377, 9.957781, -19.47287, 7.7623663, 10.0568905, -19.342148, 7.9881377, 8.76399, -18.208166, 7.9225636, 9.957781, -19.47287, 7.7623663, 10.0568905, -19.342148, 7.9881377, 8.811623, -18.208807, 7.9881372, 8.76399, -18.208166, 7.9225636, 9.957781, -19.47287, 7.7623663, 8.76399, -18.208166, 7.9225636, 8.686882, -18.207739, 7.947554, 11.162678, -19.969055, 7.749978, 9.957781, -19.47287, 7.7623663, 9.797157, -19.684116, 7.8486595, 9.957781, -19.47287, 7.7623663, 8.686882, -18.207739, 7.947554, 9.797157, -19.684116, 7.8486595, 11.162678, -19.969055, 7.749978, 9.797157, -19.684116, 7.8486595, 11.069338, -20.232845, 7.84097, 9.797157, -19.684116, 7.8486595, 8.686882, -18.207739, 7.947554, 8.686882, -18.207739, 8.028721, 11.069338, -20.232845, 7.84097, 9.797157, -19.684116, 7.8486595, 9.797157, -19.684116, 8.127617, 9.797157, -19.684116, 7.8486595, 8.686882, -18.207739, 8.028721, 9.797157, -19.684116, 8.127617, 11.069338, -20.232845, 7.84097, 9.797157, -19.684116, 8.127617, 11.069338, -20.232845, 8.135092, 9.797157, -19.684116, 8.127617, 8.686882, -18.207739, 8.028721, 8.76399, -18.208166, 8.053711, 11.069338, -20.232845, 8.135092, 9.797157, -19.684116, 8.127617, 9.957781, -19.47287, 8.213909, 9.797157, -19.684116, 8.127617, 8.76399, -18.208166, 8.053711, 9.957781, -19.47287, 8.213909, 9.957781, -19.47287, 8.213909, 8.76399, -18.208166, 8.053711, 8.811623, -18.208807, 7.9881372, 11.069338, -20.232845, 8.135092, 9.957781, -19.47287, 8.213909, 11.162678, -19.969055, 8.226085, 9.957781, -19.47287, 8.213909, 8.811623, -18.208807, 7.9881372, 10.0568905, -19.342148, 7.9881377, 11.162678, -19.969055, 8.226085, 9.957781, -19.47287, 8.213909, 10.0568905, -19.342148, 7.9881377, 11.162678, -19.969055, 8.226085, 10.0568905, -19.342148, 7.9881377, 11.220137, -19.805866, 7.988138, 10.050482, -22.030901, 7.9881387, 11.049473, -19.973967, 8.213909, 10.477888, -22.030901, 8.612481, 10.050482, -22.030901, 7.9881387, 10.8882065, -19.94449, 7.988138, 11.049473, -19.973967, 8.213909, 10.8882065, -19.94449, 7.988138, 11.13064, -17.907635, 8.087887, 11.049473, -19.973967, 8.213909, 10.8882065, -19.94449, 7.988138, 11.058657, -17.90016, 7.9881372, 11.13064, -17.907635, 8.087887, 11.049473, -19.973967, 8.213909, 11.13064, -17.907635, 8.087887, 11.247263, -17.920237, 8.049867, 10.477888, -22.030901, 8.612481, 11.049473, -19.973967, 8.213909, 11.310488, -20.021385, 8.127617, 11.049473, -19.973967, 8.213909, 11.247263, -17.920237, 8.049867, 11.310488, -20.021385, 8.127617, 10.477888, -22.030901, 8.612481, 11.310488, -20.021385, 8.127617, 11.169514, -22.031115, 8.374107, 11.310488, -20.021385, 8.127617, 11.247263, -17.920237, 8.049867, 11.247263, -17.920237, 7.926408, 11.169514, -22.031115, 8.374107, 11.310488, -20.021385, 8.127617, 11.310488, -20.021385, 7.8486595, 11.310488, -20.021385, 8.127617, 11.247263, -17.920237, 7.926408, 11.310488, -20.021385, 7.8486595, 11.169514, -22.031115, 8.374107, 11.310488, -20.021385, 7.8486595, 11.169514, -22.031115, 7.60217, 11.310488, -20.021385, 7.8486595, 11.247263, -17.920237, 7.926408, 11.13064, -17.907635, 7.8883877, 11.169514, -22.031115, 7.60217, 11.310488, -20.021385, 7.8486595, 11.049473, -19.973967, 7.762367, 11.310488, -20.021385, 7.8486595, 11.13064, -17.907635, 7.8883877, 11.049473, -19.973967, 7.762367, 11.049473, -19.973967, 7.762367, 11.13064, -17.907635, 7.8883877, 11.058657, -17.90016, 7.9881372, 11.169514, -22.031115, 7.60217, 11.049473, -19.973967, 7.762367, 10.477888, -22.030901, 7.363796, 11.049473, -19.973967, 7.762367, 11.058657, -17.90016, 7.9881372, 10.8882065, -19.94449, 7.988138, 10.477888, -22.030901, 7.363796, 11.049473, -19.973967, 7.762367, 10.8882065, -19.94449, 7.988138, 10.477888, -22.030901, 7.363796, 10.8882065, -19.94449, 7.988138, 10.050482, -22.030901, 7.9881387, 11.023628, -19.86012, 7.8364844, 10.902517, -19.637766, 6.988934, 11.162678, -19.969055, 7.749978, 11.023628, -19.86012, 7.8364844, 10.780127, -19.550406, 7.0991497, 10.902517, -19.637766, 6.988934, 10.780127, -19.550406, 7.0991497, 10.550938, -18.79342, 6.265696, 10.902517, -19.637766, 6.988934, 10.780127, -19.550406, 7.0991497, 10.519325, -18.793848, 6.3096967, 10.550938, -18.79342, 6.265696, 10.902517, -19.637766, 6.988934, 10.550938, -18.79342, 6.265696, 10.518898, -18.792994, 6.222122, 11.162678, -19.969055, 7.749978, 10.902517, -19.637766, 6.988934, 10.814302, -19.77874, 6.9052043, 10.902517, -19.637766, 6.988934, 10.518898, -18.792994, 6.222122, 10.814302, -19.77874, 6.9052043, 11.162678, -19.969055, 7.749978, 10.814302, -19.77874, 6.9052043, 11.085356, -20.145058, 7.710036, 10.814302, -19.77874, 6.9052043, 10.518898, -18.792994, 6.222122, 10.4674225, -18.792994, 6.2389965, 11.085356, -20.145058, 7.710036, 10.814302, -19.77874, 6.9052043, 10.637232, -19.77874, 6.9635158, 10.814302, -19.77874, 6.9052043, 10.4674225, -18.792994, 6.2389965, 10.637232, -19.77874, 6.9635158, 11.085356, -20.145058, 7.710036, 10.637232, -19.77874, 6.9635158, 10.898888, -20.145058, 7.7717648, 10.637232, -19.77874, 6.9635158, 10.4674225, -18.792994, 6.2389965, 10.46785, -18.79342, 6.2930365, 10.898888, -20.145058, 7.7717648, 10.637232, -19.77874, 6.9635158, 10.616299, -19.637766, 7.0833435, 10.637232, -19.77874, 6.9635158, 10.46785, -18.79342, 6.2930365, 10.616299, -19.637766, 7.0833435, 10.616299, -19.637766, 7.0833435, 10.46785, -18.79342, 6.2930365, 10.519325, -18.793848, 6.3096967, 10.898888, -20.145058, 7.7717648, 10.616299, -19.637766, 7.0833435, 10.860653, -19.969055, 7.8497276, 10.616299, -19.637766, 7.0833435, 10.519325, -18.793848, 6.3096967, 10.780127, -19.550406, 7.0991497, 10.860653, -19.969055, 7.8497276, 10.616299, -19.637766, 7.0833435, 10.780127, -19.550406, 7.0991497, 10.860653, -19.969055, 7.8497276, 10.780127, -19.550406, 7.0991497, 11.023628, -19.86012, 7.8364844, 9.806982, -18.747925, 5.422631, 9.553657, -18.25879, 6.202685, 9.731583, -18.927132, 6.0420604, 9.731583, -18.927132, 6.0420604, 9.553657, -18.25879, 6.202685, 10.069706, -18.254944, 6.927204, 9.731583, -18.927132, 6.0420604, 10.069706, -18.254944, 6.927204, 10.201281, -18.747925, 6.617062, 9.806982, -18.747925, 5.422631, 9.551735, -18.254944, 5.359193, 9.553657, -18.25879, 6.202685, 10.419577, -18.908762, 5.072546, 9.551735, -18.254944, 5.359193, 9.806982, -18.747925, 5.422631, 9.553657, -18.25879, 6.202685, 10.181844, -17.756409, 6.6728106, 10.069706, -18.254944, 6.927204, 10.419577, -18.908762, 5.072546, 10.285225, -18.244051, 5.066779, 9.551735, -18.254944, 5.359193, 11.137901, -18.725069, 5.250472, 10.285225, -18.244051, 5.066779, 10.419577, -18.908762, 5.072546, 9.551735, -18.254944, 5.359193, 9.889857, -17.58297, 6.2443357, 9.553657, -18.25879, 6.202685, 9.553657, -18.25879, 6.202685, 9.889857, -17.58297, 6.2443357, 10.181844, -17.756409, 6.6728106, 10.285225, -18.244051, 5.066779, 9.852264, -17.75641, 5.6751013, 9.551735, -18.254944, 5.359193, 9.551735, -18.254944, 5.359193, 9.852264, -17.75641, 5.6751013, 9.889857, -17.58297, 6.2443357, 11.137901, -18.725069, 5.250472, 11.16695, -18.232944, 5.223345, 10.285225, -18.244051, 5.066779, 11.16695, -18.232944, 5.223345, 11.137901, -18.725069, 5.250472, 11.625114, -18.890392, 5.6710434, 10.285225, -18.244051, 5.066779, 10.299322, -17.579124, 5.509991, 9.852264, -17.75641, 5.6751013, 11.16695, -18.232944, 5.223345, 10.299322, -17.579124, 5.509991, 10.285225, -18.244051, 5.066779, 9.852264, -17.75641, 5.6751013, 10.453112, -17.357626, 6.1091294, 9.889857, -17.58297, 6.2443357, 9.852264, -17.75641, 5.6751013, 10.299322, -17.579124, 5.509991, 10.453112, -17.357626, 6.1091294, 11.16695, -18.232944, 5.223345, 10.871332, -17.746584, 5.5153313, 10.299322, -17.579124, 5.509991, 11.16695, -18.232944, 5.223345, 11.625114, -18.890392, 5.6710434, 11.610163, -18.229313, 5.7270055, 10.871332, -17.746584, 5.5153313, 11.16695, -18.232944, 5.223345, 11.610163, -18.229313, 5.7270055, 11.610163, -18.229313, 5.7270055, 11.625114, -18.890392, 5.6710434, 11.46748, -18.725069, 6.248181, 10.299322, -17.579124, 5.509991, 10.871332, -17.746584, 5.5153313, 11.080872, -17.575493, 5.901727, 10.871332, -17.746584, 5.5153313, 11.610163, -18.229313, 5.7270055, 11.080872, -17.575493, 5.901727, 10.299322, -17.579124, 5.509991, 11.080872, -17.575493, 5.901727, 10.453112, -17.357626, 6.1091294, 11.610163, -18.229313, 5.7270055, 11.46748, -18.725069, 6.248181, 11.539036, -18.232943, 6.349426, 11.080872, -17.575493, 5.901727, 11.610163, -18.229313, 5.7270055, 11.539036, -18.232943, 6.349426, 10.937548, -18.908762, 6.640558, 11.539036, -18.232943, 6.349426, 11.46748, -18.725069, 6.248181, 10.453112, -17.357626, 6.1091294, 11.080872, -17.575493, 5.901727, 11.135979, -17.746584, 6.3167453, 11.080872, -17.575493, 5.901727, 11.539036, -18.232943, 6.349426, 11.135979, -17.746584, 6.3167453, 10.937548, -18.908762, 6.640558, 10.878382, -18.24405, 6.8629117, 11.539036, -18.232943, 6.349426, 10.878382, -18.24405, 6.8629117, 11.135979, -17.746584, 6.3167453, 11.539036, -18.232943, 6.349426, 10.201281, -18.747925, 6.617062, 10.878382, -18.24405, 6.8629117, 10.937548, -18.908762, 6.640558, 10.201281, -18.747925, 6.617062, 10.069706, -18.254944, 6.927204, 10.878382, -18.24405, 6.8629117, 10.878382, -18.24405, 6.8629117, 10.671408, -17.579124, 6.6360717, 11.135979, -17.746584, 6.3167453, 10.453112, -17.357626, 6.1091294, 11.135979, -17.746584, 6.3167453, 10.671408, -17.579124, 6.6360717, 10.069706, -18.254944, 6.927204, 10.671408, -17.579124, 6.6360717, 10.878382, -18.24405, 6.8629117, 9.889857, -17.58297, 6.2443357, 10.453112, -17.357626, 6.1091294, 10.671408, -17.579124, 6.6360717, 10.069706, -18.254944, 6.927204, 10.181844, -17.756409, 6.6728106, 10.671408, -17.579124, 6.6360717, 9.889857, -17.58297, 6.2443357, 10.671408, -17.579124, 6.6360717, 10.181844, -17.756409, 6.6728106, 11.137901, -18.725069, 5.250472, 10.710709, -19.130476, 5.820348, 11.625114, -18.890392, 5.6710434, 11.137901, -18.725069, 5.250472, 10.419577, -18.908762, 5.072546, 10.710709, -19.130476, 5.820348, 11.625114, -18.890392, 5.6710434, 10.710709, -19.130476, 5.820348, 10.937548, -18.908762, 6.640558, 11.625114, -18.890392, 5.6710434, 10.937548, -18.908762, 6.640558, 11.46748, -18.725069, 6.248181, 10.710709, -19.130476, 5.820348, 10.201281, -18.747925, 6.617062, 10.937548, -18.908762, 6.640558, 10.710709, -19.130476, 5.820348, 9.731583, -18.927132, 6.0420604, 10.201281, -18.747925, 6.617062, 10.419577, -18.908762, 5.072546, 9.731583, -18.927132, 6.0420604, 10.710709, -19.130476, 5.820348, 10.419577, -18.908762, 5.072546, 9.806982, -18.747925, 5.422631, 9.731583, -18.927132, 6.0420604, 15.540272, 10.966446, -3.3071885, 18.560064, 13.147137, -1.4604888, 18.34616, 10.143481, -1.9265723, 18.34616, 10.143481, -1.9265723, 18.560064, 13.147137, -1.4604888, 22.219316, 13.223691, -3.25878, 18.34616, 10.143481, -1.9265723, 22.219316, 13.223691, -3.25878, 21.565971, 10.925917, -3.6194124, 15.540272, 10.966446, -3.3071885, 15.0869465, 13.100228, -2.2530575, 18.560064, 13.147137, -1.4604888, 14.412212, 10.351378, -7.062508, 15.0869465, 13.100228, -2.2530575, 15.540272, 10.966446, -3.3071885, 18.560064, 13.147137, -1.4604888, 21.162558, 15.427648, -4.2870183, 22.219316, 13.223691, -3.25878, 14.412212, 10.351378, -7.062508, 14.188929, 13.313379, -6.7934413, 15.0869465, 13.100228, -2.2530575, 16.12682, 11.184851, -10.55514, 14.188929, 13.313379, -6.7934413, 14.412212, 10.351378, -7.062508, 15.0869465, 13.100228, -2.2530575, 18.959726, 16.180439, -3.5852652, 18.560064, 13.147137, -1.4604888, 18.560064, 13.147137, -1.4604888, 18.959726, 16.180439, -3.5852652, 21.162558, 15.427648, -4.2870183, 14.188929, 13.313379, -6.7934413, 16.63268, 15.373234, -3.8400722, 15.0869465, 13.100228, -2.2530575, 15.0869465, 13.100228, -2.2530575, 16.63268, 15.373234, -3.8400722, 18.959726, 16.180439, -3.5852652, 16.12682, 11.184851, -10.55514, 15.662611, 13.473243, -11.25802, 14.188929, 13.313379, -6.7934413, 15.662611, 13.473243, -11.25802, 16.12682, 11.184851, -10.55514, 19.275703, 10.291334, -11.8197975, 14.188929, 13.313379, -6.7934413, 16.33697, 16.221718, -6.44857, 16.63268, 15.373234, -3.8400722, 15.662611, 13.473243, -11.25802, 16.33697, 16.221718, -6.44857, 14.188929, 13.313379, -6.7934413, 16.63268, 15.373234, -3.8400722, 19.092947, 17.191414, -6.333738, 18.959726, 16.180439, -3.5852652, 16.63268, 15.373234, -3.8400722, 16.33697, 16.221718, -6.44857, 19.092947, 17.191414, -6.333738, 15.662611, 13.473243, -11.25802, 16.88824, 15.538725, -9.202295, 16.33697, 16.221718, -6.44857, 15.662611, 13.473243, -11.25802, 19.275703, 10.291334, -11.8197975, 19.303848, 13.265343, -11.823175, 16.88824, 15.538725, -9.202295, 15.662611, 13.473243, -11.25802, 19.303848, 13.265343, -11.823175, 19.303848, 13.265343, -11.823175, 19.275703, 10.291334, -11.8197975, 22.136757, 10.89139, -9.770828, 16.33697, 16.221718, -6.44857, 16.88824, 15.538725, -9.202295, 19.145485, 16.209707, -9.236071, 16.88824, 15.538725, -9.202295, 19.303848, 13.265343, -11.823175, 19.145485, 16.209707, -9.236071, 16.33697, 16.221718, -6.44857, 19.145485, 16.209707, -9.236071, 19.092947, 17.191414, -6.333738, 19.303848, 13.265343, -11.823175, 22.136757, 10.89139, -9.770828, 22.758953, 13.027798, -9.797472, 19.145485, 16.209707, -9.236071, 19.303848, 13.265343, -11.823175, 22.758953, 13.027798, -9.797472, 23.209652, 10.083061, -6.6834865, 22.758953, 13.027798, -9.797472, 22.136757, 10.89139, -9.770828, 19.092947, 17.191414, -6.333738, 19.145485, 16.209707, -9.236071, 21.402355, 15.34096, -8.55308, 19.145485, 16.209707, -9.236071, 22.758953, 13.027798, -9.797472, 21.402355, 15.34096, -8.55308, 23.209652, 10.083061, -6.6834865, 23.674986, 13.098725, -6.4902234, 22.758953, 13.027798, -9.797472, 23.674986, 13.098725, -6.4902234, 21.402355, 15.34096, -8.55308, 22.758953, 13.027798, -9.797472, 21.565971, 10.925917, -3.6194124, 23.674986, 13.098725, -6.4902234, 23.209652, 10.083061, -6.6834865, 21.565971, 10.925917, -3.6194124, 22.219316, 13.223691, -3.25878, 23.674986, 13.098725, -6.4902234, 23.674986, 13.098725, -6.4902234, 21.768618, 16.168053, -6.372765, 21.402355, 15.34096, -8.55308, 19.092947, 17.191414, -6.333738, 21.402355, 15.34096, -8.55308, 21.768618, 16.168053, -6.372765, 22.219316, 13.223691, -3.25878, 21.768618, 16.168053, -6.372765, 23.674986, 13.098725, -6.4902234, 18.959726, 16.180439, -3.5852652, 19.092947, 17.191414, -6.333738, 21.768618, 16.168053, -6.372765, 22.219316, 13.223691, -3.25878, 21.162558, 15.427648, -4.2870183, 21.768618, 16.168053, -6.372765, 18.959726, 16.180439, -3.5852652, 21.768618, 16.168053, -6.372765, 21.162558, 15.427648, -4.2870183, 16.12682, 11.184851, -10.55514, 18.77059, 9.221067, -6.949927, 19.275703, 10.291334, -11.8197975, 16.12682, 11.184851, -10.55514, 14.412212, 10.351378, -7.062508, 18.77059, 9.221067, -6.949927, 19.275703, 10.291334, -11.8197975, 18.77059, 9.221067, -6.949927, 23.209652, 10.083061, -6.6834865, 19.275703, 10.291334, -11.8197975, 23.209652, 10.083061, -6.6834865, 22.136757, 10.89139, -9.770828, 18.77059, 9.221067, -6.949927, 21.565971, 10.925917, -3.6194124, 23.209652, 10.083061, -6.6834865, 18.77059, 9.221067, -6.949927, 18.34616, 10.143481, -1.9265723, 21.565971, 10.925917, -3.6194124, 14.412212, 10.351378, -7.062508, 18.34616, 10.143481, -1.9265723, 18.77059, 9.221067, -6.949927, 14.412212, 10.351378, -7.062508, 15.540272, 10.966446, -3.3071885, 18.34616, 10.143481, -1.9265723, 11.936562, 9.984365, -5.3381457, 13.67631, 11.271915, -4.0618553, 13.421877, 9.512652, -4.639019, 13.421877, 9.512652, -4.639019, 13.67631, 11.271915, -4.0618553, 15.91254, 11.281297, -4.7531013, 13.421877, 9.512652, -4.639019, 15.91254, 11.281297, -4.7531013, 15.246437, 9.984365, -5.3381457, 11.936562, 9.984365, -5.3381457, 11.566921, 11.281297, -4.7531013, 13.67631, 11.271915, -4.0618553, 11.566921, 9.561062, -7.1582017, 11.566921, 11.281297, -4.7531013, 11.936562, 9.984365, -5.3381457, 13.67631, 11.271915, -4.0618553, 15.3699, 12.593614, -5.243579, 15.91254, 11.281297, -4.7531013, 11.566921, 9.561062, -7.1582017, 11.441956, 11.310567, -6.8275905, 11.566921, 11.281297, -4.7531013, 12.60454, 10.044408, -8.806385, 11.441956, 11.310567, -6.8275905, 11.566921, 9.561062, -7.1582017, 11.566921, 11.281297, -4.7531013, 14.057959, 13.050317, -4.8671837, 13.67631, 11.271915, -4.0618553, 13.67631, 11.271915, -4.0618553, 14.057959, 13.050317, -4.8671837, 15.3699, 12.593614, -5.243579, 11.441956, 11.310567, -6.8275905, 12.60454, 12.593614, -5.243579, 11.566921, 11.281297, -4.7531013, 11.566921, 11.281297, -4.7531013, 12.60454, 12.593614, -5.243579, 14.057959, 13.050317, -4.8671837, 12.60454, 10.044408, -8.806385, 12.561008, 11.339462, -8.901703, 11.441956, 11.310567, -6.8275905, 12.561008, 11.339462, -8.901703, 12.60454, 10.044408, -8.806385, 14.057959, 9.609095, -9.67701, 11.441956, 11.310567, -6.8275905, 12.561008, 13.059697, -6.4969783, 12.60454, 12.593614, -5.243579, 12.561008, 11.339462, -8.901703, 12.561008, 13.059697, -6.4969783, 11.441956, 11.310567, -6.8275905, 12.60454, 12.593614, -5.243579, 14.184799, 13.643242, -6.3866496, 14.057959, 13.050317, -4.8671837, 12.60454, 12.593614, -5.243579, 12.561008, 13.059697, -6.4969783, 14.184799, 13.643242, -6.3866496, 12.561008, 11.339462, -8.901703, 13.046232, 12.619131, -7.9215007, 12.561008, 13.059697, -6.4969783, 12.561008, 11.339462, -8.901703, 14.057959, 9.609095, -9.67701, 14.184799, 11.349218, -9.592951, 13.046232, 12.619131, -7.9215007, 12.561008, 11.339462, -8.901703, 14.184799, 11.349218, -9.592951, 14.184799, 11.349218, -9.592951, 14.057959, 9.609095, -9.67701, 15.3699, 10.044408, -8.806385, 12.561008, 13.059697, -6.4969783, 13.046232, 12.619131, -7.9215007, 14.184799, 13.069454, -8.126398, 13.046232, 12.619131, -7.9215007, 14.184799, 11.349218, -9.592951, 14.184799, 13.069454, -8.126398, 12.561008, 13.059697, -6.4969783, 14.184799, 13.069454, -8.126398, 14.184799, 13.643242, -6.3866496, 14.184799, 11.349218, -9.592951, 15.3699, 10.044408, -8.806385, 15.681749, 11.339462, -8.901703, 14.184799, 13.069454, -8.126398, 14.184799, 11.349218, -9.592951, 15.681749, 11.339462, -8.901703, 15.91254, 9.561062, -7.1582017, 15.681749, 11.339462, -8.901703, 15.3699, 10.044408, -8.806385, 14.184799, 13.643242, -6.3866496, 14.184799, 13.069454, -8.126398, 15.2670765, 12.619131, -7.9215007, 14.184799, 13.069454, -8.126398, 15.681749, 11.339462, -8.901703, 15.2670765, 12.619131, -7.9215007, 15.91254, 9.561062, -7.1582017, 16.419529, 11.310567, -6.8275905, 15.681749, 11.339462, -8.901703, 16.419529, 11.310567, -6.8275905, 15.2670765, 12.619131, -7.9215007, 15.681749, 11.339462, -8.901703, 15.246437, 9.984365, -5.3381457, 16.419529, 11.310567, -6.8275905, 15.91254, 9.561062, -7.1582017, 15.246437, 9.984365, -5.3381457, 15.91254, 11.281297, -4.7531013, 16.419529, 11.310567, -6.8275905, 16.419529, 11.310567, -6.8275905, 15.681749, 13.059697, -6.4969783, 15.2670765, 12.619131, -7.9215007, 14.184799, 13.643242, -6.3866496, 15.2670765, 12.619131, -7.9215007, 15.681749, 13.059697, -6.4969783, 15.91254, 11.281297, -4.7531013, 15.681749, 13.059697, -6.4969783, 16.419529, 11.310567, -6.8275905, 14.057959, 13.050317, -4.8671837, 14.184799, 13.643242, -6.3866496, 15.681749, 13.059697, -6.4969783, 15.91254, 11.281297, -4.7531013, 15.3699, 12.593614, -5.243579, 15.681749, 13.059697, -6.4969783, 14.057959, 13.050317, -4.8671837, 15.681749, 13.059697, -6.4969783, 15.3699, 12.593614, -5.243579, 12.60454, 10.044408, -8.806385, 13.67631, 8.977893, -7.2681556, 14.057959, 9.609095, -9.67701, 12.60454, 10.044408, -8.806385, 11.566921, 9.561062, -7.1582017, 13.67631, 8.977893, -7.2681556, 14.057959, 9.609095, -9.67701, 13.67631, 8.977893, -7.2681556, 15.91254, 9.561062, -7.1582017, 14.057959, 9.609095, -9.67701, 15.91254, 9.561062, -7.1582017, 15.3699, 10.044408, -8.806385, 13.67631, 8.977893, -7.2681556, 15.246437, 9.984365, -5.3381457, 15.91254, 9.561062, -7.1582017, 13.67631, 8.977893, -7.2681556, 13.421877, 9.512652, -4.639019, 15.246437, 9.984365, -5.3381457, 11.566921, 9.561062, -7.1582017, 13.421877, 9.512652, -4.639019, 13.67631, 8.977893, -7.2681556, 11.566921, 9.561062, -7.1582017, 11.936562, 9.984365, -5.3381457, 13.421877, 9.512652, -4.639019, 16.489328, 8.9163475, -10.894009, 16.04426, 9.775715, -9.523523, 16.35686, 8.601497, -9.805727, 16.35686, 8.601497, -9.805727, 16.04426, 9.775715, -9.523523, 16.95091, 9.78247, -8.25061, 16.35686, 8.601497, -9.805727, 16.95091, 9.78247, -8.25061, 17.182076, 8.9163475, -8.795502, 16.489328, 8.9163475, -10.894009, 16.040882, 9.782469, -11.005463, 16.04426, 9.775715, -9.523523, 17.565601, 8.633769, -11.509075, 16.040882, 9.782469, -11.005463, 16.489328, 8.9163475, -10.894009, 16.04426, 9.775715, -9.523523, 17.147926, 10.658348, -8.697557, 16.95091, 9.78247, -8.25061, 17.565601, 8.633769, -11.509075, 17.329557, 9.801607, -11.519207, 16.040882, 9.782469, -11.005463, 18.827631, 8.956501, -11.196474, 17.329557, 9.801607, -11.519207, 17.565601, 8.633769, -11.509075, 16.040882, 9.782469, -11.005463, 16.634933, 10.963442, -9.450348, 16.04426, 9.775715, -9.523523, 16.04426, 9.775715, -9.523523, 16.634933, 10.963442, -9.450348, 17.147926, 10.658348, -8.697557, 17.329557, 9.801607, -11.519207, 16.568886, 10.658348, -10.450439, 16.040882, 9.782469, -11.005463, 16.040882, 9.782469, -11.005463, 16.568886, 10.658348, -10.450439, 16.634933, 10.963442, -9.450348, 18.827631, 8.956501, -11.196474, 18.878668, 9.821121, -11.244135, 17.329557, 9.801607, -11.519207, 18.878668, 9.821121, -11.244135, 18.827631, 8.956501, -11.196474, 19.68362, 8.666042, -10.457569, 17.329557, 9.801607, -11.519207, 17.354324, 10.969821, -10.740524, 16.568886, 10.658348, -10.450439, 18.878668, 9.821121, -11.244135, 17.354324, 10.969821, -10.740524, 17.329557, 9.801607, -11.519207, 16.568886, 10.658348, -10.450439, 17.62452, 11.358975, -9.687893, 16.634933, 10.963442, -9.450348, 16.568886, 10.658348, -10.450439, 17.354324, 10.969821, -10.740524, 17.62452, 11.358975, -9.687893, 18.878668, 9.821121, -11.244135, 18.359297, 10.67561, -10.731142, 17.354324, 10.969821, -10.740524, 18.878668, 9.821121, -11.244135, 19.68362, 8.666042, -10.457569, 19.657352, 9.827501, -10.359249, 18.359297, 10.67561, -10.731142, 18.878668, 9.821121, -11.244135, 19.657352, 9.827501, -10.359249, 19.657352, 9.827501, -10.359249, 19.68362, 8.666042, -10.457569, 19.406672, 8.956502, -9.443592, 17.354324, 10.969821, -10.740524, 18.359297, 10.67561, -10.731142, 18.727434, 10.976201, -10.0522785, 18.359297, 10.67561, -10.731142, 19.657352, 9.827501, -10.359249, 18.727434, 10.976201, -10.0522785, 17.354324, 10.969821, -10.740524, 18.727434, 10.976201, -10.0522785, 17.62452, 11.358975, -9.687893, 19.657352, 9.827501, -10.359249, 19.406672, 8.956502, -9.443592, 19.532387, 9.821122, -9.265715, 18.727434, 10.976201, -10.0522785, 19.657352, 9.827501, -10.359249, 19.532387, 9.821122, -9.265715, 18.47563, 8.63377, -8.754221, 19.532387, 9.821122, -9.265715, 19.406672, 8.956502, -9.443592, 17.62452, 11.358975, -9.687893, 18.727434, 10.976201, -10.0522785, 18.824255, 10.675611, -9.323132, 18.727434, 10.976201, -10.0522785, 19.532387, 9.821122, -9.265715, 18.824255, 10.675611, -9.323132, 18.47563, 8.63377, -8.754221, 18.37168, 9.801608, -8.363567, 19.532387, 9.821122, -9.265715, 18.37168, 9.801608, -8.363567, 18.824255, 10.675611, -9.323132, 19.532387, 9.821122, -9.265715, 17.182076, 8.9163475, -8.795502, 18.37168, 9.801608, -8.363567, 18.47563, 8.63377, -8.754221, 17.182076, 8.9163475, -8.795502, 16.95091, 9.78247, -8.25061, 18.37168, 9.801608, -8.363567, 18.37168, 9.801608, -8.363567, 18.008043, 10.969822, -8.762104, 18.824255, 10.675611, -9.323132, 17.62452, 11.358975, -9.687893, 18.824255, 10.675611, -9.323132, 18.008043, 10.969822, -8.762104, 16.95091, 9.78247, -8.25061, 18.008043, 10.969822, -8.762104, 18.37168, 9.801608, -8.363567, 16.634933, 10.963442, -9.450348, 17.62452, 11.358975, -9.687893, 18.008043, 10.969822, -8.762104, 16.95091, 9.78247, -8.25061, 17.147926, 10.658348, -8.697557, 18.008043, 10.969822, -8.762104, 16.634933, 10.963442, -9.450348, 18.008043, 10.969822, -8.762104, 17.147926, 10.658348, -8.697557, 18.827631, 8.956501, -11.196474, 18.077093, 8.24424, -10.195255, 19.68362, 8.666042, -10.457569, 18.827631, 8.956501, -11.196474, 17.565601, 8.633769, -11.509075, 18.077093, 8.24424, -10.195255, 19.68362, 8.666042, -10.457569, 18.077093, 8.24424, -10.195255, 18.47563, 8.63377, -8.754221, 19.68362, 8.666042, -10.457569, 18.47563, 8.63377, -8.754221, 19.406672, 8.956502, -9.443592, 18.077093, 8.24424, -10.195255, 17.182076, 8.9163475, -8.795502, 18.47563, 8.63377, -8.754221, 18.077093, 8.24424, -10.195255, 16.35686, 8.601497, -9.805727, 17.182076, 8.9163475, -8.795502, 17.565601, 8.633769, -11.509075, 16.35686, 8.601497, -9.805727, 18.077093, 8.24424, -10.195255, 17.565601, 8.633769, -11.509075, 16.489328, 8.9163475, -10.894009, 16.35686, 8.601497, -9.805727, 18.97211, 7.05764, -6.3866477, 16.754269, 7.642685, -6.7833076, 18.871162, 6.770934, -6.8046975, 18.97211, 7.05764, -6.3866477, 16.92877, 7.8719745, -6.3866477, 16.754269, 7.642685, -6.7833076, 16.92877, 7.8719745, -6.3866477, 14.656889, 9.864279, -6.5018563, 16.754269, 7.642685, -6.7833076, 16.92877, 7.8719745, -6.3866477, 14.740574, 9.863528, -6.3866487, 14.656889, 9.864279, -6.5018563, 16.754269, 7.642685, -6.7833076, 14.656889, 9.864279, -6.5018563, 14.521416, 9.865405, -6.457949, 18.871162, 6.770934, -6.8046975, 16.754269, 7.642685, -6.7833076, 16.472067, 7.2715435, -6.631698, 16.754269, 7.642685, -6.7833076, 14.521416, 9.865405, -6.457949, 16.472067, 7.2715435, -6.631698, 18.871162, 6.770934, -6.8046975, 16.472067, 7.2715435, -6.631698, 18.70717, 6.3074765, -6.645208, 16.472067, 7.2715435, -6.631698, 14.521416, 9.865405, -6.457949, 14.521416, 9.865405, -6.315347, 18.70717, 6.3074765, -6.645208, 16.472067, 7.2715435, -6.631698, 16.472067, 7.2715435, -6.1415963, 16.472067, 7.2715435, -6.631698, 14.521416, 9.865405, -6.315347, 16.472067, 7.2715435, -6.1415963, 18.70717, 6.3074765, -6.645208, 16.472067, 7.2715435, -6.1415963, 18.70717, 6.307477, -6.1280866, 16.472067, 7.2715435, -6.1415963, 14.521416, 9.865405, -6.315347, 14.656889, 9.864279, -6.271441, 18.70717, 6.307477, -6.1280866, 16.472067, 7.2715435, -6.1415963, 16.754269, 7.642685, -5.989988, 16.472067, 7.2715435, -6.1415963, 14.656889, 9.864279, -6.271441, 16.754269, 7.642685, -5.989988, 16.754269, 7.642685, -5.989988, 14.656889, 9.864279, -6.271441, 14.740574, 9.863528, -6.3866487, 18.70717, 6.307477, -6.1280866, 16.754269, 7.642685, -5.989988, 18.871162, 6.770934, -5.968597, 16.754269, 7.642685, -5.989988, 14.740574, 9.863528, -6.3866487, 16.92877, 7.8719745, -6.3866477, 18.871162, 6.770934, -5.968597, 16.754269, 7.642685, -5.989988, 16.92877, 7.8719745, -6.3866477, 18.871162, 6.770934, -5.968597, 16.92877, 7.8719745, -6.3866477, 18.97211, 7.05764, -6.3866477, 16.917135, 3.148459, -6.386646, 18.67227, 6.762303, -5.9899874, 17.66805, 3.1484594, -5.2897334, 16.917135, 3.148459, -6.386646, 18.388943, 6.813715, -6.3866467, 18.67227, 6.762303, -5.9899874, 18.388943, 6.813715, -6.3866467, 18.814873, 10.392283, -6.2113976, 18.67227, 6.762303, -5.9899874, 18.388943, 6.813715, -6.3866467, 18.688406, 10.405793, -6.3866487, 18.814873, 10.392283, -6.2113976, 18.67227, 6.762303, -5.9899874, 18.814873, 10.392283, -6.2113976, 19.01977, 10.370893, -6.278196, 17.66805, 3.1484594, -5.2897334, 18.67227, 6.762303, -5.9899874, 19.13085, 6.678993, -6.1415963, 18.67227, 6.762303, -5.9899874, 19.01977, 10.370893, -6.278196, 19.13085, 6.678993, -6.1415963, 17.66805, 3.1484594, -5.2897334, 19.13085, 6.678993, -6.1415963, 18.883547, 3.1480842, -5.7085342, 19.13085, 6.678993, -6.1415963, 19.01977, 10.370893, -6.278196, 19.01977, 10.370893, -6.4951015, 18.883547, 3.1480842, -5.7085342, 19.13085, 6.678993, -6.1415963, 19.13085, 6.678993, -6.631698, 19.13085, 6.678993, -6.1415963, 19.01977, 10.370893, -6.4951015, 19.13085, 6.678993, -6.631698, 18.883547, 3.1480842, -5.7085342, 19.13085, 6.678993, -6.631698, 18.883547, 3.1480837, -7.064758, 19.13085, 6.678993, -6.631698, 19.01977, 10.370893, -6.4951015, 18.814873, 10.392283, -6.5618997, 18.883547, 3.1480837, -7.064758, 19.13085, 6.678993, -6.631698, 18.67227, 6.7623024, -6.7833066, 19.13085, 6.678993, -6.631698, 18.814873, 10.392283, -6.5618997, 18.67227, 6.7623024, -6.7833066, 18.67227, 6.7623024, -6.7833066, 18.814873, 10.392283, -6.5618997, 18.688406, 10.405793, -6.3866487, 18.883547, 3.1480837, -7.064758, 18.67227, 6.7623024, -6.7833066, 17.66805, 3.1484585, -7.483558, 18.67227, 6.7623024, -6.7833066, 18.688406, 10.405793, -6.3866487, 18.388943, 6.813715, -6.3866467, 17.66805, 3.1484585, -7.483558, 18.67227, 6.7623024, -6.7833066, 18.388943, 6.813715, -6.3866467, 17.66805, 3.1484585, -7.483558, 18.388943, 6.813715, -6.3866467, 16.917135, 3.148459, -6.386646, 18.627237, 6.9623213, -6.653088, 18.414085, 7.3529763, -8.142157, 18.871162, 6.770934, -6.8046975, 18.627237, 6.9623213, -6.653088, 18.199057, 7.5060863, -7.9485183, 18.414085, 7.3529763, -8.142157, 18.199057, 7.5060863, -7.9485183, 17.796392, 8.8360405, -9.412819, 18.414085, 7.3529763, -8.142157, 18.199057, 7.5060863, -7.9485183, 17.740852, 8.835665, -9.335514, 17.796392, 8.8360405, -9.412819, 18.414085, 7.3529763, -8.142157, 17.796392, 8.8360405, -9.412819, 17.7401, 8.836791, -9.489374, 18.871162, 6.770934, -6.8046975, 18.414085, 7.3529763, -8.142157, 18.258724, 7.1052985, -8.289263, 18.414085, 7.3529763, -8.142157, 17.7401, 8.836791, -9.489374, 18.258724, 7.1052985, -8.289263, 18.871162, 6.770934, -6.8046975, 18.258724, 7.1052985, -8.289263, 18.735315, 6.461337, -6.8752475, 18.258724, 7.1052985, -8.289263, 17.7401, 8.836791, -9.489374, 17.650038, 8.836791, -9.459728, 18.735315, 6.461337, -6.8752475, 18.258724, 7.1052985, -8.289263, 17.948, 7.1052985, -8.186813, 18.258724, 7.1052985, -8.289263, 17.650038, 8.836791, -9.459728, 17.948, 7.1052985, -8.186813, 18.735315, 6.461337, -6.8752475, 17.948, 7.1052985, -8.186813, 18.407705, 6.461337, -6.7671704, 17.948, 7.1052985, -8.186813, 17.650038, 8.836791, -9.459728, 17.650412, 8.8360405, -9.364784, 18.407705, 6.461337, -6.7671704, 17.948, 7.1052985, -8.186813, 17.911224, 7.3529763, -7.9759126, 17.948, 7.1052985, -8.186813, 17.650412, 8.8360405, -9.364784, 17.911224, 7.3529763, -7.9759126, 17.911224, 7.3529763, -7.9759126, 17.650412, 8.8360405, -9.364784, 17.740852, 8.835665, -9.335514, 18.407705, 6.461337, -6.7671704, 17.911224, 7.3529763, -7.9759126, 18.340908, 6.770934, -6.6298213, 17.911224, 7.3529763, -7.9759126, 17.740852, 8.835665, -9.335514, 18.199057, 7.5060863, -7.9485183, 18.340908, 6.770934, -6.6298213, 17.911224, 7.3529763, -7.9759126, 18.199057, 7.5060863, -7.9485183, 18.340908, 6.770934, -6.6298213, 18.199057, 7.5060863, -7.9485183, 18.627237, 6.9623213, -6.653088, -21.319002, 11.927798, 5.2962513, -17.92721, 14.377123, 7.370441, -18.167463, 11.003454, 6.846942, -18.167463, 11.003454, 6.846942, -17.92721, 14.377123, 7.370441, -13.817183, 14.463108, 5.3506236, -18.167463, 11.003454, 6.846942, -13.817183, 14.463108, 5.3506236, -14.551012, 11.882277, 4.945565, -21.319002, 11.927798, 5.2962513, -21.828173, 14.324436, 6.4802384, -17.92721, 14.377123, 7.370441, -22.586025, 11.236962, 1.0783223, -21.828173, 14.324436, 6.4802384, -21.319002, 11.927798, 5.2962513, -17.92721, 14.377123, 7.370441, -15.004122, 16.938564, 4.1957183, -13.817183, 14.463108, 5.3506236, -22.586025, 11.236962, 1.0783223, -22.836817, 14.563845, 1.3805351, -21.828173, 14.324436, 6.4802384, -20.660202, 12.173107, -2.8445582, -22.836817, 14.563845, 1.3805351, -22.586025, 11.236962, 1.0783223, -21.828173, 14.324436, 6.4802384, -17.478313, 17.78409, 4.983919, -17.92721, 14.377123, 7.370441, -17.92721, 14.377123, 7.370441, -17.478313, 17.78409, 4.983919, -15.004122, 16.938564, 4.1957183, -22.836817, 14.563845, 1.3805351, -20.092022, 16.877447, 4.6977224, -21.828173, 14.324436, 6.4802384, -21.828173, 14.324436, 6.4802384, -20.092022, 16.877447, 4.6977224, -17.478313, 17.78409, 4.983919, -20.660202, 12.173107, -2.8445582, -21.181595, 14.743401, -3.634025, -22.836817, 14.563845, 1.3805351, -21.181595, 14.743401, -3.634025, -20.660202, 12.173107, -2.8445582, -17.123413, 11.16952, -4.2650056, -22.836817, 14.563845, 1.3805351, -20.424162, 17.830452, 1.7678905, -20.092022, 16.877447, 4.6977224, -21.181595, 14.743401, -3.634025, -20.424162, 17.830452, 1.7678905, -22.836817, 14.563845, 1.3805351, -20.092022, 16.877447, 4.6977224, -17.328682, 18.919603, 1.8968685, -17.478313, 17.78409, 4.983919, -20.092022, 16.877447, 4.6977224, -20.424162, 17.830452, 1.7678905, -17.328682, 18.919603, 1.8968685, -21.181595, 14.743401, -3.634025, -19.804981, 17.063326, -1.3250608, -20.424162, 17.830452, 1.7678905, -21.181595, 14.743401, -3.634025, -17.123413, 11.16952, -4.2650056, -17.0918, 14.5098915, -4.2688007, -19.804981, 17.063326, -1.3250608, -21.181595, 14.743401, -3.634025, -17.0918, 14.5098915, -4.2688007, -17.0918, 14.5098915, -4.2688007, -17.123413, 11.16952, -4.2650056, -13.909914, 11.843496, -1.9636278, -20.424162, 17.830452, 1.7678905, -19.804981, 17.063326, -1.3250608, -17.269672, 17.816963, -1.3629956, -19.804981, 17.063326, -1.3250608, -17.0918, 14.5098915, -4.2688007, -17.269672, 17.816963, -1.3629956, -20.424162, 17.830452, 1.7678905, -17.269672, 17.816963, -1.3629956, -17.328682, 18.919603, 1.8968685, -17.0918, 14.5098915, -4.2688007, -13.909914, 11.843496, -1.9636278, -13.211069, 14.243084, -1.9935551, -17.269672, 17.816963, -1.3629956, -17.0918, 14.5098915, -4.2688007, -13.211069, 14.243084, -1.9935551, -12.704851, 10.935591, 1.5040352, -13.211069, 14.243084, -1.9935551, -13.909914, 11.843496, -1.9636278, -17.328682, 18.919603, 1.8968685, -17.269672, 17.816963, -1.3629956, -14.734785, 16.841198, -0.59586906, -17.269672, 17.816963, -1.3629956, -13.211069, 14.243084, -1.9935551, -14.734785, 16.841198, -0.59586906, -12.704851, 10.935591, 1.5040352, -12.182194, 14.322748, 1.7211055, -13.211069, 14.243084, -1.9935551, -12.182194, 14.322748, 1.7211055, -14.734785, 16.841198, -0.59586906, -13.211069, 14.243084, -1.9935551, -14.551012, 11.882277, 4.945565, -12.182194, 14.322748, 1.7211055, -12.704851, 10.935591, 1.5040352, -14.551012, 11.882277, 4.945565, -13.817183, 14.463108, 5.3506236, -12.182194, 14.322748, 1.7211055, -12.182194, 14.322748, 1.7211055, -14.323402, 17.77018, 1.8530331, -14.734785, 16.841198, -0.59586906, -17.328682, 18.919603, 1.8968685, -14.734785, 16.841198, -0.59586906, -14.323402, 17.77018, 1.8530331, -13.817183, 14.463108, 5.3506236, -14.323402, 17.77018, 1.8530331, -12.182194, 14.322748, 1.7211055, -17.478313, 17.78409, 4.983919, -17.328682, 18.919603, 1.8968685, -14.323402, 17.77018, 1.8530331, -13.817183, 14.463108, 5.3506236, -15.004122, 16.938564, 4.1957183, -14.323402, 17.77018, 1.8530331, -17.478313, 17.78409, 4.983919, -14.323402, 17.77018, 1.8530331, -15.004122, 16.938564, 4.1957183, -20.660202, 12.173107, -2.8445582, -17.690748, 9.96741, 1.2047721, -17.123413, 11.16952, -4.2650056, -20.660202, 12.173107, -2.8445582, -22.586025, 11.236962, 1.0783223, -17.690748, 9.96741, 1.2047721, -17.123413, 11.16952, -4.2650056, -17.690748, 9.96741, 1.2047721, -12.704851, 10.935591, 1.5040352, -17.123413, 11.16952, -4.2650056, -12.704851, 10.935591, 1.5040352, -13.909914, 11.843496, -1.9636278, -17.690748, 9.96741, 1.2047721, -14.551012, 11.882277, 4.945565, -12.704851, 10.935591, 1.5040352, -17.690748, 9.96741, 1.2047721, -18.167463, 11.003454, 6.846942, -14.551012, 11.882277, 4.945565, -22.586025, 11.236962, 1.0783223, -18.167463, 11.003454, 6.846942, -17.690748, 9.96741, 1.2047721, -22.586025, 11.236962, 1.0783223, -21.319002, 11.927798, 5.2962513, -18.167463, 11.003454, 6.846942, -25.366646, 10.824738, 3.0151052, -23.412582, 12.270897, 4.448619, -23.698359, 10.294915, 3.800356, -23.698359, 10.294915, 3.800356, -23.412582, 12.270897, 4.448619, -20.900877, 12.281435, 3.6722198, -23.698359, 10.294915, 3.800356, -20.900877, 12.281435, 3.6722198, -21.649035, 10.824738, 3.0151052, -25.366646, 10.824738, 3.0151052, -25.781822, 12.281435, 3.6722198, -23.412582, 12.270897, 4.448619, -25.781822, 10.349287, 0.97084063, -25.781822, 12.281435, 3.6722198, -25.366646, 10.824738, 3.0151052, -23.412582, 12.270897, 4.448619, -21.510363, 13.755413, 3.1213217, -20.900877, 12.281435, 3.6722198, -25.781822, 10.349287, 0.97084063, -25.92218, 12.31431, 1.3421795, -25.781822, 12.281435, 3.6722198, -24.61638, 10.892176, -0.88037825, -25.92218, 12.31431, 1.3421795, -25.781822, 10.349287, 0.97084063, -25.781822, 12.281435, 3.6722198, -22.98392, 14.268375, 3.5440836, -23.412582, 12.270897, 4.448619, -23.412582, 12.270897, 4.448619, -22.98392, 14.268375, 3.5440836, -21.510363, 13.755413, 3.1213217, -25.92218, 12.31431, 1.3421795, -24.61638, 13.755413, 3.1213217, -25.781822, 12.281435, 3.6722198, -25.781822, 12.281435, 3.6722198, -24.61638, 13.755413, 3.1213217, -22.98392, 14.268375, 3.5440836, -24.61638, 10.892176, -0.88037825, -24.665274, 12.3467655, -0.98743916, -25.92218, 12.31431, 1.3421795, -24.665274, 12.3467655, -0.98743916, -24.61638, 10.892176, -0.88037825, -22.98392, 10.403238, -1.858253, -25.92218, 12.31431, 1.3421795, -24.665274, 14.278912, 1.7135185, -24.61638, 13.755413, 3.1213217, -24.665274, 12.3467655, -0.98743916, -24.665274, 14.278912, 1.7135185, -25.92218, 12.31431, 1.3421795, -24.61638, 13.755413, 3.1213217, -22.841454, 14.934341, 1.8374386, -22.98392, 14.268375, 3.5440836, -24.61638, 13.755413, 3.1213217, -24.665274, 14.278912, 1.7135185, -22.841454, 14.934341, 1.8374386, -24.665274, 12.3467655, -0.98743916, -24.120277, 13.784073, 0.11351287, -24.665274, 14.278912, 1.7135185, -24.665274, 12.3467655, -0.98743916, -22.98392, 10.403238, -1.858253, -22.841454, 12.357724, -1.7638383, -24.120277, 13.784073, 0.11351287, -24.665274, 12.3467655, -0.98743916, -22.841454, 12.357724, -1.7638383, -22.841454, 12.357724, -1.7638383, -22.98392, 10.403238, -1.858253, -21.510363, 10.892176, -0.88037825, -24.665274, 14.278912, 1.7135185, -24.120277, 13.784073, 0.11351287, -22.841454, 14.28987, -0.11662531, -24.120277, 13.784073, 0.11351287, -22.841454, 12.357724, -1.7638383, -22.841454, 14.28987, -0.11662531, -24.665274, 14.278912, 1.7135185, -22.841454, 14.28987, -0.11662531, -22.841454, 14.934341, 1.8374386, -22.841454, 12.357724, -1.7638383, -21.510363, 10.892176, -0.88037825, -21.160097, 12.3467655, -0.98743916, -22.841454, 14.28987, -0.11662531, -22.841454, 12.357724, -1.7638383, -21.160097, 12.3467655, -0.98743916, -20.900877, 10.349287, 0.97084063, -21.160097, 12.3467655, -0.98743916, -21.510363, 10.892176, -0.88037825, -22.841454, 14.934341, 1.8374386, -22.841454, 14.28987, -0.11662531, -21.625853, 13.784073, 0.11351287, -22.841454, 14.28987, -0.11662531, -21.160097, 12.3467655, -0.98743916, -21.625853, 13.784073, 0.11351287, -20.900877, 10.349287, 0.97084063, -20.331432, 12.31431, 1.3421795, -21.160097, 12.3467655, -0.98743916, -20.331432, 12.31431, 1.3421795, -21.625853, 13.784073, 0.11351287, -21.160097, 12.3467655, -0.98743916, -21.649035, 10.824738, 3.0151052, -20.331432, 12.31431, 1.3421795, -20.900877, 10.349287, 0.97084063, -21.649035, 10.824738, 3.0151052, -20.900877, 12.281435, 3.6722198, -20.331432, 12.31431, 1.3421795, -20.331432, 12.31431, 1.3421795, -21.160097, 14.278912, 1.7135185, -21.625853, 13.784073, 0.11351287, -22.841454, 14.934341, 1.8374386, -21.625853, 13.784073, 0.11351287, -21.160097, 14.278912, 1.7135185, -20.900877, 12.281435, 3.6722198, -21.160097, 14.278912, 1.7135185, -20.331432, 12.31431, 1.3421795, -22.98392, 14.268375, 3.5440836, -22.841454, 14.934341, 1.8374386, -21.160097, 14.278912, 1.7135185, -20.900877, 12.281435, 3.6722198, -21.510363, 13.755413, 3.1213217, -21.160097, 14.278912, 1.7135185, -22.98392, 14.268375, 3.5440836, -21.160097, 14.278912, 1.7135185, -21.510363, 13.755413, 3.1213217, -24.61638, 10.892176, -0.88037825, -23.412582, 9.69428, 0.847342, -22.98392, 10.403238, -1.858253, -24.61638, 10.892176, -0.88037825, -25.781822, 10.349287, 0.97084063, -23.412582, 9.69428, 0.847342, -22.98392, 10.403238, -1.858253, -23.412582, 9.69428, 0.847342, -20.900877, 10.349287, 0.97084063, -22.98392, 10.403238, -1.858253, -20.900877, 10.349287, 0.97084063, -21.510363, 10.892176, -0.88037825, -23.412582, 9.69428, 0.847342, -21.649035, 10.824738, 3.0151052, -20.900877, 10.349287, 0.97084063, -23.412582, 9.69428, 0.847342, -23.698359, 10.294915, 3.800356, -21.649035, 10.824738, 3.0151052, -25.781822, 10.349287, 0.97084063, -23.698359, 10.294915, 3.800356, -23.412582, 9.69428, 0.847342, -25.781822, 10.349287, 0.97084063, -25.366646, 10.824738, 3.0151052, -23.698359, 10.294915, 3.800356, -20.253035, 9.625153, -3.2251701, -20.752932, 10.590384, -1.6858602, -20.401823, 9.271517, -2.0028262, -20.401823, 9.271517, -2.0028262, -20.752932, 10.590384, -1.6858602, -19.734592, 10.597971, -0.25613976, -20.401823, 9.271517, -2.0028262, -19.734592, 10.597971, -0.25613976, -19.474949, 9.625154, -0.8681545, -20.253035, 9.625153, -3.2251701, -20.756723, 10.59797, -3.3503551, -20.752932, 10.590384, -1.6858602, -19.044178, 9.307764, -3.9160056, -20.756723, 10.59797, -3.3503551, -20.253035, 9.625153, -3.2251701, -20.752932, 10.590384, -1.6858602, -19.513306, 11.581746, -0.7581439, -19.734592, 10.597971, -0.25613976, -19.044178, 9.307764, -3.9160056, -19.309301, 10.619466, -3.9273863, -20.756723, 10.59797, -3.3503551, -17.62668, 9.670253, -3.5648975, -19.309301, 10.619466, -3.9273863, -19.044178, 9.307764, -3.9160056, -20.756723, 10.59797, -3.3503551, -20.089493, 11.924424, -1.6036687, -20.752932, 10.590384, -1.6858602, -20.752932, 10.590384, -1.6858602, -20.089493, 11.924424, -1.6036687, -19.513306, 11.581746, -0.7581439, -19.309301, 10.619466, -3.9273863, -20.163677, 11.581746, -2.7269602, -20.756723, 10.59797, -3.3503551, -20.756723, 10.59797, -3.3503551, -20.163677, 11.581746, -2.7269602, -20.089493, 11.924424, -1.6036687, -17.62668, 9.670253, -3.5648975, -17.569357, 10.641384, -3.6184282, -19.309301, 10.619466, -3.9273863, -17.569357, 10.641384, -3.6184282, -17.62668, 9.670253, -3.5648975, -16.665245, 9.344013, -2.7349682, -19.309301, 10.619466, -3.9273863, -19.281483, 11.931589, -3.0527787, -20.163677, 11.581746, -2.7269602, -17.569357, 10.641384, -3.6184282, -19.281483, 11.931589, -3.0527787, -19.309301, 10.619466, -3.9273863, -20.163677, 11.581746, -2.7269602, -18.978003, 12.368683, -1.8704772, -20.089493, 11.924424, -1.6036687, -20.163677, 11.581746, -2.7269602, -19.281483, 11.931589, -3.0527787, -18.978003, 12.368683, -1.8704772, -17.569357, 10.641384, -3.6184282, -18.15271, 11.601134, -3.0422406, -19.281483, 11.931589, -3.0527787, -17.569357, 10.641384, -3.6184282, -16.665245, 9.344013, -2.7349682, -16.69475, 10.64855, -2.624536, -18.15271, 11.601134, -3.0422406, -17.569357, 10.641384, -3.6184282, -16.69475, 10.64855, -2.624536, -16.69475, 10.64855, -2.624536, -16.665245, 9.344013, -2.7349682, -16.97631, 9.670254, -1.5960813, -19.281483, 11.931589, -3.0527787, -18.15271, 11.601134, -3.0422406, -17.739222, 11.938755, -2.2797513, -18.15271, 11.601134, -3.0422406, -16.69475, 10.64855, -2.624536, -17.739222, 11.938755, -2.2797513, -19.281483, 11.931589, -3.0527787, -17.739222, 11.938755, -2.2797513, -18.978003, 12.368683, -1.8704772, -16.69475, 10.64855, -2.624536, -16.97631, 9.670254, -1.5960813, -16.835108, 10.641384, -1.3962913, -17.739222, 11.938755, -2.2797513, -16.69475, 10.64855, -2.624536, -16.835108, 10.641384, -1.3962913, -18.022047, 9.307766, -0.82178926, -16.835108, 10.641384, -1.3962913, -16.97631, 9.670254, -1.5960813, -18.978003, 12.368683, -1.8704772, -17.739222, 11.938755, -2.2797513, -17.630474, 11.601135, -1.4607811, -17.739222, 11.938755, -2.2797513, -16.835108, 10.641384, -1.3962913, -17.630474, 11.601135, -1.4607811, -18.022047, 9.307766, -0.82178926, -18.138802, 10.619467, -0.38301063, -16.835108, 10.641384, -1.3962913, -18.138802, 10.619467, -0.38301063, -17.630474, 11.601135, -1.4607811, -16.835108, 10.641384, -1.3962913, -19.474949, 9.625154, -0.8681545, -18.138802, 10.619467, -0.38301063, -18.022047, 9.307766, -0.82178926, -19.474949, 9.625154, -0.8681545, -19.734592, 10.597971, -0.25613976, -18.138802, 10.619467, -0.38301063, -18.138802, 10.619467, -0.38301063, -18.547232, 11.93159, -0.83064175, -17.630474, 11.601135, -1.4607811, -18.978003, 12.368683, -1.8704772, -17.630474, 11.601135, -1.4607811, -18.547232, 11.93159, -0.83064175, -19.734592, 10.597971, -0.25613976, -18.547232, 11.93159, -0.83064175, -18.138802, 10.619467, -0.38301063, -20.089493, 11.924424, -1.6036687, -18.978003, 12.368683, -1.8704772, -18.547232, 11.93159, -0.83064175, -19.734592, 10.597971, -0.25613976, -19.513306, 11.581746, -0.7581439, -18.547232, 11.93159, -0.83064175, -20.089493, 11.924424, -1.6036687, -18.547232, 11.93159, -0.83064175, -19.513306, 11.581746, -0.7581439, -17.62668, 9.670253, -3.5648975, -18.469677, 8.870251, -2.440341, -16.665245, 9.344013, -2.7349682, -17.62668, 9.670253, -3.5648975, -19.044178, 9.307764, -3.9160056, -18.469677, 8.870251, -2.440341, -16.665245, 9.344013, -2.7349682, -18.469677, 8.870251, -2.440341, -18.022047, 9.307766, -0.82178926, -16.665245, 9.344013, -2.7349682, -18.022047, 9.307766, -0.82178926, -16.97631, 9.670254, -1.5960813, -18.469677, 8.870251, -2.440341, -19.474949, 9.625154, -0.8681545, -18.022047, 9.307766, -0.82178926, -18.469677, 8.870251, -2.440341, -20.401823, 9.271517, -2.0028262, -19.474949, 9.625154, -0.8681545, -19.044178, 9.307764, -3.9160056, -20.401823, 9.271517, -2.0028262, -18.469677, 8.870251, -2.440341, -19.044178, 9.307764, -3.9160056, -20.253035, 9.625153, -3.2251701, -20.401823, 9.271517, -2.0028262, -17.464405, 7.5374756, 1.8374412, -19.955458, 8.194591, 1.3919177, -17.577787, 7.2154512, 1.3678927, -17.464405, 7.5374756, 1.8374412, -19.75946, 8.452126, 1.837441, -19.955458, 8.194591, 1.3919177, -19.75946, 8.452126, 1.837441, -22.311209, 10.6898575, 1.7080402, -19.955458, 8.194591, 1.3919177, -19.75946, 8.452126, 1.837441, -22.217215, 10.689015, 1.8374401, -22.311209, 10.6898575, 1.7080402, -19.955458, 8.194591, 1.3919177, -22.311209, 10.6898575, 1.7080402, -22.46337, 10.691122, 1.7573556, -17.577787, 7.2154512, 1.3678927, -19.955458, 8.194591, 1.3919177, -20.272423, 7.777729, 1.562203, -19.955458, 8.194591, 1.3919177, -22.46337, 10.691122, 1.7573556, -20.272423, 7.777729, 1.562203, -17.577787, 7.2154512, 1.3678927, -20.272423, 7.777729, 1.562203, -17.761982, 6.694901, 1.5470295, -20.272423, 7.777729, 1.562203, -22.46337, 10.691122, 1.7573556, -22.46337, 10.691122, 1.9175248, -17.761982, 6.694901, 1.5470295, -20.272423, 7.777729, 1.562203, -20.272423, 7.777729, 2.1126792, -20.272423, 7.777729, 1.562203, -22.46337, 10.691122, 1.9175248, -20.272423, 7.777729, 2.1126792, -17.761982, 6.694901, 1.5470295, -20.272423, 7.777729, 2.1126792, -17.761982, 6.6949015, 2.1278536, -20.272423, 7.777729, 2.1126792, -22.46337, 10.691122, 1.9175248, -22.311209, 10.689858, 1.96684, -17.761982, 6.6949015, 2.1278536, -20.272423, 7.777729, 2.1126792, -19.955458, 8.194591, 2.2829642, -20.272423, 7.777729, 2.1126792, -22.311209, 10.689858, 1.96684, -19.955458, 8.194591, 2.2829642, -19.955458, 8.194591, 2.2829642, -22.311209, 10.689858, 1.96684, -22.217215, 10.689015, 1.8374401, -17.761982, 6.6949015, 2.1278536, -19.955458, 8.194591, 2.2829642, -17.577787, 7.2154512, 2.3069901, -19.955458, 8.194591, 2.2829642, -22.217215, 10.689015, 1.8374401, -19.75946, 8.452126, 1.837441, -17.577787, 7.2154512, 2.3069901, -19.955458, 8.194591, 2.2829642, -19.75946, 8.452126, 1.837441, -17.577787, 7.2154512, 2.3069901, -19.75946, 8.452126, 1.837441, -17.464405, 7.5374756, 1.8374412, -19.772528, 3.1467323, 1.8374429, -17.801182, 7.2057567, 2.2829647, -18.92911, 3.1467328, 3.069481, -19.772528, 3.1467323, 1.8374429, -18.119411, 7.263502, 1.8374413, -17.801182, 7.2057567, 2.2829647, -18.119411, 7.263502, 1.8374413, -17.641012, 11.282906, 2.0342793, -17.801182, 7.2057567, 2.2829647, -18.119411, 7.263502, 1.8374413, -17.783056, 11.2980795, 1.8374399, -17.641012, 11.282906, 2.0342793, -17.801182, 7.2057567, 2.2829647, -17.641012, 11.282906, 2.0342793, -17.410875, 11.258881, 1.9592528, -18.92911, 3.1467328, 3.069481, -17.801182, 7.2057567, 2.2829647, -17.286112, 7.1121845, 2.1126795, -17.801182, 7.2057567, 2.2829647, -17.410875, 11.258881, 1.9592528, -17.286112, 7.1121845, 2.1126795, -18.92911, 3.1467328, 3.069481, -17.286112, 7.1121845, 2.1126795, -17.563877, 3.1463113, 2.5990896, -17.286112, 7.1121845, 2.1126795, -17.410875, 11.258881, 1.9592528, -17.410875, 11.25888, 1.715627, -17.563877, 3.1463113, 2.5990896, -17.286112, 7.1121845, 2.1126795, -17.286112, 7.112184, 1.5622033, -17.286112, 7.1121845, 2.1126795, -17.410875, 11.25888, 1.715627, -17.286112, 7.112184, 1.5622033, -17.563877, 3.1463113, 2.5990896, -17.286112, 7.112184, 1.5622033, -17.563877, 3.1463108, 1.0757962, -17.286112, 7.112184, 1.5622033, -17.410875, 11.25888, 1.715627, -17.641012, 11.282906, 1.6406004, -17.563877, 3.1463108, 1.0757962, -17.286112, 7.112184, 1.5622033, -17.801182, 7.205756, 1.3919182, -17.286112, 7.112184, 1.5622033, -17.641012, 11.282906, 1.6406004, -17.801182, 7.205756, 1.3919182, -17.801182, 7.205756, 1.3919182, -17.641012, 11.282906, 1.6406004, -17.783056, 11.2980795, 1.8374399, -17.563877, 3.1463108, 1.0757962, -17.801182, 7.205756, 1.3919182, -18.92911, 3.1467319, 0.6054046, -17.801182, 7.205756, 1.3919182, -17.783056, 11.2980795, 1.8374399, -18.119411, 7.263502, 1.8374413, -18.92911, 3.1467319, 0.6054046, -17.801182, 7.205756, 1.3919182, -18.119411, 7.263502, 1.8374413, -18.92911, 3.1467319, 0.6054046, -18.119411, 7.263502, 1.8374413, -19.772528, 3.1467323, 1.8374429, -17.85176, 7.430415, 1.5381777, -18.091171, 7.869194, -0.13432598, -17.577787, 7.2154512, 1.3678927, -17.85176, 7.430415, 1.5381777, -18.33269, 8.041164, 0.08316696, -18.091171, 7.869194, -0.13432598, -18.33269, 8.041164, 0.08316696, -18.784958, 9.534952, -1.5615182, -18.091171, 7.869194, -0.13432598, -18.33269, 8.041164, 0.08316696, -18.84734, 9.534531, -1.4746895, -18.784958, 9.534952, -1.5615182, -18.091171, 7.869194, -0.13432598, -18.784958, 9.534952, -1.5615182, -18.848183, 9.535795, -1.6475039, -17.577787, 7.2154512, 1.3678927, -18.091171, 7.869194, -0.13432598, -18.265673, 7.591005, -0.29955292, -18.091171, 7.869194, -0.13432598, -18.848183, 9.535795, -1.6475039, -18.265673, 7.591005, -0.29955292, -17.577787, 7.2154512, 1.3678927, -18.265673, 7.591005, -0.29955292, -17.73037, 6.8677154, 1.2886512, -18.265673, 7.591005, -0.29955292, -18.848183, 9.535795, -1.6475039, -18.94934, 9.535795, -1.6142054, -17.73037, 6.8677154, 1.2886512, -18.265673, 7.591005, -0.29955292, -18.614672, 7.5910053, -0.184484, -18.265673, 7.591005, -0.29955292, -18.94934, 9.535795, -1.6142054, -18.614672, 7.5910053, -0.184484, -17.73037, 6.8677154, 1.2886512, -18.614672, 7.5910053, -0.184484, -18.098337, 6.8677154, 1.4100426, -18.614672, 7.5910053, -0.184484, -18.94934, 9.535795, -1.6142054, -18.94892, 9.534952, -1.5075665, -18.098337, 6.8677154, 1.4100426, -18.614672, 7.5910053, -0.184484, -18.65598, 7.869194, 0.052397728, -18.614672, 7.5910053, -0.184484, -18.94892, 9.534952, -1.5075665, -18.65598, 7.869194, 0.052397728, -18.65598, 7.869194, 0.052397728, -18.94892, 9.534952, -1.5075665, -18.84734, 9.534531, -1.4746895, -18.098337, 6.8677154, 1.4100426, -18.65598, 7.869194, 0.052397728, -18.173365, 7.2154512, 1.5643108, -18.65598, 7.869194, 0.052397728, -18.84734, 9.534531, -1.4746895, -18.33269, 8.041164, 0.08316696, -18.173365, 7.2154512, 1.5643108, -18.65598, 7.869194, 0.052397728, -18.33269, 8.041164, 0.08316696, -18.173365, 7.2154512, 1.5643108, -18.33269, 8.041164, 0.08316696, -17.85176, 7.430415, 1.5381777, -18.021715, 16.363796, -17.998596, -21.99729, 20.020504, -13.591129, -21.163076, 14.9838, -13.799871, -21.163076, 14.9838, -13.799871, -21.99729, 20.020504, -13.591129, -20.16242, 20.14888, -7.004975, -21.163076, 14.9838, -13.799871, -20.16242, 20.14888, -7.004975, -19.366415, 16.295837, -7.970553, -18.021715, 16.363796, -17.998596, -19.61928, 19.941845, -19.071045, -21.99729, 20.020504, -13.591129, -11.484028, 15.332416, -18.699133, -19.61928, 19.941845, -19.071045, -18.021715, 16.363796, -17.998596, -21.99729, 20.020504, -13.591129, -18.141575, 23.8446, -8.429474, -20.16242, 20.14888, -7.004975, -11.484028, 15.332416, -18.699133, -11.858614, 20.29927, -19.150183, -19.61928, 19.941845, -19.071045, -6.256456, 16.730034, -14.795378, -11.858614, 20.29927, -19.150183, -11.484028, 15.332416, -18.699133, -19.61928, 19.941845, -19.071045, -18.618507, 25.106922, -12.27677, -21.99729, 20.020504, -13.591129, -21.99729, 20.020504, -13.591129, -18.618507, 25.106922, -12.27677, -18.141575, 23.8446, -8.429474, -11.858614, 20.29927, -19.150183, -17.480484, 23.753355, -16.033634, -19.61928, 19.941845, -19.071045, -19.61928, 19.941845, -19.071045, -17.480484, 23.753355, -16.033634, -18.618507, 25.106922, -12.27677, -6.256456, 16.730034, -14.795378, -4.954714, 20.567343, -15.343615, -11.858614, 20.29927, -19.150183, -4.954714, 20.567343, -15.343615, -6.256456, 16.730034, -14.795378, -5.143649, 15.231735, -9.215082, -11.858614, 20.29927, -19.150183, -13.089851, 25.176142, -15.716146, -17.480484, 23.753355, -16.033634, -4.954714, 20.567343, -15.343615, -13.089851, 25.176142, -15.716146, -11.858614, 20.29927, -19.150183, -17.480484, 23.753355, -16.033634, -14.129517, 26.802185, -11.209112, -18.618507, 25.106922, -12.27677, -17.480484, 23.753355, -16.033634, -13.089851, 25.176142, -15.716146, -14.129517, 26.802185, -11.209112, -4.954714, 20.567343, -15.343615, -8.721195, 24.030865, -13.9578285, -13.089851, 25.176142, -15.716146, -4.954714, 20.567343, -15.343615, -5.143649, 15.231735, -9.215082, -5.146767, 20.218727, -9.167652, -8.721195, 24.030865, -13.9578285, -4.954714, 20.567343, -15.343615, -5.146767, 20.218727, -9.167652, -5.146767, 20.218727, -9.167652, -5.143649, 15.231735, -9.215082, -9.403629, 16.237946, -5.1316648, -13.089851, 25.176142, -15.716146, -8.721195, 24.030865, -13.9578285, -9.362032, 25.156006, -10.226961, -8.721195, 24.030865, -13.9578285, -5.146767, 20.218727, -9.167652, -9.362032, 25.156006, -10.226961, -13.089851, 25.176142, -15.716146, -9.362032, 25.156006, -10.226961, -14.129517, 26.802185, -11.209112, -5.146767, 20.218727, -9.167652, -9.403629, 16.237946, -5.1316648, -9.5517025, 19.8204, -4.0979257, -9.362032, 25.156006, -10.226961, -5.146767, 20.218727, -9.167652, -9.5517025, 19.8204, -4.0979257, -14.823317, 14.88249, -4.3159366, -9.5517025, 19.8204, -4.0979257, -9.403629, 16.237946, -5.1316648, -14.129517, 26.802185, -11.209112, -9.362032, 25.156006, -10.226961, -11.184143, 23.699242, -6.7178807, -9.362032, 25.156006, -10.226961, -9.5517025, 19.8204, -4.0979257, -11.184143, 23.699242, -6.7178807, -14.823317, 14.88249, -4.3159366, -15.285444, 19.939333, -3.6085982, -9.5517025, 19.8204, -4.0979257, -15.285444, 19.939333, -3.6085982, -11.184143, 23.699242, -6.7178807, -9.5517025, 19.8204, -4.0979257, -19.366415, 16.295837, -7.970553, -15.285444, 19.939333, -3.6085982, -14.823317, 14.88249, -4.3159366, -19.366415, 16.295837, -7.970553, -20.16242, 20.14888, -7.004975, -15.285444, 19.939333, -3.6085982, -15.285444, 19.939333, -3.6085982, -14.890806, 25.086159, -6.786965, -11.184143, 23.699242, -6.7178807, -14.129517, 26.802185, -11.209112, -11.184143, 23.699242, -6.7178807, -14.890806, 25.086159, -6.786965, -20.16242, 20.14888, -7.004975, -14.890806, 25.086159, -6.786965, -15.285444, 19.939333, -3.6085982, -18.618507, 25.106922, -12.27677, -14.129517, 26.802185, -11.209112, -14.890806, 25.086159, -6.786965, -20.16242, 20.14888, -7.004975, -18.141575, 23.8446, -8.429474, -14.890806, 25.086159, -6.786965, -18.618507, 25.106922, -12.27677, -14.890806, 25.086159, -6.786965, -18.141575, 23.8446, -8.429474, -6.256456, 16.730034, -14.795378, -13.014425, 13.437046, -11.550287, -5.143649, 15.231735, -9.215082, -6.256456, 16.730034, -14.795378, -11.484028, 15.332416, -18.699133, -13.014425, 13.437046, -11.550287, -5.143649, 15.231735, -9.215082, -13.014425, 13.437046, -11.550287, -14.823317, 14.88249, -4.3159366, -5.143649, 15.231735, -9.215082, -14.823317, 14.88249, -4.3159366, -9.403629, 16.237946, -5.1316648, -13.014425, 13.437046, -11.550287, -19.366415, 16.295837, -7.970553, -14.823317, 14.88249, -4.3159366, -13.014425, 13.437046, -11.550287, -21.163076, 14.9838, -13.799871, -19.366415, 16.295837, -7.970553, -11.484028, 15.332416, -18.699133, -21.163076, 14.9838, -13.799871, -13.014425, 13.437046, -11.550287, -11.484028, 15.332416, -18.699133, -18.021715, 16.363796, -17.998596, -21.163076, 14.9838, -13.799871, -13.562267, 14.716985, -23.311638, -16.202703, 16.876022, -20.837961, -15.172899, 13.925989, -21.079227, -15.172899, 13.925989, -21.079227, -16.202703, 16.876022, -20.837961, -15.753397, 16.891756, -16.93886, -15.172899, 13.925989, -21.079227, -15.753397, 16.891756, -16.93886, -14.583576, 14.716987, -17.856222, -13.562267, 14.716985, -23.311638, -14.412495, 16.891754, -24.101412, -16.202703, 16.876022, -20.837961, -10.448349, 14.007164, -23.359283, -14.412495, 16.891754, -24.101412, -13.562267, 14.716985, -23.311638, -16.202703, 16.876022, -20.837961, -14.77754, 19.092325, -17.681908, -15.753397, 16.891756, -16.93886, -10.448349, 14.007164, -23.359283, -10.954712, 16.940838, -23.667269, -14.412495, 16.891754, -24.101412, -8.051947, 14.817669, -21.140484, -10.954712, 16.940838, -23.667269, -10.448349, 14.007164, -23.359283, -14.412495, 16.891754, -24.101412, -14.993107, 19.85815, -19.960423, -16.202703, 16.876022, -20.837961, -16.202703, 16.876022, -20.837961, -14.993107, 19.85815, -19.960423, -14.77754, 19.092325, -17.681908, -10.954712, 16.940838, -23.667269, -13.92425, 19.092323, -22.23984, -14.412495, 16.891754, -24.101412, -14.412495, 16.891754, -24.101412, -13.92425, 19.092323, -22.23984, -14.993107, 19.85815, -19.960423, -8.051947, 14.817669, -21.140484, -7.881408, 16.989292, -21.182821, -10.954712, 16.940838, -23.667269, -7.881408, 16.989292, -21.182821, -8.051947, 14.817669, -21.140484, -7.065434, 14.087713, -18.47628, -10.954712, 16.940838, -23.667269, -11.844934, 19.873882, -21.924835, -13.92425, 19.092323, -22.23984, -7.881408, 16.989292, -21.182821, -11.844934, 19.873882, -21.924835, -10.954712, 16.940838, -23.667269, -13.92425, 19.092323, -22.23984, -12.527824, 20.852402, -19.282509, -14.993107, 19.85815, -19.960423, -13.92425, 19.092323, -22.23984, -11.844934, 19.873882, -21.924835, -12.527824, 20.852402, -19.282509, -7.881408, 16.989292, -21.182821, -9.646725, 19.135115, -20.68552, -11.844934, 19.873882, -21.924835, -7.881408, 16.989292, -21.182821, -7.065434, 14.087713, -18.47628, -7.2431235, 17.005653, -18.293158, -9.646725, 19.135115, -20.68552, -7.881408, 16.989292, -21.182821, -7.2431235, 17.005653, -18.293158, -7.2431235, 17.005653, -18.293158, -7.065434, 14.087713, -18.47628, -8.905237, 14.817671, -16.582552, -11.844934, 19.873882, -21.924835, -9.646725, 19.135115, -20.68552, -9.66033, 19.890244, -18.745684, -9.646725, 19.135115, -20.68552, -7.2431235, 17.005653, -18.293158, -9.66033, 19.890244, -18.745684, -11.844934, 19.873882, -21.924835, -9.66033, 19.890244, -18.745684, -12.527824, 20.852402, -19.282509, -7.2431235, 17.005653, -18.293158, -8.905237, 14.817671, -16.582552, -8.844357, 16.989294, -16.039145, -9.66033, 19.890244, -18.745684, -7.2431235, 17.005653, -18.293158, -8.844357, 16.989294, -16.039145, -11.789251, 14.007167, -16.19673, -8.844357, 16.989294, -16.039145, -8.905237, 14.817671, -16.582552, -12.527824, 20.852402, -19.282509, -9.66033, 19.890244, -18.745684, -10.331999, 19.135117, -17.025074, -9.66033, 19.890244, -18.745684, -8.844357, 16.989294, -16.039145, -10.331999, 19.135117, -17.025074, -11.789251, 14.007167, -16.19673, -12.490612, 16.94084, -15.463116, -8.844357, 16.989294, -16.039145, -12.490612, 16.94084, -15.463116, -10.331999, 19.135117, -17.025074, -8.844357, 16.989294, -16.039145, -14.583576, 14.716987, -17.856222, -12.490612, 16.94084, -15.463116, -11.789251, 14.007167, -16.19673, -14.583576, 14.716987, -17.856222, -15.753397, 16.891756, -16.93886, -12.490612, 16.94084, -15.463116, -12.490612, 16.94084, -15.463116, -12.807882, 19.873884, -16.781157, -10.331999, 19.135117, -17.025074, -12.527824, 20.852402, -19.282509, -10.331999, 19.135117, -17.025074, -12.807882, 19.873884, -16.781157, -15.753397, 16.891756, -16.93886, -12.807882, 19.873884, -16.781157, -12.490612, 16.94084, -15.463116, -14.993107, 19.85815, -19.960423, -12.527824, 20.852402, -19.282509, -12.807882, 19.873884, -16.781157, -15.753397, 16.891756, -16.93886, -14.77754, 19.092325, -17.681908, -12.807882, 19.873884, -16.781157, -14.993107, 19.85815, -19.960423, -12.807882, 19.873884, -16.781157, -14.77754, 19.092325, -17.681908, -8.051947, 14.817669, -21.140484, -10.918001, 13.029274, -19.84861, -7.065434, 14.087713, -18.47628, -8.051947, 14.817669, -21.140484, -10.448349, 14.007164, -23.359283, -10.918001, 13.029274, -19.84861, -7.065434, 14.087713, -18.47628, -10.918001, 13.029274, -19.84861, -11.789251, 14.007167, -16.19673, -7.065434, 14.087713, -18.47628, -11.789251, 14.007167, -16.19673, -8.905237, 14.817671, -16.582552, -10.918001, 13.029274, -19.84861, -14.583576, 14.716987, -17.856222, -11.789251, 14.007167, -16.19673, -10.918001, 13.029274, -19.84861, -15.172899, 13.925989, -21.079227, -14.583576, 14.716987, -17.856222, -10.448349, 14.007164, -23.359283, -15.172899, 13.925989, -21.079227, -10.918001, 13.029274, -19.84861, -10.448349, 14.007164, -23.359283, -13.562267, 14.716985, -23.311638, -15.172899, 13.925989, -21.079227, -5.8097816, 12.926075, -14.093317, -7.931314, 14.367111, -15.249773, -7.562637, 12.398115, -14.647461, -7.562637, 12.398115, -14.647461, -7.931314, 14.367111, -15.249773, -10.30912, 14.378439, -14.148186, -7.562637, 12.398115, -14.647461, -10.30912, 14.378439, -14.148186, -9.4823475, 12.926076, -13.599037, -5.8097816, 12.926075, -14.093317, -5.4877048, 14.378439, -14.798067, -7.931314, 14.367111, -15.249773, -5.1281137, 12.452233, -12.129591, -5.4877048, 14.378439, -14.798067, -5.8097816, 12.926075, -14.093317, -7.931314, 14.367111, -15.249773, -9.633245, 15.847163, -13.685547, -10.30912, 14.378439, -14.148186, -5.1281137, 12.452233, -12.129591, -5.0385785, 14.410532, -12.51552, -5.4877048, 14.378439, -14.798067, -6.032765, 12.993409, -10.145939, -5.0385785, 14.410532, -12.51552, -5.1281137, 12.452233, -12.129591, -5.4877048, 14.378439, -14.798067, -8.234186, 16.358763, -14.29879, -7.931314, 14.367111, -15.249773, -7.931314, 14.367111, -15.249773, -8.234186, 16.358763, -14.29879, -9.633245, 15.847163, -13.685547, -5.0385785, 14.410532, -12.51552, -6.56543, 15.847163, -14.099058, -5.4877048, 14.378439, -14.798067, -5.4877048, 14.378439, -14.798067, -6.56543, 15.847163, -14.099058, -8.234186, 16.358763, -14.29879, -6.032765, 12.993409, -10.145939, -5.9699607, 14.443255, -10.047113, -5.0385785, 14.410532, -12.51552, -5.9699607, 14.443255, -10.047113, -6.032765, 12.993409, -10.145939, -7.514774, 12.506351, -8.963076, -5.0385785, 14.410532, -12.51552, -6.3296666, 16.369461, -12.714972, -6.56543, 15.847163, -14.099058, -5.9699607, 14.443255, -10.047113, -6.3296666, 16.369461, -12.714972, -5.0385785, 14.410532, -12.51552, -6.56543, 15.847163, -14.099058, -8.14801, 17.022017, -12.594435, -8.234186, 16.358763, -14.29879, -6.56543, 15.847163, -14.099058, -6.3296666, 16.369461, -12.714972, -8.14801, 17.022017, -12.594435, -5.9699607, 14.443255, -10.047113, -6.655227, 15.876111, -11.061449, -6.3296666, 16.369461, -12.714972, -5.9699607, 14.443255, -10.047113, -7.514774, 12.506351, -8.963076, -7.6687236, 14.453954, -9.036713, -6.655227, 15.876111, -11.061449, -5.9699607, 14.443255, -10.047113, -7.6687236, 14.453954, -9.036713, -7.6687236, 14.453954, -9.036713, -7.514774, 12.506351, -8.963076, -9.10058, 12.993409, -9.732428, -6.3296666, 16.369461, -12.714972, -6.655227, 15.876111, -11.061449, -7.8877397, 16.38016, -10.6641445, -6.655227, 15.876111, -11.061449, -7.6687236, 14.453954, -9.036713, -7.8877397, 16.38016, -10.6641445, -6.3296666, 16.369461, -12.714972, -7.8877397, 16.38016, -10.6641445, -8.14801, 17.022017, -12.594435, -7.6687236, 14.453954, -9.036713, -9.10058, 12.993409, -9.732428, -9.432554, 14.443255, -9.580109, -7.8877397, 16.38016, -10.6641445, -7.6687236, 14.453954, -9.036713, -9.432554, 14.443255, -9.580109, -9.94953, 12.452233, -11.47971, -9.432554, 14.443255, -9.580109, -9.10058, 12.993409, -9.732428, -8.14801, 17.022017, -12.594435, -7.8877397, 16.38016, -10.6641445, -9.119413, 15.876111, -10.729553, -7.8877397, 16.38016, -10.6641445, -9.432554, 14.443255, -9.580109, -9.119413, 15.876111, -10.729553, -9.94953, 12.452233, -11.47971, -10.561342, 14.410533, -11.771585, -9.432554, 14.443255, -9.580109, -10.561342, 14.410533, -11.771585, -9.119413, 15.876111, -10.729553, -9.432554, 14.443255, -9.580109, -9.4823475, 12.926076, -13.599037, -10.561342, 14.410533, -11.771585, -9.94953, 12.452233, -11.47971, -9.4823475, 12.926076, -13.599037, -10.30912, 14.378439, -14.148186, -10.561342, 14.410533, -11.771585, -10.561342, 14.410533, -11.771585, -9.792259, 16.369461, -12.247965, -9.119413, 15.876111, -10.729553, -8.14801, 17.022017, -12.594435, -9.119413, 15.876111, -10.729553, -9.792259, 16.369461, -12.247965, -10.30912, 14.378439, -14.148186, -9.792259, 16.369461, -12.247965, -10.561342, 14.410533, -11.771585, -8.234186, 16.358763, -14.29879, -8.14801, 17.022017, -12.594435, -9.792259, 16.369461, -12.247965, -10.30912, 14.378439, -14.148186, -9.633245, 15.847163, -13.685547, -9.792259, 16.369461, -12.247965, -8.234186, 16.358763, -14.29879, -9.792259, 16.369461, -12.247965, -9.633245, 15.847163, -13.685547, -6.032765, 12.993409, -10.145939, -7.451408, 11.799047, -11.691935, -7.514774, 12.506351, -8.963076, -6.032765, 12.993409, -10.145939, -5.1281137, 12.452233, -12.129591, -7.451408, 11.799047, -11.691935, -7.514774, 12.506351, -8.963076, -7.451408, 11.799047, -11.691935, -9.94953, 12.452233, -11.47971, -7.514774, 12.506351, -8.963076, -9.94953, 12.452233, -11.47971, -9.10058, 12.993409, -9.732428, -7.451408, 11.799047, -11.691935, -9.4823475, 12.926076, -13.599037, -9.94953, 12.452233, -11.47971, -7.451408, 11.799047, -11.691935, -7.562637, 12.398115, -14.647461, -9.4823475, 12.926076, -13.599037, -5.1281137, 12.452233, -12.129591, -7.562637, 12.398115, -14.647461, -7.451408, 11.799047, -11.691935, -5.1281137, 12.452233, -12.129591, -5.8097816, 12.926075, -14.093317, -7.562637, 12.398115, -14.647461, -14.005018, 9.809285, -11.391945, -12.666887, 10.790321, -14.925049, -13.284828, 9.32852, -11.429333, -14.005018, 9.809285, -11.391945, -13.374516, 11.174807, -14.759829, -12.666887, 10.790321, -14.925049, -13.374516, 11.174807, -14.759829, -12.483606, 14.515619, -18.46885, -12.666887, 10.790321, -14.925049, -13.374516, 11.174807, -14.759829, -12.699318, 14.51436, -18.366467, -12.483606, 14.515619, -18.46885, -12.666887, 10.790321, -14.925049, -12.483606, 14.515619, -18.46885, -12.5141735, 14.517507, -18.705687, -13.284828, 9.32852, -11.429333, -12.666887, 10.790321, -14.925049, -12.829695, 10.167969, -15.436964, -12.666887, 10.790321, -14.925049, -12.5141735, 14.517507, -18.705687, -12.829695, 10.167969, -15.436964, -13.284828, 9.32852, -11.429333, -12.829695, 10.167969, -15.436964, -13.497101, 8.551367, -11.748842, -12.829695, 10.167969, -15.436964, -12.5141735, 14.517507, -18.705687, -12.749214, 14.517507, -18.749687, -13.497101, 8.551367, -11.748842, -12.829695, 10.167969, -15.436964, -13.637493, 10.167969, -15.588192, -12.829695, 10.167969, -15.436964, -12.749214, 14.517507, -18.749687, -13.637493, 10.167969, -15.588192, -13.497101, 8.551367, -11.748842, -13.637493, 10.167969, -15.588192, -14.349432, 8.551367, -11.908407, -13.637493, 10.167969, -15.588192, -12.749214, 14.517507, -18.749687, -12.863382, 14.515619, -18.539948, -14.349432, 8.551367, -11.908407, -13.637493, 10.167969, -15.588192, -13.974455, 10.790321, -15.16984, -13.637493, 10.167969, -15.588192, -12.863382, 14.515619, -18.539948, -13.974455, 10.790321, -15.16984, -13.974455, 10.790321, -15.16984, -12.863382, 14.515619, -18.539948, -12.699318, 14.51436, -18.366467, -14.349432, 8.551367, -11.908407, -13.974455, 10.790321, -15.16984, -14.662909, 9.32852, -11.687325, -13.974455, 10.790321, -15.16984, -12.699318, 14.51436, -18.366467, -13.374516, 11.174807, -14.759829, -14.662909, 9.32852, -11.687325, -13.974455, 10.790321, -15.16984, -13.374516, 11.174807, -14.759829, -14.662909, 9.32852, -11.687325, -13.374516, 11.174807, -14.759829, -14.005018, 9.809285, -11.391945, -13.370926, 3.2541418, -14.779001, -14.566282, 9.314047, -12.008543, -15.410589, 3.2541418, -13.879795, -13.370926, 3.2541418, -14.779001, -13.825073, 9.400257, -12.353138, -14.566282, 9.314047, -12.008543, -13.825073, 9.400257, -12.353138, -14.245352, 15.401009, -11.705187, -14.566282, 9.314047, -12.008543, -13.825073, 9.400257, -12.353138, -13.917477, 15.423663, -11.859554, -14.245352, 15.401009, -11.705187, -14.566282, 9.314047, -12.008543, -14.245352, 15.401009, -11.705187, -14.198479, 15.365141, -11.346859, -15.410589, 3.2541418, -13.879795, -14.566282, 9.314047, -12.008543, -14.457897, 9.174348, -11.205921, -14.566282, 9.314047, -12.008543, -14.198479, 15.365141, -11.346859, -14.457897, 9.174348, -11.205921, -15.410589, 3.2541418, -13.879795, -14.457897, 9.174348, -11.205921, -15.095371, 3.2535143, -11.747156, -14.457897, 9.174348, -11.205921, -14.198479, 15.365141, -11.346859, -13.840969, 15.365141, -11.27993, -15.095371, 3.2535143, -11.747156, -14.457897, 9.174348, -11.205921, -13.650101, 9.174348, -11.054693, -14.457897, 9.174348, -11.205921, -13.840969, 15.365141, -11.27993, -13.650101, 9.174348, -11.054693, -15.095371, 3.2535143, -11.747156, -13.650101, 9.174348, -11.054693, -12.86001, 3.2535143, -11.328674, -13.650101, 9.174348, -11.054693, -13.840969, 15.365141, -11.27993, -13.667648, 15.401009, -11.597034, -12.86001, 3.2535143, -11.328674, -13.650101, 9.174348, -11.054693, -13.258715, 9.314047, -11.763754, -13.650101, 9.174348, -11.054693, -13.667648, 15.401009, -11.597034, -13.258715, 9.314047, -11.763754, -13.258715, 9.314047, -11.763754, -13.667648, 15.401009, -11.597034, -13.917477, 15.423663, -11.859554, -12.86001, 3.2535143, -11.328674, -13.258715, 9.314047, -11.763754, -11.794674, 3.2541418, -13.20286, -13.258715, 9.314047, -11.763754, -13.917477, 15.423663, -11.859554, -13.825073, 9.400257, -12.353138, -11.794674, 3.2541418, -13.20286, -13.258715, 9.314047, -11.763754, -13.825073, 9.400257, -12.353138, -11.794674, 3.2541418, -13.20286, -13.825073, 9.400257, -12.353138, -13.370926, 3.2541418, -14.779001, -13.459448, 9.649449, -11.878159, -10.939358, 10.304523, -11.770008, -13.284828, 9.32852, -11.429333, -13.459448, 9.649449, -11.878159, -11.192167, 10.561266, -12.1841755, -10.939358, 10.304523, -11.770008, -11.192167, 10.561266, -12.1841755, -8.654425, 12.791411, -12.396025, -10.939358, 10.304523, -11.770008, -11.192167, 10.561266, -12.1841755, -8.764704, 12.790783, -12.511421, -8.654425, 12.791411, -12.396025, -10.939358, 10.304523, -11.770008, -8.654425, 12.791411, -12.396025, -8.510876, 12.79267, -12.465183, -13.284828, 9.32852, -11.429333, -10.939358, 10.304523, -11.770008, -10.648956, 9.889202, -11.980688, -10.939358, 10.304523, -11.770008, -8.510876, 12.79267, -12.465183, -10.648956, 9.889202, -11.980688, -13.284828, 9.32852, -11.429333, -10.648956, 9.889202, -11.980688, -13.126629, 8.809369, -11.631472, -10.648956, 9.889202, -11.980688, -8.510876, 12.79267, -12.465183, -8.531948, 12.79267, -12.622778, -13.126629, 8.809369, -11.631472, -10.648956, 9.889202, -11.980688, -10.721935, 9.889202, -12.524441, -10.648956, 9.889202, -11.980688, -8.531948, 12.79267, -12.622778, -10.721935, 9.889202, -12.524441, -13.126629, 8.809369, -11.631472, -10.721935, 9.889202, -12.524441, -13.203674, 8.809369, -12.204794, -10.721935, 9.889202, -12.524441, -8.531948, 12.79267, -12.622778, -8.688553, 12.791411, -12.651455, -13.203674, 8.809369, -12.204794, -10.721935, 9.889202, -12.524441, -11.058201, 10.304523, -12.650133, -10.721935, 9.889202, -12.524441, -8.688553, 12.791411, -12.651455, -11.058201, 10.304523, -12.650133, -11.058201, 10.304523, -12.650133, -8.688553, 12.791411, -12.651455, -8.764704, 12.790783, -12.511421, -13.203674, 8.809369, -12.204794, -11.058201, 10.304523, -12.650133, -13.409445, 9.32852, -12.357273, -11.058201, 10.304523, -12.650133, -8.764704, 12.790783, -12.511421, -11.192167, 10.561266, -12.1841755, -13.409445, 9.32852, -12.357273, -11.058201, 10.304523, -12.650133, -11.192167, 10.561266, -12.1841755, -13.409445, 9.32852, -12.357273, -11.192167, 10.561266, -12.1841755, -13.459448, 9.649449, -11.878159, 1.2086613, -17.361605, 9.414331, 2.6316407, -16.334206, 10.284523, 2.5310369, -17.749496, 10.064945, 2.5310369, -17.749496, 10.064945, 2.6316407, -16.334206, 10.284523, 4.3560047, -16.29811, 9.437185, 2.5310369, -17.749496, 10.064945, 4.3560047, -16.29811, 9.437185, 4.048212, -17.380829, 9.267162, 1.2086613, -17.361605, 9.414331, 0.99485135, -16.356207, 9.910942, 2.6316407, -16.334206, 10.284523, 0.67701983, -17.651457, 7.6446843, 0.99485135, -16.356207, 9.910942, 1.2086613, -17.361605, 9.414331, 2.6316407, -16.334206, 10.284523, 3.8578978, -15.259176, 8.952535, 4.3560047, -16.29811, 9.437185, 0.67701983, -17.651457, 7.6446843, 0.57171667, -16.255604, 7.7713466, 0.99485135, -16.356207, 9.910942, 1.4848415, -17.258867, 5.998924, 0.57171667, -16.255604, 7.7713466, 0.67701983, -17.651457, 7.6446843, 0.99485135, -16.356207, 9.910942, 2.8200326, -14.9048195, 9.283182, 2.6316407, -16.334206, 10.284523, 2.6316407, -16.334206, 10.284523, 2.8200326, -14.9048195, 9.283182, 3.8578978, -15.259176, 8.952535, 0.57171667, -16.255604, 7.7713466, 1.7232151, -15.285234, 9.163141, 0.99485135, -16.356207, 9.910942, 0.99485135, -16.356207, 9.910942, 1.7232151, -15.285234, 9.163141, 2.8200326, -14.9048195, 9.283182, 1.4848415, -17.258867, 5.998924, 1.2661188, -16.180418, 5.667422, 0.57171667, -16.255604, 7.7713466, 1.2661188, -16.180418, 5.667422, 1.4848415, -17.258867, 5.998924, 2.9689095, -17.679865, 5.4027762, 0.57171667, -16.255604, 7.7713466, 1.5839503, -14.885169, 7.933893, 1.7232151, -15.285234, 9.163141, 1.2661188, -16.180418, 5.667422, 1.5839503, -14.885169, 7.933893, 0.57171667, -16.255604, 7.7713466, 1.7232151, -15.285234, 9.163141, 2.8828301, -14.428287, 7.9881463, 2.8200326, -14.9048195, 9.283182, 1.7232151, -15.285234, 9.163141, 1.5839503, -14.885169, 7.933893, 2.8828301, -14.428287, 7.9881463, 1.2661188, -16.180418, 5.667422, 1.8436835, -15.206845, 6.636295, 1.5839503, -14.885169, 7.933893, 1.2661188, -16.180418, 5.667422, 2.9689095, -17.679865, 5.4027762, 2.9821525, -16.27846, 5.4012804, 1.8436835, -15.206845, 6.636295, 1.2661188, -16.180418, 5.667422, 2.9821525, -16.27846, 5.4012804, 2.9821525, -16.27846, 5.4012804, 2.9689095, -17.679865, 5.4027762, 4.31713, -17.397064, 6.3682323, 1.5839503, -14.885169, 7.933893, 1.8436835, -15.206845, 6.636295, 2.9076073, -14.890723, 6.620275, 1.8436835, -15.206845, 6.636295, 2.9821525, -16.27846, 5.4012804, 2.9076073, -14.890723, 6.620275, 1.5839503, -14.885169, 7.933893, 2.9076073, -14.890723, 6.620275, 2.8828301, -14.428287, 7.9881463, 2.9821525, -16.27846, 5.4012804, 4.31713, -17.397064, 6.3682323, 4.6103983, -16.39017, 6.355843, 2.9076073, -14.890723, 6.620275, 2.9821525, -16.27846, 5.4012804, 4.6103983, -16.39017, 6.355843, 4.822713, -17.777906, 7.8232512, 4.6103983, -16.39017, 6.355843, 4.31713, -17.397064, 6.3682323, 2.8828301, -14.428287, 7.9881463, 2.9076073, -14.890723, 6.620275, 3.9711037, -15.300187, 6.942165, 2.9076073, -14.890723, 6.620275, 4.6103983, -16.39017, 6.355843, 3.9711037, -15.300187, 6.942165, 4.822713, -17.777906, 7.8232512, 5.0420766, -16.356634, 7.9142427, 4.6103983, -16.39017, 6.355843, 5.0420766, -16.356634, 7.9142427, 3.9711037, -15.300187, 6.942165, 4.6103983, -16.39017, 6.355843, 4.048212, -17.380829, 9.267162, 5.0420766, -16.356634, 7.9142427, 4.822713, -17.777906, 7.8232512, 4.048212, -17.380829, 9.267162, 4.3560047, -16.29811, 9.437185, 5.0420766, -16.356634, 7.9142427, 5.0420766, -16.356634, 7.9142427, 4.1434765, -14.910374, 7.969777, 3.9711037, -15.300187, 6.942165, 2.8828301, -14.428287, 7.9881463, 3.9711037, -15.300187, 6.942165, 4.1434765, -14.910374, 7.969777, 4.3560047, -16.29811, 9.437185, 4.1434765, -14.910374, 7.969777, 5.0420766, -16.356634, 7.9142427, 2.8200326, -14.9048195, 9.283182, 2.8828301, -14.428287, 7.9881463, 4.1434765, -14.910374, 7.969777, 4.3560047, -16.29811, 9.437185, 3.8578978, -15.259176, 8.952535, 4.1434765, -14.910374, 7.969777, 2.8200326, -14.9048195, 9.283182, 4.1434765, -14.910374, 7.969777, 3.8578978, -15.259176, 8.952535, 1.4848415, -17.258867, 5.998924, 2.730963, -18.184166, 7.6976566, 2.9689095, -17.679865, 5.4027762, 1.4848415, -17.258867, 5.998924, 0.67701983, -17.651457, 7.6446843, 2.730963, -18.184166, 7.6976566, 2.9689095, -17.679865, 5.4027762, 2.730963, -18.184166, 7.6976566, 4.822713, -17.777906, 7.8232512, 2.9689095, -17.679865, 5.4027762, 4.822713, -17.777906, 7.8232512, 4.31713, -17.397064, 6.3682323, 2.730963, -18.184166, 7.6976566, 4.048212, -17.380829, 9.267162, 4.822713, -17.777906, 7.8232512, 2.730963, -18.184166, 7.6976566, 2.5310369, -17.749496, 10.064945, 4.048212, -17.380829, 9.267162, 0.67701983, -17.651457, 7.6446843, 2.5310369, -17.749496, 10.064945, 2.730963, -18.184166, 7.6976566, 0.67701983, -17.651457, 7.6446843, 1.2086613, -17.361605, 9.414331, 2.5310369, -17.749496, 10.064945, -1.1748614, -18.139952, 8.584936, -0.18462825, -17.4071, 9.311378, -0.3294468, -18.408442, 8.982866, -0.3294468, -18.408442, 8.982866, -0.18462825, -17.4071, 9.311378, 1.0884066, -17.401762, 8.9179325, -0.3294468, -18.408442, 8.982866, 1.0884066, -17.401762, 8.9179325, 0.70905924, -18.139952, 8.584936, -1.1748614, -18.139952, 8.584936, -1.3850403, -17.401762, 8.9179325, -0.18462825, -17.4071, 9.311378, -1.3850403, -18.381102, 7.5489936, -1.3850403, -17.401762, 8.9179325, -1.1748614, -18.139952, 8.584936, -0.18462825, -17.4071, 9.311378, 0.7793324, -16.65503, 8.6387615, 1.0884066, -17.401762, 8.9179325, -1.3850403, -18.381102, 7.5489936, -1.4563813, -17.385315, 7.7371716, -1.3850403, -17.401762, 8.9179325, -0.7946596, -18.105776, 6.610878, -1.4563813, -17.385315, 7.7371716, -1.3850403, -18.381102, 7.5489936, -1.3850403, -17.401762, 8.9179325, 0.03259945, -16.395082, 8.852999, -0.18462825, -17.4071, 9.311378, -0.18462825, -17.4071, 9.311378, 0.03259945, -16.395082, 8.852999, 0.7793324, -16.65503, 8.6387615, -1.4563813, -17.385315, 7.7371716, -0.7946596, -16.65503, 8.6387615, -1.3850403, -17.401762, 8.9179325, -1.3850403, -17.401762, 8.9179325, -0.7946596, -16.65503, 8.6387615, 0.03259945, -16.395082, 8.852999, -0.7946596, -18.105776, 6.610878, -0.8192229, -17.368868, 6.556624, -1.4563813, -17.385315, 7.7371716, -0.8192229, -17.368868, 6.556624, -0.7946596, -18.105776, 6.610878, 0.03259945, -18.353548, 6.115334, -1.4563813, -17.385315, 7.7371716, -0.8192229, -16.389528, 7.9253497, -0.7946596, -16.65503, 8.6387615, -0.8192229, -17.368868, 6.556624, -0.8192229, -16.389528, 7.9253497, -1.4563813, -17.385315, 7.7371716, -0.7946596, -16.65503, 8.6387615, 0.1050086, -16.0576, 7.988147, 0.03259945, -16.395082, 8.852999, -0.7946596, -16.65503, 8.6387615, -0.8192229, -16.389528, 7.9253497, 0.1050086, -16.0576, 7.988147, -0.8192229, -17.368868, 6.556624, -0.54325676, -16.640291, 7.1145377, -0.8192229, -16.389528, 7.9253497, -0.8192229, -17.368868, 6.556624, 0.03259945, -18.353548, 6.115334, 0.1050086, -17.363316, 6.1631794, -0.54325676, -16.640291, 7.1145377, -0.8192229, -17.368868, 6.556624, 0.1050086, -17.363316, 6.1631794, 0.1050086, -17.363316, 6.1631794, 0.03259945, -18.353548, 6.115334, 0.7793324, -18.105776, 6.610878, -0.8192229, -16.389528, 7.9253497, -0.54325676, -16.640291, 7.1145377, 0.1050086, -16.383976, 6.997914, -0.54325676, -16.640291, 7.1145377, 0.1050086, -17.363316, 6.1631794, 0.1050086, -16.383976, 6.997914, -0.8192229, -16.389528, 7.9253497, 0.1050086, -16.383976, 6.997914, 0.1050086, -16.0576, 7.988147, 0.1050086, -17.363316, 6.1631794, 0.7793324, -18.105776, 6.610878, 0.9570446, -17.368868, 6.556624, 0.1050086, -16.383976, 6.997914, 0.1050086, -17.363316, 6.1631794, 0.9570446, -17.368868, 6.556624, 1.0884066, -18.381102, 7.5489936, 0.9570446, -17.368868, 6.556624, 0.7793324, -18.105776, 6.610878, 0.1050086, -16.0576, 7.988147, 0.1050086, -16.383976, 6.997914, 0.7210206, -16.640291, 7.1145377, 0.1050086, -16.383976, 6.997914, 0.9570446, -17.368868, 6.556624, 0.7210206, -16.640291, 7.1145377, 1.0884066, -18.381102, 7.5489936, 1.3767617, -17.385315, 7.7371716, 0.9570446, -17.368868, 6.556624, 1.3767617, -17.385315, 7.7371716, 0.7210206, -16.640291, 7.1145377, 0.9570446, -17.368868, 6.556624, 0.70905924, -18.139952, 8.584936, 1.3767617, -17.385315, 7.7371716, 1.0884066, -18.381102, 7.5489936, 0.70905924, -18.139952, 8.584936, 1.0884066, -17.401762, 8.9179325, 1.3767617, -17.385315, 7.7371716, 1.3767617, -17.385315, 7.7371716, 0.9570446, -16.389528, 7.9253497, 0.7210206, -16.640291, 7.1145377, 0.1050086, -16.0576, 7.988147, 0.7210206, -16.640291, 7.1145377, 0.9570446, -16.389528, 7.9253497, 1.0884066, -17.401762, 8.9179325, 0.9570446, -16.389528, 7.9253497, 1.3767617, -17.385315, 7.7371716, 0.03259945, -16.395082, 8.852999, 0.1050086, -16.0576, 7.988147, 0.9570446, -16.389528, 7.9253497, 1.0884066, -17.401762, 8.9179325, 0.7793324, -16.65503, 8.6387615, 0.9570446, -16.389528, 7.9253497, 0.03259945, -16.395082, 8.852999, 0.9570446, -16.389528, 7.9253497, 0.7793324, -16.65503, 8.6387615, -0.7946596, -18.105776, 6.610878, -0.18462825, -18.712818, 7.48641, 0.03259945, -18.353548, 6.115334, -0.7946596, -18.105776, 6.610878, -1.3850403, -18.381102, 7.5489936, -0.18462825, -18.712818, 7.48641, 0.03259945, -18.353548, 6.115334, -0.18462825, -18.712818, 7.48641, 1.0884066, -18.381102, 7.5489936, 0.03259945, -18.353548, 6.115334, 1.0884066, -18.381102, 7.5489936, 0.7793324, -18.105776, 6.610878, -0.18462825, -18.712818, 7.48641, 0.70905924, -18.139952, 8.584936, 1.0884066, -18.381102, 7.5489936, -0.18462825, -18.712818, 7.48641, -0.3294468, -18.408442, 8.982866, 0.70905924, -18.139952, 8.584936, -1.3850403, -18.381102, 7.5489936, -0.3294468, -18.408442, 8.982866, -0.18462825, -18.712818, 7.48641, -1.3850403, -18.381102, 7.5489936, -1.1748614, -18.139952, 8.584936, -0.3294468, -18.408442, 8.982866, 2.6649618, -19.72975, 7.988148, 1.5668625, -19.4399, 7.791853, 2.6147666, -19.871578, 7.781173, 2.6649618, -19.72975, 7.988148, 1.6531554, -19.326265, 7.988148, 1.5668625, -19.4399, 7.791853, 1.6531554, -19.326265, 7.988148, 0.52835655, -18.340092, 7.9311175, 1.5668625, -19.4399, 7.791853, 1.6531554, -19.326265, 7.988148, 0.5697943, -18.340305, 7.9881477, 0.52835655, -18.340092, 7.9311175, 1.5668625, -19.4399, 7.791853, 0.52835655, -18.340092, 7.9311175, 0.46128738, -18.33945, 7.952904, 2.6147666, -19.871578, 7.781173, 1.5668625, -19.4399, 7.791853, 1.4271704, -19.623806, 7.8668256, 1.5668625, -19.4399, 7.791853, 0.46128738, -18.33945, 7.952904, 1.4271704, -19.623806, 7.8668256, 2.6147666, -19.871578, 7.781173, 1.4271704, -19.623806, 7.8668256, 2.5338135, -20.101194, 7.860204, 1.4271704, -19.623806, 7.8668256, 0.46128738, -18.33945, 7.952904, 0.46128738, -18.33945, 8.023392, 2.5338135, -20.101194, 7.860204, 1.4271704, -19.623806, 7.8668256, 1.4271704, -19.623806, 8.10947, 1.4271704, -19.623806, 7.8668256, 0.46128738, -18.33945, 8.023392, 1.4271704, -19.623806, 8.10947, 2.5338135, -20.101194, 7.860204, 1.4271704, -19.623806, 8.10947, 2.5338135, -20.101194, 8.116093, 1.4271704, -19.623806, 8.10947, 0.46128738, -18.33945, 8.023392, 0.52835655, -18.340092, 8.045177, 2.5338135, -20.101194, 8.116093, 1.4271704, -19.623806, 8.10947, 1.5668625, -19.4399, 8.184443, 1.4271704, -19.623806, 8.10947, 0.52835655, -18.340092, 8.045177, 1.5668625, -19.4399, 8.184443, 1.5668625, -19.4399, 8.184443, 0.52835655, -18.340092, 8.045177, 0.5697943, -18.340305, 7.9881477, 2.5338135, -20.101194, 8.116093, 1.5668625, -19.4399, 8.184443, 2.6147666, -19.871578, 8.195124, 1.5668625, -19.4399, 8.184443, 0.5697943, -18.340305, 7.9881477, 1.6531554, -19.326265, 7.988148, 2.6147666, -19.871578, 8.195124, 1.5668625, -19.4399, 8.184443, 1.6531554, -19.326265, 7.988148, 2.6147666, -19.871578, 8.195124, 1.6531554, -19.326265, 7.988148, 2.6649618, -19.72975, 7.988148, 1.5335414, -22.031038, 7.988149, 2.532532, -19.97389, 8.213706, 1.9611615, -22.031036, 8.612492, 1.5335414, -22.031038, 7.988149, 2.3712666, -19.944628, 7.988148, 2.532532, -19.97389, 8.213706, 2.3712666, -19.944628, 7.988148, 2.6136985, -17.907772, 8.087897, 2.532532, -19.97389, 8.213706, 2.3712666, -19.944628, 7.988148, 2.5417166, -17.900082, 7.9881477, 2.6136985, -17.907772, 8.087897, 2.532532, -19.97389, 8.213706, 2.6136985, -17.907772, 8.087897, 2.730536, -17.92016, 8.049877, 1.9611615, -22.031036, 8.612492, 2.532532, -19.97389, 8.213706, 2.7935467, -20.021309, 8.127626, 2.532532, -19.97389, 8.213706, 2.730536, -17.92016, 8.049877, 2.7935467, -20.021309, 8.127626, 1.9611615, -22.031036, 8.612492, 2.7935467, -20.021309, 8.127626, 2.6527867, -22.031036, 8.373904, 2.7935467, -20.021309, 8.127626, 2.730536, -17.92016, 8.049877, 2.730536, -17.92016, 7.9264183, 2.6527867, -22.031036, 8.373904, 2.7935467, -20.021309, 8.127626, 2.7935467, -20.021309, 7.84867, 2.7935467, -20.021309, 8.127626, 2.730536, -17.92016, 7.9264183, 2.7935467, -20.021309, 7.84867, 2.6527867, -22.031036, 8.373904, 2.7935467, -20.021309, 7.84867, 2.6527867, -22.031038, 7.6021805, 2.7935467, -20.021309, 7.84867, 2.730536, -17.92016, 7.9264183, 2.6136985, -17.907772, 7.888398, 2.6527867, -22.031038, 7.6021805, 2.7935467, -20.021309, 7.84867, 2.532532, -19.97389, 7.762377, 2.7935467, -20.021309, 7.84867, 2.6136985, -17.907772, 7.888398, 2.532532, -19.97389, 7.762377, 2.532532, -19.97389, 7.762377, 2.6136985, -17.907772, 7.888398, 2.5417166, -17.900082, 7.9881477, 2.6527867, -22.031038, 7.6021805, 2.532532, -19.97389, 7.762377, 1.9611615, -22.031038, 7.3638067, 2.532532, -19.97389, 7.762377, 2.5417166, -17.900082, 7.9881477, 2.3712666, -19.944628, 7.988148, 1.9611615, -22.031038, 7.3638067, 2.532532, -19.97389, 7.762377, 2.3712666, -19.944628, 7.988148, 1.9611615, -22.031038, 7.3638067, 2.3712666, -19.944628, 7.988148, 1.5335414, -22.031038, 7.988149, 7.776188, 7.8527803, 15.35211, 9.199167, 8.880393, 16.222303, 9.098563, 7.4648895, 16.002726, 9.098563, 7.4648895, 16.002726, 9.199167, 8.880393, 16.222303, 10.923531, 8.916277, 15.374966, 9.098563, 7.4648895, 16.002726, 10.923531, 8.916277, 15.374966, 10.615738, 7.8335567, 15.204943, 7.776188, 7.8527803, 15.35211, 7.562378, 8.858179, 15.848722, 9.199167, 8.880393, 16.222303, 7.2445464, 7.5629296, 13.582464, 7.562378, 8.858179, 15.848722, 7.776188, 7.8527803, 15.35211, 9.199167, 8.880393, 16.222303, 10.425425, 9.95521, 14.890314, 10.923531, 8.916277, 15.374966, 7.2445464, 7.5629296, 13.582464, 7.139243, 8.958782, 13.709127, 7.562378, 8.858179, 15.848722, 8.052368, 7.955519, 11.936705, 7.139243, 8.958782, 13.709127, 7.2445464, 7.5629296, 13.582464, 7.562378, 8.858179, 15.848722, 9.38756, 10.30978, 15.220963, 9.199167, 8.880393, 16.222303, 9.199167, 8.880393, 16.222303, 9.38756, 10.30978, 15.220963, 10.425425, 9.95521, 14.890314, 7.139243, 8.958782, 13.709127, 8.290741, 9.929365, 15.10092, 7.562378, 8.858179, 15.848722, 7.562378, 8.858179, 15.848722, 8.290741, 9.929365, 15.10092, 9.38756, 10.30978, 15.220963, 8.052368, 7.955519, 11.936705, 7.8336453, 9.033968, 11.605202, 7.139243, 8.958782, 13.709127, 7.8336453, 9.033968, 11.605202, 8.052368, 7.955519, 11.936705, 9.536436, 7.5343065, 11.340557, 7.139243, 8.958782, 13.709127, 8.151477, 10.329217, 13.871674, 8.290741, 9.929365, 15.10092, 7.8336453, 9.033968, 11.605202, 8.151477, 10.329217, 13.871674, 7.139243, 8.958782, 13.709127, 8.290741, 9.929365, 15.10092, 9.4503565, 10.786099, 13.925926, 9.38756, 10.30978, 15.220963, 8.290741, 9.929365, 15.10092, 8.151477, 10.329217, 13.871674, 9.4503565, 10.786099, 13.925926, 7.8336453, 9.033968, 11.605202, 8.41121, 10.007327, 12.574076, 8.151477, 10.329217, 13.871674, 7.8336453, 9.033968, 11.605202, 9.536436, 7.5343065, 11.340557, 9.549679, 8.935926, 11.33906, 8.41121, 10.007327, 12.574076, 7.8336453, 9.033968, 11.605202, 9.549679, 8.935926, 11.33906, 9.549679, 8.935926, 11.33906, 9.536436, 7.5343065, 11.340557, 10.884657, 7.8173223, 12.306011, 8.151477, 10.329217, 13.871674, 8.41121, 10.007327, 12.574076, 9.475134, 10.323663, 12.558056, 8.41121, 10.007327, 12.574076, 9.549679, 8.935926, 11.33906, 9.475134, 10.323663, 12.558056, 8.151477, 10.329217, 13.871674, 9.475134, 10.323663, 12.558056, 9.4503565, 10.786099, 13.925926, 9.549679, 8.935926, 11.33906, 10.884657, 7.8173223, 12.306011, 11.177925, 8.824002, 12.293623, 9.475134, 10.323663, 12.558056, 9.549679, 8.935926, 11.33906, 11.177925, 8.824002, 12.293623, 11.39024, 7.4364805, 13.76103, 11.177925, 8.824002, 12.293623, 10.884657, 7.8173223, 12.306011, 9.4503565, 10.786099, 13.925926, 9.475134, 10.323663, 12.558056, 10.53863, 9.914199, 12.879946, 9.475134, 10.323663, 12.558056, 11.177925, 8.824002, 12.293623, 10.53863, 9.914199, 12.879946, 11.39024, 7.4364805, 13.76103, 11.609602, 8.857537, 13.852022, 11.177925, 8.824002, 12.293623, 11.609602, 8.857537, 13.852022, 10.53863, 9.914199, 12.879946, 11.177925, 8.824002, 12.293623, 10.615738, 7.8335567, 15.204943, 11.609602, 8.857537, 13.852022, 11.39024, 7.4364805, 13.76103, 10.615738, 7.8335567, 15.204943, 10.923531, 8.916277, 15.374966, 11.609602, 8.857537, 13.852022, 11.609602, 8.857537, 13.852022, 10.711002, 10.304012, 13.907557, 10.53863, 9.914199, 12.879946, 9.4503565, 10.786099, 13.925926, 10.53863, 9.914199, 12.879946, 10.711002, 10.304012, 13.907557, 10.923531, 8.916277, 15.374966, 10.711002, 10.304012, 13.907557, 11.609602, 8.857537, 13.852022, 9.38756, 10.30978, 15.220963, 9.4503565, 10.786099, 13.925926, 10.711002, 10.304012, 13.907557, 10.923531, 8.916277, 15.374966, 10.425425, 9.95521, 14.890314, 10.711002, 10.304012, 13.907557, 9.38756, 10.30978, 15.220963, 10.711002, 10.304012, 13.907557, 10.425425, 9.95521, 14.890314, 8.052368, 7.955519, 11.936705, 9.29849, 7.03022, 13.635437, 9.536436, 7.5343065, 11.340557, 8.052368, 7.955519, 11.936705, 7.2445464, 7.5629296, 13.582464, 9.29849, 7.03022, 13.635437, 9.536436, 7.5343065, 11.340557, 9.29849, 7.03022, 13.635437, 11.39024, 7.4364805, 13.76103, 9.536436, 7.5343065, 11.340557, 11.39024, 7.4364805, 13.76103, 10.884657, 7.8173223, 12.306011, 9.29849, 7.03022, 13.635437, 10.615738, 7.8335567, 15.204943, 11.39024, 7.4364805, 13.76103, 9.29849, 7.03022, 13.635437, 9.098563, 7.4648895, 16.002726, 10.615738, 7.8335567, 15.204943, 7.2445464, 7.5629296, 13.582464, 9.098563, 7.4648895, 16.002726, 9.29849, 7.03022, 13.635437, 7.2445464, 7.5629296, 13.582464, 7.776188, 7.8527803, 15.35211, 9.098563, 7.4648895, 16.002726, 5.392665, 7.0744348, 14.5227165, 6.3828983, 7.8070707, 15.249157, 6.23808, 6.805731, 14.920647, 6.23808, 6.805731, 14.920647, 6.3828983, 7.8070707, 15.249157, 7.655933, 7.812624, 14.855713, 6.23808, 6.805731, 14.920647, 7.655933, 7.812624, 14.855713, 7.2765856, 7.0744348, 14.5227165, 5.392665, 7.0744348, 14.5227165, 5.182486, 7.812624, 14.855713, 6.3828983, 7.8070707, 15.249157, 5.182486, 6.833284, 13.486773, 5.182486, 7.812624, 14.855713, 5.392665, 7.0744348, 14.5227165, 6.3828983, 7.8070707, 15.249157, 7.346859, 8.559357, 14.576542, 7.655933, 7.812624, 14.855713, 5.182486, 6.833284, 13.486773, 5.111145, 7.8290706, 13.674952, 5.182486, 7.812624, 14.855713, 5.772867, 7.1086097, 12.548658, 5.111145, 7.8290706, 13.674952, 5.182486, 6.833284, 13.486773, 5.182486, 7.812624, 14.855713, 6.600126, 8.819304, 14.790779, 6.3828983, 7.8070707, 15.249157, 6.3828983, 7.8070707, 15.249157, 6.600126, 8.819304, 14.790779, 7.346859, 8.559357, 14.576542, 5.111145, 7.8290706, 13.674952, 5.772867, 8.559357, 14.576542, 5.182486, 7.812624, 14.855713, 5.182486, 7.812624, 14.855713, 5.772867, 8.559357, 14.576542, 6.600126, 8.819304, 14.790779, 5.772867, 7.1086097, 12.548658, 5.7483034, 7.845517, 12.494404, 5.111145, 7.8290706, 13.674952, 5.7483034, 7.845517, 12.494404, 5.772867, 7.1086097, 12.548658, 6.600126, 6.8608375, 12.053114, 5.111145, 7.8290706, 13.674952, 5.7483034, 8.824858, 13.863131, 5.772867, 8.559357, 14.576542, 5.7483034, 7.845517, 12.494404, 5.7483034, 8.824858, 13.863131, 5.111145, 7.8290706, 13.674952, 5.772867, 8.559357, 14.576542, 6.672535, 9.156786, 13.925926, 6.600126, 8.819304, 14.790779, 5.772867, 8.559357, 14.576542, 5.7483034, 8.824858, 13.863131, 6.672535, 9.156786, 13.925926, 5.7483034, 7.845517, 12.494404, 6.02427, 8.574095, 13.052319, 5.7483034, 8.824858, 13.863131, 5.7483034, 7.845517, 12.494404, 6.600126, 6.8608375, 12.053114, 6.672535, 7.8510704, 12.10096, 6.02427, 8.574095, 13.052319, 5.7483034, 7.845517, 12.494404, 6.672535, 7.8510704, 12.10096, 6.672535, 7.8510704, 12.10096, 6.600126, 6.8608375, 12.053114, 7.346859, 7.1086097, 12.548658, 5.7483034, 8.824858, 13.863131, 6.02427, 8.574095, 13.052319, 6.672535, 8.83041, 12.935694, 6.02427, 8.574095, 13.052319, 6.672535, 7.8510704, 12.10096, 6.672535, 8.83041, 12.935694, 5.7483034, 8.824858, 13.863131, 6.672535, 8.83041, 12.935694, 6.672535, 9.156786, 13.925926, 6.672535, 7.8510704, 12.10096, 7.346859, 7.1086097, 12.548658, 7.524571, 7.845517, 12.494404, 6.672535, 8.83041, 12.935694, 6.672535, 7.8510704, 12.10096, 7.524571, 7.845517, 12.494404, 7.655933, 6.833284, 13.486773, 7.524571, 7.845517, 12.494404, 7.346859, 7.1086097, 12.548658, 6.672535, 9.156786, 13.925926, 6.672535, 8.83041, 12.935694, 7.288547, 8.574095, 13.052319, 6.672535, 8.83041, 12.935694, 7.524571, 7.845517, 12.494404, 7.288547, 8.574095, 13.052319, 7.655933, 6.833284, 13.486773, 7.9442883, 7.8290706, 13.674952, 7.524571, 7.845517, 12.494404, 7.9442883, 7.8290706, 13.674952, 7.288547, 8.574095, 13.052319, 7.524571, 7.845517, 12.494404, 7.2765856, 7.0744348, 14.5227165, 7.9442883, 7.8290706, 13.674952, 7.655933, 6.833284, 13.486773, 7.2765856, 7.0744348, 14.5227165, 7.655933, 7.812624, 14.855713, 7.9442883, 7.8290706, 13.674952, 7.9442883, 7.8290706, 13.674952, 7.524571, 8.824858, 13.863131, 7.288547, 8.574095, 13.052319, 6.672535, 9.156786, 13.925926, 7.288547, 8.574095, 13.052319, 7.524571, 8.824858, 13.863131, 7.655933, 7.812624, 14.855713, 7.524571, 8.824858, 13.863131, 7.9442883, 7.8290706, 13.674952, 6.600126, 8.819304, 14.790779, 6.672535, 9.156786, 13.925926, 7.524571, 8.824858, 13.863131, 7.655933, 7.812624, 14.855713, 7.346859, 8.559357, 14.576542, 7.524571, 8.824858, 13.863131, 6.600126, 8.819304, 14.790779, 7.524571, 8.824858, 13.863131, 7.346859, 8.559357, 14.576542, 5.772867, 7.1086097, 12.548658, 6.3828983, 6.501355, 13.4241905, 6.600126, 6.8608375, 12.053114, 5.772867, 7.1086097, 12.548658, 5.182486, 6.833284, 13.486773, 6.3828983, 6.501355, 13.4241905, 6.600126, 6.8608375, 12.053114, 6.3828983, 6.501355, 13.4241905, 7.655933, 6.833284, 13.486773, 6.600126, 6.8608375, 12.053114, 7.655933, 6.833284, 13.486773, 7.346859, 7.1086097, 12.548658, 6.3828983, 6.501355, 13.4241905, 7.2765856, 7.0744348, 14.5227165, 7.655933, 6.833284, 13.486773, 6.3828983, 6.501355, 13.4241905, 6.23808, 6.805731, 14.920647, 7.2765856, 7.0744348, 14.5227165, 5.182486, 6.833284, 13.486773, 6.23808, 6.805731, 14.920647, 6.3828983, 6.501355, 13.4241905, 5.182486, 6.833284, 13.486773, 5.392665, 7.0744348, 14.5227165, 6.23808, 6.805731, 14.920647, 9.232489, 5.4846363, 13.925928, 8.134389, 5.7744865, 13.729633, 9.182293, 5.3428082, 13.718954, 9.232489, 5.4846363, 13.925928, 8.220682, 5.8879066, 13.925928, 8.134389, 5.7744865, 13.729633, 8.220682, 5.8879066, 13.925928, 7.095883, 6.8742948, 13.868898, 8.134389, 5.7744865, 13.729633, 8.220682, 5.8879066, 13.925928, 7.137321, 6.8738675, 13.925928, 7.095883, 6.8742948, 13.868898, 8.134389, 5.7744865, 13.729633, 7.095883, 6.8742948, 13.868898, 7.028814, 6.8749356, 13.890684, 9.182293, 5.3428082, 13.718954, 8.134389, 5.7744865, 13.729633, 7.994697, 5.59058, 13.8046055, 8.134389, 5.7744865, 13.729633, 7.028814, 6.8749356, 13.890684, 7.994697, 5.59058, 13.8046055, 9.182293, 5.3428082, 13.718954, 7.994697, 5.59058, 13.8046055, 9.101339, 5.1134057, 13.797983, 7.994697, 5.59058, 13.8046055, 7.028814, 6.8749356, 13.890684, 7.028814, 6.8749356, 13.96117, 9.101339, 5.1134057, 13.797983, 7.994697, 5.59058, 13.8046055, 7.994697, 5.59058, 14.047251, 7.994697, 5.59058, 13.8046055, 7.028814, 6.8749356, 13.96117, 7.994697, 5.59058, 14.047251, 9.101339, 5.1134057, 13.797983, 7.994697, 5.59058, 14.047251, 9.101339, 5.1134057, 14.053873, 7.994697, 5.59058, 14.047251, 7.028814, 6.8749356, 13.96117, 7.095883, 6.8742948, 13.982958, 9.101339, 5.1134057, 14.053873, 7.994697, 5.59058, 14.047251, 8.134389, 5.7744865, 14.122223, 7.994697, 5.59058, 14.047251, 7.095883, 6.8742948, 13.982958, 8.134389, 5.7744865, 14.122223, 8.134389, 5.7744865, 14.122223, 7.095883, 6.8742948, 13.982958, 7.137321, 6.8738675, 13.925928, 9.101339, 5.1134057, 14.053873, 8.134389, 5.7744865, 14.122223, 9.182293, 5.3428082, 14.132904, 8.134389, 5.7744865, 14.122223, 7.137321, 6.8738675, 13.925928, 8.220682, 5.8879066, 13.925928, 9.182293, 5.3428082, 14.132904, 8.134389, 5.7744865, 14.122223, 8.220682, 5.8879066, 13.925928, 9.182293, 5.3428082, 14.132904, 8.220682, 5.8879066, 13.925928, 9.232489, 5.4846363, 13.925928, 8.1010685, 3.1833484, 13.925928, 9.100059, 5.2404957, 14.151699, 8.528688, 3.1833487, 14.550272, 8.1010685, 3.1833484, 13.925928, 8.938793, 5.269758, 13.925928, 9.100059, 5.2404957, 14.151699, 8.938793, 5.269758, 13.925928, 9.181225, 7.306614, 14.025677, 9.100059, 5.2404957, 14.151699, 8.938793, 5.269758, 13.925928, 9.109243, 7.31409, 13.925928, 9.181225, 7.306614, 14.025677, 9.100059, 5.2404957, 14.151699, 9.181225, 7.306614, 14.025677, 9.298062, 7.294225, 13.987658, 8.528688, 3.1833487, 14.550272, 9.100059, 5.2404957, 14.151699, 9.361286, 5.1928635, 14.065407, 9.100059, 5.2404957, 14.151699, 9.298062, 7.294225, 13.987658, 9.361286, 5.1928635, 14.065407, 8.528688, 3.1833487, 14.550272, 9.361286, 5.1928635, 14.065407, 9.220313, 3.1833487, 14.311897, 9.361286, 5.1928635, 14.065407, 9.298062, 7.294225, 13.987658, 9.298062, 7.294225, 13.864199, 9.220313, 3.1833487, 14.311897, 9.361286, 5.1928635, 14.065407, 9.361073, 5.1928635, 13.786449, 9.361286, 5.1928635, 14.065407, 9.298062, 7.294225, 13.864199, 9.361073, 5.1928635, 13.786449, 9.220313, 3.1833487, 14.311897, 9.361073, 5.1928635, 13.786449, 9.220313, 3.1833484, 13.539961, 9.361073, 5.1928635, 13.786449, 9.298062, 7.294225, 13.864199, 9.181225, 7.306614, 13.826178, 9.220313, 3.1833484, 13.539961, 9.361073, 5.1928635, 13.786449, 9.100059, 5.2404957, 13.700157, 9.361073, 5.1928635, 13.786449, 9.181225, 7.306614, 13.826178, 9.100059, 5.2404957, 13.700157, 9.100059, 5.2404957, 13.700157, 9.181225, 7.306614, 13.826178, 9.109243, 7.31409, 13.925928, 9.220313, 3.1833484, 13.539961, 9.100059, 5.2404957, 13.700157, 8.528688, 3.1833482, 13.301586, 9.100059, 5.2404957, 13.700157, 9.109243, 7.31409, 13.925928, 8.938793, 5.269758, 13.925928, 8.528688, 3.1833482, 13.301586, 9.100059, 5.2404957, 13.700157, 8.938793, 5.269758, 13.925928, 8.528688, 3.1833482, 13.301586, 8.938793, 5.269758, 13.925928, 8.1010685, 3.1833484, 13.925928, -21.010672, 7.810363, 2.1272945, -19.587692, 8.8379755, 2.9974866, -19.688295, 7.422472, 2.7779098, -19.688295, 7.422472, 2.7779098, -19.587692, 8.8379755, 2.9974866, -17.863329, 8.873859, 2.1501493, -19.688295, 7.422472, 2.7779098, -17.863329, 8.873859, 2.1501493, -18.17112, 7.791139, 1.9801267, -21.010672, 7.810363, 2.1272945, -21.224482, 8.815762, 2.6239061, -19.587692, 8.8379755, 2.9974866, -21.542313, 7.520512, 0.35764837, -21.224482, 8.815762, 2.6239061, -21.010672, 7.810363, 2.1272945, -19.587692, 8.8379755, 2.9974866, -18.361435, 9.912792, 1.6654985, -17.863329, 8.873859, 2.1501493, -21.542313, 7.520512, 0.35764837, -21.647615, 8.916365, 0.48431063, -21.224482, 8.815762, 2.6239061, -20.734491, 7.9131017, -1.2881122, -21.647615, 8.916365, 0.48431063, -21.542313, 7.520512, 0.35764837, -21.224482, 8.815762, 2.6239061, -19.3993, 10.267363, 1.9961456, -19.587692, 8.8379755, 2.9974866, -19.587692, 8.8379755, 2.9974866, -19.3993, 10.267363, 1.9961456, -18.361435, 9.912792, 1.6654985, -21.647615, 8.916365, 0.48431063, -20.496117, 9.886948, 1.8761046, -21.224482, 8.815762, 2.6239061, -21.224482, 8.815762, 2.6239061, -20.496117, 9.886948, 1.8761046, -19.3993, 10.267363, 1.9961456, -20.734491, 7.9131017, -1.2881122, -20.953215, 8.99155, -1.6196141, -21.647615, 8.916365, 0.48431063, -20.953215, 8.99155, -1.6196141, -20.734491, 7.9131017, -1.2881122, -19.250423, 7.491889, -1.8842597, -21.647615, 8.916365, 0.48431063, -20.635382, 10.286799, 0.646857, -20.496117, 9.886948, 1.8761046, -20.953215, 8.99155, -1.6196141, -20.635382, 10.286799, 0.646857, -21.647615, 8.916365, 0.48431063, -20.496117, 9.886948, 1.8761046, -19.336502, 10.743682, 0.70111036, -19.3993, 10.267363, 1.9961456, -20.496117, 9.886948, 1.8761046, -20.635382, 10.286799, 0.646857, -19.336502, 10.743682, 0.70111036, -20.953215, 8.99155, -1.6196141, -20.375648, 9.96491, -0.6507411, -20.635382, 10.286799, 0.646857, -20.953215, 8.99155, -1.6196141, -19.250423, 7.491889, -1.8842597, -19.23718, 8.893509, -1.8857555, -20.375648, 9.96491, -0.6507411, -20.953215, 8.99155, -1.6196141, -19.23718, 8.893509, -1.8857555, -19.23718, 8.893509, -1.8857555, -19.250423, 7.491889, -1.8842597, -17.902203, 7.7749047, -0.91880393, -20.635382, 10.286799, 0.646857, -20.375648, 9.96491, -0.6507411, -19.311726, 10.281245, -0.6667609, -20.375648, 9.96491, -0.6507411, -19.23718, 8.893509, -1.8857555, -19.311726, 10.281245, -0.6667609, -20.635382, 10.286799, 0.646857, -19.311726, 10.281245, -0.6667609, -19.336502, 10.743682, 0.70111036, -19.23718, 8.893509, -1.8857555, -17.902203, 7.7749047, -0.91880393, -17.608934, 8.781585, -0.9311929, -19.311726, 10.281245, -0.6667609, -19.23718, 8.893509, -1.8857555, -17.608934, 8.781585, -0.9311929, -17.39662, 7.394063, 0.53621507, -17.608934, 8.781585, -0.9311929, -17.902203, 7.7749047, -0.91880393, -19.336502, 10.743682, 0.70111036, -19.311726, 10.281245, -0.6667609, -18.248228, 9.871781, -0.34487104, -19.311726, 10.281245, -0.6667609, -17.608934, 8.781585, -0.9311929, -18.248228, 9.871781, -0.34487104, -17.39662, 7.394063, 0.53621507, -17.177256, 8.81512, 0.6272067, -17.608934, 8.781585, -0.9311929, -17.177256, 8.81512, 0.6272067, -18.248228, 9.871781, -0.34487104, -17.608934, 8.781585, -0.9311929, -18.17112, 7.791139, 1.9801267, -17.177256, 8.81512, 0.6272067, -17.39662, 7.394063, 0.53621507, -18.17112, 7.791139, 1.9801267, -17.863329, 8.873859, 2.1501493, -17.177256, 8.81512, 0.6272067, -17.177256, 8.81512, 0.6272067, -18.075857, 10.261595, 0.68274117, -18.248228, 9.871781, -0.34487104, -19.336502, 10.743682, 0.70111036, -18.248228, 9.871781, -0.34487104, -18.075857, 10.261595, 0.68274117, -17.863329, 8.873859, 2.1501493, -18.075857, 10.261595, 0.68274117, -17.177256, 8.81512, 0.6272067, -19.3993, 10.267363, 1.9961456, -19.336502, 10.743682, 0.70111036, -18.075857, 10.261595, 0.68274117, -17.863329, 8.873859, 2.1501493, -18.361435, 9.912792, 1.6654985, -18.075857, 10.261595, 0.68274117, -19.3993, 10.267363, 1.9961456, -18.075857, 10.261595, 0.68274117, -18.361435, 9.912792, 1.6654985, -20.734491, 7.9131017, -1.2881122, -19.488369, 6.9878025, 0.41062057, -19.250423, 7.491889, -1.8842597, -20.734491, 7.9131017, -1.2881122, -21.542313, 7.520512, 0.35764837, -19.488369, 6.9878025, 0.41062057, -19.250423, 7.491889, -1.8842597, -19.488369, 6.9878025, 0.41062057, -17.39662, 7.394063, 0.53621507, -19.250423, 7.491889, -1.8842597, -17.39662, 7.394063, 0.53621507, -17.902203, 7.7749047, -0.91880393, -19.488369, 6.9878025, 0.41062057, -18.17112, 7.791139, 1.9801267, -17.39662, 7.394063, 0.53621507, -19.488369, 6.9878025, 0.41062057, -19.688295, 7.422472, 2.7779098, -18.17112, 7.791139, 1.9801267, -21.542313, 7.520512, 0.35764837, -19.688295, 7.422472, 2.7779098, -19.488369, 6.9878025, 0.41062057, -21.542313, 7.520512, 0.35764837, -21.010672, 7.810363, 2.1272945, -19.688295, 7.422472, 2.7779098, -23.394194, 7.032017, 1.2979, -22.403961, 7.764653, 2.0243416, -22.548779, 6.7633133, 1.6958305, -22.548779, 6.7633133, 1.6958305, -22.403961, 7.764653, 2.0243416, -21.130926, 7.7702065, 1.6308968, -22.548779, 6.7633133, 1.6958305, -21.130926, 7.7702065, 1.6308968, -21.510273, 7.032017, 1.2979, -23.394194, 7.032017, 1.2979, -23.604372, 7.7702065, 1.6308968, -22.403961, 7.764653, 2.0243416, -23.604372, 6.7908664, 0.26195753, -23.604372, 7.7702065, 1.6308968, -23.394194, 7.032017, 1.2979, -22.403961, 7.764653, 2.0243416, -21.44, 8.516939, 1.3517258, -21.130926, 7.7702065, 1.6308968, -23.604372, 6.7908664, 0.26195753, -23.675714, 7.786653, 0.4501356, -23.604372, 7.7702065, 1.6308968, -23.013992, 7.066192, -0.67615795, -23.675714, 7.786653, 0.4501356, -23.604372, 6.7908664, 0.26195753, -23.604372, 7.7702065, 1.6308968, -22.186733, 8.776887, 1.5659629, -22.403961, 7.764653, 2.0243416, -22.403961, 7.764653, 2.0243416, -22.186733, 8.776887, 1.5659629, -21.44, 8.516939, 1.3517258, -23.675714, 7.786653, 0.4501356, -23.013992, 8.516939, 1.3517258, -23.604372, 7.7702065, 1.6308968, -23.604372, 7.7702065, 1.6308968, -23.013992, 8.516939, 1.3517258, -22.186733, 8.776887, 1.5659629, -23.013992, 7.066192, -0.67615795, -23.038555, 7.8030996, -0.730412, -23.675714, 7.786653, 0.4501356, -23.038555, 7.8030996, -0.730412, -23.013992, 7.066192, -0.67615795, -22.186733, 6.81842, -1.1717019, -23.675714, 7.786653, 0.4501356, -23.038555, 8.78244, 0.63831365, -23.013992, 8.516939, 1.3517258, -23.038555, 7.8030996, -0.730412, -23.038555, 8.78244, 0.63831365, -23.675714, 7.786653, 0.4501356, -23.013992, 8.516939, 1.3517258, -22.114325, 9.114368, 0.70111096, -22.186733, 8.776887, 1.5659629, -23.013992, 8.516939, 1.3517258, -23.038555, 8.78244, 0.63831365, -22.114325, 9.114368, 0.70111096, -23.038555, 7.8030996, -0.730412, -22.762589, 8.531677, -0.17249823, -23.038555, 8.78244, 0.63831365, -23.038555, 7.8030996, -0.730412, -22.186733, 6.81842, -1.1717019, -22.114325, 7.808653, -1.1238565, -22.762589, 8.531677, -0.17249823, -23.038555, 7.8030996, -0.730412, -22.114325, 7.808653, -1.1238565, -22.114325, 7.808653, -1.1238565, -22.186733, 6.81842, -1.1717019, -21.44, 7.066192, -0.67615795, -23.038555, 8.78244, 0.63831365, -22.762589, 8.531677, -0.17249823, -22.114325, 8.7879925, -0.2891221, -22.762589, 8.531677, -0.17249823, -22.114325, 7.808653, -1.1238565, -22.114325, 8.7879925, -0.2891221, -23.038555, 8.78244, 0.63831365, -22.114325, 8.7879925, -0.2891221, -22.114325, 9.114368, 0.70111096, -22.114325, 7.808653, -1.1238565, -21.44, 7.066192, -0.67615795, -21.262287, 7.8030996, -0.730412, -22.114325, 8.7879925, -0.2891221, -22.114325, 7.808653, -1.1238565, -21.262287, 7.8030996, -0.730412, -21.130926, 6.7908664, 0.26195753, -21.262287, 7.8030996, -0.730412, -21.44, 7.066192, -0.67615795, -22.114325, 9.114368, 0.70111096, -22.114325, 8.7879925, -0.2891221, -21.498312, 8.531677, -0.17249823, -22.114325, 8.7879925, -0.2891221, -21.262287, 7.8030996, -0.730412, -21.498312, 8.531677, -0.17249823, -21.130926, 6.7908664, 0.26195753, -20.842571, 7.786653, 0.4501356, -21.262287, 7.8030996, -0.730412, -20.842571, 7.786653, 0.4501356, -21.498312, 8.531677, -0.17249823, -21.262287, 7.8030996, -0.730412, -21.510273, 7.032017, 1.2979, -20.842571, 7.786653, 0.4501356, -21.130926, 6.7908664, 0.26195753, -21.510273, 7.032017, 1.2979, -21.130926, 7.7702065, 1.6308968, -20.842571, 7.786653, 0.4501356, -20.842571, 7.786653, 0.4501356, -21.262287, 8.78244, 0.63831365, -21.498312, 8.531677, -0.17249823, -22.114325, 9.114368, 0.70111096, -21.498312, 8.531677, -0.17249823, -21.262287, 8.78244, 0.63831365, -21.130926, 7.7702065, 1.6308968, -21.262287, 8.78244, 0.63831365, -20.842571, 7.786653, 0.4501356, -22.186733, 8.776887, 1.5659629, -22.114325, 9.114368, 0.70111096, -21.262287, 8.78244, 0.63831365, -21.130926, 7.7702065, 1.6308968, -21.44, 8.516939, 1.3517258, -21.262287, 8.78244, 0.63831365, -22.186733, 8.776887, 1.5659629, -21.262287, 8.78244, 0.63831365, -21.44, 8.516939, 1.3517258, -23.013992, 7.066192, -0.67615795, -22.403961, 6.4589376, 0.19937384, -22.186733, 6.81842, -1.1717019, -23.013992, 7.066192, -0.67615795, -23.604372, 6.7908664, 0.26195753, -22.403961, 6.4589376, 0.19937384, -22.186733, 6.81842, -1.1717019, -22.403961, 6.4589376, 0.19937384, -21.130926, 6.7908664, 0.26195753, -22.186733, 6.81842, -1.1717019, -21.130926, 6.7908664, 0.26195753, -21.44, 7.066192, -0.67615795, -22.403961, 6.4589376, 0.19937384, -21.510273, 7.032017, 1.2979, -21.130926, 6.7908664, 0.26195753, -22.403961, 6.4589376, 0.19937384, -22.548779, 6.7633133, 1.6958305, -21.510273, 7.032017, 1.2979, -23.604372, 6.7908664, 0.26195753, -22.548779, 6.7633133, 1.6958305, -22.403961, 6.4589376, 0.19937384, -23.604372, 6.7908664, 0.26195753, -23.394194, 7.032017, 1.2979, -22.548779, 6.7633133, 1.6958305, -19.55437, 5.442219, 0.7011123, -20.65247, 5.732069, 0.504817, -19.604567, 5.3003907, 0.4941373, -19.55437, 5.442219, 0.7011123, -20.566177, 5.845489, 0.70111203, -20.65247, 5.732069, 0.504817, -20.566177, 5.845489, 0.70111203, -21.690975, 6.831877, 0.6440815, -20.65247, 5.732069, 0.504817, -20.566177, 5.845489, 0.70111203, -21.649538, 6.83145, 0.7011118, -21.690975, 6.831877, 0.6440815, -20.65247, 5.732069, 0.504817, -21.690975, 6.831877, 0.6440815, -21.758045, 6.832518, 0.6658683, -19.604567, 5.3003907, 0.4941373, -20.65247, 5.732069, 0.504817, -20.792162, 5.5481625, 0.5797894, -20.65247, 5.732069, 0.504817, -21.758045, 6.832518, 0.6658683, -20.792162, 5.5481625, 0.5797894, -19.604567, 5.3003907, 0.4941373, -20.792162, 5.5481625, 0.5797894, -19.685518, 5.070988, 0.57316816, -20.792162, 5.5481625, 0.5797894, -21.758045, 6.832518, 0.6658683, -21.758045, 6.832518, 0.7363552, -19.685518, 5.070988, 0.57316816, -20.792162, 5.5481625, 0.5797894, -20.792162, 5.5481625, 0.822435, -20.792162, 5.5481625, 0.5797894, -21.758045, 6.832518, 0.7363552, -20.792162, 5.5481625, 0.822435, -19.685518, 5.070988, 0.57316816, -20.792162, 5.5481625, 0.822435, -19.685518, 5.070988, 0.8290566, -20.792162, 5.5481625, 0.822435, -21.758045, 6.832518, 0.7363552, -21.690975, 6.831877, 0.758142, -19.685518, 5.070988, 0.8290566, -20.792162, 5.5481625, 0.822435, -20.65247, 5.732069, 0.8974073, -20.792162, 5.5481625, 0.822435, -21.690975, 6.831877, 0.758142, -20.65247, 5.732069, 0.8974073, -20.65247, 5.732069, 0.8974073, -21.690975, 6.831877, 0.758142, -21.649538, 6.83145, 0.7011118, -19.685518, 5.070988, 0.8290566, -20.65247, 5.732069, 0.8974073, -19.604567, 5.3003907, 0.90808725, -20.65247, 5.732069, 0.8974073, -21.649538, 6.83145, 0.7011118, -20.566177, 5.845489, 0.70111203, -19.604567, 5.3003907, 0.90808725, -20.65247, 5.732069, 0.8974073, -20.566177, 5.845489, 0.70111203, -19.604567, 5.3003907, 0.90808725, -20.566177, 5.845489, 0.70111203, -19.55437, 5.442219, 0.7011123, -20.685791, 3.140931, 0.701113, -19.6868, 5.198078, 0.9268837, -20.258171, 3.1409311, 1.3254554, -20.685791, 3.140931, 0.701113, -19.848066, 5.2273407, 0.7011123, -19.6868, 5.198078, 0.9268837, -19.848066, 5.2273407, 0.7011123, -19.605635, 7.2641964, 0.8008611, -19.6868, 5.198078, 0.9268837, -19.848066, 5.2273407, 0.7011123, -19.677616, 7.2716722, 0.70111156, -19.605635, 7.2641964, 0.8008611, -19.6868, 5.198078, 0.9268837, -19.605635, 7.2641964, 0.8008611, -19.488796, 7.2518077, 0.762841, -20.258171, 3.1409311, 1.3254554, -19.6868, 5.198078, 0.9268837, -19.425571, 5.150446, 0.84059083, -19.6868, 5.198078, 0.9268837, -19.488796, 7.2518077, 0.762841, -19.425571, 5.150446, 0.84059083, -20.258171, 3.1409311, 1.3254554, -19.425571, 5.150446, 0.84059083, -19.566545, 3.1409311, 1.0870819, -19.425571, 5.150446, 0.84059083, -19.488796, 7.2518077, 0.762841, -19.488796, 7.2518077, 0.63938224, -19.566545, 3.1409311, 1.0870819, -19.425571, 5.150446, 0.84059083, -19.425785, 5.150446, 0.5616338, -19.425571, 5.150446, 0.84059083, -19.488796, 7.2518077, 0.63938224, -19.425785, 5.150446, 0.5616338, -19.566545, 3.1409311, 1.0870819, -19.425785, 5.150446, 0.5616338, -19.566545, 3.140931, 0.3151443, -19.425785, 5.150446, 0.5616338, -19.488796, 7.2518077, 0.63938224, -19.605635, 7.2641964, 0.601362, -19.566545, 3.140931, 0.3151443, -19.425785, 5.150446, 0.5616338, -19.6868, 5.198078, 0.47534084, -19.425785, 5.150446, 0.5616338, -19.605635, 7.2641964, 0.601362, -19.6868, 5.198078, 0.47534084, -19.6868, 5.198078, 0.47534084, -19.605635, 7.2641964, 0.601362, -19.677616, 7.2716722, 0.70111156, -19.566545, 3.140931, 0.3151443, -19.6868, 5.198078, 0.47534084, -20.258171, 3.1409307, 0.07677066, -19.6868, 5.198078, 0.47534084, -19.677616, 7.2716722, 0.70111156, -19.848066, 5.2273407, 0.7011123, -20.258171, 3.1409307, 0.07677066, -19.6868, 5.198078, 0.47534084, -19.848066, 5.2273407, 0.7011123, -20.258171, 3.1409307, 0.07677066, -19.848066, 5.2273407, 0.7011123, -20.685791, 3.140931, 0.701113, -9.1784725, 7.896262, -9.667166, -7.7554927, 8.923875, -8.796972, -7.856096, 7.5083714, -9.01655, -7.856096, 7.5083714, -9.01655, -7.7554927, 8.923875, -8.796972, -6.0311284, 8.959759, -9.64431, -7.856096, 7.5083714, -9.01655, -6.0311284, 8.959759, -9.64431, -6.338921, 7.8770385, -9.814333, -9.1784725, 7.896262, -9.667166, -9.3922825, 8.901661, -9.170553, -7.7554927, 8.923875, -8.796972, -9.710114, 7.6064115, -11.436811, -9.3922825, 8.901661, -9.170553, -9.1784725, 7.896262, -9.667166, -7.7554927, 8.923875, -8.796972, -6.5292354, 9.998692, -10.128962, -6.0311284, 8.959759, -9.64431, -9.710114, 7.6064115, -11.436811, -9.815416, 9.002264, -11.310148, -9.3922825, 8.901661, -9.170553, -8.902292, 7.999001, -13.082571, -9.815416, 9.002264, -11.310148, -9.710114, 7.6064115, -11.436811, -9.3922825, 8.901661, -9.170553, -7.5671, 10.353262, -9.798313, -7.7554927, 8.923875, -8.796972, -7.7554927, 8.923875, -8.796972, -7.5671, 10.353262, -9.798313, -6.5292354, 9.998692, -10.128962, -9.815416, 9.002264, -11.310148, -8.663918, 9.972847, -9.918356, -9.3922825, 8.901661, -9.170553, -9.3922825, 8.901661, -9.170553, -8.663918, 9.972847, -9.918356, -7.5671, 10.353262, -9.798313, -8.902292, 7.999001, -13.082571, -9.121014, 9.07745, -13.414074, -9.815416, 9.002264, -11.310148, -9.121014, 9.07745, -13.414074, -8.902292, 7.999001, -13.082571, -7.418224, 7.5777884, -13.678719, -9.815416, 9.002264, -11.310148, -8.803183, 10.372699, -11.147602, -8.663918, 9.972847, -9.918356, -9.121014, 9.07745, -13.414074, -8.803183, 10.372699, -11.147602, -9.815416, 9.002264, -11.310148, -8.663918, 9.972847, -9.918356, -7.5043025, 10.829581, -11.093349, -7.5671, 10.353262, -9.798313, -8.663918, 9.972847, -9.918356, -8.803183, 10.372699, -11.147602, -7.5043025, 10.829581, -11.093349, -9.121014, 9.07745, -13.414074, -8.543449, 10.050809, -12.4452, -8.803183, 10.372699, -11.147602, -9.121014, 9.07745, -13.414074, -7.418224, 7.5777884, -13.678719, -7.40498, 8.979408, -13.680216, -8.543449, 10.050809, -12.4452, -9.121014, 9.07745, -13.414074, -7.40498, 8.979408, -13.680216, -7.40498, 8.979408, -13.680216, -7.418224, 7.5777884, -13.678719, -6.070003, 7.860804, -12.713264, -8.803183, 10.372699, -11.147602, -8.543449, 10.050809, -12.4452, -7.479526, 10.367145, -12.46122, -8.543449, 10.050809, -12.4452, -7.40498, 8.979408, -13.680216, -7.479526, 10.367145, -12.46122, -8.803183, 10.372699, -11.147602, -7.479526, 10.367145, -12.46122, -7.5043025, 10.829581, -11.093349, -7.40498, 8.979408, -13.680216, -6.070003, 7.860804, -12.713264, -5.776735, 8.867484, -12.725653, -7.479526, 10.367145, -12.46122, -7.40498, 8.979408, -13.680216, -5.776735, 8.867484, -12.725653, -5.56442, 7.4799623, -11.258245, -5.776735, 8.867484, -12.725653, -6.070003, 7.860804, -12.713264, -7.5043025, 10.829581, -11.093349, -7.479526, 10.367145, -12.46122, -6.4160295, 9.957681, -12.13933, -7.479526, 10.367145, -12.46122, -5.776735, 8.867484, -12.725653, -6.4160295, 9.957681, -12.13933, -5.56442, 7.4799623, -11.258245, -5.3450565, 8.901019, -11.1672535, -5.776735, 8.867484, -12.725653, -5.3450565, 8.901019, -11.1672535, -6.4160295, 9.957681, -12.13933, -5.776735, 8.867484, -12.725653, -6.338921, 7.8770385, -9.814333, -5.3450565, 8.901019, -11.1672535, -5.56442, 7.4799623, -11.258245, -6.338921, 7.8770385, -9.814333, -6.0311284, 8.959759, -9.64431, -5.3450565, 8.901019, -11.1672535, -5.3450565, 8.901019, -11.1672535, -6.2436566, 10.347494, -11.111719, -6.4160295, 9.957681, -12.13933, -7.5043025, 10.829581, -11.093349, -6.4160295, 9.957681, -12.13933, -6.2436566, 10.347494, -11.111719, -6.0311284, 8.959759, -9.64431, -6.2436566, 10.347494, -11.111719, -5.3450565, 8.901019, -11.1672535, -7.5671, 10.353262, -9.798313, -7.5043025, 10.829581, -11.093349, -6.2436566, 10.347494, -11.111719, -6.0311284, 8.959759, -9.64431, -6.5292354, 9.998692, -10.128962, -6.2436566, 10.347494, -11.111719, -7.5671, 10.353262, -9.798313, -6.2436566, 10.347494, -11.111719, -6.5292354, 9.998692, -10.128962, -8.902292, 7.999001, -13.082571, -7.6561704, 7.073702, -11.383839, -7.418224, 7.5777884, -13.678719, -8.902292, 7.999001, -13.082571, -9.710114, 7.6064115, -11.436811, -7.6561704, 7.073702, -11.383839, -7.418224, 7.5777884, -13.678719, -7.6561704, 7.073702, -11.383839, -5.56442, 7.4799623, -11.258245, -7.418224, 7.5777884, -13.678719, -5.56442, 7.4799623, -11.258245, -6.070003, 7.860804, -12.713264, -7.6561704, 7.073702, -11.383839, -6.338921, 7.8770385, -9.814333, -5.56442, 7.4799623, -11.258245, -7.6561704, 7.073702, -11.383839, -7.856096, 7.5083714, -9.01655, -6.338921, 7.8770385, -9.814333, -9.710114, 7.6064115, -11.436811, -7.856096, 7.5083714, -9.01655, -7.6561704, 7.073702, -11.383839, -9.710114, 7.6064115, -11.436811, -9.1784725, 7.896262, -9.667166, -7.856096, 7.5083714, -9.01655, -11.561995, 7.1179166, -10.496559, -10.571762, 7.8505526, -9.770119, -10.716579, 6.8492126, -10.098629, -10.716579, 6.8492126, -10.098629, -10.571762, 7.8505526, -9.770119, -9.298727, 7.856106, -10.163563, -10.716579, 6.8492126, -10.098629, -9.298727, 7.856106, -10.163563, -9.678074, 7.1179166, -10.496559, -11.561995, 7.1179166, -10.496559, -11.772173, 7.856106, -10.163563, -10.571762, 7.8505526, -9.770119, -11.772173, 6.8767657, -11.532503, -11.772173, 7.856106, -10.163563, -11.561995, 7.1179166, -10.496559, -10.571762, 7.8505526, -9.770119, -9.607801, 8.6028385, -10.442734, -9.298727, 7.856106, -10.163563, -11.772173, 6.8767657, -11.532503, -11.843515, 7.8725524, -11.344324, -11.772173, 7.856106, -10.163563, -11.181793, 7.1520915, -12.470617, -11.843515, 7.8725524, -11.344324, -11.772173, 6.8767657, -11.532503, -11.772173, 7.856106, -10.163563, -10.354534, 8.862786, -10.228497, -10.571762, 7.8505526, -9.770119, -10.571762, 7.8505526, -9.770119, -10.354534, 8.862786, -10.228497, -9.607801, 8.6028385, -10.442734, -11.843515, 7.8725524, -11.344324, -11.181793, 8.6028385, -10.442734, -11.772173, 7.856106, -10.163563, -11.772173, 7.856106, -10.163563, -11.181793, 8.6028385, -10.442734, -10.354534, 8.862786, -10.228497, -11.181793, 7.1520915, -12.470617, -11.206356, 7.888999, -12.524872, -11.843515, 7.8725524, -11.344324, -11.206356, 7.888999, -12.524872, -11.181793, 7.1520915, -12.470617, -10.354534, 6.9043193, -12.966162, -11.843515, 7.8725524, -11.344324, -11.206356, 8.86834, -11.156145, -11.181793, 8.6028385, -10.442734, -11.206356, 7.888999, -12.524872, -11.206356, 8.86834, -11.156145, -11.843515, 7.8725524, -11.344324, -11.181793, 8.6028385, -10.442734, -10.282125, 9.200268, -11.093349, -10.354534, 8.862786, -10.228497, -11.181793, 8.6028385, -10.442734, -11.206356, 8.86834, -11.156145, -10.282125, 9.200268, -11.093349, -11.206356, 7.888999, -12.524872, -10.930389, 8.617577, -11.966957, -11.206356, 8.86834, -11.156145, -11.206356, 7.888999, -12.524872, -10.354534, 6.9043193, -12.966162, -10.282125, 7.894552, -12.918316, -10.930389, 8.617577, -11.966957, -11.206356, 7.888999, -12.524872, -10.282125, 7.894552, -12.918316, -10.282125, 7.894552, -12.918316, -10.354534, 6.9043193, -12.966162, -9.607801, 7.1520915, -12.470617, -11.206356, 8.86834, -11.156145, -10.930389, 8.617577, -11.966957, -10.282125, 8.873892, -12.083582, -10.930389, 8.617577, -11.966957, -10.282125, 7.894552, -12.918316, -10.282125, 8.873892, -12.083582, -11.206356, 8.86834, -11.156145, -10.282125, 8.873892, -12.083582, -10.282125, 9.200268, -11.093349, -10.282125, 7.894552, -12.918316, -9.607801, 7.1520915, -12.470617, -9.430088, 7.888999, -12.524872, -10.282125, 8.873892, -12.083582, -10.282125, 7.894552, -12.918316, -9.430088, 7.888999, -12.524872, -9.298727, 6.8767657, -11.532503, -9.430088, 7.888999, -12.524872, -9.607801, 7.1520915, -12.470617, -10.282125, 9.200268, -11.093349, -10.282125, 8.873892, -12.083582, -9.666113, 8.617577, -11.966957, -10.282125, 8.873892, -12.083582, -9.430088, 7.888999, -12.524872, -9.666113, 8.617577, -11.966957, -9.298727, 6.8767657, -11.532503, -9.010372, 7.8725524, -11.344324, -9.430088, 7.888999, -12.524872, -9.010372, 7.8725524, -11.344324, -9.666113, 8.617577, -11.966957, -9.430088, 7.888999, -12.524872, -9.678074, 7.1179166, -10.496559, -9.010372, 7.8725524, -11.344324, -9.298727, 6.8767657, -11.532503, -9.678074, 7.1179166, -10.496559, -9.298727, 7.856106, -10.163563, -9.010372, 7.8725524, -11.344324, -9.010372, 7.8725524, -11.344324, -9.430088, 8.86834, -11.156145, -9.666113, 8.617577, -11.966957, -10.282125, 9.200268, -11.093349, -9.666113, 8.617577, -11.966957, -9.430088, 8.86834, -11.156145, -9.298727, 7.856106, -10.163563, -9.430088, 8.86834, -11.156145, -9.010372, 7.8725524, -11.344324, -10.354534, 8.862786, -10.228497, -10.282125, 9.200268, -11.093349, -9.430088, 8.86834, -11.156145, -9.298727, 7.856106, -10.163563, -9.607801, 8.6028385, -10.442734, -9.430088, 8.86834, -11.156145, -10.354534, 8.862786, -10.228497, -9.430088, 8.86834, -11.156145, -9.607801, 8.6028385, -10.442734, -11.181793, 7.1520915, -12.470617, -10.571762, 6.544837, -11.595085, -10.354534, 6.9043193, -12.966162, -11.181793, 7.1520915, -12.470617, -11.772173, 6.8767657, -11.532503, -10.571762, 6.544837, -11.595085, -10.354534, 6.9043193, -12.966162, -10.571762, 6.544837, -11.595085, -9.298727, 6.8767657, -11.532503, -10.354534, 6.9043193, -12.966162, -9.298727, 6.8767657, -11.532503, -9.607801, 7.1520915, -12.470617, -10.571762, 6.544837, -11.595085, -9.678074, 7.1179166, -10.496559, -9.298727, 6.8767657, -11.532503, -10.571762, 6.544837, -11.595085, -10.716579, 6.8492126, -10.098629, -9.678074, 7.1179166, -10.496559, -11.772173, 6.8767657, -11.532503, -10.716579, 6.8492126, -10.098629, -10.571762, 6.544837, -11.595085, -11.772173, 6.8767657, -11.532503, -11.561995, 7.1179166, -10.496559, -10.716579, 6.8492126, -10.098629, -7.7221713, 5.528118, -11.093348, -8.820271, 5.8179684, -11.289642, -7.772366, 5.38629, -11.300322, -7.7221713, 5.528118, -11.093348, -8.733978, 5.9313884, -11.093348, -8.820271, 5.8179684, -11.289642, -8.733978, 5.9313884, -11.093348, -9.858776, 6.9177766, -11.150377, -8.820271, 5.8179684, -11.289642, -8.733978, 5.9313884, -11.093348, -9.817339, 6.9173493, -11.093348, -9.858776, 6.9177766, -11.150377, -8.820271, 5.8179684, -11.289642, -9.858776, 6.9177766, -11.150377, -9.925846, 6.9184175, -11.128592, -7.772366, 5.38629, -11.300322, -8.820271, 5.8179684, -11.289642, -8.959963, 5.634062, -11.21467, -8.820271, 5.8179684, -11.289642, -9.925846, 6.9184175, -11.128592, -8.959963, 5.634062, -11.21467, -7.772366, 5.38629, -11.300322, -8.959963, 5.634062, -11.21467, -7.8533196, 5.1568875, -11.2212925, -8.959963, 5.634062, -11.21467, -9.925846, 6.9184175, -11.128592, -9.925846, 6.9184175, -11.058105, -7.8533196, 5.1568875, -11.2212925, -8.959963, 5.634062, -11.21467, -8.959963, 5.634062, -10.972025, -8.959963, 5.634062, -11.21467, -9.925846, 6.9184175, -11.058105, -8.959963, 5.634062, -10.972025, -7.8533196, 5.1568875, -11.2212925, -8.959963, 5.634062, -10.972025, -7.8533196, 5.1568875, -10.965403, -8.959963, 5.634062, -10.972025, -9.925846, 6.9184175, -11.058105, -9.858776, 6.9177766, -11.036318, -7.8533196, 5.1568875, -10.965403, -8.959963, 5.634062, -10.972025, -8.820271, 5.8179684, -10.897053, -8.959963, 5.634062, -10.972025, -9.858776, 6.9177766, -11.036318, -8.820271, 5.8179684, -10.897053, -8.820271, 5.8179684, -10.897053, -9.858776, 6.9177766, -11.036318, -9.817339, 6.9173493, -11.093348, -7.8533196, 5.1568875, -10.965403, -8.820271, 5.8179684, -10.897053, -7.772366, 5.38629, -10.886372, -8.820271, 5.8179684, -10.897053, -9.817339, 6.9173493, -11.093348, -8.733978, 5.9313884, -11.093348, -7.772366, 5.38629, -10.886372, -8.820271, 5.8179684, -10.897053, -8.733978, 5.9313884, -11.093348, -7.772366, 5.38629, -10.886372, -8.733978, 5.9313884, -11.093348, -7.7221713, 5.528118, -11.093348, -8.853592, 3.2268302, -11.093348, -7.8546014, 5.2839775, -10.867577, -8.425972, 3.2268305, -10.469004, -8.853592, 3.2268302, -11.093348, -8.015867, 5.31324, -11.093348, -7.8546014, 5.2839775, -10.867577, -8.015867, 5.31324, -11.093348, -7.773434, 7.3500957, -10.993599, -7.8546014, 5.2839775, -10.867577, -8.015867, 5.31324, -11.093348, -7.8454165, 7.3575716, -11.093348, -7.773434, 7.3500957, -10.993599, -7.8546014, 5.2839775, -10.867577, -7.773434, 7.3500957, -10.993599, -7.6565967, 7.337707, -11.031618, -8.425972, 3.2268305, -10.469004, -7.8546014, 5.2839775, -10.867577, -7.593373, 5.2363453, -10.953869, -7.8546014, 5.2839775, -10.867577, -7.6565967, 7.337707, -11.031618, -7.593373, 5.2363453, -10.953869, -8.425972, 3.2268305, -10.469004, -7.593373, 5.2363453, -10.953869, -7.734346, 3.2268305, -10.707378, -7.593373, 5.2363453, -10.953869, -7.6565967, 7.337707, -11.031618, -7.6565967, 7.337707, -11.155077, -7.734346, 3.2268305, -10.707378, -7.593373, 5.2363453, -10.953869, -7.5935864, 5.2363453, -11.232826, -7.593373, 5.2363453, -10.953869, -7.6565967, 7.337707, -11.155077, -7.5935864, 5.2363453, -11.232826, -7.734346, 3.2268305, -10.707378, -7.5935864, 5.2363453, -11.232826, -7.734346, 3.2268302, -11.479315, -7.5935864, 5.2363453, -11.232826, -7.6565967, 7.337707, -11.155077, -7.773434, 7.3500957, -11.193098, -7.734346, 3.2268302, -11.479315, -7.5935864, 5.2363453, -11.232826, -7.8546014, 5.2839775, -11.3191185, -7.5935864, 5.2363453, -11.232826, -7.773434, 7.3500957, -11.193098, -7.8546014, 5.2839775, -11.3191185, -7.8546014, 5.2839775, -11.3191185, -7.773434, 7.3500957, -11.193098, -7.8454165, 7.3575716, -11.093348, -7.734346, 3.2268302, -11.479315, -7.8546014, 5.2839775, -11.3191185, -8.425972, 3.22683, -11.7176895, -7.8546014, 5.2839775, -11.3191185, -7.8454165, 7.3575716, -11.093348, -8.015867, 5.31324, -11.093348, -8.425972, 3.22683, -11.7176895, -7.8546014, 5.2839775, -11.3191185, -8.015867, 5.31324, -11.093348, -8.425972, 3.22683, -11.7176895, -8.015867, 5.31324, -11.093348, -8.853592, 3.2268302, -11.093348, -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, -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, -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.754601, 0.6137, 3.5372, -16.7232, 0.6137, 2.9166, -15.754601, 0.6137, 3.5372, -16.7232, 0.6137, 0, -13.378601, 0.6137, 0, -16.7232, 0.6137, 0, -15.8036995, 0.6137, -2.7091002, -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.754601, 0.6137, 3.5372, -13.378601, 0.6137, 0, -14.489201, 0.6137, 4.2778, -13.378601, 0.6137, 0, -10.033899, 0.6137, -3.3446002, -10.033899, 0.6137, 0, -14.489201, 0.6137, 4.2778, -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.0257998, 0.6137, -1.6911998, -6.6893, 0.6137, 0, -4.0257998, 0.6137, -1.6911998, -3.3446002, 0.6137, 0, -10.033899, 0.6137, 3.3446, -10.033899, 0.6137, 0, -6.6893, 0.6137, 0, -14.489201, 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.3446002, 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.3446002, 0.6137, 0, -3.3446002, 0.6137, 3.3446, -10.033899, 0.6137, 6.6893, -14.489201, 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.489201, 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.5674996, 0.0266, 7.6215, -6.6893, 0.6137, 6.6893, -2.5674996, 0.0266, 7.6215, -6.6893, 0.6137, 3.3446, -3.3446002, 0.6137, 3.3446, -2.5674996, 0.0266, 7.6215, -3.3446002, 0.6137, 3.3446, -2.1193, 0.6137, 5.2736998, -19.841799, 3.2077, -3.7668, -19.7545, 3.3913999, -2.7851996, -19.9489, 3.2063, -2.8082, -17.1359, 3.192, 9.715799, -16.285099, 3.3913999, 9.5865, -16.2818, 3.1913998, 9.8056, -16.111599, 3.066, 12.2328, -15.739, 3.2087, 12.3416, -15.5713005, 3.0688999, 12.748899, 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, -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.0746, 16.7232, 20.0679, -1.0746, 13.378601, 16.7232, -1.0746, 16.7232, 16.7232, -1.0746, 13.378601, 20.0679, -1.0746, 16.7232, 20.0679, -1.0746, 13.378601, 23.412498, -1.0746, 13.378601, 16.7232, -1.0746, 20.0679, 16.7232, -1.0746, 16.7232, 20.0679, -1.0746, 16.7232, 16.7232, -1.0746, 16.7232, 16.7232, -1.0746, 20.0679, 15.0508995, -1.0746, 20.0679, 16.7232, -1.0746, 16.7232, 15.0508995, -1.0746, 20.0679, 13.378599, -1.0746, 20.0679, 13.378599, -1.0746, 20.0679, 15.0508995, -1.0746, 20.0679, 16.7232, -1.0746, 20.0679, 13.378599, -1.0746, 20.0679, 16.7232, -1.0746, 20.0679, 13.378599, -1.0746, 23.412498, 23.412498, -1.0746, 13.378601, 26.7582, -1.0746, 13.378601, 26.7582, -1.0746, 16.7232, 23.412498, -1.0746, 13.378601, 26.7582, -1.0746, 16.7232, 23.412498, -1.0746, 16.7232, 23.412498, -1.0746, 13.378601, 23.412498, -1.0746, 16.7232, 20.0679, -1.0746, 16.7232, 23.412498, -1.0746, 16.7232, 26.7582, -1.0746, 16.7232, 26.7582, -1.0746, 20.0679, 20.0679, -1.0746, 16.7232, 20.0679, -1.0746, 20.0679, 16.7232, -1.0746, 20.0679, 20.0679, -1.0746, 20.0679, 20.0679, -1.0746, 16.7232, 23.412498, -1.0746, 16.7232, 16.7232, -1.0746, 20.0679, 16.7232, -1.0746, 23.412498, 13.378599, -1.0746, 23.412498, 16.7232, -1.0746, 23.412498, 16.7232, -1.0746, 20.0679, 20.0679, -1.0746, 20.0679, 13.378599, -1.0746, 23.412498, 16.7232, -1.0746, 23.412498, 16.7232, -1.0746, 26.7572, 13.378599, -1.0746, 23.412498, 16.7232, -1.0746, 26.7572, 13.378599, -1.0746, 26.7572, 16.7232, -1.0746, 26.7572, 16.7232, -1.0746, 23.412498, 20.0679, -1.0746, 23.412498, 16.7232, -1.0746, 23.412498, 20.0679, -1.0746, 20.0679, 20.0679, -1.0746, 23.412498, 16.7232, -1.0746, 26.7572, 20.0679, -1.0746, 23.412498, 20.0679, -1.0746, 26.7572, 20.0679, -1.0746, 26.7572, 20.0679, -1.0746, 23.412498, 23.412498, -1.0746, 23.412498, 20.0679, -1.0746, 26.7572, 23.412498, -1.0746, 23.412498, 23.412498, -1.0746, 26.7572, 20.0679, -1.0746, 23.412498, 20.0679, -1.0746, 20.0679, 23.412498, -1.0746, 20.0679, 20.0679, -1.0746, 23.412498, 23.412498, -1.0746, 20.0679, 23.412498, -1.0746, 23.412498, 20.0679, -1.0746, 20.0679, 23.412498, -1.0746, 16.7232, 23.412498, -1.0746, 20.0679, 23.412498, -1.0746, 16.7232, 26.7582, -1.0746, 20.0679, 23.412498, -1.0746, 20.0679, 23.412498, -1.0746, 20.0679, 26.7582, -1.0746, 23.412498, 23.412498, -1.0746, 23.412498, 23.412498, -1.0746, 20.0679, 26.7582, -1.0746, 20.0679, 26.7582, -1.0746, 23.412498, -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, -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, -0.9264, 26.7572, 10.033899, -0.501, 23.412498, 10.033899, -0.9264, 26.7572, 6.6893, -0.9264, 26.7572, 6.6893, -0.2814, 23.412498, 10.033899, -0.501, 23.412498, 10.033899, -0.9264, 26.7572, 13.378599, -1.0746, 23.412498, 13.378599, -1.0746, 26.7572, 10.033899, -0.9264, 26.7572, 10.033899, -0.501, 23.412498, 13.378599, -1.0746, 23.412498, 0, -0.9264, 26.7572, 3.3446, -0.2814, 23.412498, 3.3446, -0.9264, 26.7572, 3.3446, -0.9264, 26.7572, 3.3446, -0.2814, 23.412498, 6.6893, -0.2814, 23.412498, 3.3446, -0.9264, 26.7572, 6.6893, -0.2814, 23.412498, 6.6893, -0.9264, 26.7572, 0, -0.9264, 26.7572, 0, -0.2814, 23.412498, 3.3446, -0.2814, 23.412498, -3.3446002, -0.9264, 26.7572, 0, -0.2814, 23.412498, 0, -0.9264, 26.7572, -3.3446002, -0.9264, 26.7572, -3.3446002, -0.2814, 23.412498, 0, -0.2814, 23.412498, -6.6893, -0.9264, 26.7572, -3.3446002, -0.2814, 23.412498, -3.3446002, -0.9264, 26.7572, -6.6893, -0.9264, 26.7572, -6.6893, -0.2814, 23.412498, -3.3446002, -0.2814, 23.412498, -10.033899, -0.9264, 26.7572, -6.6893, -0.2814, 23.412498, -6.6893, -0.9264, 26.7572, -10.033899, -0.9264, 26.7572, -10.033899, -0.2814, 23.412498, -6.6893, -0.2814, 23.412498, -10.033899, -0.9264, 26.7572, -12.357901, -0.2814, 23.412498, -10.033899, -0.2814, 23.412498, -10.033899, -0.9264, 26.7572, -13.378601, -0.2814, 23.412498, -12.357901, -0.2814, 23.412498, -13.378601, -0.2814, 23.412498, -10.033899, -0.9264, 26.7572, -13.378601, -0.9264, 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, -0.94299996, 26.7572, -20.0679, -0.5589, 23.412498, -16.7232, -0.94299996, 26.7572, -16.7232, -0.94299996, 26.7572, -16.7232, -0.36479998, 23.412498, -13.378601, -0.9264, 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.0756, 26.7572, -23.412498, -0.7373, 23.412498, -23.412498, -0.94299996, 26.7572, -26.7572, -1.0756, 26.7572, -26.7572, -1.0756, 23.412498, -23.412498, -0.7373, 23.412498, -23.412498, -0.94299996, 26.7572, -23.412498, -0.7373, 23.412498, -20.0679, -0.94299996, 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, -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, 39.877598, -3.6808999, -26.7572, 26.7582, -1.0924, -23.412498, 26.7573, -1.3222, -26.7572, 39.877598, -3.6808999, -26.7572, 26.7582, -1.0924, -20.0679, 26.7582, -1.0924, -23.412498, 39.877598, -3.6573, -23.412498, 26.7582, -1.0924, -20.0679, 39.877598, -3.6808999, -26.7572, 39.877598, -3.6573, -23.412498, 26.7577, -0.8804, -16.7232, 26.7582, -1.0924, -20.0679, 39.877598, -3.6573, -20.0679, 26.7577, -0.8804, -16.7232, 39.877598, -3.6573, -23.412498, 39.877598, -3.6573, -16.7232, 26.7577, -0.8804, -16.7232, 39.877598, -3.6573, -20.0679, 39.877598, -3.6573, -13.378599, 26.7577, -0.8804, -16.7232, 39.877598, -3.6573, -16.7232, 39.877598, -3.6573, -10.033899, 26.7577, -0.8804, -16.7232, 39.877598, -3.6573, -13.378599, 26.7577, -0.8804, -16.7232, 26.572199, -0.4003, -10.314899, 26.572199, -0.95549995, -13.659599, 26.7577, -0.8804, -16.7232, 26.7577, -0.3464, -6.6893, 26.572199, -0.4003, -10.314899, 26.7577, -0.8804, -16.7232, 26.7577, -0.3464, -3.3446002, 26.7577, -0.3464, -6.6893, 26.7577, -0.8804, -16.7232, 26.7577, -0.3464, 3.3446, 26.7577, -0.3464, -3.3446002, 26.7577, -0.3464, -3.3446002, 26.7577, -0.3464, 3.3446, 26.7577, -0.3464, 0, 26.7577, -0.8804, -16.7232, 26.7582, -0.43339998, 6.6893, 26.7577, -0.3464, 3.3446, 39.877598, -3.6573, -10.033899, 26.7582, -0.43339998, 6.6893, 26.7577, -0.8804, -16.7232, 26.7582, -0.43339998, 6.6893, 26.7582, -1.0746, 13.378601, 26.7582, -0.43339998, 10.033899, 39.877598, -3.6573, -10.033899, 26.7582, -1.0746, 13.378601, 26.7582, -0.43339998, 6.6893, 39.877598, -3.6573, -10.033899, 26.7582, -1.0746, 16.7232, 26.7582, -1.0746, 13.378601, 39.877598, -3.6573, -10.033899, 26.7582, -1.0746, 20.0679, 26.7582, -1.0746, 16.7232, 26.7582, -1.0746, 20.0679, 26.7582, -1.6452999, 26.7572, 26.7582, -1.0746, 23.412498, 39.877598, -3.6573, -10.033899, 26.7582, -1.6452999, 26.7572, 26.7582, -1.0746, 20.0679, 26.7582, -1.6452999, 26.7572, 39.877598, -3.6573, 23.412498, 39.877598, -3.6573, 26.7572, 26.7582, -1.6452999, 26.7572, 39.877598, -3.6573, 20.0679, 39.877598, -3.6573, 23.412498, 26.7582, -1.6452999, 26.7572, 39.877598, -3.6573, -10.033899, 39.877598, -3.6573, 20.0679, 39.877598, -3.6573, 20.0679, 39.877598, -3.6573, -10.033899, 39.877598, -3.6573, 16.7232, 39.877598, -3.6573, 16.7232, 39.877598, -3.7507, 10.033899, 39.877598, -3.7507, 13.378601, 39.877598, -3.6573, 16.7232, 39.877598, -3.6573, -10.033899, 39.877598, -3.7507, 10.033899, 39.877598, -3.7507, 10.033899, 39.877598, -3.6573, -10.033899, 39.877598, -3.7507, 6.6893, 39.877598, -3.7507, 6.6893, 39.877598, -3.6573, -10.033899, 39.877598, -3.7507, 3.3446, 39.877598, -3.7507, 3.3446, 39.877598, -3.6573, -10.033899, 39.877598, -3.7507, 0, 39.877598, -3.7507, 0, 39.877598, -3.6573, -10.033899, 39.877598, -3.7507, -3.3446002, 39.877598, -3.7507, -3.3446002, 39.877598, -3.6573, -10.033899, 39.877598, -3.7507, -6.6893, 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, -20.0679, -0.94299996, 26.7572, -26.7572, -1.0756, 26.7572, -23.412498, -0.94299996, 26.7572, -16.7232, -0.94299996, 26.7572, -26.7572, -1.0756, 26.7572, -20.0679, -0.94299996, 26.7572, -26.7572, -1.0756, 26.7572, -23.412498, -3.6573, 39.456898, -26.7572, -3.6573, 39.456898, -26.7572, -1.0756, 26.7572, -20.0679, -3.6573, 39.456898, -23.412498, -3.6573, 39.456898, -16.7232, -0.94299996, 26.7572, -20.0679, -3.6573, 39.456898, -26.7572, -1.0756, 26.7572, -16.7232, -0.94299996, 26.7572, -16.7232, -3.6573, 39.456898, -20.0679, -3.6573, 39.456898, -16.7232, -0.94299996, 26.7572, -13.378601, -3.6573, 39.456898, -16.7232, -3.6573, 39.456898, -16.7232, -0.94299996, 26.7572, -10.033899, -3.6573, 39.456898, -13.378601, -3.6573, 39.456898, -16.7232, -0.94299996, 26.7572, -6.6893, -3.6573, 39.456898, -10.033899, -3.6573, 39.456898, -16.7232, -0.94299996, 26.7572, -3.3446002, -3.6573, 39.456898, -6.6893, -3.6573, 39.456898, -16.7232, -0.94299996, 26.7572, 0, -3.6573, 39.456898, -3.3446002, -3.6573, 39.456898, -16.7232, -0.94299996, 26.7572, 10.033899, -3.6573, 39.456898, 0, -3.6573, 39.456898, 0, -3.6573, 39.456898, 10.033899, -3.6573, 39.456898, 3.3446, -3.6573, 39.456898, 3.3446, -3.6573, 39.456898, 10.033899, -3.6573, 39.456898, 6.6893, -3.6573, 39.456898, 10.033899, -3.6573, 39.456898, -16.7232, -0.94299996, 26.7572, 13.378599, -3.6573, 39.456898, -10.033899, -0.9264, 26.7572, -16.7232, -0.94299996, 26.7572, -13.378601, -0.9264, 26.7572, -6.6893, -0.9264, 26.7572, -16.7232, -0.94299996, 26.7572, -10.033899, -0.9264, 26.7572, -3.3446002, -0.9264, 26.7572, -16.7232, -0.94299996, 26.7572, -6.6893, -0.9264, 26.7572, 0, -0.9264, 26.7572, -16.7232, -0.94299996, 26.7572, -3.3446002, -0.9264, 26.7572, 3.3446, -0.9264, 26.7572, -16.7232, -0.94299996, 26.7572, 0, -0.9264, 26.7572, 13.378599, -3.6573, 39.456898, -16.7232, -0.94299996, 26.7572, 13.378599, -1.0746, 26.7572, 13.378599, -1.0746, 26.7572, -16.7232, -0.94299996, 26.7572, 3.3446, -0.9264, 26.7572, 13.378599, -1.0746, 26.7572, 3.3446, -0.9264, 26.7572, 6.6893, -0.9264, 26.7572, 13.378599, -1.0746, 26.7572, 6.6893, -0.9264, 26.7572, 10.033899, -0.9264, 26.7572, 13.378599, -3.6573, 39.456898, 13.378599, -1.0746, 26.7572, 16.7232, -3.6573, 39.456898, 16.7232, -3.6573, 39.456898, 13.378599, -1.0746, 26.7572, 26.7582, -1.6452999, 26.7572, 26.7582, -1.6452999, 26.7572, 13.378599, -1.0746, 26.7572, 16.7232, -1.0746, 26.7572, 16.7232, -3.6573, 39.456898, 26.7582, -1.6452999, 26.7572, 20.0679, -3.6573, 39.456898, 26.7582, -1.6452999, 26.7572, 16.7232, -1.0746, 26.7572, 20.0679, -1.0746, 26.7572, 26.7582, -1.6452999, 26.7572, 20.0679, -1.0746, 26.7572, 23.412498, -1.0746, 26.7572, 20.0679, -3.6573, 39.456898, 26.7582, -1.6452999, 26.7572, 23.412498, -3.6573, 39.456898, 23.412498, -3.6573, 39.456898, 26.7582, -1.6452999, 26.7572, 26.7582, -3.6573, 39.456898, -29.294899, -0.6617, 3.3446, -27.9812, -0.6617, -3.3446002, -27.9812, -0.6617, 0, -29.294899, -0.6617, 3.3446, -29.294899, -0.6617, -6.6893, -27.9812, -0.6617, -3.3446002, -29.294899, -0.6617, -6.6893, -29.294899, -0.6617, -13.378599, -29.294899, -0.6617, -10.033899, -29.294899, -0.6617, 3.3446, -29.294899, -0.6617, -13.378599, -29.294899, -0.6617, -6.6893, -29.294899, -0.6617, 10.033899, -29.294899, -0.6617, 3.3446, -29.294899, -0.6617, 6.6893, -29.294899, -1.0265, 16.7232, -29.294899, -0.6617, 3.3446, -29.294899, -0.6617, 10.033899, -29.294899, -1.0265, 16.7232, -29.294899, -0.6617, 10.033899, -29.294899, -0.6617, 13.378601, -38.5239, -3.6573, 23.412498, -29.294899, -0.6617, 3.3446, -29.294899, -1.0265, 16.7232, -38.5239, -3.6573, 23.412498, -29.294899, -0.6617, -13.378599, -29.294899, -0.6617, 3.3446, -26.7572, -1.0756, 23.412498, -29.294899, -1.0265, 16.7232, -26.7572, -1.0756, 20.0679, -38.5239, -3.6573, 23.412498, -29.294899, -1.0265, 16.7232, -26.7572, -1.0756, 23.412498, -38.5239, -3.6573, 26.7572, -26.7572, -1.0756, 23.412498, -26.7572, -1.0756, 26.7572, -38.5239, -3.6573, 23.412498, -26.7572, -1.0756, 23.412498, -38.5239, -3.6573, 26.7572, -38.5239, -3.6573, 16.7232, -38.5239, -3.6573, 23.412498, -38.5239, -3.6573, 20.0679, -38.5239, -3.6573, 13.378601, -38.5239, -3.6573, 23.412498, -38.5239, -3.6573, 16.7232, -38.5239, -3.6573, 10.033899, -38.5239, -3.6573, 23.412498, -38.5239, -3.6573, 13.378601, -38.5239, -3.6573, 10.033899, -29.294899, -0.6617, -13.378599, -38.5239, -3.6573, 23.412498, -38.5239, -3.6573, 3.3446, -38.5239, -3.6573, 10.033899, -38.5239, -3.6573, 6.6893, -38.5239, -3.6573, 0, -38.5239, -3.6573, 10.033899, -38.5239, -3.6573, 3.3446, -38.5239, -3.6573, 0, -29.294899, -0.6617, -13.378599, -38.5239, -3.6573, 10.033899, -38.5239, -3.6573, -3.3446002, -29.294899, -0.6617, -13.378599, -38.5239, -3.6573, 0, -29.294899, -0.6617, -13.378599, -38.5239, -3.6573, -3.3446002, -38.5239, -3.6573, -6.6893, -29.294899, -0.6617, -13.378599, -26.7572, -1.6710999, -20.4958, -26.7572, -1.6710999, -17.1511, -29.294899, -0.6617, -13.378599, -26.7572, -2.7663999, -23.412498, -26.7572, -1.6710999, -20.4958, -29.294899, -0.6617, -13.378599, -38.5239, -3.6573, -26.7572, -26.7572, -2.7663999, -23.412498, -26.7572, -2.7663999, -23.412498, -38.5239, -3.6573, -26.7572, -26.7572, -3.0929, -26.7572, -29.294899, -0.6617, -13.378599, -38.5239, -3.6573, -6.6893, -38.5239, -3.6573, -20.0679, -29.294899, -0.6617, -13.378599, -38.5239, -3.6573, -20.0679, -38.5239, -3.6573, -26.7572, -38.5239, -3.6573, -26.7572, -38.5239, -3.6573, -20.0679, -38.5239, -3.6573, -23.412498, -38.5239, -3.6573, -20.0679, -38.5239, -3.6573, -6.6893, -38.5239, -3.6573, -10.033899, -38.5239, -3.6573, -20.0679, -38.5239, -3.6573, -10.033899, -38.5239, -3.6573, -16.7232, -38.5239, -3.6573, -16.7232, -38.5239, -3.6573, -10.033899, -38.5239, -3.6573, -13.378599, -50.290497, -8.1437, -23.412498, -62.057194, -10.4337, -26.7572, -50.290497, -8.1437, -26.7572, -50.290497, -8.1437, -23.412498, -62.057194, -10.4337, -23.412498, -62.057194, -10.4337, -26.7572, -50.290497, -8.1437, -20.0679, -62.057194, -10.4337, -23.412498, -50.290497, -8.1437, -23.412498, -50.290497, -8.1437, -20.0679, -62.057194, -10.4337, -20.0679, -62.057194, -10.4337, -23.412498, -50.290497, -8.1437, -16.7232, -62.057194, -10.4337, -20.0679, -50.290497, -8.1437, -20.0679, -50.290497, -8.1437, -16.7232, -62.057194, -10.4337, -16.7232, -62.057194, -10.4337, -20.0679, -50.290497, -8.1437, -13.378599, -62.057194, -10.4337, -16.7232, -50.290497, -8.1437, -16.7232, -50.290497, -8.1437, -13.378599, -62.057194, -10.4337, -13.378599, -62.057194, -10.4337, -16.7232, -50.290497, -8.1437, -10.033899, -62.057194, -10.4337, -13.378599, -50.290497, -8.1437, -13.378599, -50.290497, -8.1437, -10.033899, -62.057194, -10.4337, -10.033899, -62.057194, -10.4337, -13.378599, -50.290497, -8.1437, -6.6893, -62.057194, -10.4337, -10.033899, -50.290497, -8.1437, -10.033899, -50.290497, -8.1437, -6.6893, -62.057194, -10.4337, -6.6893, -62.057194, -10.4337, -10.033899, -50.290497, -8.1437, -3.3446002, -62.057194, -10.4337, -6.6893, -50.290497, -8.1437, -6.6893, -50.290497, -8.1437, -3.3446002, -62.057194, -10.4337, -3.3446002, -62.057194, -10.4337, -6.6893, -50.290497, -8.1437, 0, -62.057194, -10.4337, -3.3446002, -50.290497, -8.1437, -3.3446002, -50.290497, -8.1437, 0, -62.057194, -10.4337, 0, -62.057194, -10.4337, -3.3446002, -50.290497, -8.1437, 3.3446, -62.057194, -10.4337, 0, -50.290497, -8.1437, 0, -50.290497, -8.1437, 3.3446, -62.057194, -10.4337, 3.3446, -62.057194, -10.4337, 0, -50.290497, -8.1437, 6.6893, -62.057194, -10.4337, 3.3446, -50.290497, -8.1437, 3.3446, -50.290497, -8.1437, 6.6893, -62.057194, -10.4337, 6.6893, -62.057194, -10.4337, 3.3446, -50.290497, -8.1437, 10.033899, -62.057194, -10.4337, 6.6893, -50.290497, -8.1437, 6.6893, -50.290497, -8.1437, 10.033899, -62.057194, -10.4337, 10.033899, -62.057194, -10.4337, 6.6893, -50.290497, -8.1437, 13.378601, -62.057194, -10.4337, 10.033899, -50.290497, -8.1437, 10.033899, -50.290497, -8.1437, 13.378601, -62.057194, -10.4337, 13.378601, -62.057194, -10.4337, 10.033899, -50.290497, -8.1437, 16.7232, -62.057194, -10.4337, 13.378601, -50.290497, -8.1437, 13.378601, -50.290497, -8.1437, 16.7232, -62.057194, -10.4337, 16.7232, -62.057194, -10.4337, 13.378601, -50.290497, -8.1437, 20.0679, -62.057194, -10.4337, 16.7232, -50.290497, -8.1437, 16.7232, -50.290497, -8.1437, 20.0679, -62.057194, -10.4337, 20.0679, -62.057194, -10.4337, 16.7232, -50.290497, -8.1437, 23.412498, -62.057194, -10.4337, 20.0679, -50.290497, -8.1437, 20.0679, -62.057194, -10.4337, 20.0679, -50.290497, -8.1437, 23.412498, -62.057194, -10.4337, 23.412498, -62.057194, -10.4337, 23.412498, -50.290497, -8.1437, 23.412498, -62.057194, -10.4337, 26.7572, -62.057194, -10.4337, 26.7572, -50.290497, -8.1437, 23.412498, -50.290497, -8.1437, 26.7572, -62.057194, -10.4337, -23.412498, -73.8239, -10.4337, -26.7572, -62.057194, -10.4337, -26.7572, -73.8239, -10.4337, -26.7572, -26.7572, -10.4337, -68.371895, -62.057194, -10.4337, -26.7572, -73.8239, -10.4337, -26.7572, -26.7572, -10.4337, -70.9319, -26.7572, -10.4337, -68.371895, -73.8239, -10.4337, -26.7572, -26.7572, -10.4337, -73.7615, -26.7572, -10.4337, -70.9319, -26.7572, -10.4337, -73.7615, -73.8239, -10.4337, -26.7572, -26.7572, -10.4337, -76.6203, -26.7572, -10.4337, -76.6203, -73.8239, -10.4337, -26.7572, -26.7572, -10.4337, -79.0804, -26.7572, -10.4337, -79.0804, -73.8239, -10.4337, -26.7572, -26.7572, -10.4337, -82.2434, -62.057194, -10.4337, -23.412498, -73.8239, -10.4337, -23.412498, -73.8239, -10.4337, -26.7572, -62.057194, -10.4337, -20.0679, -73.8239, -10.4337, -23.412498, -62.057194, -10.4337, -23.412498, -62.057194, -10.4337, -20.0679, -73.8239, -10.4337, -20.0679, -73.8239, -10.4337, -23.412498, -62.057194, -10.4337, -16.7232, -73.8239, -10.4337, -20.0679, -62.057194, -10.4337, -20.0679, -62.057194, -10.4337, -16.7232, -73.8239, -10.4337, -16.7232, -73.8239, -10.4337, -20.0679, -62.057194, -10.4337, -13.378599, -73.8239, -10.4337, -16.7232, -62.057194, -10.4337, -16.7232, -62.057194, -10.4337, -13.378599, -73.8239, -10.4337, -13.378599, -73.8239, -10.4337, -16.7232, -62.057194, -10.4337, -10.033899, -73.8239, -10.4337, -13.378599, -62.057194, -10.4337, -13.378599, -62.057194, -10.4337, -10.033899, -73.8239, -10.4337, -10.033899, -73.8239, -10.4337, -13.378599, -62.057194, -10.4337, -6.6893, -73.8239, -10.4337, -10.033899, -62.057194, -10.4337, -10.033899, -62.057194, -10.4337, -6.6893, -73.8239, -10.4337, -6.6893, -73.8239, -10.4337, -10.033899, -62.057194, -10.4337, -3.3446002, -73.8239, -10.4337, -6.6893, -62.057194, -10.4337, -6.6893, -62.057194, -10.4337, -3.3446002, -73.8239, -10.4337, -3.3446002, -73.8239, -10.4337, -6.6893, -62.057194, -10.4337, 0, -73.8239, -10.4337, -3.3446002, -62.057194, -10.4337, -3.3446002, -62.057194, -10.4337, 0, -73.8239, -10.4337, 0, -73.8239, -10.4337, -3.3446002, -62.057194, -10.4337, 3.3446, -73.8239, -10.4337, 0, -62.057194, -10.4337, 0, -62.057194, -10.4337, 3.3446, -73.8239, -10.4337, 3.3446, -73.8239, -10.4337, 0, -62.057194, -10.4337, 6.6893, -73.8239, -10.4337, 3.3446, -62.057194, -10.4337, 3.3446, -62.057194, -10.4337, 6.6893, -73.8239, -10.4337, 6.6893, -73.8239, -10.4337, 3.3446, -62.057194, -10.4337, 10.033899, -73.8239, -10.4337, 6.6893, -62.057194, -10.4337, 6.6893, -62.057194, -10.4337, 10.033899, -73.8239, -10.4337, 10.033899, -73.8239, -10.4337, 6.6893, -62.057194, -10.4337, 13.378601, -73.8239, -10.4337, 10.033899, -62.057194, -10.4337, 10.033899, -62.057194, -10.4337, 13.378601, -73.8239, -10.4337, 13.378601, -73.8239, -10.4337, 10.033899, -62.057194, -10.4337, 16.7232, -73.8239, -10.4337, 13.378601, -62.057194, -10.4337, 13.378601, -62.057194, -10.4337, 16.7232, -73.8239, -10.4337, 16.7232, -73.8239, -10.4337, 13.378601, -62.057194, -10.4337, 20.0679, -73.8239, -10.4337, 16.7232, -62.057194, -10.4337, 16.7232, -62.057194, -10.4337, 20.0679, -73.8239, -10.4337, 20.0679, -73.8239, -10.4337, 16.7232, -62.057194, -10.4337, 23.412498, -73.8239, -10.4337, 20.0679, -62.057194, -10.4337, 20.0679, -73.8239, -10.4337, 20.0679, -62.057194, -10.4337, 23.412498, -73.8239, -10.4337, 23.412498, -73.8239, -10.4337, 23.412498, -62.057194, -10.4337, 23.412498, -73.8239, -10.4337, 26.7572, -73.8239, -10.4337, 26.7572, -62.057194, -10.4337, 23.412498, -62.057194, -10.4337, 26.7572, -73.8239, -10.4337, 26.7572, -62.057194, -10.4337, 26.7572, -26.7572, -10.4337, 65.2334, -73.8239, -10.4337, 26.7572, -26.7572, -10.4337, 65.2334, -26.7572, -10.4337, 78.3101, -23.412498, -10.4337, 65.2334, -26.7572, -10.4337, 78.3101, -26.7572, -10.4337, 65.2334, -23.412498, -10.4337, 65.2334, -23.412498, -10.4337, 78.3101, -26.7572, -10.4337, 78.3101, -20.0679, -10.4337, 65.2334, -23.412498, -10.4337, 78.3101, -23.412498, -10.4337, 65.2334, -20.0679, -10.4337, 65.2334, -20.0679, -10.4337, 78.3101, -23.412498, -10.4337, 78.3101, -16.7232, -10.4337, 65.2334, -20.0679, -10.4337, 78.3101, -20.0679, -10.4337, 65.2334, -16.7232, -10.4337, 65.2334, -16.7232, -10.4337, 78.3101, -20.0679, -10.4337, 78.3101, -13.378601, -10.4337, 65.2334, -16.7232, -10.4337, 78.3101, -16.7232, -10.4337, 65.2334, -13.378601, -10.4337, 65.2334, -13.378601, -10.4337, 78.3101, -16.7232, -10.4337, 78.3101, -10.033899, -10.4337, 65.2334, -13.378601, -10.4337, 78.3101, -13.378601, -10.4337, 65.2334, -10.033899, -10.4337, 65.2334, -10.033899, -10.4337, 78.3101, -13.378601, -10.4337, 78.3101, -6.6893, -10.4337, 65.2334, -10.033899, -10.4337, 78.3101, -10.033899, -10.4337, 65.2334, -6.6893, -10.4337, 65.2334, -6.6893, -10.4337, 78.3101, -10.033899, -10.4337, 78.3101, -3.3446002, -10.4337, 65.2334, -6.6893, -10.4337, 78.3101, -6.6893, -10.4337, 65.2334, -3.3446002, -10.4337, 65.2334, -3.3446002, -10.4337, 78.3101, -6.6893, -10.4337, 78.3101, 0, -10.4337, 65.2334, -3.3446002, -10.4337, 78.3101, -3.3446002, -10.4337, 65.2334, 0, -10.4337, 65.2334, 0, -10.4337, 78.3101, -3.3446002, -10.4337, 78.3101, 3.3446, -10.4337, 65.2334, 0, -10.4337, 78.3101, 0, -10.4337, 65.2334, 3.3446, -10.4337, 65.2334, 3.3446, -10.4337, 78.3101, 0, -10.4337, 78.3101, 6.6893, -10.4337, 65.2334, 3.3446, -10.4337, 78.3101, 3.3446, -10.4337, 65.2334, 6.6893, -10.4337, 65.2334, 6.6893, -10.4337, 78.3101, 3.3446, -10.4337, 78.3101, 10.033899, -10.4337, 65.2334, 6.6893, -10.4337, 78.3101, 6.6893, -10.4337, 65.2334, 10.033899, -10.4337, 65.2334, 10.033899, -10.4337, 78.3101, 6.6893, -10.4337, 78.3101, 13.378599, -10.4337, 65.2334, 10.033899, -10.4337, 78.3101, 10.033899, -10.4337, 65.2334, 13.378599, -10.4337, 65.2334, 13.378599, -10.4337, 78.3101, 10.033899, -10.4337, 78.3101, 16.7232, -10.4337, 65.2334, 13.378599, -10.4337, 78.3101, 13.378599, -10.4337, 65.2334, 16.7232, -10.4337, 65.2334, 16.7232, -10.4337, 78.3101, 13.378599, -10.4337, 78.3101, 20.0679, -10.4337, 65.2334, 16.7232, -10.4337, 78.3101, 16.7232, -10.4337, 65.2334, 20.0679, -10.4337, 65.2334, 20.0679, -10.4337, 78.3101, 16.7232, -10.4337, 78.3101, 23.412498, -10.4337, 65.2334, 20.0679, -10.4337, 78.3101, 20.0679, -10.4337, 65.2334, 20.0679, -10.4337, 78.3101, 23.412498, -10.4337, 65.2334, 23.412498, -10.4337, 78.3101, 23.412498, -10.4337, 78.3101, 23.412498, -10.4337, 65.2334, 26.7582, -10.4337, 78.3101, 26.7582, -10.4337, 78.3101, 23.412498, -10.4337, 65.2334, 26.7582, -10.4337, 65.2334, 66.1166, -10.4337, 26.7572, 26.7582, -10.4337, 78.3101, 26.7582, -10.4337, 65.2334, 66.1166, -10.4337, 26.7572, 79.236, -10.4337, 26.7572, 26.7582, -10.4337, 78.3101, -38.5239, -3.6573, -23.412498, -50.290497, -8.1437, -26.7572, -38.5239, -3.6573, -26.7572, -38.5239, -3.6573, -23.412498, -50.290497, -8.1437, -23.412498, -50.290497, -8.1437, -26.7572, -38.5239, -3.6573, -20.0679, -50.290497, -8.1437, -23.412498, -38.5239, -3.6573, -23.412498, -38.5239, -3.6573, -20.0679, -50.290497, -8.1437, -20.0679, -50.290497, -8.1437, -23.412498, -38.5239, -3.6573, -16.7232, -50.290497, -8.1437, -20.0679, -38.5239, -3.6573, -20.0679, -38.5239, -3.6573, -16.7232, -50.290497, -8.1437, -16.7232, -50.290497, -8.1437, -20.0679, -38.5239, -3.6573, -13.378599, -50.290497, -8.1437, -16.7232, -38.5239, -3.6573, -16.7232, -38.5239, -3.6573, -13.378599, -50.290497, -8.1437, -13.378599, -50.290497, -8.1437, -16.7232, -38.5239, -3.6573, -10.033899, -50.290497, -8.1437, -13.378599, -38.5239, -3.6573, -13.378599, -38.5239, -3.6573, -10.033899, -50.290497, -8.1437, -10.033899, -50.290497, -8.1437, -13.378599, -38.5239, -3.6573, -6.6893, -50.290497, -8.1437, -10.033899, -38.5239, -3.6573, -10.033899, -38.5239, -3.6573, -6.6893, -50.290497, -8.1437, -6.6893, -50.290497, -8.1437, -10.033899, -38.5239, -3.6573, -3.3446002, -50.290497, -8.1437, -6.6893, -38.5239, -3.6573, -6.6893, -38.5239, -3.6573, -3.3446002, -50.290497, -8.1437, -3.3446002, -50.290497, -8.1437, -6.6893, -38.5239, -3.6573, 0, -50.290497, -8.1437, -3.3446002, -38.5239, -3.6573, -3.3446002, -38.5239, -3.6573, 0, -50.290497, -8.1437, 0, -50.290497, -8.1437, -3.3446002, -38.5239, -3.6573, 3.3446, -50.290497, -8.1437, 0, -38.5239, -3.6573, 0, -38.5239, -3.6573, 3.3446, -50.290497, -8.1437, 3.3446, -50.290497, -8.1437, 0, -38.5239, -3.6573, 6.6893, -50.290497, -8.1437, 3.3446, -38.5239, -3.6573, 3.3446, -38.5239, -3.6573, 6.6893, -50.290497, -8.1437, 6.6893, -50.290497, -8.1437, 3.3446, -38.5239, -3.6573, 10.033899, -50.290497, -8.1437, 6.6893, -38.5239, -3.6573, 6.6893, -38.5239, -3.6573, 10.033899, -50.290497, -8.1437, 10.033899, -50.290497, -8.1437, 6.6893, -38.5239, -3.6573, 13.378601, -50.290497, -8.1437, 10.033899, -38.5239, -3.6573, 10.033899, -38.5239, -3.6573, 13.378601, -50.290497, -8.1437, 13.378601, -50.290497, -8.1437, 10.033899, -38.5239, -3.6573, 16.7232, -50.290497, -8.1437, 13.378601, -38.5239, -3.6573, 13.378601, -38.5239, -3.6573, 16.7232, -50.290497, -8.1437, 16.7232, -50.290497, -8.1437, 13.378601, -38.5239, -3.6573, 20.0679, -50.290497, -8.1437, 16.7232, -38.5239, -3.6573, 16.7232, -38.5239, -3.6573, 20.0679, -50.290497, -8.1437, 20.0679, -50.290497, -8.1437, 16.7232, -38.5239, -3.6573, 23.412498, -50.290497, -8.1437, 20.0679, -38.5239, -3.6573, 20.0679, -50.290497, -8.1437, 20.0679, -38.5239, -3.6573, 23.412498, -50.290497, -8.1437, 23.412498, -50.290497, -8.1437, 23.412498, -38.5239, -3.6573, 23.412498, -50.290497, -8.1437, 26.7572, -50.290497, -8.1437, 26.7572, -38.5239, -3.6573, 23.412498, -38.5239, -3.6573, 26.7572, -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.3464, 0, 23.412498, 0, 0, 23.412498, 0, 0, 26.7577, -0.3464, 0, 26.7577, -0.3464, 3.3446, 23.412498, 0, -3.3446002, 26.7577, -0.3464, -3.3446002, 26.7577, -0.3464, 0, 23.412498, 0, -6.6893, 26.7577, -0.3464, -3.3446002, 23.412498, 0, -3.3446002, 23.412498, 0, -6.6893, 26.7577, -0.3464, -6.6893, 26.7577, -0.3464, -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, -0.43339998, 10.033899, 23.412498, -1.0746, 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, -1.0746, 23.412498, 26.7582, -1.6452999, 26.7572, 23.412498, -1.0746, 26.7572, 23.412498, -1.0746, 23.412498, 26.7582, -1.0746, 23.412498, 26.7582, -1.6452999, 26.7572, 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, -1.0924, -20.0679, 26.7582, -1.0924, -20.0679, 26.7577, -0.8804, -16.7232, 52.997097, -8.1437, 10.033899, 52.997097, -8.1437, 16.7232, 52.997097, -8.1437, 13.378601, 52.997097, -8.1437, 6.6893, 52.997097, -8.1437, 16.7232, 52.997097, -8.1437, 10.033899, 52.997097, -8.1437, 6.6893, 66.1166, -10.4337, 6.6893, 52.997097, -8.1437, 16.7232, 66.1166, -10.4337, 6.6893, 52.997097, -8.1437, 6.6893, 52.997097, -8.1437, 3.3446, 66.1166, -10.4337, 6.6893, 52.997097, -8.1437, 3.3446, 66.1166, -10.4337, 3.3446, 66.1166, -10.4337, 3.3446, 52.997097, -8.1437, 3.3446, 52.997097, -8.1437, 0, 66.1166, -10.4337, 3.3446, 52.997097, -8.1437, 0, 66.1166, -10.4337, 0, 66.1166, -10.4337, 0, 52.997097, -8.1437, 0, 52.997097, -8.1437, -3.3446002, 66.1166, -10.4337, 0, 52.997097, -8.1437, -3.3446002, 66.1166, -10.4337, -3.3446002, 66.1166, -10.4337, -3.3446002, 52.997097, -8.1437, -3.3446002, 52.997097, -8.1437, -6.6893, 66.1166, -10.4337, -3.3446002, 52.997097, -8.1437, -6.6893, 66.1166, -10.4337, -13.378599, 66.1166, -10.4337, -3.3446002, 66.1166, -10.4337, -13.378599, 66.1166, -10.4337, -6.6893, 66.1166, -10.4337, -6.6893, 66.1166, -10.4337, -13.378599, 66.1166, -10.4337, -10.033899, 66.1166, -10.4337, -13.378599, 52.997097, -8.1437, -6.6893, 52.997097, -8.1437, -10.033899, 66.1166, -10.4337, -13.378599, 52.997097, -8.1437, -10.033899, 52.997097, -8.1672, -26.7572, 52.997097, -8.1672, -26.7572, 52.997097, -8.1437, -10.033899, 52.997097, -8.1437, -13.378599, 66.1166, -10.4337, -13.378599, 66.1166, -10.4337, -20.0679, 66.1166, -10.4337, -16.7232, 66.1166, -10.4337, -13.378599, 52.997097, -8.1672, -26.7572, 66.1166, -10.4337, -20.0679, 66.1166, -10.4337, -20.0679, 52.997097, -8.1672, -26.7572, 66.1166, -10.4337, -23.412498, 66.1166, -10.4337, -23.412498, 52.997097, -8.1672, -26.7572, 66.1166, -10.4572, -26.7572, 52.997097, -8.1672, -26.7572, 52.997097, -8.1437, -13.378599, 52.997097, -8.1437, -20.0679, 52.997097, -8.1437, -20.0679, 52.997097, -8.1437, -13.378599, 52.997097, -8.1437, -16.7232, 52.997097, -8.1672, -26.7572, 52.997097, -8.1437, -20.0679, 52.997097, -8.1437, -23.412498, 52.997097, -8.1437, 23.412498, 66.1166, -10.4337, 26.7572, 52.997097, -8.1437, 26.7572, 52.997097, -8.1437, 23.412498, 66.1166, -10.4337, 23.412498, 66.1166, -10.4337, 26.7572, 52.997097, -8.1437, 20.0679, 66.1166, -10.4337, 23.412498, 52.997097, -8.1437, 23.412498, 52.997097, -8.1437, 20.0679, 66.1166, -10.4337, 20.0679, 66.1166, -10.4337, 23.412498, 52.997097, -8.1437, 16.7232, 66.1166, -10.4337, 20.0679, 52.997097, -8.1437, 20.0679, 52.997097, -8.1437, 16.7232, 66.1166, -10.4337, 16.7232, 66.1166, -10.4337, 20.0679, 52.997097, -8.1437, 16.7232, 66.1166, -10.4337, 6.6893, 66.1166, -10.4337, 16.7232, 66.1166, -10.4337, 16.7232, 66.1166, -10.4337, 6.6893, 66.1166, -10.4337, 13.378601, 66.1166, -10.4337, 13.378601, 66.1166, -10.4337, 6.6893, 66.1166, -10.4337, 10.033899, 79.236, -10.4572, -26.7572, 66.1166, -10.4337, -23.412498, 66.1166, -10.4572, -26.7572, 79.236, -10.4572, -26.7572, 66.1166, -10.4337, -16.7232, 66.1166, -10.4337, -23.412498, 66.1166, -10.4337, -23.412498, 66.1166, -10.4337, -16.7232, 66.1166, -10.4337, -20.0679, 79.236, -10.4337, -20.0679, 66.1166, -10.4337, -16.7232, 79.236, -10.4572, -26.7572, 79.236, -10.4337, -20.0679, 79.236, -10.4572, -26.7572, 79.236, -10.4337, -23.412498, 79.236, -10.4337, -16.7232, 66.1166, -10.4337, -16.7232, 79.236, -10.4337, -20.0679, 79.236, -10.4337, -16.7232, 66.1166, -10.4337, -13.378599, 66.1166, -10.4337, -16.7232, 79.236, -10.4337, -10.033899, 79.236, -10.4337, -16.7232, 79.236, -10.4337, -13.378599, 79.236, -10.4337, -6.6893, 79.236, -10.4337, -16.7232, 79.236, -10.4337, -10.033899, 79.236, -10.4337, -6.6893, 66.1166, -10.4337, -13.378599, 79.236, -10.4337, -16.7232, 79.236, -10.4337, -6.6893, 66.1166, -10.4337, -10.033899, 66.1166, -10.4337, -13.378599, 79.236, -10.4337, -6.6893, 66.1166, -10.4337, -3.3446002, 66.1166, -10.4337, -10.033899, 66.1166, -10.4337, -10.033899, 66.1166, -10.4337, -3.3446002, 66.1166, -10.4337, -6.6893, 79.236, -10.4337, -6.6893, 66.1166, -10.4337, 0, 66.1166, -10.4337, -3.3446002, 66.1166, -10.4337, 0, 79.236, -10.4337, -6.6893, 79.236, -10.4337, 10.033899, 79.236, -10.4337, 10.033899, 79.236, -10.4337, -6.6893, 79.236, -10.4337, -3.3446002, 66.1166, -10.4337, 0, 79.236, -10.4337, 10.033899, 66.1166, -10.4337, 3.3446, 79.236, -10.4337, 10.033899, 79.236, -10.4337, -3.3446002, 79.236, -10.4337, 0, 79.236, -10.4337, 10.033899, 79.236, -10.4337, 0, 79.236, -10.4337, 3.3446, 79.236, -10.4337, 10.033899, 79.236, -10.4337, 3.3446, 79.236, -10.4337, 6.6893, 66.1166, -10.4337, 3.3446, 79.236, -10.4337, 10.033899, 66.1166, -10.4337, 6.6893, 66.1166, -10.4337, 6.6893, 79.236, -10.4337, 10.033899, 79.236, -10.4337, 23.412498, 79.236, -10.4337, 23.412498, 79.236, -10.4337, 10.033899, 79.236, -10.4337, 13.378601, 79.236, -10.4337, 23.412498, 79.236, -10.4337, 13.378601, 79.236, -10.4337, 16.7232, 79.236, -10.4337, 23.412498, 79.236, -10.4337, 16.7232, 79.236, -10.4337, 20.0679, 66.1166, -10.4337, 6.6893, 79.236, -10.4337, 23.412498, 66.1166, -10.4337, 13.378601, 66.1166, -10.4337, 6.6893, 66.1166, -10.4337, 13.378601, 66.1166, -10.4337, 10.033899, 66.1166, -10.4337, 13.378601, 79.236, -10.4337, 23.412498, 66.1166, -10.4337, 20.0679, 66.1166, -10.4337, 13.378601, 66.1166, -10.4337, 20.0679, 66.1166, -10.4337, 16.7232, 66.1166, -10.4337, 20.0679, 79.236, -10.4337, 23.412498, 66.1166, -10.4337, 26.7572, 66.1166, -10.4337, 26.7572, 79.236, -10.4337, 23.412498, 79.236, -10.4337, 26.7572, 66.1166, -10.4337, 20.0679, 66.1166, -10.4337, 26.7572, 66.1166, -10.4337, 23.412498, 39.877598, -3.6573, -13.378599, 39.877598, -3.7507, -6.6893, 39.877598, -3.6573, -10.033899, 39.877598, -3.6573, -16.7232, 39.877598, -3.7507, -6.6893, 39.877598, -3.6573, -13.378599, 39.877598, -3.6573, -20.0679, 39.877598, -3.7507, -6.6893, 39.877598, -3.6573, -16.7232, 39.877598, -3.6808999, -26.7572, 39.877598, -3.7507, -6.6893, 39.877598, -3.6573, -20.0679, 39.877598, -3.6808999, -26.7572, 39.877598, -3.6573, -20.0679, 39.877598, -3.6573, -23.412498, 52.997097, -8.1437, -16.7232, 39.877598, -3.7507, -6.6893, 39.877598, -3.6808999, -26.7572, 52.997097, -8.1437, -16.7232, 39.877598, -3.6808999, -26.7572, 52.997097, -8.1437, -23.412498, 52.997097, -8.1437, -23.412498, 39.877598, -3.6808999, -26.7572, 52.997097, -8.1672, -26.7572, 52.997097, -8.1437, -16.7232, 52.997097, -8.1437, -23.412498, 52.997097, -8.1437, -20.0679, 52.997097, -8.1437, -13.378599, 39.877598, -3.7507, -6.6893, 52.997097, -8.1437, -16.7232, 52.997097, -8.1437, -13.378599, 39.877598, -3.7507, -3.3446002, 39.877598, -3.7507, -6.6893, 52.997097, -8.1437, -6.6893, 39.877598, -3.7507, -3.3446002, 52.997097, -8.1437, -13.378599, 52.997097, -8.1437, -6.6893, 52.997097, -8.1437, -13.378599, 52.997097, -8.1437, -10.033899, 52.997097, -8.1437, -6.6893, 39.877598, -3.7507, 0, 39.877598, -3.7507, -3.3446002, 52.997097, -8.1437, 0, 52.997097, -8.1437, -6.6893, 52.997097, -8.1437, -3.3446002, 52.997097, -8.1437, 3.3446, 52.997097, -8.1437, -6.6893, 52.997097, -8.1437, 0, 52.997097, -8.1437, 3.3446, 39.877598, -3.7507, 0, 52.997097, -8.1437, -6.6893, 39.877598, -3.7507, 0, 39.877598, -3.7507, 6.6893, 39.877598, -3.7507, 3.3446, 39.877598, -3.7507, 0, 39.877598, -3.7507, 13.378601, 39.877598, -3.7507, 6.6893, 39.877598, -3.7507, 0, 52.997097, -8.1437, 3.3446, 39.877598, -3.7507, 13.378601, 39.877598, -3.7507, 6.6893, 39.877598, -3.7507, 13.378601, 39.877598, -3.7507, 10.033899, 52.997097, -8.1437, 10.033899, 52.997097, -8.1437, 3.3446, 52.997097, -8.1437, 6.6893, 52.997097, -8.1437, 16.7232, 52.997097, -8.1437, 3.3446, 52.997097, -8.1437, 10.033899, 39.877598, -3.7507, 13.378601, 52.997097, -8.1437, 3.3446, 52.997097, -8.1437, 16.7232, 52.997097, -8.1437, 16.7232, 52.997097, -8.1437, 10.033899, 52.997097, -8.1437, 13.378601, 39.877598, -3.7507, 13.378601, 52.997097, -8.1437, 16.7232, 39.877598, -3.6573, 23.412498, 39.877598, -3.7507, 13.378601, 39.877598, -3.6573, 23.412498, 39.877598, -3.6573, 16.7232, 39.877598, -3.6573, 16.7232, 39.877598, -3.6573, 23.412498, 39.877598, -3.6573, 20.0679, 39.877598, -3.6573, 23.412498, 52.997097, -8.1437, 16.7232, 52.997097, -8.1437, 20.0679, 39.877598, -3.6573, 23.412498, 52.997097, -8.1437, 20.0679, 52.997097, -8.1437, 26.7572, 52.997097, -8.1437, 26.7572, 52.997097, -8.1437, 20.0679, 52.997097, -8.1437, 23.412498, 39.877598, -3.6573, 23.412498, 52.997097, -8.1437, 26.7572, 39.877598, -3.6573, 26.7572, -23.412498, -8.1437, 52.156597, -26.7572, -10.4337, 65.2334, -26.7572, -8.1437, 52.156597, -23.412498, -8.1437, 52.156597, -23.412498, -10.4337, 65.2334, -26.7572, -10.4337, 65.2334, -20.0679, -8.1437, 52.156597, -23.412498, -10.4337, 65.2334, -23.412498, -8.1437, 52.156597, -20.0679, -8.1437, 52.156597, -20.0679, -10.4337, 65.2334, -23.412498, -10.4337, 65.2334, -16.7232, -8.1437, 52.156597, -20.0679, -10.4337, 65.2334, -20.0679, -8.1437, 52.156597, -16.7232, -8.1437, 52.156597, -16.7232, -10.4337, 65.2334, -20.0679, -10.4337, 65.2334, -13.378601, -8.1437, 52.156597, -16.7232, -10.4337, 65.2334, -16.7232, -8.1437, 52.156597, -13.378601, -8.1437, 52.156597, -13.378601, -10.4337, 65.2334, -16.7232, -10.4337, 65.2334, -10.033899, -8.1437, 52.156597, -13.378601, -10.4337, 65.2334, -13.378601, -8.1437, 52.156597, -10.033899, -8.1437, 52.156597, -10.033899, -10.4337, 65.2334, -13.378601, -10.4337, 65.2334, -6.6893, -8.1437, 52.156597, -10.033899, -10.4337, 65.2334, -10.033899, -8.1437, 52.156597, -6.6893, -8.1437, 52.156597, -6.6893, -10.4337, 65.2334, -10.033899, -10.4337, 65.2334, -3.3446002, -8.1437, 52.156597, -6.6893, -10.4337, 65.2334, -6.6893, -8.1437, 52.156597, -3.3446002, -8.1437, 52.156597, -3.3446002, -10.4337, 65.2334, -6.6893, -10.4337, 65.2334, 0, -8.1437, 52.156597, -3.3446002, -10.4337, 65.2334, -3.3446002, -8.1437, 52.156597, 0, -8.1437, 52.156597, 0, -10.4337, 65.2334, -3.3446002, -10.4337, 65.2334, 3.3446, -8.1437, 52.156597, 0, -10.4337, 65.2334, 0, -8.1437, 52.156597, 3.3446, -8.1437, 52.156597, 3.3446, -10.4337, 65.2334, 0, -10.4337, 65.2334, 6.6893, -8.1437, 52.156597, 3.3446, -10.4337, 65.2334, 3.3446, -8.1437, 52.156597, 6.6893, -8.1437, 52.156597, 6.6893, -10.4337, 65.2334, 3.3446, -10.4337, 65.2334, 10.033899, -8.1437, 52.156597, 6.6893, -10.4337, 65.2334, 6.6893, -8.1437, 52.156597, 10.033899, -8.1437, 52.156597, 10.033899, -10.4337, 65.2334, 6.6893, -10.4337, 65.2334, 13.378599, -8.1437, 52.156597, 10.033899, -10.4337, 65.2334, 10.033899, -8.1437, 52.156597, 13.378599, -8.1437, 52.156597, 13.378599, -10.4337, 65.2334, 10.033899, -10.4337, 65.2334, 16.7232, -8.1437, 52.156597, 13.378599, -10.4337, 65.2334, 13.378599, -8.1437, 52.156597, 16.7232, -8.1437, 52.156597, 16.7232, -10.4337, 65.2334, 13.378599, -10.4337, 65.2334, 20.0679, -8.1437, 52.156597, 16.7232, -10.4337, 65.2334, 16.7232, -8.1437, 52.156597, 20.0679, -8.1437, 52.156597, 20.0679, -10.4337, 65.2334, 16.7232, -10.4337, 65.2334, 23.412498, -8.1437, 52.156597, 20.0679, -10.4337, 65.2334, 20.0679, -8.1437, 52.156597, 20.0679, -10.4337, 65.2334, 23.412498, -8.1437, 52.156597, 23.412498, -10.4337, 65.2334, 23.412498, -10.4337, 65.2334, 23.412498, -8.1437, 52.156597, 26.7582, -10.4337, 65.2334, 26.7582, -10.4337, 65.2334, 23.412498, -8.1437, 52.156597, 26.7582, -8.1437, 52.156597, -23.412498, -3.6573, 39.456898, -26.7572, -8.1437, 52.156597, -26.7572, -3.6573, 39.456898, -23.412498, -3.6573, 39.456898, -23.412498, -8.1437, 52.156597, -26.7572, -8.1437, 52.156597, -20.0679, -3.6573, 39.456898, -23.412498, -8.1437, 52.156597, -23.412498, -3.6573, 39.456898, -20.0679, -3.6573, 39.456898, -20.0679, -8.1437, 52.156597, -23.412498, -8.1437, 52.156597, -16.7232, -3.6573, 39.456898, -20.0679, -8.1437, 52.156597, -20.0679, -3.6573, 39.456898, -16.7232, -3.6573, 39.456898, -16.7232, -8.1437, 52.156597, -20.0679, -8.1437, 52.156597, -13.378601, -3.6573, 39.456898, -16.7232, -8.1437, 52.156597, -16.7232, -3.6573, 39.456898, -13.378601, -3.6573, 39.456898, -13.378601, -8.1437, 52.156597, -16.7232, -8.1437, 52.156597, -10.033899, -3.6573, 39.456898, -13.378601, -8.1437, 52.156597, -13.378601, -3.6573, 39.456898, -10.033899, -3.6573, 39.456898, -10.033899, -8.1437, 52.156597, -13.378601, -8.1437, 52.156597, -6.6893, -3.6573, 39.456898, -10.033899, -8.1437, 52.156597, -10.033899, -3.6573, 39.456898, -6.6893, -3.6573, 39.456898, -6.6893, -8.1437, 52.156597, -10.033899, -8.1437, 52.156597, -3.3446002, -3.6573, 39.456898, -6.6893, -8.1437, 52.156597, -6.6893, -3.6573, 39.456898, -3.3446002, -3.6573, 39.456898, -3.3446002, -8.1437, 52.156597, -6.6893, -8.1437, 52.156597, 0, -3.6573, 39.456898, -3.3446002, -8.1437, 52.156597, -3.3446002, -3.6573, 39.456898, 0, -3.6573, 39.456898, 0, -8.1437, 52.156597, -3.3446002, -8.1437, 52.156597, 3.3446, -3.6573, 39.456898, 0, -8.1437, 52.156597, 0, -3.6573, 39.456898, 3.3446, -3.6573, 39.456898, 3.3446, -8.1437, 52.156597, 0, -8.1437, 52.156597, 6.6893, -3.6573, 39.456898, 3.3446, -8.1437, 52.156597, 3.3446, -3.6573, 39.456898, 6.6893, -3.6573, 39.456898, 6.6893, -8.1437, 52.156597, 3.3446, -8.1437, 52.156597, 10.033899, -3.6573, 39.456898, 6.6893, -8.1437, 52.156597, 6.6893, -3.6573, 39.456898, 10.033899, -3.6573, 39.456898, 10.033899, -8.1437, 52.156597, 6.6893, -8.1437, 52.156597, 13.378599, -3.6573, 39.456898, 10.033899, -8.1437, 52.156597, 10.033899, -3.6573, 39.456898, 13.378599, -3.6573, 39.456898, 13.378599, -8.1437, 52.156597, 10.033899, -8.1437, 52.156597, 16.7232, -3.6573, 39.456898, 13.378599, -8.1437, 52.156597, 13.378599, -3.6573, 39.456898, 16.7232, -3.6573, 39.456898, 16.7232, -8.1437, 52.156597, 13.378599, -8.1437, 52.156597, 20.0679, -3.6573, 39.456898, 16.7232, -8.1437, 52.156597, 16.7232, -3.6573, 39.456898, 20.0679, -3.6573, 39.456898, 20.0679, -8.1437, 52.156597, 16.7232, -8.1437, 52.156597, 23.412498, -3.6573, 39.456898, 20.0679, -8.1437, 52.156597, 20.0679, -3.6573, 39.456898, 20.0679, -8.1437, 52.156597, 23.412498, -3.6573, 39.456898, 23.412498, -8.1437, 52.156597, 23.412498, -8.1437, 52.156597, 23.412498, -3.6573, 39.456898, 26.7582, -8.1437, 52.156597, 26.7582, -8.1437, 52.156597, 23.412498, -3.6573, 39.456898, 26.7582, -3.6573, 39.456898, -26.7572, -1.0756, 26.7572, -26.7572, -3.6573, 39.456898, -38.5239, -3.6573, 26.7572, -50.290497, -8.1437, 26.7572, -26.7572, -3.6573, 39.456898, -26.7572, -8.1437, 52.156597, -50.290497, -8.1437, 26.7572, -38.5239, -3.6573, 26.7572, -26.7572, -3.6573, 39.456898, -62.057194, -10.4337, 26.7572, -26.7572, -8.1437, 52.156597, -26.7572, -10.4337, 65.2334, -62.057194, -10.4337, 26.7572, -50.290497, -8.1437, 26.7572, -26.7572, -8.1437, 52.156597, 26.7582, -10.4337, 65.2334, 52.997097, -8.1437, 26.7572, 66.1166, -10.4337, 26.7572, 26.7582, -10.4337, 65.2334, 26.7582, -8.1437, 52.156597, 52.997097, -8.1437, 26.7572, 39.877598, -3.6573, 26.7572, 26.7582, -8.1437, 52.156597, 26.7582, -3.6573, 39.456898, 39.877598, -3.6573, 26.7572, 52.997097, -8.1437, 26.7572, 26.7582, -8.1437, 52.156597, 26.7582, -1.6452999, 26.7572, 39.877598, -3.6573, 26.7572, 26.7582, -3.6573, 39.456898, 26.7573, -1.7447, -30.111198, 39.877598, -3.6808999, -26.7572, 26.7573, -1.3222, -26.7572, 26.7572, -2.5274, -33.9013, 39.877598, -3.6808999, -26.7572, 26.7573, -1.7447, -30.111198, 39.877598, -3.6808999, -26.7572, 26.7572, -2.5274, -33.9013, 26.7572, -3.2101, -37.2077, 39.877598, -3.6808999, -26.7572, 26.7572, -3.2101, -37.2077, 26.7572, -3.9166, -40.6287, 39.877598, -3.6808999, -26.7572, 26.7572, -8.403, -54.500298, 52.997097, -8.1672, -26.7572, 39.877598, -3.6808999, -26.7572, 26.7572, -6.2504, -47.8447, 26.7572, -8.403, -54.500298, 26.7572, -8.403, -54.500298, 26.7572, -6.2504, -47.8447, 26.7572, -7.3550997, -51.2602, 26.7572, -6.2504, -47.8447, 39.877598, -3.6808999, -26.7572, 26.7572, -3.9166, -40.6287, 26.7572, -6.2504, -47.8447, 26.7572, -3.9166, -40.6287, 26.7572, -5.1514, -44.446598, 52.997097, -8.1672, -26.7572, 26.7572, -10.693, -68.371895, 66.1166, -10.4572, -26.7572, 52.997097, -8.1672, -26.7572, 26.7572, -9.876599, -63.426796, 26.7572, -10.693, -68.371895, 26.7572, -9.876599, -63.426796, 52.997097, -8.1672, -26.7572, 26.7572, -9.312699, -60.011097, 26.7572, -9.312699, -60.011097, 52.997097, -8.1672, -26.7572, 26.7572, -8.881, -57.3956, 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, -73.1232, 66.1166, -10.4572, -26.7572, 26.7572, -10.693, -70.330795, 66.1166, -10.4572, -26.7572, 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, 66.1166, -10.4572, -26.7572, 26.7572, -10.693, -79.5579, 26.7572, -10.693, -82.2434, 66.1166, -10.4572, -26.7572, 26.7572, -10.693, -82.2434, 79.236, -10.4572, -26.7572, -38.5239, -3.6573, -26.7572, -26.7572, -3.2445998, -30.4851, -26.7572, -3.0929, -26.7572, -38.5239, -3.6573, -26.7572, -26.7572, -3.3862, -33.9664, -26.7572, -3.2445998, -30.4851, -26.7572, -3.3862, -33.9664, -38.5239, -3.6573, -26.7572, -26.7572, -3.6573, -40.6287, -26.7572, -3.3862, -33.9664, -26.7572, -3.6573, -40.6287, -26.7572, -3.5244, -37.3623, -50.290497, -8.1437, -26.7572, -26.7572, -3.6573, -40.6287, -38.5239, -3.6573, -26.7572, -50.290497, -8.1437, -26.7572, -26.7572, -4.8125997, -44.200798, -26.7572, -3.6573, -40.6287, -26.7572, -4.8125997, -44.200798, -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, -26.7572, -4.8125997, -44.200798, -26.7572, -7.1011, -51.2768, -26.7572, -5.9270997, -47.646698, -62.057194, -10.4337, -26.7572, -26.7572, -8.1437, -54.500298, -50.290497, -8.1437, -26.7572, -26.7572, -8.1437, -54.500298, -62.057194, -10.4337, -26.7572, -26.7572, -9.7125, -64.0031, -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, -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.43339998, 6.6893, 26.7582, -0.43339998, 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, -1.0746, 23.412498, 10.033899, -0.501, 23.412498, 23.412498, -0.35529998, 10.033899, 23.412498, -1.0746, 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, -0.9264, 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, -0.94299996, 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, -0.94299996, 26.7572, 26.7582, -0.43339998, 10.033899, 26.7582, -1.0746, 13.378601, 23.412498, -1.0746, 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, 20.0676, 0.0019999999, 3.3446, 23.4123, -0.0001, -0.000100016594, 23.4123, -0.0001, 3.3446, 20.0676, 0.0019999999, 3.3446, 20.229, 0.030399999, -0.05219996, 23.4123, -0.0001, -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.0001, 3.3446, 26.7582, -0.43339998, 6.6893, 23.4123, -0.0001, 6.6893, 23.4123, -0.0001, 3.3446, 26.7575, -0.3464, 3.3446, 26.7582, -0.43339998, 6.6893, 23.4123, -0.0001, 6.6893, 26.7582, -0.43339998, 6.6893, 23.4123, -0.3554, 10.033899, 20.0676, 0.0019999999, 3.3446, 20.0676, -0.0001, 10.0338, 17.491, 0.0033999998, 7.408, 17.491, 0.0033999998, 7.408, 14.0257, 0.0442, 7.3251996, 20.0676, 0.0019999999, 3.3446, 20.0676, -0.0001, 10.0338, 23.4123, -0.0001, 6.6893, 23.4123, -0.3554, 10.033899, 20.0676, 0.0019999999, 3.3446, 23.4123, -0.0001, 3.3446, 23.4123, -0.0001, 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.0019999999, 3.3446, 14.261799, 0.9069, 1.1804999, 14.0257, 0.0442, 7.3251996, 14.261799, 0.9069, 1.1804999, 20.0676, 0.0019999999, 3.3446, 12.7055, 1.032, 4.0926, 14.261799, 0.9069, 1.1804999, 14.0257, 0.0442, 7.3251996, 26.7575, -0.3464, 3.3446, 23.4123, -0.0001, 3.3446, 23.4123, -0.0001, -0.000100016594, 20.0676, -0.0001, 10.0338, 20.0676, 0.0019999999, 3.3446, 23.4123, -0.0001, 6.6893, 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)
-
-[sub_resource type="PlaneMesh" id="PlaneMesh_kgxi8"]
-resource_local_to_scene = true
-size = Vector2(16.1, 12.3675)
-
-[sub_resource type="BoxShape3D" id="BoxShape3D_snvkn"]
-resource_local_to_scene = true
-size = Vector3(16.1, 4, 12.3675)
-
-[sub_resource type="BoxShape3D" id="BoxShape3D_hifdc"]
-resource_local_to_scene = true
-size = Vector3(16.1, 4.8, 12.3675)
+[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(10000, 10000)
+size = Vector2(16.1, 12.3675)
[sub_resource type="BoxShape3D" id="BoxShape3D_agj3o"]
resource_local_to_scene = true
-size = Vector3(17, 2, 80)
+size = Vector3(16.1, 4, 12.3675)
[sub_resource type="BoxShape3D" id="BoxShape3D_l6210"]
resource_local_to_scene = true
-size = Vector3(42, 2, 80)
+size = Vector3(16.1, 4.8, 12.3675)
-[sub_resource type="BoxShape3D" id="BoxShape3D_6upbg"]
+[sub_resource type="PlaneMesh" id="PlaneMesh_6upbg"]
resource_local_to_scene = true
-size = Vector3(48, 2, 18)
+size = Vector2(10000, 10000)
[sub_resource type="BoxShape3D" id="BoxShape3D_umo15"]
resource_local_to_scene = true
-size = Vector3(48, 2, 18)
+size = Vector3(10000, 2, 10000)
-[sub_resource type="BoxShape3D" id="BoxShape3D_e4lmg"]
+[sub_resource type="BoxShape3D" id="BoxShape3D_1fdkm"]
resource_local_to_scene = true
size = Vector3(17, 5, 80)
-[sub_resource type="BoxShape3D" id="BoxShape3D_eb3g5"]
+[sub_resource type="BoxShape3D" id="BoxShape3D_y2c0j"]
resource_local_to_scene = true
size = Vector3(42, 5, 80)
-[sub_resource type="BoxShape3D" id="BoxShape3D_wsqap"]
+[sub_resource type="BoxShape3D" id="BoxShape3D_uwwqy"]
resource_local_to_scene = true
size = Vector3(48, 5, 18)
-[sub_resource type="BoxShape3D" id="BoxShape3D_1fdkm"]
+[sub_resource type="BoxShape3D" id="BoxShape3D_hf28p"]
resource_local_to_scene = true
size = Vector3(48, 5, 18)
@@ -142,6 +129,7 @@ fish_catalog = ExtResource("6_pool")
pelican_buyer_profile = ExtResource("7_pelicans")
main_shop_buyer_profile = ExtResource("12_main_shop")
item_catalog = ExtResource("11_items")
+gatherable_catalog = ExtResource("58_gatherable_catalog")
[node name="TitleBackgroundLayer" type="CanvasLayer" parent="." unique_id=1690754702]
unique_name_in_owner = true
@@ -149,7 +137,7 @@ layer = -1
[node name="TitleBackground" type="ColorRect" parent="TitleBackgroundLayer" unique_id=1768697365]
unique_name_in_owner = true
-material = SubResource("ShaderMaterial_title_water_native")
+material = SubResource("ShaderMaterial_title_grass_native")
anchors_preset = 15
anchor_right = 1.0
anchor_bottom = 1.0
@@ -215,7 +203,7 @@ script = ExtResource("38_identity_backup")
unique_name_in_owner = true
script = ExtResource("17_network_session")
-[node name="DiscoveryClient" type="Node" parent="."]
+[node name="DiscoveryClient" type="Node" parent="." unique_id=401971194]
unique_name_in_owner = true
script = ExtResource("57_discovery")
@@ -275,6 +263,14 @@ script = ExtResource("22_network_sale")
unique_name_in_owner = true
script = ExtResource("23_network_shop")
+[node name="NetworkHomeService" type="Node" parent="."]
+unique_name_in_owner = true
+script = ExtResource("63_network_home")
+
+[node name="HomeDecorController" type="Node" parent="."]
+unique_name_in_owner = true
+script = ExtResource("64_home_decor_controller")
+
[node name="NetworkItemUseService" type="Node" parent="." unique_id=255154670]
unique_name_in_owner = true
script = ExtResource("24_network_item")
@@ -283,6 +279,10 @@ script = ExtResource("24_network_item")
unique_name_in_owner = true
script = ExtResource("45_fish_showcase")
+[node name="NetworkWorldSpawnService" type="Node" parent="."]
+unique_name_in_owner = true
+script = ExtResource("59_world_spawns")
+
[node name="NetworkSurfaceDrawingService" type="Node" parent="." unique_id=80122665]
unique_name_in_owner = true
script = ExtResource("46_surface_drawing")
@@ -304,71 +304,25 @@ 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, -2.4475808, 0, -1.8959346)
+transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0)
[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_umo15")
-
-[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_kgxi8")
-
-[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_snvkn")
-
-[node name="Shape" parent="TestWorld/Regions/StarterIslandRegion/WaterBodies/Pond/RecoveryRegion" index="0" unique_id=1718808476]
-shape = SubResource("BoxShape3D_hifdc")
-
-[node name="VisualWater" parent="TestWorld/Regions/StarterIslandRegion/WaterBodies/Ocean" index="0" unique_id=1176408937]
-mesh = SubResource("PlaneMesh_7ny4s")
-
-[node name="WestShape" parent="TestWorld/Regions/StarterIslandRegion/WaterBodies/Ocean/FishingRegions/OceanFishingRegion" index="0" unique_id=357406062]
-shape = SubResource("BoxShape3D_agj3o")
-
-[node name="EastShape" parent="TestWorld/Regions/StarterIslandRegion/WaterBodies/Ocean/FishingRegions/OceanFishingRegion" index="1" unique_id=772224738]
-shape = SubResource("BoxShape3D_l6210")
-
-[node name="NorthShape" parent="TestWorld/Regions/StarterIslandRegion/WaterBodies/Ocean/FishingRegions/OceanFishingRegion" index="2" unique_id=646147646]
-shape = SubResource("BoxShape3D_6upbg")
-
-[node name="SouthShape" parent="TestWorld/Regions/StarterIslandRegion/WaterBodies/Ocean/FishingRegions/OceanFishingRegion" index="3" unique_id=894083854]
-shape = SubResource("BoxShape3D_umo15")
-
-[node name="WestShape" parent="TestWorld/Regions/StarterIslandRegion/WaterBodies/Ocean/RecoveryRegions/OceanRecoveryRegion" index="0" unique_id=1183368645]
-shape = SubResource("BoxShape3D_e4lmg")
-
-[node name="EastShape" parent="TestWorld/Regions/StarterIslandRegion/WaterBodies/Ocean/RecoveryRegions/OceanRecoveryRegion" index="1" unique_id=1742419808]
-shape = SubResource("BoxShape3D_eb3g5")
-
-[node name="NorthShape" parent="TestWorld/Regions/StarterIslandRegion/WaterBodies/Ocean/RecoveryRegions/OceanRecoveryRegion" index="2" unique_id=1844920260]
-shape = SubResource("BoxShape3D_wsqap")
-
-[node name="SouthShape" parent="TestWorld/Regions/StarterIslandRegion/WaterBodies/Ocean/RecoveryRegions/OceanRecoveryRegion" index="3" unique_id=1316495770]
-shape = SubResource("BoxShape3D_1fdkm")
-
-[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, 45)
+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, -45)
+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, -48, 5, 0)
+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, 72, 5, 0)
+transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 96.4386, 5, 0)
[node name="Players" type="Node3D" parent="." unique_id=550630144]
@@ -378,6 +332,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="WorldGatherables" type="Node3D" parent="."]
+
+[node name="HomeWorldService" type="Node3D" parent="."]
+unique_name_in_owner = true
+script = ExtResource("62_home_world")
+
+[node name="GatheringController" type="Node3D" parent="."]
+unique_name_in_owner = true
+script = ExtResource("60_gathering_controller")
+
[node name="FishingSpot" parent="." unique_id=1461328864 instance=ExtResource("4_spot")]
unique_name_in_owner = true
transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 1.02, -18)
@@ -390,6 +354,10 @@ script = ExtResource("8_recovery")
unique_name_in_owner = true
script = ExtResource("9_save")
+[node name="PlayerHomeState" type="Node" parent="."]
+unique_name_in_owner = true
+script = ExtResource("61_home_state")
+
[node name="PlayerSettingsManager" type="Node" parent="." unique_id=906991991]
unique_name_in_owner = true
script = ExtResource("10_settings")
@@ -398,15 +366,21 @@ script = ExtResource("10_settings")
unique_name_in_owner = true
script = ExtResource("56_controller_mapping")
+[node name="KeyboardMouseMappingManager" type="Node" parent="."]
+unique_name_in_owner = true
+script = ExtResource("keyboard_mouse_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="."]
+[node name="NewGameMusic" type="AudioStreamPlayer" parent="."]
+unique_name_in_owner = true
+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]
@@ -415,7 +389,6 @@ 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")]
@@ -444,4 +417,3 @@ unique_name_in_owner = true
unique_name_in_owner = true
[editable path="TestWorld"]
-[editable path="TestWorld/Regions/StarterIslandRegion"]
diff --git a/main/runtime_performance_profile.gd b/main/runtime_performance_profile.gd
new file mode 100644
index 0000000..f18e63a
--- /dev/null
+++ b/main/runtime_performance_profile.gd
@@ -0,0 +1,64 @@
+class_name RuntimePerformanceProfile
+extends RefCounted
+
+const PROFILE_ENVIRONMENT_VARIABLE: String = (
+ "straywild_PERFORMANCE_PROFILE"
+)
+const LEGACY_LIGHT_ENVIRONMENT_VARIABLE: String = "straywild_LOW_END"
+const NETFISHING_PROFILE_ENVIRONMENT_VARIABLE := (
+ "NETFISHING_PERFORMANCE_PROFILE"
+)
+const NETFISHING_LIGHT_ENVIRONMENT_VARIABLE := "NETFISHING_LOW_END"
+const NORMAL_PROFILE: StringName = &"normal"
+const LIGHT_PROFILE: StringName = &"light"
+const NORMAL_WORLD_RENDER_SCALE: float = 1.0
+const LIGHT_WORLD_RENDER_SCALE: float = 0.375
+
+var _profile_name: StringName = NORMAL_PROFILE
+
+
+static func from_environment() -> RuntimePerformanceProfile:
+ var profile_name: String = OS.get_environment(
+ PROFILE_ENVIRONMENT_VARIABLE
+ )
+ if profile_name.is_empty():
+ profile_name = OS.get_environment(
+ NETFISHING_PROFILE_ENVIRONMENT_VARIABLE
+ )
+ return from_name(
+ StringName(profile_name),
+ (
+ OS.get_environment(LEGACY_LIGHT_ENVIRONMENT_VARIABLE) == "1"
+ or OS.get_environment(NETFISHING_LIGHT_ENVIRONMENT_VARIABLE) == "1"
+ ),
+ )
+
+
+static func from_name(
+ profile_name: StringName,
+ legacy_light_requested: bool = false,
+) -> RuntimePerformanceProfile:
+ var profile := RuntimePerformanceProfile.new()
+ if profile_name == LIGHT_PROFILE or (
+ profile_name.is_empty() and legacy_light_requested
+ ):
+ profile._profile_name = LIGHT_PROFILE
+ else:
+ profile._profile_name = NORMAL_PROFILE
+ return profile
+
+
+func get_profile_name() -> StringName:
+ return _profile_name
+
+
+func is_light() -> bool:
+ return _profile_name == LIGHT_PROFILE
+
+
+func get_world_render_scale() -> float:
+ return (
+ LIGHT_WORLD_RENDER_SCALE
+ if is_light()
+ else NORMAL_WORLD_RENDER_SCALE
+ )
diff --git a/main/runtime_performance_profile.gd.uid b/main/runtime_performance_profile.gd.uid
new file mode 100644
index 0000000..3f9e338
--- /dev/null
+++ b/main/runtime_performance_profile.gd.uid
@@ -0,0 +1 @@
+uid://8vm104r2264h
diff --git a/main/texture_sampling_policy.gd b/main/texture_sampling_policy.gd
new file mode 100644
index 0000000..35a92a0
--- /dev/null
+++ b/main/texture_sampling_policy.gd
@@ -0,0 +1,87 @@
+extends Node
+
+## Enforces straywild'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 those 3D resource owners once deferred so the newly
+ # assigned resources are covered too. CanvasItems keep their filter setting
+ # when their texture changes, so deferring every dynamic UI node only floods
+ # the message queue during large inventory and catalog refreshes.
+ if _needs_deferred_resource_check(node):
+ 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 _needs_deferred_resource_check(node: Node) -> bool:
+ return (
+ node is GeometryInstance3D
+ or node is MultiMeshInstance3D
+ or node is GPUParticles3D
+ or node is CPUParticles3D
+ )
+
+
+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/main/world_pixelation_postprocess.gd b/main/world_pixelation_postprocess.gd
index 303fa3e..06b6e1b 100644
--- a/main/world_pixelation_postprocess.gd
+++ b/main/world_pixelation_postprocess.gd
@@ -5,6 +5,7 @@ extends CanvasLayer
var _pixel_size: int = PlayerSettings.DEFAULT_WORLD_PIXEL_SIZE
var _gameplay_active: bool = false
+var _light_performance_profile: bool = false
func _ready() -> void:
@@ -26,6 +27,15 @@ func set_gameplay_active(active: bool) -> void:
_refresh_grid()
+func set_light_performance_profile(enabled: bool) -> void:
+ _light_performance_profile = enabled
+ _refresh_grid()
+
+
+func is_light_performance_profile() -> bool:
+ return _light_performance_profile
+
+
func get_grid_size() -> Vector2i:
return PlayerSettings.get_world_grid_size(
_pixel_size,
@@ -38,6 +48,7 @@ func _refresh_grid() -> void:
return
var effect_enabled: bool = (
_gameplay_active
+ and not _light_performance_profile
and _pixel_size != PlayerSettings.MIN_WORLD_PIXEL_SIZE
)
_screen_grid.visible = effect_enabled
diff --git a/network/discovery_client.gd b/network/discovery_client.gd
index 258d232..d8e602a 100644
--- a/network/discovery_client.gd
+++ b/network/discovery_client.gd
@@ -5,20 +5,58 @@ 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)
+signal friend_presence_updated(friends: Array[Dictionary])
+signal presence_sharing_changed(enabled: bool)
+signal social_status_changed(message: String, is_error: bool)
+signal friend_invite_received(
+ fingerprint: String,
+ display_name: String,
+ room: Dictionary,
+)
+signal friend_invite_finished(success: bool, message: String)
const BASE_URL_SETTING: String = "network/discovery/base_url"
-const BASE_URL_ENVIRONMENT: String = "NETFISHING_DISCOVERY_URL"
+const BASE_URL_ENVIRONMENT: String = "straywild_DISCOVERY_URL"
+const LEGACY_BASE_URL_ENVIRONMENT: String = "NETFISHING_DISCOVERY_URL"
const SETTINGS_PATH: String = "user://network_discovery.cfg"
-const DEFAULT_ROOM_NAME: String = "NETfishing Room"
+const DEFAULT_ROOM_NAME: String = "Player's Server"
+const LEGACY_DEFAULT_ROOM_NAME: String = "straywild Room"
+const LEGACY_DEDICATED_DEFAULT_ROOM_NAME: String = (
+ "straywild 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 TRAVERSAL_PACKET_PREFIX: String = "straywild_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
+const SOCIAL_POLL_INTERVAL_SECONDS: float = 5.0
+
+enum HostState {
+ UNAVAILABLE,
+ CLOSED,
+ PRIVATE,
+ OPENING_PORT,
+ REGISTERING,
+ VERIFYING,
+ PUBLIC,
+ ERROR,
+}
+
+enum PublicJoinState {
+ IDLE,
+ REQUESTING_ROUTE,
+ CONNECTING,
+ ERROR,
+}
enum HostRequestKind {
NONE,
@@ -28,11 +66,16 @@ enum HostRequestKind {
}
var _session: NetworkSession
+var _relationships: PlayerRelationshipStore
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
@@ -54,10 +97,25 @@ 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
+var _presence_sharing: bool = false
+var _social_timer: Timer
+var _presence_request: HTTPRequest
+var _presence_query_request: HTTPRequest
+var _invitation_poll_request: HTTPRequest
+var _invitation_send_request: HTTPRequest
+var _friend_presence: Dictionary[String, Dictionary] = {}
+var _published_presence_tokens: PackedStringArray = PackedStringArray()
+var _pending_presence_online: bool = false
+var _pending_presence_tokens: PackedStringArray = PackedStringArray()
+var _pending_invite_fingerprint: String = ""
+var _joined_public_room_id: String = ""
func _ready() -> void:
@@ -92,7 +150,14 @@ func _ready() -> void:
_heartbeat.wait_time = HEARTBEAT_INTERVAL_SECONDS
_heartbeat.one_shot = false
add_child(_heartbeat)
- _heartbeat.timeout.connect(_synchronize_host_lease)
+ _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
@@ -106,13 +171,48 @@ func _ready() -> void:
_join_probe_timer.one_shot = false
add_child(_join_probe_timer)
_join_probe_timer.timeout.connect(_send_pending_join_probe)
+
+ _presence_request = _make_social_request(
+ "FriendPresencePublish", _on_presence_request_completed
+ )
+ _presence_query_request = _make_social_request(
+ "FriendPresenceQuery", _on_presence_query_completed
+ )
+ _invitation_poll_request = _make_social_request(
+ "FriendInvitationPoll", _on_invitation_poll_completed
+ )
+ _invitation_send_request = _make_social_request(
+ "FriendInvitationSend", _on_invitation_send_completed
+ )
+ _social_timer = Timer.new()
+ _social_timer.name = "FriendPresencePoll"
+ _social_timer.wait_time = SOCIAL_POLL_INTERVAL_SECONDS
+ _social_timer.one_shot = false
+ add_child(_social_timer)
+ _social_timer.timeout.connect(_refresh_social_state)
_load_settings()
_base_url = _configured_base_url()
-func setup(session: NetworkSession) -> void:
+func setup(
+ session: NetworkSession,
+ relationships: PlayerRelationshipStore = null,
+) -> void:
_session = session
+ _relationships = relationships
+ 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 (
+ _relationships != null
+ and not _relationships.relationship_changed.is_connected(
+ _on_social_relationship_changed
+ )
+ ):
+ _relationships.relationship_changed.connect(
+ _on_social_relationship_changed
+ )
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):
@@ -121,11 +221,17 @@ func setup(session: NetworkSession) -> void:
_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)
+ if is_configured() and _relationships != null:
+ _social_timer.start()
+ call_deferred("_refresh_social_state")
func is_configured() -> bool:
@@ -136,6 +242,112 @@ func get_base_url() -> String:
return _base_url
+func is_presence_sharing() -> bool:
+ return _presence_sharing
+
+
+func set_presence_sharing(enabled: bool) -> bool:
+ if enabled and (not is_configured() or _relationships == null):
+ social_status_changed.emit(
+ "Friend presence is not configured in this build.", true
+ )
+ return false
+ if _presence_sharing == enabled:
+ return true
+ _presence_sharing = enabled
+ _save_settings()
+ presence_sharing_changed.emit(enabled)
+ _refresh_social_state()
+ return true
+
+
+func get_friend_presence() -> Array[Dictionary]:
+ var result: Array[Dictionary] = []
+ if _relationships == null:
+ return result
+ for friend: Dictionary in _relationships.get_friends():
+ var fingerprint := str(friend.get("fingerprint", ""))
+ var live: Dictionary = _friend_presence.get(fingerprint, {})
+ result.append({
+ "fingerprint": fingerprint,
+ "display_name": str(
+ friend.get("last_known_display_name", "Player")
+ ),
+ "online": bool(live.get("online", false)),
+ "room": (
+ live.get("room", {}).duplicate(true)
+ if typeof(live.get("room", {})) == TYPE_DICTIONARY
+ else {}
+ ),
+ })
+ result.sort_custom(func(a: Dictionary, b: Dictionary) -> bool:
+ if bool(a["online"]) != bool(b["online"]):
+ return bool(a["online"])
+ return str(a["display_name"]).naturalnocasecmp_to(
+ str(b["display_name"])
+ ) < 0
+ )
+ return result
+
+
+func request_friend_presence() -> bool:
+ if not is_configured() or _relationships == null:
+ friend_presence_updated.emit(get_friend_presence())
+ return false
+ _refresh_social_state()
+ return true
+
+
+func send_friend_invite(fingerprint: String) -> bool:
+ if (
+ _relationships == null
+ or not _relationships.is_friend(fingerprint)
+ or _relationships.is_blocked(fingerprint)
+ or not is_configured()
+ ):
+ friend_invite_finished.emit(
+ false, "This person needs to be online to do this."
+ )
+ return false
+ if (
+ _session == null
+ or not _session.is_host()
+ or not _host_verified
+ or _lease_room_id.is_empty()
+ ):
+ friend_invite_finished.emit(
+ false,
+ "List your open room in discovery before inviting friends.",
+ )
+ return false
+ if (
+ not _pending_invite_fingerprint.is_empty()
+ or _invitation_send_request.get_http_client_status()
+ != HTTPClient.STATUS_DISCONNECTED
+ ):
+ return false
+ var friend: Dictionary = _relationships.get_friend_record(fingerprint)
+ var inbox_token := str(friend.get("remote_invite_token", ""))
+ if inbox_token.is_empty():
+ friend_invite_finished.emit(false, "Friend invitation is unavailable.")
+ return false
+ var error := _post_social_json(
+ _invitation_send_request,
+ "/v1/invitations",
+ {
+ "inbox_token": inbox_token,
+ "room_id": _lease_room_id,
+ },
+ )
+ if error != OK:
+ friend_invite_finished.emit(
+ false, "Friend invitation could not be sent."
+ )
+ return false
+ _pending_invite_fingerprint = fingerprint
+ return true
+
+
func set_base_url_override(value: String) -> bool:
var normalized: String = value.strip_edges()
while normalized.ends_with("/"):
@@ -165,14 +377,32 @@ 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)
@@ -188,6 +418,7 @@ func set_discoverable(enabled: bool) -> bool:
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()
@@ -201,6 +432,11 @@ func set_discoverable(enabled: bool) -> bool:
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()
@@ -208,9 +444,11 @@ func set_discoverable(enabled: bool) -> bool:
_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
@@ -219,12 +457,33 @@ 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 or _session == null or not is_configured():
+ 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
@@ -240,16 +499,18 @@ func prepare_public_join(room: Dictionary) -> bool:
"",
)
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([])
+ rooms_updated.emit(_empty_rooms())
browse_status_changed.emit(
"Public room discovery is not configured in this build.", true
)
@@ -266,7 +527,7 @@ func request_rooms() -> bool:
]
var error: Error = _browse_request.request(url)
if error != OK:
- rooms_updated.emit([])
+ rooms_updated.emit(_empty_rooms())
browse_status_changed.emit("Could not request public rooms.", true)
return false
_browse_request_in_flight = true
@@ -284,8 +545,19 @@ func room_endpoint(room: Dictionary) -> String:
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 = OS.get_environment(
+ LEGACY_BASE_URL_ENVIRONMENT
+ ).strip_edges()
if value.is_empty():
value = str(ProjectSettings.get_setting(BASE_URL_SETTING, "")).strip_edges()
while value.ends_with("/"):
@@ -293,11 +565,18 @@ func _configured_base_url() -> String:
if not value.is_empty() and not (
value.begins_with("https://") or value.begins_with("http://")
):
- push_warning("Ignored invalid NETfishing discovery URL.")
+ push_warning("Ignored invalid straywild 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():
@@ -315,6 +594,8 @@ func _synchronize_host_lease() -> void:
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()))
@@ -343,6 +624,7 @@ func _start_host_request(
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
@@ -367,7 +649,10 @@ func _should_advertise() -> bool:
and is_configured()
and _session != null
and _session.is_open_host()
- and not _upnp_mapping_in_progress
+ and (
+ not _upnp_mapping_in_progress
+ or _upnp_operation_is_renewal
+ )
)
@@ -390,19 +675,36 @@ func _on_host_request_completed(
_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_status("Checking the public route…", 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,
+ )
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
@@ -416,13 +718,15 @@ func _on_host_request_completed(
_clear_lease()
_host_sync_queued = true
else:
+ _set_host_state(HostState.ERROR)
_set_host_status(_request_failure(response), true)
HostRequestKind.DELETE:
_clear_lease()
- if response_code not in [
+ if not transport_ok or response_code not in [
HTTPClient.RESPONSE_NO_CONTENT,
HTTPClient.RESPONSE_NOT_FOUND,
- ] and transport_ok:
+ ]:
+ _set_host_state(HostState.ERROR)
_set_host_status(_request_failure(response), true)
_:
pass
@@ -445,6 +749,7 @@ func _on_join_request_completed(
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
@@ -455,11 +760,13 @@ func _on_join_request_completed(
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)
@@ -483,6 +790,7 @@ func _on_traversal_timer_timeout() -> void:
])
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)
@@ -506,7 +814,7 @@ func _on_traversal_request_completed(
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()
+ var punch: PackedByteArray = "straywild_PUNCH_V1".to_utf8_buffer()
for _attempt: int in 3:
_session.send_traversal_packet(address, port, punch)
@@ -564,11 +872,13 @@ func _send_traversal_probe(payload: Dictionary) -> void:
_session.send_traversal_packet(_traversal_host, _traversal_port, packet)
-func _begin_upnp_mapping(port: int) -> void:
+func _begin_upnp_mapping(port: int, is_renewal: bool = false) -> void:
if _upnp_mapping_in_progress or port < 1:
- _synchronize_host_lease()
+ 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)
@@ -576,6 +886,9 @@ func _begin_upnp_mapping(port: int) -> void:
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()
@@ -587,13 +900,13 @@ func _configure_upnp_mapping(port: int) -> Dictionary:
var mapping_result: int = upnp.add_port_mapping(
port,
port,
- "NETfishing",
+ "straywild",
"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
+ port, port, "straywild", "UDP", 0
)
return {
"success": mapping_result == UPNP.UPNP_RESULT_SUCCESS,
@@ -605,21 +918,54 @@ func _configure_upnp_mapping(port: int) -> Dictionary:
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")
@@ -636,12 +982,12 @@ func _on_browse_request_completed(
_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([])
+ rooms_updated.emit(_empty_rooms())
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([])
+ rooms_updated.emit(_empty_rooms())
browse_status_changed.emit("Discovery returned an invalid room list.", true)
return
var rooms: Array[Dictionary] = []
@@ -696,6 +1042,309 @@ func _valid_json_integer(value: Variant, minimum: int, maximum: int) -> bool:
)
+func _make_social_request(name: String, callback: Callable) -> HTTPRequest:
+ var request := HTTPRequest.new()
+ request.name = name
+ request.timeout = REQUEST_TIMEOUT_SECONDS
+ add_child(request)
+ request.request_completed.connect(callback)
+ return request
+
+
+func _social_payload(fields: Dictionary = {}) -> Dictionary:
+ var payload: Dictionary = fields.duplicate(true)
+ payload["game_version"] = NetworkProtocol.game_version()
+ payload["protocol_version"] = NetworkProtocol.PROTOCOL_VERSION
+ return payload
+
+
+func _post_social_json(
+ request: HTTPRequest,
+ path: String,
+ fields: Dictionary,
+) -> Error:
+ return request.request(
+ _base_url + path,
+ PackedStringArray(["Content-Type: application/json"]),
+ HTTPClient.METHOD_POST,
+ JSON.stringify(_social_payload(fields)),
+ )
+
+
+func _refresh_social_state() -> void:
+ if not is_configured() or _relationships == null:
+ return
+ _publish_friend_presence()
+ _query_friend_presence()
+ _poll_friend_invitations()
+
+
+func _publish_friend_presence() -> void:
+ if (
+ _presence_request.get_http_client_status()
+ != HTTPClient.STATUS_DISCONNECTED
+ ):
+ return
+ var current_tokens := _friend_social_values(
+ "local_presence_write_token"
+ )
+ var online := _presence_sharing and not current_tokens.is_empty()
+ var publish_online := online
+ var tokens := PackedStringArray()
+ if online:
+ # Revoke removed or blocked friendships before refreshing the remaining
+ # capabilities. This makes status disappear on the next request instead of
+ # waiting for the server's short presence TTL.
+ tokens = _presence_tokens_not_in(
+ _published_presence_tokens, current_tokens
+ )
+ if not tokens.is_empty():
+ publish_online = false
+ else:
+ tokens = current_tokens
+ else:
+ tokens = _published_presence_tokens
+ if tokens.is_empty():
+ return
+ var error := _post_social_json(
+ _presence_request,
+ "/v1/presence",
+ {
+ "display_name": _session.get_local_display_name(),
+ "room_id": _current_presence_room_id(),
+ "online": publish_online,
+ "write_tokens": Array(tokens),
+ },
+ )
+ if error == OK:
+ _pending_presence_online = publish_online
+ _pending_presence_tokens = tokens.duplicate()
+ else:
+ social_status_changed.emit("Could not update friend presence.", true)
+
+
+func _query_friend_presence() -> void:
+ if (
+ _presence_query_request.get_http_client_status()
+ != HTTPClient.STATUS_DISCONNECTED
+ ):
+ return
+ var channels := _friend_social_values("remote_presence_channel")
+ if channels.is_empty():
+ var had_presence := not _friend_presence.is_empty()
+ _friend_presence.clear()
+ if had_presence:
+ friend_presence_updated.emit(get_friend_presence())
+ return
+ var error := _post_social_json(
+ _presence_query_request,
+ "/v1/presence/query",
+ {"channels": Array(channels)},
+ )
+ if error != OK:
+ social_status_changed.emit("Could not refresh friend status.", true)
+
+
+func _poll_friend_invitations() -> void:
+ if (
+ _invitation_poll_request.get_http_client_status()
+ != HTTPClient.STATUS_DISCONNECTED
+ ):
+ return
+ var tokens := _friend_social_values("local_invite_token")
+ if tokens.is_empty():
+ return
+ var error := _post_social_json(
+ _invitation_poll_request,
+ "/v1/invitations/poll",
+ {"inbox_tokens": Array(tokens)},
+ )
+ if error != OK:
+ social_status_changed.emit("Could not check friend invitations.", true)
+
+
+func _friend_social_values(key: String) -> PackedStringArray:
+ var result := PackedStringArray()
+ if _relationships == null:
+ return result
+ for friend: Dictionary in _relationships.get_friends():
+ var value := str(friend.get(key, ""))
+ if not value.is_empty() and value not in result:
+ result.append(value)
+ return result
+
+
+func _current_presence_room_id() -> String:
+ if _session == null:
+ return ""
+ if _session.is_host() and _host_verified:
+ return _lease_room_id
+ if _session.is_joined_client():
+ return _joined_public_room_id
+ return ""
+
+
+func _on_presence_request_completed(
+ result: int,
+ response_code: int,
+ _headers: PackedStringArray,
+ body: PackedByteArray,
+) -> void:
+ var published_online := _pending_presence_online
+ var request_tokens := _pending_presence_tokens.duplicate()
+ _pending_presence_online = false
+ _pending_presence_tokens = PackedStringArray()
+ var response := _parse_response_dictionary(body)
+ if result != HTTPRequest.RESULT_SUCCESS or response_code != HTTPClient.RESPONSE_OK:
+ social_status_changed.emit(_request_failure(response), true)
+ return
+ if published_online:
+ for token: String in request_tokens:
+ if token not in _published_presence_tokens:
+ _published_presence_tokens.append(token)
+ else:
+ for token: String in request_tokens:
+ var index := _published_presence_tokens.find(token)
+ if index >= 0:
+ _published_presence_tokens.remove_at(index)
+ if _presence_publish_needs_follow_up():
+ call_deferred("_publish_friend_presence")
+
+
+func _presence_publish_needs_follow_up() -> bool:
+ var desired := (
+ _friend_social_values("local_presence_write_token")
+ if _presence_sharing
+ else PackedStringArray()
+ )
+ return (
+ not _presence_tokens_not_in(
+ _published_presence_tokens, desired
+ ).is_empty()
+ or not _presence_tokens_not_in(
+ desired, _published_presence_tokens
+ ).is_empty()
+ )
+
+
+func _presence_tokens_not_in(
+ first: PackedStringArray,
+ second: PackedStringArray,
+) -> PackedStringArray:
+ var result := PackedStringArray()
+ for token: String in first:
+ if token not in second:
+ result.append(token)
+ return result
+
+
+func _on_presence_query_completed(
+ result: int,
+ response_code: int,
+ _headers: PackedStringArray,
+ body: PackedByteArray,
+) -> void:
+ var response := _parse_response_dictionary(body)
+ if result != HTTPRequest.RESULT_SUCCESS or response_code != HTTPClient.RESPONSE_OK:
+ social_status_changed.emit(_request_failure(response), true)
+ return
+ var by_channel: Dictionary[String, String] = {}
+ for friend: Dictionary in _relationships.get_friends():
+ by_channel[str(friend.get("remote_presence_channel", ""))] = str(
+ friend.get("fingerprint", "")
+ )
+ var next_presence: Dictionary[String, Dictionary] = {}
+ var raw_presence: Variant = response.get("presence", [])
+ if typeof(raw_presence) == TYPE_ARRAY:
+ for value: Variant in raw_presence:
+ if typeof(value) != TYPE_DICTIONARY:
+ continue
+ var presence: Dictionary = value
+ var fingerprint := str(
+ by_channel.get(str(presence.get("channel", "")), "")
+ )
+ if fingerprint.is_empty() or _relationships.is_blocked(fingerprint):
+ continue
+ var room: Dictionary = {}
+ var raw_room: Variant = presence.get("room", {})
+ if typeof(raw_room) == TYPE_DICTIONARY and _valid_public_room(raw_room):
+ room = (raw_room as Dictionary).duplicate(true)
+ next_presence[fingerprint] = {
+ "online": true,
+ "room": room,
+ }
+ if next_presence == _friend_presence:
+ return
+ _friend_presence = next_presence
+ friend_presence_updated.emit(get_friend_presence())
+
+
+func _on_invitation_poll_completed(
+ result: int,
+ response_code: int,
+ _headers: PackedStringArray,
+ body: PackedByteArray,
+) -> void:
+ var response := _parse_response_dictionary(body)
+ if result != HTTPRequest.RESULT_SUCCESS or response_code != HTTPClient.RESPONSE_OK:
+ return
+ var by_inbox: Dictionary[String, Dictionary] = {}
+ for friend: Dictionary in _relationships.get_friends():
+ var token := str(friend.get("local_invite_token", ""))
+ if not token.is_empty():
+ by_inbox[PlayerRelationshipStore.invite_inbox_id(token)] = friend
+ var invitations: Variant = response.get("invitations", [])
+ if typeof(invitations) != TYPE_ARRAY:
+ return
+ for value: Variant in invitations:
+ if typeof(value) != TYPE_DICTIONARY:
+ continue
+ var invitation: Dictionary = value
+ var friend: Dictionary = by_inbox.get(
+ str(invitation.get("inbox_id", "")), {}
+ )
+ var raw_room: Variant = invitation.get("room", {})
+ if friend.is_empty() or typeof(raw_room) != TYPE_DICTIONARY:
+ continue
+ var room := raw_room as Dictionary
+ if not _valid_public_room(room):
+ continue
+ var fingerprint := str(friend.get("fingerprint", ""))
+ if _relationships.is_blocked(fingerprint):
+ continue
+ friend_invite_received.emit(
+ fingerprint,
+ str(friend.get("last_known_display_name", "Player")),
+ room.duplicate(true),
+ )
+
+
+func _on_invitation_send_completed(
+ result: int,
+ response_code: int,
+ _headers: PackedStringArray,
+ body: PackedByteArray,
+) -> void:
+ _pending_invite_fingerprint = ""
+ var response := _parse_response_dictionary(body)
+ var success := (
+ result == HTTPRequest.RESULT_SUCCESS
+ and response_code == HTTPClient.RESPONSE_CREATED
+ )
+ friend_invite_finished.emit(
+ success,
+ "Invitation sent." if success else _request_failure(response),
+ )
+
+
+func _on_social_relationship_changed(_fingerprint: String) -> void:
+ for fingerprint: String in _friend_presence.keys():
+ if not _relationships.is_friend(fingerprint):
+ _friend_presence.erase(fingerprint)
+ friend_presence_updated.emit(get_friend_presence())
+ call_deferred("_refresh_social_state")
+
+
func _parse_response_dictionary(body: PackedByteArray) -> Dictionary:
if body.is_empty():
return {}
@@ -706,22 +1355,88 @@ func _parse_response_dictionary(body: PackedByteArray) -> Dictionary:
func _request_failure(response: Dictionary) -> String:
var error: Variant = response.get("error", {})
if typeof(error) == TYPE_DICTIONARY:
- var message: String = str((error as Dictionary).get("message", "")).strip_edges()
+ 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 straywild 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 straywild %s. This room "
+ + "will not be listed until discovery is updated for %s."
+ ) % [required_version, local_version]
+ _:
+ return (
+ "Public discovery requires straywild %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.JOINED_CLIENT:
+ if not _pending_join_room_id.is_empty():
+ _joined_public_room_id = _pending_join_room_id
+ elif state in [
+ NetworkSession.State.PRIVATE_HOST,
+ NetworkSession.State.OPEN_HOST,
+ NetworkSession.State.SERVER_LOST,
+ NetworkSession.State.CONNECTION_FAILED,
+ ]:
+ _joined_public_room_id = ""
+ elif (
+ state == NetworkSession.State.INACTIVE
+ and not _preserve_pending_join_on_inactive
+ ):
+ _joined_public_room_id = ""
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.CONNECTION_FAILED,
NetworkSession.State.SERVER_LOST,
- NetworkSession.State.INACTIVE,
]:
+ _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:
@@ -729,6 +1444,7 @@ func _on_session_state_changed(state: NetworkSession.State) -> void:
_heartbeat.start()
_synchronize_host_lease()
else:
+ _set_host_state(HostState.PRIVATE)
_set_host_status("Room is open but unlisted.", false)
return
if state in [
@@ -742,9 +1458,15 @@ func _on_session_state_changed(state: NetworkSession.State) -> void:
_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)
+ call_deferred("_refresh_social_state")
func _on_host_openness_changed(is_open: bool) -> void:
@@ -773,6 +1495,7 @@ func _clear_pending_join() -> void:
func _exit_tree() -> void:
+ _upnp_renew_timer.stop()
if _upnp_thread != null:
var result: Variant = _upnp_thread.wait_to_finish()
_upnp_thread = null
@@ -790,6 +1513,24 @@ func _set_host_status(message: String, is_error: bool) -> void:
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 _empty_rooms() -> Array[Dictionary]:
+ return []
+
+
func _sanitize_room_name(value: String) -> String:
var cleaned: String = value.strip_edges().replace("\n", " ").replace("\r", " ")
cleaned = cleaned.replace("\t", " ")
@@ -798,6 +1539,16 @@ func _sanitize_room_name(value: String) -> String:
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:
@@ -806,11 +1557,29 @@ func _load_settings() -> void:
)
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,
+ ],
+ ))
+ _presence_sharing = bool(config.get_value(
+ "social", "share_presence", false
+ ))
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
+ )
+ config.set_value("social", "share_presence", _presence_sharing)
var error: Error = config.save(SETTINGS_PATH)
if error != OK:
- push_warning("Could not save the local NETfishing room name.")
+ push_warning("Could not save local straywild discovery settings.")
diff --git a/network/host_ban_store.gd b/network/host_ban_store.gd
index f8c4bf9..1251846 100644
--- a/network/host_ban_store.gd
+++ b/network/host_ban_store.gd
@@ -17,6 +17,10 @@ var _data_root: PlayerDataRoot
func configure_storage(path: String, data_root: PlayerDataRoot) -> void:
_store_path = path
_data_root = data_root
+ _namespaces.clear()
+ _loaded = false
+ _write_blocked = false
+ _expected_hash = ""
func is_banned(host_fingerprint: String, target_fingerprint: String) -> bool:
diff --git a/network/identity_backup_service.gd b/network/identity_backup_service.gd
index ff7396c..c383e7b 100644
--- a/network/identity_backup_service.gd
+++ b/network/identity_backup_service.gd
@@ -3,7 +3,8 @@ extends Node
signal operation_finished(success: bool, message: String)
-const MAGIC := "NETFISHING_IDENTITY_BACKUP"
+const MAGIC := "straywild_IDENTITY_BACKUP"
+const LEGACY_MAGIC := "NETFISHING_IDENTITY_BACKUP"
const FORMAT_VERSION := 1
const MAX_BACKUP_BYTES := 256 * 1024
const MIN_PASSPHRASE_LENGTH := 12
@@ -110,8 +111,9 @@ func inspect_backup(
if json.parse(text) != OK or typeof(json.data) != TYPE_DICTIONARY:
return {"ok": false}
var data: Dictionary = json.data
+ var legacy_backup: bool = data.get("magic") == LEGACY_MAGIC
if (
- data.get("magic") != MAGIC
+ data.get("magic") not in [MAGIC, LEGACY_MAGIC]
or data.get("format_version") != FORMAT_VERSION
or data.get("identity_type") != expected_type
or data.get("algorithm") != NetworkIdentityCrypto.ALGORITHM
@@ -134,12 +136,23 @@ func inspect_backup(
var signature: PackedByteArray = Marshalls.base64_to_raw(
str(data.get("self_signature", ""))
)
- if not NetworkIdentityCrypto.verify_fields(
- NetworkIdentityCrypto.load_public_key(public_pem),
- "identity_backup_self_test",
- [expected_type, fingerprint],
- signature,
- ):
+ var public_key: CryptoKey = NetworkIdentityCrypto.load_public_key(public_pem)
+ var backup_signature_valid: bool = (
+ NetworkIdentityCrypto.verify_legacy_fields(
+ public_key,
+ "identity_backup_self_test",
+ [expected_type, fingerprint],
+ signature,
+ )
+ if legacy_backup
+ else NetworkIdentityCrypto.verify_fields(
+ public_key,
+ "identity_backup_self_test",
+ [expected_type, fingerprint],
+ signature,
+ )
+ )
+ if not backup_signature_valid:
return {"ok": false}
var fresh: PackedByteArray = NetworkIdentityCrypto.sign_fields(
key, "identity_import_self_test", [fingerprint]
@@ -189,7 +202,7 @@ func import_backup(
)
_finish(
bool(result.get("ok", false)),
- "Identity imported. Restart NETfishing before multiplayer."
+ "Identity imported. Restart straywild before multiplayer."
if bool(result.get("ok", false))
else "Could not install this identity backup.",
)
diff --git a/network/known_player_store.gd b/network/known_player_store.gd
index 0c6b859..1118656 100644
--- a/network/known_player_store.gd
+++ b/network/known_player_store.gd
@@ -15,6 +15,10 @@ var _data_root: PlayerDataRoot
func configure_storage(path: String, data_root: PlayerDataRoot) -> void:
_store_path = path
_data_root = data_root
+ _records.clear()
+ _loaded = false
+ _write_blocked = false
+ _expected_hash = ""
func observe(fingerprint: String, display_name: String) -> String:
diff --git a/network/network_chat_protocol.gd b/network/network_chat_protocol.gd
index ef7294d..2e91432 100644
--- a/network/network_chat_protocol.gd
+++ b/network/network_chat_protocol.gd
@@ -9,6 +9,13 @@ const MAX_HISTORY: int = 100
const LATE_JOIN_HISTORY: int = 50
const MAX_ID_LENGTH: int = 96
+static var _invisible_edge_pattern: RegEx = RegEx.create_from_string(
+ "^[\\s\\p{Z}\\p{Cf}\\x{2800}]+|[\\s\\p{Z}\\p{Cf}\\x{2800}]+$"
+)
+static var _visible_content_pattern: RegEx = RegEx.create_from_string(
+ "[^\\s\\p{Z}\\p{Cf}\\x{2800}]"
+)
+
enum Kind { PLAYER, SYSTEM }
@@ -16,8 +23,13 @@ static func sanitize_body(value: Variant) -> String:
if typeof(value) != TYPE_STRING:
return ""
var result: String = str(value).replace("\r", " ").replace("\n", " ")
- result = result.replace("\t", " ").strip_edges()
- if result.is_empty() or result.length() > MAX_VISIBLE_CHARACTERS:
+ result = result.replace("\t", " ")
+ result = _invisible_edge_pattern.sub(result, "", true)
+ if (
+ result.is_empty()
+ or _visible_content_pattern.search(result) == null
+ or result.length() > MAX_VISIBLE_CHARACTERS
+ ):
return ""
if result.to_utf8_buffer().size() > MAX_UTF8_BYTES:
return ""
diff --git a/network/network_chat_service.gd b/network/network_chat_service.gd
index 368c529..55ba768 100644
--- a/network/network_chat_service.gd
+++ b/network/network_chat_service.gd
@@ -4,7 +4,11 @@ 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 CALL_COOLDOWN_MILLISECONDS: int = 90
+const MAX_PENDING_MESSAGES_PER_PEER: int = 8
+const DEDICATED_CHAT_WARNING: String = (
+ "Privacy notice: This dedicated server records and retains chat messages."
+)
const CALL_PITCH_VARIANTS: Array[float] = [
0.96,
1.03,
@@ -37,7 +41,34 @@ var _last_call_msec: Dictionary[int, int] = {}
var _call_variant_indices: Dictionary[int, int] = {}
var _sequence: int = 0
var _peer_names: Dictionary[int, String] = {}
+var _pending_peer_messages: Dictionary[int, Array] = {}
var _relationships: PlayerRelationshipStore
+var _dedicated_history_enabled: bool = false
+var _chat_log_path: String = ""
+
+
+func configure_dedicated_history(enabled: bool, log_path: String) -> bool:
+ _dedicated_history_enabled = false
+ _chat_log_path = ""
+ if not enabled:
+ return true
+ var normalized_path: String = log_path.strip_edges()
+ if normalized_path.is_empty() or not normalized_path.is_absolute_path():
+ return false
+ var directory: String = normalized_path.get_base_dir()
+ if DirAccess.make_dir_recursive_absolute(directory) != OK:
+ return false
+ var file: FileAccess
+ if FileAccess.file_exists(normalized_path):
+ file = FileAccess.open(normalized_path, FileAccess.READ_WRITE)
+ else:
+ file = FileAccess.open(normalized_path, FileAccess.WRITE)
+ if file == null:
+ return false
+ file.close()
+ _dedicated_history_enabled = true
+ _chat_log_path = normalized_path
+ return true
func setup(session: NetworkSession) -> void:
@@ -66,7 +97,11 @@ func is_sender_filtered(fingerprint: String) -> bool:
)
-func send_local_message(body: String) -> bool:
+func send_local_message(
+ body: String,
+ voice_id: String = VoiceProfilesType.DEFAULT_ID,
+ sample_set_id: String = VoiceProfilesType.DEFAULT_SAMPLE_SET_ID,
+) -> bool:
if (
_session == null
or not _session.is_gameplay_session_active()
@@ -88,6 +123,10 @@ func send_local_message(body: String) -> bool:
"session_id": _session.get_session_id(),
"body": clean,
"sender_fingerprint": _session.get_local_identity_fingerprint(),
+ "voice_id": VoiceProfilesType.sanitized_id(voice_id),
+ "sample_set_id": VoiceProfilesType.sanitized_sample_set_id(
+ sample_set_id
+ ),
}
request["sender_signature"] = _session.sign_local_action(
"chat_send", NetworkChatProtocol.signature_fields(request)
@@ -280,7 +319,8 @@ func _handle_request(peer_id: int, data: Dictionary) -> void:
var request_id: String = data["request_id"]
var ledger: Dictionary = _request_ledgers.get(peer_id, {})
if ledger.has(request_id):
- _send_message(peer_id, ledger[request_id])
+ if typeof(ledger[request_id]) == TYPE_DICTIONARY:
+ _send_message(peer_id, ledger[request_id])
return
if not _consume_rate(peer_id):
_send_rejection(peer_id, "Slow down.")
@@ -289,12 +329,22 @@ func _handle_request(peer_id: int, data: Dictionary) -> void:
NetworkChatProtocol.Kind.PLAYER,
peer_id,
record.display_name,
- NetworkChatProtocol.sanitize_body(data["body"])
+ NetworkChatProtocol.sanitize_body(data["body"]),
+ VoiceProfilesType.sanitized_id(str(data.get(
+ "voice_id", VoiceProfilesType.DEFAULT_ID
+ ))),
+ VoiceProfilesType.sanitized_sample_set_id(str(data.get(
+ "sample_set_id", VoiceProfilesType.DEFAULT_SAMPLE_SET_ID
+ ))),
)
message["request_id"] = request_id
message["sender_fingerprint"] = data["sender_fingerprint"]
message["sender_signature"] = data["sender_signature"]
- ledger[request_id] = message.duplicate(true)
+ ledger[request_id] = (
+ message.duplicate(true)
+ if _should_store_host_history()
+ else true
+ )
while ledger.size() > 64:
ledger.erase(ledger.keys().front())
_request_ledgers[peer_id] = ledger
@@ -306,6 +356,8 @@ func _make_message(
peer_id: int,
display_name: String,
body: String,
+ voice_id: String = VoiceProfilesType.DEFAULT_ID,
+ sample_set_id: String = VoiceProfilesType.DEFAULT_SAMPLE_SET_ID,
) -> Dictionary:
_sequence += 1
var message := {
@@ -318,6 +370,10 @@ func _make_message(
"sender_display_name": display_name.left(24),
"body": body,
"sender_fingerprint": _session.get_host_identity_fingerprint(),
+ "voice_id": VoiceProfilesType.sanitized_id(voice_id),
+ "sample_set_id": VoiceProfilesType.sanitized_sample_set_id(
+ sample_set_id
+ ),
}
if kind == NetworkChatProtocol.Kind.SYSTEM:
message["sender_signature"] = _session.sign_host_action(
@@ -343,7 +399,10 @@ func receive_chat_message(data: Dictionary) -> void:
_apply_message(data)
-func _apply_message(data: Dictionary) -> void:
+func _apply_message(
+ data: Dictionary,
+ emit_live_signals: bool = true,
+) -> void:
if (
not NetworkChatProtocol.validate_message(data)
or str(data["session_id"]) != _session.get_session_id()
@@ -361,9 +420,11 @@ func _apply_message(data: Dictionary) -> void:
else:
var sender_id := int(data["sender_peer_id"])
var record := _session.get_peer_record(sender_id)
+ if record == null:
+ _queue_pending_peer_message(sender_id, data, emit_live_signals)
+ return
valid_signature = (
- record != null
- and record.identity_fingerprint == str(data["sender_fingerprint"])
+ record.identity_fingerprint == str(data["sender_fingerprint"])
and _session.verify_peer_action(
sender_id,
"chat_send",
@@ -381,10 +442,13 @@ func _apply_message(data: Dictionary) -> void:
kind == NetworkChatProtocol.Kind.PLAYER
and is_sender_filtered(str(data.get("sender_fingerprint", "")))
)
- _history.append(stored_message)
- while _history.size() > NetworkChatProtocol.MAX_HISTORY:
- _history.pop_front()
- if _message_is_visible(stored_message):
+ if _should_store_local_history():
+ _history.append(stored_message)
+ while _history.size() > NetworkChatProtocol.MAX_HISTORY:
+ _history.pop_front()
+ if _should_log_message(stored_message):
+ _append_chat_log(stored_message)
+ if emit_live_signals and _message_is_visible(stored_message):
message_received.emit(stored_message.duplicate(true))
if (
kind == NetworkChatProtocol.Kind.PLAYER
@@ -406,12 +470,70 @@ func receive_chat_rejection(message: String) -> void:
send_rejected.emit(message.left(80))
+func _queue_pending_peer_message(
+ peer_id: int,
+ message: Dictionary,
+ emit_live_signals: bool,
+) -> void:
+ if peer_id <= 0:
+ return
+ var pending: Array = _pending_peer_messages.get(peer_id, [])
+ var message_id := str(message.get("message_id", ""))
+ for value: Variant in pending:
+ if (
+ typeof(value) == TYPE_DICTIONARY
+ and str(value.get("message", {}).get("message_id", "")) == message_id
+ ):
+ return
+ pending.append({
+ "message": message.duplicate(true),
+ "emit_live_signals": emit_live_signals,
+ })
+ while pending.size() > MAX_PENDING_MESSAGES_PER_PEER:
+ pending.pop_front()
+ _pending_peer_messages[peer_id] = pending
+
+
+func _flush_pending_peer_messages(peer_id: int) -> void:
+ var pending: Array = _pending_peer_messages.get(peer_id, [])
+ _pending_peer_messages.erase(peer_id)
+ for value: Variant in pending:
+ if typeof(value) != TYPE_DICTIONARY:
+ continue
+ var queued: Dictionary = value
+ if typeof(queued.get("message")) != TYPE_DICTIONARY:
+ continue
+ _apply_message(
+ queued["message"],
+ bool(queued.get("emit_live_signals", true)),
+ )
+
+
func _on_peer_authenticated(peer_id: int, display_name: String) -> void:
+ _flush_pending_peer_messages(peer_id)
if not _session.is_host():
return
_peer_names[peer_id] = display_name
- var start := maxi(0, _history.size() - NetworkChatProtocol.LATE_JOIN_HISTORY)
- receive_chat_history.rpc_id(peer_id, _history.slice(start))
+ var history: Array[Dictionary] = []
+ if _dedicated_history_enabled and _session.is_dedicated_host():
+ var start := maxi(
+ 0,
+ _history.size() - NetworkChatProtocol.LATE_JOIN_HISTORY,
+ )
+ history = _history.slice(start)
+ # An explicit empty replacement is intentional for player-hosted rooms and
+ # dedicated servers without logging. It destroys any stale client scrollback
+ # instead of replaying another player's live-session chat on reconnect.
+ receive_chat_history.rpc_id(peer_id, history)
+ if _dedicated_history_enabled and _session.is_dedicated_host():
+ # Deliver the disclosure as a live system message after scrollback. This
+ # raises the normal unread indicator without creating speech or audio.
+ _send_message(peer_id, _make_message(
+ NetworkChatProtocol.Kind.SYSTEM,
+ 0,
+ "",
+ DEDICATED_CHAT_WARNING,
+ ))
_broadcast(_make_message(
NetworkChatProtocol.Kind.SYSTEM, 0, "", "%s joined." % display_name
))
@@ -422,6 +544,7 @@ func _on_peer_removed(peer_id: int) -> void:
_rate_times.erase(peer_id)
_last_call_msec.erase(peer_id)
_call_variant_indices.erase(peer_id)
+ _pending_peer_messages.erase(peer_id)
if not _session.is_host():
return
var display_name: String = _peer_names.get(peer_id, "Player")
@@ -439,7 +562,10 @@ func receive_chat_history(values: Array) -> void:
_seen_messages.clear()
for value: Variant in values:
if NetworkChatProtocol.validate_message(value):
- _apply_message(value)
+ # Late-join history belongs in the scrollback, but it is not a live
+ # event. In particular, do not recreate speech bubbles or replay
+ # animalese for every stored player message when joining a room.
+ _apply_message(value, false)
history_replaced.emit(get_history())
@@ -468,6 +594,44 @@ func _consume_rate(peer_id: int) -> bool:
return true
+func _should_store_host_history() -> bool:
+ return (
+ _session == null
+ or not _session.is_dedicated_host()
+ or _dedicated_history_enabled
+ )
+
+
+func _should_store_local_history() -> bool:
+ return _should_store_host_history()
+
+
+func _should_log_message(message: Dictionary) -> bool:
+ return (
+ _dedicated_history_enabled
+ and not _chat_log_path.is_empty()
+ and _session != null
+ and _session.is_dedicated_host()
+ and int(message.get("kind", -1)) == NetworkChatProtocol.Kind.PLAYER
+ )
+
+
+func _append_chat_log(message: Dictionary) -> void:
+ var file := FileAccess.open(_chat_log_path, FileAccess.READ_WRITE)
+ if file == null:
+ push_error("The configured dedicated-server chat log is unavailable.")
+ return
+ file.seek_end()
+ file.store_line(JSON.stringify({
+ "recorded_at_utc": (
+ Time.get_datetime_string_from_system(true, false) + "Z"
+ ),
+ "sender_display_name": str(message.get("sender_display_name", "")),
+ "body": str(message.get("body", "")),
+ }))
+ file.close()
+
+
func _on_session_state_changed(state: NetworkSession.State) -> void:
if state in [
NetworkSession.State.INACTIVE,
@@ -482,6 +646,7 @@ func _on_session_state_changed(state: NetworkSession.State) -> void:
_last_call_msec.clear()
_call_variant_indices.clear()
_peer_names.clear()
+ _pending_peer_messages.clear()
_sequence = 0
history_replaced.emit([])
diff --git a/network/network_fish_showcase_protocol.gd b/network/network_fish_showcase_protocol.gd
index d69e9dd..2ecd1ee 100644
--- a/network/network_fish_showcase_protocol.gd
+++ b/network/network_fish_showcase_protocol.gd
@@ -2,7 +2,7 @@ class_name NetworkFishShowcaseProtocol
extends RefCounted
const CAPABILITY: StringName = &"fish_showcase_v1"
-const RELIABLE_CHANNEL: int = NetworkProtocol.ITEM_RELIABLE_CHANNEL
+const RELIABLE_CHANNEL: int = NetworkProtocol.SHOWCASE_RELIABLE_CHANNEL
const MAX_SESSION_ID_LENGTH: int = 96
const MAX_FISH_ID_LENGTH: int = 96
const MAX_WEIGHT_LB: float = 1000.0
diff --git a/network/network_fish_showcase_service.gd b/network/network_fish_showcase_service.gd
index 068ee7d..d956240 100644
--- a/network/network_fish_showcase_service.gd
+++ b/network/network_fish_showcase_service.gd
@@ -7,6 +7,7 @@ const FishCatchType = preload("res://fish/fish_catch.gd")
const FishQualityType = preload("res://fish/fish_quality.gd")
const FishInventoryType = preload("res://inventory/fish_inventory.gd")
const PlayerHotbarType = preload("res://inventory/player_hotbar.gd")
+const LOCAL_STATE_RETRY_SECONDS: float = 0.75
var _session: NetworkSession
var _spawn_service: PlayerSpawnService
@@ -15,6 +16,8 @@ var _local_inventory: FishInventoryType
var _local_hotbar: PlayerHotbarType
var _states: Dictionary[int, Dictionary] = {}
var _local_revision: int = 0
+var _local_acknowledged_revision: int = -1
+var _local_state_retry_elapsed: float = 0.0
var _local_visible: bool = false
var _local_catch_id: StringName
@@ -54,6 +57,30 @@ func setup(
)
+func _process(delta: float) -> void:
+ if (
+ _session == null
+ or not _session.is_joined_client()
+ or _local_revision <= 0
+ or _local_revision <= _local_acknowledged_revision
+ ):
+ _local_state_retry_elapsed = 0.0
+ return
+ _local_state_retry_elapsed += delta
+ if _local_state_retry_elapsed < LOCAL_STATE_RETRY_SECONDS:
+ return
+ var fish_catch: FishCatchType
+ if _local_visible:
+ fish_catch = _local_inventory.get_catch_by_id(_local_catch_id)
+ if fish_catch == null or not fish_catch.is_valid():
+ _submit_local_state(null, false)
+ return
+ # A client can finish a generated-world transition on the same frame that
+ # its local showcase changes. Republish until the authoritative echo arrives
+ # so that one early application-level miss cannot leave held fish out of sync.
+ _submit_local_state(fish_catch, _local_visible)
+
+
func toggle_selected_fish() -> bool:
if _local_hotbar == null or _local_inventory == null:
return false
@@ -78,6 +105,7 @@ func get_local_showcase_catch_id() -> StringName:
func _submit_local_state(fish_catch: FishCatchType, should_show: bool) -> void:
_local_revision += 1
+ _local_state_retry_elapsed = 0.0
var local_peer_id: int = (
_session.get_local_peer_id() if _session != null else 1
)
@@ -166,6 +194,11 @@ func receive_showcase_state(data: Dictionary) -> void:
):
return
var peer_id: int = int(data["owner_peer_id"])
+ if peer_id == _session.get_local_peer_id():
+ _local_acknowledged_revision = maxi(
+ _local_acknowledged_revision,
+ int(data["revision"]),
+ )
var previous: Dictionary = _states.get(peer_id, {})
if int(data["revision"]) <= int(previous.get("revision", -1)):
return
@@ -267,6 +300,8 @@ func _on_session_state_changed(state: NetworkSession.State) -> void:
_states.clear()
_local_visible = false
_local_catch_id = StringName()
+ _local_acknowledged_revision = -1
+ _local_state_retry_elapsed = 0.0
var local_avatar: Player = _spawn_service.get_avatar(
_session.get_local_peer_id()
)
diff --git a/network/network_fishing_attempt.gd b/network/network_fishing_attempt.gd
index 3a1b1de..638b4da 100644
--- a/network/network_fishing_attempt.gd
+++ b/network/network_fishing_attempt.gd
@@ -21,6 +21,8 @@ var origin: Vector3
var target: Vector3
var bobber_position: Vector3
var fish_id: StringName
+var rod_id: StringName
+var bait_id: StringName
var bait_tags: Array[StringName] = []
var lure_effects: Array[StringName] = []
var encounter_seed: int = 0
diff --git a/network/network_fishing_protocol.gd b/network/network_fishing_protocol.gd
index 2e9a342..7f15fdd 100644
--- a/network/network_fishing_protocol.gd
+++ b/network/network_fishing_protocol.gd
@@ -5,9 +5,11 @@ const MAX_ID_LENGTH: int = 96
const MAX_EVIDENCE_FISH_IDS: int = 256
const MAX_CAST_DISTANCE: float = 30.0
const MAX_REEL_SPEED: float = 10.0
-const MAX_BARRIER_DAMAGE: int = 100
+const MAX_BARRIER_DAMAGE: int = 128
const INPUT_CHANNEL: int = 3
const SNAPSHOT_CHANNEL: int = 4
+const OBSERVER_SNAPSHOT_CHANNEL: int = 10
+const RELIABLE_CHANNEL: int = NetworkProtocol.FISHING_RELIABLE_CHANNEL
const INPUT_RATE: float = 1.0 / 30.0
const SNAPSHOT_RATE: float = 1.0 / 20.0
@@ -35,6 +37,7 @@ static func validate_cast_request(data: Variant) -> String:
"rarity_multipliers",
"discovered_fish_ids",
"capacity_available",
+ "movement_sequence",
]:
if not payload.has(key):
return "Malformed fishing request."
@@ -51,11 +54,16 @@ static func validate_cast_request(data: Variant) -> String:
or typeof(payload["rarity_multipliers"]) != TYPE_ARRAY
or typeof(payload["discovered_fish_ids"]) != TYPE_ARRAY
or typeof(payload["capacity_available"]) != TYPE_BOOL
+ or typeof(payload["movement_sequence"]) != TYPE_INT
):
return "Malformed fishing request."
- if payload.has("bait_id") and typeof(payload["bait_id"]) not in [TYPE_STRING, TYPE_STRING_NAME]:
+ 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]:
+ 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"]
@@ -92,6 +100,8 @@ static func validate_cast_request(data: Variant) -> String:
or str(payload["rod_id"]).is_empty()
or str(payload["rod_id"]).length() > 96
or str(payload.get("lure_id", "")).length() > 96
+ or int(payload["movement_sequence"]) < 0
+ or int(payload["movement_sequence"]) > 2147483647
):
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 7303f12..8c9c798 100644
--- a/network/network_fishing_service.gd
+++ b/network/network_fishing_service.gd
@@ -5,6 +5,7 @@ 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 FishSelectorType = preload("res://fish/fish_selector.gd")
+const FishingRodDataType = preload("res://items/fishing_rod_data.gd")
const FishingContextType = preload("res://fishing/fishing_context.gd")
const CollectionLogType = preload("res://collection/collection_log.gd")
const FishExperienceType = preload("res://fish/fish_experience.gd")
@@ -20,6 +21,8 @@ const MAX_LEDGER_ENTRIES_PER_PEER: int = 64
const CAST_ORIGIN_TOLERANCE: float = 2.5
const CAPACITY_RESPONSE_TIMEOUT: float = 5.0
const MIN_CAST_INTERVAL: float = 0.25
+const OBSERVER_SNAPSHOT_INTERVAL: float = 0.1
+const OBSERVER_RELEVANCE_DISTANCE: float = 96.0
signal local_cast_accepted(attempt_id: String, target: Vector3)
signal local_cast_rejected(message: String)
@@ -50,7 +53,13 @@ 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 _observer_snapshot_accumulator: float = 0.0
var _local_input_sequence: int = 0
+var _presentation_enabled: bool = true
+var _owner_snapshot_packets_sent: int = 0
+var _owner_snapshot_states_sent: int = 0
+var _observer_snapshot_packets_sent: int = 0
+var _observer_snapshot_states_sent: int = 0
func setup(
@@ -66,6 +75,7 @@ func setup(
item_catalog: ItemCatalog,
fish_catalog: FishPoolType,
item_use: NetworkItemUseService,
+ presentation_enabled: bool = true,
) -> void:
_session = session
_spawn_service = spawn_service
@@ -79,6 +89,7 @@ func setup(
_item_catalog = item_catalog
_fish_catalog = fish_catalog
_item_use = item_use
+ _presentation_enabled = presentation_enabled
if not _session.peer_removed.is_connected(_on_peer_removed):
_session.peer_removed.connect(_on_peer_removed)
if not _session.state_changed.is_connected(_on_session_state_changed):
@@ -129,6 +140,7 @@ func request_local_cast(
"capacity_available": bool(evidence.get("capacity_available", false)),
"bait_id": str(bait_id),
"lure_id": str(lure_id),
+ "movement_sequence": _session.get_latest_movement_input_sequence(),
}
if _session.is_host():
_handle_cast_request(_session.get_local_peer_id(), data)
@@ -167,6 +179,7 @@ func cancel_local_attempt(reason: String = "Fishing attempt ended.") -> void:
if _session.is_host():
_cancel_attempt(peer_id, reason)
else:
+ _pending_local_bait_by_request.erase(attempt.request_id)
submit_cancel_request.rpc_id(1, attempt.attempt_id)
@@ -211,15 +224,27 @@ func _process(delta: float) -> void:
if now >= attempt.capacity_deadline:
_cancel_attempt(peer_id, "Fishing attempt ended.")
_snapshot_accumulator += delta
+ _observer_snapshot_accumulator += delta
if _snapshot_accumulator >= NetworkFishingProtocol.SNAPSHOT_RATE:
_snapshot_accumulator = fmod(
_snapshot_accumulator,
NetworkFishingProtocol.SNAPSHOT_RATE
)
- _broadcast_snapshots()
+ _broadcast_owner_snapshots()
+ if _observer_snapshot_accumulator >= OBSERVER_SNAPSHOT_INTERVAL:
+ _observer_snapshot_accumulator = fmod(
+ _observer_snapshot_accumulator,
+ OBSERVER_SNAPSHOT_INTERVAL,
+ )
+ _broadcast_observer_snapshots()
-@rpc("any_peer", "call_remote", "reliable", 0)
+@rpc(
+ "any_peer",
+ "call_remote",
+ "reliable",
+ NetworkFishingProtocol.RELIABLE_CHANNEL,
+)
func submit_cast_request(data: Dictionary) -> void:
var sender_id: int = multiplayer.get_remote_sender_id()
if not _session.is_host() or not _session.is_authenticated_peer(sender_id):
@@ -252,12 +277,12 @@ func _handle_cast_request(peer_id: int, data: Dictionary) -> void:
_record_and_reject(peer_id, request_id, "Already fishing.")
return
if not bool(data["capacity_available"]):
- _record_and_reject(peer_id, request_id, "Cooler is full.")
+ _record_and_reject(peer_id, request_id, "Inventory is full.")
return
- var rod: ItemData = _item_catalog.get_item_by_id(
+ var rod := _item_catalog.get_item_by_id(
StringName(str(data["rod_id"]))
- )
- if rod == null or rod.category != ItemData.Category.ROD:
+ ) as FishingRodDataType
+ if rod == null or not rod.is_available():
_record_and_reject(peer_id, request_id, "Select a fishing rod to cast.")
return
var avatar: Player = _spawn_service.get_avatar(peer_id)
@@ -266,7 +291,13 @@ func _handle_cast_request(peer_id: int, data: Dictionary) -> void:
return
var origin: Vector3 = NetworkFishingProtocol.array_to_vector3(data["origin"])
var target: Vector3 = NetworkFishingProtocol.array_to_vector3(data["target"])
- var authoritative_origin: Vector3 = avatar.get_cast_origin_position()
+ var movement_state: Dictionary = avatar.get_lag_compensated_movement_state(
+ int(data.get("movement_sequence", 0))
+ )
+ var authoritative_origin: Vector3 = movement_state.get(
+ "cast_origin",
+ avatar.get_cast_origin_position(),
+ )
if origin.distance_to(authoritative_origin) > CAST_ORIGIN_TOLERANCE:
_record_and_reject(peer_id, request_id, "Cannot fish here.")
return
@@ -277,7 +308,10 @@ func _handle_cast_request(peer_id: int, data: Dictionary) -> void:
_fishing_spot.maximum_cast_distance,
float(data["charge"])
)
- var facing: Vector3 = avatar.get_facing_direction()
+ var facing: Vector3 = movement_state.get(
+ "facing",
+ avatar.get_facing_direction(),
+ )
facing.y = 0.0
if (
cast_offset.length() < _fishing_spot.minimum_cast_distance - 0.25
@@ -312,7 +346,7 @@ func _handle_cast_request(peer_id: int, data: Dictionary) -> void:
if _item_use != null else null
)
var selected_fish: FishDataType = _select_authoritative_fish(
- region, data, effects
+ region, data, effects, rod
)
if selected_fish == null or not selected_fish.is_allowed_in_water(
region.water_type
@@ -339,6 +373,7 @@ func _handle_cast_request(peer_id: int, data: Dictionary) -> void:
not lure_id.is_empty()
and (
lure == null
+ or not lure.is_available()
or not lure.is_lure()
or (
owns_authoritative_bag
@@ -353,12 +388,13 @@ func _handle_cast_request(peer_id: int, data: Dictionary) -> void:
return
if not bait_id.is_empty() and (
bait == null
+ or not bait.is_available()
or not bait.is_bait()
or (
owns_authoritative_bag
and (
avatar_bag == null
- or not avatar_bag.remove_item(bait_id, 1)
+ or not avatar_bag.owns_item(bait_id)
)
)
):
@@ -374,20 +410,39 @@ 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.rod_id = rod.item_id
+ attempt.bait_id = bait_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"]) * (
+ var effect_reel_multiplier: float = (
effects.get_reel_multiplier() if effects != null else 1.0
)
- attempt.barrier_damage = int(data["barrier_damage"]) + (
+ var effect_barrier_bonus: int = (
effects.get_barrier_bonus() if effects != null else 0
)
- attempt.bite_time_remaining = _fishing_spot.roll_bite_wait_time() * float(
+ var effect_bite_multiplier: float = (
effects.get_bite_time_multiplier() if effects != null else 1.0
)
+ attempt.reel_speed = (
+ float(data["reel_speed"])
+ * effect_reel_multiplier
+ * rod.reel_speed_multiplier
+ )
+ attempt.barrier_damage = maxi(
+ roundi(
+ (float(data["barrier_damage"]) + effect_barrier_bonus)
+ * rod.barrier_power_multiplier
+ ),
+ 1,
+ )
+ attempt.bite_time_remaining = (
+ _fishing_spot.roll_bite_wait_time()
+ * effect_bite_multiplier
+ * rod.bite_time_multiplier
+ )
attempt.controller = CatchController.new()
add_child(attempt.controller)
attempt.controller.encounter_updated.connect(
@@ -472,6 +527,7 @@ func _select_authoritative_fish(
region: FishableWaterRegion,
data: Dictionary,
effects: PlayerItemEffects,
+ rod: FishingRodDataType,
) -> FishDataType:
var evidence_log := CollectionLogType.new()
for value: Variant in data["discovered_fish_ids"]:
@@ -480,6 +536,7 @@ func _select_authoritative_fish(
return null
evidence_log.mark_discovered(fish_id)
var selector := FishSelectorType.new()
+ selector.active_rod = rod
selector.undiscovered_weight_multiplier = (
_fishing_spot.undiscovered_weight_multiplier
)
@@ -503,7 +560,11 @@ func _bait_tags_for_request(data: Dictionary) -> Array[StringName]:
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 []
+ return (
+ bait.bait_tags.duplicate()
+ if bait != null and bait.is_available() and bait.is_bait()
+ else []
+ )
func _set_bite_pending(attempt: NetworkFishingAttempt) -> void:
@@ -527,13 +588,19 @@ func _start_bite(attempt: NetworkFishingAttempt) -> void:
if attempt.phase != NetworkFishingAttempt.Phase.WAITING_FOR_BITE:
return
var fish: FishDataType = _fish_catalog.get_fish_by_id(attempt.fish_id)
- if fish == null or fish.catch_profile == null:
+ var rod := _item_catalog.get_item_by_id(attempt.rod_id) as FishingRodDataType
+ if (
+ fish == null
+ or fish.catch_profile == null
+ or rod == null
+ or not rod.is_available()
+ ):
_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.active_rod = rod
selector.use_deterministic_test_seed = true
selector.deterministic_test_seed = attempt.encounter_seed ^ 0x5F3759DF
selector.begin_roll()
@@ -541,6 +608,17 @@ func _start_bite(attempt: NetworkFishingAttempt) -> void:
if fish_catch == null or not fish_catch.is_valid():
_cancel_attempt(attempt.owner_peer_id, "Fishing attempt ended.")
return
+ if (
+ attempt.owner_peer_id == _session.get_local_peer_id()
+ and not attempt.bait_id.is_empty()
+ and (
+ _local_bag == null
+ or not _local_bag.remove_item(attempt.bait_id, 1)
+ )
+ ):
+ _cancel_attempt(attempt.owner_peer_id, "No bait available.")
+ return
+ attempt.phase = NetworkFishingAttempt.Phase.FIGHTING
attempt.catch_payload = fish_catch.to_network_dict()
attempt.controller.start_authoritative_encounter(
fish.catch_profile,
@@ -548,8 +626,6 @@ func _start_bite(attempt: NetworkFishingAttempt) -> void:
attempt.barrier_damage,
attempt.encounter_seed,
fish_catch.quality,
- int(fish.rarity),
- fish.get_weight_percentile(fish_catch.weight_lb),
)
var data: Dictionary = {
"attempt_id": attempt.attempt_id,
@@ -598,7 +674,12 @@ func _handle_fishing_input(peer_id: int, data: Dictionary) -> void:
attempt.controller.handle_primary_pressed()
-@rpc("any_peer", "call_remote", "reliable", 0)
+@rpc(
+ "any_peer",
+ "call_remote",
+ "reliable",
+ NetworkFishingProtocol.RELIABLE_CHANNEL,
+)
func submit_cancel_request(attempt_id: String) -> void:
var sender_id: int = multiplayer.get_remote_sender_id()
var attempt: NetworkFishingAttempt = _attempts.get(sender_id)
@@ -643,16 +724,59 @@ func _on_encounter_updated(
})
-func _broadcast_snapshots() -> void:
- var snapshots: Array[Dictionary] = []
+func _broadcast_owner_snapshots() -> void:
for attempt: NetworkFishingAttempt in _attempts.values():
var snapshot: Dictionary = attempt.get_meta("snapshot", {})
- if not snapshot.is_empty():
- snapshots.append(snapshot)
- if snapshots.is_empty():
+ if snapshot.is_empty():
+ continue
+ var owner_peer_id: int = attempt.owner_peer_id
+ if owner_peer_id == _session.get_local_peer_id():
+ _apply_snapshots([snapshot])
+ elif owner_peer_id > 1:
+ receive_fishing_snapshots.rpc_id(owner_peer_id, [snapshot])
+ _owner_snapshot_packets_sent += 1
+ _owner_snapshot_states_sent += 1
+
+
+func _broadcast_observer_snapshots() -> void:
+ if _attempts.is_empty():
return
- _apply_snapshots(snapshots)
- receive_fishing_snapshots.rpc(snapshots)
+ var observer_ids: Array[int] = _session.get_authenticated_peer_ids()
+ for observer_peer_id: int in observer_ids:
+ var observer_avatar: Player = _spawn_service.get_avatar(observer_peer_id)
+ if observer_avatar == null:
+ continue
+ var summaries: Array = []
+ for attempt: NetworkFishingAttempt in _attempts.values():
+ if attempt.owner_peer_id == observer_peer_id:
+ continue
+ var owner_avatar: Player = _spawn_service.get_avatar(
+ attempt.owner_peer_id
+ )
+ if (
+ owner_avatar == null
+ or observer_avatar.global_position.distance_squared_to(
+ owner_avatar.global_position
+ ) > OBSERVER_RELEVANCE_DISTANCE * OBSERVER_RELEVANCE_DISTANCE
+ ):
+ continue
+ summaries.append([
+ attempt.attempt_id,
+ attempt.owner_peer_id,
+ attempt.bobber_position,
+ int(attempt.phase),
+ ])
+ if summaries.is_empty():
+ continue
+ if observer_peer_id == _session.get_local_peer_id():
+ _apply_observer_snapshots(summaries)
+ elif observer_peer_id > 1:
+ receive_fishing_observer_snapshots.rpc_id(
+ observer_peer_id,
+ summaries,
+ )
+ _observer_snapshot_packets_sent += 1
+ _observer_snapshot_states_sent += summaries.size()
@rpc("authority", "call_remote", "unreliable_ordered", 4)
@@ -660,6 +784,55 @@ func receive_fishing_snapshots(snapshots: Array) -> void:
_apply_snapshots(snapshots)
+@rpc(
+ "authority",
+ "call_remote",
+ "unreliable_ordered",
+ NetworkFishingProtocol.OBSERVER_SNAPSHOT_CHANNEL,
+)
+func receive_fishing_observer_snapshots(summaries: Array) -> void:
+ _apply_observer_snapshots(summaries)
+
+
+func _apply_observer_snapshots(summaries: Array) -> void:
+ if not _presentation_enabled or summaries.size() > 128:
+ return
+ for value: Variant in summaries:
+ if typeof(value) != TYPE_ARRAY:
+ continue
+ var fields: Array = value
+ if (
+ fields.size() != 4
+ or typeof(fields[0]) != TYPE_STRING
+ or str(fields[0]).is_empty()
+ or str(fields[0]).length() > NetworkFishingProtocol.MAX_ID_LENGTH
+ or typeof(fields[1]) != TYPE_INT
+ or int(fields[1]) <= 0
+ or typeof(fields[2]) != TYPE_VECTOR3
+ or typeof(fields[3]) != TYPE_INT
+ or not (fields[2] as Vector3).is_finite()
+ or int(fields[3]) not in [
+ NetworkFishingAttempt.Phase.WAITING_FOR_BITE,
+ NetworkFishingAttempt.Phase.FIGHTING,
+ NetworkFishingAttempt.Phase.PENDING_CAPACITY,
+ ]
+ ):
+ continue
+ var owner_peer_id: int = int(fields[1])
+ if owner_peer_id == _session.get_local_peer_id():
+ continue
+ var presentation := _get_remote_presentation(owner_peer_id)
+ if presentation != null:
+ presentation.synchronize_active(
+ str(fields[0]),
+ fields[2],
+ int(fields[3]) in [
+ NetworkFishingAttempt.Phase.FIGHTING,
+ NetworkFishingAttempt.Phase.PENDING_CAPACITY,
+ ],
+ )
+
+
func _apply_snapshots(snapshots: Array) -> void:
var local_peer_id: int = _session.get_local_peer_id()
for value: Variant in snapshots:
@@ -766,7 +939,12 @@ func _on_attempt_caught(peer_id: int) -> void:
receive_capacity_probe.rpc_id(peer_id, probe)
-@rpc("authority", "call_remote", "reliable", 0)
+@rpc(
+ "authority",
+ "call_remote",
+ "reliable",
+ NetworkFishingProtocol.RELIABLE_CHANNEL,
+)
func receive_capacity_probe(data: Dictionary) -> void:
_handle_local_capacity_probe(data)
@@ -780,11 +958,8 @@ func _handle_local_capacity_probe(data: Dictionary) -> void:
return
var can_accept: bool = (
_local_inventory != null
- and _local_capacity != null
- and (
- _local_inventory.contains_catch_id(StringName(data["catch_id"]))
- or _local_inventory.get_all_catches().size()
- < _local_capacity.get_capacity()
+ and _local_inventory.can_accept_catch(
+ StringName(data["catch_id"])
)
)
if _session.is_host():
@@ -803,7 +978,12 @@ func _handle_local_capacity_probe(data: Dictionary) -> void:
)
-@rpc("any_peer", "call_remote", "reliable", 0)
+@rpc(
+ "any_peer",
+ "call_remote",
+ "reliable",
+ NetworkFishingProtocol.RELIABLE_CHANNEL,
+)
func submit_capacity_response(
attempt_id: String,
capacity_nonce: String,
@@ -832,7 +1012,7 @@ func _handle_capacity_response(
):
return
if not can_accept:
- _cancel_attempt(peer_id, "Cooler is full.")
+ _cancel_attempt(peer_id, "Inventory is full.")
return
_finalize_catch(attempt)
@@ -861,7 +1041,12 @@ func _finalize_catch(attempt: NetworkFishingAttempt) -> void:
_dispose_attempt(attempt.owner_peer_id)
-@rpc("authority", "call_remote", "reliable", 0)
+@rpc(
+ "authority",
+ "call_remote",
+ "reliable",
+ NetworkFishingProtocol.RELIABLE_CHANNEL,
+)
func receive_target_outcome(data: Dictionary) -> void:
_apply_target_outcome(data)
@@ -883,11 +1068,7 @@ func _apply_target_outcome(data: Dictionary) -> void:
if fish_catch == null:
return
var already_owned: bool = _local_inventory.contains_catch_id(catch_id)
- if (
- not already_owned
- and _local_inventory.get_all_catches().size()
- >= _local_capacity.get_capacity()
- ):
+ if not already_owned and not _local_inventory.can_accept_catch(catch_id):
return
if not already_owned:
var experience_award: int = (
@@ -897,7 +1078,7 @@ func _apply_target_outcome(data: Dictionary) -> void:
)
)
_local_inventory.add_catch(fish_catch)
- _local_collection.mark_quality_discovered(
+ _local_collection.record_catch(
fish_id,
fish_catch.quality,
)
@@ -933,7 +1114,12 @@ func _acknowledge_result(result_id: String, catch_id: StringName) -> void:
acknowledge_fishing_result.rpc_id(1, result_id, str(catch_id))
-@rpc("any_peer", "call_remote", "reliable", 0)
+@rpc(
+ "any_peer",
+ "call_remote",
+ "reliable",
+ NetworkFishingProtocol.RELIABLE_CHANNEL,
+)
func acknowledge_fishing_result(result_id: String, catch_id: String) -> void:
var sender_id: int = multiplayer.get_remote_sender_id()
if (
@@ -955,7 +1141,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)
@@ -990,7 +1176,12 @@ func _broadcast_public_outcome(
receive_public_outcome.rpc(data)
-@rpc("authority", "call_remote", "reliable", 0)
+@rpc(
+ "authority",
+ "call_remote",
+ "reliable",
+ NetworkFishingProtocol.RELIABLE_CHANNEL,
+)
func receive_public_outcome(data: Dictionary) -> void:
_apply_public_outcome(data)
@@ -1006,6 +1197,13 @@ func _apply_public_outcome(data: Dictionary) -> void:
if peer_id == _session.get_local_peer_id():
if str(data["outcome"]) != "catch":
if not _session.is_host():
+ var local_attempt: NetworkFishingAttempt = _attempts.get(
+ peer_id
+ )
+ if local_attempt != null:
+ _pending_local_bait_by_request.erase(
+ local_attempt.request_id
+ )
_attempts.erase(peer_id)
local_attempt_ended.emit(
StringName(str(data["outcome"])),
@@ -1035,7 +1233,12 @@ func _broadcast_cast_accepted(data: Dictionary) -> void:
receive_cast_accepted.rpc(data)
-@rpc("authority", "call_remote", "reliable", 0)
+@rpc(
+ "authority",
+ "call_remote",
+ "reliable",
+ NetworkFishingProtocol.RELIABLE_CHANNEL,
+)
func receive_cast_accepted(data: Dictionary) -> void:
_apply_cast_accepted(data)
@@ -1066,28 +1269,23 @@ 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)
if presentation != null:
- presentation.show_cast(origin, target)
+ presentation.show_cast(
+ origin,
+ target,
+ str(data["attempt_id"]),
+ )
-@rpc("authority", "call_remote", "reliable", 0)
+@rpc(
+ "authority",
+ "call_remote",
+ "reliable",
+ NetworkFishingProtocol.RELIABLE_CHANNEL,
+)
func receive_bite_pending(data: Dictionary) -> void:
_apply_bite_pending(data)
@@ -1102,7 +1300,12 @@ func _apply_bite_pending(data: Dictionary) -> void:
local_bite_pending.emit(str(data["attempt_id"]))
-@rpc("authority", "call_remote", "reliable", 0)
+@rpc(
+ "authority",
+ "call_remote",
+ "reliable",
+ NetworkFishingProtocol.RELIABLE_CHANNEL,
+)
func receive_bite_started(data: Dictionary) -> void:
_apply_bite_started(data)
@@ -1115,6 +1318,22 @@ func _apply_bite_started(data: Dictionary) -> void:
return
var peer_id: int = data["owner_peer_id"]
if peer_id == _session.get_local_peer_id():
+ if not _session.is_host():
+ var attempt: NetworkFishingAttempt = _attempts.get(peer_id)
+ var pending_bait_id := StringName()
+ if attempt != null:
+ pending_bait_id = _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_bite_started.emit(str(data["attempt_id"]))
else:
var presentation := _get_remote_presentation(peer_id)
@@ -1147,7 +1366,12 @@ func _send_cast_rejected(
receive_cast_rejected.rpc_id(peer_id, request_id, message.left(128))
-@rpc("authority", "call_remote", "reliable", 0)
+@rpc(
+ "authority",
+ "call_remote",
+ "reliable",
+ NetworkFishingProtocol.RELIABLE_CHANNEL,
+)
func receive_cast_rejected(request_id: String, message: String) -> void:
_pending_local_bait_by_request.erase(request_id)
local_cast_rejected.emit(message)
@@ -1182,6 +1406,8 @@ func _resend_request_response(peer_id: int, response: Dictionary) -> void:
func _get_remote_presentation(
peer_id: int,
) -> RemoteFishingPresentation:
+ if not _presentation_enabled:
+ return null
var existing: RemoteFishingPresentation = _remote_presentations.get(peer_id)
if existing != null and is_instance_valid(existing):
return existing
@@ -1235,12 +1461,12 @@ func _dispose_attempt(peer_id: int) -> void:
attempt.controller.reset()
attempt.controller.queue_free()
var avatar: Player = _spawn_service.get_avatar(peer_id)
- var local_return_is_active: bool = (
+ var local_fishing_sequence_is_active: bool = (
peer_id == _session.get_local_peer_id()
and _fishing_spot != null
- and _fishing_spot.is_returning()
+ and _fishing_spot.is_fishing_sequence_active()
)
- if avatar != null and not local_return_is_active:
+ if avatar != null and not local_fishing_sequence_is_active:
avatar.set_movement_enabled(true)
@@ -1282,10 +1508,23 @@ func _clear_all() -> void:
_result_acknowledgements.clear()
_last_cast_time.clear()
_last_input_time.clear()
+ _pending_local_bait_by_request.clear()
_snapshot_accumulator = 0.0
+ _observer_snapshot_accumulator = 0.0
_local_input_sequence = 0
+func get_network_metrics() -> Dictionary:
+ return {
+ "active_attempts": _attempts.size(),
+ "remote_presentations": _remote_presentations.size(),
+ "owner_snapshot_packets_sent": _owner_snapshot_packets_sent,
+ "owner_snapshot_states_sent": _owner_snapshot_states_sent,
+ "observer_snapshot_packets_sent": _observer_snapshot_packets_sent,
+ "observer_snapshot_states_sent": _observer_snapshot_states_sent,
+ }
+
+
func _bound_result_ledger() -> void:
while _result_ledgers.size() > MAX_LEDGER_ENTRIES_PER_PEER:
_result_ledgers.erase(_result_ledgers.keys().front())
diff --git a/network/network_home_protocol.gd b/network/network_home_protocol.gd
new file mode 100644
index 0000000..96756e9
--- /dev/null
+++ b/network/network_home_protocol.gd
@@ -0,0 +1,188 @@
+class_name NetworkHomeProtocol
+extends RefCounted
+
+const PlayerHomeStateType = preload("res://homes/player_home_state.gd")
+
+const CAPABILITY: StringName = &"portable_home_v1"
+const RELIABLE_CHANNEL: int = NetworkProtocol.HOME_RELIABLE_CHANNEL
+const MAX_ID_LENGTH: int = 96
+const MAX_MESSAGE_LENGTH: int = 160
+const MAX_TRANSITION_COORDINATE: float = 10000.0
+const WORLD_SPACE: StringName = &"world"
+
+enum TransitionAction {
+ ENTER,
+ EXIT,
+}
+
+
+static func make_space_id(owner_fingerprint: String) -> StringName:
+ return StringName("rv:%s" % owner_fingerprint)
+
+
+static func manifest_digest(home_data: Dictionary) -> String:
+ var sanitized: Dictionary = PlayerHomeStateType.sanitize_save_data(home_data)
+ if sanitized.is_empty():
+ return ""
+ var canonical_owned: Array[Dictionary] = []
+ var owned: Dictionary = sanitized["owned_decor"]
+ var owned_ids: Array[String] = []
+ for value: Variant in owned.keys():
+ owned_ids.append(str(value))
+ owned_ids.sort()
+ for decor_id: String in owned_ids:
+ canonical_owned.append({
+ "id": decor_id,
+ "quantity": int(owned[decor_id]),
+ })
+ var placements: Array[Dictionary] = []
+ for value: Variant in sanitized["placements"]:
+ placements.append((value as Dictionary).duplicate(true))
+ placements.sort_custom(func(first: Dictionary, second: Dictionary) -> bool:
+ return str(first["instance_id"]) < str(second["instance_id"])
+ )
+ var canonical: Dictionary = {
+ "schema_version": int(sanitized["schema_version"]),
+ "tier": int(sanitized["tier"]),
+ "exterior_id": str(sanitized["exterior_id"]),
+ "privacy": int(sanitized["privacy"]),
+ "owned": canonical_owned,
+ "placements": placements,
+ "revision": int(sanitized["revision"]),
+ "next_instance_sequence": int(sanitized["next_instance_sequence"]),
+ }
+ return JSON.stringify(canonical).sha256_text()
+
+
+static func manifest_signature_fields(data: Dictionary) -> Array:
+ return [
+ str(data.get("session_id", "")),
+ str(data.get("request_id", "")),
+ str(data.get("owner_fingerprint", "")),
+ int((data.get("home", {}) as Dictionary).get("revision", -1)),
+ str(data.get("digest", "")),
+ ]
+
+
+static func validate_manifest_request(value: Variant) -> bool:
+ if typeof(value) != TYPE_DICTIONARY:
+ return false
+ var data: Dictionary = value
+ if (
+ not _valid_id(data.get("request_id"))
+ or not _valid_id(data.get("session_id"))
+ or not NetworkIdentityCrypto.valid_fingerprint(
+ data.get("owner_fingerprint")
+ )
+ or typeof(data.get("home")) != TYPE_DICTIONARY
+ or typeof(data.get("digest")) != TYPE_STRING
+ or str(data["digest"]).length() != 64
+ or typeof(data.get("sender_signature")) != TYPE_PACKED_BYTE_ARRAY
+ ):
+ return false
+ var sanitized: Dictionary = PlayerHomeStateType.sanitize_save_data(
+ data["home"]
+ )
+ return (
+ not sanitized.is_empty()
+ and manifest_digest(sanitized) == str(data["digest"])
+ )
+
+
+static func validate_manifest_broadcast(value: Variant) -> bool:
+ if typeof(value) != TYPE_DICTIONARY:
+ return false
+ var data: Dictionary = value
+ return (
+ typeof(data.get("owner_peer_id")) == TYPE_INT
+ and int(data["owner_peer_id"]) > 0
+ and NetworkIdentityCrypto.valid_fingerprint(
+ data.get("owner_fingerprint")
+ )
+ and typeof(data.get("home")) == TYPE_DICTIONARY
+ and not PlayerHomeStateType.sanitize_save_data(data["home"]).is_empty()
+ and _valid_transform(data.get("exterior_transform"))
+ and typeof(data.get("interior_index")) == TYPE_INT
+ and int(data["interior_index"]) >= 0
+ and int(data["interior_index"]) < 128
+ )
+
+
+static func transition_signature_fields(data: Dictionary) -> Array:
+ return [
+ str(data.get("session_id", "")),
+ str(data.get("request_id", "")),
+ int(data.get("action", -1)),
+ str(data.get("owner_fingerprint", "")),
+ ]
+
+
+static func validate_transition_request(value: Variant) -> bool:
+ if typeof(value) != TYPE_DICTIONARY:
+ return false
+ var data: Dictionary = value
+ return (
+ _valid_id(data.get("request_id"))
+ and _valid_id(data.get("session_id"))
+ and typeof(data.get("action")) == TYPE_INT
+ and int(data["action"]) >= TransitionAction.ENTER
+ and int(data["action"]) <= TransitionAction.EXIT
+ and NetworkIdentityCrypto.valid_fingerprint(
+ data.get("owner_fingerprint")
+ )
+ and typeof(data.get("sender_signature")) == TYPE_PACKED_BYTE_ARRAY
+ )
+
+
+static func transform_to_array(value: Transform3D) -> Array[float]:
+ return [
+ value.basis.x.x, value.basis.x.y, value.basis.x.z,
+ value.basis.y.x, value.basis.y.y, value.basis.y.z,
+ value.basis.z.x, value.basis.z.y, value.basis.z.z,
+ value.origin.x, value.origin.y, value.origin.z,
+ ]
+
+
+static func array_to_transform(value: Variant) -> Transform3D:
+ if not validate_transition_transform(value):
+ return Transform3D()
+ var fields: Array = value
+ return Transform3D(
+ Basis(
+ Vector3(float(fields[0]), float(fields[1]), float(fields[2])),
+ Vector3(float(fields[3]), float(fields[4]), float(fields[5])),
+ Vector3(float(fields[6]), float(fields[7]), float(fields[8])),
+ ),
+ Vector3(float(fields[9]), float(fields[10]), float(fields[11])),
+ )
+
+
+static func validate_transition_transform(value: Variant) -> bool:
+ if not _valid_transform(value):
+ return false
+ var fields: Array = value
+ return (
+ absf(float(fields[9])) <= MAX_TRANSITION_COORDINATE
+ and absf(float(fields[10])) <= MAX_TRANSITION_COORDINATE
+ and absf(float(fields[11])) <= MAX_TRANSITION_COORDINATE
+ )
+
+
+static func _valid_transform(value: Variant) -> bool:
+ if typeof(value) != TYPE_ARRAY or (value as Array).size() != 12:
+ return false
+ for component: Variant in value:
+ if (
+ typeof(component) not in [TYPE_FLOAT, TYPE_INT]
+ or not is_finite(float(component))
+ ):
+ return false
+ return true
+
+
+static func _valid_id(value: Variant) -> bool:
+ return (
+ typeof(value) == TYPE_STRING
+ and not str(value).is_empty()
+ and str(value).length() <= MAX_ID_LENGTH
+ )
diff --git a/network/network_home_protocol.gd.uid b/network/network_home_protocol.gd.uid
new file mode 100644
index 0000000..24b6aae
--- /dev/null
+++ b/network/network_home_protocol.gd.uid
@@ -0,0 +1 @@
+uid://fl3qwiluejhm
diff --git a/network/network_home_service.gd b/network/network_home_service.gd
new file mode 100644
index 0000000..4885961
--- /dev/null
+++ b/network/network_home_service.gd
@@ -0,0 +1,1450 @@
+class_name NetworkHomeService
+extends Node
+
+const NetworkHomeProtocolType = preload(
+ "res://network/network_home_protocol.gd"
+)
+const PlayerHomeStateType = preload("res://homes/player_home_state.gd")
+const DecorCatalogType = preload("res://homes/decor_catalog.gd")
+const OwnedItemType = preload("res://items/owned_item.gd")
+
+signal local_transition_finished(accepted: bool, message: String)
+signal local_space_changed(space_id: StringName, owner_fingerprint: String)
+signal local_manifest_rejected(message: String)
+
+const MAX_MANIFESTS: int = 128
+const MAX_PENDING_LOCAL_DECOR_TRANSACTIONS: int = 16
+
+var _session: NetworkSession
+var _spawn_service: PlayerSpawnService
+var _local_home: PlayerHomeStateType
+var _save_manager: PlayerSaveManager
+var _world_service: HomeWorldService
+var _bag: PlayerBag
+var _inventory_layout: PlayerInventoryLayout
+var _hotbar: PlayerHotbar
+var _manifests_by_fingerprint: Dictionary[String, Dictionary] = {}
+var _fingerprint_by_peer: Dictionary[int, String] = {}
+var _authorized_owned_counts: Dictionary[int, Dictionary] = {}
+var _authorization_result_owner: Dictionary[String, int] = {}
+var _authorized_home_tiers: Dictionary[int, int] = {}
+var _home_upgrade_result_owner: Dictionary[String, int] = {}
+var _interior_slots_by_space: Dictionary[StringName, Dictionary] = {}
+var _next_interior_index: int = 0
+var _suppress_local_submission: bool = false
+var _publish_queued: bool = false
+var _last_submitted_local_home_revision: int = -1
+var _pending_local_decor_transactions: Dictionary[int, Dictionary] = {}
+
+
+func setup(
+ session: NetworkSession,
+ spawn_service: PlayerSpawnService,
+ local_home: PlayerHomeStateType,
+ save_manager: PlayerSaveManager,
+ world_service: HomeWorldService,
+ bag: PlayerBag,
+ inventory_layout: PlayerInventoryLayout,
+ hotbar: PlayerHotbar,
+) -> void:
+ _session = session
+ _spawn_service = spawn_service
+ _local_home = local_home
+ _save_manager = save_manager
+ _world_service = world_service
+ _bag = bag
+ _inventory_layout = inventory_layout
+ _hotbar = hotbar
+ if not _session.state_changed.is_connected(_on_session_state_changed):
+ _session.state_changed.connect(_on_session_state_changed)
+ if not _session.join_authenticated.is_connected(_on_join_authenticated):
+ _session.join_authenticated.connect(_on_join_authenticated)
+ if not _session.peer_authenticated.is_connected(_on_peer_authenticated):
+ _session.peer_authenticated.connect(_on_peer_authenticated)
+ if not _session.peer_removed.is_connected(_on_peer_removed):
+ _session.peer_removed.connect(_on_peer_removed)
+ if not _local_home.changed.is_connected(_on_local_home_changed):
+ _local_home.changed.connect(_on_local_home_changed)
+
+
+func get_local_space() -> StringName:
+ if _session == null or not _session.is_gameplay_session_active():
+ return NetworkHomeProtocolType.WORLD_SPACE
+ return _session.get_peer_space(_session.get_local_peer_id())
+
+
+func get_local_home_owner_fingerprint() -> String:
+ var space_id: String = String(get_local_space())
+ return space_id.trim_prefix("rv:") if space_id.begins_with("rv:") else ""
+
+
+func can_local_decorate() -> bool:
+ return (
+ get_local_home_owner_fingerprint()
+ == _session.get_local_identity_fingerprint()
+ )
+
+
+func has_pending_local_decor_mutation() -> bool:
+ return not _pending_local_decor_transactions.is_empty()
+
+
+func get_authoritative_owned_count(
+ peer_id: int,
+ decor_id: StringName,
+) -> int:
+ var authorized: Dictionary = _authorized_owned_counts.get(peer_id, {})
+ if authorized.has(String(decor_id)):
+ return int(authorized[String(decor_id)])
+ var fingerprint: String = _fingerprint_for_peer(peer_id)
+ var manifest: Dictionary = _manifests_by_fingerprint.get(fingerprint, {})
+ if manifest.is_empty():
+ return -1
+ var home: Dictionary = manifest.get("home", {})
+ var owned: Dictionary = home.get("owned_decor", {})
+ return int(owned.get(String(decor_id), 0))
+
+
+func get_authoritative_home_tier(peer_id: int) -> int:
+ if _authorized_home_tiers.has(peer_id):
+ return int(_authorized_home_tiers[peer_id])
+ var fingerprint: String = _fingerprint_for_peer(peer_id)
+ var manifest: Dictionary = _manifests_by_fingerprint.get(fingerprint, {})
+ if manifest.is_empty():
+ return -1
+ var home: Dictionary = manifest.get("home", {})
+ return int(home.get("tier", -1))
+
+
+func place_local_decor(
+ decor_id: StringName,
+ position: Vector3,
+ yaw_radians: float,
+ mode: PlayerHomeStateType.PlacementMode,
+) -> String:
+ if (
+ not can_local_decorate()
+ or has_pending_local_decor_mutation()
+ or _bag == null
+ or _bag.get_quantity(decor_id) <= 0
+ or _world_service == null
+ ):
+ return ""
+ var owner_fingerprint: String = (
+ _session.get_local_identity_fingerprint()
+ )
+ var normalized: Dictionary = (
+ _local_home.get_nearest_valid_placement_transform(
+ decor_id,
+ position,
+ yaw_radians,
+ mode,
+ "",
+ Callable(
+ self, "_candidate_clear_of_players"
+ ).bind(owner_fingerprint, decor_id),
+ )
+ )
+ if normalized.is_empty():
+ return ""
+ var transaction: Dictionary = _capture_local_decor_transaction()
+ if transaction.is_empty():
+ return ""
+ _suppress_local_submission = true
+ var instance_id: String = _local_home.place_decor(
+ decor_id,
+ normalized["position"],
+ float(normalized["yaw"]),
+ mode,
+ )
+ if instance_id.is_empty():
+ _suppress_local_submission = false
+ return ""
+ if not _bag.remove_item(decor_id, 1):
+ _local_home.remove_placement(instance_id)
+ _suppress_local_submission = false
+ return ""
+ _suppress_local_submission = false
+ if not _commit_local_decor_transaction(transaction):
+ return ""
+ return instance_id
+
+
+func remove_local_decor(instance_id: String) -> bool:
+ if (
+ not can_local_decorate()
+ or has_pending_local_decor_mutation()
+ or _bag == null
+ or _inventory_layout == null
+ or _hotbar == null
+ ):
+ return false
+ var placement: Dictionary = _local_home.get_placement(instance_id)
+ if placement.is_empty():
+ return false
+ var transaction: Dictionary = _capture_local_decor_transaction()
+ if transaction.is_empty():
+ return false
+ var decor_id := StringName(str(placement["decor_id"]))
+ var target_hotbar_slot: int = _hotbar.find_item_slot(decor_id)
+ if target_hotbar_slot < 0:
+ target_hotbar_slot = _hotbar.find_first_empty_slot()
+ var prepared_hotbar_placement: bool = false
+ if (
+ target_hotbar_slot >= 0
+ and _inventory_layout.get_container(
+ PlayerInventoryLayout.EntryKind.ITEM,
+ decor_id,
+ ) < 0
+ ):
+ prepared_hotbar_placement = (
+ _inventory_layout.prepare_new_item_placement(
+ decor_id,
+ PlayerInventoryLayout.InventoryContainer.HOTBAR,
+ target_hotbar_slot,
+ )
+ )
+ var returned_to_inventory: bool = _bag.add_item(decor_id, 1)
+ if not returned_to_inventory:
+ if prepared_hotbar_placement:
+ _inventory_layout.cancel_prepared_item_placement(decor_id)
+ returned_to_inventory = _bag.add_item_to_storage(decor_id, 1)
+ if not returned_to_inventory:
+ if prepared_hotbar_placement:
+ _inventory_layout.cancel_prepared_item_placement(decor_id)
+ return false
+ if target_hotbar_slot >= 0:
+ _hotbar.assign_item(target_hotbar_slot, decor_id)
+ _hotbar.select_slot(target_hotbar_slot)
+ _suppress_local_submission = true
+ if not _local_home.remove_placement(instance_id):
+ _suppress_local_submission = false
+ _restore_local_decor_transaction(transaction)
+ return false
+ _suppress_local_submission = false
+ if not _commit_local_decor_transaction(transaction):
+ return false
+ return true
+
+
+func update_local_decor(
+ instance_id: String,
+ position: Vector3,
+ yaw_radians: float,
+ mode: PlayerHomeStateType.PlacementMode,
+) -> bool:
+ if has_pending_local_decor_mutation():
+ return false
+ var normalized: Dictionary = get_valid_local_decor_transform(
+ instance_id, position, yaw_radians, mode
+ )
+ if normalized.is_empty():
+ return false
+ var transaction: Dictionary = _capture_local_decor_transaction()
+ if transaction.is_empty():
+ return false
+ _suppress_local_submission = true
+ if not _local_home.update_placement(
+ instance_id,
+ normalized["position"],
+ float(normalized["yaw"]),
+ mode,
+ ):
+ _suppress_local_submission = false
+ return false
+ _suppress_local_submission = false
+ if not _commit_local_decor_transaction(transaction):
+ return false
+ return true
+
+
+func get_valid_local_decor_transform(
+ instance_id: String,
+ position: Vector3,
+ yaw_radians: float,
+ mode: PlayerHomeStateType.PlacementMode,
+) -> Dictionary:
+ if not can_local_decorate() or _world_service == null:
+ return {}
+ var placement: Dictionary = _local_home.get_placement(instance_id)
+ if placement.is_empty():
+ return {}
+ var decor_id := StringName(str(placement.get("decor_id", "")))
+ return _local_home.get_valid_update_transform(
+ instance_id,
+ position,
+ yaw_radians,
+ mode,
+ Callable(
+ self, "_candidate_clear_of_players"
+ ).bind(
+ _session.get_local_identity_fingerprint(), decor_id
+ ),
+ )
+
+
+func _candidate_clear_of_players(
+ candidate: Dictionary,
+ owner_fingerprint: String,
+ decor_id: StringName,
+) -> bool:
+ return (
+ _world_service != null
+ and _world_service.is_decor_placement_clear_of_players(
+ owner_fingerprint,
+ decor_id,
+ candidate["position"],
+ float(candidate["yaw"]),
+ )
+ )
+
+
+func request_enter(owner_fingerprint: String) -> bool:
+ if (
+ _session == null
+ or not _session.is_gameplay_session_active()
+ or not _manifests_by_fingerprint.has(owner_fingerprint)
+ or get_local_space() != NetworkHomeProtocolType.WORLD_SPACE
+ ):
+ return false
+ return _submit_transition(
+ NetworkHomeProtocolType.TransitionAction.ENTER,
+ owner_fingerprint,
+ )
+
+
+func request_exit() -> bool:
+ var owner_fingerprint: String = get_local_home_owner_fingerprint()
+ if owner_fingerprint.is_empty():
+ return false
+ return _submit_transition(
+ NetworkHomeProtocolType.TransitionAction.EXIT,
+ owner_fingerprint,
+ )
+
+
+func authorize_decor_purchase(
+ peer_id: int,
+ product_id: StringName,
+ resulting_count: int,
+ result_id: String,
+) -> void:
+ if not _session.is_host() or peer_id <= 0 or result_id.is_empty():
+ return
+ var counts: Dictionary = _authorized_owned_counts.get(peer_id, {})
+ counts[String(product_id)] = resulting_count
+ _authorized_owned_counts[peer_id] = counts
+ _authorization_result_owner[result_id] = peer_id
+
+
+func finalize_decor_purchase(
+ peer_id: int,
+ result_id: String,
+ applied: bool,
+) -> void:
+ if _authorization_result_owner.get(result_id, 0) != peer_id:
+ return
+ _authorization_result_owner.erase(result_id)
+ if applied:
+ return
+ _authorized_owned_counts.erase(peer_id)
+
+
+func authorize_home_upgrade(
+ peer_id: int,
+ resulting_tier: int,
+ result_id: String,
+) -> void:
+ if (
+ not _session.is_host()
+ or peer_id <= 0
+ or result_id.is_empty()
+ or resulting_tier < PlayerHomeStateType.STARTER_TIER
+ or resulting_tier > PlayerHomeStateType.MAX_TIER
+ ):
+ return
+ _authorized_home_tiers[peer_id] = resulting_tier
+ _home_upgrade_result_owner[result_id] = peer_id
+
+
+func finalize_home_upgrade(
+ peer_id: int,
+ result_id: String,
+ applied: bool,
+) -> void:
+ if _home_upgrade_result_owner.get(result_id, 0) != peer_id:
+ return
+ _home_upgrade_result_owner.erase(result_id)
+ if not applied:
+ _authorized_home_tiers.erase(peer_id)
+
+
+func refresh_world_presentations() -> void:
+ if _world_service == null:
+ return
+ _world_service.clear_presentations()
+ if not _world_service.is_world_ready():
+ return
+ if not _session.is_host():
+ for manifest: Dictionary in _manifests_by_fingerprint.values():
+ _world_service.apply_manifest(manifest)
+ return
+ var local_fingerprint: String = _session.get_local_identity_fingerprint()
+ if (
+ not _session.is_dedicated_host()
+ and not _manifests_by_fingerprint.has(local_fingerprint)
+ ):
+ publish_local_home()
+ var fingerprints: Array[String] = []
+ fingerprints.assign(_manifests_by_fingerprint.keys())
+ fingerprints.sort()
+ var reserved: Array[Transform3D] = []
+ for fingerprint: String in fingerprints:
+ var manifest: Dictionary = _manifests_by_fingerprint[fingerprint]
+ var transform: Transform3D = _world_service.choose_exterior_transform(
+ int(manifest["owner_peer_id"]), reserved
+ )
+ reserved.append(transform)
+ manifest["exterior_transform"] = (
+ NetworkHomeProtocolType.transform_to_array(transform)
+ )
+ _manifests_by_fingerprint[fingerprint] = manifest
+ _broadcast_manifest(manifest)
+ for fingerprint: String in fingerprints:
+ var manifest: Dictionary = _manifests_by_fingerprint[fingerprint]
+ _spawn_peer_at_exterior(
+ int(manifest["owner_peer_id"]), fingerprint
+ )
+
+
+func publish_local_home(
+ allow_pending_decor_transaction: bool = false,
+) -> bool:
+ if (
+ _session == null
+ or not _session.is_gameplay_session_active()
+ or _session.is_dedicated_host()
+ or _local_home == null
+ ):
+ return false
+ # A purchase can update the owned decor count while an optimistic placement
+ # is waiting on the host. Hold that follow-up manifest until the placement is
+ # accepted or rolled back; otherwise it can carry the still-unverified
+ # placement and turn one rejection into a second stale rejection.
+ if (
+ not allow_pending_decor_transaction
+ and has_pending_local_decor_mutation()
+ ):
+ _publish_queued = true
+ return true
+ if _session.is_host() and (
+ _world_service == null or not _world_service.is_world_ready()
+ ):
+ return false
+ var home_data: Dictionary = _local_home.to_save_data()
+ var revision: int = int(home_data.get("revision", -1))
+ if revision >= 0 and revision == _last_submitted_local_home_revision:
+ return true
+ var request: Dictionary = {
+ "request_id": _new_id("home"),
+ "session_id": _session.get_session_id(),
+ "owner_fingerprint": _session.get_local_identity_fingerprint(),
+ "home": home_data,
+ "digest": NetworkHomeProtocolType.manifest_digest(home_data),
+ }
+ request["sender_signature"] = _session.sign_local_action(
+ "portable_home_manifest",
+ NetworkHomeProtocolType.manifest_signature_fields(request),
+ )
+ _last_submitted_local_home_revision = revision
+ if _session.is_host():
+ _process_manifest_request(_session.get_local_peer_id(), request)
+ else:
+ submit_home_manifest.rpc_id(1, request)
+ return true
+
+
+@rpc(
+ "any_peer", "call_remote", "reliable",
+ NetworkHomeProtocolType.RELIABLE_CHANNEL,
+)
+func submit_home_manifest(data: Dictionary) -> void:
+ var sender_id: int = multiplayer.get_remote_sender_id()
+ if (
+ _session == null
+ or not _session.is_host()
+ or not _session.is_authenticated_peer(sender_id)
+ ):
+ return
+ _process_manifest_request(sender_id, data)
+
+
+func _process_manifest_request(peer_id: int, data: Dictionary) -> void:
+ if (
+ not NetworkHomeProtocolType.validate_manifest_request(data)
+ or str(data["session_id"]) != _session.get_session_id()
+ ):
+ _reject_manifest(peer_id, "The home update was invalid.")
+ return
+ var record := _session.get_peer_record(peer_id)
+ var fingerprint: String = str(data["owner_fingerprint"])
+ if (
+ record == null
+ or record.identity_fingerprint != fingerprint
+ or not _session.verify_peer_action(
+ peer_id,
+ "portable_home_manifest",
+ NetworkHomeProtocolType.manifest_signature_fields(data),
+ data["sender_signature"],
+ )
+ ):
+ _reject_manifest(peer_id, "The home owner could not be verified.")
+ return
+ var home_data: Dictionary = PlayerHomeStateType.sanitize_save_data(
+ data["home"]
+ )
+ var prior: Dictionary = _manifests_by_fingerprint.get(fingerprint, {})
+ var is_first_manifest: bool = prior.is_empty()
+ if not _valid_manifest_revision(peer_id, prior, home_data):
+ _reject_manifest(peer_id, "The home update was rejected by the host.")
+ return
+ if not _moved_decor_clear_of_players(fingerprint, prior, home_data):
+ _reject_manifest(peer_id, "A player is in the way of that decor.")
+ return
+ var manifest: Dictionary
+ if prior.is_empty():
+ if _manifests_by_fingerprint.size() >= MAX_MANIFESTS:
+ _reject_manifest(peer_id, "This room cannot place another home.")
+ return
+ var reserved: Array[Transform3D] = []
+ for existing: Dictionary in _manifests_by_fingerprint.values():
+ reserved.append(NetworkHomeProtocolType.array_to_transform(
+ existing["exterior_transform"]
+ ))
+ var exterior_transform: Transform3D = (
+ _world_service.choose_exterior_transform(peer_id, reserved)
+ )
+ manifest = {
+ "owner_peer_id": peer_id,
+ "owner_fingerprint": fingerprint,
+ "home": home_data,
+ "exterior_transform": (
+ NetworkHomeProtocolType.transform_to_array(exterior_transform)
+ ),
+ "interior_index": _next_interior_index,
+ }
+ _next_interior_index += 1
+ else:
+ manifest = prior.duplicate(true)
+ manifest["home"] = home_data
+ manifest["owner_peer_id"] = peer_id
+ _manifests_by_fingerprint[fingerprint] = manifest
+ _fingerprint_by_peer[peer_id] = fingerprint
+ _consume_owned_authorizations(peer_id, home_data)
+ if peer_id == _session.get_local_peer_id():
+ _discard_local_decor_transactions_through(
+ int(home_data.get("revision", -1))
+ )
+ if _last_submitted_local_home_revision == int(
+ home_data.get("revision", -1)
+ ):
+ _last_submitted_local_home_revision = -1
+ _flush_queued_local_home_publish()
+ _broadcast_manifest(manifest)
+ if is_first_manifest:
+ _spawn_peer_at_exterior(peer_id, fingerprint)
+
+
+func _valid_manifest_revision(
+ peer_id: int,
+ prior_manifest: Dictionary,
+ home_data: Dictionary,
+) -> bool:
+ if prior_manifest.is_empty():
+ # A portable RV is owned by the player's signed local save. A new host has
+ # no prior room ledger with which to compare its first manifest.
+ return true
+ var prior_home: Dictionary = prior_manifest["home"]
+ var prior_tier: int = int(prior_home["tier"])
+ var next_tier: int = int(home_data["tier"])
+ var tier_is_authorized: bool = (
+ next_tier != prior_tier
+ and int(_authorized_home_tiers.get(peer_id, -1)) == next_tier
+ )
+ if (
+ int(home_data["revision"]) <= int(prior_home["revision"])
+ or (next_tier != prior_tier and not tier_is_authorized)
+ or str(home_data["exterior_id"])
+ != str(PlayerHomeStateType.exterior_id_for_tier(next_tier))
+ ):
+ return false
+ var prior_owned: Dictionary = prior_home["owned_decor"]
+ var next_owned: Dictionary = home_data["owned_decor"]
+ var all_ids: Dictionary[String, bool] = {}
+ for value: Variant in prior_owned.keys():
+ all_ids[str(value)] = true
+ for value: Variant in next_owned.keys():
+ all_ids[str(value)] = true
+ var authorized: Dictionary = _authorized_owned_counts.get(peer_id, {})
+ for product_id: String in all_ids:
+ var prior_count: int = int(prior_owned.get(product_id, 0))
+ var next_count: int = int(next_owned.get(product_id, 0))
+ if prior_count == next_count:
+ continue
+ if next_count < prior_count or int(authorized.get(product_id, -1)) != next_count:
+ return false
+ return true
+
+
+func _moved_decor_clear_of_players(
+ owner_fingerprint: String,
+ prior_manifest: Dictionary,
+ home_data: Dictionary,
+) -> bool:
+ if _world_service == null:
+ return false
+ var prior_by_instance: Dictionary[String, Dictionary] = {}
+ if not prior_manifest.is_empty():
+ var prior_home: Dictionary = prior_manifest.get("home", {})
+ for value: Variant in prior_home.get("placements", []):
+ if typeof(value) != TYPE_DICTIONARY:
+ continue
+ var prior_placement: Dictionary = value
+ prior_by_instance[str(
+ prior_placement.get("instance_id", "")
+ )] = prior_placement
+ for value: Variant in home_data.get("placements", []):
+ if typeof(value) != TYPE_DICTIONARY:
+ return false
+ var placement: Dictionary = value
+ var instance_id: String = str(placement.get("instance_id", ""))
+ var prior_placement: Dictionary = prior_by_instance.get(
+ instance_id, {}
+ )
+ if (
+ not prior_placement.is_empty()
+ and _same_decor_transform(prior_placement, placement)
+ ):
+ continue
+ var decor_id := StringName(str(placement.get("decor_id", "")))
+ var position: Vector3 = _placement_position(placement)
+ if (
+ not position.is_finite()
+ or not _world_service.is_decor_placement_clear_of_players(
+ owner_fingerprint,
+ decor_id,
+ position,
+ float(placement.get("yaw", 0.0)),
+ )
+ ):
+ return false
+ return true
+
+
+static func _same_decor_transform(
+ first: Dictionary,
+ second: Dictionary,
+) -> bool:
+ if str(first.get("decor_id", "")) != str(second.get("decor_id", "")):
+ return false
+ var first_position: Vector3 = _placement_position(first)
+ var second_position: Vector3 = _placement_position(second)
+ return (
+ first_position.is_equal_approx(second_position)
+ and is_equal_approx(
+ float(first.get("yaw", 0.0)),
+ float(second.get("yaw", 0.0)),
+ )
+ )
+
+
+static func _placement_position(placement: Dictionary) -> Vector3:
+ var values: Array = placement.get("position", [])
+ if values.size() != 3:
+ return Vector3.INF
+ return Vector3(
+ float(values[0]), float(values[1]), float(values[2])
+ )
+
+
+func _consume_owned_authorizations(peer_id: int, home_data: Dictionary) -> void:
+ if (
+ _authorized_home_tiers.has(peer_id)
+ and int(home_data["tier"]) == int(_authorized_home_tiers[peer_id])
+ ):
+ _authorized_home_tiers.erase(peer_id)
+ var authorized: Dictionary = _authorized_owned_counts.get(peer_id, {})
+ if authorized.is_empty():
+ return
+ var owned: Dictionary = home_data["owned_decor"]
+ for product_id: String in authorized.keys():
+ if int(owned.get(product_id, -1)) == int(authorized[product_id]):
+ authorized.erase(product_id)
+ if authorized.is_empty():
+ _authorized_owned_counts.erase(peer_id)
+ else:
+ _authorized_owned_counts[peer_id] = authorized
+
+
+func _broadcast_manifest(manifest: Dictionary) -> void:
+ if _world_service != null and _world_service.is_world_ready():
+ _world_service.apply_manifest(manifest)
+ for peer_id: int in _session.get_authenticated_peer_ids():
+ if peer_id == 1:
+ continue
+ receive_home_manifest.rpc_id(peer_id, manifest)
+
+
+@rpc(
+ "authority", "call_remote", "reliable",
+ NetworkHomeProtocolType.RELIABLE_CHANNEL,
+)
+func receive_home_manifest(data: Dictionary) -> void:
+ if not NetworkHomeProtocolType.validate_manifest_broadcast(data):
+ return
+ var fingerprint: String = str(data["owner_fingerprint"])
+ _manifests_by_fingerprint[fingerprint] = data.duplicate(true)
+ _fingerprint_by_peer[int(data["owner_peer_id"])] = fingerprint
+ if fingerprint == _session.get_local_identity_fingerprint():
+ var home: Dictionary = data.get("home", {})
+ _discard_local_decor_transactions_through(
+ int(home.get("revision", -1))
+ )
+ if _last_submitted_local_home_revision == int(
+ home.get("revision", -1)
+ ):
+ _last_submitted_local_home_revision = -1
+ _flush_queued_local_home_publish()
+ if _world_service != null and _world_service.is_world_ready():
+ _world_service.apply_manifest(data)
+
+
+func _reject_manifest(peer_id: int, message: String) -> void:
+ var fingerprint: String = _fingerprint_for_peer(peer_id)
+ var canonical: Dictionary = _manifests_by_fingerprint.get(fingerprint, {})
+ if peer_id == _session.get_local_peer_id():
+ _apply_manifest_rejection(message, canonical)
+ else:
+ receive_home_manifest_rejection.rpc_id(peer_id, message, canonical)
+
+
+@rpc(
+ "authority", "call_remote", "reliable",
+ NetworkHomeProtocolType.RELIABLE_CHANNEL,
+)
+func receive_home_manifest_rejection(
+ message: String,
+ canonical: Dictionary,
+) -> void:
+ _apply_manifest_rejection(message, canonical)
+
+
+func _apply_manifest_rejection(message: String, canonical: Dictionary) -> void:
+ _last_submitted_local_home_revision = -1
+ var canonical_home: Dictionary = canonical.get("home", {})
+ var canonical_revision: int = int(canonical_home.get("revision", -1))
+ var transaction: Dictionary = _get_local_decor_rejection_transaction(
+ canonical_revision
+ )
+ if canonical.is_empty() or not NetworkHomeProtocolType.validate_manifest_broadcast(canonical):
+ if not transaction.is_empty():
+ _restore_local_decor_transaction(transaction)
+ _pending_local_decor_transactions.clear()
+ if _save_manager != null and not _save_manager.save_if_dirty():
+ push_warning("Unable to save the restored RV decor state.")
+ _flush_queued_local_home_publish()
+ local_manifest_rejected.emit(message)
+ return
+ # A shop result can be accepted locally while its own home manifest is still
+ # in flight. A delayed rejection for an earlier placement must not erase that
+ # accepted purchase just because the host's canonical manifest predates it.
+ var locally_owned_decor: Dictionary[StringName, int] = (
+ _capture_local_owned_decor_counts()
+ )
+ # Only carry ownership forward across an actual pending decor-action
+ # rejection. A standalone rejected manifest must remain authoritative rather
+ # than repeatedly re-submitting an unverified owned-count increase.
+ var has_owned_decor_increase: bool = (
+ not transaction.is_empty()
+ and _has_local_owned_decor_increase(
+ locally_owned_decor,
+ canonical_home,
+ )
+ )
+ _suppress_local_submission = true
+ var restored: bool = _local_home.restore_from_save_data(canonical_home)
+ var owned_decor_restored: bool = (
+ restored
+ and (
+ not has_owned_decor_increase
+ or _restore_local_owned_decor_increases(locally_owned_decor)
+ )
+ )
+ var reconciled: bool = (
+ restored
+ and owned_decor_restored
+ and _reconcile_local_decor_inventory(transaction)
+ )
+ _suppress_local_submission = false
+ _discard_local_decor_transactions_after(canonical_revision)
+ if not reconciled:
+ push_error("Unable to reconcile RV decor after a host rejection.")
+ elif _save_manager != null and not _save_manager.save_if_dirty():
+ push_warning("Unable to save the restored RV decor state.")
+ receive_home_manifest(canonical)
+ if reconciled and has_owned_decor_increase:
+ # Submit the preserved purchase as a fresh revision after applying the
+ # canonical placement rollback. This is deliberately separate from the
+ # rejected action, so the host can validate the already-authorized count.
+ if not publish_local_home():
+ push_warning("Unable to publish the restored RV decor ownership.")
+ local_manifest_rejected.emit(message)
+
+
+func _capture_local_decor_transaction() -> Dictionary:
+ if (
+ _local_home == null
+ or _bag == null
+ or _inventory_layout == null
+ or _hotbar == null
+ ):
+ return {}
+ var saved_items: Array[OwnedItemType] = []
+ for item: OwnedItemType in _bag.get_all_items():
+ saved_items.append(item.duplicate_record())
+ return {
+ "home": _local_home.to_save_data(),
+ "bag_items": saved_items,
+ "layout": _inventory_layout.to_save_data(),
+ "hotbar_slots": _hotbar.get_slots(),
+ "hotbar_fish_slots": _hotbar.get_fish_slots(),
+ "selected_hotbar_slot": _hotbar.get_selected_slot(),
+ }
+
+
+func _capture_local_owned_decor_counts() -> Dictionary[StringName, int]:
+ var counts: Dictionary[StringName, int] = {}
+ if _local_home == null:
+ return counts
+ for decor_id: StringName in DecorCatalogType.get_product_ids():
+ counts[decor_id] = _local_home.get_owned_count(decor_id)
+ return counts
+
+
+func _has_local_owned_decor_increase(
+ local_counts: Dictionary[StringName, int],
+ canonical_home: Dictionary,
+) -> bool:
+ var canonical_owned: Dictionary = canonical_home.get("owned_decor", {})
+ for decor_id: StringName in DecorCatalogType.get_product_ids():
+ if int(local_counts.get(decor_id, 0)) > int(
+ canonical_owned.get(String(decor_id), 0)
+ ):
+ return true
+ return false
+
+
+func _restore_local_owned_decor_increases(
+ local_counts: Dictionary[StringName, int],
+) -> bool:
+ if _local_home == null:
+ return false
+ for decor_id: StringName in DecorCatalogType.get_product_ids():
+ var local_count: int = int(local_counts.get(decor_id, 0))
+ var canonical_count: int = _local_home.get_owned_count(decor_id)
+ while canonical_count < local_count:
+ if not _local_home.add_owned_decor(decor_id, 1):
+ return false
+ canonical_count += 1
+ return true
+
+
+func _commit_local_decor_transaction(transaction: Dictionary) -> bool:
+ if _save_manager == null or not _save_manager.save_if_dirty():
+ _restore_local_decor_transaction(transaction)
+ return false
+ var revision: int = _local_home.get_revision()
+ _pending_local_decor_transactions[revision] = transaction.duplicate(true)
+ _trim_pending_local_decor_transactions()
+ if publish_local_home(true):
+ return true
+ _pending_local_decor_transactions.erase(revision)
+ _restore_local_decor_transaction(transaction)
+ _flush_queued_local_home_publish()
+ # The optimistic mutation was already written before we attempted to publish
+ # it. If the session ended or could not accept the manifest, immediately
+ # persist the restored state so a restart cannot resurrect the rejected
+ # placement or consume its decor item.
+ if _save_manager != null and not _save_manager.save_if_dirty():
+ push_warning("Unable to save the restored RV decor state.")
+ return false
+
+
+func _restore_local_decor_transaction(transaction: Dictionary) -> bool:
+ if (
+ transaction.is_empty()
+ or _local_home == null
+ or _bag == null
+ or _inventory_layout == null
+ or _hotbar == null
+ ):
+ return false
+ var home_data: Variant = transaction.get("home", {})
+ var layout_data: Variant = transaction.get("layout", {})
+ var stored_items: Variant = transaction.get("bag_items", [])
+ var stored_slots: Variant = transaction.get("hotbar_slots", [])
+ var stored_fish_slots: Variant = transaction.get("hotbar_fish_slots", [])
+ if (
+ typeof(home_data) != TYPE_DICTIONARY
+ or typeof(layout_data) != TYPE_DICTIONARY
+ or typeof(stored_items) != TYPE_ARRAY
+ or typeof(stored_slots) != TYPE_ARRAY
+ or typeof(stored_fish_slots) != TYPE_ARRAY
+ ):
+ return false
+ var items: Array[OwnedItemType] = []
+ for value: Variant in stored_items as Array:
+ if not value is OwnedItemType:
+ return false
+ items.append((value as OwnedItemType).duplicate_record())
+ var slots: Array[StringName] = []
+ for value: Variant in stored_slots as Array:
+ slots.append(StringName(str(value)))
+ var fish_slots: Array[StringName] = []
+ for value: Variant in stored_fish_slots as Array:
+ fish_slots.append(StringName(str(value)))
+ var was_suppressed: bool = _suppress_local_submission
+ _suppress_local_submission = true
+ var home_restored: bool = _local_home.restore_from_save_data(
+ home_data as Dictionary
+ )
+ var bag_restored: bool = _bag.replace_all_items(items)
+ var layout_restored: bool = _inventory_layout.restore_from_save_data(
+ layout_data as Dictionary
+ )
+ var hotbar_restored: bool = _hotbar.replace_state(
+ slots,
+ int(transaction.get("selected_hotbar_slot", 0)),
+ fish_slots,
+ false,
+ )
+ _suppress_local_submission = was_suppressed
+ return home_restored and bag_restored and layout_restored and hotbar_restored
+
+
+func _reconcile_local_decor_inventory(transaction: Dictionary) -> bool:
+ if _bag == null or _local_home == null:
+ return false
+ var preferred_slots: Dictionary[StringName, int] = (
+ _decor_hotbar_slots_from_transaction(transaction)
+ )
+ for decor_id: StringName in DecorCatalogType.get_product_ids():
+ var target_quantity: int = _local_home.get_available_count(decor_id)
+ var current_quantity: int = _bag.get_quantity(decor_id)
+ if current_quantity > target_quantity:
+ if not _bag.remove_item(
+ decor_id, current_quantity - target_quantity
+ ):
+ return false
+ continue
+ while current_quantity < target_quantity:
+ var preferred_slot: int = int(
+ preferred_slots.get(decor_id, -1)
+ )
+ if not _restore_decor_item_to_bag(decor_id, preferred_slot):
+ return false
+ current_quantity += 1
+ return true
+
+
+func _decor_hotbar_slots_from_transaction(
+ transaction: Dictionary,
+) -> Dictionary[StringName, int]:
+ var result: Dictionary[StringName, int] = {}
+ var stored_slots: Variant = transaction.get("hotbar_slots", [])
+ if typeof(stored_slots) != TYPE_ARRAY:
+ return result
+ for slot_index: int in range((stored_slots as Array).size()):
+ var decor_id := StringName(str((stored_slots as Array)[slot_index]))
+ if DecorCatalogType.has_product(decor_id):
+ result[decor_id] = slot_index
+ return result
+
+
+func _restore_decor_item_to_bag(
+ decor_id: StringName,
+ preferred_hotbar_slot: int,
+) -> bool:
+ if _bag == null:
+ return false
+ var prepared_slot: int = -1
+ if (
+ _inventory_layout != null
+ and _hotbar != null
+ and _inventory_layout.get_container(
+ PlayerInventoryLayout.EntryKind.ITEM, decor_id
+ ) < 0
+ ):
+ var candidate_slot: int = preferred_hotbar_slot
+ if candidate_slot < 0:
+ candidate_slot = _hotbar.find_first_empty_slot()
+ if candidate_slot >= 0 and _inventory_layout.prepare_new_item_placement(
+ decor_id,
+ PlayerInventoryLayout.InventoryContainer.HOTBAR,
+ candidate_slot,
+ ):
+ prepared_slot = candidate_slot
+ var restored: bool = _bag.add_item(decor_id, 1)
+ if not restored:
+ if prepared_slot >= 0:
+ _inventory_layout.cancel_prepared_item_placement(decor_id)
+ if _bag.get_owned_item(decor_id) == null:
+ restored = _bag.add_item_to_storage(decor_id, 1)
+ if restored and prepared_slot >= 0:
+ _hotbar.assign_item(prepared_slot, decor_id)
+ _hotbar.select_slot(prepared_slot)
+ return restored
+
+
+func _get_local_decor_rejection_transaction(
+ canonical_revision: int,
+) -> Dictionary:
+ var revisions: Array[int] = []
+ revisions.assign(_pending_local_decor_transactions.keys())
+ revisions.sort()
+ for revision: int in revisions:
+ if canonical_revision < 0 or revision > canonical_revision:
+ return _pending_local_decor_transactions[revision].duplicate(true)
+ return {}
+
+
+func _discard_local_decor_transactions_through(revision: int) -> void:
+ for pending_revision: int in _pending_local_decor_transactions.keys():
+ if pending_revision <= revision:
+ _pending_local_decor_transactions.erase(pending_revision)
+
+
+func _discard_local_decor_transactions_after(revision: int) -> void:
+ for pending_revision: int in _pending_local_decor_transactions.keys():
+ if pending_revision > revision:
+ _pending_local_decor_transactions.erase(pending_revision)
+
+
+func _trim_pending_local_decor_transactions() -> void:
+ var revisions: Array[int] = []
+ revisions.assign(_pending_local_decor_transactions.keys())
+ revisions.sort()
+ while revisions.size() > MAX_PENDING_LOCAL_DECOR_TRANSACTIONS:
+ _pending_local_decor_transactions.erase(revisions.pop_front())
+
+
+func _submit_transition(action: int, owner_fingerprint: String) -> bool:
+ var request: Dictionary = {
+ "request_id": _new_id("home_transition"),
+ "session_id": _session.get_session_id(),
+ "action": action,
+ "owner_fingerprint": owner_fingerprint,
+ }
+ request["sender_signature"] = _session.sign_local_action(
+ "portable_home_transition",
+ NetworkHomeProtocolType.transition_signature_fields(request),
+ )
+ if _session.is_host():
+ _process_transition_request(_session.get_local_peer_id(), request)
+ else:
+ submit_home_transition.rpc_id(1, request)
+ return true
+
+
+@rpc(
+ "any_peer", "call_remote", "reliable",
+ NetworkHomeProtocolType.RELIABLE_CHANNEL,
+)
+func submit_home_transition(data: Dictionary) -> void:
+ var sender_id: int = multiplayer.get_remote_sender_id()
+ if (
+ not _session.is_host()
+ or not _session.is_authenticated_peer(sender_id)
+ ):
+ return
+ _process_transition_request(sender_id, data)
+
+
+func _process_transition_request(peer_id: int, data: Dictionary) -> void:
+ if (
+ not NetworkHomeProtocolType.validate_transition_request(data)
+ or str(data["session_id"]) != _session.get_session_id()
+ or not _session.verify_peer_action(
+ peer_id,
+ "portable_home_transition",
+ NetworkHomeProtocolType.transition_signature_fields(data),
+ data["sender_signature"],
+ )
+ ):
+ _send_transition_rejection(peer_id, "The home transition was invalid.")
+ return
+ var owner_fingerprint: String = str(data["owner_fingerprint"])
+ var action: int = int(data["action"])
+ var avatar: Player = _spawn_service.get_avatar(peer_id)
+ var manifest: Dictionary = _manifests_by_fingerprint.get(
+ owner_fingerprint, {}
+ )
+ if avatar == null or manifest.is_empty():
+ _send_transition_rejection(peer_id, "That home is unavailable.")
+ return
+ if action == NetworkHomeProtocolType.TransitionAction.ENTER:
+ var record := _session.get_peer_record(peer_id)
+ var owner_is_local: bool = (
+ record != null and record.identity_fingerprint == owner_fingerprint
+ )
+ var privacy: int = int((manifest["home"] as Dictionary)["privacy"])
+ if (
+ (not owner_is_local and privacy != PlayerHomeStateType.Privacy.OPEN)
+ or _session.get_peer_space(peer_id)
+ != NetworkHomeProtocolType.WORLD_SPACE
+ or not _world_service.is_avatar_near_exterior(
+ avatar, owner_fingerprint
+ )
+ ):
+ _send_transition_rejection(peer_id, "You cannot enter that home.")
+ return
+ var interior_space: StringName = (
+ NetworkHomeProtocolType.make_space_id(owner_fingerprint)
+ )
+ var visitor_slot: int = _reserve_interior_slot(
+ interior_space, peer_id
+ )
+ _apply_host_transition(
+ peer_id,
+ interior_space,
+ owner_fingerprint,
+ _world_service.get_interior_spawn_transform(
+ owner_fingerprint, visitor_slot
+ ),
+ )
+ return
+ if (
+ _session.get_peer_space(peer_id)
+ != NetworkHomeProtocolType.make_space_id(owner_fingerprint)
+ or not _world_service.is_avatar_near_interior_exit(
+ avatar, owner_fingerprint
+ )
+ ):
+ _send_transition_rejection(peer_id, "Move closer to the home door.")
+ return
+ _apply_host_transition(
+ peer_id,
+ NetworkHomeProtocolType.WORLD_SPACE,
+ owner_fingerprint,
+ _world_service.get_exterior_spawn_transform(owner_fingerprint),
+ )
+
+
+func _spawn_peer_at_exterior(
+ peer_id: int,
+ owner_fingerprint: String,
+) -> void:
+ if (
+ not _session.is_host()
+ or _session.get_peer_space(peer_id)
+ != NetworkHomeProtocolType.WORLD_SPACE
+ or _spawn_service.get_avatar(peer_id) == null
+ ):
+ return
+ _apply_host_transition(
+ peer_id,
+ NetworkHomeProtocolType.WORLD_SPACE,
+ owner_fingerprint,
+ _world_service.get_exterior_spawn_transform(owner_fingerprint),
+ )
+
+
+func _apply_host_transition(
+ peer_id: int,
+ space_id: StringName,
+ owner_fingerprint: String,
+ transform: Transform3D,
+) -> void:
+ var avatar: Player = _spawn_service.get_avatar(peer_id)
+ var movement_sequence: int = 0
+ if avatar != null:
+ movement_sequence = int(
+ avatar.make_network_snapshot(peer_id).get(
+ "acknowledged_input", 0
+ )
+ )
+ var transform_data: Array[float] = (
+ NetworkHomeProtocolType.transform_to_array(transform)
+ )
+ if not NetworkHomeProtocolType.validate_transition_transform(
+ transform_data
+ ):
+ push_error("Refused an out-of-bounds RV transition transform.")
+ return
+ var previous_space: StringName = _session.get_peer_space(peer_id)
+ if (
+ String(previous_space).begins_with("rv:")
+ and previous_space != space_id
+ ):
+ _release_interior_slot(previous_space, peer_id)
+ var result: Dictionary = {
+ "peer_id": peer_id,
+ "space_id": String(space_id),
+ "owner_fingerprint": owner_fingerprint,
+ "transform": transform_data,
+ "movement_sequence": movement_sequence,
+ }
+ _apply_transition_result(result)
+ for recipient_id: int in _session.get_authenticated_peer_ids():
+ if recipient_id == 1:
+ continue
+ receive_home_transition.rpc_id(recipient_id, result)
+
+
+@rpc(
+ "authority", "call_remote", "reliable",
+ NetworkHomeProtocolType.RELIABLE_CHANNEL,
+)
+func receive_home_transition(data: Dictionary) -> void:
+ if (
+ typeof(data.get("peer_id")) != TYPE_INT
+ or typeof(data.get("space_id")) not in [TYPE_STRING, TYPE_STRING_NAME]
+ or typeof(data.get("movement_sequence", 0)) != TYPE_INT
+ or int(data.get("movement_sequence", 0)) < 0
+ or not NetworkIdentityCrypto.valid_fingerprint(
+ data.get("owner_fingerprint")
+ )
+ or not NetworkHomeProtocolType.validate_transition_transform(
+ data.get("transform")
+ )
+ ):
+ return
+ _apply_transition_result(data)
+
+
+func _apply_transition_result(data: Dictionary) -> void:
+ var peer_id: int = int(data["peer_id"])
+ var space_id := StringName(str(data["space_id"]))
+ if not NetworkHomeProtocolType.validate_transition_transform(
+ data.get("transform")
+ ):
+ return
+ var target_transform: Transform3D = (
+ NetworkHomeProtocolType.array_to_transform(data["transform"])
+ )
+ if not _session.set_peer_space(peer_id, space_id):
+ return
+ if not _session.apply_authoritative_space_transition(
+ peer_id,
+ target_transform,
+ int(data.get("movement_sequence", 0)),
+ ):
+ return
+ if peer_id == _session.get_local_peer_id():
+ _world_service.set_local_space(space_id)
+ local_space_changed.emit(space_id, str(data["owner_fingerprint"]))
+ local_transition_finished.emit(true, "")
+ _refresh_peer_visibility()
+
+
+func _send_transition_rejection(peer_id: int, message: String) -> void:
+ if peer_id == _session.get_local_peer_id():
+ local_transition_finished.emit(false, message)
+ else:
+ receive_home_transition_rejection.rpc_id(peer_id, message)
+
+
+@rpc(
+ "authority", "call_remote", "reliable",
+ NetworkHomeProtocolType.RELIABLE_CHANNEL,
+)
+func receive_home_transition_rejection(message: String) -> void:
+ local_transition_finished.emit(
+ false, message.left(NetworkHomeProtocolType.MAX_MESSAGE_LENGTH)
+ )
+
+
+func _refresh_peer_visibility() -> void:
+ var local_peer_id: int = _session.get_local_peer_id()
+ if local_peer_id <= 0:
+ return
+ for peer_id: int in _spawn_service.get_peer_ids():
+ if peer_id == local_peer_id:
+ continue
+ _spawn_service.set_peer_presentation_visible(
+ peer_id, _session.peers_share_space(local_peer_id, peer_id)
+ )
+
+
+func _on_local_home_changed() -> void:
+ if _suppress_local_submission or _publish_queued:
+ return
+ _publish_queued = true
+ call_deferred("_publish_queued_home")
+
+
+func _publish_queued_home() -> void:
+ if has_pending_local_decor_mutation():
+ return
+ _publish_queued = false
+ if _save_manager != null:
+ _save_manager.save_if_dirty()
+ publish_local_home()
+
+
+func _flush_queued_local_home_publish() -> void:
+ if not _publish_queued or has_pending_local_decor_mutation():
+ return
+ call_deferred("_publish_queued_home")
+
+
+func _on_join_authenticated() -> void:
+ publish_local_home()
+
+
+func _on_peer_authenticated(peer_id: int, _display_name: String) -> void:
+ if not _session.is_host():
+ return
+ for manifest: Dictionary in _manifests_by_fingerprint.values():
+ receive_home_manifest.rpc_id(peer_id, manifest)
+ for existing_peer_id: int in _session.get_authenticated_peer_ids():
+ if existing_peer_id == peer_id:
+ continue
+ var avatar: Player = _spawn_service.get_avatar(existing_peer_id)
+ if avatar == null:
+ continue
+ var space_id: StringName = _session.get_peer_space(existing_peer_id)
+ var owner_fingerprint: String = (
+ String(space_id).trim_prefix("rv:")
+ if String(space_id).begins_with("rv:")
+ else _fingerprint_for_peer(existing_peer_id)
+ )
+ if owner_fingerprint.is_empty():
+ continue
+ receive_home_transition.rpc_id(peer_id, {
+ "peer_id": existing_peer_id,
+ "space_id": String(space_id),
+ "owner_fingerprint": owner_fingerprint,
+ "transform": NetworkHomeProtocolType.transform_to_array(
+ avatar.global_transform
+ ),
+ })
+
+
+func _on_peer_removed(peer_id: int) -> void:
+ _release_peer_interior_slots(peer_id)
+ var fingerprint: String = _fingerprint_by_peer.get(peer_id, "")
+ _fingerprint_by_peer.erase(peer_id)
+ _authorized_owned_counts.erase(peer_id)
+ _authorized_home_tiers.erase(peer_id)
+ for result_id: String in _home_upgrade_result_owner.keys():
+ if int(_home_upgrade_result_owner[result_id]) == peer_id:
+ _home_upgrade_result_owner.erase(result_id)
+ if fingerprint.is_empty():
+ return
+ if _session.is_host():
+ for occupant_id: int in _session.get_authenticated_peer_ids():
+ if (
+ occupant_id == peer_id
+ or _session.get_peer_space(occupant_id)
+ != NetworkHomeProtocolType.make_space_id(fingerprint)
+ ):
+ continue
+ _apply_host_transition(
+ occupant_id,
+ NetworkHomeProtocolType.WORLD_SPACE,
+ fingerprint,
+ _world_service.get_exterior_spawn_transform(fingerprint),
+ )
+ _manifests_by_fingerprint.erase(fingerprint)
+ _world_service.remove_presentation(fingerprint)
+ if _session.is_host():
+ for recipient_id: int in _session.get_authenticated_peer_ids():
+ if recipient_id != 1:
+ receive_home_removed.rpc_id(recipient_id, fingerprint)
+
+
+@rpc(
+ "authority", "call_remote", "reliable",
+ NetworkHomeProtocolType.RELIABLE_CHANNEL,
+)
+func receive_home_removed(owner_fingerprint: String) -> void:
+ if not NetworkIdentityCrypto.valid_fingerprint(owner_fingerprint):
+ return
+ _manifests_by_fingerprint.erase(owner_fingerprint)
+ _world_service.remove_presentation(owner_fingerprint)
+
+
+func _on_session_state_changed(state: NetworkSession.State) -> void:
+ if state in [NetworkSession.State.PRIVATE_HOST, NetworkSession.State.OPEN_HOST]:
+ if (
+ not _session.is_dedicated_host()
+ and _world_service != null
+ and _world_service.is_world_ready()
+ ):
+ publish_local_home()
+ return
+ if state in [
+ NetworkSession.State.INACTIVE,
+ NetworkSession.State.DISCONNECTING,
+ NetworkSession.State.CONNECTION_FAILED,
+ NetworkSession.State.SERVER_LOST,
+ ]:
+ _manifests_by_fingerprint.clear()
+ _fingerprint_by_peer.clear()
+ _authorized_owned_counts.clear()
+ _authorization_result_owner.clear()
+ _authorized_home_tiers.clear()
+ _home_upgrade_result_owner.clear()
+ _publish_queued = false
+ _last_submitted_local_home_revision = -1
+ _pending_local_decor_transactions.clear()
+ _interior_slots_by_space.clear()
+ _next_interior_index = 0
+ _world_service.clear_presentations()
+ _world_service.set_local_space(NetworkHomeProtocolType.WORLD_SPACE)
+ local_space_changed.emit(NetworkHomeProtocolType.WORLD_SPACE, "")
+
+
+func _fingerprint_for_peer(peer_id: int) -> String:
+ if _fingerprint_by_peer.has(peer_id):
+ return _fingerprint_by_peer[peer_id]
+ var record := _session.get_peer_record(peer_id)
+ return record.identity_fingerprint if record != null else ""
+
+
+func _reserve_interior_slot(space_id: StringName, peer_id: int) -> int:
+ var slots: Dictionary = _interior_slots_by_space.get(space_id, {})
+ if slots.has(peer_id):
+ return int(slots[peer_id])
+ var used_slots: Dictionary[int, bool] = {}
+ for value: Variant in slots.values():
+ used_slots[int(value)] = true
+ var slot: int = 0
+ while used_slots.has(slot):
+ slot += 1
+ slots[peer_id] = slot
+ _interior_slots_by_space[space_id] = slots
+ return slot
+
+
+func _release_interior_slot(space_id: StringName, peer_id: int) -> void:
+ var slots: Dictionary = _interior_slots_by_space.get(space_id, {})
+ if slots.is_empty():
+ return
+ slots.erase(peer_id)
+ if slots.is_empty():
+ _interior_slots_by_space.erase(space_id)
+ else:
+ _interior_slots_by_space[space_id] = slots
+
+
+func _release_peer_interior_slots(peer_id: int) -> void:
+ for space_id: StringName in _interior_slots_by_space.keys():
+ _release_interior_slot(space_id, peer_id)
+
+
+func _new_id(prefix: String) -> String:
+ return "%s:%s" % [
+ prefix, Crypto.new().generate_random_bytes(16).hex_encode(),
+ ]
diff --git a/network/network_home_service.gd.uid b/network/network_home_service.gd.uid
new file mode 100644
index 0000000..be233ed
--- /dev/null
+++ b/network/network_home_service.gd.uid
@@ -0,0 +1 @@
+uid://ong8kug36ku7
diff --git a/network/network_identity_crypto.gd b/network/network_identity_crypto.gd
index 4886cca..9d4f09c 100644
--- a/network/network_identity_crypto.gd
+++ b/network/network_identity_crypto.gd
@@ -5,7 +5,8 @@ const ALGORITHM: String = "RSA-3072-SHA256"
const FINGERPRINT_LENGTH: int = 64
const MAX_PUBLIC_KEY_BYTES: int = 8192
const MAX_SIGNATURE_BYTES: int = 1024
-const DOMAIN_PREFIX: String = "NETFISHING"
+const DOMAIN_PREFIX: String = "straywild"
+const LEGACY_DOMAIN_PREFIX: String = "NETFISHING"
const IDENTITY_VERSION: String = "identity_v1"
@@ -50,8 +51,16 @@ static func secure_id(byte_count: int = 16) -> String:
static func canonical_bytes(domain: String, fields: Array) -> PackedByteArray:
+ return canonical_bytes_for_prefix(DOMAIN_PREFIX, domain, fields)
+
+
+static func canonical_bytes_for_prefix(
+ prefix: String,
+ domain: String,
+ fields: Array,
+) -> PackedByteArray:
var output := PackedByteArray()
- _append_string(output, DOMAIN_PREFIX)
+ _append_string(output, prefix)
_append_string(output, IDENTITY_VERSION)
_append_string(output, domain)
for value: Variant in fields:
@@ -117,6 +126,37 @@ static func verify_fields(
)
+static func verify_legacy_fields(
+ public_key: CryptoKey,
+ domain: String,
+ fields: Array,
+ signature: PackedByteArray,
+) -> bool:
+ if (
+ public_key == null
+ or signature.is_empty()
+ or signature.size() > MAX_SIGNATURE_BYTES
+ ):
+ return false
+ var bytes: PackedByteArray = canonical_bytes_for_prefix(
+ LEGACY_DOMAIN_PREFIX,
+ domain,
+ fields,
+ )
+ if bytes.is_empty():
+ return false
+ var context := HashingContext.new()
+ if context.start(HashingContext.HASH_SHA256) != OK:
+ return false
+ context.update(bytes)
+ return Crypto.new().verify(
+ HashingContext.HASH_SHA256,
+ context.finish(),
+ signature,
+ public_key,
+ )
+
+
static func load_public_key(public_pem: String) -> CryptoKey:
var normalized := normalize_public_pem(public_pem)
if normalized.is_empty() or normalized.to_utf8_buffer().size() > MAX_PUBLIC_KEY_BYTES:
diff --git a/network/network_item_use_service.gd b/network/network_item_use_service.gd
index f39d198..024eca9 100644
--- a/network/network_item_use_service.gd
+++ b/network/network_item_use_service.gd
@@ -1,7 +1,11 @@
class_name NetworkItemUseService
extends Node
+const FishingRodDataType = preload("res://items/fishing_rod_data.gd")
+
const ArtShopStockType = preload("res://economy/art_shop_stock.gd")
+const FishingShopStockType = preload("res://economy/fishing_shop_stock.gd")
+const DecorCatalogType = preload("res://homes/decor_catalog.gd")
const MAX_LEDGER_ENTRIES: int = 64
@@ -44,6 +48,7 @@ func setup(
_session.peer_removed.connect(_on_peer_removed)
_session.state_changed.connect(_on_session_state_changed)
_session.peer_authenticated.connect(_on_peer_authenticated)
+ _spawn_service.avatar_spawned.connect(_on_avatar_spawned)
func request_use(item_id: StringName) -> String:
@@ -128,6 +133,7 @@ func _handle_use_request(peer_id: int, data: Dictionary) -> void:
)
if error.is_empty() and (
item == null
+ or not item.is_available()
or (
item.category != ItemData.Category.CONSUMABLE
and not is_fish_finder
@@ -289,18 +295,28 @@ func get_effects_for_peer(peer_id: int) -> PlayerItemEffects:
return avatar.item_effects if avatar != null else null
+func get_equipped_item_id(peer_id: int) -> StringName:
+ var state: Dictionary = _equipped_states.get(peer_id, {})
+ if state.is_empty() or not bool(state.get("owns_item", false)):
+ return StringName()
+ return StringName(str(state.get("item_id", "")))
+
+
func submit_local_equipped(item_id: StringName, owns_item: bool) -> void:
if _session == null or not _session.is_gameplay_session_active():
return
_local_equipped_revision += 1
var item: ItemData = _catalog.get_item_by_id(item_id)
+ var can_equip: bool = (
+ item != null and item.is_available() and owns_item
+ )
var data := {
"session_id": _session.get_session_id(),
"owner_peer_id": _session.get_local_peer_id(),
- "item_id": str(item_id) if item != null and owns_item else "",
- "category": int(item.category) if item != null and owns_item else -1,
+ "item_id": str(item_id) if can_equip else "",
+ "category": int(item.category) if can_equip else -1,
"revision": _local_equipped_revision,
- "owns_item": owns_item and item != null,
+ "owns_item": can_equip,
}
if _session.is_host():
_handle_equipped_state(_session.get_local_peer_id(), data)
@@ -331,6 +347,7 @@ func _handle_equipped_state(peer_id: int, data: Dictionary) -> void:
var item: ItemData = _catalog.get_item_by_id(item_id)
if not item_id.is_empty() and (
item == null
+ or not item.is_available()
or not bool(data["owns_item"])
or int(item.category) != int(data["category"])
):
@@ -353,14 +370,35 @@ 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_fishing_rod(
+ (
+ item as FishingRodDataType
+ if (
+ item != null
+ and item.is_available()
+ and int(data["category"]) == ItemData.Category.ROD
+ and bool(data["owns_item"])
+ )
+ else null
+ ),
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"]),
)
+ avatar.set_active_catching_net(
+ item_id == FishingShopStockType.CRAB_NET_ID
+ and bool(data["owns_item"]),
+ )
+ avatar.set_active_shovel(
+ item_id == FishingShopStockType.STANDARD_SHOVEL_ID
+ and bool(data["owns_item"]),
+ )
+ avatar.set_active_decor_package(
+ DecorCatalogType.has_product(item_id)
+ and bool(data["owns_item"]),
+ )
equipped_state_changed.emit(
peer_id, StringName(str(data["item_id"])), int(data["category"])
)
@@ -404,6 +442,12 @@ func _on_peer_authenticated(peer_id: int, _display_name: String) -> void:
receive_equipped_state.rpc_id(peer_id, state)
+func _on_avatar_spawned(peer_id: int, _avatar: Player) -> void:
+ var state: Dictionary = _equipped_states.get(peer_id, {})
+ if not state.is_empty():
+ _apply_equipped(state)
+
+
func _on_peer_removed(peer_id: int) -> void:
_requests.erase(peer_id)
_pending_by_peer.erase(peer_id)
diff --git a/network/network_job_service.gd b/network/network_job_service.gd
index 7745903..ebf5a6e 100644
--- a/network/network_job_service.gd
+++ b/network/network_job_service.gd
@@ -2,11 +2,13 @@ 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:
@@ -15,13 +17,36 @@ func setup(session: NetworkSession, jobs: PlayerJobService) -> void:
_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 not _session.supports_server_capability(NetworkProtocol.JOBS_CAPABILITY):
+ if _session.supports_server_capability(NetworkProtocol.JOBS_CAPABILITY):
+ _request_remote_board()
+ else:
_jobs.clear_remote_board()
elif state in [
NetworkSession.State.INACTIVE,
@@ -69,6 +94,33 @@ func _send_board(peer_id: int) -> void:
})
+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 (
@@ -86,6 +138,7 @@ func receive_job_board(data: Dictionary) -> void:
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:
diff --git a/network/network_mail_service.gd b/network/network_mail_service.gd
index eb9fe0f..675a5aa 100644
--- a/network/network_mail_service.gd
+++ b/network/network_mail_service.gd
@@ -928,7 +928,9 @@ func _can_receive(attachment: Dictionary) -> bool:
return _wallet.can_credit(int(attachment["amount"]))
PlayerAssetReservationService.AttachmentType.FISH:
return (
- _inventory.get_all_catches().size() < _cooler_capacity.get_capacity()
+ _inventory.can_accept_catch(
+ StringName(str(attachment["catch_id"]))
+ )
and not _inventory.contains_catch_id(
StringName(str(attachment["catch_id"]))
)
diff --git a/network/network_player_animation_protocol.gd b/network/network_player_animation_protocol.gd
new file mode 100644
index 0000000..40fe1c8
--- /dev/null
+++ b/network/network_player_animation_protocol.gd
@@ -0,0 +1,102 @@
+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"
+const LOCOMOTION_SNEAKING: StringName = &"sneaking"
+
+
+static func make_state(
+ locomotion_id: StringName,
+ grounded: bool,
+ action_id: StringName = &"",
+ action_sequence: int = 0,
+ action_elapsed: float = 0.0,
+ action_paused: bool = false,
+) -> Dictionary:
+ return {
+ "format_version": FORMAT_VERSION,
+ "locomotion_id": String(locomotion_id),
+ "grounded": grounded,
+ "action": make_action_state(
+ action_id,
+ action_sequence,
+ action_elapsed,
+ action_paused,
+ ),
+ }
+
+
+static func make_action_state(
+ action_id: StringName = &"",
+ action_sequence: int = 0,
+ action_elapsed: float = 0.0,
+ paused: bool = false,
+) -> Dictionary:
+ return {
+ "id": String(action_id),
+ "sequence": action_sequence,
+ "elapsed": action_elapsed,
+ "paused": paused,
+ }
+
+
+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]
+ or typeof(action.get("paused", false)) != TYPE_BOOL
+ ):
+ 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 1a8136a..677df05 100644
--- a/network/network_player_list_service.gd
+++ b/network/network_player_list_service.gd
@@ -3,6 +3,15 @@ extends Node
signal entries_changed
signal moderation_finished(success: bool, message: String)
+signal friend_request_received(
+ request_id: String,
+ fingerprint: String,
+ display_name: String,
+)
+signal friend_action_finished(success: bool, message: String)
+
+const MAX_PENDING_FRIEND_REQUESTS := 16
+const FRIEND_REQUEST_ID_LENGTH := 32
var _session: NetworkSession
var _relationships: PlayerRelationshipStore
@@ -15,6 +24,12 @@ 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
+var _pending_outgoing_friends: Dictionary[String, Dictionary] = {}
+var _pending_incoming_friends: Dictionary[String, Dictionary] = {}
+var _host_friend_routes: Dictionary[String, Dictionary] = {}
func setup(
@@ -40,6 +55,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 +80,37 @@ 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.is_friend = _relationships.is_friend(record.identity_fingerprint)
+ entry.can_request_friend = (
+ not entry.is_local_player
+ and not entry.is_friend
+ and not entry.blocked
+ and _session.supports_server_capability(
+ NetworkProtocol.FRIENDS_CAPABILITY
+ )
+ and _session.peer_supports_capability(
+ peer_id, NetworkProtocol.FRIENDS_CAPABILITY
+ )
+ )
+ 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_clear_art = 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,16 +129,37 @@ 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_friends() -> Array[Dictionary]:
+ return _relationships.get_friends()
+
+
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(
@@ -127,14 +188,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:
@@ -149,37 +208,359 @@ func set_blocked(fingerprint: String, display_name: String, value: bool) -> bool
return _relationships.set_blocked(fingerprint, display_name, value)
-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.")
+func remove_friend(fingerprint: String, display_name: String) -> bool:
+ if fingerprint == _session.get_local_identity_fingerprint():
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.")
+ var ok := _relationships.remove_friend(fingerprint, display_name)
+ friend_action_finished.emit(
+ ok,
+ "Friend removed." if ok else "Friend could not be removed.",
+ )
return ok
+func send_friend_request(
+ peer_id: int,
+ fingerprint: String,
+ display_name: String,
+) -> bool:
+ if (
+ _pending_outgoing_friends.size() >= MAX_PENDING_FRIEND_REQUESTS
+ or fingerprint == _session.get_local_identity_fingerprint()
+ or _relationships.is_blocked(fingerprint)
+ or _relationships.is_friend(fingerprint)
+ or not _valid_friend_target(peer_id, fingerprint)
+ ):
+ friend_action_finished.emit(
+ false, "This person needs to be online to do this."
+ )
+ return false
+ var local_capabilities: Dictionary = (
+ _relationships.create_friend_capabilities()
+ )
+ if local_capabilities.is_empty():
+ friend_action_finished.emit(false, "Friend request could not be created.")
+ return false
+ var request_id := NetworkIdentityCrypto.secure_id(16)
+ _pending_outgoing_friends[request_id] = {
+ "target_peer_id": peer_id,
+ "target_fingerprint": fingerprint,
+ "target_display_name": display_name,
+ "local_capabilities": local_capabilities,
+ }
+ var public_capabilities := _public_friend_capabilities(local_capabilities)
+ if _session.is_host():
+ _route_friend_request(
+ _session.get_local_peer_id(), peer_id, request_id, public_capabilities
+ )
+ else:
+ request_friendship.rpc_id(
+ 1, peer_id, request_id, public_capabilities
+ )
+ friend_action_finished.emit(true, "Friend request sent.")
+ return true
+
+
+func respond_friend_request(request_id: String, accepted: bool) -> bool:
+ var pending: Dictionary = _pending_incoming_friends.get(request_id, {})
+ if pending.is_empty():
+ friend_action_finished.emit(
+ false, "This friend request is no longer available."
+ )
+ return false
+ _pending_incoming_friends.erase(request_id)
+ var local_capabilities: Dictionary = {}
+ var public_capabilities: Dictionary = {}
+ var response_accepted := accepted
+ if accepted:
+ local_capabilities = _relationships.create_friend_capabilities()
+ response_accepted = (
+ not local_capabilities.is_empty()
+ and _relationships.add_friend(
+ str(pending["requester_fingerprint"]),
+ str(pending["requester_display_name"]),
+ local_capabilities,
+ pending["remote_capabilities"],
+ )
+ )
+ if response_accepted:
+ public_capabilities = _public_friend_capabilities(local_capabilities)
+ if _session.is_host():
+ _route_friend_response(
+ _session.get_local_peer_id(),
+ request_id,
+ response_accepted,
+ public_capabilities,
+ )
+ else:
+ respond_friendship.rpc_id(
+ 1, request_id, response_accepted, public_capabilities
+ )
+ friend_action_finished.emit(
+ response_accepted or not accepted,
+ "Friend added."
+ if response_accepted
+ else "Friend request declined."
+ if not accepted
+ else "Friend could not be saved.",
+ )
+ return response_accepted or not accepted
+
+
+@rpc("any_peer", "call_remote", "reliable", 0)
+func request_friendship(
+ target_peer_id: int,
+ request_id: String,
+ public_capabilities: Dictionary,
+) -> void:
+ _route_friend_request(
+ multiplayer.get_remote_sender_id(),
+ target_peer_id,
+ request_id,
+ public_capabilities,
+ )
+
+
+@rpc("authority", "call_remote", "reliable", 0)
+func receive_friend_request(
+ request_id: String,
+ requester_fingerprint: String,
+ requester_display_name: String,
+ public_capabilities: Dictionary,
+) -> void:
+ _receive_friend_request_local(
+ request_id,
+ requester_fingerprint,
+ requester_display_name,
+ public_capabilities,
+ )
+
+
+@rpc("any_peer", "call_remote", "reliable", 0)
+func respond_friendship(
+ request_id: String,
+ accepted: bool,
+ public_capabilities: Dictionary,
+) -> void:
+ _route_friend_response(
+ multiplayer.get_remote_sender_id(),
+ request_id,
+ accepted,
+ public_capabilities,
+ )
+
+
+@rpc("authority", "call_remote", "reliable", 0)
+func receive_friend_result(
+ request_id: String,
+ accepted: bool,
+ public_capabilities: Dictionary,
+ message: String,
+) -> void:
+ _receive_friend_result_local(
+ request_id, accepted, public_capabilities, message
+ )
+
+
+func kick(peer_id: int, fingerprint: String, revision: int) -> bool:
+ 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
+ request_kick.rpc_id(1, peer_id, fingerprint)
+ return true
+
+
+func clear_art(peer_id: int, fingerprint: String, revision: int) -> bool:
+ if _session.is_host():
+ return _clear_art_on_host(
+ peer_id, fingerprint, revision, true
+ )
+ if not _session.is_local_operator():
+ moderation_finished.emit(
+ false, "Only the host or an operator can clear player artwork."
+ )
+ return false
+ request_clear_art.rpc_id(1, peer_id, fingerprint)
+ return true
+
+
func ban(
peer_id: int,
fingerprint: String,
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_clear_art(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 = _clear_art_on_host(
+ peer_id, fingerprint, -1, false
+ )
+ _send_moderation_result(
+ sender_id,
+ ok,
+ "Player artwork cleared."
+ if ok
+ else "Player artwork could not be cleared.",
+ )
+
+
+@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:
@@ -255,6 +636,32 @@ func _on_peer_removed(peer_id: int) -> void:
for key: String in _host_block_pairs.keys():
if fingerprint in key.split(":"):
_host_block_pairs.erase(key)
+ for request_id: String in _host_friend_routes.keys():
+ var route: Dictionary = _host_friend_routes[request_id]
+ if int(route.get("target_peer_id", 0)) == peer_id:
+ _send_friend_result_to_peer(
+ int(route.get("requester_peer_id", 0)),
+ request_id,
+ false,
+ {},
+ "This person needs to be online to do this.",
+ )
+ if (
+ int(route.get("target_peer_id", 0)) == peer_id
+ or int(route.get("requester_peer_id", 0)) == peer_id
+ ):
+ _host_friend_routes.erase(request_id)
+ for request_id: String in _pending_outgoing_friends.keys():
+ var pending: Dictionary = _pending_outgoing_friends[request_id]
+ if str(pending.get("target_fingerprint", "")) == fingerprint:
+ _pending_outgoing_friends.erase(request_id)
+ friend_action_finished.emit(
+ false, "This person needs to be online to do this."
+ )
+ for request_id: String in _pending_incoming_friends.keys():
+ var pending: Dictionary = _pending_incoming_friends[request_id]
+ if str(pending.get("requester_fingerprint", "")) == fingerprint:
+ _pending_incoming_friends.erase(request_id)
_changed()
@@ -267,6 +674,25 @@ 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
+ _pending_outgoing_friends.clear()
+ _pending_incoming_friends.clear()
+ _host_friend_routes.clear()
+ 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()
@@ -291,16 +717,418 @@ 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_friend_target(peer_id: int, fingerprint: String) -> bool:
+ if (
+ not _session.is_gameplay_session_active()
+ or not _session.supports_server_capability(
+ NetworkProtocol.FRIENDS_CAPABILITY
+ )
+ or not _session.peer_supports_capability(
+ peer_id, NetworkProtocol.FRIENDS_CAPABILITY
+ )
+ ):
+ return false
+ var record: PeerRegistry.PeerRecord = _session.get_peer_record(peer_id)
+ return (
+ record != null
+ and record.identity_authenticated
+ and record.identity_fingerprint == fingerprint
+ )
+
+
+func _route_friend_request(
+ requester_peer_id: int,
+ target_peer_id: int,
+ request_id: String,
+ public_capabilities: Dictionary,
+) -> void:
+ if (
+ not _session.is_host()
+ or not _valid_friend_request_id(request_id)
+ or not _valid_public_friend_capabilities(public_capabilities)
+ or not _session.is_authenticated_peer(requester_peer_id)
+ ):
+ return
+ var requester: PeerRegistry.PeerRecord = (
+ _session.get_peer_record(requester_peer_id)
+ )
+ var target: PeerRegistry.PeerRecord = _session.get_peer_record(target_peer_id)
+ var can_route := (
+ requester != null
+ and target != null
+ and requester_peer_id != target_peer_id
+ and _valid_friend_target(
+ target_peer_id, target.identity_fingerprint
+ )
+ and _session.peer_supports_capability(
+ requester_peer_id, NetworkProtocol.FRIENDS_CAPABILITY
+ )
+ and not pair_is_blocked(
+ requester.identity_fingerprint, target.identity_fingerprint
+ )
+ and _host_friend_routes.size() < (
+ MAX_PENDING_FRIEND_REQUESTS * 8
+ )
+ )
+ if not can_route:
+ _send_friend_result_to_peer(
+ requester_peer_id,
+ request_id,
+ false,
+ {},
+ "This person needs to be online to do this.",
+ )
+ return
+ var requester_pending_count := 0
+ for route: Dictionary in _host_friend_routes.values():
+ if int(route.get("requester_peer_id", 0)) == requester_peer_id:
+ requester_pending_count += 1
+ if requester_pending_count >= MAX_PENDING_FRIEND_REQUESTS:
+ _send_friend_result_to_peer(
+ requester_peer_id,
+ request_id,
+ false,
+ {},
+ "Too many friend requests are pending.",
+ )
+ return
+ _host_friend_routes[request_id] = {
+ "requester_peer_id": requester_peer_id,
+ "target_peer_id": target_peer_id,
+ }
+ if target_peer_id == _session.get_local_peer_id():
+ _receive_friend_request_local(
+ request_id,
+ requester.identity_fingerprint,
+ requester.display_name,
+ public_capabilities,
+ )
+ else:
+ receive_friend_request.rpc_id(
+ target_peer_id,
+ request_id,
+ requester.identity_fingerprint,
+ requester.display_name,
+ public_capabilities,
+ )
+
+
+func _receive_friend_request_local(
+ request_id: String,
+ requester_fingerprint: String,
+ requester_display_name: String,
+ public_capabilities: Dictionary,
+) -> void:
+ if (
+ not _valid_friend_request_id(request_id)
+ or not _valid_public_friend_capabilities(public_capabilities)
+ or not NetworkIdentityCrypto.valid_fingerprint(requester_fingerprint)
+ or not NetworkProfilePreferences.is_valid_display_name(
+ requester_display_name
+ )
+ or _pending_incoming_friends.size()
+ >= MAX_PENDING_FRIEND_REQUESTS
+ or _relationships.is_blocked(requester_fingerprint)
+ or _relationships.is_friend(requester_fingerprint)
+ ):
+ _decline_friend_request(request_id)
+ return
+ _pending_incoming_friends[request_id] = {
+ "requester_fingerprint": requester_fingerprint,
+ "requester_display_name": requester_display_name,
+ "remote_capabilities": public_capabilities.duplicate(true),
+ }
+ friend_request_received.emit(
+ request_id, requester_fingerprint, requester_display_name
+ )
+
+
+func _decline_friend_request(request_id: String) -> void:
+ if _session.is_host():
+ _route_friend_response(
+ _session.get_local_peer_id(), request_id, false, {}
+ )
+ elif _session.is_joined_client():
+ respond_friendship.rpc_id(1, request_id, false, {})
+
+
+func _route_friend_response(
+ responder_peer_id: int,
+ request_id: String,
+ accepted: bool,
+ public_capabilities: Dictionary,
+) -> void:
+ if not _session.is_host():
+ return
+ var route: Dictionary = _host_friend_routes.get(request_id, {})
+ if (
+ route.is_empty()
+ or int(route.get("target_peer_id", 0)) != responder_peer_id
+ ):
+ return
+ _host_friend_routes.erase(request_id)
+ var response_accepted := (
+ accepted and _valid_public_friend_capabilities(public_capabilities)
+ )
+ _send_friend_result_to_peer(
+ int(route["requester_peer_id"]),
+ request_id,
+ response_accepted,
+ public_capabilities if response_accepted else {},
+ "Friend request accepted."
+ if response_accepted
+ else "Friend request declined."
+ if not accepted
+ else "Friend request could not be completed.",
+ )
+
+
+func _send_friend_result_to_peer(
+ peer_id: int,
+ request_id: String,
+ accepted: bool,
+ public_capabilities: Dictionary,
+ message: String,
+) -> void:
+ if peer_id == _session.get_local_peer_id():
+ _receive_friend_result_local(
+ request_id, accepted, public_capabilities, message
+ )
+ elif _session.is_authenticated_peer(peer_id):
+ receive_friend_result.rpc_id(
+ peer_id,
+ request_id,
+ accepted,
+ public_capabilities,
+ message.left(120),
+ )
+
+
+func _receive_friend_result_local(
+ request_id: String,
+ accepted: bool,
+ public_capabilities: Dictionary,
+ message: String,
+) -> void:
+ var pending: Dictionary = _pending_outgoing_friends.get(request_id, {})
+ if pending.is_empty():
+ return
+ _pending_outgoing_friends.erase(request_id)
+ var success := false
+ if accepted and _valid_public_friend_capabilities(public_capabilities):
+ success = _relationships.add_friend(
+ str(pending["target_fingerprint"]),
+ str(pending["target_display_name"]),
+ pending["local_capabilities"],
+ public_capabilities,
+ )
+ message = "Friend added." if success else "Friend could not be saved."
+ friend_action_finished.emit(success, message.left(120))
+
+
+func _public_friend_capabilities(capabilities: Dictionary) -> Dictionary:
+ return {
+ "presence_channel": str(capabilities.get("presence_channel", "")),
+ "invite_token": str(capabilities.get("invite_token", "")),
+ }
+
+
+func _valid_public_friend_capabilities(capabilities: Dictionary) -> bool:
+ return (
+ _valid_friend_hex(str(capabilities.get("presence_channel", "")))
+ and _valid_friend_hex(str(capabilities.get("invite_token", "")))
+ )
+
+
+func _valid_friend_request_id(request_id: String) -> bool:
+ return _valid_friend_hex(request_id, FRIEND_REQUEST_ID_LENGTH)
+
+
+func _valid_friend_hex(
+ value: String,
+ expected_length: int = PlayerRelationshipStore.SOCIAL_TOKEN_LENGTH,
+) -> bool:
+ if value.length() != expected_length:
+ return false
+ for character: String in value:
+ if character not in "0123456789abcdef":
+ return false
+ return true
+
+
+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
+ if _surface_drawing != null:
+ _surface_drawing.clear_artwork_by_fingerprint(fingerprint)
+ 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 _clear_art_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
+ if _surface_drawing == null:
+ if local_host_request:
+ moderation_finished.emit(
+ false, "Player artwork could not be cleared."
+ )
+ return false
+ var affected_layers: int = (
+ _surface_drawing.clear_artwork_by_fingerprint(fingerprint)
+ )
+ var ok: bool = affected_layers >= 0
+ if local_host_request:
+ moderation_finished.emit(
+ ok,
+ "Player artwork cleared."
+ if affected_layers > 0
+ else "No shared artwork from that player was found."
+ if ok
+ else "Player artwork could not be cleared.",
+ )
+ return ok
+
+
+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 5ecb6fd..b6cac63 100644
--- a/network/network_profile_preferences.gd
+++ b/network/network_profile_preferences.gd
@@ -10,6 +10,7 @@ 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 sample_set_id: String = VoiceProfilesType.DEFAULT_SAMPLE_SET_ID
var created_at_unix: int = 0
var _profile_path := ""
var _expected_hash := ""
@@ -20,6 +21,8 @@ var _future_version := false
func configure_storage(path: String, data_root: PlayerDataRoot) -> void:
_profile_path = path
_data_root = data_root
+ _expected_hash = ""
+ _future_version = false
func _temp_path() -> String:
@@ -57,12 +60,19 @@ func load_or_create() -> bool:
voice_id = VoiceProfilesType.DEFAULT_ID
speech_speed_id = VoiceProfilesType.DEFAULT_SPEED_ID
call_id = VoiceProfilesType.DEFAULT_CALL_ID
+ sample_set_id = VoiceProfilesType.DEFAULT_SAMPLE_SET_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)
+ return set_profile_identity(
+ value,
+ voice_id,
+ speech_speed_id,
+ call_id,
+ sample_set_id,
+ )
func set_profile_identity(
@@ -70,6 +80,7 @@ func set_profile_identity(
selected_voice_id: String,
selected_speech_speed_id: String,
selected_call_id: String,
+ selected_sample_set_id: String = VoiceProfilesType.DEFAULT_SAMPLE_SET_ID,
) -> bool:
var clean_name: String = value.strip_edges()
if (
@@ -77,22 +88,26 @@ func set_profile_identity(
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)
+ or not VoiceProfilesType.is_valid_sample_set(selected_sample_set_id)
):
return false
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
+ var previous_sample_set_id: String = sample_set_id
display_name = clean_name
voice_id = selected_voice_id
speech_speed_id = selected_speech_speed_id
call_id = selected_call_id
+ sample_set_id = selected_sample_set_id
if _save_atomic():
return true
display_name = previous_name
voice_id = previous_voice_id
speech_speed_id = previous_speech_speed_id
call_id = previous_call_id
+ sample_set_id = previous_sample_set_id
return false
@@ -150,6 +165,12 @@ func _load_existing() -> bool:
call_id = VoiceProfilesType.sanitized_call_id(
str(data.get("call_id", VoiceProfilesType.DEFAULT_CALL_ID))
)
+ sample_set_id = VoiceProfilesType.sanitized_sample_set_id(
+ str(data.get(
+ "sample_set_id",
+ VoiceProfilesType.DEFAULT_SAMPLE_SET_ID,
+ ))
+ )
created_at_unix = int(data["created_at_unix"])
return true
@@ -162,6 +183,7 @@ func _save_atomic() -> bool:
"voice_id": voice_id,
"speech_speed_id": speech_speed_id,
"call_id": call_id,
+ "sample_set_id": sample_set_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 b4c2d3a..5c1e879 100644
--- a/network/network_profile_protocol.gd
+++ b/network/network_profile_protocol.gd
@@ -41,25 +41,44 @@ static func valid_apply_request(data: Variant) -> bool:
)
+static func valid_appearance_preview_request(data: Variant) -> bool:
+ return (
+ typeof(data) == TYPE_DICTIONARY
+ and valid_request_id(data.get("request_id"))
+ and typeof(data.get("session_id")) == TYPE_STRING
+ and not str(data.get("session_id", "")).is_empty()
+ and typeof(data.get("appearance")) == TYPE_DICTIONARY
+ and valid_snapshot(data["appearance"])
+ and NetworkIdentityCrypto.valid_fingerprint(
+ data.get("sender_fingerprint")
+ )
+ and typeof(data.get("sender_signature")) == TYPE_PACKED_BYTE_ARRAY
+ )
+
+
static func signature_fields(data: Dictionary) -> Array:
var appearance: Dictionary = data.get("appearance", {})
- return [
+ var result: Array = [
str(data.get("session_id", "")),
str(data.get("request_id", "")),
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("ears", "")),
- str(appearance.get("eyes", "")),
- str(appearance.get("nose", "")),
- str(appearance.get("mouth", "")),
- str(appearance.get("tail", "")),
- bool(data.get("use_anyway", false)),
]
+ result.append_array(
+ CharacterCustomizationCatalog.appearance_signature_values(appearance)
+ )
+ result.append(bool(data.get("use_anyway", false)))
+ return result
+
+
+static func appearance_preview_signature_fields(data: Dictionary) -> Array:
+ var appearance: Dictionary = data.get("appearance", {})
+ var result: Array = [
+ str(data.get("session_id", "")),
+ str(data.get("request_id", "")),
+ str(data.get("sender_fingerprint", "")),
+ ]
+ result.append_array(
+ CharacterCustomizationCatalog.appearance_signature_values(appearance)
+ )
+ return result
diff --git a/network/network_profile_service.gd b/network/network_profile_service.gd
index 86b1be1..ff967f1 100644
--- a/network/network_profile_service.gd
+++ b/network/network_profile_service.gd
@@ -25,6 +25,7 @@ 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 _pending_sample_set_ids: Dictionary[String, String] = {}
var _host_pending_apply: Dictionary[String, Dictionary] = {}
var _latest_check_id: String = ""
var _latest_check_name: String = ""
@@ -50,6 +51,17 @@ func setup(
_apply_local_voice_to_avatar()
+func reload_persisted_profile() -> bool:
+ if (
+ not _preferences.load_or_create()
+ or not _appearance_store.load_preferences()
+ ):
+ return false
+ _apply_to_avatar(1, _appearance_store.get_snapshot())
+ _apply_local_voice_to_avatar()
+ return true
+
+
func get_persisted_name() -> String:
return _preferences.display_name
@@ -70,6 +82,10 @@ func get_persisted_call_id() -> String:
return _preferences.call_id
+func get_persisted_sample_set_id() -> String:
+ return _preferences.sample_set_id
+
+
func get_identity_fingerprint() -> String:
return (
_session.get_local_identity_fingerprint()
@@ -77,6 +93,58 @@ func get_identity_fingerprint() -> String:
)
+func preview_appearance(appearance: Dictionary) -> bool:
+ if not CharacterCustomizationCatalog.validate_snapshot(appearance):
+ return false
+ var snapshot := CharacterCustomizationCatalog.sanitized_snapshot(appearance)
+ var local_peer_id: int = (
+ _session.get_local_peer_id() if _session != null else 1
+ )
+ _apply_to_avatar(local_peer_id, snapshot)
+ if _session == null or not _session.is_gameplay_session_active():
+ return true
+ _session.apply_canonical_profile(
+ local_peer_id,
+ _preferences.display_name,
+ snapshot,
+ )
+ var authorization := _make_appearance_preview_authorization(snapshot)
+ var local_record := _session.get_peer_record(local_peer_id)
+ if local_record != null:
+ local_record.profile_authorization = authorization.duplicate(true)
+ if _session.is_host():
+ _broadcast_appearance_preview_to_supported_peers(
+ local_peer_id,
+ snapshot,
+ )
+ elif _session.supports_server_capability(
+ NetworkProtocol.APPEARANCE_PREVIEW_CAPABILITY
+ ):
+ submit_appearance_preview.rpc_id(1, authorization)
+ return true
+
+
+func _make_appearance_preview_authorization(
+ appearance: Dictionary,
+) -> Dictionary:
+ var request: Dictionary = {
+ "domain": "appearance_preview",
+ "request_id": _new_id(),
+ "session_id": _session.get_session_id(),
+ "appearance": appearance.duplicate(true),
+ "sender_fingerprint": _session.get_local_identity_fingerprint(),
+ }
+ request["sender_signature"] = _session.sign_local_action(
+ "appearance_preview",
+ NetworkProfileProtocol.appearance_preview_signature_fields(request),
+ )
+ return request
+
+
+func restore_persisted_appearance() -> void:
+ preview_appearance(_appearance_store.get_snapshot())
+
+
func request_name_check(display_name: String) -> String:
var request_id := _new_id()
_latest_check_id = request_id
@@ -110,6 +178,7 @@ func apply_profile(
voice_id: String = VoiceProfilesType.DEFAULT_ID,
speech_speed_id: String = VoiceProfilesType.DEFAULT_SPEED_ID,
call_id: String = VoiceProfilesType.DEFAULT_CALL_ID,
+ sample_set_id: String = VoiceProfilesType.DEFAULT_SAMPLE_SET_ID,
) -> bool:
var clean_name := display_name.strip_edges()
if (
@@ -118,6 +187,7 @@ func apply_profile(
or not VoiceProfilesType.is_valid(voice_id)
or not VoiceProfilesType.is_valid_speed(speech_speed_id)
or not VoiceProfilesType.is_valid_call(call_id)
+ or not VoiceProfilesType.is_valid_sample_set(sample_set_id)
):
apply_finished.emit(false, "Check the player name and appearance choices.")
return false
@@ -137,6 +207,7 @@ func apply_profile(
_pending_voice_ids[request_id] = voice_id
_pending_speech_speed_ids[request_id] = speech_speed_id
_pending_call_ids[request_id] = call_id
+ _pending_sample_set_ids[request_id] = sample_set_id
if _session == null or not _session.is_gameplay_session_active():
_apply_local_result(request_id, true, "", false, PackedStringArray())
elif _session.is_host():
@@ -200,6 +271,43 @@ func submit_profile_apply(data: Dictionary) -> void:
_process_apply_request(sender_id, data)
+@rpc("any_peer", "call_remote", "reliable", NetworkProfileProtocol.RELIABLE_CHANNEL)
+func submit_appearance_preview(data: Dictionary) -> void:
+ var sender_id := multiplayer.get_remote_sender_id()
+ if (
+ not _session.is_host()
+ or not _session.is_authenticated_peer(sender_id)
+ or not NetworkProfileProtocol.valid_appearance_preview_request(data)
+ or str(data["session_id"]) != _session.get_session_id()
+ ):
+ return
+ var record := _session.get_peer_record(sender_id)
+ if (
+ record == null
+ or record.identity_fingerprint != str(data["sender_fingerprint"])
+ or not _session.verify_peer_action(
+ sender_id,
+ "appearance_preview",
+ NetworkProfileProtocol.appearance_preview_signature_fields(data),
+ data["sender_signature"],
+ )
+ ):
+ return
+ var appearance := CharacterCustomizationCatalog.sanitized_snapshot(
+ data["appearance"]
+ )
+ var authorization := data.duplicate(true)
+ authorization["domain"] = "appearance_preview"
+ record.profile_authorization = authorization
+ _session.apply_canonical_profile(
+ sender_id,
+ record.display_name,
+ appearance,
+ )
+ _apply_to_avatar(sender_id, appearance)
+ _broadcast_appearance_preview_to_supported_peers(sender_id, appearance)
+
+
func _process_apply_request(peer_id: int, data: Dictionary) -> void:
var display_name: String = str(data["display_name"]).strip_edges()
var conflict: bool = _name_conflicts(peer_id, display_name)
@@ -256,7 +364,7 @@ func confirm_profile_saved(request_id: String) -> void:
_apply_to_avatar(sender_id, appearance)
broadcast_profile_snapshot.rpc(
sender_id,
- name,
+ display_name,
appearance,
pending.get("authorization", {}),
)
@@ -296,6 +404,7 @@ func _apply_local_result(
_pending_voice_ids.erase(request_id)
_pending_speech_speed_ids.erase(request_id)
_pending_call_ids.erase(request_id)
+ _pending_sample_set_ids.erase(request_id)
conflict_result.emit(request_id, conflict, suggestions)
apply_finished.emit(false, message)
return
@@ -303,6 +412,7 @@ func _apply_local_result(
var previous_voice_id := _preferences.voice_id
var previous_speech_speed_id := _preferences.speech_speed_id
var previous_call_id := _preferences.call_id
+ var previous_sample_set_id := _preferences.sample_set_id
var requested_voice_id: String = _pending_voice_ids.get(
request_id,
VoiceProfilesType.DEFAULT_ID,
@@ -315,16 +425,22 @@ func _apply_local_result(
request_id,
VoiceProfilesType.DEFAULT_CALL_ID,
)
+ var requested_sample_set_id: String = _pending_sample_set_ids.get(
+ request_id,
+ VoiceProfilesType.DEFAULT_SAMPLE_SET_ID,
+ )
if not _preferences.set_profile_identity(
str(request["display_name"]),
requested_voice_id,
requested_speech_speed_id,
requested_call_id,
+ requested_sample_set_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)
+ _pending_sample_set_ids.erase(request_id)
apply_finished.emit(false, "Profile could not be saved.")
return
if not _appearance_store.save_snapshot(request["appearance"]):
@@ -333,17 +449,22 @@ func _apply_local_result(
previous_voice_id,
previous_speech_speed_id,
previous_call_id,
+ previous_sample_set_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)
+ _pending_sample_set_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)
+ _pending_sample_set_ids.erase(request_id)
+ if _session != null:
+ _session.set_local_appearance_snapshot(_appearance_store.get_snapshot())
if _session != null and _session.is_gameplay_session_active():
if _session.is_host():
_session.apply_canonical_profile(
@@ -386,28 +507,63 @@ func broadcast_profile_snapshot(
or not CharacterCustomizationCatalog.validate_snapshot(appearance)
):
return
- if not authorization.is_empty():
- var signed := authorization.duplicate(true)
- signed["display_name"] = display_name
- signed["appearance"] = appearance
- var record := _session.get_peer_record(peer_id)
- if (
- record == null
- or record.identity_fingerprint
- != str(signed.get("sender_fingerprint", ""))
- or not _session.verify_peer_action(
- peer_id,
- "profile_update",
- NetworkProfileProtocol.signature_fields(signed),
- signed.get("sender_signature", PackedByteArray()),
- )
- ):
- return
+ if (
+ not authorization.is_empty()
+ and not _session.verify_profile_authorization(
+ peer_id,
+ display_name,
+ appearance,
+ authorization,
+ )
+ ):
+ return
_session.apply_canonical_profile(peer_id, display_name, appearance)
_apply_to_avatar(peer_id, appearance)
profile_snapshot_changed.emit(peer_id, display_name, appearance.duplicate(true))
+@rpc("authority", "call_remote", "reliable", NetworkProfileProtocol.RELIABLE_CHANNEL)
+func broadcast_appearance_preview(
+ peer_id: int,
+ appearance: Dictionary,
+) -> void:
+ if not CharacterCustomizationCatalog.validate_snapshot(appearance):
+ return
+ var record := _session.get_peer_record(peer_id)
+ if record == null:
+ return
+ var snapshot := CharacterCustomizationCatalog.sanitized_snapshot(appearance)
+ _session.apply_canonical_profile(peer_id, record.display_name, snapshot)
+ _apply_to_avatar(peer_id, snapshot)
+ profile_snapshot_changed.emit(
+ peer_id,
+ record.display_name,
+ snapshot.duplicate(true),
+ )
+
+
+func _broadcast_appearance_preview_to_supported_peers(
+ peer_id: int,
+ appearance: Dictionary,
+) -> void:
+ if _session == null or not _session.is_host():
+ return
+ for recipient_id: int in _session.get_authenticated_peer_ids():
+ if (
+ recipient_id == _session.get_local_peer_id()
+ or not _session.peer_supports_capability(
+ recipient_id,
+ NetworkProtocol.APPEARANCE_PREVIEW_CAPABILITY,
+ )
+ ):
+ continue
+ broadcast_appearance_preview.rpc_id(
+ recipient_id,
+ peer_id,
+ appearance,
+ )
+
+
func _on_peer_authenticated(peer_id: int, _display_name: String) -> void:
if not _session.is_host():
return
@@ -422,9 +578,19 @@ func _on_peer_authenticated(peer_id: int, _display_name: String) -> void:
record.appearance_snapshot,
record.profile_authorization,
)
+ if _session.peer_supports_capability(
+ peer_id,
+ NetworkProtocol.APPEARANCE_PREVIEW_CAPABILITY,
+ ):
+ broadcast_appearance_preview.rpc_id(
+ peer_id,
+ existing_id,
+ record.appearance_snapshot,
+ )
func _on_join_authenticated() -> void:
+ _session.set_local_appearance_snapshot(_appearance_store.get_snapshot())
_apply_to_avatar(
_session.get_local_peer_id(), _appearance_store.get_snapshot()
)
@@ -471,9 +637,21 @@ func _on_session_state_changed(state: NetworkSession.State) -> void:
_pending_voice_ids.clear()
_pending_speech_speed_ids.clear()
_pending_call_ids.clear()
+ _pending_sample_set_ids.clear()
_host_pending_apply.clear()
_latest_check_id = ""
_latest_check_name = ""
+ _session.set_local_appearance_snapshot(_appearance_store.get_snapshot())
+ _apply_to_avatar(1, _appearance_store.get_snapshot())
+ return
+ if state in [
+ NetworkSession.State.PRIVATE_HOST,
+ NetworkSession.State.OPEN_HOST,
+ NetworkSession.State.JOINED_CLIENT,
+ ]:
+ var local_peer_id := _session.get_local_peer_id()
+ _apply_to_avatar(local_peer_id, _appearance_store.get_snapshot())
+ _apply_local_voice_to_avatar()
func _emit_conflict_result(data: Dictionary) -> void:
@@ -545,6 +723,7 @@ func _apply_local_voice_to_avatar() -> void:
var avatar := _spawn_service.get_avatar(local_peer_id)
if avatar != null:
avatar.apply_animalese_voice_id(_preferences.voice_id)
+ avatar.apply_animalese_sample_set_id(_preferences.sample_set_id)
func _new_id() -> String:
diff --git a/network/network_protocol.gd b/network/network_protocol.gd
index cfe5a0b..5b3ca89 100644
--- a/network/network_protocol.gd
+++ b/network/network_protocol.gd
@@ -1,8 +1,11 @@
class_name NetworkProtocol
extends RefCounted
-const PROTOCOL_VERSION: int = 3
+const WorldLayoutType = preload("res://world/world_layout.gd")
+
+const PROTOCOL_VERSION: int = 12
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
@@ -10,20 +13,43 @@ const MAX_PUBLIC_KEY_LENGTH: int = 8192
const MAX_SIGNATURE_LENGTH: int = 2048
# ENet channels: 0 reliable lifecycle, 1 movement input, 2 movement
# snapshots, 3 fishing input, 4 fishing snapshots, 5 reliable sales,
-# 6 reliable shop transactions, 7 reliable item/equipment/showcase/drawing,
-# 8 reliable ordered session chat, 9 reliable private session mail.
+# 6 reliable shop transactions, 7 reliable item/equipment,
+# 8 reliable ordered session chat, 9 reliable private session mail,
+# 10 observer-only fishing summaries, 11 reliable movement animation state,
+# 12 reliable fish showcase state, 13 reliable world-spawn events,
+# 14 reliable artwork state, 15 world-spawn snapshots, 16 reliable fishing
+# lifecycle, and 17 reliable portable-home state. Bulk world/art payloads must
+# not head-of-line block held items, fishing, animation, or home transitions.
const SALE_RELIABLE_CHANNEL: int = 5
const SHOP_RELIABLE_CHANNEL: int = 6
const ITEM_RELIABLE_CHANNEL: int = 7
const CHAT_RELIABLE_CHANNEL: int = 8
const MAIL_RELIABLE_CHANNEL: int = 9
-const ENET_CHANNEL_COUNT: int = 10
+const MOVEMENT_ANIMATION_CHANNEL: int = 11
+const SHOWCASE_RELIABLE_CHANNEL: int = 12
+const WORLD_SPAWN_RELIABLE_CHANNEL: int = 13
+const DRAWING_RELIABLE_CHANNEL: int = 14
+const FISHING_RELIABLE_CHANNEL: int = 16
+const HOME_RELIABLE_CHANNEL: int = 17
+const ENET_CHANNEL_COUNT: int = 18
const SURFACE_DRAWING_CAPABILITY: String = "surface_drawing_v2"
const ART_SHOP_CAPABILITY: String = "art_shop_v1"
-const WORLD_TIME_CAPABILITY: String = "world_time_v1"
+const BACKPACK_SHOP_CAPABILITY: String = "backpack_shop_v1"
+const WORLD_TIME_CAPABILITY: String = "world_time_v2"
const WORLD_WEATHER_CAPABILITY: String = "world_weather_v1"
const FISH_QUALITY_CAPABILITY: String = "fish_quality_v1"
const JOBS_CAPABILITY: String = "jobs_v1"
+const WORLD_SPAWN_CAPABILITY: String = "world_spawn_envelope_v1"
+const APPEARANCE_PREVIEW_CAPABILITY: String = "appearance_preview_v1"
+const WORLD_GENERATION_CAPABILITY: String = "world_generation_v1"
+const WORLD_LAYOUT_CAPABILITY: String = "world_layout_v1"
+const FRIENDS_CAPABILITY: String = "friends_v1"
+const MOVEMENT_RECONCILIATION_CAPABILITY: String = "movement_reconciliation_v2"
+const FISHING_REPLICATION_CAPABILITY: String = "fishing_replication_v2"
+const HOME_CAPABILITY: String = "portable_home_v1"
+const HOME_UPGRADE_CAPABILITY: String = "home_upgrade_v1"
+const DEFAULT_WORLD_SEED: int = 13001
+const MAX_WORLD_SEED: int = 2147483646
enum RejectionCode {
NONE,
@@ -37,9 +63,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,
@@ -48,6 +145,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,
@@ -62,6 +160,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"))
@@ -85,6 +186,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,
@@ -95,6 +197,16 @@ static func make_client_hello(
WORLD_TIME_CAPABILITY,
WORLD_WEATHER_CAPABILITY,
JOBS_CAPABILITY,
+ WORLD_SPAWN_CAPABILITY,
+ APPEARANCE_PREVIEW_CAPABILITY,
+ BACKPACK_SHOP_CAPABILITY,
+ WORLD_GENERATION_CAPABILITY,
+ WORLD_LAYOUT_CAPABILITY,
+ FRIENDS_CAPABILITY,
+ MOVEMENT_RECONCILIATION_CAPABILITY,
+ FISHING_REPLICATION_CAPABILITY,
+ HOME_CAPABILITY,
+ HOME_UPGRADE_CAPABILITY,
]),
"cosmetic_snapshot": cosmetic_snapshot,
"identity_fingerprint": identity_fingerprint,
@@ -108,6 +220,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",
@@ -121,6 +234,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:
@@ -170,25 +289,17 @@ static func validate_client_hello(data: Variant) -> String:
static func client_profile_fields(data: Dictionary) -> Array:
var appearance: Dictionary = data.get("cosmetic_snapshot", {})
- return [
+ var result: Array = [
+ 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("ears", "")),
- str(appearance.get("eyes", "")),
- str(appearance.get("nose", "")),
- str(appearance.get("mouth", "")),
- str(appearance.get("tail", "")),
]
+ result.append_array(
+ CharacterCustomizationCatalog.appearance_signature_values(appearance)
+ )
+ return result
static func make_server_hello(
@@ -198,23 +309,29 @@ static func make_server_hello(
assigned_peer_id: int,
player_count: int,
max_players: int,
- server_display_name: String = "NETfishing",
+ server_display_name: String = "straywild",
+ world_seed: int = DEFAULT_WORLD_SEED,
+ world_layout: StringName = WorldLayoutType.GENERATED,
) -> 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": server_display_name,
"player_count": player_count,
"max_players": max_players,
+ "world_seed": world_seed,
+ "world_layout": String(world_layout),
"capability_flags": PackedStringArray([
"movement_v1",
"fishing_v1",
"sale_v1",
"shop_v1",
ART_SHOP_CAPABILITY,
+ BACKPACK_SHOP_CAPABILITY,
"item_use_v1",
"equipment_v1",
"fish_showcase_v1",
@@ -223,6 +340,15 @@ static func make_server_hello(
WORLD_TIME_CAPABILITY,
WORLD_WEATHER_CAPABILITY,
JOBS_CAPABILITY,
+ WORLD_SPAWN_CAPABILITY,
+ APPEARANCE_PREVIEW_CAPABILITY,
+ WORLD_GENERATION_CAPABILITY,
+ WORLD_LAYOUT_CAPABILITY,
+ FRIENDS_CAPABILITY,
+ MOVEMENT_RECONCILIATION_CAPABILITY,
+ FISHING_REPLICATION_CAPABILITY,
+ HOME_CAPABILITY,
+ HOME_UPGRADE_CAPABILITY,
"chat_v1",
"mail_v1",
"profile_v1",
@@ -251,5 +377,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 straywild 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 straywild."
+ )
+ RejectionCode.VERSION_MISMATCH:
+ return (
+ "This game and server use different straywild versions. "
+ + "Update both to the latest release."
+ )
_:
return "The server rejected the connection."
diff --git a/network/network_sale_protocol.gd b/network/network_sale_protocol.gd
index 3b80de6..190b718 100644
--- a/network/network_sale_protocol.gd
+++ b/network/network_sale_protocol.gd
@@ -6,6 +6,8 @@ const RELIABLE_CHANNEL: int = NetworkProtocol.SALE_RELIABLE_CHANNEL
const MAX_ID_LENGTH: int = 96
const MAX_CATCH_ID_LENGTH: int = 160
const MAX_CATCHES_PER_REQUEST: int = 64
+const MAX_ITEM_STACKS_PER_REQUEST: int = 20
+const MAX_ITEM_QUANTITY: int = 999
const MAX_MESSAGE_LENGTH: int = 160
enum Rejection {
@@ -40,6 +42,7 @@ static func validate_request(data: Variant) -> String:
var session_id: String = payload["session_id"]
var buyer_id: String = str(payload["buyer_id"])
var catches: Array = payload["catches"]
+ var items: Array = payload.get("items", [])
if (
request_id.is_empty()
or request_id.length() > MAX_ID_LENGTH
@@ -47,8 +50,10 @@ static func validate_request(data: Variant) -> String:
or session_id.length() > MAX_ID_LENGTH
or buyer_id.is_empty()
or buyer_id.length() > MAX_ID_LENGTH
- or catches.is_empty()
+ or typeof(items) != TYPE_ARRAY
+ or (catches.is_empty() and items.is_empty())
or catches.size() > MAX_CATCHES_PER_REQUEST
+ or items.size() > MAX_ITEM_STACKS_PER_REQUEST
):
return "Sale could not be completed."
var seen: Dictionary[String, bool] = {}
@@ -74,6 +79,23 @@ static func validate_request(data: Variant) -> String:
):
return "Sale could not be completed."
seen[catch_id] = true
+ var seen_items: Dictionary[String, bool] = {}
+ for value: Variant in items:
+ if typeof(value) != TYPE_DICTIONARY:
+ return "Sale could not be completed."
+ var item: Dictionary = value
+ var item_id := str(item.get("item_id", ""))
+ var quantity := int(item.get("quantity", 0))
+ if (
+ item_id.is_empty()
+ or item_id.length() > MAX_ID_LENGTH
+ or seen_items.has(item_id)
+ or typeof(item.get("quantity")) != TYPE_INT
+ or quantity < 1
+ or quantity > MAX_ITEM_QUANTITY
+ ):
+ return "Sale could not be completed."
+ seen_items[item_id] = true
return ""
@@ -93,6 +115,8 @@ static func validate_result(data: Variant) -> bool:
and typeof(payload.get("accepted")) == TYPE_BOOL
and typeof(payload.get("catch_ids")) == TYPE_ARRAY
and payload["catch_ids"].size() <= MAX_CATCHES_PER_REQUEST
+ and typeof(payload.get("items", [])) == TYPE_ARRAY
+ and payload.get("items", []).size() <= MAX_ITEM_STACKS_PER_REQUEST
and typeof(payload.get("payout")) == TYPE_INT
and int(payload["payout"]) >= 0
and typeof(payload.get("base_value")) == TYPE_INT
@@ -119,4 +143,20 @@ static func validate_result(data: Variant) -> bool:
):
return false
seen[catch_id] = true
+ var seen_items: Dictionary[String, bool] = {}
+ for value: Variant in payload.get("items", []):
+ if typeof(value) != TYPE_DICTIONARY:
+ return false
+ var item := value as Dictionary
+ var item_id := str(item.get("item_id", ""))
+ if (
+ item_id.is_empty()
+ or item_id.length() > MAX_ID_LENGTH
+ or seen_items.has(item_id)
+ or typeof(item.get("quantity")) != TYPE_INT
+ or int(item["quantity"]) < 1
+ or int(item["quantity"]) > MAX_ITEM_QUANTITY
+ ):
+ return false
+ seen_items[item_id] = true
return true
diff --git a/network/network_sale_service.gd b/network/network_sale_service.gd
index 11a499c..a80f221 100644
--- a/network/network_sale_service.gd
+++ b/network/network_sale_service.gd
@@ -8,8 +8,13 @@ const FishPoolType = preload("res://fish/fish_pool.gd")
const FishBuyerProfileType = preload("res://economy/fish_buyer_profile.gd")
const FishSaleServiceType = preload("res://economy/fish_sale_service.gd")
const FishSaleResultType = preload("res://economy/fish_sale_result.gd")
+const ItemDataType = preload("res://items/item_data.gd")
+const ItemCatalogType = preload("res://items/item_catalog.gd")
+const PlayerBagType = preload("res://inventory/player_bag.gd")
+const ItemResalePolicyType = preload("res://economy/item_resale_policy.gd")
const MAX_LEDGER_ENTRIES_PER_PEER: int = 64
+const LOCAL_REQUEST_RETRY_SECONDS: float = 2.0
const PELICAN_BUYER_ID: StringName = &"pelicans"
const MAIN_SHOP_BUYER_ID: StringName = &"main_fishing_shop"
@@ -27,6 +32,8 @@ var _spawn_service: PlayerSpawnService
var _network_fishing: NetworkFishingService
var _shop_interaction: FishingShopInteraction
var _inventory: FishInventory
+var _bag: PlayerBagType
+var _item_catalog: ItemCatalogType
var _wallet: PlayerWallet
var _sale_service: FishSaleServiceType
var _save_manager: PlayerSaveManager
@@ -39,9 +46,32 @@ var _acknowledged_results: Dictionary[String, bool] = {}
var _applied_results: Dictionary[String, bool] = {}
var _received_results: Dictionary[String, bool] = {}
var _pending_local_request_id: String = ""
+var _pending_local_request: Dictionary = {}
+var _local_request_retry_elapsed: float = 0.0
var _pending_local_catch_ids: Array[StringName] = []
+var _pending_local_items: Array[Dictionary] = []
var _pending_local_buyer_id: StringName
var _reservations: PlayerAssetReservationService
+var _inventory_layout: PlayerInventoryLayout
+
+
+func _process(delta: float) -> void:
+ if (
+ _pending_local_request.is_empty()
+ or _session == null
+ or not _session.is_joined_client()
+ ):
+ _local_request_retry_elapsed = 0.0
+ return
+ _local_request_retry_elapsed += delta
+ if _local_request_retry_elapsed < LOCAL_REQUEST_RETRY_SECONDS:
+ return
+ _local_request_retry_elapsed = 0.0
+ # The request ID is stable and the host ledger is idempotent, so an
+ # application-level retry cannot apply a sale twice. This covers the narrow
+ # transition where a generated client has authenticated but is only just
+ # resuming regular multiplayer polling.
+ submit_sale_request.rpc_id(1, _pending_local_request)
func setup(
@@ -50,18 +80,23 @@ func setup(
network_fishing: NetworkFishingService,
shop_interaction: FishingShopInteraction,
inventory: FishInventory,
+ bag: PlayerBagType,
+ item_catalog: ItemCatalogType,
wallet: PlayerWallet,
sale_service: FishSaleServiceType,
save_manager: PlayerSaveManager,
fish_catalog: FishPoolType,
buyers: Array[FishBuyerProfileType],
reservations: PlayerAssetReservationService,
+ inventory_layout: PlayerInventoryLayout = null,
) -> void:
_session = session
_spawn_service = spawn_service
_network_fishing = network_fishing
_shop_interaction = shop_interaction
_inventory = inventory
+ _bag = bag
+ _item_catalog = item_catalog
_wallet = wallet
_sale_service = sale_service
_save_manager = save_manager
@@ -71,12 +106,17 @@ func setup(
if buyer != null and buyer.is_valid():
_buyers[buyer.id] = buyer
_reservations = reservations
+ _inventory_layout = inventory_layout
if not _session.peer_removed.is_connected(_on_peer_removed):
_session.peer_removed.connect(_on_peer_removed)
if not _session.state_changed.is_connected(_on_session_state_changed):
_session.state_changed.connect(_on_session_state_changed)
+func set_shop_interaction(interaction: FishingShopInteraction) -> void:
+ _shop_interaction = interaction
+
+
func is_local_sale_pending() -> bool:
return not _pending_local_request_id.is_empty()
@@ -102,6 +142,15 @@ func can_request_sale(
func request_local_sale(
catch_ids: Array[StringName],
buyer_id: StringName = PELICAN_BUYER_ID,
+) -> String:
+ var no_items: Dictionary[StringName, int] = {}
+ return request_local_mixed_sale(catch_ids, no_items, buyer_id)
+
+
+func request_local_mixed_sale(
+ catch_ids: Array[StringName],
+ item_quantities: Dictionary[StringName, int],
+ buyer_id: StringName = MAIN_SHOP_BUYER_ID,
) -> String:
for catch_id: StringName in catch_ids:
if _reservations != null and _reservations.is_fish_reserved(catch_id):
@@ -123,7 +172,12 @@ func request_local_sale(
0
)
return ""
- if catch_ids.is_empty() or _inventory == null:
+ if (
+ (catch_ids.is_empty() and item_quantities.is_empty())
+ or _inventory == null
+ or _bag == null
+ or _item_catalog == null
+ ):
local_sale_finished.emit(
"", false, "Sale could not be completed.", _empty_catch_ids(), 0
)
@@ -143,15 +197,43 @@ func request_local_sale(
)
return ""
evidence.append(fish_catch.to_network_dict())
+ var item_evidence: Array[Dictionary] = []
+ var item_ids: Array[StringName] = []
+ item_ids.assign(item_quantities.keys())
+ item_ids.sort_custom(
+ func(left: StringName, right: StringName) -> bool:
+ return str(left) < str(right)
+ )
+ for item_id: StringName in item_ids:
+ var quantity := int(item_quantities.get(item_id, 0))
+ var item: ItemDataType = _item_catalog.get_item_by_id(item_id)
+ if (
+ quantity < 1
+ or not ItemResalePolicyType.is_sellable(item)
+ or quantity > _bag.get_quantity(item_id)
+ or (
+ _reservations != null
+ and quantity > _reservations.get_available_item_quantity(item_id)
+ )
+ ):
+ local_sale_finished.emit(
+ "", false, "That item is no longer available.", _empty_catch_ids(), 0
+ )
+ return ""
+ item_evidence.append({"item_id": str(item_id), "quantity": quantity})
var request_id: String = _new_id("sale")
var request: Dictionary = {
"request_id": request_id,
"session_id": _session.get_session_id(),
"buyer_id": str(buyer.id),
"catches": evidence,
+ "items": item_evidence,
}
_pending_local_request_id = request_id
+ _pending_local_request = request.duplicate(true)
+ _local_request_retry_elapsed = 0.0
_pending_local_catch_ids = catch_ids.duplicate()
+ _pending_local_items = item_evidence.duplicate(true)
_pending_local_buyer_id = buyer.id
local_sale_pending.emit(request_id)
if _session.is_host():
@@ -219,7 +301,11 @@ func _handle_sale_request(peer_id: int, data: Dictionary) -> void:
))
return
var result: Dictionary = _build_authoritative_result(
- peer_id, request_id, data["catches"], buyer
+ peer_id,
+ request_id,
+ data["catches"],
+ data.get("items", []),
+ buyer,
)
_pending_by_peer[peer_id] = request_id
_record_and_send(peer_id, result)
@@ -229,9 +315,14 @@ func _build_authoritative_result(
peer_id: int,
request_id: String,
evidence_values: Array,
+ item_values: Array,
buyer: FishBuyerProfileType,
) -> Dictionary:
- if _fish_catalog == null or buyer == null:
+ if (
+ buyer == null
+ or (not evidence_values.is_empty() and _fish_catalog == null)
+ or (not item_values.is_empty() and _item_catalog == null)
+ ):
return _rejected_result(
request_id, "The buyer is unavailable."
)
@@ -283,6 +374,27 @@ func _build_authoritative_result(
base_value += decoded.sale_value
payout += offer
catch_ids.append(str(decoded.catch_id))
+ var items: Array[Dictionary] = []
+ if not item_values.is_empty() and buyer.id != MAIN_SHOP_BUYER_ID:
+ return _rejected_result(request_id, "This buyer only accepts catches.")
+ for value: Variant in item_values:
+ var evidence := value as Dictionary
+ var item_id := StringName(str(evidence.get("item_id", "")))
+ var quantity := int(evidence.get("quantity", 0))
+ var item: ItemDataType = _item_catalog.get_item_by_id(item_id)
+ var unit_value := ItemResalePolicyType.get_unit_value(item)
+ if quantity < 1 or unit_value < 0:
+ return _rejected_result(request_id, "That item cannot be sold.")
+ var item_value := unit_value * quantity
+ if (
+ item_value < 0
+ or base_value > 9223372036854775807 - item_value
+ or payout > 9223372036854775807 - item_value
+ ):
+ return _rejected_result(request_id, "Sale could not be completed.")
+ base_value += item_value
+ payout += item_value
+ items.append({"item_id": str(item_id), "quantity": quantity})
return {
"result_id": _new_id("sale_result"),
"request_id": request_id,
@@ -291,6 +403,7 @@ func _build_authoritative_result(
"buyer_id": str(buyer.id),
"accepted": true,
"catch_ids": catch_ids,
+ "items": items,
"payout": payout,
"base_value": base_value,
"message": "Sale complete.",
@@ -313,6 +426,7 @@ func _rejected_result(request_id: String, message: String) -> Dictionary:
"target_peer_id": 0,
"accepted": false,
"catch_ids": [],
+ "items": [],
"payout": 0,
"base_value": 0,
"message": message.left(NetworkSaleProtocol.MAX_MESSAGE_LENGTH),
@@ -388,6 +502,12 @@ func _apply_sale_result(data: Dictionary) -> void:
if catch_ids != _pending_local_catch_ids:
_fail_local_apply(data, "Sale could not be completed.")
return
+ var items: Array[Dictionary] = []
+ for value: Variant in data.get("items", []):
+ items.append((value as Dictionary).duplicate(true))
+ if items != _pending_local_items:
+ _fail_local_apply(data, "Sale could not be completed.")
+ return
for catch_id: StringName in catch_ids:
if _reservations != null and _reservations.is_fish_reserved(catch_id):
_fail_local_apply(data, "Reserved in a letter.")
@@ -396,10 +516,12 @@ func _apply_sale_result(data: Dictionary) -> void:
if buyer == null or not buyer.is_valid():
_fail_local_apply(data, "The buyer is unavailable.")
return
- var preview: FishSaleResultType = _sale_service.preview_batch(
- catch_ids, buyer
- )
- if not preview.is_success():
+ var catch_payout: int = 0
+ var catch_base_value: int = 0
+ var preview: FishSaleResultType
+ if not catch_ids.is_empty():
+ preview = _sale_service.preview_batch(catch_ids, buyer)
+ if preview != null and not preview.is_success():
var message: String = (
"Favorite catches cannot be sold."
if preview.status == FishSaleResultType.Status.FAVORITED
@@ -407,22 +529,63 @@ func _apply_sale_result(data: Dictionary) -> void:
)
_fail_local_apply(data, message)
return
+ if preview != null:
+ catch_payout = preview.payout
+ catch_base_value = preview.base_value
+ var item_payout: int = 0
+ for record: Dictionary in items:
+ var item_id := StringName(str(record.get("item_id", "")))
+ var quantity := int(record.get("quantity", 0))
+ var item: ItemDataType = _item_catalog.get_item_by_id(item_id)
+ var unit_value := ItemResalePolicyType.get_unit_value(item)
+ if (
+ quantity < 1
+ or unit_value < 0
+ or quantity > _bag.get_quantity(item_id)
+ or (
+ _reservations != null
+ and quantity > _reservations.get_available_item_quantity(item_id)
+ )
+ ):
+ _fail_local_apply(data, "That item is no longer available.")
+ return
+ item_payout += unit_value * quantity
if (
- preview.payout != int(data["payout"])
- or preview.base_value != int(data["base_value"])
+ catch_payout + item_payout != int(data["payout"])
+ or catch_base_value + item_payout != int(data["base_value"])
):
_fail_local_apply(data, "Sale could not be completed.")
return
var inventory_snapshot: Array[FishCatch] = _inventory.get_all_catches()
var sequence_snapshot: int = _inventory.get_next_catch_sequence()
- var wallet_snapshot: int = _wallet.get_balance()
- var local_result: FishSaleResultType = _sale_service.sell_batch(
- catch_ids, buyer
+ var bag_snapshot := _bag.get_all_items()
+ var layout_snapshot: Dictionary = (
+ _inventory_layout.to_save_data()
+ if _inventory_layout != null else {}
)
- if not local_result.is_success() or not _save_manager.save_if_dirty():
+ var wallet_snapshot: int = _wallet.get_balance()
+ var applied: bool = true
+ if not catch_ids.is_empty():
+ applied = (
+ _inventory.remove_catches_by_ids(catch_ids).size()
+ == catch_ids.size()
+ )
+ if applied:
+ for record: Dictionary in items:
+ if not _bag.remove_item(
+ StringName(str(record["item_id"])), int(record["quantity"])
+ ):
+ applied = false
+ break
+ if applied:
+ applied = _wallet.credit(int(data["payout"]))
+ if not applied or not _save_manager.save_if_dirty():
_inventory.replace_all_catches(
inventory_snapshot, sequence_snapshot
)
+ _bag.replace_all_items(bag_snapshot)
+ if _inventory_layout != null:
+ _inventory_layout.restore_from_save_data(layout_snapshot)
_wallet.restore_balance(wallet_snapshot)
_save_manager.save_if_dirty()
_fail_local_apply(data, "Sale could not be completed.")
@@ -456,7 +619,10 @@ func _finish_local_sale(
payout: int,
) -> void:
_pending_local_request_id = ""
+ _pending_local_request.clear()
+ _local_request_retry_elapsed = 0.0
_pending_local_catch_ids.clear()
+ _pending_local_items.clear()
_pending_local_buyer_id = StringName()
local_sale_finished.emit(
request_id, accepted, message, catch_ids, payout
@@ -590,7 +756,10 @@ func _clear_session_state() -> void:
_applied_results.clear()
_received_results.clear()
_pending_local_request_id = ""
+ _pending_local_request.clear()
+ _local_request_retry_elapsed = 0.0
_pending_local_catch_ids.clear()
+ _pending_local_items.clear()
_pending_local_buyer_id = StringName()
diff --git a/network/network_session.gd b/network/network_session.gd
index 3ad3892..7c259e0 100644
--- a/network/network_session.gd
+++ b/network/network_session.gd
@@ -7,8 +7,51 @@ 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 ENET_TIMEOUT_LIMIT: int = 32
+const ENET_TIMEOUT_MINIMUM_MS: int = 10000
+const ENET_TIMEOUT_MAXIMUM_MS: int = 120000
const INPUT_INTERVAL: float = 1.0 / 30.0
+const IDLE_INPUT_INTERVAL: float = 1.0 / 5.0
const SNAPSHOT_INTERVAL: float = 1.0 / 30.0
+const OWNER_SNAPSHOT_DIVISOR: int = 3
+const NEAR_REMOTE_SNAPSHOT_DIVISOR: int = 2
+const FAR_REMOTE_SNAPSHOT_DIVISOR: int = 6
+const DISTANT_REMOTE_SNAPSHOT_DIVISOR: int = 8
+const NEAR_REMOTE_DISTANCE: float = 48.0
+const FAR_REMOTE_DISTANCE: float = 96.0
+const MOVEMENT_SNAPSHOT_BATCH_SIZE: int = 8
+const MOVEMENT_INPUT_FIELD_COUNT: int = 8
+const MOVEMENT_SNAPSHOT_FIELD_COUNT: int = 6
+const MOVEMENT_ANIMATION_FIELD_COUNT: int = 7
+const MOVEMENT_ANIMATION_ACTION_FIELD_COUNT: int = 5
+const MAX_PENDING_MOVEMENT_INPUTS: int = 96
+const ANIMATION_REFRESH_INTERVAL: float = 1.0
+const MAX_MOVEMENT_INPUT_SEQUENCE: int = 2147483647
+const MAX_MOVEMENT_ONE_WAY_TRANSIT_SECONDS: float = 0.25
+const SPACE_TRANSITION_SNAPSHOT_GUARD_DISTANCE: float = 12.0
+
+const MOVEMENT_FLAG_JUMP: int = 1 << 0
+const MOVEMENT_FLAG_SPRINT: int = 1 << 1
+const MOVEMENT_FLAG_SNEAK: int = 1 << 2
+const MOVEMENT_FLAG_SLOW_WALK: int = 1 << 3
+const MOVEMENT_FLAG_SITTING: int = 1 << 4
+const MOVEMENT_FLAG_CASTING: int = 1 << 5
+const MOVEMENT_ALLOWED_FLAGS: int = (
+ MOVEMENT_FLAG_JUMP
+ | MOVEMENT_FLAG_SPRINT
+ | MOVEMENT_FLAG_SNEAK
+ | MOVEMENT_FLAG_SLOW_WALK
+ | MOVEMENT_FLAG_SITTING
+ | MOVEMENT_FLAG_CASTING
+)
+const SNAPSHOT_FLAG_GROUNDED: int = 1 << 0
+const SNAPSHOT_FLAG_SITTING: int = 1 << 1
+const SNAPSHOT_FLAG_CASTING: int = 1 << 2
+const SNAPSHOT_ALLOWED_FLAGS: int = (
+ SNAPSHOT_FLAG_GROUNDED
+ | SNAPSHOT_FLAG_SITTING
+ | SNAPSHOT_FLAG_CASTING
+)
signal state_changed(state: State)
signal status_message_changed(message: String)
@@ -32,7 +75,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,
@@ -75,12 +119,33 @@ var _client_nonce: String = ""
var _current_route: ConnectionRoute
var _input_sequence: int = 0
var _input_accumulator: float = 0.0
+var _idle_input_accumulator: float = 0.0
+var _last_input_state_hash: int = 0
+var _pending_movement_inputs: Array[Dictionary] = []
var _snapshot_accumulator: float = 0.0
+var _movement_snapshot_tick: int = 0
+var _last_local_snapshot_received_msec: int = 0
+var _local_space_transition_guard_active: bool = false
+var _local_space_transition_guard_position: Vector3 = Vector3.ZERO
+var _local_space_transition_minimum_ack: int = 0
+var _animation_refresh_accumulator: float = 0.0
+var _last_animation_state_by_peer: Dictionary[int, Dictionary] = {}
+var _pending_animation_state_by_peer: Dictionary[int, Dictionary] = {}
+var _last_local_animation_action_signature: Array = []
+var _movement_inputs_sent: int = 0
+var _movement_inputs_received: int = 0
+var _movement_snapshot_packets_sent: int = 0
+var _movement_snapshot_states_sent: int = 0
+var _movement_animation_packets_sent: int = 0
+var _movement_animation_states_sent: int = 0
var _last_server_max_players: int = DEFAULT_SESSION_MAX_PLAYERS
var _last_server_player_count: int = 0
var _last_server_display_name: String = ""
var _last_server_protocol_version: int = 0
+var _last_server_world_seed: int = NetworkProtocol.DEFAULT_WORLD_SEED
+var _last_server_world_layout: StringName = WorldLayout.GENERATED
var _server_capabilities: PackedStringArray = PackedStringArray()
+var _peer_space_ids: Dictionary[int, StringName] = {}
var _profile_ready: bool = false
var _player_identity: PlayerIdentityStore
var _host_identity: HostIdentityStore
@@ -100,8 +165,14 @@ var _local_appearance_snapshot: Dictionary = (
)
var _moderation_disconnect_message := ""
var _host_port: int = 0
-var _session_display_name: String = "NETfishing Room"
+var _session_display_name: String = "straywild 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
+var _host_world_seed: int = NetworkProtocol.DEFAULT_WORLD_SEED
+var _host_world_layout: StringName = WorldLayout.GENERATED
func _ready() -> void:
@@ -137,6 +208,20 @@ func setup(
_profile_ready = _profile_ready and _player_identity.load_or_create()
+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,
@@ -215,6 +300,9 @@ func _start_host(
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()
@@ -244,6 +332,15 @@ func _register_player_host() -> void:
NetworkProtocol.WORLD_TIME_CAPABILITY,
NetworkProtocol.WORLD_WEATHER_CAPABILITY,
NetworkProtocol.JOBS_CAPABILITY,
+ NetworkProtocol.WORLD_SPAWN_CAPABILITY,
+ NetworkProtocol.BACKPACK_SHOP_CAPABILITY,
+ NetworkProtocol.WORLD_GENERATION_CAPABILITY,
+ NetworkProtocol.WORLD_LAYOUT_CAPABILITY,
+ NetworkProtocol.FRIENDS_CAPABILITY,
+ NetworkProtocol.MOVEMENT_RECONCILIATION_CAPABILITY,
+ NetworkProtocol.FISHING_REPLICATION_CAPABILITY,
+ NetworkProtocol.HOME_CAPABILITY,
+ NetworkProtocol.HOME_UPGRADE_CAPABILITY,
]),
)
_registry.update_appearance(1, _local_appearance_snapshot)
@@ -301,10 +398,48 @@ 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"
+ cleaned = "straywild Room"
if cleaned == _session_display_name:
return
_session_display_name = cleaned
@@ -315,6 +450,10 @@ 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)
@@ -457,9 +596,40 @@ func get_last_server_metadata() -> Dictionary:
"protocol_version": _last_server_protocol_version,
"player_count": _last_server_player_count,
"max_players": _last_server_max_players,
+ "world_seed": _last_server_world_seed,
+ "world_layout": String(_last_server_world_layout),
}
+func set_host_world_seed(seed: int) -> bool:
+ return set_host_world(WorldLayout.GENERATED, seed)
+
+
+func set_host_world(world_layout: StringName, seed: int) -> bool:
+ if (
+ state != State.INACTIVE
+ or not WorldLayout.is_valid(world_layout)
+ or seed <= 0
+ or seed > NetworkProtocol.MAX_WORLD_SEED
+ ):
+ return false
+ _host_world_layout = world_layout
+ _host_world_seed = seed
+ return true
+
+
+func get_authoritative_world_layout() -> StringName:
+ return (
+ _last_server_world_layout
+ if is_joined_client()
+ else _host_world_layout
+ )
+
+
+func get_authoritative_world_seed() -> int:
+ return _last_server_world_seed if is_joined_client() else _host_world_seed
+
+
func can_use_host_gameplay() -> bool:
return is_host()
@@ -485,11 +655,21 @@ func supports_server_capability(capability: StringName) -> bool:
return str(capability) in PackedStringArray([
"movement_v1", "fishing_v1", "sale_v1", "shop_v1",
NetworkProtocol.ART_SHOP_CAPABILITY,
+ NetworkProtocol.BACKPACK_SHOP_CAPABILITY,
"item_use_v1", "equipment_v1", "fish_showcase_v1",
NetworkProtocol.FISH_QUALITY_CAPABILITY,
NetworkProtocol.SURFACE_DRAWING_CAPABILITY,
NetworkProtocol.WORLD_TIME_CAPABILITY,
NetworkProtocol.WORLD_WEATHER_CAPABILITY,
+ NetworkProtocol.WORLD_SPAWN_CAPABILITY,
+ NetworkProtocol.APPEARANCE_PREVIEW_CAPABILITY,
+ NetworkProtocol.WORLD_GENERATION_CAPABILITY,
+ NetworkProtocol.WORLD_LAYOUT_CAPABILITY,
+ NetworkProtocol.FRIENDS_CAPABILITY,
+ NetworkProtocol.MOVEMENT_RECONCILIATION_CAPABILITY,
+ NetworkProtocol.FISHING_REPLICATION_CAPABILITY,
+ NetworkProtocol.HOME_CAPABILITY,
+ NetworkProtocol.HOME_UPGRADE_CAPABILITY,
"chat_v1",
"mail_v1",
"profile_v1",
@@ -514,6 +694,55 @@ func get_authenticated_peer_ids() -> Array[int]:
return _registry.get_peer_ids()
+func set_peer_space(peer_id: int, space_id: StringName) -> bool:
+ if not _registry.has_peer(peer_id) or space_id.is_empty():
+ return false
+ _peer_space_ids[peer_id] = space_id
+ return true
+
+
+func get_peer_space(peer_id: int) -> StringName:
+ return _peer_space_ids.get(peer_id, &"world")
+
+
+func peers_share_space(first_peer_id: int, second_peer_id: int) -> bool:
+ return get_peer_space(first_peer_id) == get_peer_space(second_peer_id)
+
+
+func apply_authoritative_space_transition(
+ peer_id: int,
+ target_transform: Transform3D,
+ acknowledged_input: int,
+) -> bool:
+ if not target_transform.origin.is_finite():
+ return false
+ var local_peer_id: int = get_local_peer_id()
+ var local_transition: bool = peer_id == local_peer_id
+ var avatar: Player = _spawn_service.get_avatar(peer_id)
+ # Remote home transitions can arrive before their player-spawn message. Keep
+ # the new space assignment so the later spawn inherits correct visibility;
+ # only a missing local avatar is a transition failure.
+ if avatar == null:
+ return not local_transition
+ avatar.apply_network_space_transition(target_transform, local_transition)
+ if (
+ local_transition
+ and state == State.JOINED_CLIENT
+ ):
+ # Movement snapshots use another ENet channel and a pre-transition audit
+ # can therefore arrive after the reliable home transition. Clear local
+ # prediction and reject those old-world snapshots until the host publishes
+ # its first audit from the new space.
+ _pending_movement_inputs.clear()
+ _last_local_snapshot_received_msec = 0
+ _last_input_state_hash = 0
+ _idle_input_accumulator = IDLE_INPUT_INTERVAL
+ _local_space_transition_guard_active = true
+ _local_space_transition_guard_position = target_transform.origin
+ _local_space_transition_minimum_ack = maxi(acknowledged_input, 0)
+ return true
+
+
func get_peer_rtt_ms(peer_id: int) -> int:
if peer_id == 1:
return 0
@@ -537,6 +766,18 @@ func get_peer_rtt_ms(peer_id: int) -> int:
return int(packet_peer.get_statistic(ENetPacketPeer.PEER_ROUND_TRIP_TIME))
+func get_movement_metrics() -> Dictionary:
+ return {
+ "inputs_sent": _movement_inputs_sent,
+ "inputs_received": _movement_inputs_received,
+ "snapshot_packets_sent": _movement_snapshot_packets_sent,
+ "snapshot_states_sent": _movement_snapshot_states_sent,
+ "animation_packets_sent": _movement_animation_packets_sent,
+ "animation_states_sent": _movement_animation_states_sent,
+ "pending_local_inputs": _pending_movement_inputs.size(),
+ }
+
+
func kick_authenticated_peer(
peer_id: int,
fingerprint: String,
@@ -701,6 +942,10 @@ func get_local_peer_id() -> int:
return multiplayer.get_unique_id() if is_gameplay_session_active() else 0
+func get_latest_movement_input_sequence() -> int:
+ return _input_sequence if state == State.JOINED_CLIENT else 0
+
+
func _process(delta: float) -> void:
var now: float = Time.get_ticks_msec() / 1000.0
if (
@@ -721,9 +966,13 @@ func _process(delta: float) -> void:
return
_input_accumulator += delta
_snapshot_accumulator += delta
+ _animation_refresh_accumulator += delta
if state == State.JOINED_CLIENT and _input_accumulator >= INPUT_INTERVAL:
+ var elapsed_input_time: float = _input_accumulator
_input_accumulator = fmod(_input_accumulator, INPUT_INTERVAL)
- _send_local_input()
+ _idle_input_accumulator += elapsed_input_time
+ _maybe_send_local_animation_action()
+ _maybe_send_local_input()
if is_host() and _snapshot_accumulator >= SNAPSHOT_INTERVAL:
_snapshot_accumulator = fmod(_snapshot_accumulator, SNAPSHOT_INTERVAL)
_broadcast_movement_snapshots()
@@ -761,6 +1010,7 @@ func _on_peer_connected(peer_id: int) -> void:
if state != State.OPEN_HOST:
multiplayer.multiplayer_peer.disconnect_peer(peer_id)
return
+ _configure_enet_peer_timeout(peer_id)
_pending_authentication[peer_id] = (
Time.get_ticks_msec() / 1000.0 + AUTHENTICATION_TIMEOUT_SECONDS
)
@@ -769,6 +1019,7 @@ func _on_peer_connected(peer_id: int) -> void:
func _on_connected_to_server() -> void:
if state != State.CONNECTING:
return
+ _configure_enet_peer_timeout(1)
_set_state(State.AUTHENTICATING, "Authenticating...")
_client_nonce = NetworkIdentityCrypto.secure_id(32)
_client_identity_attempt = NetworkProtocol.make_identity_hello(
@@ -780,6 +1031,24 @@ func _on_connected_to_server() -> void:
submit_identity_hello.rpc_id(1, _client_identity_attempt)
+func _configure_enet_peer_timeout(peer_id: int) -> void:
+ var enet := multiplayer.multiplayer_peer as ENetMultiplayerPeer
+ if enet == null:
+ return
+ var packet_peer: ENetPacketPeer = enet.get_peer(peer_id)
+ if packet_peer == null:
+ return
+ # A deterministic generated world can temporarily occupy the client's main
+ # thread during the authenticated join transition. Keep that bounded load
+ # from looking like a dead connection while retaining normal ENet liveness
+ # checks once the client resumes polling.
+ packet_peer.set_timeout(
+ ENET_TIMEOUT_LIMIT,
+ ENET_TIMEOUT_MINIMUM_MS,
+ ENET_TIMEOUT_MAXIMUM_MS,
+ )
+
+
func _on_connection_failed() -> void:
if state not in [
State.CONNECTING,
@@ -810,7 +1079,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)
@@ -818,6 +1087,10 @@ 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)
+ _peer_space_ids.erase(peer_id)
+ _last_animation_state_by_peer.erase(peer_id)
+ _pending_animation_state_by_peer.erase(peer_id)
var recovery_attempt: String = _recovery_attempts.get(peer_id, "")
if not recovery_attempt.is_empty():
_recovery_attempts.erase(peer_id)
@@ -838,6 +1111,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
@@ -868,8 +1151,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,
@@ -886,7 +1171,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", ""))
@@ -943,7 +1246,7 @@ func resolve_server_trust(accepted: bool) -> void:
if not _server_trust.trust(
get_current_endpoint(),
str(_pending_server_proof["server_fingerprint"]),
- "NETfishing",
+ "straywild",
):
_fail_identity("The server identity could not be pinned.")
return
@@ -995,6 +1298,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
@@ -1011,7 +1315,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)
@@ -1041,6 +1344,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", [])
)
@@ -1050,12 +1363,39 @@ func submit_client_hello(data: Dictionary) -> void:
NetworkProtocol.RejectionCode.UNSUPPORTED_CLIENT,
)
return
+ if NetworkProtocol.WORLD_GENERATION_CAPABILITY not in client_capabilities:
+ _reject_peer(
+ sender_id,
+ NetworkProtocol.RejectionCode.UNSUPPORTED_CLIENT,
+ )
+ return
+ for required_capability: String in [
+ NetworkProtocol.MOVEMENT_RECONCILIATION_CAPABILITY,
+ NetworkProtocol.FISHING_REPLICATION_CAPABILITY,
+ NetworkProtocol.HOME_CAPABILITY,
+ ]:
+ if required_capability not in client_capabilities:
+ _reject_peer(
+ sender_id,
+ NetworkProtocol.RejectionCode.UNSUPPORTED_CLIENT,
+ )
+ return
+ if (
+ _host_world_layout == WorldLayout.STARTER_ISLAND
+ and NetworkProtocol.WORLD_LAYOUT_CAPABILITY not in client_capabilities
+ ):
+ _reject_peer(
+ sender_id,
+ NetworkProtocol.RejectionCode.UNSUPPORTED_CLIENT,
+ )
+ return
var identity: Dictionary = _authenticated_identity_cache.get(sender_id, {})
if identity.is_empty():
_reject_peer(sender_id, NetworkProtocol.RejectionCode.INVALID_IDENTITY_PROOF)
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",
@@ -1090,6 +1430,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"]
)
@@ -1131,12 +1476,17 @@ func submit_client_hello(data: Dictionary) -> void:
_registry.size(),
session_max_players,
_session_display_name,
+ _host_world_seed,
+ _host_world_layout,
)
)
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()
@@ -1179,7 +1529,12 @@ 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
+ or typeof(data.get("world_seed")) != TYPE_INT
+ or typeof(
+ data.get("world_layout", String(WorldLayout.GENERATED))
+ ) not in [TYPE_STRING, TYPE_STRING_NAME]
):
_teardown_peer()
_fail("The server sent an invalid handshake response.")
@@ -1191,10 +1546,33 @@ 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.")
return
+ var received_world_seed := int(data["world_seed"])
+ if received_world_seed <= 0 or received_world_seed > NetworkProtocol.MAX_WORLD_SEED:
+ _teardown_peer()
+ _fail("The server sent an invalid world seed.")
+ return
+ var received_world_layout := WorldLayout.normalized(
+ data.get("world_layout", String(WorldLayout.GENERATED)),
+ &"",
+ )
+ if received_world_layout.is_empty():
+ _teardown_peer()
+ _fail("The server sent an invalid world layout.")
+ return
_session_id = str(data.get("session_id", ""))
_last_server_max_players = int(data.get(
"max_players",
@@ -1205,6 +1583,8 @@ func receive_server_hello(data: Dictionary) -> void:
_last_server_protocol_version = int(data.get(
"protocol_version", NetworkProtocol.PROTOCOL_VERSION
))
+ _last_server_world_seed = received_world_seed
+ _last_server_world_layout = received_world_layout
_server_capabilities = PackedStringArray()
var advertised_capabilities: Variant = data.get(
"capability_flags", PackedStringArray()
@@ -1219,6 +1599,26 @@ func receive_server_hello(data: Dictionary) -> void:
_teardown_peer()
_fail("This server does not support fish quality data.")
return
+ if NetworkProtocol.WORLD_GENERATION_CAPABILITY not in _server_capabilities:
+ _teardown_peer()
+ _fail("This server does not support generated worlds.")
+ return
+ for required_capability: String in [
+ NetworkProtocol.MOVEMENT_RECONCILIATION_CAPABILITY,
+ NetworkProtocol.FISHING_REPLICATION_CAPABILITY,
+ NetworkProtocol.HOME_CAPABILITY,
+ ]:
+ if required_capability not in _server_capabilities:
+ _teardown_peer()
+ _fail("This server is missing required network capabilities.")
+ return
+ if (
+ received_world_layout == WorldLayout.STARTER_ISLAND
+ and NetworkProtocol.WORLD_LAYOUT_CAPABILITY not in _server_capabilities
+ ):
+ _teardown_peer()
+ _fail("This server does not support selectable world layouts.")
+ return
var local_peer_id: int = multiplayer.get_unique_id()
_registry.clear()
_registry.add_peer(
@@ -1233,6 +1633,14 @@ func receive_server_hello(data: Dictionary) -> void:
NetworkProtocol.SURFACE_DRAWING_CAPABILITY,
NetworkProtocol.WORLD_TIME_CAPABILITY,
NetworkProtocol.WORLD_WEATHER_CAPABILITY,
+ NetworkProtocol.BACKPACK_SHOP_CAPABILITY,
+ NetworkProtocol.WORLD_GENERATION_CAPABILITY,
+ NetworkProtocol.WORLD_LAYOUT_CAPABILITY,
+ NetworkProtocol.FRIENDS_CAPABILITY,
+ NetworkProtocol.MOVEMENT_RECONCILIATION_CAPABILITY,
+ NetworkProtocol.FISHING_REPLICATION_CAPABILITY,
+ NetworkProtocol.HOME_CAPABILITY,
+ NetworkProtocol.HOME_UPGRADE_CAPABILITY,
]),
)
_registry.update_appearance(local_peer_id, _local_appearance_snapshot)
@@ -1277,12 +1685,44 @@ 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)
+ _peer_space_ids.erase(peer_id)
+ _last_animation_state_by_peer.erase(peer_id)
+ _pending_animation_state_by_peer.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
@@ -1290,6 +1730,10 @@ func _apply_spawn_entry(entry: Dictionary) -> void:
or typeof(entry.get("display_name")) != TYPE_STRING
or typeof(entry.get("position")) != TYPE_ARRAY
or typeof(entry.get("yaw")) not in [TYPE_FLOAT, TYPE_INT]
+ or typeof(entry.get("sitting")) != TYPE_BOOL
+ or not NetworkPlayerAnimationProtocol.validate_state(
+ entry.get("animation_state")
+ )
):
return
if not _verify_spawn_identity(entry):
@@ -1305,10 +1749,12 @@ func _apply_spawn_entry(entry: Dictionary) -> void:
)
own_snapshot["position"] = own_position
own_snapshot["visual_yaw"] = float(entry["yaw"])
+ own_snapshot["sitting"] = bool(entry["sitting"])
own_avatar.apply_network_teleport(own_snapshot)
return
+ var peer_was_added: bool = false
if not _registry.has_peer(peer_id):
- _registry.add_peer(
+ if not _registry.add_peer(
peer_id,
entry["profile_id"],
entry["display_name"],
@@ -1316,8 +1762,14 @@ func _apply_spawn_entry(entry: Dictionary) -> void:
str(entry.get("identity_fingerprint", "")),
str(entry.get("identity_public_key", "")),
_sanitized_capabilities(entry.get("capability_flags", [])),
- )
+ ):
+ return
+ peer_was_added = true
var added_record := _registry.get_peer(peer_id)
+ if added_record != null:
+ added_record.profile_authorization = Dictionary(
+ entry.get("profile_authorization", {})
+ ).duplicate(true)
if added_record != null and added_record.identity_authenticated:
_archive_authenticated_identity(added_record)
var status := _known_players.observe(
@@ -1344,6 +1796,20 @@ func _apply_spawn_entry(entry: Dictionary) -> void:
var record := _registry.get_peer(peer_id)
if avatar != null and record != null:
avatar.apply_appearance_snapshot(record.appearance_snapshot)
+ # Lifecycle and animation updates use separate reliable ENet channels.
+ # Include the current authoritative state in the spawn record so a peer
+ # joining during an action presents it immediately, regardless of which
+ # channel arrives first.
+ avatar.apply_network_animation_state(entry["animation_state"])
+ avatar.apply_network_sitting_state(bool(entry["sitting"]))
+ var pending_animation: Dictionary = (
+ _pending_animation_state_by_peer.get(peer_id, {})
+ )
+ if not pending_animation.is_empty():
+ avatar.apply_network_animation_state(pending_animation)
+ _pending_animation_state_by_peer.erase(peer_id)
+ if peer_was_added and record != null:
+ peer_authenticated.emit(peer_id, record.display_name)
func _build_spawn_list() -> Array[Dictionary]:
@@ -1366,6 +1832,15 @@ func _make_spawn_entry(
transform: Transform3D,
) -> Dictionary:
var record: PeerRegistry.PeerRecord = _registry.get_peer(peer_id)
+ var avatar: Player = _spawn_service.get_avatar(peer_id)
+ var animation_state: Dictionary = (
+ avatar.make_network_animation_state()
+ if avatar != null
+ else NetworkPlayerAnimationProtocol.make_state(
+ NetworkPlayerAnimationProtocol.LOCOMOTION_IDLE,
+ true,
+ )
+ )
return {
"peer_id": peer_id,
"profile_id": record.profile_id if record != null else "",
@@ -1376,6 +1851,12 @@ func _make_spawn_entry(
transform.origin.z,
],
"yaw": transform.basis.get_euler().y,
+ "sitting": (
+ avatar.get_network_sitting_state()
+ if avatar != null
+ else false
+ ),
+ "animation_state": animation_state,
"appearance": (
record.appearance_snapshot.duplicate(true)
if record != null
@@ -1413,25 +1894,92 @@ func _sanitized_capabilities(value: Variant) -> PackedStringArray:
return result
-func _verify_spawn_identity(entry: Dictionary) -> bool:
- var fingerprint := str(entry.get("identity_fingerprint", ""))
- var public_pem := NetworkIdentityCrypto.normalize_public_pem(
- str(entry.get("identity_public_key", ""))
- )
- if (
- NetworkIdentityCrypto.fingerprint_public_pem(public_pem) != fingerprint
- or typeof(entry.get("profile_authorization")) != TYPE_DICTIONARY
- ):
+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_profile_authorization(
+ peer_id: int,
+ display_name: String,
+ appearance: Dictionary,
+ authorization: Dictionary,
+) -> bool:
+ var record := _registry.get_peer(peer_id)
+ if record == null:
+ return false
+ return _verify_profile_authorization_fields(
+ peer_id,
+ record.profile_id,
+ display_name,
+ appearance,
+ record.identity_fingerprint,
+ record.identity_public_key,
+ authorization,
+ )
+
+
+func _verify_spawn_identity(entry: Dictionary) -> bool:
+ if typeof(entry.get("profile_authorization")) != TYPE_DICTIONARY:
+ return false
+ return _verify_profile_authorization_fields(
+ int(entry.get("peer_id", 0)),
+ str(entry.get("profile_id", "")),
+ str(entry.get("display_name", "")),
+ Dictionary(entry.get("appearance", {})),
+ str(entry.get("identity_fingerprint", "")),
+ str(entry.get("identity_public_key", "")),
+ Dictionary(entry.get("profile_authorization", {})),
+ )
+
+
+func _verify_profile_authorization_fields(
+ peer_id: int,
+ profile_id: String,
+ display_name: String,
+ appearance: Dictionary,
+ fingerprint: String,
+ identity_public_key: String,
+ authorization: Dictionary,
+) -> bool:
+ var public_pem := NetworkIdentityCrypto.normalize_public_pem(
+ identity_public_key
+ )
+ if NetworkIdentityCrypto.fingerprint_public_pem(public_pem) != fingerprint:
return false
- var authorization: Dictionary = entry["profile_authorization"]
if authorization.is_empty():
- return int(entry.get("peer_id", 0)) == 1
+ return peer_id == 1
if authorization.get("domain") == "handshake_client_profile":
var hello := NetworkProtocol.make_client_hello(
- str(entry.get("profile_id", "")),
- str(entry.get("display_name", "")),
+ profile_id,
+ display_name,
str(authorization.get("client_nonce", "")),
- Dictionary(entry.get("appearance", {})),
+ appearance,
fingerprint,
authorization.get("signature", PackedByteArray()),
)
@@ -1441,10 +1989,23 @@ func _verify_spawn_identity(entry: Dictionary) -> bool:
NetworkProtocol.client_profile_fields(hello),
hello["identity_signature"],
)
+ if authorization.get("domain") == "appearance_preview":
+ if str(authorization.get("sender_fingerprint", "")) != fingerprint:
+ return false
+ var preview := authorization.duplicate(true)
+ preview["appearance"] = appearance
+ return NetworkIdentityCrypto.verify_fields(
+ NetworkIdentityCrypto.load_public_key(public_pem),
+ "appearance_preview",
+ NetworkProfileProtocol.appearance_preview_signature_fields(preview),
+ preview.get("sender_signature", PackedByteArray()),
+ )
if authorization.has("sender_signature"):
+ if str(authorization.get("sender_fingerprint", "")) != fingerprint:
+ return false
var signed := authorization.duplicate(true)
- signed["display_name"] = entry.get("display_name", "")
- signed["appearance"] = entry.get("appearance", {})
+ signed["display_name"] = display_name
+ signed["appearance"] = appearance
return NetworkIdentityCrypto.verify_fields(
NetworkIdentityCrypto.load_public_key(public_pem),
"profile_update",
@@ -1457,6 +2018,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", "")),
@@ -1497,6 +2060,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
@@ -1515,14 +2082,69 @@ func _fail_identity(message: String) -> void:
_fail(message)
+func _maybe_send_local_input() -> void:
+ var peer_id: int = multiplayer.get_unique_id()
+ var avatar: Player = _spawn_service.get_avatar(peer_id)
+ if avatar == null:
+ return
+ var state_hash: int = avatar.get_network_input_state_hash()
+ var state_changed: bool = state_hash != _last_input_state_hash
+ if (
+ avatar.has_active_network_input()
+ or state_changed
+ or _idle_input_accumulator >= IDLE_INPUT_INTERVAL
+ ):
+ _send_local_input()
+ _last_input_state_hash = state_hash
+ _idle_input_accumulator = 0.0
+
+
func _send_local_input() -> void:
var peer_id: int = multiplayer.get_unique_id()
var avatar: Player = _spawn_service.get_avatar(peer_id)
if avatar == null:
return
- _input_sequence += 1
+ _input_sequence = (
+ 1
+ if _input_sequence >= MAX_MOVEMENT_INPUT_SEQUENCE
+ else _input_sequence + 1
+ )
var input: Dictionary = avatar.capture_network_input(_input_sequence)
- submit_movement_input.rpc_id(1, input)
+ var encoded: Array = _encode_movement_input(input)
+ if encoded.is_empty():
+ return
+ _pending_movement_inputs.append(input.duplicate(true))
+ while _pending_movement_inputs.size() > MAX_PENDING_MOVEMENT_INPUTS:
+ _pending_movement_inputs.pop_front()
+ _movement_inputs_sent += 1
+ submit_movement_input.rpc_id(1, encoded)
+
+
+func _maybe_send_local_animation_action() -> void:
+ var peer_id: int = multiplayer.get_unique_id()
+ var avatar: Player = _spawn_service.get_avatar(peer_id)
+ if avatar == null:
+ return
+ var state: Dictionary = avatar.make_network_animation_state()
+ var action: Dictionary = state.get("action", {})
+ if not NetworkPlayerAnimationProtocol.validate_action_state(action):
+ return
+ var signature: Array = [
+ str(action["id"]),
+ int(action["sequence"]),
+ bool(action.get("paused", false)),
+ avatar.get_network_sitting_intent(),
+ ]
+ if signature == _last_local_animation_action_signature:
+ return
+ var encoded: Array = _encode_movement_animation_action(
+ action,
+ avatar.get_network_sitting_intent(),
+ )
+ if encoded.is_empty():
+ return
+ _last_local_animation_action_signature = signature
+ submit_movement_animation_action.rpc_id(1, encoded)
func submit_neutral_local_movement() -> void:
@@ -1532,17 +2154,112 @@ func submit_neutral_local_movement() -> void:
@rpc("any_peer", "call_remote", "unreliable_ordered", 1)
-func submit_movement_input(data: Dictionary) -> void:
+func submit_movement_input(encoded: Array) -> void:
var sender_id: int = multiplayer.get_remote_sender_id()
if not is_host() or not _registry.has_peer(sender_id):
return
var avatar: Player = _spawn_service.get_avatar(sender_id)
- if avatar == null or not _is_valid_movement_input(data):
+ var data: Dictionary = _decode_movement_input(encoded)
+ if avatar == null or data.is_empty():
return
- avatar.apply_authoritative_network_input(data)
+ _movement_inputs_received += 1
+ avatar.apply_authoritative_network_input(
+ data,
+ Player.resolve_network_input_stale_timeout_seconds(
+ get_peer_rtt_ms(sender_id)
+ ),
+ )
-func _is_valid_movement_input(data: Dictionary) -> bool:
+@rpc(
+ "any_peer",
+ "call_remote",
+ "reliable",
+ NetworkProtocol.MOVEMENT_ANIMATION_CHANNEL,
+)
+func submit_movement_animation_action(encoded: Array) -> void:
+ var sender_id: int = multiplayer.get_remote_sender_id()
+ if not is_host() or not _registry.has_peer(sender_id):
+ return
+ var avatar: Player = _spawn_service.get_avatar(sender_id)
+ var state: Dictionary = _decode_movement_animation_action(encoded)
+ if avatar == null or state.is_empty():
+ return
+ avatar.apply_authoritative_network_animation_action(state["action"])
+ avatar.apply_authoritative_network_sitting_state(bool(state["sitting"]))
+
+
+static func _encode_movement_input(data: Dictionary) -> Array:
+ if not _is_valid_movement_input(data):
+ return []
+ var axis: Array = data["axis"]
+ var flags: int = 0
+ if bool(data["jump"]):
+ flags |= MOVEMENT_FLAG_JUMP
+ if bool(data["sprint"]):
+ flags |= MOVEMENT_FLAG_SPRINT
+ if bool(data["sneak"]):
+ flags |= MOVEMENT_FLAG_SNEAK
+ if bool(data["slow_walk"]):
+ flags |= MOVEMENT_FLAG_SLOW_WALK
+ if bool(data["sitting"]):
+ flags |= MOVEMENT_FLAG_SITTING
+ if bool(data["casting"]):
+ flags |= MOVEMENT_FLAG_CASTING
+ var action: Dictionary = data["animation_action"]
+ return [
+ int(data["sequence"]),
+ Vector2(float(axis[0]), float(axis[1])),
+ float(data["camera_yaw"]),
+ flags,
+ str(action["id"]),
+ int(action["sequence"]),
+ float(action["elapsed"]),
+ bool(action.get("paused", false)),
+ ]
+
+
+static func _decode_movement_input(value: Variant) -> Dictionary:
+ if typeof(value) != TYPE_ARRAY:
+ return {}
+ var fields: Array = value
+ if (
+ fields.size() != MOVEMENT_INPUT_FIELD_COUNT
+ or typeof(fields[0]) != TYPE_INT
+ or typeof(fields[1]) != TYPE_VECTOR2
+ or typeof(fields[2]) not in [TYPE_FLOAT, TYPE_INT]
+ or typeof(fields[3]) != TYPE_INT
+ or typeof(fields[4]) not in [TYPE_STRING, TYPE_STRING_NAME]
+ or typeof(fields[5]) != TYPE_INT
+ or typeof(fields[6]) not in [TYPE_FLOAT, TYPE_INT]
+ or typeof(fields[7]) != TYPE_BOOL
+ ):
+ return {}
+ var axis: Vector2 = fields[1]
+ var flags: int = int(fields[3])
+ if flags < 0 or flags & ~MOVEMENT_ALLOWED_FLAGS != 0:
+ return {}
+ var decoded: Dictionary = {
+ "sequence": int(fields[0]),
+ "axis": [axis.x, axis.y],
+ "camera_yaw": float(fields[2]),
+ "jump": bool(flags & MOVEMENT_FLAG_JUMP),
+ "sprint": bool(flags & MOVEMENT_FLAG_SPRINT),
+ "sneak": bool(flags & MOVEMENT_FLAG_SNEAK),
+ "slow_walk": bool(flags & MOVEMENT_FLAG_SLOW_WALK),
+ "sitting": bool(flags & MOVEMENT_FLAG_SITTING),
+ "casting": bool(flags & MOVEMENT_FLAG_CASTING),
+ "animation_action": NetworkPlayerAnimationProtocol.make_action_state(
+ StringName(str(fields[4])),
+ int(fields[5]),
+ float(fields[6]),
+ bool(fields[7]),
+ ),
+ }
+ return decoded if _is_valid_movement_input(decoded) else {}
+
+
+static func _is_valid_movement_input(data: Dictionary) -> bool:
if (
typeof(data.get("sequence")) != TYPE_INT
or typeof(data.get("axis")) != TYPE_ARRAY
@@ -1550,9 +2267,10 @@ 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 (
- data.has("sitting")
- and typeof(data.get("sitting")) != 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]
):
@@ -1560,11 +2278,18 @@ func _is_valid_movement_input(data: Dictionary) -> bool:
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
@@ -1574,24 +2299,310 @@ func _is_valid_movement_input(data: Dictionary) -> bool:
func _broadcast_movement_snapshots() -> void:
- var snapshots: Array[Dictionary] = []
- for peer_id: int in _registry.get_peer_ids():
- var avatar: Player = _spawn_service.get_avatar(peer_id)
+ _movement_snapshot_tick += 1
+ var peer_ids: Array[int] = _registry.get_peer_ids()
+ for recipient_id: int in peer_ids:
+ # Peer 1 is the local listen-server player. Dedicated servers do not
+ # register a peer 1, so every remaining record is a remote recipient.
+ if recipient_id == 1:
+ continue
+ var recipient_avatar: Player = _spawn_service.get_avatar(recipient_id)
+ if recipient_avatar == null:
+ continue
+ var snapshots: Array = []
+ for subject_id: int in peer_ids:
+ if not peers_share_space(recipient_id, subject_id):
+ continue
+ var subject_avatar: Player = _spawn_service.get_avatar(subject_id)
+ if subject_avatar == null:
+ continue
+ if subject_id == recipient_id:
+ # The owner already simulates locally. Its authoritative state is
+ # an audit and acknowledgement, not a presentation stream, so it
+ # does not need the full 30 Hz observer snapshot rate.
+ if _movement_snapshot_tick % OWNER_SNAPSHOT_DIVISOR != 0:
+ continue
+ elif not _should_send_remote_snapshot(
+ recipient_avatar.global_position,
+ subject_avatar.global_position,
+ ):
+ continue
+ var encoded: Array = _encode_movement_snapshot(
+ subject_avatar.make_network_snapshot(subject_id)
+ )
+ if not encoded.is_empty():
+ snapshots.append(encoded)
+ _send_movement_snapshot_batches(recipient_id, snapshots)
+ _broadcast_movement_animation_updates(peer_ids)
+
+
+func _should_send_remote_snapshot(
+ recipient_position: Vector3,
+ subject_position: Vector3,
+) -> bool:
+ var distance_squared: float = recipient_position.distance_squared_to(
+ subject_position
+ )
+ var divisor: int = DISTANT_REMOTE_SNAPSHOT_DIVISOR
+ if distance_squared <= NEAR_REMOTE_DISTANCE * NEAR_REMOTE_DISTANCE:
+ divisor = NEAR_REMOTE_SNAPSHOT_DIVISOR
+ elif distance_squared <= FAR_REMOTE_DISTANCE * FAR_REMOTE_DISTANCE:
+ divisor = FAR_REMOTE_SNAPSHOT_DIVISOR
+ return _movement_snapshot_tick % divisor == 0
+
+
+func _send_movement_snapshot_batches(
+ recipient_id: int,
+ snapshots: Array,
+) -> void:
+ for start_index: int in range(
+ 0,
+ snapshots.size(),
+ MOVEMENT_SNAPSHOT_BATCH_SIZE,
+ ):
+ var batch: Array = snapshots.slice(
+ start_index,
+ mini(
+ start_index + MOVEMENT_SNAPSHOT_BATCH_SIZE,
+ snapshots.size(),
+ ),
+ )
+ receive_movement_snapshots.rpc_id(recipient_id, batch)
+ _movement_snapshot_packets_sent += 1
+ _movement_snapshot_states_sent += batch.size()
+
+
+func _broadcast_movement_animation_updates(peer_ids: Array[int]) -> void:
+ var refresh_all: bool = (
+ _animation_refresh_accumulator >= ANIMATION_REFRESH_INTERVAL
+ )
+ if refresh_all:
+ _animation_refresh_accumulator = fmod(
+ _animation_refresh_accumulator,
+ ANIMATION_REFRESH_INTERVAL,
+ )
+ var updates_by_subject: Dictionary[int, Array] = {}
+ for subject_id: int in peer_ids:
+ var avatar: Player = _spawn_service.get_avatar(subject_id)
if avatar == null:
continue
- snapshots.append(avatar.make_network_snapshot(peer_id))
- receive_movement_snapshots.rpc(snapshots)
+ var state: Dictionary = avatar.make_network_animation_state()
+ var previous: Dictionary = _last_animation_state_by_peer.get(
+ subject_id, {}
+ )
+ if (
+ not refresh_all
+ and _movement_animation_signature(state)
+ == _movement_animation_signature(previous)
+ ):
+ continue
+ _last_animation_state_by_peer[subject_id] = state.duplicate(true)
+ var encoded: Array = _encode_movement_animation(subject_id, state)
+ if not encoded.is_empty():
+ updates_by_subject[subject_id] = encoded
+ if updates_by_subject.is_empty():
+ return
+ for recipient_id: int in peer_ids:
+ if recipient_id == 1:
+ continue
+ var updates: Array = []
+ for subject_id: int in updates_by_subject:
+ if peers_share_space(recipient_id, subject_id):
+ updates.append(updates_by_subject[subject_id])
+ if updates.is_empty():
+ continue
+ receive_movement_animations.rpc_id(recipient_id, updates)
+ _movement_animation_packets_sent += 1
+ _movement_animation_states_sent += updates.size()
+
+
+static func _movement_animation_signature(state: Dictionary) -> Array:
+ if not NetworkPlayerAnimationProtocol.validate_state(state):
+ return []
+ var action: Dictionary = state["action"]
+ return [
+ str(state["locomotion_id"]),
+ bool(state["grounded"]),
+ str(action["id"]),
+ int(action["sequence"]),
+ bool(action.get("paused", false)),
+ ]
+
+
+static func _encode_movement_snapshot(snapshot: Dictionary) -> Array:
+ var position: Variant = snapshot.get("position")
+ var snapshot_velocity: Variant = snapshot.get("velocity")
+ if (
+ typeof(position) != TYPE_ARRAY
+ or position.size() != 3
+ or typeof(snapshot_velocity) != TYPE_ARRAY
+ or snapshot_velocity.size() != 3
+ ):
+ return []
+ var flags: int = 0
+ if bool(snapshot.get("grounded", false)):
+ flags |= SNAPSHOT_FLAG_GROUNDED
+ if bool(snapshot.get("sitting", false)):
+ flags |= SNAPSHOT_FLAG_SITTING
+ if bool(snapshot.get("casting", false)):
+ flags |= SNAPSHOT_FLAG_CASTING
+ return [
+ int(snapshot.get("peer_id", 0)),
+ int(snapshot.get("acknowledged_input", 0)),
+ Vector3(float(position[0]), float(position[1]), float(position[2])),
+ Vector3(
+ float(snapshot_velocity[0]),
+ float(snapshot_velocity[1]),
+ float(snapshot_velocity[2]),
+ ),
+ float(snapshot.get("visual_yaw", 0.0)),
+ flags,
+ ]
+
+
+static func _decode_movement_snapshot(value: Variant) -> Dictionary:
+ if typeof(value) != TYPE_ARRAY:
+ return {}
+ var fields: Array = value
+ if (
+ fields.size() != MOVEMENT_SNAPSHOT_FIELD_COUNT
+ or typeof(fields[0]) != TYPE_INT
+ or typeof(fields[1]) != TYPE_INT
+ or typeof(fields[2]) != TYPE_VECTOR3
+ or typeof(fields[3]) != TYPE_VECTOR3
+ or typeof(fields[4]) not in [TYPE_FLOAT, TYPE_INT]
+ or typeof(fields[5]) != TYPE_INT
+ ):
+ return {}
+ var position: Vector3 = fields[2]
+ var snapshot_velocity: Vector3 = fields[3]
+ var flags: int = int(fields[5])
+ if (
+ int(fields[0]) <= 0
+ or int(fields[1]) < 0
+ or not position.is_finite()
+ or not snapshot_velocity.is_finite()
+ or not is_finite(float(fields[4]))
+ or flags < 0
+ or flags & ~SNAPSHOT_ALLOWED_FLAGS != 0
+ ):
+ return {}
+ return {
+ "peer_id": int(fields[0]),
+ "acknowledged_input": int(fields[1]),
+ "position": [position.x, position.y, position.z],
+ "velocity": [
+ snapshot_velocity.x,
+ snapshot_velocity.y,
+ snapshot_velocity.z,
+ ],
+ "visual_yaw": float(fields[4]),
+ "grounded": bool(flags & SNAPSHOT_FLAG_GROUNDED),
+ "sitting": bool(flags & SNAPSHOT_FLAG_SITTING),
+ "casting": bool(flags & SNAPSHOT_FLAG_CASTING),
+ }
+
+
+static func _encode_movement_animation(
+ peer_id: int,
+ state: Dictionary,
+) -> Array:
+ if not NetworkPlayerAnimationProtocol.validate_state(state):
+ return []
+ var action: Dictionary = state["action"]
+ return [
+ peer_id,
+ str(state["locomotion_id"]),
+ bool(state["grounded"]),
+ str(action["id"]),
+ int(action["sequence"]),
+ float(action["elapsed"]),
+ bool(action.get("paused", false)),
+ ]
+
+
+static func _decode_movement_animation(value: Variant) -> Dictionary:
+ if typeof(value) != TYPE_ARRAY:
+ return {}
+ var fields: Array = value
+ if (
+ fields.size() != MOVEMENT_ANIMATION_FIELD_COUNT
+ or typeof(fields[0]) != TYPE_INT
+ or typeof(fields[1]) not in [TYPE_STRING, TYPE_STRING_NAME]
+ or typeof(fields[2]) != TYPE_BOOL
+ or typeof(fields[3]) not in [TYPE_STRING, TYPE_STRING_NAME]
+ or typeof(fields[4]) != TYPE_INT
+ or typeof(fields[5]) not in [TYPE_FLOAT, TYPE_INT]
+ or typeof(fields[6]) != TYPE_BOOL
+ ):
+ return {}
+ var state: Dictionary = NetworkPlayerAnimationProtocol.make_state(
+ StringName(str(fields[1])),
+ bool(fields[2]),
+ StringName(str(fields[3])),
+ int(fields[4]),
+ float(fields[5]),
+ bool(fields[6]),
+ )
+ if (
+ int(fields[0]) <= 0
+ or not NetworkPlayerAnimationProtocol.validate_state(state)
+ ):
+ return {}
+ return {"peer_id": int(fields[0]), "state": state}
+
+
+static func _encode_movement_animation_action(
+ action: Dictionary,
+ sitting: bool,
+) -> Array:
+ if not NetworkPlayerAnimationProtocol.validate_action_state(action):
+ return []
+ return [
+ str(action["id"]),
+ int(action["sequence"]),
+ float(action["elapsed"]),
+ bool(action.get("paused", false)),
+ sitting,
+ ]
+
+
+static func _decode_movement_animation_action(value: Variant) -> Dictionary:
+ if typeof(value) != TYPE_ARRAY:
+ return {}
+ var fields: Array = value
+ if (
+ fields.size() != MOVEMENT_ANIMATION_ACTION_FIELD_COUNT
+ or typeof(fields[0]) not in [TYPE_STRING, TYPE_STRING_NAME]
+ or typeof(fields[1]) != TYPE_INT
+ or typeof(fields[2]) not in [TYPE_FLOAT, TYPE_INT]
+ or typeof(fields[3]) != TYPE_BOOL
+ or typeof(fields[4]) != TYPE_BOOL
+ ):
+ return {}
+ var action: Dictionary = NetworkPlayerAnimationProtocol.make_action_state(
+ StringName(str(fields[0])),
+ int(fields[1]),
+ float(fields[2]),
+ bool(fields[3]),
+ )
+ if not NetworkPlayerAnimationProtocol.validate_action_state(action):
+ return {}
+ return {"action": action, "sitting": bool(fields[4])}
@rpc("authority", "call_remote", "unreliable_ordered", 2)
-func receive_movement_snapshots(snapshots: Array) -> void:
+func receive_movement_snapshots(encoded_snapshots: Array) -> void:
if state != State.JOINED_CLIENT:
return
var local_peer_id: int = multiplayer.get_unique_id()
- for value: Variant in snapshots:
- if typeof(value) != TYPE_DICTIONARY:
+ var estimated_transit_seconds: float = (
+ _estimated_movement_transit_seconds()
+ )
+ for value: Variant in encoded_snapshots:
+ var snapshot: Dictionary = _decode_movement_snapshot(value)
+ if snapshot.is_empty():
continue
- var snapshot: Dictionary = value
if typeof(snapshot.get("peer_id")) != TYPE_INT:
continue
var peer_id: int = snapshot["peer_id"]
@@ -1599,9 +2610,109 @@ func receive_movement_snapshots(snapshots: Array) -> void:
if avatar == null:
continue
if peer_id == local_peer_id:
- avatar.apply_local_prediction_correction(snapshot)
+ if _reject_stale_space_transition_snapshot(snapshot):
+ continue
+ _discard_acknowledged_movement_inputs(
+ int(snapshot.get("acknowledged_input", 0))
+ )
+ avatar.apply_local_prediction_correction(
+ snapshot,
+ _input_sequence,
+ INPUT_INTERVAL,
+ estimated_transit_seconds,
+ _local_snapshot_delta_seconds(),
+ )
else:
- avatar.push_network_snapshot(snapshot)
+ avatar.push_network_snapshot(
+ snapshot,
+ estimated_transit_seconds,
+ )
+
+
+func _reject_stale_space_transition_snapshot(snapshot: Dictionary) -> bool:
+ if not _local_space_transition_guard_active:
+ return false
+ var position_values: Array = snapshot.get("position", [])
+ if position_values.size() != 3:
+ return true
+ var position := Vector3(
+ float(position_values[0]),
+ float(position_values[1]),
+ float(position_values[2]),
+ )
+ var acknowledged_input: int = int(
+ snapshot.get("acknowledged_input", 0)
+ )
+ if acknowledged_input < _local_space_transition_minimum_ack:
+ return true
+ if acknowledged_input > _local_space_transition_minimum_ack:
+ _local_space_transition_guard_active = false
+ return false
+ if position.distance_to(_local_space_transition_guard_position) > (
+ SPACE_TRANSITION_SNAPSHOT_GUARD_DISTANCE
+ ):
+ return true
+ _local_space_transition_guard_active = false
+ return false
+
+
+func _discard_acknowledged_movement_inputs(acknowledged_sequence: int) -> void:
+ while (
+ not _pending_movement_inputs.is_empty()
+ and int(_pending_movement_inputs[0].get("sequence", 0))
+ <= acknowledged_sequence
+ ):
+ _pending_movement_inputs.pop_front()
+
+
+func _local_snapshot_delta_seconds() -> float:
+ var now_msec: int = Time.get_ticks_msec()
+ if _last_local_snapshot_received_msec <= 0:
+ _last_local_snapshot_received_msec = now_msec
+ return 0.0
+ var elapsed_seconds: float = clampf(
+ float(now_msec - _last_local_snapshot_received_msec) / 1000.0,
+ 0.0,
+ Player.LOCAL_PREDICTION_MAX_AUDIT_DELTA_SECONDS,
+ )
+ _last_local_snapshot_received_msec = now_msec
+ return elapsed_seconds
+
+
+@rpc(
+ "authority",
+ "call_remote",
+ "reliable",
+ NetworkProtocol.MOVEMENT_ANIMATION_CHANNEL,
+)
+func receive_movement_animations(encoded_states: Array) -> void:
+ if state != State.JOINED_CLIENT:
+ return
+ for value: Variant in encoded_states:
+ var decoded: Dictionary = _decode_movement_animation(value)
+ if decoded.is_empty():
+ continue
+ var avatar: Player = _spawn_service.get_avatar(
+ int(decoded["peer_id"])
+ )
+ if avatar == null:
+ _pending_animation_state_by_peer[int(decoded["peer_id"])] = (
+ Dictionary(decoded["state"]).duplicate(true)
+ )
+ continue
+ avatar.apply_network_animation_state(decoded["state"])
+
+
+func _estimated_movement_transit_seconds() -> float:
+ if state != State.JOINED_CLIENT:
+ return -1.0
+ var round_trip_msec: int = get_peer_rtt_ms(get_local_peer_id())
+ if round_trip_msec < 0:
+ return -1.0
+ return minf(
+ float(round_trip_msec) / 2000.0,
+ MAX_MOVEMENT_ONE_WAY_TRANSIT_SECONDS,
+ )
func publish_authoritative_teleport(peer_id: int) -> void:
@@ -1843,6 +2954,11 @@ func _fail(message: String) -> void:
func _teardown_peer() -> void:
+ var local_avatar: Player
+ if _spawn_service != null:
+ local_avatar = _spawn_service.get_local_player()
+ if local_avatar != null:
+ local_avatar.reset_network_movement_state()
for peer_id: int in _recovery_attempts.keys():
remote_recovery_presentation_changed.emit(
peer_id, false, _recovery_attempts[peer_id]
@@ -1860,11 +2976,28 @@ func _teardown_peer() -> void:
_transport.disconnect_transport()
multiplayer.multiplayer_peer = OfflineMultiplayerPeer.new()
_connection_deadline = 0.0
+ _input_sequence = 0
_input_accumulator = 0.0
+ _idle_input_accumulator = 0.0
+ _last_input_state_hash = 0
+ _pending_movement_inputs.clear()
_snapshot_accumulator = 0.0
+ _movement_snapshot_tick = 0
+ _last_local_snapshot_received_msec = 0
+ _local_space_transition_guard_active = false
+ _local_space_transition_guard_position = Vector3.ZERO
+ _local_space_transition_minimum_ack = 0
+ _animation_refresh_accumulator = 0.0
+ _last_animation_state_by_peer.clear()
+ _pending_animation_state_by_peer.clear()
+ _last_local_animation_action_signature.clear()
_server_capabilities = PackedStringArray()
+ _peer_space_ids.clear()
_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 11900e9..e58556e 100644
--- a/network/network_shop_protocol.gd
+++ b/network/network_shop_protocol.gd
@@ -3,6 +3,9 @@ extends RefCounted
const CAPABILITY: StringName = &"shop_v1"
const ART_CAPABILITY: StringName = &"art_shop_v1"
+const BACKPACK_CAPABILITY: StringName = &"backpack_shop_v1"
+const DECOR_CAPABILITY: StringName = &"portable_home_v1"
+const HOME_UPGRADE_CAPABILITY: StringName = &"home_upgrade_v1"
const RELIABLE_CHANNEL: int = NetworkProtocol.SHOP_RELIABLE_CHANNEL
const MAX_ID_LENGTH: int = 96
const MAX_MESSAGE_LENGTH: int = 160
@@ -13,9 +16,11 @@ enum ProductCategory {
ROD,
REEL_SPEED_UPGRADE,
BARRIER_POWER_UPGRADE,
- COOLER_CAPACITY_UPGRADE,
+ HOME_UPGRADE,
ART_KIT,
ART_UPGRADE,
+ BACKPACK_CAPACITY_UPGRADE,
+ DECOR,
}
diff --git a/network/network_shop_service.gd b/network/network_shop_service.gd
index a91c5bc..e4d6f9c 100644
--- a/network/network_shop_service.gd
+++ b/network/network_shop_service.gd
@@ -5,13 +5,18 @@ const FishingShopStockType = preload(
"res://economy/fishing_shop_stock.gd"
)
const ItemDataType = preload("res://items/item_data.gd")
+const FishingRodDataType = preload("res://items/fishing_rod_data.gd")
const OwnedItemType = preload("res://items/owned_item.gd")
const ArtShopStockType = preload("res://economy/art_shop_stock.gd")
+const DecorCatalogType = preload("res://homes/decor_catalog.gd")
-const SHOP_ID: StringName = &"main_fishing_shop"
+const FISHING_SHOP_ID: StringName = &"main_fishing_shop"
+const DECOR_SHOP_ID: StringName = &"rv_decor_shop"
+const RV_UPGRADE_SHOP_ID: StringName = &"rv_upgrade_shop"
const REEL_PRODUCT_ID: StringName = &"reel_speed_upgrade"
const BARRIER_PRODUCT_ID: StringName = &"barrier_power_upgrade"
-const COOLER_PRODUCT_ID: StringName = &"cooler_capacity_upgrade"
+const HOME_UPGRADE_PRODUCT_ID: StringName = &"starter_rv_upgrade"
+const BACKPACK_PRODUCT_ID: StringName = &"backpack_capacity_upgrade"
const MAX_LEDGER_ENTRIES_PER_PEER: int = 64
signal local_purchase_pending(request_id: String)
@@ -29,11 +34,14 @@ var _session: NetworkSession
var _spawn_service: PlayerSpawnService
var _network_fishing: NetworkFishingService
var _interaction: FishingShopInteraction
+var _decor_interaction: DecorShopInteraction
+var _rv_upgrade_interaction: FishingShopInteraction
var _wallet: PlayerWallet
var _bag: PlayerBag
var _item_catalog: ItemCatalog
var _upgrades: PlayerFishingUpgrades
var _cooler_capacity: PlayerCoolerCapacity
+var _inventory_layout: PlayerInventoryLayout
var _art_unlocks: PlayerArtUnlocks
var _save_manager: PlayerSaveManager
var _request_ledgers: Dictionary[int, Dictionary] = {}
@@ -44,6 +52,8 @@ var _received_results: Dictionary[String, bool] = {}
var _applied_results: Dictionary[String, bool] = {}
var _pending_local_request: Dictionary = {}
var _reservations: PlayerAssetReservationService
+var _home_state: PlayerHomeState
+var _network_home: NetworkHomeService
func setup(
@@ -59,6 +69,11 @@ func setup(
art_unlocks: PlayerArtUnlocks,
save_manager: PlayerSaveManager,
reservations: PlayerAssetReservationService,
+ inventory_layout: PlayerInventoryLayout = null,
+ home_state: PlayerHomeState = null,
+ network_home: NetworkHomeService = null,
+ decor_interaction: DecorShopInteraction = null,
+ rv_upgrade_interaction: FishingShopInteraction = null,
) -> void:
_session = session
_spawn_service = spawn_service
@@ -72,12 +87,33 @@ func setup(
_art_unlocks = art_unlocks
_save_manager = save_manager
_reservations = reservations
+ _inventory_layout = inventory_layout
+ _home_state = home_state
+ _network_home = network_home
+ _decor_interaction = decor_interaction
+ _rv_upgrade_interaction = rv_upgrade_interaction
if not _session.peer_removed.is_connected(_on_peer_removed):
_session.peer_removed.connect(_on_peer_removed)
if not _session.state_changed.is_connected(_on_session_state_changed):
_session.state_changed.connect(_on_session_state_changed)
+func set_shop_interaction(interaction: FishingShopInteraction) -> void:
+ _interaction = interaction
+
+
+func set_decor_shop_interaction(
+ interaction: DecorShopInteraction,
+) -> void:
+ _decor_interaction = interaction
+
+
+func set_rv_upgrade_shop_interaction(
+ interaction: FishingShopInteraction,
+) -> void:
+ _rv_upgrade_interaction = interaction
+
+
func can_request_purchase() -> bool:
return (
_session != null
@@ -103,6 +139,44 @@ func can_request_art_purchase() -> bool:
)
+func can_request_backpack_purchase() -> bool:
+ return (
+ can_request_purchase()
+ and (
+ _session.is_host()
+ or _session.supports_server_capability(
+ NetworkShopProtocol.BACKPACK_CAPABILITY
+ )
+ )
+ )
+
+
+func can_request_decor_purchase() -> bool:
+ return (
+ can_request_purchase()
+ and _home_state != null
+ and (
+ _session.is_host()
+ or _session.supports_server_capability(
+ NetworkShopProtocol.DECOR_CAPABILITY
+ )
+ )
+ )
+
+
+func can_request_home_upgrade() -> bool:
+ return (
+ can_request_purchase()
+ and _home_state != null
+ and (
+ _session.is_host()
+ or _session.supports_server_capability(
+ NetworkShopProtocol.HOME_UPGRADE_CAPABILITY
+ )
+ )
+ )
+
+
func is_local_purchase_pending() -> bool:
return not _pending_local_request.is_empty()
@@ -126,6 +200,15 @@ func request_supply(item_id: StringName) -> String:
)
+func request_rod(item_id: StringName) -> String:
+ return _request_purchase(
+ item_id,
+ NetworkShopProtocol.ProductCategory.ROD,
+ 1,
+ _bag.get_quantity(item_id) if _bag != null else 0,
+ )
+
+
func request_reel_speed_upgrade() -> String:
return _request_purchase(
REEL_PRODUCT_ID,
@@ -144,12 +227,31 @@ func request_barrier_power_upgrade() -> String:
)
-func request_cooler_capacity_upgrade() -> String:
+func request_home_upgrade() -> String:
return _request_purchase(
- COOLER_PRODUCT_ID,
- NetworkShopProtocol.ProductCategory.COOLER_CAPACITY_UPGRADE,
+ HOME_UPGRADE_PRODUCT_ID,
+ NetworkShopProtocol.ProductCategory.HOME_UPGRADE,
1,
- _cooler_capacity.get_level() if _cooler_capacity != null else 0
+ _home_state.get_tier() if _home_state != null else 0
+ )
+
+
+func request_backpack_capacity_upgrade() -> String:
+ return _request_purchase(
+ BACKPACK_PRODUCT_ID,
+ NetworkShopProtocol.ProductCategory.BACKPACK_CAPACITY_UPGRADE,
+ 1,
+ _inventory_layout.get_backpack_level()
+ if _inventory_layout != null else 0,
+ )
+
+
+func request_decor(product_id: StringName) -> String:
+ return _request_purchase(
+ product_id,
+ NetworkShopProtocol.ProductCategory.DECOR,
+ 1,
+ _home_state.get_owned_count(product_id) if _home_state != null else 0,
)
@@ -220,11 +322,39 @@ func _request_purchase(
product_id, category, 0, 0,
)
return ""
+ if (
+ category == NetworkShopProtocol.ProductCategory.BACKPACK_CAPACITY_UPGRADE
+ and not can_request_backpack_purchase()
+ ):
+ local_purchase_finished.emit(
+ "", false, "Backpack upgrades require a newer server.",
+ product_id, category, 0, 0,
+ )
+ return ""
+ if (
+ category == NetworkShopProtocol.ProductCategory.HOME_UPGRADE
+ and not can_request_home_upgrade()
+ ):
+ local_purchase_finished.emit(
+ "", false, "RV upgrades require a newer server.",
+ product_id, category, 0, 0,
+ )
+ return ""
+ if (
+ category == NetworkShopProtocol.ProductCategory.DECOR
+ and not can_request_decor_purchase()
+ ):
+ local_purchase_finished.emit(
+ "", false, "Decor require a newer server.",
+ product_id, category, 0, 0,
+ )
+ return ""
var request_id: String = _new_id("shop")
+ var shop_id: StringName = _shop_id_for_category(category)
var request: Dictionary = {
"request_id": request_id,
"session_id": _session.get_session_id(),
- "shop_id": str(SHOP_ID),
+ "shop_id": str(shop_id),
"product_id": str(product_id),
"category": category,
"quantity": quantity,
@@ -296,16 +426,38 @@ func _handle_purchase_request(peer_id: int, data: Dictionary) -> void:
_rejected_result(data, "A purchase is already pending.")
)
return
- if not _is_shop_available_for_peer(peer_id):
+ var requested_shop_id := StringName(str(data["shop_id"]))
+ if not _is_shop_available_for_peer(peer_id, requested_shop_id):
var avatar: Player = _spawn_service.get_avatar(peer_id)
+ var requested_interaction: FishingShopInteraction = (
+ _interaction_for_shop(requested_shop_id)
+ )
var message: String = (
"The shop is unavailable."
- if avatar == null or _interaction == null
+ if avatar == null or requested_interaction == null
else "Move closer to the shop."
)
_record_and_send(peer_id, _rejected_result(data, message))
return
var result: Dictionary = _build_authoritative_result(peer_id, data)
+ if (
+ bool(result.get("accepted", false))
+ and _network_home != null
+ ):
+ match int(result.get("category", -1)):
+ NetworkShopProtocol.ProductCategory.DECOR:
+ _network_home.authorize_decor_purchase(
+ peer_id,
+ StringName(str(result["product_id"])),
+ int(result["resulting_state"]),
+ str(result["result_id"]),
+ )
+ NetworkShopProtocol.ProductCategory.HOME_UPGRADE:
+ _network_home.authorize_home_upgrade(
+ peer_id,
+ int(result["resulting_state"]),
+ str(result["result_id"]),
+ )
_pending_by_peer[peer_id] = request_id
_record_and_send(peer_id, result)
@@ -319,10 +471,29 @@ func _build_authoritative_result(
var quantity: int = request["quantity"]
var wallet_balance: int = request["wallet_balance"]
var current_state: int = request["current_state"]
+ if (
+ category == NetworkShopProtocol.ProductCategory.DECOR
+ and _network_home != null
+ ):
+ var authoritative_count: int = (
+ _network_home.get_authoritative_owned_count(peer_id, product_id)
+ )
+ if authoritative_count >= 0:
+ current_state = authoritative_count
+ elif (
+ category == NetworkShopProtocol.ProductCategory.HOME_UPGRADE
+ and _network_home != null
+ ):
+ var authoritative_tier: int = (
+ _network_home.get_authoritative_home_tier(peer_id)
+ )
+ if authoritative_tier >= 0:
+ current_state = authoritative_tier
var cost: int = -1
var resulting_state: int = current_state
var rejection: String = ""
- if StringName(str(request["shop_id"])) != SHOP_ID:
+ var expected_shop_id: StringName = _shop_id_for_category(category)
+ if StringName(str(request["shop_id"])) != expected_shop_id:
rejection = "The shop is unavailable."
elif quantity < 1:
rejection = "Purchase could not be completed."
@@ -340,7 +511,7 @@ func _build_authoritative_result(
)
if (
item == null
- or not item.is_valid()
+ or not item.is_available()
or product_id not in (
FishingShopStockType.get_stock_item_ids()
)
@@ -362,7 +533,7 @@ func _build_authoritative_result(
or current_state + quantity > item.max_stack
):
rejection = (
- "Your Bag is full."
+ "Your inventory is full."
if item != null and current_state >= item.max_stack
else "Purchase could not be completed."
)
@@ -384,7 +555,29 @@ func _build_authoritative_result(
rejection = "Purchase could not be completed."
resulting_state = current_state + quantity
NetworkShopProtocol.ProductCategory.ROD:
- rejection = "This item is not sold here."
+ var rod := (
+ _item_catalog.get_item_by_id(product_id)
+ as FishingRodDataType
+ if _item_catalog != null else null
+ )
+ var available_rods: Array[FishingRodDataType] = (
+ FishingShopStockType.get_rod_stock(_item_catalog)
+ )
+ if (
+ rod == null
+ or not rod.is_shop_available()
+ or rod not in available_rods
+ or quantity != 1
+ or current_state != 0
+ ):
+ rejection = (
+ "Rod already owned."
+ if current_state > 0
+ else "Purchase could not be completed."
+ )
+ else:
+ cost = rod.shop_price
+ resulting_state = 1
NetworkShopProtocol.ProductCategory.REEL_SPEED_UPGRADE:
if (
product_id != REEL_PRODUCT_ID
@@ -413,18 +606,25 @@ func _build_authoritative_result(
]
)
resulting_state = current_state + 1
- NetworkShopProtocol.ProductCategory.COOLER_CAPACITY_UPGRADE:
+ NetworkShopProtocol.ProductCategory.HOME_UPGRADE:
if (
- product_id != COOLER_PRODUCT_ID
- or current_state >= PlayerCoolerCapacity.MAX_LEVEL
+ product_id != HOME_UPGRADE_PRODUCT_ID
+ or current_state >= PlayerHomeState.MAX_TIER
):
rejection = "Upgrade is already at maximum."
else:
- cost = (
- PlayerCoolerCapacity.EXPANSION_COSTS[
- current_state
- ]
- )
+ cost = PlayerHomeState.HOME_UPGRADE_COSTS[current_state]
+ resulting_state = current_state + 1
+ NetworkShopProtocol.ProductCategory.BACKPACK_CAPACITY_UPGRADE:
+ if (
+ product_id != BACKPACK_PRODUCT_ID
+ or current_state >= PlayerInventoryLayout.MAX_BACKPACK_LEVEL
+ ):
+ rejection = "Upgrade is already at maximum."
+ else:
+ cost = PlayerInventoryLayout.BACKPACK_EXPANSION_COSTS[
+ current_state
+ ]
resulting_state = current_state + 1
NetworkShopProtocol.ProductCategory.ART_KIT:
var art_item: ItemDataType = _item_catalog.get_item_by_id(
@@ -434,7 +634,7 @@ func _build_authoritative_result(
if (
product_id != ArtShopStockType.ART_KIT_ITEM_ID
or art_item == null
- or not art_item.is_valid()
+ or not art_item.is_available()
or art_item.category != ItemDataType.Category.TOOL
or art_item.stackable
or not art_item.equippable
@@ -463,8 +663,21 @@ func _build_authoritative_result(
resulting_state = PlayerArtUnlocks.resulting_mask(
current_state, product_id
)
+ NetworkShopProtocol.ProductCategory.DECOR:
+ cost = DecorCatalogType.get_price(product_id)
+ if (
+ not DecorCatalogType.has_product(product_id)
+ or quantity != 1
+ or current_state < 0
+ or current_state
+ >= DecorCatalogType.MAX_OWNED_PER_PRODUCT
+ or cost < 0
+ ):
+ rejection = "Decor is unavailable."
+ else:
+ resulting_state = current_state + 1
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 {
@@ -580,14 +793,25 @@ func _apply_purchase_result(data: Dictionary) -> void:
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()
+ var backpack_snapshot: int = (
+ _inventory_layout.get_backpack_level()
+ if _inventory_layout != null else 0
+ )
var art_snapshot: int = _art_unlocks.get_unlock_mask()
+ var home_snapshot: Dictionary = (
+ _home_state.to_save_data() if _home_state != null else {}
+ )
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)
+ if _inventory_layout != null:
+ _inventory_layout.restore_backpack_level(backpack_snapshot)
_art_unlocks.restore_mask(art_snapshot)
+ if _home_state != null and not home_snapshot.is_empty():
+ _home_state.restore_from_save_data(home_snapshot)
_wallet.restore_balance(wallet_snapshot)
_save_manager.save_if_dirty()
_fail_local_apply(data, "Purchase could not be completed.")
@@ -608,6 +832,7 @@ func _validate_local_result(data: Dictionary) -> String:
or _cooler_capacity == null
or _art_unlocks == null
or _save_manager == null
+ or _home_state == null
or _wallet.get_balance() != int(data["expected_wallet"])
or str(data["product_id"])
!= str(_pending_local_request.get("product_id", ""))
@@ -633,7 +858,7 @@ func _validate_local_result(data: Dictionary) -> String:
):
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:
var item: ItemDataType = (
@@ -643,6 +868,7 @@ func _validate_local_result(data: Dictionary) -> String:
var bait_unlocked: bool = _bag.is_bait_unlocked(product_id)
if (
item == null
+ or not item.is_available()
or _bag.get_quantity(product_id) != expected_state
or int(data["quantity"]) != FishingShopStockType.get_purchase_quantity(
product_id, expected_state, item.max_stack
@@ -654,7 +880,23 @@ func _validate_local_result(data: Dictionary) -> String:
):
return "Purchase could not be completed."
if not _bag.can_add_item(product_id, data["quantity"]):
- return "Your Bag is full."
+ return "Your inventory is full."
+ NetworkShopProtocol.ProductCategory.ROD:
+ var rod := (
+ _item_catalog.get_item_by_id(product_id)
+ as FishingRodDataType
+ if _item_catalog != null else null
+ )
+ if (
+ rod == null
+ or not rod.is_shop_available()
+ or _bag.get_quantity(product_id) != expected_state
+ or expected_state != 0
+ or int(data["quantity"]) != 1
+ or cost != rod.shop_price
+ or not _bag.can_add_item(product_id, 1)
+ ):
+ return "Purchase could not be completed."
NetworkShopProtocol.ProductCategory.REEL_SPEED_UPGRADE:
if _upgrades.get_reel_speed_level() != expected_state:
return "Purchase could not be completed."
@@ -665,14 +907,29 @@ func _validate_local_result(data: Dictionary) -> String:
return "Purchase could not be completed."
if _upgrades.get_next_barrier_power_cost() != cost:
return "Purchase could not be completed."
- NetworkShopProtocol.ProductCategory.COOLER_CAPACITY_UPGRADE:
- if _cooler_capacity.get_level() != expected_state:
+ NetworkShopProtocol.ProductCategory.HOME_UPGRADE:
+ if (
+ product_id != HOME_UPGRADE_PRODUCT_ID
+ or _home_state.get_tier() != expected_state
+ or _home_state.get_next_upgrade_cost() != cost
+ ):
return "Purchase could not be completed."
- if _cooler_capacity.get_next_cost() != cost:
+ NetworkShopProtocol.ProductCategory.BACKPACK_CAPACITY_UPGRADE:
+ if (
+ _inventory_layout == null
+ or _inventory_layout.get_backpack_level() != expected_state
+ or _inventory_layout.get_next_backpack_cost() != cost
+ ):
return "Purchase could not be completed."
NetworkShopProtocol.ProductCategory.ART_KIT:
+ var art_item: ItemDataType = (
+ _item_catalog.get_item_by_id(product_id)
+ if _item_catalog != null else null
+ )
if (
product_id != ArtShopStockType.ART_KIT_ITEM_ID
+ or art_item == null
+ or not art_item.is_available()
or _bag.get_quantity(product_id) != expected_state
or not _bag.can_add_item(product_id, 1)
or cost != ArtShopStockType.ART_KIT_PRICE
@@ -688,6 +945,20 @@ func _validate_local_result(data: Dictionary) -> String:
or cost != ArtShopStockType.UPGRADE_PRICE
):
return "Purchase could not be completed."
+ NetworkShopProtocol.ProductCategory.DECOR:
+ if (
+ not DecorCatalogType.has_product(product_id)
+ or _home_state.get_owned_count(product_id) != expected_state
+ or resulting_state != expected_state + 1
+ or cost != DecorCatalogType.get_price(product_id)
+ or expected_state >= DecorCatalogType.MAX_OWNED_PER_PRODUCT
+ or not _bag.can_add_item(product_id, 1)
+ ):
+ return (
+ "Your inventory is full."
+ if not _bag.can_add_item(product_id, 1)
+ else "Purchase could not be completed."
+ )
_:
return "Purchase could not be completed."
return ""
@@ -702,12 +973,25 @@ func _apply_local_product(data: Dictionary) -> bool:
_wallet.debit(data["total_cost"])
and _bag.add_item(product_id, data["quantity"])
)
+ NetworkShopProtocol.ProductCategory.ROD:
+ return (
+ _wallet.debit(int(data["total_cost"]))
+ and _bag.add_item(product_id, 1)
+ )
NetworkShopProtocol.ProductCategory.REEL_SPEED_UPGRADE:
return _upgrades.purchase_reel_speed(_wallet)
NetworkShopProtocol.ProductCategory.BARRIER_POWER_UPGRADE:
return _upgrades.purchase_barrier_power(_wallet)
- NetworkShopProtocol.ProductCategory.COOLER_CAPACITY_UPGRADE:
- return _cooler_capacity.purchase(_wallet)
+ NetworkShopProtocol.ProductCategory.HOME_UPGRADE:
+ return (
+ _wallet.debit(int(data["total_cost"]))
+ and _home_state.upgrade_to_next_tier()
+ )
+ NetworkShopProtocol.ProductCategory.BACKPACK_CAPACITY_UPGRADE:
+ return (
+ _inventory_layout != null
+ and _inventory_layout.purchase_backpack(_wallet)
+ )
NetworkShopProtocol.ProductCategory.ART_KIT:
return (
_wallet.debit(int(data["total_cost"]))
@@ -717,6 +1001,12 @@ func _apply_local_product(data: Dictionary) -> bool:
return _art_unlocks.purchase_product(
product_id, _wallet, int(data["total_cost"])
)
+ NetworkShopProtocol.ProductCategory.DECOR:
+ return (
+ _wallet.debit(int(data["total_cost"]))
+ and _bag.add_item(product_id, 1)
+ and _home_state.add_owned_decor(product_id, 1)
+ )
return false
@@ -793,7 +1083,7 @@ func _handle_acknowledgement(
peer_id: int,
result_id: String,
request_id: String,
- _applied: bool,
+ applied: bool,
message: String,
) -> void:
if (
@@ -805,20 +1095,31 @@ func _handle_acknowledgement(
or _result_owners.get(result_id, 0) != peer_id
):
return
+ if _network_home != null:
+ _network_home.finalize_decor_purchase(
+ peer_id, result_id, applied
+ )
+ _network_home.finalize_home_upgrade(
+ peer_id, result_id, applied
+ )
_acknowledged_results[result_id] = true
_bound_dictionary(_acknowledged_results)
if _pending_by_peer.get(peer_id, "") == request_id:
_pending_by_peer.erase(peer_id)
-func _is_shop_available_for_peer(peer_id: int) -> bool:
+func _is_shop_available_for_peer(
+ peer_id: int,
+ shop_id: StringName,
+) -> bool:
+ var interaction: FishingShopInteraction = _interaction_for_shop(shop_id)
if (
_session == null
or not _session.is_host()
or not _session.is_gameplay_session_active()
or _spawn_service == null
- or _interaction == null
- or not is_instance_valid(_interaction)
+ or interaction == null
+ or not is_instance_valid(interaction)
):
return false
var avatar: Player = _spawn_service.get_avatar(peer_id)
@@ -829,10 +1130,28 @@ func _is_shop_available_for_peer(peer_id: int) -> bool:
_network_fishing == null
or not _network_fishing.has_peer_attempt(peer_id)
)
- and _interaction.is_avatar_in_range(avatar)
+ and interaction.is_avatar_in_range(avatar)
)
+func _shop_id_for_category(category: int) -> StringName:
+ match category:
+ NetworkShopProtocol.ProductCategory.DECOR:
+ return DECOR_SHOP_ID
+ NetworkShopProtocol.ProductCategory.HOME_UPGRADE:
+ return RV_UPGRADE_SHOP_ID
+ return FISHING_SHOP_ID
+
+
+func _interaction_for_shop(shop_id: StringName) -> FishingShopInteraction:
+ match shop_id:
+ DECOR_SHOP_ID:
+ return _decor_interaction
+ RV_UPGRADE_SHOP_ID:
+ return _rv_upgrade_interaction
+ return _interaction
+
+
func _on_peer_removed(peer_id: int) -> void:
_request_ledgers.erase(peer_id)
_pending_by_peer.erase(peer_id)
diff --git a/network/network_surface_drawing_service.gd b/network/network_surface_drawing_service.gd
index e4de2c3..485dfaa 100644
--- a/network/network_surface_drawing_service.gd
+++ b/network/network_surface_drawing_service.gd
@@ -27,6 +27,9 @@ 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 ARTWORK_DIRECTORY_NAME: String = "artwork"
+const STAMP_ALPHA_THRESHOLD: float = 0.5
+const STAMP_COLOR_TOLERANCE: float = 0.012
const INVALID_POINTER_SCREEN_POSITION := Vector2(-1.0, -1.0)
enum GuideAction {
@@ -41,18 +44,28 @@ var _spawn_service: PlayerSpawnService
var _relationships: PlayerRelationshipStore
var _local_player: Player
var _bag: PlayerBag
+var _hotbar: PlayerHotbar
var _art_unlocks: PlayerArtUnlocks
+var _data_root: PlayerDataRoot
var _drawing_root: Node3D
var _canvas_states: Dictionary[String, Dictionary] = {}
var _canvas_nodes: Dictionary[String, SurfaceDrawingCanvas] = {}
var _selected_canvas_id: String = ""
+var _hovered_canvas_id: String = ""
+var _last_hovered_canvas_id: String = ""
var _brush_preview: MultiMeshInstance3D
var _brush_preview_multimesh: MultiMesh
var _brush_preview_material: ShaderMaterial
var _placement_preview: MeshInstance3D
var _placement_preview_material: StandardMaterial3D
+var _stamp_preview: MeshInstance3D
+var _stamp_preview_material: StandardMaterial3D
var _active: bool = false
var _placing_grid: bool = false
+var _stamp_mode: bool = false
+var _stamp_path: String = ""
+var _stamp_pixels := PackedByteArray()
+var _stamp_image: Image
var _brush_size: int = 1
var _grid_size: int = SurfaceDrawingProtocol.DEFAULT_GRID_SIZE
var _color_ids: Array[StringName] = []
@@ -87,7 +100,9 @@ func setup(
local_player: Player,
drawing_root: Node3D,
bag: PlayerBag,
+ hotbar: PlayerHotbar,
art_unlocks: PlayerArtUnlocks,
+ data_root: PlayerDataRoot = null,
) -> void:
_session = session
_spawn_service = spawn_service
@@ -95,7 +110,9 @@ func setup(
_local_player = local_player
_drawing_root = drawing_root
_bag = bag
+ _hotbar = hotbar
_art_unlocks = art_unlocks
+ _data_root = data_root
_refresh_local_unlocks()
if _session != null:
_session.state_changed.connect(_on_session_state_changed)
@@ -123,20 +140,6 @@ func handle_input(
can_open: bool,
pointer_screen_position: Vector2 = INVALID_POINTER_SCREEN_POSITION,
) -> 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(pointer_screen_position)
- return true
- return false
if not _active:
return false
if not can_open:
@@ -149,8 +152,7 @@ func handle_input(
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:
@@ -172,33 +174,26 @@ func handle_input(
return true
if event is InputEventMouseButton:
var mouse_event := event as InputEventMouseButton
+ # 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()
- if (
- mouse_event.shift_pressed
- and mouse_event.button_index in [
- MOUSE_BUTTON_WHEEL_UP,
- MOUSE_BUTTON_WHEEL_DOWN,
- ]
- ):
- return false
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:
if _armed_guide_action != GuideAction.NONE:
_execute_armed_guide_action()
+ elif _stamp_mode:
+ _request_stamp_at_aim()
elif mouse_event.shift_pressed and mouse_event.ctrl_pressed:
_finalize_selected_guide()
elif mouse_event.shift_pressed:
@@ -241,7 +236,9 @@ func activate(
if _active or not can_activate():
return
_active = true
- _placing_grid = true
+ _placing_grid = false
+ _last_hovered_canvas_id = ""
+ _clear_stamp_mode()
_eraser_mode = false
_clear_armed_guide_action(false)
_refresh_local_unlocks()
@@ -253,13 +250,15 @@ func activate(
_pointer_screen_position = _clamped_pointer_position(
initial_pointer_position
)
- _prior_mouse_mode = Input.mouse_mode
+ # Art-kit activation can occur while the prior right-click camera drag is
+ # still held. Stop that drag before recording a return mode, then restore
+ # normal camera availability so right-click continues to work while drawing.
+ _stop_camera_drag_for_art_kit()
+ _prior_mouse_mode = _get_restorable_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:
@@ -267,18 +266,39 @@ func deactivate() -> void:
return
_active = false
_placing_grid = false
+ _clear_stamp_mode()
_eraser_mode = false
_clear_armed_guide_action(false)
_camera_look_active = false
+ # Do not leave a drag captured when drawing is closed by a menu, hotbar
+ # change, or session transition. The next camera drag must start on a fresh
+ # physical right-click press.
+ _stop_camera_drag_for_art_kit()
_reset_stroke()
_selected_canvas_id = ""
+ _hovered_canvas_id = ""
+ _last_hovered_canvas_id = ""
_aim_hit.clear()
_hide_previews()
_refresh_stencil_visibility()
- Input.mouse_mode = _prior_mouse_mode
+ Input.mouse_mode = _get_restorable_mouse_mode(_prior_mouse_mode)
_emit_hud_state("")
+func _stop_camera_drag_for_art_kit() -> void:
+ if _local_player == null or not is_instance_valid(_local_player):
+ return
+ var camera_input_enabled: bool = _local_player.is_camera_input_enabled()
+ _local_player.set_camera_input_enabled(false)
+ _local_player.set_camera_input_enabled(camera_input_enabled)
+
+
+func _get_restorable_mouse_mode(mouse_mode: Input.MouseMode) -> Input.MouseMode:
+ if mouse_mode == Input.MOUSE_MODE_CAPTURED:
+ return Input.MOUSE_MODE_VISIBLE
+ return mouse_mode
+
+
func is_active() -> bool:
return _active
@@ -288,7 +308,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:
@@ -300,6 +324,7 @@ func set_color_id(color_id: StringName) -> bool:
if color_index < 0:
return false
_clear_armed_guide_action(true)
+ _clear_stamp_mode()
_eraser_mode = false
_color_index = color_index
_reset_stroke()
@@ -311,6 +336,7 @@ func set_eraser_mode(enabled: bool) -> void:
if not _active:
return
_clear_armed_guide_action(true)
+ _clear_stamp_mode()
_eraser_mode = enabled
if _eraser_mode and _placing_grid:
_placing_grid = false
@@ -337,6 +363,7 @@ func arm_guide_action(action: int) -> bool:
if _armed_guide_action == GuideAction.NONE:
_armed_return_placement_mode = _placing_grid
_armed_return_eraser_mode = _eraser_mode
+ _clear_stamp_mode()
_armed_guide_action = action
_placing_grid = true
_eraser_mode = false
@@ -344,7 +371,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
@@ -368,6 +395,7 @@ func set_grid_size(value: int) -> bool:
return false
if _grid_size == value:
return true
+ _clear_stamp_mode()
_grid_size = value
_rebuild_placement_preview()
_update_previews()
@@ -415,6 +443,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(),
@@ -486,6 +523,28 @@ func _peer_can_place_grid(peer_id: int, grid_size: int) -> bool:
)
+func _peer_can_stamp(peer_id: int, request: Dictionary) -> bool:
+ var entitlement: Dictionary = _peer_entitlement(peer_id)
+ var unlock_mask: int = int(entitlement.get("unlock_mask", 0))
+ if (
+ not bool(entitlement.get("has_kit", false))
+ or not _mask_owns_product(
+ unlock_mask, PlayerArtUnlocks.STAMP_PRODUCT_ID
+ )
+ or not _mask_unlocks_grid(unlock_mask, int(request["width"]))
+ ):
+ return false
+ var palette_ids: Array[StringName] = SurfaceDrawingPalette.get_color_ids()
+ var pixels: PackedByteArray = request["pixels"]
+ for palette_index: int in pixels:
+ if palette_index <= 0:
+ continue
+ var color_id: StringName = palette_ids[palette_index - 1]
+ if not _mask_unlocks_color(unlock_mask, color_id):
+ return false
+ return true
+
+
func _peer_can_edit(peer_id: int, request: Dictionary) -> bool:
var entitlement: Dictionary = _peer_entitlement(peer_id)
if not bool(entitlement.get("has_kit", false)):
@@ -550,8 +609,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()
@@ -575,14 +633,49 @@ func request_canvas_at_surface(
return true
+func request_stamp_at_surface(
+ origin: Vector3,
+ normal: Vector3,
+ tangent: Vector3,
+) -> bool:
+ if (
+ not can_activate()
+ or not _stamp_mode
+ or _stamp_pixels.size() != _grid_size * _grid_size
+ or _art_unlocks == null
+ or not _art_unlocks.is_stamp_unlocked()
+ or not _art_unlocks.is_grid_size_unlocked(_grid_size)
+ or normal.is_zero_approx()
+ or tangent.is_zero_approx()
+ ):
+ return false
+ var data: Dictionary = {
+ "request_id": _new_request_id("stamp"),
+ "session_id": _session.get_session_id(),
+ "origin": SurfaceDrawingProtocol.vector_to_array(origin),
+ "normal": SurfaceDrawingProtocol.vector_to_array(normal.normalized()),
+ "tangent": SurfaceDrawingProtocol.vector_to_array(tangent.normalized()),
+ "width": _grid_size,
+ "height": _grid_size,
+ "cell_size": SurfaceDrawingProtocol.CELL_SIZE,
+ "pixels": _stamp_pixels,
+ }
+ if not SurfaceDrawingProtocol.validate_stamp_request(data):
+ return false
+ if _session.is_host():
+ _handle_stamp_request(_session.get_local_peer_id(), data)
+ else:
+ submit_stamp_request.rpc_id(1, data)
+ return true
+
+
func request_cell_edits(
canvas_id: String,
edits: Array[Dictionary],
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()
):
@@ -613,7 +706,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"),
@@ -633,8 +726,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")
@@ -683,6 +775,29 @@ func clear_session_artwork() -> bool:
return true
+func clear_artwork_by_fingerprint(fingerprint: String) -> int:
+ if (
+ not _drawing_available()
+ or not _session.is_host()
+ or not NetworkIdentityCrypto.valid_fingerprint(fingerprint)
+ ):
+ return -1
+ var affected_layers: int = _apply_artwork_fingerprint_clear(fingerprint)
+ 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, SurfaceDrawingProtocol.CAPABILITY
+ )
+ ):
+ receive_artwork_fingerprint_clear.rpc_id(
+ peer_id,
+ _session.get_session_id(),
+ fingerprint,
+ )
+ return affected_layers
+
+
func get_canvas_ids() -> Array[String]:
var result: Array[String] = []
for canvas_id: String in _canvas_states:
@@ -695,9 +810,230 @@ func get_canvas_state(canvas_id: String) -> Dictionary:
return Dictionary(_canvas_states.get(canvas_id, {})).duplicate(true)
+func is_stamp_mode() -> bool:
+ return _stamp_mode
+
+
+func get_saved_stamp_entries() -> Array[Dictionary]:
+ var result: Array[Dictionary] = []
+ var directory: String = _artwork_directory()
+ if directory.is_empty() or not DirAccess.dir_exists_absolute(directory):
+ return result
+ var file_names: PackedStringArray = DirAccess.get_files_at(directory)
+ file_names.sort()
+ file_names.reverse()
+ for file_name: String in file_names:
+ if file_name.get_extension().to_lower() != "png":
+ continue
+ var entry: Dictionary = _decode_stamp_file(
+ directory.path_join(file_name)
+ )
+ if entry.is_empty():
+ continue
+ entry["available"] = _stamp_entry_is_unlocked(entry)
+ result.append(entry)
+ if result.size() >= 64:
+ break
+ return result
+
+
+func select_saved_stamp(path: String) -> bool:
+ if (
+ not _active
+ or _art_unlocks == null
+ or not _art_unlocks.is_stamp_unlocked()
+ ):
+ _emit_hud_state("unlock the stamp tool in Art Supplies")
+ return false
+ var entry: Dictionary = _decode_stamp_file(path)
+ if entry.is_empty():
+ _emit_hud_state("this artwork PNG cannot be used as a stamp")
+ return false
+ if not _stamp_entry_is_unlocked(entry):
+ _emit_hud_state("this stamp uses an art supply that is still locked")
+ return false
+ _clear_armed_guide_action(true)
+ _stamp_path = str(entry["path"])
+ _stamp_pixels = entry["pixels"]
+ _stamp_image = entry["image"]
+ _stamp_mode = true
+ _placing_grid = true
+ _eraser_mode = false
+ _grid_size = int(entry["grid_size"])
+ _reset_stroke()
+ _selected_canvas_id = ""
+ _rebuild_placement_preview()
+ _rebuild_stamp_preview()
+ _update_aim()
+ _refresh_stencil_visibility()
+ _emit_hud_state(
+ "stamp selected • click a solid surface to place finalized artwork"
+ )
+ return true
+
+
+func export_aimed_canvas() -> String:
+ _update_aim()
+ var canvas_id: String = (
+ _hovered_canvas_id
+ if not _hovered_canvas_id.is_empty()
+ else _last_hovered_canvas_id
+ )
+ var canvas: SurfaceDrawingCanvas = _canvas_nodes.get(canvas_id)
+ if (
+ canvas_id.is_empty()
+ or canvas == null
+ or not is_instance_valid(canvas)
+ or canvas.is_hidden_by_relationship()
+ ):
+ _last_hovered_canvas_id = ""
+ _emit_hud_state("aim at artwork before exporting")
+ return ""
+ return export_canvas_png(canvas_id)
+
+
+func export_canvas_png(canvas_id: String) -> String:
+ var canvas: SurfaceDrawingCanvas = _canvas_nodes.get(canvas_id)
+ if canvas == null or not is_instance_valid(canvas):
+ _emit_hud_state("artwork is no longer available")
+ return ""
+ if canvas.get_rendered_pixel_count() <= 0:
+ _emit_hud_state("paint something before exporting")
+ return ""
+ if _data_root == null or _data_root.root_path.is_empty():
+ _emit_hud_state("choose a straywild data folder before exporting")
+ return ""
+ var export_directory: String = _artwork_directory()
+ if DirAccess.make_dir_recursive_absolute(export_directory) != OK:
+ _emit_hud_state("the artwork folder could not be created")
+ return ""
+ var image: Image = canvas.get_export_image()
+ if image == null or image.is_empty():
+ _emit_hud_state("the artwork image could not be prepared")
+ return ""
+ var export_path: String = _next_artwork_export_path(export_directory)
+ if image.save_png(export_path) != OK:
+ _emit_hud_state("the artwork PNG could not be saved")
+ return ""
+ print("Art Kit artwork exported: ", export_path)
+ _emit_hud_state("artwork exported to the data folder • artwork")
+ return export_path
+
+
+func _artwork_directory() -> String:
+ if _data_root == null or _data_root.root_path.is_empty():
+ return ""
+ return _data_root.root_path.path_join(ARTWORK_DIRECTORY_NAME)
+
+
+func _decode_stamp_file(path: String) -> Dictionary:
+ var directory: String = _artwork_directory()
+ if directory.is_empty():
+ return {}
+ var normalized_directory: String = directory.simplify_path().trim_suffix(
+ "/"
+ )
+ var normalized_path: String = path.simplify_path()
+ if not normalized_path.begins_with(normalized_directory + "/"):
+ return {}
+ if normalized_path.get_extension().to_lower() != "png":
+ return {}
+ var image: Image = Image.load_from_file(normalized_path)
+ if image == null or image.is_empty():
+ return {}
+ if (
+ image.get_width() != image.get_height()
+ or image.get_width() not in SurfaceDrawingProtocol.GRID_SIZES
+ ):
+ return {}
+ if image.is_compressed() and image.decompress() != OK:
+ return {}
+ image.convert(Image.FORMAT_RGBA8)
+ var grid_size: int = image.get_width()
+ var pixels := PackedByteArray()
+ pixels.resize(grid_size * grid_size)
+ var used_color_ids: Array[StringName] = []
+ var painted_count: int = 0
+ for cell_y: int in range(grid_size):
+ var image_y: int = grid_size - 1 - cell_y
+ for x: int in range(grid_size):
+ var color: Color = image.get_pixel(x, image_y)
+ var palette_index: int = 0
+ if color.a >= STAMP_ALPHA_THRESHOLD:
+ palette_index = _stamp_palette_index(color)
+ if palette_index <= 0:
+ return {}
+ painted_count += 1
+ var color_id: StringName = SurfaceDrawingPalette.get_color_ids()[
+ palette_index - 1
+ ]
+ if color_id not in used_color_ids:
+ used_color_ids.append(color_id)
+ pixels[cell_y * grid_size + x] = palette_index
+ if painted_count <= 0:
+ return {}
+ return {
+ "path": normalized_path,
+ "file_name": normalized_path.get_file(),
+ "grid_size": grid_size,
+ "painted_count": painted_count,
+ "used_color_ids": used_color_ids,
+ "pixels": pixels,
+ "image": image,
+ }
+
+
+func _stamp_palette_index(color: Color) -> int:
+ var palette_ids: Array[StringName] = SurfaceDrawingPalette.get_color_ids()
+ for index: int in range(palette_ids.size()):
+ var palette_color: Color = SurfaceDrawingPalette.get_color(
+ palette_ids[index]
+ )
+ var difference := Vector3(
+ color.r - palette_color.r,
+ color.g - palette_color.g,
+ color.b - palette_color.b,
+ )
+ if difference.length() <= STAMP_COLOR_TOLERANCE:
+ return index + 1
+ return 0
+
+
+func _stamp_entry_is_unlocked(entry: Dictionary) -> bool:
+ if _art_unlocks == null or not _art_unlocks.is_stamp_unlocked():
+ return false
+ if not _art_unlocks.is_grid_size_unlocked(int(entry.get("grid_size", 0))):
+ return false
+ for color_id: StringName in entry.get("used_color_ids", []):
+ if not _art_unlocks.is_color_unlocked(color_id):
+ return false
+ return true
+
+
+func _next_artwork_export_path(directory: String) -> String:
+ var date_time: Dictionary = Time.get_datetime_dict_from_system()
+ var stem: String = "straywild-artwork-%04d%02d%02d-%02d%02d%02d" % [
+ int(date_time.get("year", 0)),
+ int(date_time.get("month", 0)),
+ int(date_time.get("day", 0)),
+ int(date_time.get("hour", 0)),
+ int(date_time.get("minute", 0)),
+ int(date_time.get("second", 0)),
+ ]
+ var candidate: String = directory.path_join(stem + ".png")
+ var suffix: int = 2
+ while FileAccess.file_exists(candidate):
+ candidate = directory.path_join("%s-%d.png" % [stem, suffix])
+ suffix += 1
+ return candidate
+
+
func _process(_delta: float) -> void:
if not _active:
return
+ if not can_activate():
+ deactivate()
+ return
_update_aim()
if _placing_grid:
return
@@ -724,26 +1060,55 @@ func _update_aim() -> void:
_aim_hit = _raycast_from_pointer()
var previous_canvas_id: String = _selected_canvas_id
var found_canvas_id: String = ""
+ var found_hovered_canvas_id: String = ""
var nearest_plane_distance: float = INF
+ var nearest_hovered_plane_distance: float = INF
+ var selected_layer: int = -1
+ var hovered_layer: int = -1
if not _aim_hit.is_empty():
var hit_position: Vector3 = _aim_hit["position"]
for canvas_id: String in _canvas_nodes:
var canvas: SurfaceDrawingCanvas = _canvas_nodes[canvas_id]
- if canvas.is_finalized():
+ if canvas.is_hidden_by_relationship():
continue
var plane_distance: float = canvas.get_surface_plane_distance(
hit_position
)
+ if not canvas.contains_world_point(
+ hit_position,
+ SurfaceDrawingCanvas.SURFACE_SAMPLE_DEPTH + 0.05,
+ ):
+ continue
+ var state: Dictionary = _canvas_states.get(canvas_id, {})
+ var layer: int = int(state.get("layer", 0))
if (
- canvas.contains_world_point(
- hit_position,
- SurfaceDrawingCanvas.SURFACE_SAMPLE_DEPTH + 0.05,
+ plane_distance < nearest_hovered_plane_distance
+ or (
+ is_equal_approx(
+ plane_distance, nearest_hovered_plane_distance
+ )
+ and layer > hovered_layer
+ )
+ ):
+ found_hovered_canvas_id = canvas_id
+ nearest_hovered_plane_distance = plane_distance
+ hovered_layer = layer
+ if canvas.is_finalized():
+ continue
+ if (
+ plane_distance < nearest_plane_distance
+ or (
+ is_equal_approx(plane_distance, nearest_plane_distance)
+ and layer > selected_layer
)
- and plane_distance < nearest_plane_distance
):
found_canvas_id = canvas_id
nearest_plane_distance = plane_distance
+ selected_layer = layer
_selected_canvas_id = found_canvas_id
+ _hovered_canvas_id = found_hovered_canvas_id
+ if not _hovered_canvas_id.is_empty():
+ _last_hovered_canvas_id = _hovered_canvas_id
if previous_canvas_id != _selected_canvas_id:
_reset_stroke()
_update_previews()
@@ -781,6 +1146,8 @@ func _update_previews() -> void:
return
if _placement_preview != null:
_placement_preview.hide()
+ if _stamp_preview != null:
+ _stamp_preview.hide()
_update_brush_preview()
@@ -788,6 +1155,8 @@ func _update_placement_preview() -> void:
if _placement_preview == null or _aim_hit.is_empty():
if _placement_preview != null:
_placement_preview.hide()
+ if _stamp_preview != null:
+ _stamp_preview.hide()
return
var placement: Dictionary = _resolved_placement()
var normal: Vector3 = placement["normal"]
@@ -798,6 +1167,11 @@ func _update_placement_preview() -> void:
Basis(tangent, bitangent, normal),
hit_position + normal * GRID_PREVIEW_SURFACE_OFFSET,
)
+ if _stamp_preview != null:
+ _stamp_preview.global_transform = Transform3D(
+ Basis(tangent, bitangent, normal),
+ hit_position + normal * (GRID_PREVIEW_SURFACE_OFFSET + 0.001),
+ )
if _placement_preview_material != null:
_placement_preview_material.albedo_color = (
Color(0.34, 1.0, 0.72, 0.88)
@@ -805,6 +1179,8 @@ func _update_placement_preview() -> void:
else Color(0.46, 0.91, 0.95, 0.72)
)
_placement_preview.show()
+ if _stamp_preview != null:
+ _stamp_preview.visible = _stamp_mode
func _update_brush_preview() -> void:
@@ -851,6 +1227,8 @@ func _hide_previews() -> void:
_hide_brush_preview()
if _placement_preview != null:
_placement_preview.hide()
+ if _stamp_preview != null:
+ _stamp_preview.hide()
func _resolved_placement() -> Dictionary:
@@ -899,6 +1277,20 @@ func _request_canvas_at_aim() -> void:
)
+func _request_stamp_at_aim() -> void:
+ if _aim_hit.is_empty():
+ _emit_hud_state("aim at a solid surface before placing a stamp")
+ return
+ var placement: Dictionary = _resolved_placement()
+ var normal: Vector3 = placement["normal"]
+ var origin: Vector3 = placement["origin"]
+ if _overlaps_existing_canvas(origin, normal, _grid_size):
+ _emit_hud_state("an active shared grid already covers this area")
+ return
+ if request_stamp_at_surface(origin, normal, placement["tangent"]):
+ _emit_hud_state("placing finalized stamp...")
+
+
@rpc(
"any_peer",
"call_remote",
@@ -979,6 +1371,7 @@ func _handle_canvas_request(peer_id: int, data: Dictionary) -> void:
"finalized": false,
"layer": _canvas_sequence,
"creator_fingerprint": record.identity_fingerprint,
+ "participant_fingerprints": [record.identity_fingerprint],
"cells": [],
}
_apply_canvas_state(state)
@@ -986,6 +1379,117 @@ func _handle_canvas_request(peer_id: int, data: Dictionary) -> void:
_emit_hud_state("shared grid placed • everyone can draw here")
+@rpc(
+ "any_peer",
+ "call_remote",
+ "reliable",
+ SurfaceDrawingProtocol.RELIABLE_CHANNEL,
+)
+func submit_stamp_request(data: Dictionary) -> void:
+ var sender_id: int = multiplayer.get_remote_sender_id()
+ if _session.is_host() and _session.is_authenticated_peer(sender_id):
+ _handle_stamp_request(sender_id, data)
+
+
+func _handle_stamp_request(peer_id: int, data: Dictionary) -> void:
+ var requested_size: int = int(data.get("width", 0))
+ if (
+ not _session.is_host()
+ or not SurfaceDrawingProtocol.validate_stamp_request(data)
+ or str(data["session_id"]) != _session.get_session_id()
+ or not _accept_request(peer_id, str(data["request_id"]))
+ or not _peer_can_stamp(peer_id, data)
+ or _canvas_states.size() >= SurfaceDrawingProtocol.MAX_CANVASES
+ or _allocated_grid_cells() + requested_size * requested_size
+ > SurfaceDrawingProtocol.MAX_SESSION_GRID_CELLS
+ ):
+ return
+ var avatar: Player = _spawn_service.get_avatar(peer_id)
+ var requested_origin: Vector3 = SurfaceDrawingProtocol.array_to_vector(
+ data["origin"]
+ )
+ if (
+ avatar == null
+ or avatar.global_position.distance_to(requested_origin)
+ > MAX_DRAW_DISTANCE + 2.0
+ ):
+ return
+ var requested_normal: Vector3 = SurfaceDrawingProtocol.array_to_vector(
+ data["normal"]
+ ).normalized()
+ var surface_hit: Dictionary = _validate_surface(
+ requested_origin, requested_normal
+ )
+ if surface_hit.is_empty():
+ return
+ var normal: Vector3 = _normalized_or(
+ surface_hit.get("normal", requested_normal), requested_normal
+ )
+ if _overlaps_existing_canvas(
+ surface_hit["position"], normal, requested_size
+ ):
+ return
+ var tangent: Vector3 = SurfaceDrawingProtocol.array_to_vector(
+ data["tangent"]
+ )
+ tangent = (tangent - normal * tangent.dot(normal)).normalized()
+ if tangent.is_zero_approx():
+ tangent = _surface_tangent(normal)
+ var record: PeerRegistry.PeerRecord = _session.get_peer_record(peer_id)
+ if record == null or not record.identity_authenticated:
+ return
+ _canvas_sequence += 1
+ var canvas_id: String = "%s-%d" % [
+ _session.get_session_id().left(16), _canvas_sequence,
+ ]
+ var state: Dictionary = {
+ "session_id": _session.get_session_id(),
+ "canvas_id": canvas_id,
+ "origin": SurfaceDrawingProtocol.vector_to_array(
+ surface_hit["position"]
+ ),
+ "normal": SurfaceDrawingProtocol.vector_to_array(normal),
+ "tangent": SurfaceDrawingProtocol.vector_to_array(tangent),
+ "width": requested_size,
+ "height": requested_size,
+ "cell_size": SurfaceDrawingProtocol.CELL_SIZE,
+ "revision": 1,
+ "guide_visible": false,
+ "finalized": true,
+ "layer": _canvas_sequence,
+ "creator_fingerprint": record.identity_fingerprint,
+ "participant_fingerprints": [record.identity_fingerprint],
+ "cells": _stamp_cells(
+ data["pixels"], requested_size, record.identity_fingerprint
+ ),
+ "stamp": true,
+ }
+ _apply_canvas_state(state)
+ _broadcast_canvas_state(state)
+ _emit_hud_state("stamp placed • finalized artwork shared with everyone")
+
+
+func _stamp_cells(
+ pixels: PackedByteArray,
+ grid_size: int,
+ author_fingerprint: String,
+) -> Array[Dictionary]:
+ var result: Array[Dictionary] = []
+ var palette_ids: Array[StringName] = SurfaceDrawingPalette.get_color_ids()
+ for cell_key: int in range(pixels.size()):
+ var palette_index: int = pixels[cell_key]
+ if palette_index <= 0:
+ continue
+ var cell_y: int = floori(float(cell_key) / float(grid_size))
+ result.append({
+ "x": cell_key % grid_size,
+ "y": cell_y,
+ "color_id": str(palette_ids[palette_index - 1]),
+ "author_fingerprint": author_fingerprint,
+ })
+ return result
+
+
@rpc(
"any_peer",
"call_remote",
@@ -1010,7 +1514,13 @@ func _handle_guide_request(peer_id: int, data: Dictionary) -> void:
var canvas_id: String = str(data["canvas_id"])
var state: Dictionary = _canvas_states.get(canvas_id, {})
var avatar: Player = _spawn_service.get_avatar(peer_id)
- if state.is_empty() or avatar == null:
+ var record: PeerRegistry.PeerRecord = _session.get_peer_record(peer_id)
+ if (
+ state.is_empty()
+ or avatar == null
+ or record == null
+ or not record.identity_authenticated
+ ):
return
var origin: Vector3 = SurfaceDrawingProtocol.array_to_vector(state["origin"])
if avatar.global_position.distance_to(origin) > MAX_DRAW_DISTANCE + 2.0:
@@ -1025,6 +1535,11 @@ func _handle_guide_request(peer_id: int, data: Dictionary) -> void:
and was_finalized == finalized
):
return
+ if not _register_canvas_participant(
+ canvas_id, record.identity_fingerprint
+ ):
+ return
+ state = _canvas_states.get(canvas_id, {})
state["revision"] = int(state["revision"]) + 1
state["guide_visible"] = guide_visible
state["finalized"] = finalized
@@ -1035,6 +1550,9 @@ func _handle_guide_request(peer_id: int, data: Dictionary) -> void:
"revision": int(state["revision"]),
"guide_visible": guide_visible,
"finalized": finalized,
+ "participant_fingerprints": state.get(
+ "participant_fingerprints", []
+ ),
}
var canvas: SurfaceDrawingCanvas = _canvas_nodes.get(canvas_id)
if canvas != null:
@@ -1215,6 +1733,15 @@ func _queue_cell_mutation(
var next: Dictionary = _cell_value_from_edit(edit)
if _same_cell_value(current, next):
return
+ var record: PeerRegistry.PeerRecord = _session.get_peer_record(peer_id)
+ if (
+ record == null
+ or not record.identity_authenticated
+ or not _register_canvas_participant(
+ canvas_id, record.identity_fingerprint
+ )
+ ):
+ return
_record_stroke_change(
peer_id, stroke_id, canvas_id, x, y, current, next
)
@@ -1356,12 +1883,18 @@ func _publish_cell_mutations(
continue
state["revision"] = int(state["revision"]) + 1
state["cells"] = _sorted_cells(cells)
+ state["participant_fingerprints"] = _merged_participants(
+ state, {"edits": edits}
+ )
_canvas_states[canvas_id] = state
var update: Dictionary = {
"session_id": _session.get_session_id(),
"canvas_id": canvas_id,
"revision": int(state["revision"]),
"edits": edits,
+ "participant_fingerprints": state.get(
+ "participant_fingerprints", []
+ ),
}
var local_canvas: SurfaceDrawingCanvas = _canvas_nodes.get(canvas_id)
if local_canvas != null:
@@ -1499,6 +2032,23 @@ func receive_session_artwork_reset(session_id: String) -> void:
_clear_session_artwork_state()
+@rpc(
+ "authority",
+ "call_remote",
+ "reliable",
+ SurfaceDrawingProtocol.RELIABLE_CHANNEL,
+)
+func receive_artwork_fingerprint_clear(
+ session_id: String,
+ fingerprint: String,
+) -> void:
+ if (
+ session_id == _session.get_session_id()
+ and NetworkIdentityCrypto.valid_fingerprint(fingerprint)
+ ):
+ _apply_artwork_fingerprint_clear(fingerprint)
+
+
@rpc(
"any_peer",
"call_remote",
@@ -1535,7 +2085,28 @@ func _apply_canvas_state(data: Dictionary) -> void:
and int(data["revision"]) <= int(previous_state["revision"])
):
return
- _canvas_states[canvas_id] = data.duplicate(true)
+ var normalized_state: Dictionary = data.duplicate(true)
+ normalized_state["participant_fingerprints"] = _state_participants(
+ normalized_state
+ )
+ _canvas_states[canvas_id] = normalized_state
+ _rebuild_canvas_node(normalized_state)
+ _refresh_stencil_visibility()
+ _emit_artwork_changed()
+ if (
+ _active
+ and str(normalized_state.get("creator_fingerprint", ""))
+ == _session.get_local_identity_fingerprint()
+ ):
+ _emit_hud_state(
+ "stamp placed • move and click to place another"
+ if bool(normalized_state.get("stamp", false))
+ else "shared grid placed • move and click to place another"
+ )
+
+
+func _rebuild_canvas_node(data: Dictionary) -> void:
+ var canvas_id: String = str(data["canvas_id"])
var previous_canvas: SurfaceDrawingCanvas = _canvas_nodes.get(canvas_id)
if previous_canvas != null:
previous_canvas.queue_free()
@@ -1546,14 +2117,6 @@ func _apply_canvas_state(data: Dictionary) -> void:
canvas.queue_free()
return
_canvas_nodes[canvas_id] = canvas
- _refresh_stencil_visibility()
- _emit_artwork_changed()
- if (
- _active
- and str(data.get("creator_fingerprint", ""))
- == _session.get_local_identity_fingerprint()
- ):
- _emit_hud_state("shared grid placed • move and click to place another")
func _apply_canvas_update(data: Dictionary) -> void:
@@ -1585,6 +2148,9 @@ func _apply_canvas_update(data: Dictionary) -> void:
cells[key] = edit.duplicate(true)
state["revision"] = int(data["revision"])
state["cells"] = _sorted_cells(cells)
+ state["participant_fingerprints"] = _merged_participants(
+ state, data
+ )
_canvas_states[canvas_id] = state
canvas.apply_update(data)
_emit_artwork_changed()
@@ -1610,6 +2176,9 @@ func _apply_guide_update(data: Dictionary) -> void:
state["guide_visible"] = (
bool(data["guide_visible"]) and not bool(data["finalized"])
)
+ state["participant_fingerprints"] = _merged_participants(
+ state, data
+ )
_canvas_states[canvas_id] = state
canvas.apply_guide_update(data)
if _active:
@@ -1844,10 +2413,46 @@ func _rebuild_placement_preview() -> void:
_drawing_root.add_child(_placement_preview)
+func _rebuild_stamp_preview() -> void:
+ if _stamp_preview != null:
+ _stamp_preview.queue_free()
+ _stamp_preview = null
+ _stamp_preview_material = null
+ if (
+ _drawing_root == null
+ or not _stamp_mode
+ or _stamp_image == null
+ or _stamp_image.is_empty()
+ ):
+ return
+ var quad := QuadMesh.new()
+ quad.size = Vector2.ONE * (
+ float(_grid_size) * SurfaceDrawingProtocol.CELL_SIZE
+ )
+ _stamp_preview_material = StandardMaterial3D.new()
+ _stamp_preview_material.shading_mode = BaseMaterial3D.SHADING_MODE_UNSHADED
+ _stamp_preview_material.transparency = BaseMaterial3D.TRANSPARENCY_ALPHA
+ _stamp_preview_material.texture_filter = BaseMaterial3D.TEXTURE_FILTER_NEAREST
+ _stamp_preview_material.cull_mode = BaseMaterial3D.CULL_DISABLED
+ _stamp_preview_material.albedo_color = Color(1.0, 1.0, 1.0, 0.72)
+ _stamp_preview_material.albedo_texture = ImageTexture.create_from_image(
+ _stamp_image
+ )
+ quad.material = _stamp_preview_material
+ _stamp_preview = MeshInstance3D.new()
+ _stamp_preview.name = "ArtworkStampPlacementPreview"
+ _stamp_preview.mesh = quad
+ _stamp_preview.cast_shadow = GeometryInstance3D.SHADOW_CASTING_SETTING_OFF
+ _stamp_preview.hide()
+ _drawing_root.add_child(_stamp_preview)
+
+
func _set_placement_mode(enabled: bool) -> void:
if not _active:
return
_clear_armed_guide_action(true)
+ if _stamp_mode:
+ _clear_stamp_mode()
if _placing_grid == enabled:
_emit_hud_state("")
return
@@ -1865,6 +2470,17 @@ func _set_placement_mode(enabled: bool) -> void:
)
+func _clear_stamp_mode() -> void:
+ _stamp_mode = false
+ _stamp_path = ""
+ _stamp_pixels = PackedByteArray()
+ _stamp_image = null
+ if _stamp_preview != null:
+ _stamp_preview.queue_free()
+ _stamp_preview = null
+ _stamp_preview_material = null
+
+
func _execute_armed_guide_action() -> void:
var action: int = _armed_guide_action
match action:
@@ -1874,8 +2490,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:
@@ -1904,12 +2522,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")
@@ -2015,7 +2639,11 @@ func _current_color() -> Color:
func _emit_hud_state(status: String) -> void:
hud_state_changed.emit(
_active,
- "place grid" if _placing_grid else "marker",
+ (
+ "stamp"
+ if _stamp_mode
+ else "place grid" if _placing_grid else "marker"
+ ),
SurfaceDrawingPalette.get_display_name(_current_color_id()),
_current_color(),
_brush_size,
@@ -2076,6 +2704,68 @@ func _state_cell(state: Dictionary, x: int, y: int) -> Dictionary:
).duplicate(true)
+func _register_canvas_participant(
+ canvas_id: String,
+ fingerprint: String,
+) -> bool:
+ if not NetworkIdentityCrypto.valid_fingerprint(fingerprint):
+ return false
+ var state: Dictionary = _canvas_states.get(canvas_id, {})
+ if state.is_empty():
+ return false
+ var participants: Array[String] = _state_participants(state)
+ if fingerprint in participants:
+ return true
+ if participants.size() >= SurfaceDrawingProtocol.MAX_PARTICIPANTS:
+ return false
+ participants.append(fingerprint)
+ state["participant_fingerprints"] = participants
+ _canvas_states[canvas_id] = state
+ return true
+
+
+func _merged_participants(
+ state: Dictionary,
+ update: Dictionary,
+) -> Array[String]:
+ var result: Array[String] = _state_participants(state)
+ for value: Variant in update.get("participant_fingerprints", []):
+ _append_participant(result, str(value))
+ for edit_value: Variant in update.get("edits", []):
+ var edit: Dictionary = edit_value
+ _append_participant(
+ result, str(edit.get("author_fingerprint", ""))
+ )
+ return result
+
+
+func _state_participants(state: Dictionary) -> Array[String]:
+ var result: Array[String] = []
+ _append_participant(
+ result, str(state.get("creator_fingerprint", ""))
+ )
+ for value: Variant in state.get("participant_fingerprints", []):
+ _append_participant(result, str(value))
+ for cell_value: Variant in state.get("cells", []):
+ var cell: Dictionary = cell_value
+ _append_participant(
+ result, str(cell.get("author_fingerprint", ""))
+ )
+ return result
+
+
+func _append_participant(
+ participants: Array[String],
+ fingerprint: String,
+) -> void:
+ if (
+ NetworkIdentityCrypto.valid_fingerprint(fingerprint)
+ and fingerprint not in participants
+ and participants.size() < SurfaceDrawingProtocol.MAX_PARTICIPANTS
+ ):
+ participants.append(fingerprint)
+
+
func _cell_value_from_edit(edit: Dictionary) -> Dictionary:
if edit.is_empty() or str(edit.get("color_id", "")).is_empty():
return {}
@@ -2163,7 +2853,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()
@@ -2213,6 +2903,8 @@ func _clear_session_artwork_state() -> void:
_canvas_nodes.clear()
_canvas_sequence = 0
_selected_canvas_id = ""
+ _hovered_canvas_id = ""
+ _last_hovered_canvas_id = ""
_stroke_history_by_peer.clear()
_cell_last_stroke.clear()
_last_local_stroke_id = ""
@@ -2220,6 +2912,38 @@ func _clear_session_artwork_state() -> void:
_emit_artwork_changed()
+func _apply_artwork_fingerprint_clear(fingerprint: String) -> int:
+ var affected_canvas_ids: Array[String] = []
+ for canvas_id: String in _canvas_states:
+ var state: Dictionary = _canvas_states[canvas_id]
+ if fingerprint in _state_participants(state):
+ affected_canvas_ids.append(canvas_id)
+ if affected_canvas_ids.is_empty():
+ return 0
+ for canvas_id: String in affected_canvas_ids:
+ _remove_canvas_state(canvas_id)
+ _stroke_history_by_peer.clear()
+ _cell_last_stroke.clear()
+ _last_local_stroke_id = ""
+ _hide_previews()
+ _emit_artwork_changed()
+ return affected_canvas_ids.size()
+
+
+func _remove_canvas_state(canvas_id: String) -> void:
+ _canvas_states.erase(canvas_id)
+ var canvas: SurfaceDrawingCanvas = _canvas_nodes.get(canvas_id)
+ if canvas != null:
+ canvas.queue_free()
+ _canvas_nodes.erase(canvas_id)
+ if _selected_canvas_id == canvas_id:
+ _selected_canvas_id = ""
+ if _hovered_canvas_id == canvas_id:
+ _hovered_canvas_id = ""
+ if _last_hovered_canvas_id == canvas_id:
+ _last_hovered_canvas_id = ""
+
+
func _emit_artwork_changed() -> void:
session_artwork_changed.emit(
get_canvas_count(), get_painted_cell_count()
diff --git a/network/network_transport.gd b/network/network_transport.gd
index ce68172..5aed6ca 100644
--- a/network/network_transport.gd
+++ b/network/network_transport.gd
@@ -22,6 +22,25 @@ func connect_to_route(_route: ConnectionRoute) -> Error:
func disconnect_transport() -> void:
if _peer != null:
+ # Give ENet one poll cycle to emit its graceful disconnect packets before
+ # closing the socket. This keeps a host from retaining a departed player
+ # until the extended generated-world liveness timeout expires.
+ if (
+ _peer is ENetMultiplayerPeer
+ and _peer.get_connection_status()
+ == MultiplayerPeer.CONNECTION_CONNECTED
+ ):
+ var enet_peer := _peer as ENetMultiplayerPeer
+ # A host explicitly notifies each still-live client before closing.
+ # Clients rely on ENetMultiplayerPeer.close(); their cached SceneTree
+ # peer list can briefly outlive the underlying ENet peer after a remote
+ # disconnect, making disconnect_peer() race and report an engine error.
+ if enet_peer.get_unique_id() == 1:
+ for peer_id: int in multiplayer.get_peers():
+ if enet_peer.get_peer(peer_id) != null:
+ enet_peer.disconnect_peer(peer_id, false)
+ if enet_peer.host != null:
+ enet_peer.host.flush()
_peer.close()
_peer = null
_route_description = ""
diff --git a/network/network_world_spawn_protocol.gd b/network/network_world_spawn_protocol.gd
new file mode 100644
index 0000000..4bd15b0
--- /dev/null
+++ b/network/network_world_spawn_protocol.gd
@@ -0,0 +1,94 @@
+class_name NetworkWorldSpawnProtocol
+extends RefCounted
+
+const CAPABILITY: StringName = &"world_spawn_envelope_v1"
+const ENVELOPE_VERSION: int = 1
+const RELIABLE_CHANNEL: int = NetworkProtocol.WORLD_SPAWN_RELIABLE_CHANNEL
+const SNAPSHOT_CHANNEL: int = 15
+const MAX_SESSION_ID_LENGTH: int = 96
+const MAX_EVENT_ID_LENGTH: int = 48
+const MAX_ENTITY_ID_LENGTH: int = 128
+const MAX_TYPE_ID_LENGTH: int = 96
+const MAX_ENTITIES_PER_SNAPSHOT: int = 128
+## Keeps unreliable movement envelopes below ENet's practical MTU while
+## allowing reliable population envelopes to carry a larger initial state.
+const SNAPSHOT_ENTITIES_PER_ENVELOPE: int = 4
+
+
+static func make_envelope(
+ session_id: String,
+ sequence: int,
+ event_id: StringName,
+ payload: Dictionary,
+) -> Dictionary:
+ return {
+ "envelope_version": ENVELOPE_VERSION,
+ "session_id": session_id,
+ "sequence": sequence,
+ "event_id": str(event_id),
+ "payload": payload.duplicate(true),
+ }
+
+
+static func validate_envelope(value: Variant) -> bool:
+ if typeof(value) != TYPE_DICTIONARY:
+ return false
+ var data: Dictionary = value
+ return (
+ typeof(data.get("envelope_version")) == TYPE_INT
+ and int(data["envelope_version"]) == ENVELOPE_VERSION
+ and typeof(data.get("session_id")) == TYPE_STRING
+ and not str(data["session_id"]).is_empty()
+ and str(data["session_id"]).length() <= MAX_SESSION_ID_LENGTH
+ and typeof(data.get("sequence")) == TYPE_INT
+ and int(data["sequence"]) >= 0
+ and typeof(data.get("event_id")) == TYPE_STRING
+ and not str(data["event_id"]).is_empty()
+ and str(data["event_id"]).length() <= MAX_EVENT_ID_LENGTH
+ and typeof(data.get("payload")) == TYPE_DICTIONARY
+ )
+
+
+static func validate_entity_state(value: Variant) -> bool:
+ if typeof(value) != TYPE_DICTIONARY:
+ return false
+ var data: Dictionary = value
+ var position: Variant = data.get("position")
+ if typeof(position) != TYPE_ARRAY or position.size() != 3:
+ return false
+ for component: Variant in position:
+ if typeof(component) not in [TYPE_FLOAT, TYPE_INT]:
+ return false
+ if not is_finite(float(component)):
+ return false
+ return (
+ typeof(data.get("entity_id")) == TYPE_STRING
+ and not str(data["entity_id"]).is_empty()
+ and str(data["entity_id"]).length() <= MAX_ENTITY_ID_LENGTH
+ and typeof(data.get("type_id")) == TYPE_STRING
+ and not str(data["type_id"]).is_empty()
+ and str(data["type_id"]).length() <= MAX_TYPE_ID_LENGTH
+ and typeof(data.get("yaw")) in [TYPE_FLOAT, TYPE_INT]
+ and is_finite(float(data["yaw"]))
+ and typeof(data.get("revision")) == TYPE_INT
+ and int(data["revision"]) >= 0
+ )
+
+
+static func vector3_to_array(value: Vector3) -> Array[float]:
+ return [value.x, value.y, value.z]
+
+
+static func array_to_vector3(value: Variant) -> Vector3:
+ if typeof(value) != TYPE_ARRAY or value.size() != 3:
+ return Vector3(INF, INF, INF)
+ for component: Variant in value:
+ if typeof(component) not in [TYPE_FLOAT, TYPE_INT]:
+ return Vector3(INF, INF, INF)
+ if not is_finite(float(component)):
+ return Vector3(INF, INF, INF)
+ return _array_to_vector3(value)
+
+
+static func _array_to_vector3(value: Array) -> Vector3:
+ return Vector3(float(value[0]), float(value[1]), float(value[2]))
diff --git a/network/network_world_spawn_protocol.gd.uid b/network/network_world_spawn_protocol.gd.uid
new file mode 100644
index 0000000..d245393
--- /dev/null
+++ b/network/network_world_spawn_protocol.gd.uid
@@ -0,0 +1 @@
+uid://wstcwpwk41mf
diff --git a/network/network_world_spawn_service.gd b/network/network_world_spawn_service.gd
new file mode 100644
index 0000000..8981237
--- /dev/null
+++ b/network/network_world_spawn_service.gd
@@ -0,0 +1,1532 @@
+class_name NetworkWorldSpawnService
+extends Node
+
+const FishCatchType = preload("res://fish/fish_catch.gd")
+const FishDataType = preload("res://fish/fish_data.gd")
+const FishExperienceType = preload("res://fish/fish_experience.gd")
+const FishPoolType = preload("res://fish/fish_pool.gd")
+const FishQualityType = preload("res://fish/fish_quality.gd")
+const FishSelectorType = preload("res://fish/fish_selector.gd")
+const GatherableCatalogType = preload("res://gathering/gatherable_catalog.gd")
+const GatherableDataType = preload("res://gathering/gatherable_data.gd")
+const WorldGatherableType = preload("res://gathering/world_gatherable.gd")
+const TestWorldType = preload("res://world/test_world.gd")
+const CalendarSeasonType = preload("res://world/calendar_season.gd")
+
+signal local_capture_received(fish_catch: FishCatch)
+signal local_interaction_finished(accepted: bool, message: String)
+
+const SIMULATION_INTERVAL_SECONDS: float = 1.0 / 15.0
+const SNAPSHOT_INTERVAL_SECONDS: float = 0.1
+const PLAYER_SPATIAL_CELL_SIZE: float = 8.0
+const CAPACITY_RESPONSE_TIMEOUT_SECONDS: float = 3.0
+const MAX_REQUEST_ID_LENGTH: int = 128
+const MAX_LEDGER_ENTRIES: int = 96
+
+var _session: NetworkSession
+var _spawn_service: PlayerSpawnService
+var _world: TestWorldType
+var _world_root: Node3D
+var _world_time: WorldTimeService
+var _catalog: GatherableCatalogType
+var _fish_catalog: FishPoolType
+var _local_inventory: FishInventory
+var _local_collection: CollectionLog
+var _local_capacity: PlayerCoolerCapacity
+var _local_experience: PlayerExperience
+var _save_manager: PlayerSaveManager
+var _item_use: NetworkItemUseService
+
+var _entities: Dictionary = {}
+var _presentations: Dictionary = {}
+var _entity_revisions: Dictionary = {}
+var _surface_triangles: Dictionary = {}
+var _surface_areas: Dictionary = {}
+var _surface_total_areas: Dictionary = {}
+var _surface_height_ranges: Dictionary = {}
+var _spawn_anchor_positions: Dictionary = {}
+var _respawns: Array[Dictionary] = []
+var _next_respawn_by_type: Dictionary[StringName, float] = {}
+var _charge_requests: Dictionary = {}
+var _pending_captures: Dictionary = {}
+var _received_results: Dictionary = {}
+var _showcase_deadlines: Dictionary = {}
+var _envelope_sequence: int = 0
+var _simulation_elapsed: float = 0.0
+var _snapshot_elapsed: float = 0.0
+var _population_session_id: String = ""
+var _dirty_entity_ids: Dictionary[String, bool] = {}
+var _moving_players_by_cell: Dictionary[Vector2i, Array] = {}
+var _presentation_enabled: bool = true
+var _spawn_events_sent: int = 0
+var _despawn_events_sent: int = 0
+var _snapshot_packets_sent: int = 0
+var _snapshot_states_sent: int = 0
+var _rng := RandomNumberGenerator.new()
+
+
+func setup(
+ session: NetworkSession,
+ spawn_service: PlayerSpawnService,
+ world: TestWorldType,
+ world_root: Node3D,
+ catalog: GatherableCatalogType,
+ fish_catalog: FishPoolType,
+ local_inventory: FishInventory,
+ local_collection: CollectionLog,
+ local_capacity: PlayerCoolerCapacity,
+ local_experience: PlayerExperience,
+ save_manager: PlayerSaveManager,
+ item_use: NetworkItemUseService,
+ world_time: WorldTimeService = null,
+ presentation_enabled: bool = true,
+) -> void:
+ _session = session
+ _spawn_service = spawn_service
+ _world = world
+ _world_root = world_root
+ _catalog = catalog
+ _fish_catalog = fish_catalog
+ _local_inventory = local_inventory
+ _local_collection = local_collection
+ _local_capacity = local_capacity
+ _local_experience = local_experience
+ _save_manager = save_manager
+ _item_use = item_use
+ _world_time = world_time
+ _presentation_enabled = presentation_enabled
+ _rng.randomize()
+ _session.peer_authenticated.connect(_on_peer_authenticated)
+ _session.peer_removed.connect(_on_peer_removed)
+ _session.state_changed.connect(_on_session_state_changed)
+ if (
+ _world_time != null
+ and not _world_time.calendar_date_changed.is_connected(
+ _on_calendar_date_changed
+ )
+ ):
+ _world_time.calendar_date_changed.connect(_on_calendar_date_changed)
+ set_physics_process(true)
+ _begin_population_if_ready.call_deferred()
+
+
+func begin_local_interaction() -> String:
+ if (
+ _session == null
+ or not _session.is_gameplay_session_active()
+ or (
+ not _session.is_host()
+ and not _session.supports_server_capability(
+ NetworkWorldSpawnProtocol.CAPABILITY
+ )
+ )
+ ):
+ local_interaction_finished.emit(false, "Catching is unavailable.")
+ return ""
+ var request_id: String = _new_id("gather")
+ var data: Dictionary = {
+ "session_id": _session.get_session_id(),
+ "request_id": request_id,
+ }
+ if _session.is_host():
+ _handle_interaction_begin(_session.get_local_peer_id(), data)
+ else:
+ submit_interaction_begin.rpc_id(1, data)
+ return request_id
+
+
+func finish_local_interaction(
+ request_id: String,
+ entity_id: String,
+ target_position: Vector3,
+) -> void:
+ if request_id.is_empty() or not target_position.is_finite():
+ return
+ var data: Dictionary = {
+ "session_id": _session.get_session_id(),
+ "request_id": request_id,
+ "entity_id": entity_id,
+ "target_position": NetworkWorldSpawnProtocol.vector3_to_array(
+ target_position
+ ),
+ "movement_sequence": _session.get_latest_movement_input_sequence(),
+ }
+ if _session.is_host():
+ _handle_interaction_finish(_session.get_local_peer_id(), data)
+ else:
+ submit_interaction_finish.rpc_id(1, data)
+
+
+func cancel_local_interaction(request_id: String) -> void:
+ if request_id.is_empty() or _session == null:
+ return
+ if _session.is_host():
+ _handle_interaction_cancel(_session.get_local_peer_id(), request_id)
+ else:
+ submit_interaction_cancel.rpc_id(1, request_id)
+
+
+func find_entity_near(
+ position: Vector3,
+ radius: float,
+) -> String:
+ var best_id: String = ""
+ var best_distance_squared: float = radius * radius
+ for entity_id: String in _entities:
+ var state: Dictionary = _entities[entity_id]
+ if bool(state.get("locked", false)):
+ continue
+ var entity_position: Vector3 = state.get("position", Vector3.ZERO)
+ var distance_squared: float = entity_position.distance_squared_to(
+ position
+ )
+ if distance_squared <= best_distance_squared:
+ best_distance_squared = distance_squared
+ best_id = entity_id
+ return best_id
+
+
+func find_capture_target(
+ position: Vector3,
+ tool_id: StringName,
+) -> String:
+ if tool_id.is_empty():
+ return ""
+ var best_id: String = ""
+ var best_distance_squared: float = INF
+ for entity_id: String in _entities:
+ var state: Dictionary = _entities[entity_id]
+ if bool(state.get("locked", false)):
+ continue
+ var entry := state.get("data") as GatherableDataType
+ if entry == null or entry.required_tool_id != tool_id:
+ continue
+ var entity_position: Vector3 = state.get("position", Vector3.ZERO)
+ var distance_squared: float = entity_position.distance_squared_to(
+ position
+ )
+ if (
+ distance_squared <= entry.capture_radius * entry.capture_radius
+ and distance_squared <= best_distance_squared
+ ):
+ best_distance_squared = distance_squared
+ best_id = entity_id
+ return best_id
+
+
+func get_charge_duration_for_tool(tool_id: StringName) -> float:
+ var duration: float = 0.0
+ if _catalog == null or tool_id.is_empty():
+ return duration
+ for entry: GatherableDataType in _catalog.get_available_entries(
+ _current_season()
+ ):
+ if entry.required_tool_id == tool_id:
+ duration = maxf(duration, entry.charge_duration)
+ return duration
+
+
+func get_entry_for_entity(entity_id: String) -> GatherableDataType:
+ var state: Dictionary = _entities.get(entity_id, {})
+ return state.get("data") as GatherableDataType
+
+
+func refresh_world_context() -> void:
+ _clear_world()
+ _population_session_id = ""
+ if _session != null and _session.is_gameplay_session_active():
+ _begin_population_if_ready.call_deferred()
+
+
+func _physics_process(delta: float) -> void:
+ if (
+ _session == null
+ or not _session.is_host()
+ or not _session.is_gameplay_session_active()
+ ):
+ return
+ _begin_population_if_ready()
+ # There is no gameplay state to advance while a dedicated server has no
+ # spawned players. Pausing the roaming/respawn loop also prevents idle
+ # gatherables from producing a continuous stream of snapshots to nobody.
+ if _spawn_service == null or _spawn_service.get_peer_ids().is_empty():
+ _simulation_elapsed = 0.0
+ _snapshot_elapsed = 0.0
+ return
+ _simulation_elapsed += delta
+ _snapshot_elapsed += delta
+ if _simulation_elapsed < SIMULATION_INTERVAL_SECONDS:
+ return
+ var simulation_delta: float = minf(_simulation_elapsed, 0.2)
+ _simulation_elapsed = fmod(
+ _simulation_elapsed,
+ SIMULATION_INTERVAL_SECONDS,
+ )
+ _update_pending_capture_timeouts()
+ _update_respawns()
+ _update_showcase_deadlines()
+ _rebuild_moving_player_spatial_index()
+ _update_host_entities(simulation_delta)
+ if _snapshot_elapsed >= SNAPSHOT_INTERVAL_SECONDS:
+ _snapshot_elapsed = fmod(
+ _snapshot_elapsed,
+ SNAPSHOT_INTERVAL_SECONDS,
+ )
+ _broadcast_entity_snapshots()
+
+
+func _begin_population_if_ready() -> void:
+ if (
+ _session == null
+ or not _session.is_host()
+ or not _session.is_gameplay_session_active()
+ or _catalog == null
+ or _world == null
+ or _world_root == null
+ or not _world.is_world_ready()
+ ):
+ return
+ var session_id: String = _session.get_session_id()
+ if session_id.is_empty() or session_id == _population_session_id:
+ return
+ _clear_world()
+ _population_session_id = session_id
+ for entry: GatherableDataType in _catalog.get_available_entries(
+ _current_season()
+ ):
+ _cache_spawn_surface(entry)
+ for _spawn_index: int in _target_population(entry):
+ _spawn_entity(entry)
+
+
+func _cache_spawn_surface(entry: GatherableDataType) -> void:
+ if (
+ entry == null
+ or _surface_triangles.has(entry.type_id)
+ or _spawn_anchor_positions.has(entry.type_id)
+ ):
+ return
+ if not entry.spawn_anchor_set_id.is_empty():
+ var anchor_positions: PackedVector3Array = (
+ _world.get_gatherable_spawn_positions(entry.spawn_anchor_set_id)
+ )
+ _spawn_anchor_positions[entry.type_id] = anchor_positions
+ if (
+ anchor_positions.is_empty()
+ and _world.get_world_layout() == WorldLayout.GENERATED
+ ):
+ push_warning(
+ "No gatherable spawn anchors were found for %s." % entry.type_id
+ )
+ return
+ var triangles: Array[PackedVector3Array]
+ if not entry.diggable_area_id.is_empty():
+ triangles = _world.get_diggable_area_triangles(entry.diggable_area_id)
+ else:
+ var height_range := _resolved_surface_height_range(entry)
+ triangles = _world.get_spawn_surface_triangles(
+ entry.surface_materials,
+ height_range.x,
+ 0.6,
+ height_range.y,
+ )
+ _surface_height_ranges[entry.type_id] = height_range
+ var areas := PackedFloat32Array()
+ var total_area: float = 0.0
+ for triangle: PackedVector3Array in triangles:
+ var area: float = (
+ (triangle[1] - triangle[0]).cross(
+ triangle[2] - triangle[0]
+ ).length() * 0.5
+ )
+ total_area += area
+ areas.append(total_area)
+ _surface_triangles[entry.type_id] = triangles
+ _surface_areas[entry.type_id] = areas
+ _surface_total_areas[entry.type_id] = total_area
+ if (
+ triangles.is_empty()
+ and _world.get_world_layout() == WorldLayout.GENERATED
+ ):
+ push_warning(
+ "No valid spawn surface was found for %s." % entry.type_id
+ )
+
+
+func _spawn_entity(entry: GatherableDataType) -> void:
+ var position: Vector3 = _sample_surface_position(entry)
+ if not position.is_finite():
+ return
+ var target: Vector3 = (
+ position
+ if entry.is_stationary_spawn()
+ else _sample_surface_position(entry, position, entry.roam_radius)
+ )
+ var quality: int = FishQualityType.roll(_rng)
+ var entity_id: String = _new_id("world")
+ var state: Dictionary = {
+ "entity_id": entity_id,
+ "type_id": entry.type_id,
+ "data": entry,
+ "position": position,
+ "target": target,
+ "yaw": _rng.randf_range(-PI, PI),
+ "quality": quality,
+ "revision": 1,
+ "locked": false,
+ "expires_at": (
+ _now() + entry.active_lifetime_seconds
+ if entry.active_lifetime_seconds > 0.0
+ else INF
+ ),
+ }
+ if not (state["target"] as Vector3).is_finite():
+ state["target"] = position
+ _entities[entity_id] = state
+ var envelope: Dictionary = _make_envelope(
+ &"spawn",
+ {"entity": _state_to_network(state)},
+ )
+ _apply_envelope(envelope)
+ receive_world_envelope.rpc(envelope)
+ _spawn_events_sent += 1
+
+
+func _update_host_entities(delta: float) -> void:
+ for entity_id: String in _entities.keys():
+ var state: Dictionary = _entities.get(entity_id, {})
+ if state.is_empty() or bool(state.get("locked", false)):
+ continue
+ var entry := state.get("data") as GatherableDataType
+ if entry == null:
+ continue
+ if _now() >= float(state.get("expires_at", INF)):
+ _despawn_entity(entity_id, &"expired", false, true)
+ continue
+ if entry.is_stationary_spawn():
+ continue
+ var quality: int = _get_state_quality(state)
+ var position: Vector3 = state["position"]
+ var target: Vector3 = state["target"]
+ var horizontal_delta := Vector3(
+ target.x - position.x,
+ 0.0,
+ target.z - position.z,
+ )
+ if horizontal_delta.length_squared() <= 0.04:
+ target = _sample_surface_position(
+ entry,
+ position,
+ entry.roam_radius,
+ )
+ if not target.is_finite():
+ target = position
+ state["target"] = target
+ horizontal_delta = Vector3(
+ target.x - position.x,
+ 0.0,
+ target.z - position.z,
+ )
+ if not horizontal_delta.is_zero_approx():
+ var step: float = minf(
+ entry.get_movement_speed_for_quality(quality) * delta,
+ horizontal_delta.length(),
+ )
+ var direction: Vector3 = horizontal_delta.normalized()
+ position.x += direction.x * step
+ position.z += direction.z * step
+ position.y = lerpf(
+ position.y,
+ target.y,
+ minf(step / maxf(horizontal_delta.length(), 0.001), 1.0),
+ )
+ state["yaw"] = atan2(-direction.x, -direction.z)
+ state["revision"] = int(state["revision"]) + 1
+ _dirty_entity_ids[entity_id] = true
+ state["position"] = position
+ _entities[entity_id] = state
+ if entry.can_be_scared() and _should_scare(entry, position, quality):
+ _despawn_entity(entity_id, &"scared", true, true)
+
+
+func _should_scare(
+ entry: GatherableDataType,
+ position: Vector3,
+ quality: int,
+) -> bool:
+ var scare_radius: float = entry.get_scare_radius_for_quality(quality)
+ var radius_squared: float = scare_radius * scare_radius
+ var center_cell := _player_spatial_cell(position)
+ var cell_radius: int = ceili(scare_radius / PLAYER_SPATIAL_CELL_SIZE)
+ for cell_x: int in range(
+ center_cell.x - cell_radius,
+ center_cell.x + cell_radius + 1,
+ ):
+ for cell_y: int in range(
+ center_cell.y - cell_radius,
+ center_cell.y + cell_radius + 1,
+ ):
+ for value: Variant in _moving_players_by_cell.get(
+ Vector2i(cell_x, cell_y),
+ [],
+ ):
+ var avatar := value as Player
+ if avatar == null:
+ continue
+ var player_delta := Vector2(
+ avatar.global_position.x - position.x,
+ avatar.global_position.z - position.z,
+ )
+ if player_delta.length_squared() <= radius_squared:
+ return true
+ return false
+
+
+func _rebuild_moving_player_spatial_index() -> void:
+ _moving_players_by_cell.clear()
+ for peer_id: int in _spawn_service.get_peer_ids():
+ var avatar: Player = _spawn_service.get_avatar(peer_id)
+ if (
+ avatar == null
+ or not avatar.is_moving_horizontally()
+ or avatar.is_sneaking()
+ ):
+ continue
+ var cell: Vector2i = _player_spatial_cell(avatar.global_position)
+ var players: Array = _moving_players_by_cell.get(cell, [])
+ players.append(avatar)
+ _moving_players_by_cell[cell] = players
+
+
+static func _player_spatial_cell(position: Vector3) -> Vector2i:
+ return Vector2i(
+ floori(position.x / PLAYER_SPATIAL_CELL_SIZE),
+ floori(position.z / PLAYER_SPATIAL_CELL_SIZE),
+ )
+
+
+func _sample_surface_position(
+ entry: GatherableDataType,
+ origin: Vector3 = Vector3(INF, INF, INF),
+ maximum_distance: float = INF,
+) -> Vector3:
+ _cache_spawn_surface(entry)
+ if not entry.spawn_anchor_set_id.is_empty():
+ return _sample_anchor_position(entry, origin, maximum_distance)
+ var triangles: Array = _surface_triangles.get(entry.type_id, [])
+ var cumulative_areas: PackedFloat32Array = _surface_areas.get(
+ entry.type_id,
+ PackedFloat32Array(),
+ )
+ var total_area: float = float(
+ _surface_total_areas.get(entry.type_id, 0.0)
+ )
+ if triangles.is_empty() or total_area <= 0.0:
+ return Vector3(INF, INF, INF)
+ var validates_diggable_surface := not entry.diggable_area_id.is_empty()
+ var height_range: Vector2 = _surface_height_ranges.get(
+ entry.type_id,
+ Vector2(entry.minimum_surface_y, entry.maximum_surface_y),
+ )
+ var validates_height_range := _surface_height_ranges.has(entry.type_id)
+ var attempts: int = (
+ 48
+ if (
+ validates_diggable_surface
+ or validates_height_range
+ or is_finite(maximum_distance)
+ )
+ else 1
+ )
+ var fallback: Vector3 = Vector3(INF, INF, INF)
+ for _attempt: int in attempts:
+ var roll: float = _rng.randf() * total_area
+ var triangle_index: int = cumulative_areas.bsearch(roll)
+ triangle_index = clampi(triangle_index, 0, triangles.size() - 1)
+ var triangle: PackedVector3Array = triangles[triangle_index]
+ var root: float = sqrt(_rng.randf())
+ var barycentric_b: float = root * (1.0 - _rng.randf())
+ var barycentric_c: float = root - barycentric_b
+ var point: Vector3 = (
+ triangle[0] * (1.0 - root)
+ + triangle[1] * barycentric_b
+ + triangle[2] * barycentric_c
+ )
+ fallback = point
+ var within_requested_distance := (
+ not is_finite(maximum_distance)
+ or Vector2(point.x - origin.x, point.z - origin.z).length()
+ <= maximum_distance
+ )
+ var valid_diggable_surface := (
+ not validates_diggable_surface
+ or _world.is_diggable_surface_point_valid(
+ entry.diggable_area_id,
+ point,
+ )
+ )
+ var valid_height := (
+ not validates_height_range
+ or (
+ point.y > height_range.x
+ and point.y < height_range.y
+ )
+ )
+ if (
+ within_requested_distance
+ and valid_diggable_surface
+ and valid_height
+ ):
+ return point
+ if validates_diggable_surface or validates_height_range:
+ return Vector3(INF, INF, INF)
+ if origin.is_finite() and is_finite(maximum_distance):
+ return origin
+ return fallback
+
+
+func _sample_anchor_position(
+ entry: GatherableDataType,
+ origin: Vector3,
+ maximum_distance: float,
+) -> Vector3:
+ var anchors: PackedVector3Array = _spawn_anchor_positions.get(
+ entry.type_id,
+ PackedVector3Array(),
+ )
+ var candidates := PackedVector3Array()
+ for anchor: Vector3 in anchors:
+ if not anchor.is_finite() or _anchor_is_occupied(entry.type_id, anchor):
+ continue
+ if (
+ origin.is_finite()
+ and is_finite(maximum_distance)
+ and anchor.distance_to(origin) > maximum_distance
+ ):
+ continue
+ candidates.append(anchor)
+ if candidates.is_empty():
+ return Vector3(INF, INF, INF)
+ return candidates[_rng.randi_range(0, candidates.size() - 1)]
+
+
+func _anchor_is_occupied(type_id: StringName, anchor: Vector3) -> bool:
+ for state: Dictionary in _entities.values():
+ if StringName(state.get("type_id", StringName())) != type_id:
+ continue
+ var position: Variant = state.get("position")
+ if (
+ typeof(position) == TYPE_VECTOR3
+ and (position as Vector3).distance_squared_to(anchor) <= 0.0001
+ ):
+ return true
+ return false
+
+
+func _broadcast_entity_snapshots() -> void:
+ if _dirty_entity_ids.is_empty() or not _can_broadcast_snapshots():
+ return
+ var has_remote_recipients: bool = _has_authenticated_remote_peers()
+ var snapshots: Array[Dictionary] = []
+ for entity_id: String in _dirty_entity_ids:
+ var state: Dictionary = _entities.get(entity_id, {})
+ if not state.is_empty():
+ snapshots.append(_state_to_network(state))
+ _dirty_entity_ids.clear()
+ var maximum_chunk_size: int = (
+ NetworkWorldSpawnProtocol.SNAPSHOT_ENTITIES_PER_ENVELOPE
+ )
+ for start_index: int in range(
+ 0,
+ snapshots.size(),
+ maximum_chunk_size,
+ ):
+ var chunk: Array[Dictionary] = []
+ for value: Dictionary in snapshots.slice(
+ start_index,
+ mini(start_index + maximum_chunk_size, snapshots.size()),
+ ):
+ chunk.append(value)
+ var envelope: Dictionary = _make_envelope(
+ &"snapshot",
+ {"entities": chunk},
+ )
+ _apply_envelope(envelope)
+ if not has_remote_recipients:
+ continue
+ # A peer-disconnected callback can resume validation/gameplay code and
+ # close the host while this physics update is still unwinding. Never send
+ # through the replacement OfflineMultiplayerPeer in that narrow window.
+ if not _can_broadcast_snapshots():
+ return
+ receive_world_snapshot_envelope.rpc(envelope)
+ _snapshot_packets_sent += 1
+ _snapshot_states_sent += chunk.size()
+
+
+func _has_authenticated_remote_peers() -> bool:
+ if _session == null:
+ return false
+ var local_peer_id: int = _session.get_local_peer_id()
+ for peer_id: int in _session.get_authenticated_peer_ids():
+ if peer_id != local_peer_id:
+ return true
+ return false
+
+
+func _can_broadcast_snapshots() -> bool:
+ if (
+ _session == null
+ or not _session.is_host()
+ or not _session.is_gameplay_session_active()
+ ):
+ return false
+ var peer: MultiplayerPeer = multiplayer.multiplayer_peer
+ return (
+ peer != null
+ and peer.get_connection_status()
+ == MultiplayerPeer.CONNECTION_CONNECTED
+ )
+
+
+func _despawn_entity(
+ entity_id: String,
+ reason: StringName,
+ with_dust: bool,
+ schedule_respawn: bool,
+) -> void:
+ var state: Dictionary = _entities.get(entity_id, {})
+ if state.is_empty():
+ return
+ var entry := state.get("data") as GatherableDataType
+ _entities.erase(entity_id)
+ _dirty_entity_ids.erase(entity_id)
+ var envelope: Dictionary = _make_envelope(
+ &"despawn",
+ {
+ "entity_id": entity_id,
+ "reason": str(reason),
+ "with_dust": with_dust,
+ },
+ )
+ _apply_envelope(envelope)
+ receive_world_envelope.rpc(envelope)
+ _despawn_events_sent += 1
+ if schedule_respawn and entry != null:
+ _respawns.append({
+ "type_id": entry.type_id,
+ "due": _now() + entry.get_respawn_delay(reason, _rng),
+ })
+
+
+func _update_respawns() -> void:
+ var now: float = _now()
+ var remaining: Array[Dictionary] = []
+ for respawn: Dictionary in _respawns:
+ var type_id := StringName(str(respawn.get("type_id", "")))
+ var next_allowed: float = float(
+ _next_respawn_by_type.get(type_id, 0.0)
+ )
+ if now < maxf(float(respawn.get("due", INF)), next_allowed):
+ remaining.append(respawn)
+ continue
+ var entry: GatherableDataType = _catalog.get_entry(
+ type_id
+ )
+ if entry != null and entry.is_available(_current_season()):
+ _spawn_entity(entry)
+ _next_respawn_by_type[type_id] = (
+ now + entry.minimum_respawn_spacing_seconds
+ )
+ _respawns = remaining
+
+
+@rpc(
+ "any_peer",
+ "call_remote",
+ "reliable",
+ NetworkWorldSpawnProtocol.RELIABLE_CHANNEL,
+)
+func submit_interaction_begin(data: Dictionary) -> void:
+ var peer_id: int = multiplayer.get_remote_sender_id()
+ if _session.is_host() and _session.is_authenticated_peer(peer_id):
+ _handle_interaction_begin(peer_id, data)
+
+
+func _handle_interaction_begin(peer_id: int, data: Dictionary) -> void:
+ var request_id: String = str(data.get("request_id", ""))
+ if (
+ str(data.get("session_id", "")) != _session.get_session_id()
+ or request_id.is_empty()
+ or request_id.length() > MAX_REQUEST_ID_LENGTH
+ or _pending_captures.has(peer_id)
+ ):
+ _send_interaction_result(
+ peer_id, request_id, false, "Cannot use that gathering tool now."
+ )
+ return
+ _charge_requests[peer_id] = {
+ "request_id": request_id,
+ "started": _now(),
+ }
+
+
+@rpc(
+ "any_peer",
+ "call_remote",
+ "reliable",
+ NetworkWorldSpawnProtocol.RELIABLE_CHANNEL,
+)
+func submit_interaction_finish(data: Dictionary) -> void:
+ var peer_id: int = multiplayer.get_remote_sender_id()
+ if _session.is_host() and _session.is_authenticated_peer(peer_id):
+ _handle_interaction_finish(peer_id, data)
+
+
+func _handle_interaction_finish(peer_id: int, data: Dictionary) -> void:
+ var request_id: String = str(data.get("request_id", ""))
+ var entity_id: String = str(data.get("entity_id", ""))
+ var charge: Dictionary = _charge_requests.get(peer_id, {})
+ _charge_requests.erase(peer_id)
+ var target_position: Vector3 = NetworkWorldSpawnProtocol.array_to_vector3(
+ data.get("target_position", [])
+ )
+ var state: Dictionary = _entities.get(entity_id, {})
+ var entry := state.get("data") as GatherableDataType
+ var avatar: Player = _spawn_service.get_avatar(peer_id)
+ var movement_sequence_value: Variant = data.get("movement_sequence", null)
+ var error: String = ""
+ if (
+ str(data.get("session_id", "")) != _session.get_session_id()
+ or request_id.is_empty()
+ or request_id.length() > MAX_REQUEST_ID_LENGTH
+ or str(charge.get("request_id", "")) != request_id
+ or not target_position.is_finite()
+ or typeof(movement_sequence_value) != TYPE_INT
+ or int(movement_sequence_value) < 0
+ or int(movement_sequence_value) > 2147483647
+ ):
+ error = "The catch attempt was invalid."
+ elif state.is_empty() or entry == null or bool(state.get("locked", false)):
+ error = "That gathering spot is no longer there."
+ elif _now() - float(charge.get("started", _now())) + 0.05 < entry.charge_duration:
+ error = "Finish readying the tool first."
+ elif _item_use.get_equipped_item_id(peer_id) != entry.required_tool_id:
+ error = "Equip the correct gathering tool."
+ elif avatar == null:
+ error = "The player is unavailable."
+ else:
+ var movement_state: Dictionary = (
+ avatar.get_lag_compensated_movement_state(
+ int(movement_sequence_value)
+ )
+ )
+ if entry.requires_sneaking and not bool(
+ movement_state.get("sneaking", avatar.is_sneaking())
+ ):
+ error = "Sneak closer before using the tool."
+ if not error.is_empty():
+ _send_interaction_result(peer_id, request_id, false, error)
+ return
+ var entity_position: Vector3 = state["position"]
+ var target_distance: float = entity_position.distance_to(target_position)
+ var compensated_position: Vector3 = movement_state.get(
+ "position",
+ avatar.global_position,
+ )
+ var player_distance: float = Vector2(
+ compensated_position.x - entity_position.x,
+ compensated_position.z - entity_position.z,
+ ).length()
+ if target_distance > entry.capture_radius:
+ error = "The gathering tool missed."
+ elif player_distance > entry.interaction_range:
+ error = "Move closer before using the tool."
+ if not error.is_empty():
+ _send_interaction_result(peer_id, request_id, false, error)
+ return
+ var fish_catch: FishCatch = _create_catch_for_state(entry, state)
+ if fish_catch == null or not fish_catch.is_valid():
+ _send_interaction_result(peer_id, request_id, false, "The catch could not be recorded.")
+ return
+ state["locked"] = true
+ _entities[entity_id] = state
+ var capacity_nonce: String = _new_id("capacity")
+ _pending_captures[peer_id] = {
+ "request_id": request_id,
+ "entity_id": entity_id,
+ "catch": fish_catch.to_network_dict(),
+ "result_id": _new_id("gather_result"),
+ "capacity_nonce": capacity_nonce,
+ "deadline": _now() + CAPACITY_RESPONSE_TIMEOUT_SECONDS,
+ }
+ var probe: Dictionary = {
+ "session_id": _session.get_session_id(),
+ "request_id": request_id,
+ "capacity_nonce": capacity_nonce,
+ "catch_id": str(fish_catch.catch_id),
+ }
+ if peer_id == _session.get_local_peer_id():
+ _handle_local_capacity_probe(probe)
+ else:
+ receive_capacity_probe.rpc_id(peer_id, probe)
+
+
+func _create_catch_for_state(
+ entry: GatherableDataType,
+ state: Dictionary,
+) -> FishCatch:
+ var selector := FishSelectorType.new()
+ selector.begin_roll()
+ var fish_catch: FishCatch = selector.create_catch(entry.catch_data)
+ if fish_catch == null:
+ return null
+ var quality: int = _get_state_quality(state)
+ fish_catch.quality = quality
+ fish_catch.sale_value = FishQualityType.apply_sale_value(
+ entry.catch_data.get_sale_value_for_weight(fish_catch.weight_lb),
+ quality,
+ )
+ return fish_catch
+
+
+func _get_state_quality(state: Dictionary) -> int:
+ var quality: int = int(
+ state.get("quality", FishQualityType.Tier.BORING)
+ )
+ return (
+ quality
+ if FishQualityType.is_valid(quality)
+ else FishQualityType.Tier.BORING
+ )
+
+
+@rpc(
+ "any_peer",
+ "call_remote",
+ "reliable",
+ NetworkWorldSpawnProtocol.RELIABLE_CHANNEL,
+)
+func submit_interaction_cancel(request_id: String) -> void:
+ var peer_id: int = multiplayer.get_remote_sender_id()
+ if _session.is_host() and _session.is_authenticated_peer(peer_id):
+ _handle_interaction_cancel(peer_id, request_id)
+
+
+func _handle_interaction_cancel(peer_id: int, request_id: String) -> void:
+ var charge: Dictionary = _charge_requests.get(peer_id, {})
+ if str(charge.get("request_id", "")) == request_id:
+ _charge_requests.erase(peer_id)
+
+
+@rpc(
+ "authority",
+ "call_remote",
+ "reliable",
+ NetworkWorldSpawnProtocol.RELIABLE_CHANNEL,
+)
+func receive_capacity_probe(data: Dictionary) -> void:
+ _handle_local_capacity_probe(data)
+
+
+func _handle_local_capacity_probe(data: Dictionary) -> void:
+ if str(data.get("session_id", "")) != _session.get_session_id():
+ return
+ var catch_id := StringName(str(data.get("catch_id", "")))
+ var can_accept: bool = (
+ _local_inventory != null
+ and _local_inventory.can_accept_catch(catch_id)
+ )
+ if _session.is_host():
+ _handle_capacity_response(
+ _session.get_local_peer_id(),
+ str(data.get("request_id", "")),
+ str(data.get("capacity_nonce", "")),
+ can_accept,
+ )
+ else:
+ submit_capacity_response.rpc_id(
+ 1,
+ str(data.get("request_id", "")),
+ str(data.get("capacity_nonce", "")),
+ can_accept,
+ )
+
+
+@rpc(
+ "any_peer",
+ "call_remote",
+ "reliable",
+ NetworkWorldSpawnProtocol.RELIABLE_CHANNEL,
+)
+func submit_capacity_response(
+ request_id: String,
+ capacity_nonce: String,
+ can_accept: bool,
+) -> void:
+ var peer_id: int = multiplayer.get_remote_sender_id()
+ if _session.is_host() and _session.is_authenticated_peer(peer_id):
+ _handle_capacity_response(
+ peer_id,
+ request_id,
+ capacity_nonce,
+ can_accept,
+ )
+
+
+func _handle_capacity_response(
+ peer_id: int,
+ request_id: String,
+ capacity_nonce: String,
+ can_accept: bool,
+) -> void:
+ var pending: Dictionary = _pending_captures.get(peer_id, {})
+ if (
+ pending.is_empty()
+ or str(pending.get("request_id", "")) != request_id
+ or str(pending.get("capacity_nonce", "")) != capacity_nonce
+ ):
+ return
+ if not can_accept:
+ _reject_pending_capture(peer_id, "Inventory is full.")
+ return
+ _pending_captures.erase(peer_id)
+ var entity_id: String = str(pending["entity_id"])
+ _despawn_entity(entity_id, &"captured", false, true)
+ var outcome: Dictionary = {
+ "session_id": _session.get_session_id(),
+ "result_id": str(pending["result_id"]),
+ "request_id": request_id,
+ "target_peer_id": peer_id,
+ "catch": (pending["catch"] as Dictionary).duplicate(true),
+ }
+ if peer_id == _session.get_local_peer_id():
+ _apply_capture_result(outcome)
+ else:
+ receive_capture_result.rpc_id(peer_id, outcome)
+ _broadcast_showcase(peer_id, outcome["catch"], true)
+ _showcase_deadlines[peer_id] = _now() + 4.5
+
+
+func _reject_pending_capture(peer_id: int, message: String) -> void:
+ var pending: Dictionary = _pending_captures.get(peer_id, {})
+ _pending_captures.erase(peer_id)
+ if pending.is_empty():
+ return
+ var entity_id: String = str(pending.get("entity_id", ""))
+ var state: Dictionary = _entities.get(entity_id, {})
+ if not state.is_empty():
+ state["locked"] = false
+ _entities[entity_id] = state
+ _send_interaction_result(
+ peer_id,
+ str(pending.get("request_id", "")),
+ false,
+ message,
+ )
+
+
+func _update_pending_capture_timeouts() -> void:
+ var now: float = _now()
+ for peer_id: int in _pending_captures.keys():
+ var pending: Dictionary = _pending_captures[peer_id]
+ if now >= float(pending.get("deadline", INF)):
+ _reject_pending_capture(peer_id, "The catch attempt timed out.")
+
+
+@rpc(
+ "authority",
+ "call_remote",
+ "reliable",
+ NetworkWorldSpawnProtocol.RELIABLE_CHANNEL,
+)
+func receive_capture_result(data: Dictionary) -> void:
+ _apply_capture_result(data)
+
+
+func _apply_capture_result(data: Dictionary) -> void:
+ if (
+ str(data.get("session_id", "")) != _session.get_session_id()
+ or int(data.get("target_peer_id", 0)) != _session.get_local_peer_id()
+ or typeof(data.get("catch")) != TYPE_DICTIONARY
+ ):
+ return
+ var result_id: String = str(data.get("result_id", ""))
+ var request_id: String = str(data.get("request_id", ""))
+ if result_id.is_empty() or request_id.is_empty():
+ return
+ if _received_results.has(result_id):
+ local_interaction_finished.emit(true, "Caught.")
+ return
+ var catch_data: Dictionary = data["catch"]
+ var fish: FishDataType = _fish_catalog.get_fish_by_id(
+ StringName(str(catch_data.get("fish_id", "")))
+ )
+ var fish_catch: FishCatch = FishCatchType.from_network_dict(
+ catch_data,
+ fish,
+ )
+ if fish_catch == null or not fish_catch.is_valid():
+ return
+ var already_owned: bool = _local_inventory.contains_catch_id(
+ fish_catch.catch_id
+ )
+ if (
+ not already_owned
+ and not _local_inventory.can_accept_catch(fish_catch.catch_id)
+ ):
+ return
+ if not already_owned:
+ var experience: int = FishExperienceType.calculate_for_collection(
+ fish_catch,
+ _local_collection,
+ )
+ _local_inventory.add_catch(fish_catch)
+ _local_collection.record_catch(
+ fish_catch.fish_id,
+ fish_catch.quality,
+ )
+ _local_experience.award_experience(experience)
+ if not _save_manager.save_if_dirty():
+ return
+ _received_results[result_id] = true
+ _bound(_received_results)
+ local_interaction_finished.emit(true, "Caught %s." % fish.display_name)
+ local_capture_received.emit(fish_catch)
+
+
+func _send_interaction_result(
+ peer_id: int,
+ request_id: String,
+ accepted: bool,
+ message: String,
+) -> void:
+ var result: Dictionary = {
+ "session_id": _session.get_session_id(),
+ "request_id": request_id if not request_id.is_empty() else "invalid",
+ "target_peer_id": peer_id,
+ "accepted": accepted,
+ "message": message.left(160),
+ }
+ if peer_id == _session.get_local_peer_id():
+ _apply_interaction_result(result)
+ else:
+ receive_interaction_result.rpc_id(peer_id, result)
+
+
+@rpc(
+ "authority",
+ "call_remote",
+ "reliable",
+ NetworkWorldSpawnProtocol.RELIABLE_CHANNEL,
+)
+func receive_interaction_result(data: Dictionary) -> void:
+ _apply_interaction_result(data)
+
+
+func _apply_interaction_result(data: Dictionary) -> void:
+ if (
+ str(data.get("session_id", "")) != _session.get_session_id()
+ or int(data.get("target_peer_id", 0)) != _session.get_local_peer_id()
+ or typeof(data.get("accepted")) != TYPE_BOOL
+ ):
+ return
+ local_interaction_finished.emit(
+ bool(data["accepted"]),
+ str(data.get("message", "")),
+ )
+
+
+func _broadcast_showcase(
+ peer_id: int,
+ catch_data: Dictionary,
+ visible: bool,
+) -> void:
+ var envelope: Dictionary = _make_envelope(
+ &"showcase",
+ {
+ "owner_peer_id": peer_id,
+ "visible": visible,
+ "catch": catch_data.duplicate(true) if visible else {},
+ },
+ )
+ _apply_envelope(envelope)
+ receive_world_envelope.rpc(envelope)
+
+
+func _update_showcase_deadlines() -> void:
+ var now: float = _now()
+ for peer_id: int in _showcase_deadlines.keys():
+ if now < float(_showcase_deadlines[peer_id]):
+ continue
+ _showcase_deadlines.erase(peer_id)
+ _broadcast_showcase(peer_id, {}, false)
+
+
+func _make_envelope(
+ event_id: StringName,
+ payload: Dictionary,
+) -> Dictionary:
+ _envelope_sequence += 1
+ return NetworkWorldSpawnProtocol.make_envelope(
+ _session.get_session_id(),
+ _envelope_sequence,
+ event_id,
+ payload,
+ )
+
+
+@rpc(
+ "authority",
+ "call_remote",
+ "reliable",
+ NetworkWorldSpawnProtocol.RELIABLE_CHANNEL,
+)
+func receive_world_envelope(envelope: Dictionary) -> void:
+ _apply_envelope(envelope)
+
+
+@rpc(
+ "authority",
+ "call_remote",
+ "unreliable_ordered",
+ NetworkWorldSpawnProtocol.SNAPSHOT_CHANNEL,
+)
+func receive_world_snapshot_envelope(envelope: Dictionary) -> void:
+ _apply_envelope(envelope)
+
+
+func _apply_envelope(envelope: Dictionary) -> void:
+ if (
+ not NetworkWorldSpawnProtocol.validate_envelope(envelope)
+ or str(envelope["session_id"]) != _session.get_session_id()
+ ):
+ return
+ var event_id := StringName(str(envelope["event_id"]))
+ var payload: Dictionary = envelope["payload"]
+ match event_id:
+ &"spawn":
+ _apply_entity_state(payload.get("entity", {}), true)
+ &"snapshot":
+ _apply_entity_states(payload.get("entities", []), false, false)
+ &"population":
+ _apply_entity_states(payload.get("entities", []), true, true)
+ &"despawn":
+ _apply_despawn(payload)
+ &"showcase":
+ _apply_showcase(payload)
+
+
+func _apply_entity_states(
+ values: Variant,
+ immediate: bool,
+ reconcile: bool,
+) -> void:
+ if (
+ typeof(values) != TYPE_ARRAY
+ or values.size() > NetworkWorldSpawnProtocol.MAX_ENTITIES_PER_SNAPSHOT
+ ):
+ return
+ var included: Dictionary = {}
+ for value: Variant in values:
+ if not NetworkWorldSpawnProtocol.validate_entity_state(value):
+ continue
+ var state: Dictionary = value
+ included[str(state["entity_id"])] = true
+ _apply_entity_state(state, immediate)
+ if not reconcile:
+ return
+ for entity_id: String in _presentations.keys():
+ if not included.has(entity_id):
+ _remove_presentation(entity_id, false)
+
+
+func _apply_entity_state(value: Variant, immediate: bool) -> void:
+ if not NetworkWorldSpawnProtocol.validate_entity_state(value):
+ return
+ var state: Dictionary = value
+ var entity_id: String = str(state["entity_id"])
+ var revision: int = int(state["revision"])
+ if revision < int(_entity_revisions.get(entity_id, -1)):
+ return
+ var entry: GatherableDataType = _catalog.get_entry(
+ StringName(str(state["type_id"]))
+ )
+ if entry == null:
+ return
+ var position: Vector3 = NetworkWorldSpawnProtocol.array_to_vector3(
+ state["position"]
+ )
+ if not _presentation_enabled:
+ _entity_revisions[entity_id] = revision
+ return
+ var presentation := _presentations.get(entity_id) as WorldGatherableType
+ if presentation == null or not is_instance_valid(presentation):
+ presentation = WorldGatherableType.new()
+ presentation.name = "WorldGatherable_%s" % entity_id.right(12)
+ _world_root.add_child(presentation)
+ presentation.configure(
+ entity_id,
+ entry,
+ position,
+ float(state["yaw"]),
+ )
+ _presentations[entity_id] = presentation
+ else:
+ presentation.apply_network_state(
+ position,
+ float(state["yaw"]),
+ immediate,
+ )
+ _entity_revisions[entity_id] = revision
+ if not _session.is_host():
+ _entities[entity_id] = {
+ "entity_id": entity_id,
+ "type_id": entry.type_id,
+ "data": entry,
+ "position": position,
+ "yaw": float(state["yaw"]),
+ "revision": revision,
+ "locked": false,
+ }
+
+
+func _apply_despawn(payload: Dictionary) -> void:
+ var entity_id: String = str(payload.get("entity_id", ""))
+ if entity_id.is_empty():
+ return
+ if not _session.is_host():
+ _entities.erase(entity_id)
+ _remove_presentation(entity_id, bool(payload.get("with_dust", false)))
+
+
+func _remove_presentation(entity_id: String, with_dust: bool) -> void:
+ var presentation := _presentations.get(entity_id) as WorldGatherableType
+ _presentations.erase(entity_id)
+ _entity_revisions.erase(entity_id)
+ if presentation != null and is_instance_valid(presentation):
+ presentation.play_despawn(with_dust)
+
+
+func _apply_showcase(payload: Dictionary) -> void:
+ if not _presentation_enabled:
+ return
+ if (
+ typeof(payload.get("owner_peer_id")) != TYPE_INT
+ or typeof(payload.get("visible")) != TYPE_BOOL
+ ):
+ return
+ var peer_id: int = int(payload["owner_peer_id"])
+ if peer_id == _session.get_local_peer_id():
+ return
+ var avatar: Player = _spawn_service.get_avatar(peer_id)
+ if avatar == null:
+ return
+ if not bool(payload["visible"]):
+ avatar.end_catch_showcase()
+ return
+ if typeof(payload.get("catch")) != TYPE_DICTIONARY:
+ return
+ var catch_data: Dictionary = payload["catch"]
+ var fish: FishDataType = _fish_catalog.get_fish_by_id(
+ StringName(str(catch_data.get("fish_id", "")))
+ )
+ var fish_catch: FishCatch = FishCatchType.from_network_dict(
+ catch_data,
+ fish,
+ )
+ if fish_catch != null:
+ avatar.begin_remote_catch_showcase(fish_catch)
+
+
+func _state_to_network(state: Dictionary) -> Dictionary:
+ return {
+ "entity_id": str(state["entity_id"]),
+ "type_id": str(state["type_id"]),
+ "position": NetworkWorldSpawnProtocol.vector3_to_array(
+ state["position"]
+ ),
+ "yaw": float(state["yaw"]),
+ "revision": int(state["revision"]),
+ }
+
+
+func _on_peer_authenticated(peer_id: int, _display_name: String) -> void:
+ if not _session.is_host():
+ return
+ var states: Array[Dictionary] = []
+ for state: Dictionary in _entities.values():
+ states.append(_state_to_network(state))
+ var envelope: Dictionary = _make_envelope(
+ &"population",
+ {"entities": states},
+ )
+ receive_world_envelope.rpc_id(peer_id, envelope)
+
+
+func _on_peer_removed(peer_id: int) -> void:
+ _charge_requests.erase(peer_id)
+ if _pending_captures.has(peer_id):
+ _unlock_pending_capture(peer_id)
+ _showcase_deadlines.erase(peer_id)
+
+
+func _unlock_pending_capture(peer_id: int) -> void:
+ var pending: Dictionary = _pending_captures.get(peer_id, {})
+ _pending_captures.erase(peer_id)
+ if pending.is_empty():
+ return
+ var entity_id: String = str(pending.get("entity_id", ""))
+ var state: Dictionary = _entities.get(entity_id, {})
+ if state.is_empty():
+ return
+ state["locked"] = false
+ _entities[entity_id] = state
+
+
+func _on_session_state_changed(_state: NetworkSession.State) -> void:
+ if _session.is_gameplay_session_active():
+ _begin_population_if_ready.call_deferred()
+ return
+ _clear_world()
+ _population_session_id = ""
+
+
+func _on_calendar_date_changed(_date_id: String) -> void:
+ if (
+ _session == null
+ or not _session.is_host()
+ or not _session.is_gameplay_session_active()
+ or _population_session_id.is_empty()
+ ):
+ return
+ _reconcile_seasonal_population()
+
+
+func _reconcile_seasonal_population() -> void:
+ if _catalog == null:
+ return
+ var season: int = _current_season()
+ for entity_id: String in _entities.keys():
+ var state: Dictionary = _entities.get(entity_id, {})
+ var entry := state.get("data") as GatherableDataType
+ if entry != null and not entry.is_available(season):
+ _despawn_entity(entity_id, &"season_changed", false, false)
+ var retained_respawns: Array[Dictionary] = []
+ for respawn: Dictionary in _respawns:
+ var entry: GatherableDataType = _catalog.get_entry(
+ StringName(str(respawn.get("type_id", "")))
+ )
+ if entry != null and entry.is_available(season):
+ retained_respawns.append(respawn)
+ _respawns = retained_respawns
+ for entry: GatherableDataType in _catalog.get_available_entries(season):
+ _cache_spawn_surface(entry)
+ var current_population: int = _population_for_type(entry.type_id)
+ var target_population := _target_population(entry)
+ while current_population < target_population:
+ _spawn_entity(entry)
+ current_population += 1
+
+
+func _target_population(entry: GatherableDataType) -> int:
+ if entry == null:
+ return 0
+ if entry.spawn_anchor_set_id.is_empty():
+ return entry.target_population_for_surface_area(
+ float(_surface_total_areas.get(entry.type_id, 0.0))
+ )
+ var anchors: PackedVector3Array = _spawn_anchor_positions.get(
+ entry.type_id,
+ PackedVector3Array(),
+ )
+ return entry.target_population_for_anchor_count(anchors.size())
+
+
+func _resolved_surface_height_range(entry: GatherableDataType) -> Vector2:
+ var minimum_y := entry.minimum_surface_y
+ var maximum_y := entry.maximum_surface_y
+ var saltwater_height := _world.get_saltwater_surface_height()
+ if is_finite(saltwater_height):
+ match entry.surface_water_relation:
+ GatherableDataType.SurfaceWaterRelation.ABOVE_SALT_WATER:
+ minimum_y = maxf(
+ minimum_y,
+ saltwater_height + entry.waterline_margin,
+ )
+ GatherableDataType.SurfaceWaterRelation.BELOW_SALT_WATER:
+ maximum_y = minf(
+ maximum_y,
+ saltwater_height - entry.waterline_margin,
+ )
+ return Vector2(minimum_y, maximum_y)
+
+
+func _population_for_type(type_id: StringName) -> int:
+ var count: int = 0
+ for state: Dictionary in _entities.values():
+ if StringName(str(state.get("type_id", ""))) == type_id:
+ count += 1
+ for respawn: Dictionary in _respawns:
+ if StringName(str(respawn.get("type_id", ""))) == type_id:
+ count += 1
+ return count
+
+
+func _current_season() -> int:
+ return (
+ _world_time.get_season()
+ if _world_time != null
+ else CalendarSeasonType.UNKNOWN
+ )
+
+
+func _clear_world() -> void:
+ for presentation: WorldGatherableType in _presentations.values():
+ if presentation != null and is_instance_valid(presentation):
+ presentation.queue_free()
+ _entities.clear()
+ _dirty_entity_ids.clear()
+ _moving_players_by_cell.clear()
+ _presentations.clear()
+ _entity_revisions.clear()
+ _surface_triangles.clear()
+ _surface_areas.clear()
+ _surface_total_areas.clear()
+ _surface_height_ranges.clear()
+ _spawn_anchor_positions.clear()
+ _respawns.clear()
+ _next_respawn_by_type.clear()
+ _charge_requests.clear()
+ _pending_captures.clear()
+ _showcase_deadlines.clear()
+ _simulation_elapsed = 0.0
+ _snapshot_elapsed = 0.0
+
+
+func get_network_metrics() -> Dictionary:
+ return {
+ "entities": _entities.size(),
+ "presentations": _presentations.size(),
+ "dirty_entities": _dirty_entity_ids.size(),
+ "spawn_events_sent": _spawn_events_sent,
+ "despawn_events_sent": _despawn_events_sent,
+ "snapshot_packets_sent": _snapshot_packets_sent,
+ "snapshot_states_sent": _snapshot_states_sent,
+ }
+
+
+func _bound(values: Dictionary) -> void:
+ while values.size() > MAX_LEDGER_ENTRIES:
+ values.erase(values.keys().front())
+
+
+func _new_id(prefix: String) -> String:
+ return "%s:%s" % [
+ prefix,
+ Crypto.new().generate_random_bytes(16).hex_encode(),
+ ]
+
+
+func _now() -> float:
+ return Time.get_ticks_msec() / 1000.0
diff --git a/network/network_world_spawn_service.gd.uid b/network/network_world_spawn_service.gd.uid
new file mode 100644
index 0000000..e9badc5
--- /dev/null
+++ b/network/network_world_spawn_service.gd.uid
@@ -0,0 +1 @@
+uid://bmuhkkl2cxsck
diff --git a/network/network_world_time_service.gd b/network/network_world_time_service.gd
index 246af77..9cbd990 100644
--- a/network/network_world_time_service.gd
+++ b/network/network_world_time_service.gd
@@ -23,6 +23,12 @@ func setup(session: NetworkSession, world_time: WorldTimeService) -> void:
_world_time.authoritative_time_set.connect(
_on_authoritative_time_set
)
+ if not _world_time.calendar_date_changed.is_connected(
+ _on_authoritative_calendar_date_changed
+ ):
+ _world_time.calendar_date_changed.connect(
+ _on_authoritative_calendar_date_changed
+ )
set_process(true)
@@ -45,20 +51,16 @@ func _on_session_state_changed(state: NetworkSession.State) -> void:
_sequence = 0
_last_received_sequence = -1
_sync_elapsed = 0.0
- _world_time.set_persistence_tracking_enabled(true)
- _world_time.begin_session(
- _world_time.get_persistent_time_hours()
- )
+ _world_time.begin_authoritative_session()
return
if state == NetworkSession.State.JOINED_CLIENT:
_active_session_id = _session.get_session_id()
_last_received_sequence = -1
_sync_elapsed = 0.0
- _world_time.set_persistence_tracking_enabled(false)
if _session.supports_server_capability(
NetworkProtocol.WORLD_TIME_CAPABILITY
):
- _world_time.begin_session()
+ _world_time.begin_remote_session()
else:
_world_time.end_session()
return
@@ -72,7 +74,6 @@ func _on_session_state_changed(state: NetworkSession.State) -> void:
_sequence = 0
_last_received_sequence = -1
_sync_elapsed = 0.0
- _world_time.set_persistence_tracking_enabled(false)
_world_time.end_session()
@@ -95,6 +96,13 @@ func _on_authoritative_time_set(_time_hours: float) -> void:
_broadcast_snapshot()
+func _on_authoritative_calendar_date_changed(_date_id: String) -> 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
@@ -112,6 +120,7 @@ func _send_snapshot(peer_id: int) -> void:
receive_world_time_snapshot.rpc_id(peer_id, {
"session_id": _session.get_session_id(),
"time_hours": _world_time.get_time_hours(),
+ "calendar_date_id": _world_time.get_calendar_date_id(),
"sequence": _sequence,
})
@@ -133,7 +142,10 @@ func receive_world_time_snapshot(data: Dictionary) -> void:
if sequence <= _last_received_sequence:
return
_last_received_sequence = sequence
- _world_time.synchronize_time(float(data["time_hours"]))
+ _world_time.synchronize_calendar_time(
+ float(data["time_hours"]),
+ str(data["calendar_date_id"]),
+ )
static func validate_snapshot(data: Variant) -> bool:
@@ -148,6 +160,10 @@ static func validate_snapshot(data: Variant) -> bool:
and is_finite(float(value["time_hours"]))
and float(value["time_hours"]) >= 0.0
and float(value["time_hours"]) < WorldTimeService.HOURS_PER_DAY
+ and typeof(value.get("calendar_date_id")) == TYPE_STRING
+ and WorldTimeService.is_valid_calendar_date_id(
+ str(value["calendar_date_id"])
+ )
and typeof(value.get("sequence")) == TYPE_INT
and int(value["sequence"]) >= 0
)
diff --git a/network/network_world_weather_service.gd b/network/network_world_weather_service.gd
index 43d5762..ba0e6e2 100644
--- a/network/network_world_weather_service.gd
+++ b/network/network_world_weather_service.gd
@@ -3,7 +3,7 @@ extends Node
const SYNC_INTERVAL_SECONDS: float = 15.0
const MAX_SESSION_ID_LENGTH: int = 96
-const MAX_REMAINING_SECONDS: float = 1800.0
+const MAX_REMAINING_SECONDS: float = WorldWeatherService.WEATHER_PERIOD_SECONDS
var _session: NetworkSession
var _world_weather: WorldWeatherService
@@ -44,7 +44,6 @@ 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()
)
@@ -53,7 +52,6 @@ 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
):
@@ -71,7 +69,6 @@ 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 525c545..e173577 100644
--- a/network/player_data_root.gd
+++ b/network/player_data_root.gd
@@ -9,11 +9,42 @@ const BOOTSTRAP_TEMP_PATH := "user://data_root_bootstrap.json.tmp"
const BOOTSTRAP_VERSION := 1
const MANIFEST_VERSION := 1
const LAYOUT_VERSION := 1
-const MANIFEST_FILENAME := "netfishing_data.json"
+const MANIFEST_FILENAME := "straywild_data.json"
+const LEGACY_MANIFEST_FILENAME := "netfishing_data.json"
const README_FILENAME := "README.txt"
-const ENVIRONMENT_VARIABLE := "NETFISHING_DATA_DIR"
-const APPLICATION_ID := "netfishing"
+const ENVIRONMENT_VARIABLE := "straywild_DATA_DIR"
+const CREATE_ENVIRONMENT_VARIABLE := "straywild_CREATE_DATA_DIR"
+const LEGACY_ENVIRONMENT_VARIABLE := "NETFISHING_DATA_DIR"
+const LEGACY_CREATE_ENVIRONMENT_VARIABLE := "NETFISHING_CREATE_DATA_DIR"
+const APPLICATION_ID := "straywild"
+const LEGACY_APPLICATION_ID := "netfishing"
const APP_DATA_PORTABLE_PATH := "user://portable-data"
+const LEGACY_USER_DIRECTORY_NAME := "NETFISHING"
+const LEGACY_USER_FILES := [
+ "data_root_bootstrap.json",
+ "data_root_bootstrap.json.backup",
+ "network_discovery.cfg",
+ "player_settings.json",
+ "player_settings.json.backup",
+ "controller_mappings.json",
+ "controller_mappings.json.backup",
+ "keyboard_mouse_bindings.json",
+ "keyboard_mouse_bindings.json.backup",
+ "player_save.json",
+ "network_profile.json",
+ "player_appearance.json",
+ "saved_servers.json",
+ "known_players.json",
+ "player_relationships.json",
+ "server_trust.json",
+ "host_bans.json",
+ "player_identity.key",
+ "player_identity.pub",
+ "player_identity.json",
+ "host_identity.key",
+ "host_identity.pub",
+ "host_identity.json",
+]
const PATH_ALLOWED: StringName = &"allowed"
const PATH_SOURCE_PROJECT: StringName = &"source_project"
const PATH_INSTALLATION: StringName = &"installation"
@@ -36,18 +67,33 @@ var override_active := false
func resolve() -> bool:
+ prepare_legacy_user_data()
_load_bootstrap_identity()
var command_line: String = _command_line_override()
if not command_line.is_empty():
override_active = true
mode = Mode.COMMAND_LINE_OVERRIDE
return _activate_existing(command_line, "", false)
- if OS.has_environment(ENVIRONMENT_VARIABLE):
+ var environment_variable: String = _available_environment_variable(
+ ENVIRONMENT_VARIABLE,
+ LEGACY_ENVIRONMENT_VARIABLE,
+ )
+ if not environment_variable.is_empty():
override_active = true
mode = Mode.ENVIRONMENT_OVERRIDE
- var environment_path: String = OS.get_environment(ENVIRONMENT_VARIABLE)
+ var environment_path: String = OS.get_environment(environment_variable)
if not environment_path.is_absolute_path():
- return _fail("NETFISHING_DATA_DIR must be an absolute path.")
+ return _fail("straywild_DATA_DIR must be an absolute path.")
+ if (
+ _environment_flag(
+ CREATE_ENVIRONMENT_VARIABLE,
+ LEGACY_CREATE_ENVIRONMENT_VARIABLE,
+ )
+ and not has_data_manifest(environment_path)
+ ):
+ var created: Dictionary = create_unbound_root(environment_path)
+ if not bool(created.get("ok", false)):
+ return false
return _activate_existing(environment_path, "", false)
var bootstrap: Dictionary = _read_json(BOOTSTRAP_PATH, 64 * 1024)
if bootstrap.is_empty() and FileAccess.file_exists(BOOTSTRAP_PATH + ".backup"):
@@ -70,11 +116,35 @@ func resolve() -> bool:
return false
+func prepare_legacy_user_data() -> void:
+ var current_root: String = _normalize(
+ ProjectSettings.globalize_path("user://")
+ )
+ if current_root.is_empty():
+ return
+ var legacy_root: String = _normalize(
+ current_root.get_base_dir().path_join(LEGACY_USER_DIRECTORY_NAME)
+ )
+ if legacy_root == current_root or not DirAccess.dir_exists_absolute(legacy_root):
+ return
+ for filename: String in LEGACY_USER_FILES:
+ var destination: String = current_root.path_join(filename)
+ if FileAccess.file_exists(destination):
+ continue
+ var source: String = legacy_root.path_join(filename)
+ if FileAccess.file_exists(source):
+ _copy_legacy_user_file(source, destination)
+
+
+func has_data_manifest(path: String) -> bool:
+ return not _existing_manifest_path(_normalize(path)).is_empty()
+
+
func default_visible_path() -> String:
var documents: String = OS.get_system_dir(OS.SYSTEM_DIR_DOCUMENTS)
if documents.is_empty():
return ""
- var candidate: String = _normalize(documents.path_join("NETFISHING"))
+ var candidate: String = _normalize(documents.path_join("straywild"))
if not candidate.is_absolute_path():
return ""
var classification: StringName = classify_candidate_path(
@@ -95,20 +165,22 @@ func select_new_root(path: String, app_data: bool = false) -> bool:
normalized = ProjectSettings.globalize_path(APP_DATA_PORTABLE_PATH)
if not _validate_candidate(normalized, true):
return false
- var manifest_path: String = normalized.path_join(MANIFEST_FILENAME)
- if FileAccess.file_exists(manifest_path):
+ var manifest_path: String = _existing_manifest_path(normalized)
+ if not manifest_path.is_empty():
var manifest: Dictionary = _read_json(manifest_path)
if not _valid_manifest(manifest):
- return _fail("The selected folder has a malformed NETfishing manifest.")
+ return _fail("The selected folder has a malformed straywild manifest.")
root_id = str(manifest["root_id"])
+ if not _upgrade_legacy_manifest(normalized, manifest, manifest_path):
+ return _fail("The legacy data-folder manifest could not be upgraded.")
else:
if not _directory_is_empty(normalized) and not app_data:
return _fail(
- "Choose an empty folder or an existing NETfishing data folder."
+ "Choose an empty folder or an existing straywild data folder."
)
root_id = Crypto.new().generate_random_bytes(16).hex_encode()
if not _create_layout(normalized, root_id):
- return _fail("The NETfishing data folder could not be created.")
+ return _fail("The straywild data folder could not be created.")
if not _write_bootstrap(normalized, root_id):
return _fail("The data-folder pointer could not be saved.")
root_path = normalized
@@ -135,14 +207,14 @@ func create_app_data_layout_for_migration(path: String) -> Dictionary:
var normalized: String = _normalize(path)
if not _validate_candidate(normalized, false):
return {"ok": false, "message": error_message}
- var manifest_path: String = normalized.path_join(MANIFEST_FILENAME)
- if FileAccess.file_exists(manifest_path):
+ var manifest_path: String = _existing_manifest_path(normalized)
+ if not manifest_path.is_empty():
var manifest: Dictionary = _read_json(manifest_path)
- return (
- {"ok": true, "root_id": str(manifest.get("root_id", ""))}
- if _valid_manifest(manifest)
- else {"ok": false, "message": "The app-data manifest is malformed."}
- )
+ if not _valid_manifest(manifest):
+ return {"ok": false, "message": "The app-data manifest is malformed."}
+ if not _upgrade_legacy_manifest(normalized, manifest, manifest_path):
+ return {"ok": false, "message": "The app-data manifest could not be upgraded."}
+ return {"ok": true, "root_id": str(manifest.get("root_id", ""))}
var id: String = Crypto.new().generate_random_bytes(16).hex_encode()
if not _create_layout(normalized, id):
return {"ok": false, "message": "The app-data layout could not be created."}
@@ -178,7 +250,8 @@ func activate_process_root(path: String) -> bool:
func path_for(store_owner: StringName) -> String:
var relative: String = {
- &"player_save": "player/player_save.json",
+ &"player_save": "player/player_save.nfsave",
+ &"save_slots": "player/save_slots.json",
&"network_profile": "player/network_profile.json",
&"player_appearance": "player/player_appearance.json",
&"saved_servers": "social/saved_servers.json",
@@ -198,6 +271,10 @@ func identity_backup_directory() -> String:
return root_path.path_join("identity-backups")
+func progression_backup_directory() -> String:
+ return root_path.path_join("progression-backups")
+
+
func migration_backup_directory() -> String:
return root_path.path_join("backups/migrations")
@@ -211,10 +288,6 @@ func storage_mode_text() -> String:
}.get(mode, "Unavailable")
-func open_folder() -> bool:
- return not root_path.is_empty() and OS.shell_open(root_path) == OK
-
-
func report_conflict(message: String, conflict_path: String) -> void:
error_message = message
conflict_detected.emit(message, conflict_path)
@@ -225,17 +298,19 @@ func _activate_existing(path: String, expected_id: String, permit_creation: bool
var normalized: String = _normalize(path)
if not _validate_candidate(normalized, permit_creation):
return false
- var manifest_path: String = normalized.path_join(MANIFEST_FILENAME)
- if not FileAccess.file_exists(manifest_path):
- return _fail("The selected folder is not a NETfishing data folder.")
+ var manifest_path: String = _existing_manifest_path(normalized)
+ if manifest_path.is_empty():
+ return _fail("The selected folder is not a straywild data folder.")
var manifest: Dictionary = _read_json(manifest_path)
if not _valid_manifest(manifest):
- return _fail("The NETfishing data-folder manifest is malformed.")
+ return _fail("The straywild data-folder manifest is malformed.")
+ if not _upgrade_legacy_manifest(normalized, manifest, manifest_path):
+ return _fail("The legacy data-folder manifest could not be upgraded.")
var found_id: String = str(manifest["root_id"])
if not expected_id.is_empty() and expected_id != found_id:
return _fail("The selected data folder does not match this device pointer.")
if not _test_writable(normalized):
- return _fail("The NETfishing data folder is unavailable or unwritable.")
+ return _fail("The straywild data folder is unavailable or unwritable.")
root_path = normalized
root_id = found_id
requires_selection = false
@@ -328,7 +403,7 @@ static func _is_same_or_child_path(candidate: String, reference: String) -> bool
func _test_writable(path: String) -> bool:
var probe: String = path.path_join(
- ".netfishing-write-%s.tmp" % device_id.left(12)
+ ".straywild-write-%s.tmp" % device_id.left(12)
)
var file: FileAccess = FileAccess.open(probe, FileAccess.WRITE)
if file == null:
@@ -340,8 +415,8 @@ func _test_writable(path: String) -> bool:
func _create_layout(path: String, id: String) -> bool:
for relative: String in [
- "player", "social", "backups/saves", "backups/migrations",
- "backups/conflicts", "identity-backups",
+ "player", "player/saves", "social", "backups/saves", "backups/migrations",
+ "backups/conflicts", "identity-backups", "progression-backups",
]:
if DirAccess.make_dir_recursive_absolute(path.path_join(relative)) != OK:
return false
@@ -359,10 +434,11 @@ func _create_layout(path: String, id: String) -> bool:
):
return false
var readme: String = (
- "NETfishing player data\n\n"
+ "straywild player data\n\n"
+ "This folder is safe to synchronize with tools such as Syncthing.\n"
+ "Active private identity keys remain device-local.\n"
+ "Encrypted identity backups require their passphrase.\n"
+ + "Progression exports do not contain identity keys or social data.\n"
+ "Chat and Session Mail are not stored here.\n"
+ "Do not play the same profile on two devices at the same time.\n"
+ "Conflicting edits are preserved under backups/conflicts; they are not merged.\n"
@@ -410,12 +486,71 @@ func _valid_manifest(data: Dictionary) -> bool:
return (
data.get("format_version") == MANIFEST_VERSION
and data.get("layout_version") == LAYOUT_VERSION
- and data.get("application") == APPLICATION_ID
+ and data.get("application") in [APPLICATION_ID, LEGACY_APPLICATION_ID]
and typeof(data.get("root_id")) == TYPE_STRING
and str(data["root_id"]).length() == 32
)
+func _existing_manifest_path(path: String) -> String:
+ var current: String = path.path_join(MANIFEST_FILENAME)
+ if FileAccess.file_exists(current):
+ return current
+ var legacy: String = path.path_join(LEGACY_MANIFEST_FILENAME)
+ return legacy if FileAccess.file_exists(legacy) else ""
+
+
+func _upgrade_legacy_manifest(
+ path: String,
+ manifest: Dictionary,
+ manifest_path: String,
+) -> bool:
+ if (
+ manifest_path.get_file() == MANIFEST_FILENAME
+ and manifest.get("application") == APPLICATION_ID
+ ):
+ return true
+ var upgraded: Dictionary = manifest.duplicate(true)
+ upgraded["application"] = APPLICATION_ID
+ upgraded["last_opened_at_unix"] = int(Time.get_unix_time_from_system())
+ return _write_text_atomic(
+ path.path_join(MANIFEST_FILENAME),
+ JSON.stringify(upgraded, "\t"),
+ )
+
+
+func _available_environment_variable(primary: String, legacy: String) -> String:
+ if OS.has_environment(primary):
+ return primary
+ return legacy if OS.has_environment(legacy) else ""
+
+
+func _environment_flag(primary: String, legacy: String) -> bool:
+ var variable: String = _available_environment_variable(primary, legacy)
+ return not variable.is_empty() and OS.get_environment(variable) == "1"
+
+
+func _copy_legacy_user_file(source: String, destination: String) -> bool:
+ var bytes: PackedByteArray = PortableFileGuard.read_bytes(
+ source,
+ 16 * 1024 * 1024,
+ )
+ if bytes.is_empty() and FileAccess.get_open_error() != OK:
+ return false
+ if DirAccess.make_dir_recursive_absolute(destination.get_base_dir()) != OK:
+ return false
+ var file: FileAccess = FileAccess.open(destination, FileAccess.WRITE)
+ if file == null:
+ return false
+ file.store_buffer(bytes)
+ file.flush()
+ var copied: bool = file.get_error() == OK
+ file.close()
+ if copied and source.ends_with(".key"):
+ FileAccess.set_unix_permissions(destination, 384)
+ return copied
+
+
func _directory_is_empty(path: String) -> bool:
var access: DirAccess = DirAccess.open(path)
if access == null:
diff --git a/network/player_list_entry.gd b/network/player_list_entry.gd
index fbe0d8f..c75832a 100644
--- a/network/player_list_entry.gd
+++ b/network/player_list_entry.gd
@@ -6,11 +6,16 @@ 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
var muted := false
var blocked := false
+var is_friend := false
+var can_request_friend := false
var can_kick := false
var can_ban := false
+var can_clear_art := false
+var can_manage_operator := false
var revision := 0
diff --git a/network/player_relationship_store.gd b/network/player_relationship_store.gd
index 2ea4502..de04d49 100644
--- a/network/player_relationship_store.gd
+++ b/network/player_relationship_store.gd
@@ -3,8 +3,12 @@ extends Node
signal relationship_changed(fingerprint: String)
-const FORMAT_VERSION := 1
+const FORMAT_VERSION := 2
const MAX_RECORDS := 500
+const MAX_FRIENDS := 200
+const SOCIAL_TOKEN_LENGTH := 64
+const PRESENCE_CHANNEL_DOMAIN := "straywild_PRESENCE_V1:"
+const INVITE_INBOX_DOMAIN := "straywild_INVITE_V1:"
var _records: Dictionary = {}
var _loaded := false
@@ -17,6 +21,10 @@ var _data_root: PlayerDataRoot
func configure_storage(path: String, data_root: PlayerDataRoot) -> void:
_store_path = path
_data_root = data_root
+ _records.clear()
+ _loaded = false
+ _write_blocked = false
+ _expected_hash = ""
func is_muted(fingerprint: String) -> bool:
@@ -29,6 +37,11 @@ func is_blocked(fingerprint: String) -> bool:
return bool(_records.get(fingerprint, {}).get("blocked", false))
+func is_friend(fingerprint: String) -> bool:
+ _ensure_loaded()
+ return bool(_records.get(fingerprint, {}).get("friend", false))
+
+
func set_muted(fingerprint: String, display_name: String, value: bool) -> bool:
if not _valid_target(fingerprint, display_name):
return false
@@ -48,20 +61,101 @@ func set_blocked(fingerprint: String, display_name: String, value: bool) -> bool
# fresh visibility boundary for future messages; already suppressed
# messages retain their immutable local suppression flag.
record["muted"] = value
+ if value:
+ _clear_friend_fields(record)
return _commit(fingerprint, record)
+func create_friend_capabilities() -> Dictionary:
+ var presence_write_token := NetworkIdentityCrypto.secure_id(32)
+ var invite_token := NetworkIdentityCrypto.secure_id(32)
+ if not _valid_social_token(presence_write_token) or not _valid_social_token(invite_token):
+ return {}
+ return {
+ "local_presence_write_token": presence_write_token,
+ "local_invite_token": invite_token,
+ "presence_channel": presence_channel_for_write_token(
+ presence_write_token
+ ),
+ "invite_token": invite_token,
+ }
+
+
+func add_friend(
+ fingerprint: String,
+ display_name: String,
+ local_capabilities: Dictionary,
+ remote_capabilities: Dictionary,
+) -> bool:
+ if (
+ not _valid_target(fingerprint, display_name)
+ or is_blocked(fingerprint)
+ or not _valid_local_capabilities(local_capabilities)
+ or not _valid_public_capabilities(remote_capabilities)
+ ):
+ return false
+ _ensure_loaded()
+ if not is_friend(fingerprint) and get_friends().size() >= MAX_FRIENDS:
+ return false
+ var record := _record(fingerprint, display_name)
+ var now := int(Time.get_unix_time_from_system())
+ record["friend"] = true
+ record["friend_since_unix"] = int(record.get("friend_since_unix", now))
+ record["local_presence_write_token"] = str(
+ local_capabilities["local_presence_write_token"]
+ )
+ record["local_invite_token"] = str(
+ local_capabilities["local_invite_token"]
+ )
+ record["remote_presence_channel"] = str(
+ remote_capabilities["presence_channel"]
+ )
+ record["remote_invite_token"] = str(
+ remote_capabilities["invite_token"]
+ )
+ return _commit(fingerprint, record)
+
+
+func remove_friend(fingerprint: String, display_name: String) -> bool:
+ if not _valid_target(fingerprint, display_name):
+ return false
+ _ensure_loaded()
+ var record := _record(fingerprint, display_name)
+ _clear_friend_fields(record)
+ return _commit(fingerprint, record)
+
+
+func get_friend_record(fingerprint: String) -> Dictionary:
+ _ensure_loaded()
+ var record: Dictionary = _records.get(fingerprint, {})
+ return record.duplicate(true) if bool(record.get("friend", false)) else {}
+
+
+func get_friends() -> Array[Dictionary]:
+ _ensure_loaded()
+ var result: Array[Dictionary] = []
+ for value: Dictionary in _records.values():
+ if bool(value.get("friend", false)):
+ result.append(value.duplicate(true))
+ _sort_records(result)
+ return result
+
+
+static func presence_channel_for_write_token(write_token: String) -> String:
+ return (PRESENCE_CHANNEL_DOMAIN + write_token).sha256_text()
+
+
+static func invite_inbox_id(invite_token: String) -> String:
+ return (INVITE_INBOX_DOMAIN + invite_token).sha256_text()
+
+
func get_records() -> Array[Dictionary]:
_ensure_loaded()
var result: Array[Dictionary] = []
for value: Dictionary in _records.values():
if bool(value.get("muted", false)) or bool(value.get("blocked", false)):
result.append(value.duplicate(true))
- result.sort_custom(func(a: Dictionary, b: Dictionary) -> bool:
- return str(a.get("last_known_display_name", "")).naturalnocasecmp_to(
- str(b.get("last_known_display_name", ""))
- ) < 0
- )
+ _sort_records(result)
return result
@@ -72,6 +166,7 @@ func _record(fingerprint: String, display_name: String) -> Dictionary:
"created_unix": now,
"muted": false,
"blocked": false,
+ "friend": false,
})
record["last_known_display_name"] = display_name
record["updated_unix"] = now
@@ -82,7 +177,11 @@ func _commit(fingerprint: String, record: Dictionary) -> bool:
if _write_blocked:
return false
var previous: Dictionary = _records.duplicate(true)
- if not bool(record["muted"]) and not bool(record["blocked"]):
+ if (
+ not bool(record.get("muted", false))
+ and not bool(record.get("blocked", false))
+ and not bool(record.get("friend", false))
+ ):
_records.erase(fingerprint)
else:
_records[fingerprint] = record
@@ -115,7 +214,8 @@ func _ensure_loaded() -> void:
if json.parse(file.get_as_text()) != OK or typeof(json.data) != TYPE_DICTIONARY:
return
var data: Dictionary = json.data
- if data.get("format_version") != FORMAT_VERSION:
+ var format_version: int = int(data.get("format_version", 0))
+ if format_version not in [1, FORMAT_VERSION]:
_write_blocked = true
return
for value: Variant in data.get("records", []):
@@ -127,6 +227,13 @@ func _ensure_loaded() -> void:
continue
record["blocked"] = bool(record.get("blocked", false))
record["muted"] = bool(record.get("muted", false)) or record["blocked"]
+ record["friend"] = (
+ bool(record.get("friend", false))
+ and not record["blocked"]
+ and _valid_stored_friend_capabilities(record)
+ )
+ if not record["friend"]:
+ _clear_friend_fields(record)
_records[fingerprint] = record.duplicate(true)
_expected_hash = PortableFileGuard.hash_file(_store_path)
@@ -145,3 +252,69 @@ func _save() -> bool:
if bool(result.get("ok", false)):
_expected_hash = str(result["hash"])
return bool(result.get("ok", false))
+
+
+func _clear_friend_fields(record: Dictionary) -> void:
+ record["friend"] = false
+ for key: String in [
+ "friend_since_unix",
+ "local_presence_write_token",
+ "local_invite_token",
+ "remote_presence_channel",
+ "remote_invite_token",
+ ]:
+ record.erase(key)
+
+
+func _valid_local_capabilities(value: Dictionary) -> bool:
+ var write_token := str(value.get("local_presence_write_token", ""))
+ var invite_token := str(value.get("local_invite_token", ""))
+ return (
+ _valid_social_token(write_token)
+ and _valid_social_token(invite_token)
+ and str(value.get("presence_channel", ""))
+ == presence_channel_for_write_token(write_token)
+ and str(value.get("invite_token", "")) == invite_token
+ )
+
+
+func _valid_public_capabilities(value: Dictionary) -> bool:
+ return (
+ _valid_social_token(str(value.get("presence_channel", "")))
+ and _valid_social_token(str(value.get("invite_token", "")))
+ )
+
+
+func _valid_stored_friend_capabilities(record: Dictionary) -> bool:
+ var local: Dictionary = {
+ "local_presence_write_token": str(
+ record.get("local_presence_write_token", "")
+ ),
+ "local_invite_token": str(record.get("local_invite_token", "")),
+ "presence_channel": presence_channel_for_write_token(str(
+ record.get("local_presence_write_token", "")
+ )),
+ "invite_token": str(record.get("local_invite_token", "")),
+ }
+ var remote: Dictionary = {
+ "presence_channel": str(record.get("remote_presence_channel", "")),
+ "invite_token": str(record.get("remote_invite_token", "")),
+ }
+ return _valid_local_capabilities(local) and _valid_public_capabilities(remote)
+
+
+func _valid_social_token(value: String) -> bool:
+ if value.length() != SOCIAL_TOKEN_LENGTH:
+ return false
+ for character: String in value:
+ if character not in "0123456789abcdef":
+ return false
+ return true
+
+
+func _sort_records(records: Array[Dictionary]) -> void:
+ records.sort_custom(func(a: Dictionary, b: Dictionary) -> bool:
+ return str(a.get("last_known_display_name", "")).naturalnocasecmp_to(
+ str(b.get("last_known_display_name", ""))
+ ) < 0
+ )
diff --git a/network/player_spawn_service.gd b/network/player_spawn_service.gd
index 1352758..1d1c292 100644
--- a/network/player_spawn_service.gd
+++ b/network/player_spawn_service.gd
@@ -11,16 +11,23 @@ var _local_player: Player
var _spawn_transform: Transform3D
var _avatars: Dictionary[int, Player] = {}
var _local_peer_id: int = 1
+var _dedicated_runtime: bool = false
func setup(
players_root: Node3D,
local_player: Player,
spawn_transform: Transform3D,
+ dedicated_runtime: bool = false,
) -> void:
_players_root = players_root
_local_player = local_player
_spawn_transform = spawn_transform
+ _dedicated_runtime = dedicated_runtime
+
+
+func set_spawn_transform(spawn_transform: Transform3D) -> void:
+ _spawn_transform = spawn_transform
func register_local_player(peer_id: int) -> void:
@@ -52,7 +59,10 @@ func spawn_remote_player(
avatar.set_network_peer_id(peer_id)
_players_root.add_child(avatar)
avatar.global_transform = transform
- avatar.configure_network_remote(authoritative_simulation)
+ avatar.configure_network_remote(
+ authoritative_simulation,
+ _dedicated_runtime and authoritative_simulation,
+ )
_avatars[peer_id] = avatar
avatar_spawned.emit(peer_id, avatar)
return avatar
@@ -78,6 +88,10 @@ func get_avatar(peer_id: int) -> Player:
return _avatars.get(peer_id)
+func get_local_player() -> Player:
+ return _local_player
+
+
func set_peer_presentation_visible(peer_id: int, should_be_visible: bool) -> void:
var avatar: Player = _avatars.get(peer_id)
if avatar != null and is_instance_valid(avatar):
diff --git a/network/portable_data_migration.gd b/network/portable_data_migration.gd
index ab8f21d..2de0acb 100644
--- a/network/portable_data_migration.gd
+++ b/network/portable_data_migration.gd
@@ -26,19 +26,16 @@ static func migrate_legacy_to(
) -> Dictionary:
var normalized: String = destination.simplify_path().trim_suffix("/")
if DirAccess.dir_exists_absolute(normalized):
- var existing_manifest: String = normalized.path_join(
- PlayerDataRoot.MANIFEST_FILENAME
- )
- if FileAccess.file_exists(existing_manifest):
+ if data_root.has_data_manifest(normalized):
return {
"ok": false,
"requires_existing_root_decision": true,
- "message": "The selected folder already contains NETfishing data.",
+ "message": "The selected folder already contains straywild data.",
}
if not _directory_empty(normalized):
return {
"ok": false,
- "message": "Choose an empty folder or create a NETfishing subfolder.",
+ "message": "Choose an empty folder or create a straywild subfolder.",
}
var staging: String = "%s.migration-%s" % [
normalized, Crypto.new().generate_random_bytes(8).hex_encode()
@@ -100,18 +97,22 @@ static func migrate_active_to(
) -> Dictionary:
var normalized: String = destination.simplify_path().trim_suffix("/")
if normalized == data_root.root_path:
- return {"ok": true, "message": "This data folder is already active."}
+ return {
+ "ok": true,
+ "changed": false,
+ "message": "This data folder is already active.",
+ }
if DirAccess.dir_exists_absolute(normalized) and not _directory_empty(normalized):
- if FileAccess.file_exists(normalized.path_join(PlayerDataRoot.MANIFEST_FILENAME)):
+ if data_root.has_data_manifest(normalized):
return {
"ok": false,
"requires_existing_root_decision": true,
"message": (
- "The selected folder already contains NETfishing data. "
+ "The selected folder already contains straywild data. "
+ "Choose it through the existing-data recovery flow."
),
}
- return {"ok": false, "message": "Choose an empty folder or a NETfishing subfolder."}
+ return {"ok": false, "message": "Choose an empty folder or a straywild subfolder."}
var staging: String = "%s.migration-%s" % [
normalized, Crypto.new().generate_random_bytes(8).hex_encode()
]
@@ -122,7 +123,9 @@ static func migrate_active_to(
_remove_tree(staging)
return {"ok": false, "message": str(created.get("message", ""))}
for relative: String in [
+ "player/player_save.nfsave",
"player/player_save.json",
+ "player/save_slots.json",
"player/network_profile.json",
"player/player_appearance.json",
"social/saved_servers.json",
@@ -134,9 +137,23 @@ static func migrate_active_to(
var source: String = data_root.root_path.path_join(relative)
if not FileAccess.file_exists(source):
continue
- if not bool(_copy_verified_json(source, staging.path_join(relative)).get("ok", false)):
+ if not bool(
+ _copy_verified_owned_file(
+ source, staging.path_join(relative)
+ ).get("ok", false)
+ ):
_remove_tree(staging)
return {"ok": false, "message": "Migration validation failed for %s." % relative}
+ var source_slots: String = data_root.root_path.path_join("player/saves")
+ if (
+ DirAccess.dir_exists_absolute(source_slots)
+ and not _copy_progression_slots(
+ source_slots,
+ staging.path_join("player/saves"),
+ )
+ ):
+ _remove_tree(staging)
+ return {"ok": false, "message": "Migration validation failed for save slots."}
if DirAccess.dir_exists_absolute(normalized):
DirAccess.remove_absolute(normalized)
if DirAccess.rename_absolute(staging, normalized) != OK:
@@ -147,6 +164,7 @@ static func migrate_active_to(
return {"ok": false, "message": "Migration copied data but did not change the pointer."}
return {
"ok": true,
+ "changed": true,
"message": "Data folder changed. Previous data was preserved.",
"previous_root": old_root,
}
@@ -186,9 +204,8 @@ static func replace_existing_with_legacy(
destination: String,
) -> Dictionary:
var normalized: String = destination.simplify_path().trim_suffix("/")
- var manifest: String = normalized.path_join(PlayerDataRoot.MANIFEST_FILENAME)
- if not FileAccess.file_exists(manifest):
- return {"ok": false, "message": "The selected folder is not a NETfishing data root."}
+ if not data_root.has_data_manifest(normalized):
+ return {"ok": false, "message": "The selected folder is not a straywild data root."}
var recovery: String = ProjectSettings.globalize_path(
"user://migration-recovery"
).path_join(
@@ -208,8 +225,8 @@ static func replace_existing_with_active(
destination: String,
) -> Dictionary:
var normalized: String = destination.simplify_path().trim_suffix("/")
- if not FileAccess.file_exists(normalized.path_join(PlayerDataRoot.MANIFEST_FILENAME)):
- return {"ok": false, "message": "The selected folder is not a NETfishing data root."}
+ if not data_root.has_data_manifest(normalized):
+ return {"ok": false, "message": "The selected folder is not a straywild data root."}
var recovery: String = ProjectSettings.globalize_path(
"user://migration-recovery"
).path_join(
@@ -248,16 +265,70 @@ static func _copy_verified_json(source: String, destination: String) -> Dictiona
}
+static func _copy_verified_owned_file(
+ source: String,
+ destination: String,
+) -> Dictionary:
+ if source.get_extension().to_lower() != "nfsave":
+ return _copy_verified_json(source, destination)
+ var decoded: Dictionary = ProgressionSaveCodec.read_local_save(source)
+ if not bool(decoded.get("ok", false)):
+ return {"ok": false}
+ var save_data: Dictionary = decoded["data"]
+ var version: int = int(save_data.get("save_version", -1))
+ if version < 1 or version > PlayerSaveManager.SAVE_VERSION:
+ return {"ok": false}
+ var bytes: PackedByteArray = PortableFileGuard.read_bytes(source)
+ if bytes.is_empty() or not _write_bytes(destination, bytes):
+ return {"ok": false}
+ return {
+ "ok": (
+ PortableFileGuard.hash_file(destination)
+ == PortableFileGuard.hash_bytes(bytes)
+ )
+ }
+
+
static func _valid_owned_data(filename: String, data: Dictionary) -> bool:
if filename == "player_save.json":
var version: int = int(data.get("save_version", -1))
return version >= 1 and version <= PlayerSaveManager.SAVE_VERSION
+ if filename == "save_slots.json":
+ return (
+ int(data.get("format_version", -1)) == PlayerSaveSlotCatalog.FORMAT_VERSION
+ and typeof(data.get("active_slot_id")) == TYPE_STRING
+ and typeof(data.get("slots")) == TYPE_ARRAY
+ )
return (
filename not in LEGACY_FILES
or int(data.get("format_version", -1)) == 1
)
+static func _copy_progression_slots(source: String, destination: String) -> bool:
+ if DirAccess.make_dir_recursive_absolute(destination) != OK:
+ return false
+ var access := DirAccess.open(source)
+ if access == null:
+ return false
+ access.list_dir_begin()
+ var filename: String = access.get_next()
+ while not filename.is_empty():
+ if (
+ not access.current_is_dir()
+ and filename.get_extension().to_lower() == "nfsave"
+ and not bool(_copy_verified_owned_file(
+ source.path_join(filename),
+ destination.path_join(filename),
+ ).get("ok", false))
+ ):
+ access.list_dir_end()
+ return false
+ filename = access.get_next()
+ access.list_dir_end()
+ return true
+
+
static func _copy_bytes(source: String, destination: String) -> bool:
return _write_bytes(destination, PortableFileGuard.read_bytes(source))
diff --git a/network/saved_server_store.gd b/network/saved_server_store.gd
index de48c2c..4e00259 100644
--- a/network/saved_server_store.gd
+++ b/network/saved_server_store.gd
@@ -22,6 +22,12 @@ var _data_root: PlayerDataRoot
func configure_storage(path: String, data_root: PlayerDataRoot) -> void:
_store_path = path
_data_root = data_root
+ _saved_entries.clear()
+ _recent_entries.clear()
+ _loaded = false
+ _recovery_warning = ""
+ _write_blocked = false
+ _expected_hash = ""
func _temp_path() -> String:
diff --git a/network/server_trust_store.gd b/network/server_trust_store.gd
index fda774b..1d4dd29 100644
--- a/network/server_trust_store.gd
+++ b/network/server_trust_store.gd
@@ -19,6 +19,10 @@ var _data_root: PlayerDataRoot
func configure_storage(path: String, data_root: PlayerDataRoot) -> void:
_store_path = path
_data_root = data_root
+ _records.clear()
+ _loaded = false
+ _write_blocked = false
+ _expected_hash = ""
func verify(endpoint: ConnectionEndpoint, fingerprint: String) -> Verification:
diff --git a/player/animalese_voice_profiles.gd b/player/animalese_voice_profiles.gd
index 2ea6cab..ded0898 100644
--- a/player/animalese_voice_profiles.gd
+++ b/player/animalese_voice_profiles.gd
@@ -2,9 +2,27 @@ class_name AnimaleseVoiceProfiles
extends RefCounted
const DEFAULT_ID: String = "natural"
+const DEFAULT_SAMPLE_SET_ID: String = "kat"
const DEFAULT_SPEED_ID: String = "normal"
const DEFAULT_CALL_ID: String = "meow"
const CALL_AUDIO_DIRECTORY: String = "res://sound/dialogue/calls"
+const SAMPLE_SET_OPTIONS: Array[Dictionary] = [
+ {
+ "id": "kat",
+ "label": "kat",
+ "directory": "res://sound/dialogue/animalese/alphanumeric",
+ },
+ {
+ "id": "robot",
+ "label": "robot",
+ "directory": "res://sound/dialogue/animalese/robot",
+ },
+ {
+ "id": "kim",
+ "label": "kim",
+ "directory": "res://sound/dialogue/animalese/kim",
+ },
+]
const OPTIONS: Array[Dictionary] = [
{"id": "tiny", "label": "tiny", "pitch": 1.24},
{"id": "bright", "label": "bright", "pitch": 1.10},
@@ -12,6 +30,31 @@ const OPTIONS: Array[Dictionary] = [
{"id": "mellow", "label": "mellow", "pitch": 0.89},
{"id": "deep", "label": "deep", "pitch": 0.77},
]
+
+
+static func is_valid_sample_set(sample_set_id: String) -> bool:
+ for option: Dictionary in SAMPLE_SET_OPTIONS:
+ if str(option.get("id", "")) == sample_set_id:
+ return true
+ return false
+
+
+static func sanitized_sample_set_id(sample_set_id: String) -> String:
+ return (
+ sample_set_id
+ if is_valid_sample_set(sample_set_id)
+ else DEFAULT_SAMPLE_SET_ID
+ )
+
+
+static func sample_directory_for(sample_set_id: String) -> String:
+ var resolved_id := sanitized_sample_set_id(sample_set_id)
+ for option: Dictionary in SAMPLE_SET_OPTIONS:
+ if str(option.get("id", "")) == resolved_id:
+ return str(option.get("directory", ""))
+ return ""
+
+
const SPEED_OPTIONS: Array[Dictionary] = [
{"id": "slow", "label": "slow", "characters_per_second": 20.0},
{"id": "relaxed", "label": "relaxed", "characters_per_second": 24.0},
diff --git a/player/fishing_rod_attachment.tscn b/player/fishing_rod_attachment.tscn
index 46da419..d803e28 100644
--- a/player/fishing_rod_attachment.tscn
+++ b/player/fishing_rod_attachment.tscn
@@ -1,4 +1,6 @@
-[gd_scene load_steps=3 format=3]
+[gd_scene load_steps=4 format=3]
+
+[ext_resource type="Script" path="res://player/tool_socket_attachment.gd" id="1_socket"]
[sub_resource type="CylinderMesh" id="FishingRodMesh"]
top_radius = 0.025
@@ -11,13 +13,17 @@ roughness = 0.8
[node name="FishingRodAttachment" type="BoneAttachment3D"]
bone_name = "rod_socket"
+script = ExtResource("1_socket")
+aligned_mount_path = NodePath("FishingRod")
[node name="FishingRod" type="Node3D" parent="."]
-[node name="RodMesh" type="MeshInstance3D" parent="FishingRod"]
+[node name="FallbackRodMesh" type="MeshInstance3D" parent="FishingRod"]
position = Vector3(0, 0.5, 0)
mesh = SubResource("FishingRodMesh")
material_override = SubResource("FishingRodMaterial")
+[node name="ModelMount" type="Node3D" parent="FishingRod"]
+
[node name="FishingRodTip" type="Marker3D" parent="FishingRod"]
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
index 05eff4b..66b3b7c 100644
--- a/player/held_item_attachment.tscn
+++ b/player/held_item_attachment.tscn
@@ -23,10 +23,12 @@ 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 = 1
+texture_filter = 0
[node name="CatchDisplay" type="Node3D" parent="."]
unique_name_in_owner = true
diff --git a/player/net_attachment.gd b/player/net_attachment.gd
new file mode 100644
index 0000000..3c7843a
--- /dev/null
+++ b/player/net_attachment.gd
@@ -0,0 +1,88 @@
+extends "res://player/tool_socket_attachment.gd"
+
+const TextureSamplingPolicyScript := preload(
+ "res://main/texture_sampling_policy.gd"
+)
+const SPRING_ROOT_BONE: StringName = &"net_mid"
+const SPRING_END_BONE: StringName = &"net_tip"
+const IMPACT_BONE: StringName = &"net_rim"
+
+@export_range(0.0, 10.0, 0.05) var spring_stiffness: float = 1.8
+@export_range(0.0, 1.0, 0.01) var spring_drag: float = 0.68
+@export_range(0.0, 10.0, 0.05) var spring_gravity: float = 0.3
+@export_range(0.01, 0.5, 0.01) var spring_end_length: float = 0.1
+
+@onready var _catching_net: Node3D = %CatchingNet
+@onready var _net_model: Node3D = %NetModel
+
+var _spring: SpringBoneSimulator3D
+var _spring_was_visible: bool = false
+var _net_skeleton: Skeleton3D
+
+
+func _ready() -> void:
+ super._ready()
+ TextureSamplingPolicyScript.enforce_subtree(_net_model)
+ _configure_spring()
+ _sync_spring_activity()
+
+
+func _process(_delta: float) -> void:
+ _sync_spring_activity()
+
+
+func _configure_spring() -> void:
+ _net_skeleton = _net_model.find_child(
+ "Skeleton3D", true, false
+ ) as Skeleton3D
+ if _net_skeleton == null:
+ push_error("Imported net model is missing its Skeleton3D.")
+ return
+ if _net_skeleton.find_bone(SPRING_ROOT_BONE) < 0:
+ push_error("Imported net model is missing bone %s." % SPRING_ROOT_BONE)
+ return
+ if _net_skeleton.find_bone(SPRING_END_BONE) < 0:
+ push_error("Imported net model is missing bone %s." % SPRING_END_BONE)
+ return
+ if _net_skeleton.find_bone(IMPACT_BONE) < 0:
+ push_error("Imported net model is missing bone %s." % IMPACT_BONE)
+ return
+
+ _spring = SpringBoneSimulator3D.new()
+ _spring.name = "NetSpring"
+ _spring.set_setting_count(1)
+ _spring.set_root_bone_name(0, SPRING_ROOT_BONE)
+ _spring.set_end_bone_name(0, SPRING_END_BONE)
+ _spring.set_extend_end_bone(0, true)
+ _spring.set_end_bone_direction(
+ 0, SkeletonModifier3D.BONE_DIRECTION_FROM_PARENT
+ )
+ _spring.set_end_bone_length(0, spring_end_length)
+ _spring.set_stiffness(0, spring_stiffness)
+ _spring.set_drag(0, spring_drag)
+ _spring.set_gravity(0, spring_gravity)
+ _spring.set_gravity_direction(0, Vector3.DOWN)
+ _net_skeleton.add_child(_spring)
+
+
+func get_impact_world_position() -> Vector3:
+ if _net_skeleton == null:
+ return _catching_net.global_position
+ var impact_bone_index := _net_skeleton.find_bone(IMPACT_BONE)
+ if impact_bone_index < 0:
+ return _catching_net.global_position
+ return _net_skeleton.global_transform * (
+ _net_skeleton.get_bone_global_pose(impact_bone_index).origin
+ )
+
+
+func _sync_spring_activity() -> void:
+ if _spring == null:
+ return
+ var is_visible := _catching_net.is_visible_in_tree()
+ if is_visible and not _spring_was_visible:
+ _spring.active = true
+ _spring.reset()
+ elif not is_visible:
+ _spring.active = false
+ _spring_was_visible = is_visible
diff --git a/player/net_attachment.gd.uid b/player/net_attachment.gd.uid
new file mode 100644
index 0000000..9bf2fd6
--- /dev/null
+++ b/player/net_attachment.gd.uid
@@ -0,0 +1 @@
+uid://cw5vc02hoelgm
diff --git a/player/net_attachment.tscn b/player/net_attachment.tscn
new file mode 100644
index 0000000..da49a88
--- /dev/null
+++ b/player/net_attachment.tscn
@@ -0,0 +1,20 @@
+[gd_scene load_steps=3 format=3]
+
+[ext_resource type="Script" path="res://player/net_attachment.gd" id="1_script"]
+[ext_resource type="PackedScene" path="res://art/exported/items/equipment/net.glb" id="2_model"]
+
+[node name="NetAttachment" type="BoneAttachment3D"]
+bone_name = "rod_socket"
+script = ExtResource("1_script")
+aligned_mount_path = NodePath("CatchingNet")
+
+[node name="CatchingNet" type="Node3D" parent="." unique_id=947004727]
+unique_name_in_owner = true
+visible = false
+
+[node name="ModelMount" type="Node3D" parent="CatchingNet"]
+position = Vector3(-0.000217, -0.098636, -0.020218)
+rotation = Vector3(0, 3.1415927, 0)
+
+[node name="NetModel" parent="CatchingNet/ModelMount" unique_id=1627767240 instance=ExtResource("2_model")]
+unique_name_in_owner = true
diff --git a/player/player.gd b/player/player.gd
index b9b4133..78dac3d 100644
--- a/player/player.gd
+++ b/player/player.gd
@@ -19,6 +19,9 @@ const PlayerItemEffectsType = preload(
const PlayerCoolerCapacityType = preload(
"res://progression/player_cooler_capacity.gd"
)
+const PlayerInventoryLayoutType = preload(
+ "res://inventory/player_inventory_layout.gd"
+)
const PlayerArtUnlocksType = preload(
"res://progression/player_art_unlocks.gd"
)
@@ -31,9 +34,17 @@ const ControllerMappingManagerType = preload(
const FishingRodAttachmentScene = preload(
"res://player/fishing_rod_attachment.tscn"
)
+const NetAttachmentScene = preload("res://player/net_attachment.tscn")
+const NetAttachmentType = preload("res://player/net_attachment.gd")
+const ShovelAttachmentScene = preload("res://player/shovel_attachment.tscn")
+const FishingRodDataType = preload("res://items/fishing_rod_data.gd")
const HeldItemAttachmentScene = preload(
"res://player/held_item_attachment.tscn"
)
+const SprintDustTrailType = preload("res://player/sprint_dust_trail.gd")
+const VoiceProfilesType = preload(
+ "res://player/animalese_voice_profiles.gd"
+)
signal retract_visual_finished
@@ -45,6 +56,8 @@ const CHARACTER_WALKING_ANIMATION: StringName = &"walking"
const CHARACTER_WALKING_SHOW_ANIMATION: StringName = &"walking_show"
const CHARACTER_RUNNING_ANIMATION: StringName = &"running"
const CHARACTER_RUNNING_SHOW_ANIMATION: StringName = &"running_show"
+const CHARACTER_IDLE_SNEAK_ANIMATION: StringName = &"idle_sneak"
+const CHARACTER_SNEAKING_ANIMATION: StringName = &"sneaking"
const CHARACTER_CASTING_ANIMATION: StringName = &"casting"
const CHARACTER_CASTING_SIT_ANIMATION: StringName = &"casting_sit"
const CHARACTER_RELEASE_ANIMATION: StringName = &"release"
@@ -67,6 +80,21 @@ 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"
+const CHARACTER_NET_DRAW_ANIMATION: StringName = &"draw"
+const CHARACTER_NET_STRIKE_ANIMATION: StringName = &"strike"
+const CATCH_PRESENTATION_REFERENCE_LONG_SIDE_PX: float = 1280.0
+const CATCH_PRESENTATION_MIN_TEXTURE_SCALE: float = 0.1
+const CATCH_PRESENTATION_MAX_TEXTURE_SCALE: float = 20.0
+const CATCH_SHOWCASE_INPUT_OWNER: StringName = &"catch_showcase"
+# 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] = [
+ CHARACTER_WALKING_ANIMATION,
+ CHARACTER_CASTING_ANIMATION,
+ CHARACTER_NET_DRAW_ANIMATION,
+ CHARACTER_NET_STRIKE_ANIMATION,
+]
enum FishingVisualPhase {
NONE,
@@ -83,8 +111,49 @@ enum PocketVisualTarget {
ART_KIT,
CATCH_SHOWCASE,
}
+
+enum LocomotionState {
+ IDLE,
+ WALKING,
+ RUNNING,
+ SNEAKING,
+}
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 SPEECH_MOUTH_IDS: Array[String] = [
+ "open_ah",
+ "open_oh",
+ "open_small",
+ "open_smile",
+]
+const SPEECH_MOUTH_FRAME_SECONDS: float = 0.09
const BASE_REEL_SPEED: float = 0.16
+const LANDING_DUST_MIN_FALL_SPEED: float = 2.5
+const NETWORK_EXTRAPOLATION_LIMIT_SECONDS: float = 0.25
+const NETWORK_EXPECTED_SNAPSHOT_INTERVAL_SECONDS: float = 1.0 / 30.0
+const NETWORK_SNAPSHOT_JITTER_LIMIT_SECONDS: float = 0.1
+const NETWORK_SNAPSHOT_JITTER_WEIGHT: float = 0.15
+const NETWORK_REMOTE_SMOOTHING_RATE: float = 12.0
+const NETWORK_REMOTE_JITTER_SMOOTHING_RATE: float = 6.0
+const NETWORK_INPUT_STALE_TIMEOUT_SECONDS: float = 0.25
+const NETWORK_INPUT_STALE_TIMEOUT_MAX_SECONDS: float = 1.25
+const NETWORK_INPUT_STALE_RTT_MULTIPLIER: float = 1.5
+const NETWORK_MOVEMENT_HISTORY_SECONDS: float = 1.25
+const NETWORK_MAX_LAG_COMPENSATION_SECONDS: float = 0.75
+const LOCAL_PREDICTION_EXTRAPOLATION_LIMIT_SECONDS: float = 0.25
+const LOCAL_PREDICTION_FALLBACK_TRANSIT_RATIO: float = 0.5
+const LOCAL_PREDICTION_CORRECTION_THRESHOLD: float = 1.5
+const LOCAL_PREDICTION_SNAP_DISTANCE: float = 6.0
+const LOCAL_PREDICTION_CORRECTION_DELAY_SECONDS: float = 0.35
+const LOCAL_PREDICTION_MIN_CORRECTION_AUDITS: int = 3
+const LOCAL_PREDICTION_SOFT_CORRECTION_RATE: float = 2.5
+const LOCAL_PREDICTION_MAX_SOFT_CORRECTION_STEP: float = 0.15
+const LOCAL_PREDICTION_MAX_AUDIT_DELTA_SECONDS: float = 0.15
+const LOCAL_RECONCILIATION_PRESENTATION_RECENTER_RATE: float = 5.0
# 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.
@@ -93,7 +162,8 @@ const CONTROLLER_ZOOM_TRIGGER_AXIS: JoyAxis = JOY_AXIS_TRIGGER_LEFT
var appearance_snapshot: Dictionary = (
CharacterCustomizationCatalog.default_snapshot()
)
-var animalese_voice_id: String = "natural"
+var animalese_voice_id: String = VoiceProfilesType.DEFAULT_ID
+var animalese_sample_set_id: String = VoiceProfilesType.DEFAULT_SAMPLE_SET_ID
var active_bait_id: StringName = StringName()
var active_lure_id: StringName = StringName()
signal active_bait_changed(item_id: StringName)
@@ -153,11 +223,64 @@ 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 play_speech_visual(duration_seconds: float) -> void:
+ stop_speech_visual()
+ if duration_seconds <= 0.0 or SPEECH_MOUTH_IDS.is_empty():
+ return
+ var generation: int = _speech_visual_generation
+ var elapsed: float = 0.0
+ var frame_index: int = 0
+ _speech_mouth_active = true
+ while elapsed < duration_seconds:
+ _speech_mouth_id = SPEECH_MOUTH_IDS[
+ frame_index % SPEECH_MOUTH_IDS.size()
+ ]
+ _apply_presented_appearance()
+ var frame_duration := minf(
+ SPEECH_MOUTH_FRAME_SECONDS,
+ duration_seconds - elapsed,
+ )
+ await get_tree().create_timer(frame_duration).timeout
+ if generation != _speech_visual_generation:
+ return
+ elapsed += frame_duration
+ frame_index += 1
+ _speech_mouth_active = false
+ _speech_mouth_id = ""
+ _apply_presented_appearance()
+
+
+func stop_speech_visual() -> void:
+ _speech_visual_generation += 1
+ if not _speech_mouth_active and _speech_mouth_id.is_empty():
+ return
+ _speech_mouth_active = false
+ _speech_mouth_id = ""
+ _apply_presented_appearance()
+
+
func set_fishing_visual(active: bool) -> void:
if (
active
@@ -220,19 +343,40 @@ 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 _speech_mouth_active and not _speech_mouth_id.is_empty():
+ if presented_appearance == appearance_snapshot:
+ presented_appearance = appearance_snapshot.duplicate(true)
+ presented_appearance["mouth"] = _speech_mouth_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
+ animalese_voice_id = VoiceProfilesType.sanitized_id(voice_id)
func get_animalese_voice_id() -> String:
return animalese_voice_id
+
+func apply_animalese_sample_set_id(sample_set_id: String) -> void:
+ animalese_sample_set_id = VoiceProfilesType.sanitized_sample_set_id(
+ sample_set_id
+ )
+
+
+func get_animalese_sample_set_id() -> String:
+ return animalese_sample_set_id
+
class ShowcaseCameraSnapshot:
extends RefCounted
@@ -241,7 +385,6 @@ class ShowcaseCameraSnapshot:
var pitch_rotation: Vector3
var spring_length: float
var target_zoom: float
- var camera_input_enabled: bool
var camera_dragging: bool
@@ -268,22 +411,37 @@ class ShowcaseCameraSnapshot:
@export_range(0.05, 1.0, 0.01) var showcase_restore_duration: float = 0.24
@export_range(0.05, 1.5, 0.01) var showcase_camera_transition_duration: float = 0.42
@export_range(-180.0, 180.0, 1.0) var showcase_camera_yaw_offset: float = 180.0
+@export_range(-180.0, 180.0, 1.0) var net_showcase_camera_yaw_offset: float = 0.0
@export_range(-80.0, 80.0, 1.0) var showcase_camera_pitch: float = -8.0
@export_range(1.0, 10.0, 0.1) var showcase_camera_zoom_distance: float = 3.6
@export_range(0.5, 3.0, 0.05) var showcase_camera_target_height: float = 1.45
@export_range(0.01, 1.0, 0.01) var catch_presentation_base_scale: float = 0.10
+@export_category("Gathering")
+@export_flags_3d_physics var net_impact_collision_mask: int = 1
+@export_range(0.02, 0.5, 0.01) var net_impact_radius: float = 0.16
+@export_range(0.0, 1.0, 0.01) var net_success_contact_pause_duration: float = 0.45
+
@export_category("Camera")
@export var mouse_sensitivity: float = 0.005
@export var controller_camera_speed: float = 2.5
@export_range(0.0, 1.0, 0.01) var controller_camera_deadzone: float = 0.2
@export var invert_camera_y: bool = false
+var _swap_hotbar_camera_scroll: bool = false
+var _rv_interior_camera_mode: bool = false
+var _rv_interior_environment: Environment
+var _rv_previous_camera_environment: Environment
@export_range(-89.0, 0.0, 0.5) var minimum_pitch_degrees: float = -65.0
@export_range(0.0, 89.0, 0.5) var maximum_pitch_degrees: float = 45.0
@export var minimum_zoom: float = 2.0
@export var maximum_zoom: float = 8.0
@export var zoom_step: float = 0.75
@export var zoom_smoothing: float = 12.0
+@export_range(0.0, 1.0, 0.01) var fishing_reel_zoom_amount: float = 0.22
+@export_range(0.0, 1.5, 0.01) var fishing_barrier_break_zoom: float = 0.38
+@export_range(0.05, 1.0, 0.01) var fishing_break_zoom_decay: float = 0.34
+@export_range(0.0, 0.2, 0.005) var fishing_barrier_hit_shake: float = 0.018
+@export_range(0.0, 0.3, 0.005) var fishing_barrier_break_shake: float = 0.045
@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
@@ -310,6 +468,7 @@ class ShowcaseCameraSnapshot:
@onready var fishing_upgrades: PlayerFishingUpgradesType = %FishingUpgrades
@onready var item_effects: PlayerItemEffectsType = %ItemEffects
@onready var cooler_capacity: PlayerCoolerCapacityType = %CoolerCapacity
+@onready var inventory_layout: PlayerInventoryLayoutType = %InventoryLayout
@onready var art_unlocks: PlayerArtUnlocksType = %ArtUnlocks
@onready var experience: PlayerExperienceType = %Experience
@onready var _cast_origin: Marker3D = %CastOrigin
@@ -319,13 +478,18 @@ 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 _held_decor_package_display: Node3D
+var _held_decor_package_visible: bool = false
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 _camera_drag_pointer_origin: Vector2 = Vector2.ZERO
+var _camera_drag_restore_pointer: bool = false
var _free_camera_active: bool = false
var _free_camera_body: CharacterBody3D
var _free_camera: Camera3D
@@ -333,12 +497,21 @@ 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 _camera_input_suppressors: Dictionary[StringName, bool] = {}
var _water_recovery_active: bool = false
var _remote_recovery_presentation_active: bool = false
var _remote_recovery_visual_origin: Vector3
var _remote_recovery_elapsed: float = 0.0
var _target_zoom: float = 5.0
+var _fishing_reel_camera_active: bool = false
+var _fishing_break_zoom_offset: float = 0.0
+var _fishing_camera_shake_remaining: float = 0.0
+var _fishing_camera_shake_duration: float = 0.0
+var _fishing_camera_shake_strength: float = 0.0
+var _fishing_camera_shake_phase: float = 0.0
var _showcase_rod_visibility: bool = true
+var _showcase_net_visibility: bool = false
+var _showcase_shovel_visibility: bool = false
var _remote_presentation_visible := true
var _showcase_rod_state_stored: bool = false
var _showcase_visual_rotation: Vector3
@@ -358,11 +531,43 @@ var _network_sprint: bool = false
var _network_sneak: bool = false
var _network_slow_walk: bool = false
var _last_network_input_sequence: int = 0
+var _network_input_age: float = 0.0
+var _network_input_stale: bool = false
+var _network_input_stale_timeout_seconds: float = (
+ NETWORK_INPUT_STALE_TIMEOUT_SECONDS
+)
+var _network_jump_intent_active: bool = false
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_target_animation_action_paused: bool = false
var _network_snapshot_ready: bool = false
var _network_snapshot_age: float = 0.0
+var _network_snapshot_jitter: float = 0.0
+var _network_simulation_only: bool = false
+var _local_reconciliation_visual_offset: Vector3 = Vector3.ZERO
+var _local_reconciliation_camera_offset: Vector3 = Vector3.ZERO
+var _local_prediction_error_seconds: float = 0.0
+var _local_prediction_error_audits: int = 0
+var _local_prediction_error_direction: Vector3 = Vector3.ZERO
+var _local_prediction_soft_corrections: int = 0
+var _local_prediction_hard_corrections: int = 0
+var _local_prediction_largest_error: float = 0.0
+var _authoritative_movement_history: Array[Dictionary] = []
+var _local_network_jump_intent_pending: bool = false
+var _local_network_jump_intent_sequence: int = -1
+var _animation_action_id: StringName = &""
+var _animation_action_sequence: int = 0
+var _animation_action_elapsed: float = 0.0
+var _animation_action_paused: bool = false
+var _presented_animation_action_id: StringName = &""
+var _presented_animation_action_sequence: int = -1
+var _presented_animation_action_paused: bool = false
var _character_animation_name: StringName = &""
var _sitting: bool = false
var _sit_after_landing: bool = false
@@ -382,19 +587,52 @@ 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 _speech_mouth_active: bool = false
+var _speech_mouth_id: String = ""
+var _speech_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 _catching_net: Node3D
+var _catching_net_attachment: NetAttachmentType
+var _shovel: Node3D
+var _net_strike_collision_armed: bool = false
+var _net_strike_has_previous_sample: bool = false
+var _net_strike_previous_position: Vector3
+var _net_strike_previous_animation_position: float = 0.0
+var _net_strike_contact_held: bool = false
+var _net_strike_result_known: bool = false
+var _net_strike_successful: bool = false
+var _net_strike_showcase_pause_tween: Tween
+var _net_impact_shape := SphereShape3D.new()
+var _pending_net_showcase_catch: FishCatchType
+var _pending_net_showcase_remote: bool = false
var _fishing_rod_tip: Marker3D
+var _fishing_rod_model_mount: Node3D
+var _fishing_rod_fallback_visual: GeometryInstance3D
+var _custom_fishing_rod_visual: Node3D
+var _active_fishing_rod_id: StringName
var _active_item_is_rod: bool = false
+var _active_item_is_net: bool = false
+var _active_item_is_shovel: 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: CPUParticles3D = %SprintDust
+@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(
@@ -408,6 +646,8 @@ func _ready() -> void:
bag.contents_changed.connect(_on_bag_contents_changed)
_apply_presented_appearance()
_initialize_fishing_rod()
+ _initialize_catching_net()
+ _initialize_shovel()
_target_zoom = clampf(_spring_arm.spring_length, minimum_zoom, maximum_zoom)
_spring_arm.spring_length = _target_zoom
_camera.current = local_control_enabled
@@ -433,6 +673,261 @@ func set_controller_mapping_manager(
_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 begin_net_draw_visual() -> bool:
+ return begin_animation_action(
+ CHARACTER_NET_DRAW_ANIMATION
+ if is_sneaking()
+ else CHARACTER_CASTING_ANIMATION
+ )
+
+
+func play_net_strike_visual() -> bool:
+ if begin_animation_action(CHARACTER_NET_STRIKE_ANIMATION):
+ _net_strike_collision_armed = local_control_enabled
+ _net_strike_has_previous_sample = false
+ _net_strike_contact_held = false
+ _net_strike_result_known = false
+ _net_strike_successful = false
+ return true
+ cancel_net_action_visual()
+ return false
+
+
+func resolve_net_strike_visual(successful: bool) -> void:
+ if _animation_action_id != CHARACTER_NET_STRIKE_ANIMATION:
+ return
+ _net_strike_result_known = true
+ _net_strike_successful = successful
+ if successful:
+ _net_strike_collision_armed = false
+ if successful and _animation_action_paused:
+ _net_strike_contact_held = false
+ _set_animation_action_paused(false)
+
+
+func is_net_strike_held() -> bool:
+ return (
+ _animation_action_id == CHARACTER_NET_STRIKE_ANIMATION
+ and _net_strike_contact_held
+ )
+
+
+func release_net_strike_hold() -> bool:
+ if not is_net_strike_held():
+ return false
+ cancel_net_action_visual()
+ return true
+
+
+func cancel_net_action_visual() -> void:
+ _clear_net_strike_state()
+ if _animation_action_id in [
+ CHARACTER_CASTING_ANIMATION,
+ CHARACTER_NET_DRAW_ANIMATION,
+ CHARACTER_NET_STRIKE_ANIMATION,
+ ]:
+ end_animation_action()
+
+
+func _clear_net_strike_state() -> void:
+ _kill_net_strike_showcase_pause()
+ _net_strike_collision_armed = false
+ _net_strike_has_previous_sample = false
+ _net_strike_contact_held = false
+ _net_strike_result_known = false
+ _net_strike_successful = false
+ if _animation_action_paused:
+ _set_animation_action_paused(false)
+
+
+func _kill_net_strike_showcase_pause() -> void:
+ if (
+ _net_strike_showcase_pause_tween != null
+ and _net_strike_showcase_pause_tween.is_valid()
+ ):
+ _net_strike_showcase_pause_tween.kill()
+ _net_strike_showcase_pause_tween = null
+
+
+func _hold_successful_net_strike_before_showcase() -> void:
+ _kill_net_strike_showcase_pause()
+ var strike_animation := _character_animation_player.get_animation(
+ CHARACTER_NET_STRIKE_ANIMATION
+ )
+ if strike_animation != null:
+ _animation_action_elapsed = maxf(
+ strike_animation.length - 0.001,
+ 0.0,
+ )
+ _net_strike_collision_armed = false
+ _net_strike_contact_held = false
+ _set_animation_action_paused(true)
+ if net_success_contact_pause_duration <= 0.0:
+ _finish_successful_net_strike_pause()
+ return
+ _net_strike_showcase_pause_tween = create_tween()
+ _net_strike_showcase_pause_tween.tween_interval(
+ net_success_contact_pause_duration
+ )
+ _net_strike_showcase_pause_tween.finished.connect(
+ _finish_successful_net_strike_pause
+ )
+
+
+func _finish_successful_net_strike_pause() -> void:
+ _net_strike_showcase_pause_tween = null
+ if local_control_enabled:
+ if not _net_strike_result_known or not _net_strike_successful:
+ return
+ _clear_net_strike_state()
+ end_animation_action()
+ _begin_pending_net_showcase()
+
+
+func _set_animation_action_paused(paused: bool) -> void:
+ if _animation_action_id.is_empty() or _animation_action_paused == paused:
+ 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(
+ _animation_action_id,
+ next_sequence,
+ _animation_action_elapsed,
+ paused,
+ )
+ )
+
+
+func _update_net_strike_contact() -> void:
+ if (
+ not local_control_enabled
+ or not _net_strike_collision_armed
+ or _animation_action_paused
+ or _animation_action_id != CHARACTER_NET_STRIKE_ANIMATION
+ or _catching_net_attachment == null
+ or _character_animation_player == null
+ or get_world_3d() == null
+ ):
+ return
+ var current_position: Vector3 = (
+ _catching_net_attachment.get_impact_world_position()
+ )
+ var current_animation_position: float = (
+ _character_animation_player.current_animation_position
+ )
+ if not _net_strike_has_previous_sample:
+ _net_strike_previous_position = current_position
+ _net_strike_previous_animation_position = current_animation_position
+ _net_strike_has_previous_sample = true
+ return
+ var motion := current_position - _net_strike_previous_position
+ if motion.length_squared() <= 0.000001:
+ _net_strike_previous_animation_position = current_animation_position
+ return
+ _net_impact_shape.radius = net_impact_radius
+ var query := PhysicsShapeQueryParameters3D.new()
+ query.shape = _net_impact_shape
+ query.transform = Transform3D(
+ Basis.IDENTITY,
+ _net_strike_previous_position,
+ )
+ query.motion = motion
+ query.collision_mask = net_impact_collision_mask
+ query.collide_with_areas = false
+ query.collide_with_bodies = true
+ query.exclude = [get_rid()]
+ var fractions: PackedFloat32Array = (
+ get_world_3d().direct_space_state.cast_motion(query)
+ )
+ if fractions.size() >= 2 and float(fractions[1]) < 1.0:
+ var impact_fraction := clampf(float(fractions[0]), 0.0, 1.0)
+ _animation_action_elapsed = lerpf(
+ _net_strike_previous_animation_position,
+ current_animation_position,
+ impact_fraction,
+ )
+ _net_strike_collision_armed = false
+ _net_strike_contact_held = true
+ _set_animation_action_paused(true)
+ return
+ _net_strike_previous_position = current_position
+ _net_strike_previous_animation_position = current_animation_position
+
+
+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
+ or bool(action_state.get("paused", false))
+ != _animation_action_paused
+ )
+ _animation_action_id = action_id
+ _animation_action_sequence = action_sequence
+ _animation_action_elapsed = float(action_state["elapsed"])
+ _animation_action_paused = bool(action_state.get("paused", false))
+ if action_changed:
+ _character_animation_name = &""
+ _update_character_animation()
+
+
func _initialize_fishing_rod() -> void:
var skeleton := get_node_or_null(
"Visuals/CharacterRig/CharacterRig/Skeleton3D"
@@ -449,6 +944,47 @@ func _initialize_fishing_rod() -> void:
_fishing_rod_tip = attachment.get_node(
"FishingRod/FishingRodTip"
) as Marker3D
+ _fishing_rod_model_mount = attachment.get_node(
+ "FishingRod/ModelMount"
+ ) as Node3D
+ _fishing_rod_fallback_visual = attachment.get_node(
+ "FishingRod/FallbackRodMesh"
+ ) as GeometryInstance3D
+
+
+func _initialize_catching_net() -> void:
+ var skeleton := get_node_or_null(
+ "Visuals/CharacterRig/CharacterRig/Skeleton3D"
+ ) as Skeleton3D
+ if skeleton == null:
+ push_error("Player character skeleton is unavailable for the net.")
+ return
+ var attachment := NetAttachmentScene.instantiate() as NetAttachmentType
+ if attachment == null:
+ push_error("Catching net attachment could not be instantiated.")
+ return
+ skeleton.add_child(attachment)
+ _catching_net_attachment = attachment
+ _catching_net = attachment.get_node("CatchingNet") as Node3D
+ if _catching_net != null:
+ _catching_net.visible = false
+
+
+func _initialize_shovel() -> void:
+ var skeleton := get_node_or_null(
+ "Visuals/CharacterRig/CharacterRig/Skeleton3D"
+ ) as Skeleton3D
+ if skeleton == null:
+ push_error("Player character skeleton is unavailable for the shovel.")
+ return
+ var attachment := ShovelAttachmentScene.instantiate() as BoneAttachment3D
+ if attachment == null:
+ push_error("Shovel attachment could not be instantiated.")
+ return
+ skeleton.add_child(attachment)
+ _shovel = attachment.get_node("Shovel") as Node3D
+ if _shovel != null:
+ _shovel.visible = false
func _initialize_held_item_attachment() -> void:
@@ -469,9 +1005,11 @@ func _initialize_held_item_attachment() -> void:
_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
+ _held_decor_package_display = _build_decor_package_display(attachment)
_catch_display = attachment.get_node("CatchDisplay") as Node3D
_catch_attachment_offset = _catch_display.position
_catch_sprite = attachment.get_node("CatchDisplay/CatchSprite") as Sprite3D
@@ -481,6 +1019,14 @@ func _physics_process(delta: float) -> void:
if _network_interpolation_enabled:
_update_network_interpolation(delta)
return
+ _simulate_movement_physics(delta)
+ if _network_authoritative_simulation:
+ _record_authoritative_movement_state()
+
+
+func _simulate_movement_physics(delta: float) -> void:
+ if _network_authoritative_simulation:
+ _update_network_input_freshness(delta)
if local_control_enabled and _free_camera_active:
_update_free_camera_physics()
velocity.x = 0.0
@@ -490,15 +1036,33 @@ func _physics_process(delta: float) -> void:
move_and_slide()
_network_jump_pending = false
return
+ var local_jump_pressed: bool = (
+ local_control_enabled
+ and Input.is_action_just_pressed("jump")
+ )
+ if (
+ local_jump_pressed
+ and _is_movement_input_enabled()
+ and not _free_camera_active
+ ):
+ _queue_local_network_jump_intent()
var jump_requested: bool = (
_is_movement_input_enabled()
and not _free_camera_active
and (
- (local_control_enabled and Input.is_action_just_pressed("jump"))
+ local_jump_pressed
or (_network_authoritative_simulation and _network_jump_pending)
)
)
- if _sit_after_landing and is_on_floor():
+ if jump_requested:
+ _cancel_sitting_for_jump()
+ # Sitting deliberately stops movement processing, so never allow a stale
+ # grounded flag or a reconciled network state to leave an airborne avatar in
+ # that early-return path. Preserve the intent and sit once landing is real.
+ if _sitting and not _can_begin_sitting():
+ _sit_after_landing = true
+ _set_sitting(false)
+ if _sit_after_landing and not jump_requested and _can_begin_sitting():
_sit_after_landing = false
_set_sitting(true, local_control_enabled)
if _sitting:
@@ -551,6 +1115,8 @@ func _physics_process(delta: float) -> void:
move_direction.y = 0.0
var input_strength: float = minf(input_vector.length(), 1.0)
move_direction = move_direction.normalized()
+ if input_strength > 0.05:
+ release_net_strike_hold()
var speed: float = _get_network_aware_speed()
if item_effects != null:
@@ -570,8 +1136,18 @@ func _physics_process(delta: float) -> void:
func _process(delta: float) -> void:
+ if _network_simulation_only:
+ return
+ _update_local_reconciliation_visuals(delta)
+ if not _animation_action_id.is_empty() and not _animation_action_paused:
+ _animation_action_elapsed = minf(
+ _animation_action_elapsed + delta,
+ NetworkPlayerAnimationProtocol.MAX_ACTION_ELAPSED_SECONDS,
+ )
+ _update_blink(delta)
_update_character_animation()
- _update_sprint_dust()
+ _update_net_strike_contact()
+ _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.
@@ -598,12 +1174,17 @@ func _process(delta: float) -> void:
)
if not local_control_enabled:
return
+ _update_fishing_camera_effects(delta)
if _free_camera_active:
+ if _is_camera_input_enabled():
+ _rotate_free_camera(
+ _scale_controller_camera_input(
+ _get_controller_camera_stick(),
+ delta,
+ )
+ )
return
- if _camera_dragging and not Input.is_action_pressed("camera_drag"):
- _set_camera_dragging(false)
-
if _is_camera_input_enabled():
var stick: Vector2 = _get_controller_camera_stick()
if stick.length() > controller_camera_deadzone:
@@ -617,42 +1198,134 @@ func _process(delta: float) -> void:
+ 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
+ _scale_controller_camera_input(stick, delta)
)
var zoom_weight: float = 1.0 - exp(-zoom_smoothing * delta)
+ var fishing_zoom_offset: float = (
+ fishing_reel_zoom_amount
+ if _fishing_reel_camera_active
+ else 0.0
+ ) + _fishing_break_zoom_offset
+ var effective_target_zoom: float = clampf(
+ _target_zoom - fishing_zoom_offset,
+ 1.25 if _rv_interior_camera_mode else minimum_zoom,
+ 16.0 if _rv_interior_camera_mode else maximum_zoom,
+ )
_spring_arm.spring_length = lerpf(
_spring_arm.spring_length,
- _target_zoom,
+ effective_target_zoom,
zoom_weight
)
-func _update_sprint_dust() -> void:
+func set_fishing_reel_camera_active(active: bool) -> void:
+ _fishing_reel_camera_active = active
+
+
+func play_fishing_barrier_camera_feedback(barrier_broken: bool) -> void:
+ if not local_control_enabled or _free_camera_active:
+ return
+ _fishing_camera_shake_strength = (
+ fishing_barrier_break_shake
+ if barrier_broken
+ else fishing_barrier_hit_shake
+ )
+ _fishing_camera_shake_duration = 0.20 if barrier_broken else 0.10
+ _fishing_camera_shake_remaining = _fishing_camera_shake_duration
+ if barrier_broken:
+ _fishing_break_zoom_offset = fishing_barrier_break_zoom
+
+
+func _update_fishing_camera_effects(delta: float) -> void:
+ _fishing_break_zoom_offset = move_toward(
+ _fishing_break_zoom_offset,
+ 0.0,
+ (
+ fishing_barrier_break_zoom
+ / maxf(fishing_break_zoom_decay, 0.05)
+ ) * delta,
+ )
+ if _fishing_camera_shake_remaining <= 0.0:
+ _camera.h_offset = 0.0
+ _camera.v_offset = 0.0
+ return
+ _fishing_camera_shake_remaining = maxf(
+ _fishing_camera_shake_remaining - delta,
+ 0.0,
+ )
+ _fishing_camera_shake_phase += delta * 92.0
+ var decay: float = (
+ _fishing_camera_shake_remaining
+ / maxf(_fishing_camera_shake_duration, 0.001)
+ )
+ var strength: float = _fishing_camera_shake_strength * decay
+ _camera.h_offset = sin(_fishing_camera_shake_phase) * strength
+ _camera.v_offset = cos(_fishing_camera_shake_phase * 1.31) * strength * 0.7
+
+
+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_speed_squared: float = (
- velocity.x * velocity.x + velocity.z * velocity.z
- )
- var fastest_non_sprint_speed := maxf(
- walk_speed,
- maxf(sneak_speed, slow_walk_speed)
- )
- var running_threshold := fastest_non_sprint_speed + 0.5
- _sprint_dust.emitting = (
- is_on_floor()
+ 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 horizontal_speed_squared
- > running_threshold * running_threshold
+ and _get_presented_locomotion_state() == LocomotionState.RUNNING
+ )
+ _sprint_dust.update_trail(
+ delta,
+ global_position,
+ horizontal_velocity,
+ should_emit,
)
@@ -672,6 +1345,25 @@ func _get_controller_camera_stick() -> Vector2:
)
+func _scale_controller_camera_input(
+ stick: Vector2,
+ delta: float,
+) -> Vector2:
+ var strength: float = stick.length()
+ if strength <= controller_camera_deadzone:
+ return Vector2.ZERO
+ var adjusted_strength: float = (
+ (strength - controller_camera_deadzone)
+ / (1.0 - controller_camera_deadzone)
+ )
+ return (
+ stick.normalized()
+ * adjusted_strength
+ * controller_camera_speed
+ * delta
+ )
+
+
func _get_controller_zoom_strength() -> float:
if _controller_mapping_manager != null:
return _controller_mapping_manager.get_role_strength(
@@ -683,22 +1375,28 @@ func _get_controller_zoom_strength() -> float:
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 is_walking: bool = horizontal_speed_squared > 0.0025
- var fastest_non_sprint_speed := maxf(
- walk_speed,
- maxf(sneak_speed, slow_walk_speed)
+ var is_walking: bool = locomotion_state != LocomotionState.IDLE
+ var is_running: bool = locomotion_state == LocomotionState.RUNNING
+ var is_sneaking_pose: bool = (
+ locomotion_state == LocomotionState.SNEAKING
)
- var running_threshold := fastest_non_sprint_speed + 0.5
- var is_running := (
- is_walking
- and horizontal_speed_squared > running_threshold * running_threshold
+ var is_moving_horizontally: bool = (
+ _is_presented_moving_horizontally()
)
- var held_show_item_visible: bool = (
- _held_fish_visible or _held_art_kit_visible
+ var animation_action: Dictionary = _get_presented_animation_action()
+ var animation_action_id := StringName(str(animation_action.get("id", "")))
+ var animation_action_paused: bool = bool(
+ animation_action.get("paused", false)
)
+ 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 = _has_held_show_item()
var requested_animation: Array[StringName] = []
if _pocket_visual_active:
requested_animation = [_pocket_visual_animation]
@@ -783,6 +1481,8 @@ func _update_character_animation() -> void:
CHARACTER_FISHING_ANIMATION,
CHARACTER_IDLE_ANIMATION,
]
+ elif animation_action_available:
+ requested_animation = [animation_action_id]
elif _sitting:
if held_show_item_visible:
requested_animation = [
@@ -799,6 +1499,19 @@ func _update_character_animation() -> void:
&"sitting",
&"idle_loop_sit",
]
+ elif is_sneaking_pose:
+ if is_moving_horizontally:
+ requested_animation = [
+ CHARACTER_SNEAKING_ANIMATION,
+ CHARACTER_WALKING_ANIMATION,
+ &"walking_loop",
+ ]
+ else:
+ requested_animation = [
+ CHARACTER_IDLE_SNEAK_ANIMATION,
+ CHARACTER_IDLE_ANIMATION,
+ &"idle_loop",
+ ]
elif is_running and held_show_item_visible:
requested_animation = [
CHARACTER_RUNNING_SHOW_ANIMATION,
@@ -839,13 +1552,114 @@ func _update_character_animation() -> void:
break
if next_animation.is_empty():
return
- if _character_animation_name == next_animation:
+ 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
+ or animation_action_paused
+ != _presented_animation_action_paused
+ )
+ )
+ if not action_selected:
+ _presented_animation_action_id = &""
+ _presented_animation_action_sequence = -1
+ _presented_animation_action_paused = false
+ if (
+ _character_animation_playback_matches(
+ next_animation,
+ action_selected and animation_action_paused,
+ )
+ and not action_changed
+ ):
return
+ # The animation name is only a selection cache. A remote player's
+ # AnimationPlayer can occasionally stop or lose its assigned animation while
+ # its replicated locomotion state remains valid. Reassert the selected
+ # presentation here instead of waiting for another network state transition;
+ # movement simulation and replication remain untouched.
+ _character_animation_player.active = true
_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
+ _presented_animation_action_paused = animation_action_paused
+ var elapsed: float = float(animation_action.get("elapsed", 0.0))
+ var animation: Animation = _character_animation_player.get_animation(
+ next_animation
+ )
+ if (
+ (elapsed > 0.0 or animation_action_paused)
+ and animation != null
+ and animation.length > 0.0
+ ):
+ _character_animation_player.seek(
+ fposmod(elapsed, animation.length), true
+ )
+ if animation_action_paused:
+ _character_animation_player.pause()
+
+
+func _character_animation_playback_matches(
+ animation_name: StringName,
+ paused_action_selected: bool,
+) -> bool:
+ if (
+ _character_animation_name != animation_name
+ or _character_animation_player.assigned_animation != animation_name
+ or not _character_animation_player.active
+ ):
+ return false
+ if paused_action_selected:
+ return true
+ var animation := _character_animation_player.get_animation(animation_name)
+ if animation == null or animation.loop_mode == Animation.LOOP_NONE:
+ # Completed one-shot actions deliberately keep their final pose until the
+ # authoritative action state advances; they must not be restarted here.
+ return true
+ return _character_animation_player.is_playing()
func _on_character_animation_finished(animation_name: StringName) -> void:
+ if animation_name == CHARACTER_NET_STRIKE_ANIMATION:
+ if local_control_enabled:
+ if _net_strike_result_known and _net_strike_successful:
+ _hold_successful_net_strike_before_showcase()
+ else:
+ var strike_animation := (
+ _character_animation_player.get_animation(
+ CHARACTER_NET_STRIKE_ANIMATION
+ )
+ )
+ if strike_animation != null:
+ _animation_action_elapsed = maxf(
+ strike_animation.length - 0.001,
+ 0.0,
+ )
+ _net_strike_collision_armed = false
+ _net_strike_contact_held = true
+ _set_animation_action_paused(true)
+ return
+ if _pending_net_showcase_remote:
+ _hold_successful_net_strike_before_showcase()
+ return
+ if (
+ local_control_enabled
+ and not _animation_action_id.is_empty()
+ and animation_name == _animation_action_id
+ ):
+ if animation_name in [
+ CHARACTER_CASTING_ANIMATION,
+ CHARACTER_NET_DRAW_ANIMATION,
+ ]:
+ return
+ end_animation_action()
+ return
if animation_name in [
CHARACTER_POCKET_IDLE_IDLE_ANIMATION,
CHARACTER_POCKET_IDLE_SHOW_ANIMATION,
@@ -895,6 +1709,7 @@ func _complete_retract_animation() -> void:
func toggle_sitting() -> void:
if _sit_after_landing:
_sit_after_landing = false
+ _set_sitting(false, local_control_enabled)
return
var should_sit: bool = not _sitting
if should_sit:
@@ -908,7 +1723,7 @@ func toggle_sitting() -> void:
])
):
return
- if not is_on_floor():
+ if not _can_begin_sitting():
_sit_after_landing = true
return
_set_sitting(should_sit, local_control_enabled)
@@ -927,12 +1742,12 @@ 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 == should_sit:
+ return
+ _sitting = should_sit
if _sitting:
velocity = Vector3.ZERO
_character_animation_name = &""
@@ -943,32 +1758,91 @@ func is_sitting() -> bool:
return _sitting
+func _can_begin_sitting() -> bool:
+ return is_on_floor() and velocity.y <= 0.0
+
+
+func _cancel_sitting_for_jump() -> void:
+ if not _sitting and not _sit_after_landing:
+ return
+ _sit_after_landing = false
+ _set_sitting(false, local_control_enabled)
+
+
+func get_network_sitting_state() -> bool:
+ return _sitting
+
+
+func get_network_sitting_intent() -> bool:
+ # Jumping is an explicit cancellation of both a current sit and a deferred
+ # sit. Input capture can run before the matching physics frame, so suppress
+ # the stale seated bit as soon as the reliable jump intent is queued.
+ if _local_network_jump_intent_pending:
+ return false
+ return _sitting or _sit_after_landing
+
+
+func apply_network_sitting_state(should_sit: bool) -> void:
+ _set_sitting(should_sit)
+
+
+func apply_authoritative_network_sitting_state(should_sit: bool) -> void:
+ if should_sit and (
+ not _can_begin_sitting() or _network_jump_pending
+ ):
+ _sit_after_landing = true
+ _set_sitting(false)
+ return
+ _sit_after_landing = false
+ _set_sitting(should_sit)
+
+
+func _input(event: InputEvent) -> void:
+ # Camera dragging must observe the complete mouse gesture before GUI controls
+ # get a chance to consume one part of it. Do not claim the event here: the
+ # Art Kit still needs the same right-button state to suspend its pointer.
+ if not local_control_enabled or not _is_camera_input_enabled():
+ return
+ var mouse_button := event as InputEventMouseButton
+ if mouse_button != null and event.is_action("camera_drag"):
+ _set_camera_dragging(mouse_button.pressed)
+ return
+ var mouse_motion := event as InputEventMouseMotion
+ if mouse_motion == null or not _camera_dragging:
+ return
+ if _free_camera_active:
+ _rotate_free_camera(mouse_motion.screen_relative * mouse_sensitivity)
+ else:
+ _rotate_camera(mouse_motion.screen_relative * mouse_sensitivity)
+
+
+func _notification(what: int) -> void:
+ # A captured pointer can miss its release when the application loses focus.
+ # Normal camera drags are ended by the matching mouse-button event; polling
+ # InputMap here is intentionally avoided because changing mouse mode can
+ # transiently clear the action and release an otherwise active drag.
+ if what == NOTIFICATION_APPLICATION_FOCUS_OUT and _camera_dragging:
+ _set_camera_dragging(false)
+
+
func _unhandled_input(event: InputEvent) -> void:
if not local_control_enabled or not _is_camera_input_enabled():
return
if event.is_action_pressed("toggle_free_camera"):
- _set_free_camera_active(not _free_camera_active)
+ toggle_free_camera()
get_viewport().set_input_as_handled()
return
- if event.is_action("camera_drag"):
- _set_camera_dragging(event.is_pressed())
- get_viewport().set_input_as_handled()
- return
-
- if event is InputEventMouseMotion and _camera_dragging:
- 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
var mouse_zoom_in: bool = (
event is InputEventMouseButton
- and event.shift_pressed
+ and (
+ (not event.shift_pressed)
+ if _rv_interior_camera_mode
+ else event.shift_pressed != _swap_hotbar_camera_scroll
+ )
and event.is_action_pressed("camera_zoom_in")
)
var controller_zoom_in: bool = (
@@ -977,7 +1851,11 @@ func _unhandled_input(event: InputEvent) -> void:
)
var mouse_zoom_out: bool = (
event is InputEventMouseButton
- and event.shift_pressed
+ and (
+ (not event.shift_pressed)
+ if _rv_interior_camera_mode
+ else event.shift_pressed != _swap_hotbar_camera_scroll
+ )
and event.is_action_pressed("camera_zoom_out")
)
var controller_zoom_out: bool = (
@@ -1008,9 +1886,22 @@ func _set_camera_dragging(active: bool) -> void:
_camera_dragging = active
if active:
_camera_drag_prior_mouse_mode = Input.mouse_mode
+ _camera_drag_restore_pointer = _camera_drag_prior_mouse_mode in [
+ Input.MOUSE_MODE_VISIBLE,
+ Input.MOUSE_MODE_CONFINED,
+ ]
+ if _camera_drag_restore_pointer:
+ _camera_drag_pointer_origin = get_viewport().get_mouse_position()
+ # Capture starts with the physical press, before any motion is applied.
+ # Switching modes after the first movement can synthesize a center warp
+ # in the middle of the drag and interrupt camera control.
Input.mouse_mode = Input.MOUSE_MODE_CAPTURED
- elif Input.mouse_mode == Input.MOUSE_MODE_CAPTURED:
- Input.mouse_mode = _camera_drag_prior_mouse_mode
+ else:
+ if Input.mouse_mode == Input.MOUSE_MODE_CAPTURED:
+ Input.mouse_mode = _camera_drag_prior_mouse_mode
+ if _camera_drag_restore_pointer:
+ Input.warp_mouse(_camera_drag_pointer_origin)
+ _camera_drag_restore_pointer = false
func _get_network_aware_speed() -> float:
@@ -1082,10 +1973,27 @@ func _set_free_camera_active(active: bool) -> void:
_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
+ # Freecam translation is camera input, not avatar movement. Fishing locks
+ # the avatar after a cast, but the detached camera must remain movable.
+ # Camera/menu input ownership still stops freecam while an overlay is active.
+ if not _is_camera_input_enabled():
+ _free_camera_body.velocity = Vector3.ZERO
+ _free_camera_body.move_and_slide()
+ return
var input_vector: Vector2 = Input.get_vector(
"move_left",
"move_right",
@@ -1135,7 +2043,11 @@ func get_active_gameplay_camera() -> Camera3D:
func _set_target_zoom(value: float) -> void:
- _target_zoom = clampf(value, minimum_zoom, maximum_zoom)
+ _target_zoom = clampf(
+ value,
+ 1.25 if _rv_interior_camera_mode else minimum_zoom,
+ 16.0 if _rv_interior_camera_mode else maximum_zoom,
+ )
func _apply_axis_deadzone(value: float) -> float:
@@ -1157,6 +2069,7 @@ func set_local_control(enabled: bool) -> void:
_camera.current = enabled
if not enabled:
_set_camera_dragging(false)
+ _clear_local_network_jump_intent()
func set_network_peer_id(peer_id: int) -> void:
@@ -1167,23 +2080,134 @@ func get_network_peer_id() -> int:
return _network_peer_id
-func configure_network_remote(authoritative_simulation: bool) -> void:
+func configure_network_remote(
+ authoritative_simulation: bool,
+ simulation_only: bool = false,
+) -> void:
set_local_control(false)
_network_authoritative_simulation = authoritative_simulation
_network_interpolation_enabled = not authoritative_simulation
+ set_network_simulation_only(simulation_only)
+ _network_axis = Vector2.ZERO
+ _network_jump_pending = false
+ _network_sprint = false
+ _network_sneak = false
+ _network_slow_walk = false
+ _network_input_age = 0.0
+ _network_input_stale = false
+ _network_input_stale_timeout_seconds = NETWORK_INPUT_STALE_TIMEOUT_SECONDS
+ _network_jump_intent_active = false
_network_snapshot_ready = false
_network_snapshot_age = 0.0
+ _network_snapshot_jitter = 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
+ _network_target_animation_action_paused = false
_camera.current = false
+ _authoritative_movement_history.clear()
+
+
+func set_network_simulation_only(enabled: bool) -> void:
+ _network_simulation_only = enabled
+ if not is_node_ready():
+ return
+ set_process(not enabled)
+ for presentation_root: Node in [
+ _visuals,
+ get_node_or_null("GroundShadow"),
+ get_node_or_null("SprintDust"),
+ _camera_yaw,
+ ]:
+ if presentation_root == null:
+ continue
+ presentation_root.process_mode = (
+ Node.PROCESS_MODE_DISABLED
+ if enabled
+ else Node.PROCESS_MODE_INHERIT
+ )
+ if presentation_root is Node3D:
+ (presentation_root as Node3D).visible = not enabled
+
+
+func is_network_simulation_only() -> bool:
+ return _network_simulation_only
+
+
+func reset_network_movement_state() -> void:
+ _clear_local_network_jump_intent()
+ if not _animation_action_id.is_empty():
+ end_animation_action()
+ _sit_after_landing = false
+ _sitting_intent_pending = false
+ _sitting_intent_sequence = -1
+ _set_sitting(false)
+ _network_axis = Vector2.ZERO
+ _network_jump_pending = false
+ _network_sprint = false
+ _network_sneak = false
+ _network_slow_walk = false
+ _network_input_age = 0.0
+ _network_input_stale = false
+ _network_input_stale_timeout_seconds = NETWORK_INPUT_STALE_TIMEOUT_SECONDS
+ _network_jump_intent_active = false
+ _last_network_input_sequence = 0
+ _reset_local_prediction_error()
+ _clear_local_reconciliation_offsets()
+ _local_prediction_soft_corrections = 0
+ _local_prediction_hard_corrections = 0
+ _local_prediction_largest_error = 0.0
+
+
+func apply_network_space_transition(
+ target_transform: Transform3D,
+ align_local_camera: bool,
+) -> void:
+ if not target_transform.origin.is_finite():
+ return
+ # A room transition is a discontinuity, not ordinary movement. Preserve the
+ # host's input acknowledgement while clearing every motion, animation, and
+ # reconciliation state that could pull the avatar toward the previous space.
+ var acknowledged_input: int = _last_network_input_sequence
+ reset_network_movement_state()
+ _last_network_input_sequence = acknowledged_input
+ _apply_network_casting(false)
+ velocity = Vector3.ZERO
+ global_transform = Transform3D(Basis.IDENTITY, target_transform.origin)
+ var target_yaw: float = target_transform.basis.get_euler().y
+ _visuals.rotation.y = target_yaw
+ _network_camera_yaw = target_yaw
+ _network_target_position = global_position
+ _network_target_velocity = Vector3.ZERO
+ _network_target_visual_yaw = target_yaw
+ _network_snapshot_age = 0.0
+ _network_snapshot_ready = true
+ _authoritative_movement_history.clear()
+ if align_local_camera:
+ _set_camera_dragging(false)
+ if _free_camera_active:
+ _set_free_camera_active(false)
+ _camera_yaw.rotation.y = target_yaw
func capture_network_input(sequence: int) -> Dictionary:
if _sitting_intent_pending and _sitting_intent_sequence < 0:
_sitting_intent_sequence = sequence
+ if (
+ local_control_enabled
+ and _is_movement_input_enabled()
+ and not _free_camera_active
+ and Input.is_action_just_pressed("jump")
+ ):
+ _queue_local_network_jump_intent()
if (
not _is_movement_input_enabled()
or _water_recovery_active
or _free_camera_active
):
+ _clear_local_network_jump_intent()
return {
"sequence": sequence,
"axis": [0.0, 0.0],
@@ -1192,10 +2216,11 @@ func capture_network_input(sequence: int) -> Dictionary:
"sprint": false,
"sneak": false,
"slow_walk": false,
- "sitting": _sitting,
+ "sitting": get_network_sitting_intent(),
"casting": (
_fishing_visual_phase == FishingVisualPhase.CASTING
),
+ "animation_action": _make_animation_action_state(),
}
var axis: Vector2 = Input.get_vector(
"move_left",
@@ -1203,20 +2228,79 @@ func capture_network_input(sequence: int) -> Dictionary:
"move_forward",
"move_backward"
)
+ if (
+ _local_network_jump_intent_pending
+ and _local_network_jump_intent_sequence < 0
+ ):
+ _local_network_jump_intent_sequence = sequence
return {
"sequence": sequence,
"axis": [axis.x, axis.y],
"camera_yaw": _camera_yaw.global_rotation.y,
- "jump": Input.is_action_just_pressed("jump"),
+ "jump": _local_network_jump_intent_pending,
"sprint": Input.is_action_pressed("sprint"),
"sneak": Input.is_action_pressed("sneak"),
"slow_walk": Input.is_action_pressed("slow_walk"),
- "sitting": _sitting,
+ "sitting": get_network_sitting_intent(),
"casting": _fishing_visual_phase == FishingVisualPhase.CASTING,
+ "animation_action": _make_animation_action_state(),
}
-func apply_authoritative_network_input(data: Dictionary) -> void:
+func has_active_network_input() -> bool:
+ if not local_control_enabled:
+ return false
+ if _local_network_jump_intent_pending or _sitting_intent_pending:
+ return true
+ if not _is_movement_input_enabled() or _free_camera_active:
+ return false
+ return (
+ not Input.get_vector(
+ "move_left",
+ "move_right",
+ "move_forward",
+ "move_backward",
+ ).is_zero_approx()
+ or Input.is_action_pressed("sprint")
+ or Input.is_action_pressed("sneak")
+ or Input.is_action_pressed("slow_walk")
+ or _fishing_visual_phase == FishingVisualPhase.CASTING
+ or not _animation_action_id.is_empty()
+ )
+
+
+func get_network_input_state_hash() -> int:
+ var axis: Vector2 = Vector2.ZERO
+ if (
+ local_control_enabled
+ and _is_movement_input_enabled()
+ and not _free_camera_active
+ ):
+ axis = Input.get_vector(
+ "move_left",
+ "move_right",
+ "move_forward",
+ "move_backward",
+ )
+ return hash([
+ axis,
+ _camera_yaw.global_rotation.y,
+ _local_network_jump_intent_pending,
+ Input.is_action_pressed("sprint"),
+ Input.is_action_pressed("sneak"),
+ Input.is_action_pressed("slow_walk"),
+ get_network_sitting_intent(),
+ _fishing_visual_phase == FishingVisualPhase.CASTING,
+ _animation_action_id,
+ _animation_action_sequence,
+ _animation_action_paused,
+ ])
+
+
+func apply_authoritative_network_input(
+ data: Dictionary,
+ stale_timeout_seconds: float = NETWORK_INPUT_STALE_TIMEOUT_SECONDS,
+) -> void:
var sequence: int = int(data.get("sequence", 0))
if sequence <= _last_network_input_sequence:
return
@@ -1224,20 +2308,29 @@ func apply_authoritative_network_input(data: Dictionary) -> void:
if axis.size() != 2:
return
_last_network_input_sequence = sequence
+ _network_input_age = 0.0
+ _network_input_stale = false
+ _network_input_stale_timeout_seconds = clampf(
+ stale_timeout_seconds,
+ NETWORK_INPUT_STALE_TIMEOUT_SECONDS,
+ NETWORK_INPUT_STALE_TIMEOUT_MAX_SECONDS,
+ )
_network_axis = Vector2(float(axis[0]), float(axis[1])).limit_length(1.0)
_network_camera_yaw = float(data.get("camera_yaw", 0.0))
- _network_jump_pending = bool(data.get("jump", false))
+ var jump_intent_active: bool = bool(data.get("jump", false))
+ if jump_intent_active and not _network_jump_intent_active:
+ _network_jump_pending = true
+ _network_jump_intent_active = jump_intent_active
_network_sprint = bool(data.get("sprint", false))
_network_sneak = bool(data.get("sneak", false))
_network_slow_walk = bool(data.get("slow_walk", false))
+ apply_authoritative_network_animation_action(
+ 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)
+ apply_authoritative_network_sitting_state(
+ bool(data.get("sitting", false))
+ )
func make_network_snapshot(peer_id: int) -> Dictionary:
@@ -1247,19 +2340,84 @@ 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,
+ "animation_state": make_network_animation_state(),
"grounded": is_on_floor(),
- "sitting": _sitting,
+ "sitting": get_network_sitting_state(),
"casting": _fishing_visual_phase == FishingVisualPhase.CASTING,
}
-func push_network_snapshot(snapshot: Dictionary) -> void:
+func make_network_animation_state() -> Dictionary:
+ return NetworkPlayerAnimationProtocol.make_state(
+ _get_authoritative_locomotion_id(),
+ is_on_floor(),
+ _animation_action_id,
+ _animation_action_sequence,
+ _animation_action_elapsed,
+ _animation_action_paused,
+ )
+
+
+func apply_network_animation_state(state: Dictionary) -> void:
+ _apply_network_target_animation_state(state)
+
+
+func apply_authoritative_network_animation_action(
+ action_state: Dictionary,
+) -> void:
+ if not NetworkPlayerAnimationProtocol.validate_action_state(action_state):
+ return
+ var incoming_sequence: int = int(action_state["sequence"])
+ var wrapped_sequence: bool = (
+ _animation_action_sequence
+ == NetworkPlayerAnimationProtocol.MAX_ACTION_SEQUENCE
+ and incoming_sequence == 1
+ )
+ if incoming_sequence < _animation_action_sequence and not wrapped_sequence:
+ return
+ if (
+ incoming_sequence == _animation_action_sequence
+ and StringName(str(action_state["id"])) != _animation_action_id
+ ):
+ return
+ _apply_animation_action_state(action_state)
+
+
+func push_network_snapshot(
+ snapshot: Dictionary,
+ estimated_transit_seconds: float = -1.0,
+) -> void:
var parsed: Dictionary = _parse_network_snapshot(snapshot)
if parsed.is_empty():
return
- _network_target_position = parsed["position"]
+ if _network_snapshot_ready:
+ var arrival_error: float = absf(
+ _network_snapshot_age
+ - NETWORK_EXPECTED_SNAPSHOT_INTERVAL_SECONDS
+ )
+ _network_snapshot_jitter = lerpf(
+ _network_snapshot_jitter,
+ minf(arrival_error, NETWORK_SNAPSHOT_JITTER_LIMIT_SECONDS),
+ NETWORK_SNAPSHOT_JITTER_WEIGHT,
+ )
+ var transit_seconds: float = (
+ clampf(
+ estimated_transit_seconds,
+ 0.0,
+ NETWORK_EXTRAPOLATION_LIMIT_SECONDS,
+ )
+ if is_finite(estimated_transit_seconds)
+ and estimated_transit_seconds >= 0.0
+ else 0.0
+ )
+ _network_target_position = (
+ (parsed["position"] as Vector3)
+ + (parsed["velocity"] as Vector3) * transit_seconds
+ )
_network_target_velocity = parsed["velocity"]
_network_target_visual_yaw = parsed["visual_yaw"]
+ if not Dictionary(parsed.get("animation_state", {})).is_empty():
+ _apply_network_target_animation_state(parsed["animation_state"])
_network_snapshot_age = 0.0
_apply_network_casting(bool(parsed["casting"]))
_set_sitting(bool(parsed["sitting"]))
@@ -1270,7 +2428,13 @@ func push_network_snapshot(snapshot: Dictionary) -> void:
_network_snapshot_ready = true
-func apply_local_prediction_correction(snapshot: Dictionary) -> void:
+func apply_local_prediction_correction(
+ snapshot: Dictionary,
+ latest_input_sequence: int = 0,
+ input_interval_seconds: float = 0.0,
+ estimated_transit_seconds: float = -1.0,
+ audit_delta_seconds: float = 0.0,
+) -> void:
var parsed: Dictionary = _parse_network_snapshot(snapshot)
if parsed.is_empty():
return
@@ -1283,16 +2447,274 @@ func apply_local_prediction_correction(snapshot: Dictionary) -> void:
if sitting_intent_acknowledged:
_sitting_intent_pending = false
_sitting_intent_sequence = -1
+ if (
+ _local_network_jump_intent_pending
+ and _local_network_jump_intent_sequence >= 0
+ and acknowledged_input >= _local_network_jump_intent_sequence
+ ):
+ _clear_local_network_jump_intent()
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
+ var transit_seconds: float = resolve_local_prediction_transit_seconds(
+ acknowledged_input,
+ latest_input_sequence,
+ input_interval_seconds,
+ estimated_transit_seconds,
)
- if error_distance > 2.0:
+ if transit_seconds > 0.0:
+ authoritative_position += (
+ (parsed["velocity"] as Vector3) * transit_seconds
+ )
+ var error_offset: Vector3 = authoritative_position - global_position
+ var error_distance: float = error_offset.length()
+ _local_prediction_largest_error = maxf(
+ _local_prediction_largest_error,
+ error_distance,
+ )
+ if error_distance <= LOCAL_PREDICTION_CORRECTION_THRESHOLD:
+ _reset_local_prediction_error()
+ return
+ if error_distance >= LOCAL_PREDICTION_SNAP_DISTANCE:
+ _clear_local_reconciliation_offsets()
global_position = authoritative_position
- elif error_distance > 0.05:
- global_position = global_position.lerp(authoritative_position, 0.18)
+ velocity = parsed["velocity"]
+ _local_prediction_hard_corrections += 1
+ _reset_local_prediction_error()
+ return
+ if _should_defer_local_prediction_correction(parsed):
+ # A host and its client do not reach the apex or landing on the same
+ # frame. Correcting that expected vertical disagreement while either
+ # side is airborne can pull the local body off narrow platforms.
+ _reset_local_prediction_error()
+ return
+ # Both simulations agree the player is standing. Let ordinary floor motion
+ # and gravity resolve the vertical component; a stale height sample must not
+ # press a locally controlled player down or turn a horizontal correction
+ # into an early floor collision.
+ error_offset.y = 0.0
+ error_distance = error_offset.length()
+ if error_distance <= LOCAL_PREDICTION_CORRECTION_THRESHOLD:
+ _reset_local_prediction_error()
+ return
+ var error_direction: Vector3 = error_offset.normalized()
+ if (
+ not _local_prediction_error_direction.is_zero_approx()
+ and _local_prediction_error_direction.dot(error_direction) < 0.5
+ ):
+ _reset_local_prediction_error()
+ _local_prediction_error_direction = error_direction
+ _local_prediction_error_audits += 1
+ _local_prediction_error_seconds += clampf(
+ audit_delta_seconds,
+ 0.0,
+ LOCAL_PREDICTION_MAX_AUDIT_DELTA_SECONDS,
+ )
+ if (
+ _local_prediction_error_audits
+ < LOCAL_PREDICTION_MIN_CORRECTION_AUDITS
+ or _local_prediction_error_seconds
+ < LOCAL_PREDICTION_CORRECTION_DELAY_SECONDS
+ ):
+ return
+ var correction_weight: float = 1.0 - exp(
+ -LOCAL_PREDICTION_SOFT_CORRECTION_RATE
+ * clampf(
+ audit_delta_seconds,
+ 0.0,
+ LOCAL_PREDICTION_MAX_AUDIT_DELTA_SECONDS,
+ )
+ )
+ var correction: Vector3 = error_offset * correction_weight
+ if correction.length() > LOCAL_PREDICTION_MAX_SOFT_CORRECTION_STEP:
+ correction = (
+ correction.normalized()
+ * LOCAL_PREDICTION_MAX_SOFT_CORRECTION_STEP
+ )
+ if _apply_camera_safe_local_correction(correction):
+ _local_prediction_soft_corrections += 1
+
+
+func _should_defer_local_prediction_correction(parsed: Dictionary) -> bool:
+ if not bool(parsed.get("grounded", true)):
+ return true
+ return _local_network_jump_intent_pending or not is_on_floor()
+
+
+func _apply_camera_safe_local_correction(correction: Vector3) -> bool:
+ if correction.is_zero_approx():
+ return false
+ var previous_visual_position: Vector3 = _visuals.global_position
+ var previous_camera_position: Vector3 = _camera_yaw.global_position
+ var previous_body_position: Vector3 = global_position
+ var base_visual_local_position: Vector3 = (
+ _visuals.position - _local_reconciliation_visual_offset
+ )
+ var base_camera_local_position: Vector3 = (
+ _camera_yaw.position - _local_reconciliation_camera_offset
+ )
+ # Reconciliation is still physical movement. Going through the physics
+ # server prevents a delayed host audit from placing the capsule through a
+ # tree, cliff, or stepping stone on the way to the authoritative position.
+ move_and_collide(correction)
+ if global_position.is_equal_approx(previous_body_position):
+ return false
+ _visuals.global_position = previous_visual_position
+ _camera_yaw.global_position = previous_camera_position
+ _local_reconciliation_visual_offset = (
+ _visuals.position - base_visual_local_position
+ )
+ _local_reconciliation_camera_offset = (
+ _camera_yaw.position - base_camera_local_position
+ )
+ return true
+
+
+func _reset_local_prediction_error() -> void:
+ _local_prediction_error_seconds = 0.0
+ _local_prediction_error_audits = 0
+ _local_prediction_error_direction = Vector3.ZERO
+
+
+func _clear_local_reconciliation_offsets() -> void:
+ if not _local_reconciliation_visual_offset.is_zero_approx():
+ _visuals.position -= _local_reconciliation_visual_offset
+ if not _local_reconciliation_camera_offset.is_zero_approx():
+ _camera_yaw.position -= _local_reconciliation_camera_offset
+ _local_reconciliation_visual_offset = Vector3.ZERO
+ _local_reconciliation_camera_offset = Vector3.ZERO
+
+
+func _update_local_reconciliation_visuals(delta: float) -> void:
+ if (
+ _local_reconciliation_visual_offset.is_zero_approx()
+ and _local_reconciliation_camera_offset.is_zero_approx()
+ ):
+ _local_reconciliation_visual_offset = Vector3.ZERO
+ _local_reconciliation_camera_offset = Vector3.ZERO
+ return
+ var retained_ratio: float = exp(
+ -LOCAL_RECONCILIATION_PRESENTATION_RECENTER_RATE * delta
+ )
+ var retained_visual_offset: Vector3 = (
+ _local_reconciliation_visual_offset * retained_ratio
+ )
+ var retained_camera_offset: Vector3 = (
+ _local_reconciliation_camera_offset * retained_ratio
+ )
+ _visuals.position += (
+ retained_visual_offset - _local_reconciliation_visual_offset
+ )
+ _camera_yaw.position += (
+ retained_camera_offset - _local_reconciliation_camera_offset
+ )
+ _local_reconciliation_visual_offset = retained_visual_offset
+ _local_reconciliation_camera_offset = retained_camera_offset
+
+
+func get_local_prediction_metrics() -> Dictionary:
+ return {
+ "soft_corrections": _local_prediction_soft_corrections,
+ "hard_corrections": _local_prediction_hard_corrections,
+ "largest_error": _local_prediction_largest_error,
+ "out_of_bounds_audits": _local_prediction_error_audits,
+ "out_of_bounds_seconds": _local_prediction_error_seconds,
+ }
+
+
+static func resolve_network_input_stale_timeout_seconds(
+ round_trip_msec: int,
+) -> float:
+ if round_trip_msec < 0:
+ return NETWORK_INPUT_STALE_TIMEOUT_SECONDS
+ return clampf(
+ NETWORK_INPUT_STALE_TIMEOUT_SECONDS
+ + float(round_trip_msec) / 1000.0
+ * NETWORK_INPUT_STALE_RTT_MULTIPLIER,
+ NETWORK_INPUT_STALE_TIMEOUT_SECONDS,
+ NETWORK_INPUT_STALE_TIMEOUT_MAX_SECONDS,
+ )
+
+
+func _record_authoritative_movement_state() -> void:
+ var now_seconds: float = Time.get_ticks_msec() / 1000.0
+ _authoritative_movement_history.append({
+ "sequence": _last_network_input_sequence,
+ "recorded_at": now_seconds,
+ "position": global_position,
+ "velocity": velocity,
+ "cast_origin": get_cast_origin_position(),
+ "facing": get_facing_direction(),
+ "sneaking": is_sneaking(),
+ })
+ var oldest_allowed: float = now_seconds - NETWORK_MOVEMENT_HISTORY_SECONDS
+ while (
+ not _authoritative_movement_history.is_empty()
+ and float(
+ _authoritative_movement_history[0].get("recorded_at", 0.0)
+ ) < oldest_allowed
+ ):
+ _authoritative_movement_history.pop_front()
+
+
+func get_lag_compensated_movement_state(
+ input_sequence: int,
+ max_age_seconds: float = NETWORK_MAX_LAG_COMPENSATION_SECONDS,
+) -> Dictionary:
+ var now_seconds: float = Time.get_ticks_msec() / 1000.0
+ for index: int in range(
+ _authoritative_movement_history.size() - 1,
+ -1,
+ -1,
+ ):
+ var candidate: Dictionary = _authoritative_movement_history[index]
+ if now_seconds - float(candidate.get("recorded_at", 0.0)) > (
+ max_age_seconds
+ ):
+ break
+ if (
+ input_sequence <= 0
+ or int(candidate.get("sequence", 0)) <= input_sequence
+ ):
+ return candidate.duplicate(true)
+ return {
+ "sequence": _last_network_input_sequence,
+ "recorded_at": now_seconds,
+ "position": global_position,
+ "velocity": velocity,
+ "cast_origin": get_cast_origin_position(),
+ "facing": get_facing_direction(),
+ "sneaking": is_sneaking(),
+ }
+
+
+static func resolve_local_prediction_transit_seconds(
+ acknowledged_input: int,
+ latest_input_sequence: int,
+ input_interval_seconds: float,
+ estimated_transit_seconds: float,
+) -> float:
+ if is_finite(estimated_transit_seconds) and estimated_transit_seconds >= 0.0:
+ return minf(
+ estimated_transit_seconds,
+ LOCAL_PREDICTION_EXTRAPOLATION_LIMIT_SECONDS,
+ )
+ if (
+ acknowledged_input <= 0
+ or latest_input_sequence <= acknowledged_input
+ or input_interval_seconds <= 0.0
+ ):
+ return 0.0
+ # The input gap spans approximately the full round trip: from the input
+ # acknowledged by the host to the newest input at snapshot receipt. Only
+ # half of that interval lies between the host snapshot and the client now.
+ var sequence_gap: int = latest_input_sequence - acknowledged_input
+ return minf(
+ float(sequence_gap)
+ * input_interval_seconds
+ * LOCAL_PREDICTION_FALLBACK_TRANSIT_RATIO,
+ LOCAL_PREDICTION_EXTRAPOLATION_LIMIT_SECONDS,
+ )
func apply_network_teleport(snapshot: Dictionary) -> void:
@@ -1307,6 +2729,8 @@ func apply_network_teleport(snapshot: Dictionary) -> void:
_network_target_position = global_position
_network_target_velocity = velocity
_network_target_visual_yaw = _visuals.rotation.y
+ if not Dictionary(parsed.get("animation_state", {})).is_empty():
+ _apply_network_target_animation_state(parsed["animation_state"])
_network_snapshot_age = 0.0
_network_snapshot_ready = true
@@ -1332,6 +2756,16 @@ 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: Dictionary = {}
+ if snapshot.has("animation_state"):
+ var animation_state_value: Variant = snapshot.get("animation_state")
+ if not NetworkPlayerAnimationProtocol.validate_state(
+ animation_state_value
+ ):
+ return {}
+ animation_state = (
+ animation_state_value as Dictionary
+ ).duplicate(true)
if (
snapshot.has("acknowledged_input")
and (
@@ -1349,6 +2783,9 @@ func _parse_network_snapshot(snapshot: Dictionary) -> Dictionary:
if snapshot.has("casting") and typeof(snapshot.get("casting")) != TYPE_BOOL:
return {}
var casting: bool = bool(snapshot.get("casting", false))
+ if snapshot.has("grounded") and typeof(snapshot.get("grounded")) != TYPE_BOOL:
+ return {}
+ var grounded: bool = bool(snapshot.get("grounded", true))
if (
not parsed_position.is_finite()
or not parsed_velocity.is_finite()
@@ -1359,12 +2796,170 @@ func _parse_network_snapshot(snapshot: Dictionary) -> Dictionary:
"position": parsed_position,
"velocity": parsed_velocity,
"visual_yaw": visual_yaw,
+ "animation_state": animation_state,
+ "grounded": grounded,
"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,
+ _animation_action_paused,
+ )
+
+
+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"]
+ var incoming_sequence: int = int(action["sequence"])
+ var wrapped_sequence: bool = (
+ _network_target_animation_action_sequence
+ == NetworkPlayerAnimationProtocol.MAX_ACTION_SEQUENCE
+ and incoming_sequence == 1
+ )
+ if (
+ incoming_sequence < _network_target_animation_action_sequence
+ and not wrapped_sequence
+ ):
+ return
+ if (
+ incoming_sequence == _network_target_animation_action_sequence
+ and StringName(str(action["id"]))
+ != _network_target_animation_action_id
+ ):
+ return
+ _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"])
+ _network_target_animation_action_paused = bool(
+ action.get("paused", false)
+ )
+
+
+func _get_authoritative_locomotion_state() -> LocomotionState:
+ if (
+ _network_authoritative_simulation
+ and _is_movement_input_enabled()
+ and not _water_recovery_active
+ and not _sitting
+ ):
+ if _network_sneak:
+ return LocomotionState.SNEAKING
+ if _network_axis.length_squared() <= 0.0025:
+ return LocomotionState.IDLE
+ return (
+ LocomotionState.RUNNING
+ if _network_sprint
+ else LocomotionState.WALKING
+ )
+ return _locomotion_state_from_velocity(is_sneaking())
+
+
+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(is_sneaking())
+
+
+func _is_presented_moving_horizontally() -> bool:
+ var presented_velocity: Vector3 = (
+ _network_target_velocity
+ if _network_interpolation_enabled and _network_snapshot_ready
+ else velocity
+ )
+ return Vector2(
+ presented_velocity.x,
+ presented_velocity.z,
+ ).length_squared() > 0.0025
+
+
+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
+ + (
+ 0.0
+ if _network_target_animation_action_paused
+ else _network_snapshot_age
+ ),
+ NetworkPlayerAnimationProtocol.MAX_ACTION_ELAPSED_SECONDS,
+ ),
+ _network_target_animation_action_paused,
+ )
+ return _make_animation_action_state()
+
+
+func _locomotion_id_from_state(state: LocomotionState) -> StringName:
+ match state:
+ LocomotionState.SNEAKING:
+ return NetworkPlayerAnimationProtocol.LOCOMOTION_SNEAKING
+ 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_SNEAKING:
+ return LocomotionState.SNEAKING
+ 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(
+ sneaking: bool = false,
+) -> LocomotionState:
+ if sneaking:
+ return LocomotionState.SNEAKING
+ 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:
@@ -1376,20 +2971,38 @@ func _apply_network_casting(casting: bool) -> void:
func _update_network_interpolation(delta: float) -> void:
if not _network_snapshot_ready:
return
- _network_snapshot_age = minf(_network_snapshot_age + delta, 0.1)
+ var previous_snapshot_age: float = _network_snapshot_age
+ _network_snapshot_age = minf(
+ _network_snapshot_age + delta,
+ NETWORK_EXTRAPOLATION_LIMIT_SECONDS,
+ )
+ var extrapolation_delta: float = (
+ _network_snapshot_age - previous_snapshot_age
+ )
var predicted_position: Vector3 = (
_network_target_position
+ _network_target_velocity * _network_snapshot_age
)
var projected_position: Vector3 = (
- global_position + _network_target_velocity * delta
+ global_position + _network_target_velocity * extrapolation_delta
)
if projected_position.distance_to(predicted_position) > 2.0:
global_position = predicted_position
else:
+ var jitter_ratio: float = clampf(
+ _network_snapshot_jitter
+ / NETWORK_SNAPSHOT_JITTER_LIMIT_SECONDS,
+ 0.0,
+ 1.0,
+ )
+ var smoothing_rate: float = lerpf(
+ NETWORK_REMOTE_SMOOTHING_RATE,
+ NETWORK_REMOTE_JITTER_SMOOTHING_RATE,
+ jitter_ratio,
+ )
global_position = projected_position.lerp(
predicted_position,
- 1.0 - exp(-10.0 * delta)
+ 1.0 - exp(-smoothing_rate * delta)
)
velocity = _network_target_velocity
_visuals.rotation.y = lerp_angle(
@@ -1399,10 +3012,53 @@ func _update_network_interpolation(delta: float) -> void:
)
+func _update_network_input_freshness(delta: float) -> void:
+ _network_input_age = minf(
+ _network_input_age + delta,
+ _network_input_stale_timeout_seconds + 1.0,
+ )
+ if (
+ _network_input_stale
+ or _network_input_age <= _network_input_stale_timeout_seconds
+ ):
+ return
+ _network_input_stale = true
+ _network_axis = Vector2.ZERO
+ _network_jump_pending = false
+ _network_sprint = false
+ _network_sneak = false
+ _network_slow_walk = false
+
+
+func _queue_local_network_jump_intent() -> void:
+ if _local_network_jump_intent_pending:
+ return
+ _local_network_jump_intent_pending = true
+ _local_network_jump_intent_sequence = -1
+
+
+func _clear_local_network_jump_intent() -> void:
+ _local_network_jump_intent_pending = false
+ _local_network_jump_intent_sequence = -1
+
+
func is_local_control_enabled() -> bool:
return local_control_enabled
+func is_sneaking() -> bool:
+ if local_control_enabled:
+ return (
+ _is_movement_input_enabled()
+ and Input.is_action_pressed("sneak")
+ )
+ return _network_sneak
+
+
+func is_moving_horizontally() -> bool:
+ return Vector2(velocity.x, velocity.z).length_squared() > 0.01
+
+
func set_movement_enabled(enabled: bool) -> void:
_movement_enabled = enabled
if not enabled:
@@ -1431,7 +3087,11 @@ func _is_movement_input_enabled() -> bool:
func _is_camera_input_enabled() -> bool:
- return _camera_input_enabled and _local_input_suppressors.is_empty()
+ return (
+ _camera_input_enabled
+ and _local_input_suppressors.is_empty()
+ and _camera_input_suppressors.is_empty()
+ )
func set_camera_input_enabled(enabled: bool) -> void:
@@ -1440,6 +3100,19 @@ func set_camera_input_enabled(enabled: bool) -> void:
_set_camera_dragging(false)
+func set_camera_input_suppressed(
+ owner: StringName,
+ suppressed: bool,
+) -> void:
+ if owner.is_empty():
+ return
+ if suppressed:
+ _camera_input_suppressors[owner] = true
+ _set_camera_dragging(false)
+ else:
+ _camera_input_suppressors.erase(owner)
+
+
func set_camera_active(active: bool) -> void:
if _free_camera_active and _free_camera != null:
_free_camera.current = active
@@ -1447,10 +3120,24 @@ func set_camera_active(active: bool) -> void:
_camera.current = active
+func face_world_direction(direction: Vector3) -> void:
+ var horizontal_direction := Vector3(direction.x, 0.0, direction.z)
+ if horizontal_direction.is_zero_approx() or _visuals == null:
+ return
+ horizontal_direction = horizontal_direction.normalized()
+ var target_world_yaw: float = atan2(
+ -horizontal_direction.x,
+ -horizontal_direction.z,
+ )
+ _visuals.rotation.y = target_world_yaw - global_rotation.y
+ _network_target_visual_yaw = _visuals.rotation.y
+
+
func apply_camera_settings(
new_mouse_sensitivity: float,
new_controller_sensitivity: float,
invert_vertical: bool,
+ swap_hotbar_camera_scroll: bool = false,
) -> void:
mouse_sensitivity = clampf(new_mouse_sensitivity, 0.001, 0.012)
controller_camera_speed = clampf(
@@ -1459,6 +3146,50 @@ func apply_camera_settings(
5.0
)
invert_camera_y = invert_vertical
+ _swap_hotbar_camera_scroll = swap_hotbar_camera_scroll
+
+
+func set_rv_interior_camera_mode(
+ enabled: bool,
+ excluded_wall_rids: Array[RID] = [],
+) -> void:
+ if enabled != _rv_interior_camera_mode:
+ if enabled:
+ _rv_previous_camera_environment = _camera.environment
+ _camera.environment = _get_rv_interior_environment()
+ if _free_camera != null:
+ _free_camera.environment = _camera.environment
+ else:
+ _camera.environment = _rv_previous_camera_environment
+ if _free_camera != null:
+ _free_camera.environment = _camera.environment
+ _rv_previous_camera_environment = null
+ _rv_interior_camera_mode = enabled
+ if _spring_arm == null:
+ return
+ _spring_arm.clear_excluded_objects()
+ if enabled:
+ for collider_rid: RID in excluded_wall_rids:
+ if collider_rid.is_valid():
+ _spring_arm.add_excluded_object(collider_rid)
+ _set_target_zoom(_target_zoom)
+
+
+func _get_rv_interior_environment() -> Environment:
+ if _rv_interior_environment != null:
+ return _rv_interior_environment
+ _rv_interior_environment = Environment.new()
+ _rv_interior_environment.background_mode = Environment.BG_COLOR
+ _rv_interior_environment.background_color = Color("020506")
+ _rv_interior_environment.ambient_light_source = (
+ Environment.AMBIENT_SOURCE_COLOR
+ )
+ _rv_interior_environment.ambient_light_color = Color("d6caaa")
+ _rv_interior_environment.ambient_light_energy = 1.0
+ _rv_interior_environment.reflected_light_source = (
+ Environment.REFLECTION_SOURCE_DISABLED
+ )
+ return _rv_interior_environment
func is_camera_input_enabled() -> bool:
@@ -1551,6 +3282,90 @@ func get_fishing_rod() -> Node3D:
return _fishing_rod
+func set_active_fishing_rod(
+ rod: FishingRodDataType,
+ animate_transition: bool = false,
+) -> void:
+ _apply_fishing_rod_model(rod)
+ set_active_item_is_rod(rod != null and rod.is_available(), animate_transition)
+
+
+func set_active_catching_net(should_show: bool) -> void:
+ _active_item_is_net = should_show
+ if not should_show:
+ cancel_net_action_visual()
+ if _catching_net == null:
+ return
+ if _showcase_rod_state_stored:
+ _showcase_net_visibility = should_show
+ return
+ _catching_net.visible = (
+ should_show and not _has_held_show_item() and not _showcase_animation_active
+ )
+
+
+func set_active_shovel(should_show: bool) -> void:
+ _active_item_is_shovel = should_show
+ if not should_show:
+ cancel_net_action_visual()
+ if _shovel == null:
+ return
+ if _showcase_rod_state_stored:
+ _showcase_shovel_visibility = should_show
+ return
+ _shovel.visible = (
+ should_show and not _has_held_show_item() and not _showcase_animation_active
+ )
+
+
+func set_active_decor_package(should_show: bool) -> void:
+ var changed: bool = _held_decor_package_visible != should_show
+ _held_decor_package_visible = should_show
+ if _held_decor_package_display != null:
+ _held_decor_package_display.visible = should_show
+ if should_show:
+ _fishing_rod.visible = false
+ if _catching_net != null:
+ _catching_net.visible = false
+ if _shovel != null:
+ _shovel.visible = false
+ elif changed:
+ _apply_active_rod_visibility()
+ if changed:
+ _update_character_animation()
+
+
+func _apply_fishing_rod_model(rod: FishingRodDataType) -> void:
+ var next_id: StringName = rod.item_id if rod != null else StringName()
+ if next_id == _active_fishing_rod_id:
+ return
+ _active_fishing_rod_id = next_id
+ if _custom_fishing_rod_visual != null:
+ _custom_fishing_rod_visual.hide()
+ _custom_fishing_rod_visual.queue_free()
+ _custom_fishing_rod_visual = null
+ if _fishing_rod_fallback_visual == null or _fishing_rod_tip == null:
+ return
+ _fishing_rod_fallback_visual.show()
+ _fishing_rod_tip.position = Vector3(0.0, 1.1, 0.0)
+ if rod == null:
+ return
+ _fishing_rod_tip.position = rod.rod_tip_position
+ if rod.rod_model_scene == null or _fishing_rod_model_mount == null:
+ return
+ var instance := rod.rod_model_scene.instantiate() as Node3D
+ if instance == null:
+ push_warning(
+ "Rod %s model root is not Node3D; using fallback."
+ % rod.item_id
+ )
+ return
+ _custom_fishing_rod_visual = instance
+ _fishing_rod_model_mount.add_child(instance)
+ instance.transform = rod.rod_model_transform
+ _fishing_rod_fallback_visual.hide()
+
+
func set_active_item_is_rod(
active_is_rod: bool,
animate_transition: bool = false,
@@ -1582,6 +3397,18 @@ func _apply_active_rod_visibility() -> void:
_showcase_rod_visibility = _active_item_is_rod
return
_fishing_rod.visible = _active_item_is_rod
+ if _catching_net != null:
+ _catching_net.visible = (
+ _active_item_is_net
+ and not _has_held_show_item()
+ and not _showcase_animation_active
+ )
+ if _shovel != null:
+ _shovel.visible = (
+ _active_item_is_shovel
+ and not _has_held_show_item()
+ and not _showcase_animation_active
+ )
func set_active_art_kit(icon: Texture2D, should_show: bool) -> void:
@@ -1607,15 +3434,24 @@ func set_active_art_kit(icon: Texture2D, should_show: bool) -> void:
_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 _catching_net != null:
+ _catching_net.visible = false
+ if _shovel != null:
+ _shovel.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()
@@ -1645,14 +3481,17 @@ func set_held_fish(
)
_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
+ _pending_held_fish_scale = _get_catch_presentation_scale(
+ fish,
+ display_scale,
)
if not previous_show_pose:
_held_fish_display.visible = false
_fishing_rod.visible = false
+ if _catching_net != null:
+ _catching_net.visible = false
+ if _shovel != null:
+ _shovel.visible = false
if item_changed:
_begin_pocket_visual(
PocketVisualTarget.HELD_FISH,
@@ -1668,7 +3507,52 @@ func set_held_fish(
func _has_held_show_item() -> bool:
- return _held_fish_visible or _held_art_kit_visible
+ return (
+ _held_fish_visible
+ or _held_art_kit_visible
+ or _held_decor_package_visible
+ )
+
+
+func _build_decor_package_display(parent: Node3D) -> Node3D:
+ var display := Node3D.new()
+ display.name = "HeldDecorPackageDisplay"
+ display.position = Vector3(0.0, 0.1, 0.04)
+ display.rotation = Vector3(0.15, 0.0, -0.55)
+ display.visible = false
+ parent.add_child(display)
+
+ var sack := MeshInstance3D.new()
+ sack.name = "Sack"
+ var sack_mesh := SphereMesh.new()
+ sack_mesh.radius = 0.22
+ sack_mesh.height = 0.48
+ sack_mesh.radial_segments = 8
+ sack_mesh.rings = 4
+ sack_mesh.material = _decor_package_material(Color("a87843"))
+ sack.mesh = sack_mesh
+ display.add_child(sack)
+
+ var tie := MeshInstance3D.new()
+ tie.name = "Tie"
+ var tie_mesh := CylinderMesh.new()
+ tie_mesh.top_radius = 0.1
+ tie_mesh.bottom_radius = 0.12
+ tie_mesh.height = 0.08
+ tie_mesh.radial_segments = 8
+ tie_mesh.material = _decor_package_material(Color("513829"))
+ tie.mesh = tie_mesh
+ tie.position = Vector3(0.0, 0.22, 0.0)
+ display.add_child(tie)
+ return display
+
+
+func _decor_package_material(color: Color) -> StandardMaterial3D:
+ var material := StandardMaterial3D.new()
+ material.albedo_color = color
+ material.roughness = 1.0
+ material.shading_mode = BaseMaterial3D.SHADING_MODE_PER_VERTEX
+ return material
func _clear_held_fish() -> void:
@@ -1695,6 +3579,7 @@ func _apply_pending_held_fish() -> void:
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:
@@ -1704,6 +3589,12 @@ func _clear_active_art_kit() -> void:
_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,
@@ -1856,8 +3747,21 @@ 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 _is_net_strike_visual_active():
+ _pending_net_showcase_catch = fish_catch
+ _pending_net_showcase_remote = false
+ return
+ _begin_catch_showcase_now(fish_catch, showcase_camera_yaw_offset)
+
+
+func _begin_catch_showcase_now(
+ fish_catch: FishCatchType,
+ camera_yaw_offset_degrees: float,
+) -> void:
if _pocket_visual_target == PocketVisualTarget.CATCH_SHOWCASE:
_cancel_pocket_visual()
+ if local_control_enabled:
+ set_local_input_suppressed(CATCH_SHOWCASE_INPUT_OWNER, true)
_showcase_animation_active = true
set_fishing_visual(false)
_kill_showcase_camera_tween()
@@ -1866,17 +3770,28 @@ func begin_catch_showcase(fish_catch: FishCatchType) -> void:
if not _showcase_visual_rotation_stored:
_showcase_visual_rotation = _visuals.rotation
_showcase_visual_rotation_stored = true
- var showcase_camera_position: Vector3 = _begin_showcase_camera_transition()
+ var showcase_camera_position: Vector3 = _begin_showcase_camera_transition(
+ camera_yaw_offset_degrees
+ )
_turn_showcase_toward_position(showcase_camera_position)
if not _showcase_rod_state_stored:
_showcase_rod_visibility = _fishing_rod.visible
+ _showcase_net_visibility = (
+ _catching_net.visible if _catching_net != null else false
+ )
+ _showcase_shovel_visibility = (
+ _shovel.visible if _shovel != null else false
+ )
_showcase_rod_state_stored = true
_fishing_rod.visible = false
+ if _catching_net != null:
+ _catching_net.visible = false
+ if _shovel != null:
+ _shovel.visible = false
_catch_sprite.texture = fish_catch.fish.display_texture
- _catch_display.scale = (
- Vector3.ONE
- * fish_catch.display_scale
- * catch_presentation_base_scale
+ _catch_display.scale = _get_catch_presentation_scale(
+ fish_catch.fish,
+ fish_catch.display_scale,
)
_catch_display.visible = _catch_sprite.texture != null
_update_character_animation()
@@ -1885,22 +3800,95 @@ func begin_catch_showcase(fish_catch: FishCatchType) -> void:
func begin_remote_catch_showcase(fish_catch: FishCatchType) -> void:
if local_control_enabled or fish_catch == null or not fish_catch.is_valid():
return
+ if _is_net_strike_visual_active():
+ _pending_net_showcase_catch = fish_catch
+ _pending_net_showcase_remote = true
+ return
+ _begin_remote_catch_showcase_now(fish_catch)
+
+
+func _begin_remote_catch_showcase_now(fish_catch: FishCatchType) -> void:
end_catch_showcase(Callable(), true)
_showcase_animation_active = true
set_fishing_visual(false)
_showcase_rod_visibility = _fishing_rod.visible
+ _showcase_net_visibility = (
+ _catching_net.visible if _catching_net != null else false
+ )
+ _showcase_shovel_visibility = (
+ _shovel.visible if _shovel != null else false
+ )
_showcase_rod_state_stored = true
_fishing_rod.visible = false
+ if _catching_net != null:
+ _catching_net.visible = false
+ if _shovel != null:
+ _shovel.visible = false
_catch_sprite.texture = fish_catch.fish.display_texture
- _catch_display.scale = (
- Vector3.ONE
- * fish_catch.display_scale
- * catch_presentation_base_scale
+ _catch_display.scale = _get_catch_presentation_scale(
+ fish_catch.fish,
+ fish_catch.display_scale,
)
_catch_display.visible = _catch_sprite.texture != null
_update_character_animation()
+func _get_catch_presentation_scale(
+ fish: FishDataType,
+ display_scale: float,
+) -> Vector3:
+ var texture: Texture2D = fish.display_texture if fish != null else null
+ return (
+ Vector3.ONE
+ * maxf(display_scale, 0.01)
+ * catch_presentation_base_scale
+ * get_catch_texture_resolution_scale(texture)
+ )
+
+
+static func get_catch_texture_resolution_scale(texture: Texture2D) -> float:
+ if texture == null:
+ return 1.0
+ var texture_size: Vector2 = texture.get_size()
+ var longest_side: float = maxf(texture_size.x, texture_size.y)
+ if not is_finite(longest_side) or longest_side <= 0.0:
+ return 1.0
+ return clampf(
+ CATCH_PRESENTATION_REFERENCE_LONG_SIDE_PX / longest_side,
+ CATCH_PRESENTATION_MIN_TEXTURE_SCALE,
+ CATCH_PRESENTATION_MAX_TEXTURE_SCALE,
+ )
+
+
+func _is_net_strike_visual_active() -> bool:
+ return (
+ _animation_action_id == CHARACTER_NET_STRIKE_ANIMATION
+ or _network_target_animation_action_id
+ == CHARACTER_NET_STRIKE_ANIMATION
+ or (
+ _character_animation_player != null
+ and _character_animation_player.current_animation
+ == CHARACTER_NET_STRIKE_ANIMATION
+ )
+ )
+
+
+func _begin_pending_net_showcase() -> void:
+ var fish_catch := _pending_net_showcase_catch
+ var remote: bool = _pending_net_showcase_remote
+ _pending_net_showcase_catch = null
+ _pending_net_showcase_remote = false
+ if fish_catch == null or not fish_catch.is_valid():
+ return
+ if remote:
+ _begin_remote_catch_showcase_now(fish_catch)
+ else:
+ _begin_catch_showcase_now(
+ fish_catch,
+ net_showcase_camera_yaw_offset,
+ )
+
+
func end_catch_showcase(
restored_callback: Callable = Callable(),
immediate: bool = false,
@@ -1930,12 +3918,20 @@ func end_catch_showcase(
_catch_sprite.texture = null
if _showcase_rod_state_stored:
_fishing_rod.visible = _showcase_rod_visibility
+ if _catching_net != null:
+ _catching_net.visible = _showcase_net_visibility
+ if _shovel != null:
+ _shovel.visible = _showcase_shovel_visibility
_showcase_rod_visibility = true
+ _showcase_net_visibility = false
+ _showcase_shovel_visibility = false
_showcase_rod_state_stored = false
if (
not _showcase_visual_rotation_stored
and _showcase_camera_snapshot == null
):
+ if local_control_enabled:
+ set_local_input_suppressed(CATCH_SHOWCASE_INPUT_OWNER, false)
if restored_callback.is_valid():
restored_callback.call()
return
@@ -2089,14 +4085,13 @@ func _complete_showcase_restore(
_camera_pitch.rotation = _showcase_camera_snapshot.pitch_rotation
_spring_arm.spring_length = _showcase_camera_snapshot.spring_length
_target_zoom = _showcase_camera_snapshot.target_zoom
- _camera_input_enabled = (
- _showcase_camera_snapshot.camera_input_enabled
- )
_set_camera_dragging(
_showcase_camera_snapshot.camera_dragging
and Input.is_action_pressed("camera_drag")
)
_showcase_camera_snapshot = null
+ if local_control_enabled:
+ set_local_input_suppressed(CATCH_SHOWCASE_INPUT_OWNER, false)
if restored_callback.is_valid():
restored_callback.call()
@@ -2110,16 +4105,16 @@ func _capture_showcase_camera_snapshot() -> void:
_showcase_camera_snapshot.pitch_rotation = _camera_pitch.rotation
_showcase_camera_snapshot.spring_length = _spring_arm.spring_length
_showcase_camera_snapshot.target_zoom = _target_zoom
- _showcase_camera_snapshot.camera_input_enabled = _camera_input_enabled
_showcase_camera_snapshot.camera_dragging = _camera_dragging
- _camera_input_enabled = false
_set_camera_dragging(false)
-func _begin_showcase_camera_transition() -> Vector3:
+func _begin_showcase_camera_transition(
+ camera_yaw_offset_degrees: float,
+) -> Vector3:
var target_world_yaw: float = (
_visuals.global_rotation.y
- + deg_to_rad(showcase_camera_yaw_offset)
+ + deg_to_rad(camera_yaw_offset_degrees)
)
var target_local_yaw: float = target_world_yaw - global_rotation.y
var shortest_target_yaw: float = (
diff --git a/player/player.tscn b/player/player.tscn
index b0ad70c..3288d2d 100644
--- a/player/player.tscn
+++ b/player/player.tscn
@@ -1,4 +1,4 @@
-[gd_scene load_steps=21 format=3]
+[gd_scene load_steps=23 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"]
@@ -12,9 +12,11 @@
[ext_resource type="Script" path="res://progression/player_cooler_capacity.gd" id="10_capacity"]
[ext_resource type="Script" path="res://player/player_ground_shadow.gd" id="11_ground_shadow"]
[ext_resource type="Material" path="res://player/materials/player_blob_shadow.tres" id="12_blob_shadow"]
-[ext_resource type="PackedScene" path="res://art/exported/characters/base/netfishing_base_character.glb" id="13_character"]
+[ext_resource type="PackedScene" path="res://art/exported/characters/base/straywild_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"]
+[ext_resource type="Script" path="res://inventory/player_inventory_layout.gd" id="17_inventory_layout"]
[sub_resource type="CapsuleShape3D" id="PlayerShape"]
radius = 0.45
@@ -25,7 +27,7 @@ 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 vec4 dust_color : source_color = vec4(0.47, 0.39, 0.27, 0.42);\nvarying float particle_phase;\n\nvoid vertex() {\n\tparticle_phase = INSTANCE_CUSTOM.y;\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 = sin(angle * 5.0 + particle_phase * 8.0) * 0.07;\n\tfloat radius = length(centered_uv);\n\tfloat soft_shape = 1.0 - smoothstep(0.48 + edge_wobble, 1.0 + edge_wobble, radius);\n\tfloat fade_in = smoothstep(0.0, 0.12, particle_phase);\n\tfloat fade_out = 1.0 - smoothstep(0.38, 1.0, particle_phase);\n\tALBEDO = dust_color.rgb;\n\tALPHA = soft_shape * dust_color.a * fade_in * fade_out;\n}\n"
+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
@@ -33,7 +35,7 @@ shader = SubResource("SprintDustShader")
[sub_resource type="QuadMesh" id="SprintDustMesh"]
material = SubResource("SprintDustMaterial")
-size = Vector2(0.38, 0.28)
+size = Vector2(1, 1)
[node name="Player" type="CharacterBody3D"]
collision_layer = 2
@@ -66,27 +68,11 @@ rotation_degrees = Vector3(-90, 0, 0)
cast_shadow = 0
mesh = SubResource("GroundShadowMesh")
-[node name="SprintDust" type="CPUParticles3D" parent="."]
+[node name="SprintDust" type="MultiMeshInstance3D" parent="."]
unique_name_in_owner = true
-position = Vector3(0, 0.08, 0)
-emitting = false
-amount = 16
-lifetime = 0.65
-randomness = 0.55
-visibility_aabb = AABB(-4, -2, -4, 8, 5, 8)
-local_coords = false
-emission_shape = 3
-emission_box_extents = Vector3(0.24, 0.03, 0.16)
-direction = Vector3(0, 1, 0)
-spread = 65.0
-gravity = Vector3(0, -0.8, 0)
-initial_velocity_min = 0.35
-initial_velocity_max = 0.8
-angular_velocity_min = -90.0
-angular_velocity_max = 90.0
-scale_amount_min = 0.35
-scale_amount_max = 0.72
-mesh = SubResource("SprintDustMesh")
+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)
@@ -134,6 +120,10 @@ script = ExtResource("9_effects")
unique_name_in_owner = true
script = ExtResource("10_capacity")
+[node name="InventoryLayout" type="Node" parent="."]
+unique_name_in_owner = true
+script = ExtResource("17_inventory_layout")
+
[node name="ArtUnlocks" type="Node" parent="."]
unique_name_in_owner = true
script = ExtResource("14_art_unlocks")
diff --git a/player/player_visual_presenter.gd b/player/player_visual_presenter.gd
index f29cff7..b071b2c 100644
--- a/player/player_visual_presenter.gd
+++ b/player/player_visual_presenter.gd
@@ -2,7 +2,11 @@ class_name PlayerVisualPresenter
extends RefCounted
const PlayerScene: PackedScene = preload("res://player/player.tscn")
-const RUNTIME_MATERIAL_META: StringName = &"netfishing_runtime_material"
+const FurPatternShader: Shader = preload("res://player/fur_pattern.gdshader")
+const RUNTIME_MATERIAL_META: StringName = &"straywild_runtime_material"
+const RUNTIME_FUR_PATTERN_MATERIAL_META: StringName = (
+ &"straywild_runtime_fur_pattern_material"
+)
const EAR_MESH_NAMES: Array[String] = [
"ears_antlers_round",
"ears_bear",
@@ -144,14 +148,17 @@ static func apply_appearance(
var fur_color: Color = CharacterCustomizationCatalog.option_color(
"fur_pattern", str(snapshot.get("fur_pattern", "white"))
)
- _set_mesh_color(skeleton, "body_arms", fur_color)
- _set_mesh_color(skeleton, "body_main", fur_color)
- _set_mesh_color(skeleton, "head_pointy", fur_color)
- _set_mesh_color(skeleton, "head_round", fur_color)
+ 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_mesh_color(skeleton, ear_name, fur_color)
+ _set_fur_mesh(skeleton, ear_name, snapshot, fur_color)
for tail_name: String in TAIL_MESH_NAMES:
- _set_mesh_color(skeleton, tail_name, fur_color)
+ _set_fur_mesh(skeleton, tail_name, snapshot, fur_color)
var eye_id: String = CharacterCustomizationCatalog.canonical_option_id(
"eyes", str(snapshot.get("eyes", "simple_shine"))
@@ -202,17 +209,63 @@ static func _set_feature_texture(
material.albedo_texture = texture
-static func _set_mesh_color(
+static func _set_fur_mesh(
skeleton: Skeleton3D,
mesh_name: String,
- color: Color,
+ snapshot: Dictionary,
+ base_color: Color,
) -> void:
- var mesh_instance: MeshInstance3D = skeleton.get_node_or_null(
- mesh_name
- ) as MeshInstance3D
- var material: StandardMaterial3D = _runtime_material(mesh_instance)
- if material != null:
- material.albedo_color = color
+ var mesh_instance := skeleton.get_node_or_null(mesh_name) as MeshInstance3D
+ if mesh_instance == null:
+ return
+ var style_field := (
+ CharacterCustomizationCatalog.fur_style_field_for_component(mesh_name)
+ )
+ var style_id := CharacterCustomizationCatalog.canonical_option_id(
+ style_field,
+ str(snapshot.get(
+ style_field,
+ 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(
@@ -221,7 +274,9 @@ static func _runtime_material(
if mesh_instance == null or mesh_instance.mesh == null:
return null
if mesh_instance.has_meta(RUNTIME_MATERIAL_META):
- var runtime := mesh_instance.material_override as StandardMaterial3D
+ 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(
@@ -232,13 +287,27 @@ static func _runtime_material(
var material: StandardMaterial3D = source.duplicate() as StandardMaterial3D
_configure_matte_material(material)
mesh_instance.material_override = material
- mesh_instance.set_meta(RUNTIME_MATERIAL_META, true)
+ 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.
diff --git a/player/shovel_attachment.tscn b/player/shovel_attachment.tscn
new file mode 100644
index 0000000..a57dfe5
--- /dev/null
+++ b/player/shovel_attachment.tscn
@@ -0,0 +1,68 @@
+[gd_scene load_steps=8 format=3]
+
+[ext_resource type="Script" path="res://player/tool_socket_attachment.gd" id="1_socket"]
+
+[sub_resource type="StandardMaterial3D" id="HandleMaterial"]
+shading_mode = 0
+albedo_color = Color(0.36, 0.2, 0.1, 1)
+metallic = 0.0
+roughness = 1.0
+
+[sub_resource type="StandardMaterial3D" id="BladeMaterial"]
+shading_mode = 0
+albedo_color = Color(0.34, 0.57, 0.58, 1)
+metallic = 0.0
+roughness = 1.0
+
+[sub_resource type="CylinderMesh" id="ShaftMesh"]
+top_radius = 0.018
+bottom_radius = 0.018
+height = 0.7
+radial_segments = 8
+rings = 1
+material = SubResource("HandleMaterial")
+
+[sub_resource type="BoxMesh" id="BladeMesh"]
+size = Vector3(0.19, 0.25, 0.025)
+material = SubResource("BladeMaterial")
+
+[sub_resource type="BoxMesh" id="GripCrossMesh"]
+size = Vector3(0.18, 0.035, 0.035)
+material = SubResource("HandleMaterial")
+
+[sub_resource type="BoxMesh" id="GripSideMesh"]
+size = Vector3(0.035, 0.13, 0.035)
+material = SubResource("HandleMaterial")
+
+[node name="ShovelAttachment" type="BoneAttachment3D"]
+bone_name = "rod_socket"
+script = ExtResource("1_socket")
+aligned_mount_path = NodePath("Shovel")
+
+[node name="Shovel" type="Node3D" parent="."]
+visible = false
+
+[node name="Shaft" type="MeshInstance3D" parent="Shovel"]
+position = Vector3(0, 0.35, 0)
+cast_shadow = 0
+mesh = SubResource("ShaftMesh")
+
+[node name="Blade" type="MeshInstance3D" parent="Shovel"]
+position = Vector3(0, 0.78, 0)
+cast_shadow = 0
+mesh = SubResource("BladeMesh")
+
+[node name="GripCross" type="MeshInstance3D" parent="Shovel"]
+position = Vector3(0, -0.09, 0)
+cast_shadow = 0
+mesh = SubResource("GripCrossMesh")
+
+[node name="GripLeft" type="MeshInstance3D" parent="Shovel"]
+position = Vector3(-0.0725, -0.035, 0)
+cast_shadow = 0
+mesh = SubResource("GripSideMesh")
+
+[node name="GripRight" type="MeshInstance3D" parent="Shovel"]
+position = Vector3(0.0725, -0.035, 0)
+cast_shadow = 0
+mesh = SubResource("GripSideMesh")
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/player/tool_socket_attachment.gd b/player/tool_socket_attachment.gd
new file mode 100644
index 0000000..4bbd1af
--- /dev/null
+++ b/player/tool_socket_attachment.gd
@@ -0,0 +1,57 @@
+class_name ToolSocketAttachment
+extends BoneAttachment3D
+
+@export var direction_guide_bone: StringName = &"rod_socket.001"
+@export_node_path("Node3D") var aligned_mount_path: NodePath
+
+
+func _ready() -> void:
+ _align_mount_to_direction_guide()
+
+
+func _align_mount_to_direction_guide() -> void:
+ var skeleton := get_parent() as Skeleton3D
+ var mount := get_node_or_null(aligned_mount_path) as Node3D
+ if skeleton == null or mount == null:
+ push_error("Tool socket alignment requires a Skeleton3D parent and mount.")
+ return
+ var socket_index := skeleton.find_bone(bone_name)
+ var guide_index := skeleton.find_bone(direction_guide_bone)
+ if socket_index < 0 or guide_index < 0:
+ push_error(
+ "Tool socket alignment is missing %s or %s."
+ % [bone_name, direction_guide_bone]
+ )
+ return
+ if skeleton.get_bone_parent(guide_index) != socket_index:
+ push_error(
+ "Tool direction guide %s must be a child of %s."
+ % [direction_guide_bone, bone_name]
+ )
+ return
+ var socket_rest := skeleton.get_bone_global_rest(socket_index)
+ var guide_rest := skeleton.get_bone_global_rest(guide_index)
+ var guide_in_socket := socket_rest.affine_inverse() * guide_rest
+ var rear_direction := guide_in_socket.basis.y.normalized()
+ # rod_socket already aims local +Y from the hand toward the working end.
+ # Its child guide is perpendicular to that axis and identifies which face
+ # of the held tool points rearward. Projecting it onto the roll plane keeps
+ # imperfect authored guides from changing the tool's lengthwise direction.
+ rear_direction -= Vector3.UP * rear_direction.dot(Vector3.UP)
+ if rear_direction.is_zero_approx():
+ push_error("Tool direction guide has no usable direction.")
+ return
+ rear_direction = rear_direction.normalized()
+ var rear_roll := Vector3.BACK.signed_angle_to(
+ rear_direction,
+ Vector3.UP,
+ )
+ var mount_transform := mount.transform
+ mount_transform.basis = (
+ Basis(Vector3.UP, rear_roll).orthonormalized()
+ * mount_transform.basis
+ )
+ # The guide starts at the socket tip, but the held tool must remain anchored
+ # at the socket origin in the hand. Only its authored direction is applied.
+ mount_transform.origin = mount.transform.origin
+ mount.transform = mount_transform
diff --git a/player/tool_socket_attachment.gd.uid b/player/tool_socket_attachment.gd.uid
new file mode 100644
index 0000000..7ddb71c
--- /dev/null
+++ b/player/tool_socket_attachment.gd.uid
@@ -0,0 +1 @@
+uid://toolsocketalignment01
diff --git a/playtest/README-PLAYTEST.txt b/playtest/README-PLAYTEST.txt
deleted file mode 100644
index 47e0a86..0000000
--- a/playtest/README-PLAYTEST.txt
+++ /dev/null
@@ -1,111 +0,0 @@
-NETfishing
-v0.6.5-alpha
-Alpha 0.6.5
-
-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.
-
-INSTALLATION
-
-1. Extract the entire ZIP into a new folder.
-2. Keep the executable and netfishing.pck together.
-3. Launch netfishing.exe on Windows or netfishing on Linux.
-
-Windows may show a warning for an unsigned application. This private build is
-not code signed. Only run a copy received from the playtest organizer.
-
-Linux users may need to allow execution:
-
- chmod +x netfishing
-
-CONTROLS
-
-Move W / A / S / D or left stick
-Jump Space
-Sprint Shift
-Sneak Ctrl
-Slow walk Alt
-Rotate camera Hold right mouse, or use right stick
-Cycle active hotbar slot Mouse wheel
-Select hotbar slot 1 through 9
-Zoom camera Shift + mouse wheel
-Cast / withdraw / reel Left mouse
-Cooler, Bag, and Logbook Tab
-Game Menu / back Escape
-Open Fishing Shop E while near the shop
-
-FISHING
-
-- Hold left mouse to select cast distance, then release to cast.
-- The target preview shows whether the selected landing point is fishable.
-- While waiting, hold left mouse to withdraw the bobber.
-- A bite starts the chase automatically.
-- Hold left mouse to advance the green catch meter.
-- At barriers, use distinct left-click presses to damage the barrier.
-- Do not let the red chase meter catch the green meter.
-- Accessibility auto-click can be enabled in Settings.
-- Approach the bright Fishing Shop booth and press E to open it.
-- The Fishing Shop buys one Cooler fish at a time for full base value.
-- Buy Coffee, Energy Drinks, Snacks, and Fish Finders as Bag supplies.
-- Drag supplies from Bag to the hotbar and left-click in READY to use one.
-- 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.
-- 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
-
-- Movement, jumping, camera rotation, and zoom
-- Short, medium, and maximum-distance casts
-- Valid water and invalid land casts
-- Manual withdrawal before a bite
-- Barrier-and-chase catching
-- Accessibility auto-click settings
-- Catch showcase and fish size variation
-- Cooler, Bag, Logbook, favorites, and sorting
-- Basic Fishing Rod equipment and the 1–9 hotbar
-- Pelican selling and wallet updates
-- Physical Fishing Shop sales and persistent fishing upgrades
-- Save, Continue, New Game, and Delete Save
-- Game Menu and persistent camera settings
-- Water-entry recovery from several shores
-
-SAVES AND SETTINGS
-
-Progression and settings are stored in Godot's per-user application-data
-directory, outside this extracted game folder. Delete Save removes progression
-but preserves settings.
-
-Windows:
- %APPDATA%\Godot\app_userdata\NETFISHING\
-
-Linux:
- ~/.local/share/godot/app_userdata/NETFISHING/
-
-This is an early save format. Keep expectations modest and report any failure
-to Continue, save, sell, favorite, or retain an individual fish.
-
-FEEDBACK
-
-Please include:
-
-- Your operating system and whether you used mouse/keyboard or controller
-- What you were doing when a problem occurred
-- Whether the issue repeated after restarting
-- Screenshots or a short recording when useful
-- How movement, casting, chase pressure, barrier clicking, menus, and camera
- controls felt
-
-KNOWN PRE-ALPHA LIMITATIONS
-
-- Multiplayer/networking is not implemented.
-- Fish and character art are temporary.
-- UI and balance values are placeholders.
-- There is no audio.
-- There is no installer or automatic updater.
-- Windows builds are unsigned.
diff --git a/progression/character_customization_catalog.gd b/progression/character_customization_catalog.gd
index 7350425..5a02f09 100644
--- a/progression/character_customization_catalog.gd
+++ b/progression/character_customization_catalog.gd
@@ -12,10 +12,32 @@ 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_style_arms",
+ "fur_style_head",
+ "fur_style_ears",
+ "fur_style_tail",
+ "fur_color_2",
+ "fur_color_3",
+ "fur_color_4",
+ "ears",
+ "eyes",
+ "nose",
+ "mouth",
+ "tail",
+]
+
const CATEGORY_LABELS: Dictionary = {
"species": "head",
"scale": "size",
- "fur_pattern": "fur color",
+ "fur_pattern": "fur",
"ears": "ears",
"eyes": "eyes",
"nose": "nose",
@@ -28,6 +50,43 @@ 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_STYLE_ARMS_ID: String = "fur_style_arms"
+const FUR_STYLE_HEAD_ID: String = "fur_style_head"
+const FUR_STYLE_EARS_ID: String = "fur_style_ears"
+const FUR_STYLE_TAIL_ID: String = "fur_style_tail"
+const FUR_STYLE_IDS: PackedStringArray = [
+ FUR_STYLE_ID,
+ FUR_STYLE_ARMS_ID,
+ FUR_STYLE_HEAD_ID,
+ FUR_STYLE_EARS_ID,
+ FUR_STYLE_TAIL_ID,
+]
+const FUR_STYLE_LABELS: Dictionary = {
+ FUR_STYLE_ID: "body",
+ FUR_STYLE_ARMS_ID: "arms",
+ FUR_STYLE_HEAD_ID: "head",
+ FUR_STYLE_EARS_ID: "ears",
+ FUR_STYLE_TAIL_ID: "tail",
+}
+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": "tertiary",
+ "fur_color_4": "quaternary",
+}
+const CUSTOM_FUR_COLOR_PREFIX: String = "custom_"
+const DEFAULT_FUR_STYLE: String = "solid"
+const FUR_PATTERN_ASSET_ROOT: String = (
+ "res://art/exported/characters/patterns"
+)
+const FEATURE_TEXTURE_CACHE_LIMIT: int = 24
const FEATURE_CATEGORIES: PackedStringArray = ["eyes", "nose", "mouth"]
const FEATURE_ASSET_ROOTS: Dictionary = {
@@ -41,29 +100,49 @@ const FEATURE_LABEL_OVERRIDES: Dictionary = {
"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"},
],
- # The serialized field remains `fur_pattern` for save and network
- # compatibility. It holds the initial solid fur-color selection until the
- # authored pattern layer is ready.
- "fur_pattern": [
- {"id": "white", "label": "white", "color": Color("f2f0e8")},
- {"id": "cream", "label": "cream", "color": Color("e6d39b")},
- {"id": "gray", "label": "gray", "color": Color("819398")},
- {"id": "charcoal", "label": "charcoal", "color": Color("34444a")},
- {"id": "brown", "label": "brown", "color": Color("7b4a32")},
- {"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": "green", "label": "green", "color": Color("6f913c")},
- {"id": "teal", "label": "teal", "color": Color("3a8790")},
- {"id": "blue", "label": "blue", "color": Color("4d76a8")},
- {"id": "purple", "label": "purple", "color": Color("76558f")},
- ],
+ # `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,
"ears": [
{"id": "none", "label": "none"},
{"id": "antlers_round", "label": "antlers"},
@@ -96,6 +175,7 @@ const OPTIONS: Dictionary = {
const LEGACY_OPTION_ALIASES: Dictionary = {
"species": {"default": "round"},
"fur_pattern": {"solid": "white"},
+ "fur_style": {"spots_bengal": "bengal"},
"ears": {"default": "none"},
"tail": {"default": "none"},
"eyes": {"default": "simple_shine"},
@@ -105,7 +185,13 @@ const LEGACY_OPTION_ALIASES: Dictionary = {
static var _feature_assets_ready: bool = false
static var _feature_options: Dictionary = {}
+static var _feature_resource_paths: Dictionary = {}
static var _feature_textures: Dictionary = {}
+static var _feature_texture_cache_order: Array[String] = []
+static var _fur_pattern_assets_ready: bool = false
+static var _fur_pattern_options: Array = []
+static var _fur_pattern_resource_paths: Dictionary = {}
+static var _fur_pattern_textures: Dictionary = {}
static func default_snapshot() -> Dictionary:
@@ -114,6 +200,14 @@ static func default_snapshot() -> Dictionary:
"species": "round",
"scale": DEFAULT_CHARACTER_SCALE,
"fur_pattern": "white",
+ "fur_style": DEFAULT_FUR_STYLE,
+ "fur_style_arms": DEFAULT_FUR_STYLE,
+ "fur_style_head": DEFAULT_FUR_STYLE,
+ "fur_style_ears": DEFAULT_FUR_STYLE,
+ "fur_style_tail": 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"),
@@ -141,6 +235,9 @@ 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
+ if category_id in FUR_STYLE_IDS:
+ _ensure_fur_pattern_assets()
+ return _fur_pattern_options
return OPTIONS.get(category_id, [])
@@ -225,24 +322,158 @@ static func texture_for(category_id: String, option_id: String) -> Texture2D:
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
+ var cache_id := "%s:%s" % [category_id, canonical_id]
+ if _feature_textures.has(cache_id):
+ _touch_feature_texture(cache_id)
+ return _feature_textures[cache_id] as Texture2D
+ var resource_path := feature_texture_path(category_id, canonical_id)
+ if resource_path.is_empty():
+ return null
+ var texture := ResourceLoader.load(
+ resource_path,
+ "Texture2D",
+ ResourceLoader.CACHE_MODE_IGNORE,
+ ) as Texture2D
+ if texture == null:
+ return null
+ _feature_textures[cache_id] = texture
+ _touch_feature_texture(cache_id)
+ _trim_feature_texture_cache()
+ return texture
+
+
+static func feature_texture_path(category_id: String, option_id: String) -> String:
+ if category_id not in FEATURE_CATEGORIES:
+ return ""
+ _ensure_feature_assets()
+ var canonical_id := canonical_option_id(category_id, option_id)
+ var paths: Dictionary = (
+ _feature_resource_paths.get(category_id, {}) as Dictionary
+ )
+ return str(paths.get(canonical_id, ""))
+
+
+static func _touch_feature_texture(cache_id: String) -> void:
+ var existing_index := _feature_texture_cache_order.find(cache_id)
+ if existing_index >= 0:
+ _feature_texture_cache_order.remove_at(existing_index)
+ _feature_texture_cache_order.append(cache_id)
+
+
+static func _trim_feature_texture_cache() -> void:
+ while _feature_texture_cache_order.size() > FEATURE_TEXTURE_CACHE_LIMIT:
+ var oldest_id: String = str(_feature_texture_cache_order.pop_front())
+ _feature_textures.erase(oldest_id)
static func refresh_feature_assets() -> void:
_feature_assets_ready = false
_feature_options.clear()
+ _feature_resource_paths.clear()
_feature_textures.clear()
+ _feature_texture_cache_order.clear()
_ensure_feature_assets()
+static func refresh_fur_pattern_assets() -> void:
+ _fur_pattern_assets_ready = false
+ _fur_pattern_options.clear()
+ _fur_pattern_resource_paths.clear()
+ _fur_pattern_textures.clear()
+ _ensure_fur_pattern_assets()
+
+
+static func _ensure_fur_pattern_assets() -> void:
+ if _fur_pattern_assets_ready:
+ return
+ _fur_pattern_assets_ready = true
+ _fur_pattern_options = [{"id": DEFAULT_FUR_STYLE, "label": "solid"}]
+ var root_directory := DirAccess.open(FUR_PATTERN_ASSET_ROOT)
+ if root_directory == null:
+ return
+ var directory_names := root_directory.get_directories()
+ directory_names.sort()
+ for directory_name: String in directory_names:
+ var pattern_id := _normalized_asset_id(directory_name)
+ if pattern_id.is_empty() or pattern_id == DEFAULT_FUR_STYLE:
+ continue
+ var pattern_directory_path := FUR_PATTERN_ASSET_ROOT.path_join(
+ directory_name
+ )
+ var pattern_directory := DirAccess.open(pattern_directory_path)
+ if pattern_directory == null:
+ continue
+ var file_names := pattern_directory.get_files()
+ file_names.sort()
+ var canonical_files: Dictionary = {}
+ var found_pattern_texture := false
+ for file_name: String in file_names:
+ var canonical_file_name := _canonical_feature_filename(file_name)
+ if (
+ canonical_file_name.is_empty()
+ or canonical_files.has(canonical_file_name)
+ ):
+ continue
+ canonical_files[canonical_file_name] = true
+ var file_base_name := canonical_file_name.get_basename().to_lower()
+ var pattern_suffix := "_" + pattern_id
+ if not file_base_name.ends_with(pattern_suffix):
+ continue
+ var component_id := file_base_name.substr(
+ 0,
+ file_base_name.length() - pattern_suffix.length(),
+ )
+ component_id = _normalized_asset_id(component_id)
+ if component_id.is_empty():
+ continue
+ var resource_path := pattern_directory_path.path_join(
+ canonical_file_name
+ )
+ if not ResourceLoader.exists(resource_path, "Texture2D"):
+ push_warning(
+ "Ignoring fur mask without a loadable texture: "
+ + resource_path
+ )
+ continue
+ _fur_pattern_resource_paths[
+ _fur_pattern_cache_id(pattern_id, component_id)
+ ] = resource_path
+ found_pattern_texture = true
+ if found_pattern_texture:
+ _fur_pattern_options.append({
+ "id": pattern_id,
+ "label": pattern_id.replace("_", " "),
+ })
+
+
+static func _normalized_asset_id(value: String) -> String:
+ var result := value.to_lower()
+ result = result.replace("-", "_")
+ result = result.replace(" ", "_")
+ result = result.replace(".", "_")
+ while result.contains("__"):
+ result = result.replace("__", "_")
+ while result.begins_with("_"):
+ result = result.substr(1)
+ while result.ends_with("_"):
+ result = result.substr(0, result.length() - 1)
+ return result
+
+
+static func _fur_pattern_cache_id(
+ pattern_id: String,
+ component_id: String,
+) -> String:
+ return "%s:%s" % [pattern_id, component_id]
+
+
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 resource_paths: Dictionary = {}
var root_path := str(FEATURE_ASSET_ROOTS[category_id])
var directory := DirAccess.open(root_path)
if directory != null:
@@ -260,24 +491,23 @@ static func _ensure_feature_assets() -> void:
var option_id := _feature_id_from_filename(
category_id, normalized_file_name
)
- if option_id.is_empty() or textures.has(option_id):
+ if option_id.is_empty() or resource_paths.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:
+ if not ResourceLoader.exists(resource_path, "Texture2D"):
push_warning(
"Ignoring facial feature without a loadable texture: "
+ resource_path
)
continue
- textures[option_id] = texture
+ resource_paths[option_id] = resource_path
options.append({
"id": option_id,
"label": _feature_label(option_id),
})
_feature_options[category_id] = options
- _feature_textures[category_id] = textures
+ _feature_resource_paths[category_id] = resource_paths
static func _canonical_feature_filename(file_name: String) -> String:
@@ -324,6 +554,8 @@ static func category_label(category_id: String) -> String:
static func is_valid_option(category_id: String, option_id: String) -> bool:
option_id = canonical_option_id(category_id, option_id)
+ if category_id in FUR_COLOR_IDS and is_custom_fur_color_id(option_id):
+ return true
for option: Dictionary in options_for(category_id):
if str(option.get("id", "")) == option_id:
return true
@@ -331,25 +563,158 @@ static func is_valid_option(category_id: String, option_id: String) -> bool:
static func canonical_option_id(category_id: String, option_id: String) -> String:
- var aliases: Dictionary = LEGACY_OPTION_ALIASES.get(category_id, {})
+ if (
+ category_id in FUR_COLOR_IDS
+ and option_id.to_lower().begins_with(CUSTOM_FUR_COLOR_PREFIX)
+ ):
+ return option_id.to_lower()
+ var alias_category_id := (
+ FUR_STYLE_ID if category_id in FUR_STYLE_IDS else category_id
+ )
+ var aliases: Dictionary = LEGACY_OPTION_ALIASES.get(alias_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)
+ if category_id in FUR_COLOR_IDS and is_custom_fur_color_id(canonical_id):
+ return Color(canonical_id.trim_prefix(CUSTOM_FUR_COLOR_PREFIX))
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 custom_fur_color_id(color: Color) -> String:
+ var opaque_color := Color(color.r, color.g, color.b, 1.0)
+ return CUSTOM_FUR_COLOR_PREFIX + opaque_color.to_html(false)
+
+
+static func is_custom_fur_color_id(option_id: String) -> bool:
+ var canonical_id := option_id.to_lower()
+ if (
+ canonical_id.length() != CUSTOM_FUR_COLOR_PREFIX.length() + 6
+ or not canonical_id.begins_with(CUSTOM_FUR_COLOR_PREFIX)
+ ):
+ return false
+ var hex_value := canonical_id.trim_prefix(CUSTOM_FUR_COLOR_PREFIX)
+ for character_index: int in range(hex_value.length()):
+ if hex_value[character_index] not in "0123456789abcdef":
+ return false
+ return true
+
+
+static func fur_color_label(category_id: String) -> String:
+ return str(FUR_COLOR_LABELS.get(category_id, category_id))
+
+
+static func fur_style_label(category_id: String) -> String:
+ return str(FUR_STYLE_LABELS.get(category_id, category_id))
+
+
+static func fur_style_field_for_component(component_id: String) -> String:
+ if component_id == "body_arms":
+ return FUR_STYLE_ARMS_ID
+ if component_id.begins_with("head_"):
+ return FUR_STYLE_HEAD_ID
+ if component_id.begins_with("ears_"):
+ return FUR_STYLE_EARS_ID
+ if component_id.begins_with("tails_"):
+ return FUR_STYLE_TAIL_ID
+ return FUR_STYLE_ID
+
+
+static func fur_component_for_style_field(
+ category_id: String,
+ snapshot: Dictionary,
+) -> String:
+ match category_id:
+ FUR_STYLE_ID:
+ return "body_main"
+ FUR_STYLE_ARMS_ID:
+ return "body_arms"
+ FUR_STYLE_HEAD_ID:
+ var species_id := canonical_option_id(
+ "species", str(snapshot.get("species", "round"))
+ )
+ return "head_pointy" if species_id == "pointy" else "head_round"
+ FUR_STYLE_EARS_ID:
+ var ears_id := canonical_option_id(
+ "ears", str(snapshot.get("ears", "none"))
+ )
+ return "" if ears_id == "none" else "ears_" + ears_id
+ FUR_STYLE_TAIL_ID:
+ var tail_id := canonical_option_id(
+ "tail", str(snapshot.get("tail", "none"))
+ )
+ return "" if tail_id == "none" else "tails_" + tail_id
+ return ""
+
+
+static func fur_pattern_options_for_field(
+ category_id: String,
+ snapshot: Dictionary,
+) -> Array:
+ if category_id not in FUR_STYLE_IDS:
+ return []
+ _ensure_fur_pattern_assets()
+ var component_id := fur_component_for_style_field(category_id, snapshot)
+ var result: Array = []
+ for option: Dictionary in _fur_pattern_options:
+ var pattern_id := str(option.get("id", ""))
+ if (
+ pattern_id == DEFAULT_FUR_STYLE
+ or (
+ not component_id.is_empty()
+ and _fur_pattern_resource_paths.has(
+ _fur_pattern_cache_id(pattern_id, component_id)
+ )
+ )
+ ):
+ result.append(option)
+ return result
+
+
+static func fur_pattern_texture(
+ option_id: String,
+ component_id: String = "body_main",
+) -> Texture2D:
+ _ensure_fur_pattern_assets()
+ var canonical_id := canonical_option_id(FUR_STYLE_ID, option_id)
+ if canonical_id == DEFAULT_FUR_STYLE:
+ return null
+ var cache_id := _fur_pattern_cache_id(canonical_id, component_id)
+ if _fur_pattern_textures.has(cache_id):
+ return _fur_pattern_textures[cache_id] as Texture2D
+ var resource_path := str(_fur_pattern_resource_paths.get(cache_id, ""))
+ if resource_path.is_empty():
+ return null
+ var texture := ResourceLoader.load(resource_path, "Texture2D") as Texture2D
+ if texture != null:
+ _fur_pattern_textures[cache_id] = texture
+ return texture
+
+
+static func appearance_signature_values(snapshot: Dictionary) -> Array:
+ var result: Array = []
+ for category_id: String in SNAPSHOT_IDS:
+ if category_id == SCALE_CATEGORY_ID:
+ result.append(character_scale_percent(snapshot.get(
+ category_id,
+ DEFAULT_CHARACTER_SCALE,
+ )))
+ else:
+ result.append(str(snapshot.get(category_id, "")))
+ return result
+
+
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
@@ -367,14 +732,21 @@ 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:
+ 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 raw_option: Variant = snapshot.get(category_id, "")
+ if (
+ category_id in FUR_STYLE_IDS
+ and category_id != FUR_STYLE_ID
+ and not snapshot.has(category_id)
+ ):
+ raw_option = snapshot.get(FUR_STYLE_ID, DEFAULT_FUR_STYLE)
var option_id: String = canonical_option_id(
- category_id, str(snapshot.get(category_id, ""))
+ category_id, str(raw_option)
)
if is_valid_option(category_id, option_id):
result[category_id] = option_id
diff --git a/progression/player_appearance_store.gd b/progression/player_appearance_store.gd
index 7ac8767..febe43d 100644
--- a/progression/player_appearance_store.gd
+++ b/progression/player_appearance_store.gd
@@ -13,6 +13,10 @@ var _data_root: PlayerDataRoot
func configure_storage(path: String, data_root: PlayerDataRoot) -> void:
_profile_path = path
_data_root = data_root
+ _snapshot = CharacterCustomizationCatalog.default_snapshot()
+ _loaded = false
+ _future_version = false
+ _expected_hash = ""
func _temp_path() -> String:
diff --git a/progression/player_art_unlocks.gd b/progression/player_art_unlocks.gd
index c6a6e57..0bc7acc 100644
--- a/progression/player_art_unlocks.gd
+++ b/progression/player_art_unlocks.gd
@@ -7,6 +7,7 @@ signal unlocks_changed(unlock_mask: int)
const BASE_BRUSH_SIZE: int = 1
const BASE_GRID_SIZE: int = 16
+const STAMP_PRODUCT_ID: StringName = &"stamp_tool"
const BRUSH_SIZES: Array[int] = [1, 2, 3, 4]
const GRID_SIZES: Array[int] = [16, 32, 64, 128]
@@ -25,6 +26,7 @@ const PRODUCT_BITS: Dictionary[StringName, int] = {
&"grid_32x": 10,
&"grid_64x": 11,
&"grid_128x": 12,
+ STAMP_PRODUCT_ID: 13,
}
const COLOR_PRODUCTS: Dictionary[StringName, StringName] = {
&"marker_ocean_teal": &"ocean_teal",
@@ -45,7 +47,7 @@ const GRID_PRODUCTS: Dictionary[StringName, int] = {
&"grid_64x": 64,
&"grid_128x": 128,
}
-const ALL_UNLOCK_MASK: int = (1 << 13) - 1
+const ALL_UNLOCK_MASK: int = (1 << 14) - 1
var _unlock_mask: int = 0
@@ -146,6 +148,10 @@ func is_grid_size_unlocked(grid_size: int) -> bool:
return grid_size in get_unlocked_grid_sizes()
+func is_stamp_unlocked() -> bool:
+ return owns_product(STAMP_PRODUCT_ID)
+
+
static func get_product_bit(product_id: StringName) -> int:
return int(PRODUCT_BITS.get(product_id, -1))
diff --git a/progression/player_cooler_capacity.gd b/progression/player_cooler_capacity.gd
index 1e77784..bbd0b87 100644
--- a/progression/player_cooler_capacity.gd
+++ b/progression/player_cooler_capacity.gd
@@ -2,58 +2,77 @@ class_name PlayerCoolerCapacity
extends Node
const PlayerWalletType = preload("res://economy/player_wallet.gd")
+const PlayerHomeStateType = preload("res://homes/player_home_state.gd")
signal capacity_changed(level: int, capacity: int)
-const CAPACITIES: Array[int] = [12, 18, 24, 32, 40]
-const EXPANSION_COSTS: Array[int] = [75, 175, 400, 850]
-const MAX_LEVEL: int = 4
+## Compatibility adapter for saves and systems that still refer to the old
+## cooler-capacity object. Storage is now derived exclusively from the player's
+## home tier; this object is not independently purchasable.
+const CAPACITIES: Array[int] = [9, 18, 27, 36, 45, 54, 63, 72]
+const EXPANSION_COSTS: Array[int] = [75, 175, 400, 850, 1500, 2500, 4000]
+const MAX_LEVEL: int = 7
+const MAX_CAPACITY: int = 72
-var _capacity_level: int = 0
+var _home_state: PlayerHomeStateType
+var _legacy_capacity_level: int = 0
+
+
+func bind_home_state(home_state: PlayerHomeStateType) -> void:
+ if _home_state == home_state:
+ return
+ if (
+ _home_state != null
+ and _home_state.changed.is_connected(_on_home_changed)
+ ):
+ _home_state.changed.disconnect(_on_home_changed)
+ _home_state = home_state
+ if (
+ _home_state != null
+ and not _home_state.changed.is_connected(_on_home_changed)
+ ):
+ _home_state.changed.connect(_on_home_changed)
+ _on_home_changed()
func get_level() -> int:
- return _capacity_level
+ return _legacy_capacity_level
func get_capacity() -> int:
- return CAPACITIES[_capacity_level]
+ if _home_state != null:
+ # A new home upgrade grants its authored capacity. Older saves retain any
+ # separately purchased storage level so reducing the first RV tier cannot
+ # hide or discard items that were already placed in later slots.
+ return maxi(
+ _home_state.get_storage_capacity(),
+ CAPACITIES[clampi(_legacy_capacity_level, 0, MAX_LEVEL)],
+ )
+ return CAPACITIES[clampi(_legacy_capacity_level, 0, MAX_LEVEL)]
func get_next_capacity() -> int:
- if _capacity_level >= MAX_LEVEL:
- return -1
- return CAPACITIES[_capacity_level + 1]
+ return -1
func get_next_cost() -> int:
- if _capacity_level >= MAX_LEVEL:
- return -1
- return EXPANSION_COSTS[_capacity_level]
+ return -1
-func can_purchase(wallet: PlayerWalletType) -> bool:
- var cost: int = get_next_cost()
- return wallet != null and cost >= 0 and wallet.can_afford(cost)
+func can_purchase(_wallet: PlayerWalletType) -> bool:
+ return false
-func purchase(wallet: PlayerWalletType) -> bool:
- if not can_purchase(wallet):
- return false
- var cost: int = get_next_cost()
- if not wallet.debit(cost):
- return false
- _capacity_level += 1
- capacity_changed.emit(_capacity_level, get_capacity())
- return true
+func purchase(_wallet: PlayerWalletType) -> bool:
+ return false
func restore_level(level: int) -> bool:
var validated: int = clampi(level, 0, MAX_LEVEL)
- var changed: bool = _capacity_level != validated
- _capacity_level = validated
+ var changed: bool = _legacy_capacity_level != validated
+ _legacy_capacity_level = validated
if changed:
- capacity_changed.emit(_capacity_level, get_capacity())
+ capacity_changed.emit(get_level(), get_capacity())
return true
@@ -62,4 +81,8 @@ func reset_to_defaults() -> void:
func to_save_data() -> Dictionary:
- return {"capacity_level": _capacity_level}
+ return {"capacity_level": get_level()}
+
+
+func _on_home_changed() -> void:
+ capacity_changed.emit(get_level(), get_capacity())
diff --git a/progression/player_fishing_upgrades.gd b/progression/player_fishing_upgrades.gd
index 7bc6abd..e21a31e 100644
--- a/progression/player_fishing_upgrades.gd
+++ b/progression/player_fishing_upgrades.gd
@@ -6,9 +6,17 @@ const PlayerWalletType = preload("res://economy/player_wallet.gd")
signal upgrades_changed(reel_speed_level: int, barrier_power_level: int)
const MAX_REEL_SPEED_LEVEL: int = 5
-const MAX_BARRIER_POWER_LEVEL: int = 3
+const MAX_BARRIER_POWER_LEVEL: int = 7
const REEL_SPEED_COSTS: Array[int] = [50, 125, 300, 650, 1250]
-const BARRIER_POWER_COSTS: Array[int] = [100, 275, 700]
+const BARRIER_POWER_COSTS: Array[int] = [
+ 100,
+ 275,
+ 700,
+ 1500,
+ 3000,
+ 6000,
+ 12000,
+]
var _reel_speed_level: int = 0
var _barrier_power_level: int = 0
@@ -27,7 +35,11 @@ func get_reel_speed_multiplier() -> float:
func get_barrier_damage() -> int:
- return _barrier_power_level + 1
+ return get_barrier_damage_for_level(_barrier_power_level)
+
+
+static func get_barrier_damage_for_level(level: int) -> int:
+ return 1 << clampi(level, 0, MAX_BARRIER_POWER_LEVEL)
func get_next_reel_speed_cost() -> int:
diff --git a/progression/player_item_effects.gd b/progression/player_item_effects.gd
index 1a73c43..2d4c23d 100644
--- a/progression/player_item_effects.gd
+++ b/progression/player_item_effects.gd
@@ -11,6 +11,12 @@ 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 EFFECT_IDS: Array[StringName] = [
+ COFFEE_ID,
+ ENERGY_DRINK_ID,
+ SNACK_ID,
+ FISH_FINDER_ID,
+]
const FISH_FINDER_DEAD_MESSAGE: String = "hmm... batteries are dead..."
const COFFEE_DURATION: float = 90.0
const ENERGY_DRINK_DURATION: float = 90.0
@@ -100,6 +106,10 @@ func get_remaining_snapshot() -> Dictionary[StringName, float]:
return _remaining.duplicate()
+func get_registered_effect_ids() -> Array[StringName]:
+ return EFFECT_IDS.duplicate()
+
+
func get_effect_duration(item_id: StringName) -> float:
return _get_duration(item_id)
diff --git a/project.godot b/project.godot
index ac103f3..5bcbb4d 100644
--- a/project.godot
+++ b/project.godot
@@ -10,18 +10,27 @@ config_version=5
[application]
-config/name="NETFISHING"
-config/version="0.6.5-alpha"
+config/name="straywild"
+config/version="0.20.2-alpha"
run/main_scene="res://main/main.tscn"
config/features=PackedStringArray("4.7", "GL Compatibility")
-config/icon="res://art/exported/system_icons/netfishing_256.png"
+config/icon="res://art/exported/system_icons/straywild_256.png"
+
+[autoload]
+
+TextureSamplingPolicy="*res://main/texture_sampling_policy.gd"
[display]
window/stretch/aspect="expand"
+[editor_plugins]
+
+enabled=PackedStringArray("res://addons/straywild_shoreline_baker/plugin.cfg")
+
[gui]
+timers/tooltip_delay_sec=0.0
theme/custom="res://ui/game_theme.tres"
[input]
@@ -79,6 +88,7 @@ jump={
sneak={
"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":4194326,"key_label":0,"unicode":0,"location":0,"echo":false,"script":null)
+, Object(InputEventJoypadButton,"resource_local_to_scene":false,"resource_name":"","device":0,"button_index":8,"pressure":0.0,"pressed":false,"script":null)
]
}
slow_walk={
@@ -98,6 +108,11 @@ fish_primary={
, Object(InputEventJoypadButton,"resource_local_to_scene":false,"resource_name":"","device":0,"button_index":10,"pressure":0.0,"pressed":false,"script":null)
]
}
+reel_alternate={
+"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":96,"key_label":0,"unicode":0,"location":0,"echo":false,"script":null)
+]
+}
camera_drag={
"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":2,"canceled":false,"pressed":false,"double_click":false,"script":null)
@@ -188,6 +203,7 @@ open_emotes={
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)
+, Object(InputEventJoypadButton,"resource_local_to_scene":false,"resource_name":"","device":0,"button_index":3,"pressure":0.0,"pressed":false,"script":null)
]
}
open_quick_actions={
@@ -197,7 +213,8 @@ open_quick_actions={
}
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)
+"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":84,"key_label":0,"unicode":0,"location":0,"echo":false,"script":null)
+, Object(InputEventJoypadButton,"resource_local_to_scene":false,"resource_name":"","device":0,"button_index":4,"pressure":0.0,"pressed":false,"script":null)
]
}
focus_gameplay={
@@ -243,7 +260,7 @@ alternate_fishing={
[network]
-discovery/base_url="https://discovery.netfishing.org"
+discovery/base_url="https://discovery.straywild.io/patron-test"
[physics]
diff --git a/save/player_save_inspection.gd b/save/player_save_inspection.gd
index be5fd24..ff14de6 100644
--- a/save/player_save_inspection.gd
+++ b/save/player_save_inspection.gd
@@ -14,6 +14,10 @@ var detected_version: int = -1
var catch_count: int = 0
var wallet_balance: int = 0
var discovered_species_count: int = 0
+var total_experience: int = 0
+var player_level: int = 1
+var world_layout: StringName = &"generated_world"
+var world_seed: int = 0
var has_primary_file: bool = false
var message: String = ""
diff --git a/save/player_save_manager.gd b/save/player_save_manager.gd
index 57e355d..c41f861 100644
--- a/save/player_save_manager.gd
+++ b/save/player_save_manager.gd
@@ -12,6 +12,9 @@ const ItemCatalogType = preload("res://items/item_catalog.gd")
const OwnedItemType = preload("res://items/owned_item.gd")
const PlayerBagType = preload("res://inventory/player_bag.gd")
const PlayerHotbarType = preload("res://inventory/player_hotbar.gd")
+const PlayerInventoryLayoutType = preload(
+ "res://inventory/player_inventory_layout.gd"
+)
const PlayerFishingUpgradesType = preload(
"res://progression/player_fishing_upgrades.gd"
)
@@ -28,11 +31,18 @@ const WorldTimeServiceType = preload("res://world/world_time_service.gd")
const WorldWeatherServiceType = preload(
"res://world/world_weather_service.gd"
)
+const WorldLayoutType = preload("res://world/world_layout.gd")
const PlayerJobServiceType = preload("res://jobs/player_job_service.gd")
+const PlayerType = preload("res://player/player.gd")
+const PlayerHomeStateType = preload("res://homes/player_home_state.gd")
-const SAVE_VERSION: int = 7
+const SAVE_VERSION: int = 11
+const LEGACY_SAVE_FILENAME := "player_save.json"
+const ARCHIVE_EXTENSION := ".nfsave"
const BASIC_ROD_ID: StringName = &"basic_fishing_rod"
const MAX_SAFE_BALANCE: int = 1000000000000
+const DEFAULT_WORLD_SEED: int = 13001
+const MAX_WORLD_SEED: int = 2147483646
class LoadSnapshot:
extends RefCounted
@@ -40,18 +50,24 @@ class LoadSnapshot:
var catches: Array[FishCatchType] = []
var discovered_ids: Array[StringName] = []
var discovered_quality_masks: Dictionary[StringName, int] = {}
+ var catch_counts: Dictionary[StringName, int] = {}
var wallet_balance: int = 0
var next_catch_sequence: int = 1
var bag_items: Array[OwnedItemType] = []
var unlocked_bait_ids: Array[StringName] = []
+ var active_bait_id: StringName
+ var active_lure_id: StringName
var hotbar_slots: Array[StringName] = []
var fish_hotbar_slots: Array[StringName] = []
var selected_hotbar_slot: int = 0
+ var inventory_layout_data: Dictionary = {}
var reel_speed_level: int = 0
var barrier_power_level: int = 0
var cooler_capacity_level: int = 0
var art_unlock_mask: int = 0
var total_experience: int = 0
+ var world_layout: StringName = WorldLayoutType.GENERATED
+ var world_seed: int = DEFAULT_WORLD_SEED
var world_time_hours: float = WorldTimeServiceType.DEFAULT_START_HOUR
var has_world_weather_state: bool = false
var world_weather: WorldWeatherServiceType.Weather = (
@@ -61,6 +77,7 @@ class LoadSnapshot:
WorldWeatherServiceType.SUNNY_DURATION_RANGE.x
)
var jobs_data: Dictionary = PlayerJobServiceType.default_save_data()
+ var home_data: Dictionary = PlayerHomeStateType.default_save_data()
@export_range(0.05, 5.0, 0.05) var autosave_delay: float = 0.5
@@ -71,6 +88,7 @@ var _wallet: PlayerWalletType
var _catalog: FishPoolType
var _bag: PlayerBagType
var _hotbar: PlayerHotbarType
+var _inventory_layout: PlayerInventoryLayoutType
var _item_catalog: ItemCatalogType
var _fishing_upgrades: PlayerFishingUpgradesType
var _cooler_capacity: PlayerCoolerCapacityType
@@ -79,6 +97,8 @@ var _experience: PlayerExperienceType
var _world_time: WorldTimeServiceType
var _world_weather: WorldWeatherServiceType
var _jobs: PlayerJobServiceType
+var _player: PlayerType
+var _home_state: PlayerHomeStateType
var _autosave_timer: Timer
var _is_configured: bool = false
var _is_restoring: bool = false
@@ -88,11 +108,27 @@ var _autosave_enabled: bool = false
var _save_path := ""
var _expected_hash := ""
var _data_root: PlayerDataRoot
+var _world_layout: StringName = WorldLayoutType.GENERATED
+var _world_seed: int = DEFAULT_WORLD_SEED
func configure_storage(path: String, data_root: PlayerDataRoot) -> void:
+ if not select_storage(path, data_root):
+ push_error("PlayerSaveManager could not configure progression storage.")
+
+
+func select_storage(path: String, data_root: PlayerDataRoot) -> bool:
+ if path.is_empty() or data_root == null:
+ return false
+ if _autosave_enabled or _is_dirty:
+ return false
+ if _autosave_timer != null:
+ _autosave_timer.stop()
_save_path = path
_data_root = data_root
+ _expected_hash = ""
+ _automatic_saving_blocked = false
+ return true
func _temp_path() -> String:
@@ -103,6 +139,21 @@ func _backup_path() -> String:
return _save_path + ".backup"
+func _codec_scratch_path() -> String:
+ return _save_path + ".codec.tmp"
+
+
+func _legacy_save_path() -> String:
+ return _save_path.get_base_dir().path_join(LEGACY_SAVE_FILENAME)
+
+
+func _read_path() -> String:
+ if FileAccess.file_exists(_save_path):
+ return _save_path
+ var legacy: String = _legacy_save_path()
+ return legacy if FileAccess.file_exists(legacy) else ""
+
+
func _ready() -> void:
_autosave_timer = Timer.new()
_autosave_timer.one_shot = true
@@ -117,6 +168,7 @@ func setup(
catalog: FishPoolType,
bag: PlayerBagType,
hotbar: PlayerHotbarType,
+ inventory_layout: PlayerInventoryLayoutType,
item_catalog: ItemCatalogType,
fishing_upgrades: PlayerFishingUpgradesType,
cooler_capacity: PlayerCoolerCapacityType,
@@ -125,6 +177,8 @@ func setup(
world_time: WorldTimeServiceType,
world_weather: WorldWeatherServiceType,
jobs: PlayerJobServiceType,
+ player: PlayerType,
+ home_state: PlayerHomeStateType,
) -> void:
_inventory = inventory
_collection_log = collection_log
@@ -132,6 +186,7 @@ func setup(
_catalog = catalog
_bag = bag
_hotbar = hotbar
+ _inventory_layout = inventory_layout
_item_catalog = item_catalog
_fishing_upgrades = fishing_upgrades
_cooler_capacity = cooler_capacity
@@ -140,6 +195,8 @@ func setup(
_world_time = world_time
_world_weather = world_weather
_jobs = jobs
+ _player = player
+ _home_state = home_state
_is_configured = (
_inventory != null
and _collection_log != null
@@ -147,6 +204,7 @@ func setup(
and _catalog != null
and _bag != null
and _hotbar != null
+ and _inventory_layout != null
and _item_catalog != null
and _fishing_upgrades != null
and _cooler_capacity != null
@@ -155,6 +213,8 @@ func setup(
and _world_time != null
and _world_weather != null
and _jobs != null
+ and _player != null
+ and _home_state != null
)
if not _is_configured:
push_error("PlayerSaveManager setup is missing required references.")
@@ -171,6 +231,8 @@ func setup(
_bag.contents_changed.connect(_mark_dirty)
if not _hotbar.slots_changed.is_connected(_mark_dirty):
_hotbar.slots_changed.connect(_mark_dirty)
+ if not _inventory_layout.layout_changed.is_connected(_mark_dirty):
+ _inventory_layout.layout_changed.connect(_mark_dirty)
if not _hotbar.selected_slot_changed.is_connected(
_on_selected_hotbar_slot_changed
):
@@ -195,6 +257,12 @@ func setup(
_experience.experience_changed.connect(_on_experience_changed)
if not _jobs.changed.is_connected(_mark_dirty):
_jobs.changed.connect(_mark_dirty)
+ if not _player.active_bait_changed.is_connected(_on_active_tackle_changed):
+ _player.active_bait_changed.connect(_on_active_tackle_changed)
+ if not _player.active_lure_changed.is_connected(_on_active_tackle_changed):
+ _player.active_lure_changed.connect(_on_active_tackle_changed)
+ if not _home_state.changed.is_connected(_mark_dirty):
+ _home_state.changed.connect(_mark_dirty)
func load_player_data() -> bool:
@@ -202,24 +270,22 @@ func load_player_data() -> bool:
return false
_automatic_saving_blocked = false
_recover_interrupted_write()
- if _save_path.is_empty() or not FileAccess.file_exists(_save_path):
+ var read_path: String = _read_path()
+ if _save_path.is_empty() or read_path.is_empty():
_is_dirty = false
return true
-
- var save_file := FileAccess.open(_save_path, FileAccess.READ)
- if save_file == null:
- _handle_corrupt_save("Unable to open player save.")
+ var decoded: Dictionary = ProgressionSaveCodec.read_local_save(
+ read_path,
+ read_path == _legacy_save_path(),
+ )
+ if not bool(decoded.get("ok", false)):
+ _handle_corrupt_save("Player save could not be decoded.", read_path)
return false
- var json_text: String = save_file.get_as_text()
- save_file.close()
- _expected_hash = PortableFileGuard.hash_file(_save_path)
-
- var json := JSON.new()
- var parse_error: Error = json.parse(json_text)
- if parse_error != OK or typeof(json.data) != TYPE_DICTIONARY:
- _handle_corrupt_save("Player save contains malformed JSON.")
- return false
- var save_data: Dictionary = json.data
+ _expected_hash = (
+ PortableFileGuard.hash_file(_save_path)
+ if read_path == _save_path else ""
+ )
+ var save_data: Dictionary = decoded["data"]
var version: int = _read_integer(save_data.get("save_version"), -1)
if version > SAVE_VERSION:
_automatic_saving_blocked = true
@@ -236,13 +302,16 @@ func load_player_data() -> bool:
save_data = _migrate_save(save_data, version)
if save_data.is_empty():
_handle_corrupt_save(
- "Player save version %d is unsupported." % version
+ "Player save version %d is unsupported." % version,
+ read_path,
)
return false
var snapshot: LoadSnapshot = _build_load_snapshot(save_data)
if snapshot == null:
- _handle_corrupt_save("Player save failed structural validation.")
+ _handle_corrupt_save(
+ "Player save failed structural validation.", read_path
+ )
return false
_is_restoring = true
@@ -251,9 +320,10 @@ func load_player_data() -> bool:
snapshot.next_catch_sequence
)
var collection_restored: bool = (
- _collection_log.replace_discovery_state(
+ _collection_log.replace_collection_state(
snapshot.discovered_ids,
snapshot.discovered_quality_masks,
+ snapshot.catch_counts,
)
)
var wallet_restored: bool = _wallet.restore_balance(
@@ -263,18 +333,28 @@ func load_player_data() -> 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,
- snapshot.fish_hotbar_slots,
- )
+ var tackle_restored: bool = _restore_tackle_selection(snapshot)
var upgrades_restored: bool = _fishing_upgrades.restore_levels(
snapshot.reel_speed_level,
snapshot.barrier_power_level
)
+ # Home tier is now the authority for private-storage capacity, so restore it
+ # before the compatibility capacity adapter and inventory layout.
+ var home_restored: bool = _home_state.restore_from_save_data(
+ snapshot.home_data
+ )
var cooler_restored: bool = _cooler_capacity.restore_level(
snapshot.cooler_capacity_level
)
+ var layout_restored: bool = _inventory_layout.restore_from_save_data(
+ snapshot.inventory_layout_data
+ )
+ var hotbar_restored: bool = _hotbar.replace_state(
+ snapshot.hotbar_slots,
+ snapshot.selected_hotbar_slot,
+ snapshot.fish_hotbar_slots,
+ false,
+ )
var art_restored: bool = _art_unlocks.restore_mask(
snapshot.art_unlock_mask
)
@@ -284,6 +364,8 @@ func load_player_data() -> bool:
var world_time_restored: bool = (
_world_time.restore_persistent_time_hours(snapshot.world_time_hours)
)
+ _world_layout = snapshot.world_layout
+ _world_seed = snapshot.world_seed
var world_weather_restored: bool = true
if snapshot.has_world_weather_state:
world_weather_restored = _world_weather.restore_persistent_state(
@@ -297,19 +379,48 @@ func load_player_data() -> bool:
or not collection_restored
or not wallet_restored
or not bag_restored
+ or not tackle_restored
or not hotbar_restored
or not upgrades_restored
or not cooler_restored
+ or not layout_restored
or not art_restored
or not experience_restored
or not world_time_restored
or not world_weather_restored
or not jobs_restored
+ or not home_restored
):
- push_error("Validated player save could not be restored.")
+ push_error(
+ (
+ "Validated player save could not be restored: "
+ + "inventory=%s collection=%s wallet=%s bag=%s tackle=%s hotbar=%s "
+ + "upgrades=%s cooler=%s layout=%s art=%s experience=%s "
+ + "time=%s weather=%s jobs=%s home=%s"
+ )
+ % [
+ inventory_restored,
+ collection_restored,
+ wallet_restored,
+ bag_restored,
+ tackle_restored,
+ hotbar_restored,
+ upgrades_restored,
+ cooler_restored,
+ layout_restored,
+ art_restored,
+ experience_restored,
+ world_time_restored,
+ world_weather_restored,
+ jobs_restored,
+ home_restored,
+ ]
+ )
return false
_is_dirty = false
+ if read_path == _legacy_save_path():
+ _migrate_legacy_plaintext_save(save_data, read_path)
print(
"Loaded player save version %d with %d catches."
% [SAVE_VERSION, snapshot.catches.size()]
@@ -318,26 +429,36 @@ func load_player_data() -> bool:
func inspect_save() -> SaveInspectionType:
- var result := SaveInspectionType.new()
_recover_interrupted_write()
- result.has_primary_file = FileAccess.file_exists(_save_path)
+ var read_path: String = _read_path()
+ return inspect_progression_at_path(
+ read_path,
+ read_path == _legacy_save_path(),
+ )
+
+
+func inspect_progression_at_path(
+ path: String,
+ allow_legacy_plaintext: bool = false,
+) -> SaveInspectionType:
+ var result := SaveInspectionType.new()
+ if not path.is_empty():
+ _remove_if_present(path + ".codec.tmp")
+ _recover_path_write(path)
+ result.has_primary_file = not path.is_empty() and FileAccess.file_exists(path)
if not result.has_primary_file:
result.status = SaveInspectionType.Status.MISSING
result.message = "no save found."
return result
- var save_file := FileAccess.open(_save_path, FileAccess.READ)
- if save_file == null:
+ var decoded: Dictionary = ProgressionSaveCodec.read_local_save(
+ path,
+ allow_legacy_plaintext,
+ )
+ if not bool(decoded.get("ok", false)):
result.status = SaveInspectionType.Status.IO_ERROR
result.message = "the save could not be read."
return result
- var json := JSON.new()
- var parse_error: Error = json.parse(save_file.get_as_text())
- save_file.close()
- if parse_error != OK or typeof(json.data) != TYPE_DICTIONARY:
- result.status = SaveInspectionType.Status.MALFORMED
- result.message = "the save is corrupt and was preserved."
- return result
- var save_data: Dictionary = json.data
+ var save_data: Dictionary = decoded["data"]
result.detected_version = _read_integer(save_data.get("save_version"), -1)
if result.detected_version > SAVE_VERSION:
result.status = SaveInspectionType.Status.UNSUPPORTED_VERSION
@@ -362,22 +483,237 @@ func inspect_save() -> SaveInspectionType:
result.catch_count = snapshot.catches.size()
result.wallet_balance = snapshot.wallet_balance
result.discovered_species_count = snapshot.discovered_ids.size()
+ result.total_experience = snapshot.total_experience
+ result.player_level = PlayerExperienceType.level_for_total_experience(
+ snapshot.total_experience
+ )
+ result.world_layout = snapshot.world_layout
+ result.world_seed = snapshot.world_seed
result.message = "save ready."
return result
-func initialize_new_game() -> bool:
+func export_progression_archive(path: String) -> Dictionary:
+ if not _is_configured or path.is_empty():
+ return {"ok": false, "message": "progression export is unavailable."}
+ if _is_dirty and not save_if_dirty():
+ return {"ok": false, "message": "progression could not be saved first."}
+ var source_path: String = _read_path()
+ if source_path.is_empty():
+ return {"ok": false, "message": "there is no progression to export."}
+ return export_progression_archive_from_path(
+ source_path,
+ path,
+ source_path == _legacy_save_path(),
+ )
+
+
+func export_progression_archive_from_path(
+ source_path: String,
+ destination_path: String,
+ allow_legacy_plaintext: bool = false,
+) -> Dictionary:
+ if source_path.is_empty() or destination_path.is_empty():
+ return {"ok": false, "message": "progression export is unavailable."}
+ var decoded: Dictionary = ProgressionSaveCodec.read_local_save(
+ source_path,
+ allow_legacy_plaintext,
+ )
+ if not bool(decoded.get("ok", false)):
+ return {"ok": false, "message": "the selected progression could not be read."}
+ var prepared: Dictionary = _prepare_external_save_data(decoded["data"])
+ if not bool(prepared.get("ok", false)):
+ return prepared
+ var bytes: PackedByteArray = ProgressionSaveCodec.encode_archive(
+ prepared["data"],
+ destination_path + ".codec.tmp",
+ )
+ if bytes.is_empty():
+ return {"ok": false, "message": "the progression archive could not be encoded."}
+ var result: Dictionary = PortableFileGuard.write_guarded(
+ destination_path,
+ bytes,
+ PortableFileGuard.hash_file(destination_path),
+ _data_root.conflict_directory(),
+ _data_root.device_id,
+ )
+ if not bool(result.get("ok", false)):
+ return {"ok": false, "message": "the progression archive could not be written."}
+ var verified: Dictionary = inspect_progression_archive(destination_path)
+ if not bool(verified.get("ok", false)):
+ return {"ok": false, "message": "the progression archive could not be verified."}
+ verified["message"] = "progression archive created."
+ return verified
+
+
+func inspect_progression_archive(path: String) -> Dictionary:
+ var decoded: Dictionary = ProgressionSaveCodec.read_archive(path)
+ if not bool(decoded.get("ok", false)):
+ return {
+ "ok": false,
+ "message": str(decoded.get("message", "could not open progression archive.")),
+ }
+ var prepared: Dictionary = _prepare_external_save_data(decoded["data"])
+ if not bool(prepared.get("ok", false)):
+ return prepared
+ prepared["game_version"] = str(decoded.get("game_version", ""))
+ prepared["created_at_unix"] = int(decoded.get("created_at_unix", 0))
+ return prepared
+
+
+func import_progression_archive(path: String) -> Dictionary:
if not _is_configured:
+ return {"ok": false, "message": "progression import is unavailable."}
+ var inspected: Dictionary = inspect_progression_archive(path)
+ if not bool(inspected.get("ok", false)):
+ return inspected
+ var current_path: String = _read_path()
+ if not current_path.is_empty() and not _archive_replaced_save(current_path):
+ return {"ok": false, "message": "the current progression could not be backed up."}
+ var expected_hash: String = PortableFileGuard.hash_file(_save_path)
+ var result: Dictionary = _write_current_save_data(
+ inspected["data"], expected_hash
+ )
+ if not bool(result.get("ok", false)):
+ return {"ok": false, "message": "the imported progression could not be installed."}
+ _expected_hash = str(result.get("hash", ""))
+ _remove_legacy_save_files()
+ _is_dirty = false
+ _automatic_saving_blocked = false
+ return {
+ "ok": true,
+ "message": "progression imported.",
+ "catch_count": inspected["catch_count"],
+ "wallet_balance": inspected["wallet_balance"],
+ "discovered_species_count": inspected["discovered_species_count"],
+ }
+
+
+func install_progression_archive_at_path(
+ archive_path: String,
+ destination_path: String,
+) -> Dictionary:
+ if not _is_configured or destination_path.is_empty():
+ return {"ok": false, "message": "progression import is unavailable."}
+ var inspected: Dictionary = inspect_progression_archive(archive_path)
+ if not bool(inspected.get("ok", false)):
+ return inspected
+ var result: Dictionary = _write_save_data_at_path(
+ inspected["data"],
+ destination_path,
+ PortableFileGuard.hash_file(destination_path),
+ )
+ if not bool(result.get("ok", false)):
+ return {"ok": false, "message": "the imported progression could not be installed."}
+ return {
+ "ok": true,
+ "message": "progression imported as a new save slot.",
+ "catch_count": inspected["catch_count"],
+ "wallet_balance": inspected["wallet_balance"],
+ "discovered_species_count": inspected["discovered_species_count"],
+ "world_layout": inspected["world_layout"],
+ "world_seed": inspected["world_seed"],
+ }
+
+
+func copy_progression_to_path(
+ source_path: String,
+ destination_path: String,
+ allow_legacy_plaintext: bool,
+ world_layout: StringName,
+ world_seed: int,
+) -> Dictionary:
+ if (
+ not _is_configured
+ or source_path.is_empty()
+ or destination_path.is_empty()
+ or not WorldLayoutType.is_valid(world_layout)
+ or world_seed <= 0
+ or world_seed > MAX_WORLD_SEED
+ ):
+ return {"ok": false, "message": "progression duplication is unavailable."}
+ var decoded: Dictionary = ProgressionSaveCodec.read_local_save(
+ source_path,
+ allow_legacy_plaintext,
+ )
+ if not bool(decoded.get("ok", false)):
+ return {"ok": false, "message": "the selected progression could not be read."}
+ var prepared: Dictionary = _prepare_external_save_data(decoded["data"])
+ if not bool(prepared.get("ok", false)):
+ return prepared
+ var save_data: Dictionary = (prepared["data"] as Dictionary).duplicate(true)
+ var world_data: Dictionary = {}
+ if typeof(save_data.get("world")) == TYPE_DICTIONARY:
+ world_data = (save_data["world"] as Dictionary).duplicate(true)
+ world_data["layout"] = String(world_layout)
+ world_data["seed"] = world_seed
+ save_data["world"] = world_data
+ prepared = _prepare_external_save_data(save_data)
+ if not bool(prepared.get("ok", false)):
+ return prepared
+ var result: Dictionary = _write_save_data_at_path(
+ prepared["data"],
+ destination_path,
+ PortableFileGuard.hash_file(destination_path),
+ )
+ if not bool(result.get("ok", false)):
+ return {"ok": false, "message": "the duplicated progression could not be written."}
+ return {
+ "ok": true,
+ "message": "save slot duplicated.",
+ "world_layout": String(world_layout),
+ "world_seed": world_seed,
+ }
+
+
+func initialize_new_game(
+ world_seed: int = DEFAULT_WORLD_SEED,
+ world_layout: StringName = WorldLayoutType.GENERATED,
+) -> bool:
+ if not _is_configured:
+ return false
+ if (
+ world_seed <= 0
+ or world_seed > MAX_WORLD_SEED
+ or not WorldLayoutType.is_valid(world_layout)
+ ):
return false
_automatic_saving_blocked = false
_restore_defaults()
+ _world_layout = world_layout
+ _world_seed = world_seed
+ _is_dirty = true
return true
+func get_world_layout() -> StringName:
+ return _world_layout
+
+
+func get_world_seed() -> int:
+ return _world_seed
+
+
+static func roll_world_seed() -> int:
+ var random := RandomNumberGenerator.new()
+ random.randomize()
+ return random.randi_range(1, MAX_WORLD_SEED)
+
+
func delete_progression_save() -> bool:
- if FileAccess.file_exists(_save_path) and not _remove_if_present(_save_path):
- return false
- for path: String in [_temp_path(), _backup_path()]:
+ for primary_path: String in [_save_path, _legacy_save_path()]:
+ if (
+ FileAccess.file_exists(primary_path)
+ and not _remove_if_present(primary_path)
+ ):
+ return false
+ for path: String in [
+ _temp_path(),
+ _backup_path(),
+ _codec_scratch_path(),
+ _legacy_save_path() + ".tmp",
+ _legacy_save_path() + ".backup",
+ ]:
if FileAccess.file_exists(path) and not _remove_if_present(path):
push_warning("Unable to remove stale player-save auxiliary file.")
if _autosave_timer != null:
@@ -429,16 +765,8 @@ func save_now() -> bool:
var save_data: Dictionary = _build_save_dictionary()
if save_data.is_empty():
return false
- var json_text: String = JSON.stringify(save_data, "\t")
- if json_text.is_empty():
- return false
-
- var result := PortableFileGuard.write_guarded(
- _save_path,
- json_text.to_utf8_buffer(),
- _expected_hash,
- _data_root.conflict_directory(),
- _data_root.device_id,
+ var result: Dictionary = _write_current_save_data(
+ save_data, _expected_hash
)
if bool(result.get("conflict", false)):
_data_root.report_conflict(
@@ -491,6 +819,20 @@ func _build_save_dictionary() -> Dictionary:
):
return {}
serialized_quality_masks[String(fish_id)] = quality_mask
+ var serialized_catch_counts: Dictionary = {}
+ var catch_counts: Dictionary[StringName, int] = (
+ _collection_log.get_catch_counts()
+ )
+ for fish_id: StringName in catch_counts:
+ var catch_count: int = catch_counts[fish_id]
+ if (
+ fish_id.is_empty()
+ or catch_count <= 0
+ or catch_count > CollectionLogType.MAX_CATCH_COUNT
+ or not _collection_log.has_discovered(fish_id)
+ ):
+ return {}
+ serialized_catch_counts[String(fish_id)] = catch_count
var serialized_items: Array[Dictionary] = []
for owned: OwnedItemType in _bag.get_all_items():
if owned == null or not owned.is_valid():
@@ -518,6 +860,17 @@ func _build_save_dictionary() -> Dictionary:
)
):
return {}
+ var active_bait_id: StringName = _player.active_bait_id
+ var active_lure_id: StringName = _player.active_lure_id
+ if (
+ (not active_bait_id.is_empty() and not _valid_active_tackle(
+ active_bait_id, true
+ ))
+ or (not active_lure_id.is_empty() and not _valid_active_tackle(
+ active_lure_id, false
+ ))
+ ):
+ return {}
return {
"save_version": SAVE_VERSION,
@@ -527,6 +880,7 @@ func _build_save_dictionary() -> Dictionary:
"collection": {
"discovered_fish_ids": discovered_strings,
"discovered_quality_masks": serialized_quality_masks,
+ "catch_counts": serialized_catch_counts,
},
"inventory": {
"next_catch_sequence": _inventory.get_next_catch_sequence(),
@@ -536,16 +890,23 @@ func _build_save_dictionary() -> Dictionary:
"items": serialized_items,
"unlocked_bait_ids": serialized_unlocked_baits,
},
+ "tackle": {
+ "active_bait_id": String(active_bait_id),
+ "active_lure_id": String(active_lure_id),
+ },
"hotbar": {
"selected_slot": _hotbar.get_selected_slot(),
"slots": serialized_slots,
"fish_slots": serialized_fish_slots,
},
+ "inventory_layout": _inventory_layout.to_save_data(),
"upgrades": _fishing_upgrades.to_save_data(),
"cooler": _cooler_capacity.to_save_data(),
"art": _art_unlocks.to_save_data(),
"experience": _experience.to_save_data(),
"world": {
+ "layout": String(_world_layout),
+ "seed": _world_seed,
"time_hours": _world_time.get_persistent_time_hours(),
"weather": int(_world_weather.get_persistent_weather()),
"weather_seconds_remaining": (
@@ -553,6 +914,7 @@ func _build_save_dictionary() -> Dictionary:
),
},
"jobs": _jobs.to_save_data(),
+ "home": _home_state.to_save_data(),
}
@@ -562,18 +924,24 @@ func _build_load_snapshot(save_data: Dictionary) -> LoadSnapshot:
or typeof(save_data.get("collection")) != TYPE_DICTIONARY
or typeof(save_data.get("inventory")) != TYPE_DICTIONARY
or typeof(save_data.get("bag")) != TYPE_DICTIONARY
+ or typeof(save_data.get("tackle")) != TYPE_DICTIONARY
or typeof(save_data.get("hotbar")) != TYPE_DICTIONARY
+ or typeof(save_data.get("inventory_layout")) != TYPE_DICTIONARY
or typeof(save_data.get("experience")) != TYPE_DICTIONARY
or typeof(save_data.get("jobs")) != TYPE_DICTIONARY
+ or typeof(save_data.get("home")) != TYPE_DICTIONARY
):
return null
var wallet_data: Dictionary = save_data["wallet"]
var collection_data: Dictionary = save_data["collection"]
var inventory_data: Dictionary = save_data["inventory"]
var bag_data: Dictionary = save_data["bag"]
+ var tackle_data: Dictionary = save_data["tackle"]
var hotbar_data: Dictionary = save_data["hotbar"]
+ var inventory_layout_data: Dictionary = save_data["inventory_layout"]
var experience_data: Dictionary = save_data["experience"]
var jobs_data: Dictionary = save_data["jobs"]
+ var home_data: Dictionary = save_data["home"]
var world_data: Dictionary = {}
if typeof(save_data.get("world")) == TYPE_DICTIONARY:
world_data = save_data["world"]
@@ -605,6 +973,12 @@ func _build_load_snapshot(save_data: Dictionary) -> LoadSnapshot:
and typeof(bag_data.get("unlocked_bait_ids")) != TYPE_ARRAY
)
or typeof(hotbar_data.get("slots")) != TYPE_ARRAY
+ or typeof(tackle_data.get("active_bait_id")) not in [
+ TYPE_STRING, TYPE_STRING_NAME,
+ ]
+ or typeof(tackle_data.get("active_lure_id")) not in [
+ TYPE_STRING, TYPE_STRING_NAME,
+ ]
or not hotbar_data.has("selected_slot")
or not experience_data.has("total_experience")
):
@@ -624,6 +998,21 @@ func _build_load_snapshot(save_data: Dictionary) -> LoadSnapshot:
return null
var snapshot := LoadSnapshot.new()
+ if world_data.has("layout"):
+ if not WorldLayoutType.is_valid(world_data["layout"]):
+ return null
+ snapshot.world_layout = WorldLayoutType.normalized(
+ world_data["layout"]
+ )
+ if world_data.has("seed"):
+ snapshot.world_seed = _read_integer(
+ world_data["seed"],
+ -1,
+ MAX_WORLD_SEED,
+ )
+ if snapshot.world_seed <= 0:
+ return null
+ snapshot.inventory_layout_data = inventory_layout_data.duplicate(true)
snapshot.wallet_balance = balance
snapshot.total_experience = _read_integer(
experience_data["total_experience"],
@@ -635,6 +1024,12 @@ func _build_load_snapshot(save_data: Dictionary) -> LoadSnapshot:
if not PlayerJobServiceType.validate_save_data(jobs_data):
return null
snapshot.jobs_data = jobs_data.duplicate(true)
+ var sanitized_home: Dictionary = PlayerHomeStateType.sanitize_save_data(
+ home_data
+ )
+ if sanitized_home.is_empty():
+ return null
+ snapshot.home_data = sanitized_home
if world_data.has("time_hours"):
snapshot.world_time_hours = _read_world_time_hours(
world_data["time_hours"]
@@ -696,6 +1091,30 @@ func _build_load_snapshot(save_data: Dictionary) -> LoadSnapshot:
):
return null
snapshot.discovered_quality_masks[fish_id] = mask
+ var has_saved_catch_counts: bool = collection_data.has("catch_counts")
+ if (
+ has_saved_catch_counts
+ and typeof(collection_data.get("catch_counts")) != TYPE_DICTIONARY
+ ):
+ return null
+ if has_saved_catch_counts:
+ var saved_catch_counts: Dictionary = collection_data["catch_counts"]
+ for key: Variant in saved_catch_counts:
+ if typeof(key) not in [TYPE_STRING, TYPE_STRING_NAME]:
+ return null
+ var fish_id := StringName(str(key))
+ var catch_count: int = _read_integer(
+ saved_catch_counts[key],
+ -1,
+ CollectionLogType.MAX_CATCH_COUNT,
+ )
+ if (
+ fish_id.is_empty()
+ or not seen_discoveries.has(fish_id)
+ or catch_count <= 0
+ ):
+ return null
+ snapshot.catch_counts[fish_id] = catch_count
var seen_ids: Dictionary[StringName, bool] = {}
var seen_sequences: Dictionary[int, bool] = {}
@@ -746,6 +1165,13 @@ func _build_load_snapshot(save_data: Dictionary) -> LoadSnapshot:
fish_catch.catch_sequence
)
snapshot.catches.append(fish_catch)
+ if (
+ not has_saved_catch_counts
+ and seen_discoveries.has(fish_catch.fish_id)
+ ):
+ snapshot.catch_counts[fish_catch.fish_id] = (
+ int(snapshot.catch_counts.get(fish_catch.fish_id, 0)) + 1
+ )
snapshot.next_catch_sequence = maxi(
requested_next_sequence,
@@ -793,6 +1219,11 @@ func _build_load_snapshot(save_data: Dictionary) -> LoadSnapshot:
var owned := OwnedItemType.new()
owned.item_id = item_id
owned.quantity = quantity
+ owned.storage_slot = _read_integer(
+ item_record.get("storage_slot", -1),
+ -1,
+ PlayerBagType.MAX_STORAGE_SLOT_INDEX,
+ )
seen_items[item_id] = true
snapshot.bag_items.append(owned)
@@ -823,6 +1254,16 @@ func _build_load_snapshot(save_data: Dictionary) -> LoadSnapshot:
):
seen_unlocked_baits[owned.item_id] = true
snapshot.unlocked_bait_ids.append(owned.item_id)
+ snapshot.active_bait_id = _validated_tackle_id(
+ StringName(str(tackle_data.get("active_bait_id", ""))),
+ true,
+ seen_items,
+ )
+ snapshot.active_lure_id = _validated_tackle_id(
+ StringName(str(tackle_data.get("active_lure_id", ""))),
+ false,
+ seen_items,
+ )
var slot_values: Array = hotbar_data["slots"]
snapshot.hotbar_slots.resize(PlayerHotbarType.SLOT_COUNT)
@@ -885,6 +1326,26 @@ func _build_load_snapshot(save_data: Dictionary) -> LoadSnapshot:
PlayerCoolerCapacityType.MAX_LEVEL,
"cooler.capacity_level"
)
+ # Before homes had upgrade tiers, storage was purchased separately. Preserve
+ # those saves by granting the starter RV to anybody who owned at least one
+ # legacy expansion. PlayerCoolerCapacity also retains the purchased legacy
+ # capacity so entries beyond the new RV's first 18 slots remain accessible.
+ if (
+ snapshot.cooler_capacity_level > 0
+ and int(snapshot.home_data.get("tier", 0))
+ == PlayerHomeStateType.STARTER_TIER
+ ):
+ var migrated_home: Dictionary = home_data.duplicate(true)
+ migrated_home["tier"] = PlayerHomeStateType.STARTER_RV_TIER
+ migrated_home["exterior_id"] = String(
+ PlayerHomeStateType.STARTER_RV_EXTERIOR_ID
+ )
+ var sanitized_migrated_home := (
+ PlayerHomeStateType.sanitize_save_data(migrated_home)
+ )
+ if sanitized_migrated_home.is_empty():
+ return null
+ snapshot.home_data = sanitized_migrated_home
snapshot.art_unlock_mask = _read_integer(
art_data.get("unlock_mask"),
0,
@@ -917,6 +1378,14 @@ func _migrate_save(
migrated = _migrate_version_5_to_6(migrated)
6:
migrated = _migrate_version_6_to_7(migrated)
+ 7:
+ migrated = _migrate_version_7_to_8(migrated)
+ 8:
+ migrated = _migrate_version_8_to_9(migrated)
+ 9:
+ migrated = _migrate_version_9_to_10(migrated)
+ 10:
+ migrated = _migrate_version_10_to_11(migrated)
_:
return {}
if migrated.is_empty():
@@ -1035,6 +1504,152 @@ func _migrate_version_6_to_7(data: Dictionary) -> Dictionary:
return migrated
+func _migrate_version_7_to_8(data: Dictionary) -> Dictionary:
+ var migrated: Dictionary = data.duplicate(true)
+ if (
+ typeof(migrated.get("bag")) != TYPE_DICTIONARY
+ or typeof(migrated.get("inventory")) != TYPE_DICTIONARY
+ or typeof(migrated.get("hotbar")) != TYPE_DICTIONARY
+ ):
+ return {}
+ var bag_data: Dictionary = migrated["bag"]
+ var inventory_data: Dictionary = migrated["inventory"]
+ var hotbar_data: Dictionary = migrated["hotbar"]
+ if (
+ typeof(bag_data.get("items")) != TYPE_ARRAY
+ or typeof(inventory_data.get("catches")) != TYPE_ARRAY
+ or typeof(hotbar_data.get("slots")) != TYPE_ARRAY
+ ):
+ return {}
+
+ var placements: Array[Dictionary] = []
+ var assigned: Dictionary[String, bool] = {}
+ var item_slots: Array = hotbar_data["slots"]
+ var fish_slots: Array = []
+ if typeof(hotbar_data.get("fish_slots")) == TYPE_ARRAY:
+ fish_slots = hotbar_data["fish_slots"]
+ for slot: int in PlayerHotbarType.SLOT_COUNT:
+ var item_id := StringName(
+ str(item_slots[slot]) if slot < item_slots.size() else ""
+ )
+ if not item_id.is_empty():
+ var item = _item_catalog.get_item_by_id(item_id)
+ if item != null and not item.is_bait() and not item.is_lure():
+ var key := PlayerInventoryLayoutType.item_key(item_id)
+ if not assigned.has(key):
+ placements.append({
+ "kind": PlayerInventoryLayoutType.EntryKind.ITEM,
+ "identity": String(item_id),
+ "container": PlayerInventoryLayoutType.InventoryContainer.HOTBAR,
+ "slot": slot,
+ })
+ assigned[key] = true
+ continue
+ var catch_id := StringName(
+ str(fish_slots[slot]) if slot < fish_slots.size() else ""
+ )
+ if catch_id.is_empty():
+ continue
+ var catch_key := PlayerInventoryLayoutType.catch_key(catch_id)
+ if assigned.has(catch_key):
+ continue
+ placements.append({
+ "kind": PlayerInventoryLayoutType.EntryKind.CATCH,
+ "identity": String(catch_id),
+ "container": PlayerInventoryLayoutType.InventoryContainer.HOTBAR,
+ "slot": slot,
+ })
+ assigned[catch_key] = true
+
+ var next_inventory_slot: int = 0
+ var next_storage_slot: int = 0
+ for value: Variant in bag_data["items"] as Array:
+ if typeof(value) != TYPE_DICTIONARY:
+ continue
+ var item_id := StringName(str((value as Dictionary).get("item_id", "")))
+ var item = _item_catalog.get_item_by_id(item_id)
+ if item == null or item.is_bait() or item.is_lure():
+ continue
+ var key := PlayerInventoryLayoutType.item_key(item_id)
+ if assigned.has(key):
+ continue
+ var target := PlayerInventoryLayoutType.InventoryContainer.INVENTORY
+ var target_slot := next_inventory_slot
+ if next_inventory_slot < PlayerInventoryLayoutType.INVENTORY_CAPACITIES[0]:
+ next_inventory_slot += 1
+ else:
+ target = PlayerInventoryLayoutType.InventoryContainer.STORAGE
+ target_slot = next_storage_slot
+ next_storage_slot += 1
+ placements.append({
+ "kind": PlayerInventoryLayoutType.EntryKind.ITEM,
+ "identity": String(item_id),
+ "container": target,
+ "slot": target_slot,
+ })
+ assigned[key] = true
+ for value: Variant in inventory_data["catches"] as Array:
+ if typeof(value) != TYPE_DICTIONARY:
+ continue
+ var catch_id := StringName(
+ str((value as Dictionary).get("catch_id", ""))
+ )
+ if catch_id.is_empty():
+ continue
+ var key := PlayerInventoryLayoutType.catch_key(catch_id)
+ if assigned.has(key):
+ continue
+ var target := PlayerInventoryLayoutType.InventoryContainer.INVENTORY
+ var target_slot := next_inventory_slot
+ if next_inventory_slot < PlayerInventoryLayoutType.INVENTORY_CAPACITIES[0]:
+ next_inventory_slot += 1
+ else:
+ target = PlayerInventoryLayoutType.InventoryContainer.STORAGE
+ target_slot = next_storage_slot
+ next_storage_slot += 1
+ placements.append({
+ "kind": PlayerInventoryLayoutType.EntryKind.CATCH,
+ "identity": String(catch_id),
+ "container": target,
+ "slot": target_slot,
+ })
+ assigned[key] = true
+ migrated["inventory_layout"] = {
+ "backpack_level": 0,
+ "placements": placements,
+ }
+ migrated["save_version"] = 8
+ return migrated
+
+
+func _migrate_version_8_to_9(data: Dictionary) -> Dictionary:
+ var migrated: Dictionary = data.duplicate(true)
+ var world_data: Dictionary = {}
+ if typeof(migrated.get("world")) == TYPE_DICTIONARY:
+ world_data = (migrated["world"] as Dictionary).duplicate(true)
+ world_data["seed"] = DEFAULT_WORLD_SEED
+ migrated["world"] = world_data
+ migrated["save_version"] = 9
+ return migrated
+
+
+func _migrate_version_9_to_10(data: Dictionary) -> Dictionary:
+ var migrated: Dictionary = data.duplicate(true)
+ migrated["tackle"] = {
+ "active_bait_id": "",
+ "active_lure_id": "",
+ }
+ migrated["save_version"] = 10
+ return migrated
+
+
+func _migrate_version_10_to_11(data: Dictionary) -> Dictionary:
+ var migrated: Dictionary = data.duplicate(true)
+ migrated["home"] = PlayerHomeStateType.default_save_data()
+ migrated["save_version"] = 11
+ return migrated
+
+
func _mark_dirty() -> void:
if (
_is_restoring
@@ -1084,33 +1699,177 @@ func _on_experience_changed(_total_experience: int, _level: int) -> void:
_mark_dirty()
+func _on_active_tackle_changed(_item_id: StringName) -> void:
+ _mark_dirty()
+
+
func _on_autosave_timeout() -> void:
if _is_dirty:
save_now()
func _recover_interrupted_write() -> void:
- if FileAccess.file_exists(_save_path):
- _remove_if_present(_temp_path())
- _remove_if_present(_backup_path())
+ _remove_if_present(_codec_scratch_path())
+ _recover_path_write(_save_path)
+ _recover_path_write(_legacy_save_path())
+
+
+func _recover_path_write(path: String) -> void:
+ var temporary: String = path + ".tmp"
+ var backup: String = path + ".backup"
+ if FileAccess.file_exists(path):
+ _remove_if_present(temporary)
+ _remove_if_present(backup)
return
- if FileAccess.file_exists(_backup_path()):
- _rename_file(_backup_path(), _save_path)
- _remove_if_present(_temp_path())
+ if FileAccess.file_exists(backup):
+ _rename_file(backup, path)
+ _remove_if_present(temporary)
-func _handle_corrupt_save(message: String) -> void:
+func _write_current_save_data(
+ save_data: Dictionary,
+ expected_hash: String,
+) -> Dictionary:
+ return _write_save_data_at_path(save_data, _save_path, expected_hash)
+
+
+func _write_save_data_at_path(
+ save_data: Dictionary,
+ destination_path: String,
+ expected_hash: String,
+) -> Dictionary:
+ var bytes: PackedByteArray = ProgressionSaveCodec.encode_local_save(
+ save_data,
+ destination_path + ".codec.tmp",
+ )
+ if bytes.is_empty():
+ return {"ok": false}
+ return PortableFileGuard.write_guarded(
+ destination_path,
+ bytes,
+ expected_hash,
+ _data_root.conflict_directory(),
+ _data_root.device_id,
+ )
+
+
+func _prepare_external_save_data(value: Variant) -> Dictionary:
+ if typeof(value) != TYPE_DICTIONARY:
+ return {"ok": false, "message": "progression data is malformed."}
+ var save_data: Dictionary = (value as Dictionary).duplicate(true)
+ var version: int = _read_integer(save_data.get("save_version"), -1)
+ if version > SAVE_VERSION:
+ return {
+ "ok": false,
+ "message": "this progression belongs to a newer game version.",
+ }
+ if version < 1:
+ return {"ok": false, "message": "the progression version is unsupported."}
+ if version != SAVE_VERSION:
+ save_data = _migrate_save(save_data, version)
+ if save_data.is_empty():
+ return {
+ "ok": false,
+ "message": "the progression version is unsupported.",
+ }
+ var snapshot: LoadSnapshot = _build_load_snapshot(save_data)
+ if snapshot == null:
+ return {"ok": false, "message": "the progression is structurally invalid."}
+ return {
+ "ok": true,
+ "data": save_data,
+ "catch_count": snapshot.catches.size(),
+ "wallet_balance": snapshot.wallet_balance,
+ "discovered_species_count": snapshot.discovered_ids.size(),
+ "world_layout": String(snapshot.world_layout),
+ "world_seed": snapshot.world_seed,
+ }
+
+
+func _migrate_legacy_plaintext_save(
+ save_data: Dictionary,
+ legacy_path: String,
+) -> void:
+ var result: Dictionary = _write_current_save_data(save_data, "")
+ if not bool(result.get("ok", false)):
+ push_warning("Legacy progression loaded but could not be encrypted yet.")
+ return
+ _expected_hash = str(result.get("hash", ""))
+ if not _archive_replaced_save(legacy_path, "plaintext-migration"):
+ push_warning(
+ "Legacy progression was encrypted, but its migration backup failed."
+ )
+ return
+ _remove_legacy_save_files()
+ print("Upgraded legacy plaintext progression to the opaque save format.")
+
+
+func _archive_replaced_save(
+ source_path: String,
+ reason: String = "before-import",
+) -> bool:
+ if source_path.is_empty() or not FileAccess.file_exists(source_path):
+ return true
+ var timestamp: String = Time.get_datetime_string_from_system().replace(
+ ":", "-"
+ )
+ var destination: String = _data_root.root_path.path_join(
+ "backups/saves/player-save-%s-%s%s"
+ % [reason, timestamp, ARCHIVE_EXTENSION]
+ )
+ if FileAccess.file_exists(destination):
+ destination = destination.trim_suffix(ARCHIVE_EXTENSION) + (
+ "-%s%s" % [Time.get_ticks_usec(), ARCHIVE_EXTENSION]
+ )
+ var decoded: Dictionary = ProgressionSaveCodec.read_local_save(
+ source_path,
+ source_path == _legacy_save_path(),
+ )
+ var bytes: PackedByteArray
+ if bool(decoded.get("ok", false)):
+ var prepared: Dictionary = _prepare_external_save_data(decoded["data"])
+ if bool(prepared.get("ok", false)):
+ bytes = ProgressionSaveCodec.encode_archive(
+ prepared["data"], destination + ".codec.tmp"
+ )
+ if bytes.is_empty():
+ bytes = PortableFileGuard.read_bytes(source_path)
+ destination = (
+ destination.trim_suffix(ARCHIVE_EXTENSION) + ".preserved"
+ )
+ if bytes.is_empty():
+ return false
+ var result: Dictionary = PortableFileGuard.write_guarded(
+ destination,
+ bytes,
+ "",
+ _data_root.conflict_directory(),
+ _data_root.device_id,
+ )
+ return bool(result.get("ok", false))
+
+
+func _remove_legacy_save_files() -> void:
+ for path: String in [
+ _legacy_save_path(),
+ _legacy_save_path() + ".tmp",
+ _legacy_save_path() + ".backup",
+ ]:
+ _remove_if_present(path)
+
+
+func _handle_corrupt_save(message: String, source_path: String) -> void:
push_warning(message)
_restore_defaults()
- if not FileAccess.file_exists(_save_path):
+ if not FileAccess.file_exists(source_path):
return
var timestamp: int = int(Time.get_unix_time_from_system())
var corrupt_path: String = (
- "%s.corrupt-%d" % [_save_path, timestamp]
+ "%s.corrupt-%d" % [source_path, timestamp]
)
if FileAccess.file_exists(corrupt_path):
corrupt_path += "-%d" % Time.get_ticks_usec()
- if not _rename_file(_save_path, corrupt_path):
+ if not _rename_file(source_path, corrupt_path):
_automatic_saving_blocked = true
push_warning(
"Corrupt player save was left in place; automatic saving is "
@@ -1125,6 +1884,7 @@ func _restore_defaults() -> void:
var empty_catches: Array[FishCatchType] = []
var empty_discoveries: Array[StringName] = []
var empty_quality_masks: Dictionary[StringName, int] = {}
+ var empty_catch_counts: Dictionary[StringName, int] = {}
var default_items: Array[OwnedItemType] = []
var basic_rod := OwnedItemType.new()
basic_rod.item_id = BASIC_ROD_ID
@@ -1135,28 +1895,82 @@ func _restore_defaults() -> void:
default_slots.fill(StringName())
default_slots[0] = BASIC_ROD_ID
_inventory.replace_all_catches(empty_catches, 1)
- _collection_log.replace_discovery_state(
+ _collection_log.replace_collection_state(
empty_discoveries,
empty_quality_masks,
+ empty_catch_counts,
)
_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)
+ _player.unequip_bait()
+ _player.unequip_lure()
_fishing_upgrades.reset_to_defaults()
_cooler_capacity.reset_to_defaults()
+ _inventory_layout.reset_to_defaults()
+ _hotbar.replace_state(default_slots, 0)
_art_unlocks.reset_to_defaults()
_experience.reset_to_defaults()
_world_time.restore_persistent_time_hours(
WorldTimeServiceType.DEFAULT_START_HOUR
)
_world_weather.reset_persistent_state()
+ _world_layout = WorldLayoutType.GENERATED
+ _world_seed = DEFAULT_WORLD_SEED
_jobs.reset_to_defaults()
+ _home_state.reset_to_defaults()
_is_restoring = false
_is_dirty = false
+func _valid_active_tackle(item_id: StringName, bait: bool) -> bool:
+ var item = _item_catalog.get_item_by_id(item_id)
+ return (
+ item != null
+ and item.is_available()
+ and (item.is_bait() if bait else item.is_lure())
+ and _bag.owns_item(item_id)
+ )
+
+
+func _validated_tackle_id(
+ item_id: StringName,
+ bait: bool,
+ seen_items: Dictionary[StringName, bool],
+) -> StringName:
+ if item_id.is_empty():
+ return StringName()
+ var item = _item_catalog.get_item_by_id(item_id)
+ if (
+ item == null
+ or not item.is_available()
+ or not seen_items.has(item_id)
+ or not (item.is_bait() if bait else item.is_lure())
+ ):
+ push_warning("Skipped invalid saved active tackle '%s'." % String(item_id))
+ return StringName()
+ return item_id
+
+
+func _restore_tackle_selection(snapshot: LoadSnapshot) -> bool:
+ _player.unequip_bait()
+ _player.unequip_lure()
+ var bait_restored: bool = (
+ snapshot.active_bait_id.is_empty()
+ or _player.equip_bait(
+ _item_catalog.get_item_by_id(snapshot.active_bait_id)
+ )
+ )
+ var lure_restored: bool = (
+ snapshot.active_lure_id.is_empty()
+ or _player.equip_lure(
+ _item_catalog.get_item_by_id(snapshot.active_lure_id)
+ )
+ )
+ return bait_restored and lure_restored
+
+
func _read_integer(
value: Variant,
invalid_value: int,
diff --git a/save/player_save_slot_catalog.gd b/save/player_save_slot_catalog.gd
new file mode 100644
index 0000000..d3b9d23
--- /dev/null
+++ b/save/player_save_slot_catalog.gd
@@ -0,0 +1,591 @@
+class_name PlayerSaveSlotCatalog
+extends RefCounted
+
+const FORMAT_VERSION: int = 1
+const MAX_SLOTS: int = 32
+const MAX_NAME_LENGTH: int = 32
+const LEGACY_SAVE_ID: StringName = &"player_save"
+
+signal slots_changed
+
+var _data_root: PlayerDataRoot
+var _save_manager: PlayerSaveManager
+var _manifest_path := ""
+var _slots_directory := ""
+var _slots: Array[Dictionary] = []
+var _active_slot_id := ""
+var _expected_hash := ""
+var _error_message := ""
+
+
+func configure(
+ data_root: PlayerDataRoot,
+ save_manager: PlayerSaveManager,
+) -> bool:
+ if data_root == null or save_manager == null or data_root.root_path.is_empty():
+ return false
+ _data_root = data_root
+ _save_manager = save_manager
+ _manifest_path = data_root.path_for(&"save_slots")
+ _slots_directory = data_root.root_path.path_join("player/saves")
+ _slots.clear()
+ _active_slot_id = ""
+ _expected_hash = ""
+ _error_message = ""
+ if DirAccess.make_dir_recursive_absolute(_slots_directory) != OK:
+ _error_message = "the save-slot directory could not be created."
+ return false
+ _recover_interrupted_manifest_write()
+ var loaded: bool = _load_manifest()
+ if not loaded and FileAccess.file_exists(_manifest_path):
+ if not _preserve_invalid_manifest():
+ _error_message = "the invalid save-slot catalog could not be preserved."
+ return false
+ var slot_count_before_discovery: int = _slots.size()
+ var active_before_discovery: String = _active_slot_id
+ _discover_untracked_saves()
+ if _active_slot_id.is_empty() and not _slots.is_empty():
+ _active_slot_id = str(_slots.front().get("slot_id", ""))
+ if (
+ not loaded
+ or _slots.size() != slot_count_before_discovery
+ or _active_slot_id != active_before_discovery
+ ):
+ if not _save_manifest():
+ return false
+ return _select_configured_storage()
+
+
+func get_error_message() -> String:
+ return _error_message
+
+
+func list_slots() -> Array[Dictionary]:
+ var result: Array[Dictionary] = []
+ for entry: Dictionary in _slots:
+ result.append(_build_slot_summary(entry))
+ result.sort_custom(
+ func(a: Dictionary, b: Dictionary) -> bool:
+ var a_played: int = int(a.get("last_played_at_unix", 0))
+ var b_played: int = int(b.get("last_played_at_unix", 0))
+ if a_played == b_played:
+ return int(a.get("created_at_unix", 0)) > int(
+ b.get("created_at_unix", 0)
+ )
+ return a_played > b_played
+ )
+ return result
+
+
+func get_slot(slot_id: String) -> Dictionary:
+ var entry: Dictionary = _find_slot(slot_id)
+ return _build_slot_summary(entry) if not entry.is_empty() else {}
+
+
+func get_active_slot_id() -> String:
+ return _active_slot_id
+
+
+func has_slots() -> bool:
+ return not _slots.is_empty()
+
+
+func ensure_active_slot() -> Dictionary:
+ if not _active_slot_id.is_empty() and not _find_slot(_active_slot_id).is_empty():
+ return {"ok": true, "slot_id": _active_slot_id}
+ var created: Dictionary = create_empty_slot(_next_default_name())
+ if not bool(created.get("ok", false)):
+ return created
+ var slot_id: String = str(created.get("slot_id", ""))
+ if not activate_slot(slot_id):
+ return {"ok": false, "message": "the new save slot could not be selected."}
+ return {"ok": true, "slot_id": slot_id}
+
+
+func create_empty_slot(display_name: String) -> Dictionary:
+ if _slots.size() >= MAX_SLOTS:
+ return {"ok": false, "message": "the maximum number of save slots has been reached."}
+ var clean_name: String = normalized_name(display_name)
+ if clean_name.is_empty():
+ return {"ok": false, "message": "enter a name for this save slot."}
+ var slot_id: String = _generate_slot_id()
+ var now: int = int(Time.get_unix_time_from_system())
+ _slots.append({
+ "slot_id": slot_id,
+ "display_name": clean_name,
+ "created_at_unix": now,
+ "last_played_at_unix": 0,
+ "legacy": false,
+ })
+ if not _save_manifest():
+ _slots.pop_back()
+ return {"ok": false, "message": "the save-slot catalog could not be updated."}
+ slots_changed.emit()
+ return {"ok": true, "slot_id": slot_id}
+
+
+func duplicate_slot(
+ source_slot_id: String,
+ display_name: String,
+ world_layout: StringName,
+ world_seed: int,
+) -> Dictionary:
+ if _slots.size() >= MAX_SLOTS:
+ return {"ok": false, "message": "the maximum number of save slots has been reached."}
+ var source: Dictionary = _find_slot(source_slot_id)
+ var clean_name: String = normalized_name(display_name)
+ if source.is_empty() or clean_name.is_empty():
+ return {"ok": false, "message": "the selected save slot cannot be duplicated."}
+ var source_path: String = _existing_slot_path(source)
+ if source_path.is_empty():
+ return {"ok": false, "message": "the selected save slot has no progression to duplicate."}
+ var slot_id: String = _generate_slot_id()
+ var destination: String = _slots_directory.path_join(slot_id + ".nfsave")
+ var copied: Dictionary = _save_manager.copy_progression_to_path(
+ source_path,
+ destination,
+ source_path.ends_with(".json"),
+ world_layout,
+ world_seed,
+ )
+ if not bool(copied.get("ok", false)):
+ return copied
+ var now: int = int(Time.get_unix_time_from_system())
+ _slots.append({
+ "slot_id": slot_id,
+ "display_name": clean_name,
+ "created_at_unix": now,
+ "last_played_at_unix": 0,
+ "legacy": false,
+ })
+ if not _save_manifest():
+ _slots.pop_back()
+ _remove_if_present(destination)
+ return {"ok": false, "message": "the duplicated slot could not be recorded."}
+ slots_changed.emit()
+ return {"ok": true, "slot_id": slot_id}
+
+
+func import_slot(archive_path: String, display_name: String) -> Dictionary:
+ if _slots.size() >= MAX_SLOTS:
+ return {"ok": false, "message": "the maximum number of save slots has been reached."}
+ var clean_name: String = normalized_name(display_name)
+ if clean_name.is_empty():
+ return {"ok": false, "message": "the imported save needs a slot name."}
+ var slot_id: String = _generate_slot_id()
+ var destination: String = _slots_directory.path_join(slot_id + ".nfsave")
+ var installed: Dictionary = _save_manager.install_progression_archive_at_path(
+ archive_path,
+ destination,
+ )
+ if not bool(installed.get("ok", false)):
+ return installed
+ var now: int = int(Time.get_unix_time_from_system())
+ _slots.append({
+ "slot_id": slot_id,
+ "display_name": clean_name,
+ "created_at_unix": now,
+ "last_played_at_unix": 0,
+ "legacy": false,
+ })
+ if not _save_manifest():
+ _slots.pop_back()
+ _remove_if_present(destination)
+ return {"ok": false, "message": "the imported slot could not be recorded."}
+ slots_changed.emit()
+ installed["slot_id"] = slot_id
+ return installed
+
+
+func export_slot(slot_id: String, destination_path: String) -> Dictionary:
+ var entry: Dictionary = _find_slot(slot_id)
+ var source_path: String = _existing_slot_path(entry)
+ if entry.is_empty() or source_path.is_empty():
+ return {"ok": false, "message": "the selected save slot cannot be exported."}
+ return _save_manager.export_progression_archive_from_path(
+ source_path,
+ destination_path,
+ source_path.ends_with(".json"),
+ )
+
+
+func activate_slot(slot_id: String) -> bool:
+ var entry: Dictionary = _find_slot(slot_id)
+ if entry.is_empty():
+ return false
+ var previous_id: String = _active_slot_id
+ var previous_path: String = _configured_storage_path(previous_id)
+ var next_path: String = _slot_primary_path(entry)
+ if not _save_manager.select_storage(next_path, _data_root):
+ return false
+ _active_slot_id = slot_id
+ if _save_manifest():
+ slots_changed.emit()
+ return true
+ _active_slot_id = previous_id
+ _save_manager.select_storage(previous_path, _data_root)
+ return false
+
+
+func mark_played(slot_id: String) -> bool:
+ var index: int = _find_slot_index(slot_id)
+ if index < 0:
+ return false
+ var previous_played_at: int = int(
+ _slots[index].get("last_played_at_unix", 0)
+ )
+ var previous_active: String = _active_slot_id
+ _slots[index]["last_played_at_unix"] = int(Time.get_unix_time_from_system())
+ _active_slot_id = slot_id
+ if not _save_manifest():
+ _slots[index]["last_played_at_unix"] = previous_played_at
+ _active_slot_id = previous_active
+ return false
+ slots_changed.emit()
+ return true
+
+
+func rename_slot(slot_id: String, display_name: String) -> bool:
+ var index: int = _find_slot_index(slot_id)
+ var clean_name: String = normalized_name(display_name)
+ if index < 0 or clean_name.is_empty():
+ return false
+ var previous: String = str(_slots[index].get("display_name", ""))
+ _slots[index]["display_name"] = clean_name
+ if not _save_manifest():
+ _slots[index]["display_name"] = previous
+ return false
+ slots_changed.emit()
+ return true
+
+
+func delete_slot(slot_id: String) -> bool:
+ var index: int = _find_slot_index(slot_id)
+ if index < 0:
+ return false
+ var previous_slots: Array[Dictionary] = []
+ for previous_entry: Dictionary in _slots:
+ previous_slots.append(previous_entry.duplicate(true))
+ var previous_active: String = _active_slot_id
+ var entry: Dictionary = _slots[index]
+ var source_path: String = _existing_slot_path(entry)
+ var preserved_path := ""
+ if not source_path.is_empty():
+ preserved_path = _deleted_backup_path(entry, source_path)
+ if not _rename_file(source_path, preserved_path):
+ return false
+ _slots.remove_at(index)
+ if _active_slot_id == slot_id:
+ _active_slot_id = (
+ str(_slots.front().get("slot_id", ""))
+ if not _slots.is_empty()
+ else ""
+ )
+ if not _select_configured_storage() or not _save_manifest():
+ _slots = previous_slots
+ _active_slot_id = previous_active
+ _select_configured_storage()
+ if not preserved_path.is_empty():
+ _rename_file(preserved_path, source_path)
+ return false
+ for auxiliary: String in _slot_auxiliary_paths(entry):
+ _remove_if_present(auxiliary)
+ slots_changed.emit()
+ return true
+
+
+static func normalized_name(value: String) -> String:
+ var clean_name: String = value.strip_edges().left(MAX_NAME_LENGTH)
+ if clean_name.is_empty():
+ return ""
+ for index: int in clean_name.length():
+ var codepoint: int = clean_name.unicode_at(index)
+ if codepoint < 32 or codepoint == 127:
+ return ""
+ return clean_name
+
+
+func _build_slot_summary(entry: Dictionary) -> Dictionary:
+ if entry.is_empty():
+ return {}
+ var summary: Dictionary = entry.duplicate(true)
+ var path: String = _existing_slot_path(entry)
+ var inspection: PlayerSaveInspection = _save_manager.inspect_progression_at_path(
+ path,
+ path.ends_with(".json"),
+ )
+ summary["active"] = str(entry.get("slot_id", "")) == _active_slot_id
+ summary["has_save"] = inspection.can_continue()
+ summary["save_status"] = inspection.status
+ summary["status_message"] = inspection.message
+ summary["catch_count"] = inspection.catch_count
+ summary["wallet_balance"] = inspection.wallet_balance
+ summary["discovered_species_count"] = inspection.discovered_species_count
+ summary["player_level"] = inspection.player_level
+ summary["world_layout"] = String(inspection.world_layout)
+ summary["world_seed"] = inspection.world_seed
+ return summary
+
+
+func _load_manifest() -> bool:
+ if not FileAccess.file_exists(_manifest_path):
+ return false
+ var file := FileAccess.open(_manifest_path, FileAccess.READ)
+ if file == null:
+ return false
+ var parser := JSON.new()
+ var error: Error = parser.parse(file.get_as_text())
+ file.close()
+ if error != OK or typeof(parser.data) != TYPE_DICTIONARY:
+ return false
+ var data: Dictionary = parser.data
+ if int(data.get("format_version", -1)) != FORMAT_VERSION:
+ return false
+ if typeof(data.get("slots")) != TYPE_ARRAY:
+ return false
+ var loaded_slots: Array[Dictionary] = []
+ var seen_ids: Dictionary[String, bool] = {}
+ for value: Variant in data["slots"]:
+ if typeof(value) != TYPE_DICTIONARY:
+ return false
+ var entry: Dictionary = (value as Dictionary).duplicate(true)
+ if not _valid_slot_entry(entry):
+ return false
+ var slot_id: String = str(entry["slot_id"])
+ if seen_ids.has(slot_id):
+ return false
+ seen_ids[slot_id] = true
+ loaded_slots.append(entry)
+ _slots = loaded_slots
+ _active_slot_id = str(data.get("active_slot_id", ""))
+ if not _active_slot_id.is_empty() and not seen_ids.has(_active_slot_id):
+ _active_slot_id = ""
+ _expected_hash = PortableFileGuard.hash_file(_manifest_path)
+ return true
+
+
+func _save_manifest() -> bool:
+ var data: Dictionary = {
+ "format_version": FORMAT_VERSION,
+ "active_slot_id": _active_slot_id,
+ "slots": _slots,
+ }
+ var result: Dictionary = PortableFileGuard.write_guarded(
+ _manifest_path,
+ JSON.stringify(data, "\t").to_utf8_buffer(),
+ _expected_hash,
+ _data_root.conflict_directory(),
+ _data_root.device_id,
+ )
+ if bool(result.get("conflict", false)):
+ _data_root.report_conflict(
+ str(result.get("message", "")),
+ str(result.get("conflict_path", "")),
+ )
+ if bool(result.get("ok", false)):
+ _expected_hash = str(result.get("hash", ""))
+ return true
+ _error_message = "the save-slot catalog could not be written safely."
+ return false
+
+
+func _discover_untracked_saves() -> void:
+ var known_ids: Dictionary[String, bool] = {}
+ var has_legacy: bool = false
+ for entry: Dictionary in _slots:
+ known_ids[str(entry.get("slot_id", ""))] = true
+ has_legacy = has_legacy or bool(entry.get("legacy", false))
+ var legacy_primary: String = _data_root.path_for(LEGACY_SAVE_ID)
+ var legacy_plaintext: String = legacy_primary.get_base_dir().path_join(
+ PlayerSaveManager.LEGACY_SAVE_FILENAME
+ )
+ if (
+ not has_legacy
+ and (
+ FileAccess.file_exists(legacy_primary)
+ or FileAccess.file_exists(legacy_plaintext)
+ )
+ ):
+ var legacy_id: String = _generate_slot_id()
+ _slots.append({
+ "slot_id": legacy_id,
+ "display_name": _next_default_name(),
+ "created_at_unix": int(Time.get_unix_time_from_system()),
+ "last_played_at_unix": 0,
+ "legacy": true,
+ })
+ known_ids[legacy_id] = true
+ var directory := DirAccess.open(_slots_directory)
+ if directory == null:
+ return
+ directory.list_dir_begin()
+ var filename: String = directory.get_next()
+ while not filename.is_empty():
+ if not directory.current_is_dir() and filename.ends_with(".nfsave"):
+ var slot_id: String = filename.trim_suffix(".nfsave")
+ if _valid_slot_id(slot_id) and not known_ids.has(slot_id):
+ _slots.append({
+ "slot_id": slot_id,
+ "display_name": _next_default_name(),
+ "created_at_unix": int(Time.get_unix_time_from_system()),
+ "last_played_at_unix": 0,
+ "legacy": false,
+ })
+ known_ids[slot_id] = true
+ filename = directory.get_next()
+ directory.list_dir_end()
+
+
+func _select_configured_storage() -> bool:
+ return _save_manager.select_storage(
+ _configured_storage_path(_active_slot_id),
+ _data_root,
+ )
+
+
+func _configured_storage_path(slot_id: String) -> String:
+ var entry: Dictionary = _find_slot(slot_id)
+ if not entry.is_empty():
+ return _slot_primary_path(entry)
+ return _data_root.path_for(LEGACY_SAVE_ID)
+
+
+func _slot_primary_path(entry: Dictionary) -> String:
+ if bool(entry.get("legacy", false)):
+ return _data_root.path_for(LEGACY_SAVE_ID)
+ return _slots_directory.path_join(str(entry.get("slot_id", "")) + ".nfsave")
+
+
+func _existing_slot_path(entry: Dictionary) -> String:
+ if entry.is_empty():
+ return ""
+ var primary: String = _slot_primary_path(entry)
+ if FileAccess.file_exists(primary):
+ return primary
+ if bool(entry.get("legacy", false)):
+ var plaintext: String = primary.get_base_dir().path_join(
+ PlayerSaveManager.LEGACY_SAVE_FILENAME
+ )
+ if FileAccess.file_exists(plaintext):
+ return plaintext
+ return ""
+
+
+func _slot_auxiliary_paths(entry: Dictionary) -> Array[String]:
+ var primary: String = _slot_primary_path(entry)
+ var paths: Array[String] = [
+ primary + ".tmp",
+ primary + ".backup",
+ primary + ".codec.tmp",
+ ]
+ if bool(entry.get("legacy", false)):
+ var plaintext: String = primary.get_base_dir().path_join(
+ PlayerSaveManager.LEGACY_SAVE_FILENAME
+ )
+ paths.append_array([
+ plaintext + ".tmp",
+ plaintext + ".backup",
+ ])
+ return paths
+
+
+func _deleted_backup_path(entry: Dictionary, source_path: String) -> String:
+ var timestamp: String = Time.get_datetime_string_from_system().replace(":", "-")
+ var extension: String = ".json" if source_path.ends_with(".json") else ".nfsave"
+ var destination: String = _data_root.root_path.path_join(
+ "backups/saves/deleted-slot-%s-%s%s"
+ % [str(entry.get("slot_id", "")).left(8), timestamp, extension]
+ )
+ if FileAccess.file_exists(destination):
+ destination = destination.trim_suffix(extension) + (
+ "-%d%s" % [Time.get_ticks_usec(), extension]
+ )
+ return destination
+
+
+func _find_slot(slot_id: String) -> Dictionary:
+ var index: int = _find_slot_index(slot_id)
+ return _slots[index] if index >= 0 else {}
+
+
+func _find_slot_index(slot_id: String) -> int:
+ for index: int in _slots.size():
+ if str(_slots[index].get("slot_id", "")) == slot_id:
+ return index
+ return -1
+
+
+func _next_default_name() -> String:
+ var used: Dictionary[String, bool] = {}
+ for entry: Dictionary in _slots:
+ used[str(entry.get("display_name", "")).to_lower()] = true
+ var number: int = 1
+ while used.has("save %d" % number):
+ number += 1
+ return "save %d" % number
+
+
+func _generate_slot_id() -> String:
+ var slot_id: String = Crypto.new().generate_random_bytes(16).hex_encode()
+ while _find_slot_index(slot_id) >= 0:
+ slot_id = Crypto.new().generate_random_bytes(16).hex_encode()
+ return slot_id
+
+
+func _valid_slot_entry(entry: Dictionary) -> bool:
+ var display_name: String = str(entry.get("display_name", ""))
+ return (
+ _valid_slot_id(str(entry.get("slot_id", "")))
+ and display_name.length() <= MAX_NAME_LENGTH
+ and not normalized_name(display_name).is_empty()
+ and typeof(entry.get("created_at_unix")) in [TYPE_INT, TYPE_FLOAT]
+ and typeof(entry.get("last_played_at_unix")) in [TYPE_INT, TYPE_FLOAT]
+ and typeof(entry.get("legacy")) == TYPE_BOOL
+ )
+
+
+func _valid_slot_id(slot_id: String) -> bool:
+ if slot_id.length() != 32:
+ return false
+ for index: int in slot_id.length():
+ var character: String = slot_id.substr(index, 1).to_lower()
+ if character not in "0123456789abcdef":
+ return false
+ return true
+
+
+func _recover_interrupted_manifest_write() -> void:
+ var temporary: String = _manifest_path + ".tmp"
+ var backup: String = _manifest_path + ".backup"
+ if FileAccess.file_exists(_manifest_path):
+ _remove_if_present(temporary)
+ _remove_if_present(backup)
+ return
+ if FileAccess.file_exists(backup):
+ _rename_file(backup, _manifest_path)
+ _remove_if_present(temporary)
+
+
+func _preserve_invalid_manifest() -> bool:
+ var destination: String = _data_root.migration_backup_directory().path_join(
+ "invalid-save-slots-%s.json"
+ % Time.get_datetime_string_from_system().replace(":", "-")
+ )
+ if not _rename_file(_manifest_path, destination):
+ return false
+ _expected_hash = ""
+ _slots.clear()
+ _active_slot_id = ""
+ return true
+
+
+func _rename_file(source: String, destination: String) -> bool:
+ if source.is_empty() or destination.is_empty():
+ return false
+ if DirAccess.make_dir_recursive_absolute(destination.get_base_dir()) != OK:
+ return false
+ return DirAccess.rename_absolute(source, destination) == OK
+
+
+func _remove_if_present(path: String) -> bool:
+ return not FileAccess.file_exists(path) or DirAccess.remove_absolute(path) == OK
diff --git a/save/player_save_slot_catalog.gd.uid b/save/player_save_slot_catalog.gd.uid
new file mode 100644
index 0000000..8906f5a
--- /dev/null
+++ b/save/player_save_slot_catalog.gd.uid
@@ -0,0 +1 @@
+uid://djfeifv7xrqdt
diff --git a/save/progression_save_codec.gd b/save/progression_save_codec.gd
new file mode 100644
index 0000000..4d0ba33
--- /dev/null
+++ b/save/progression_save_codec.gd
@@ -0,0 +1,191 @@
+class_name ProgressionSaveCodec
+extends RefCounted
+
+const MAGIC := "straywild_PROGRESSION_CONTAINER"
+const LEGACY_MAGIC := "NETFISHING_PROGRESSION_CONTAINER"
+const FORMAT_VERSION := 1
+const LOCAL_KIND := "local_save"
+const ARCHIVE_KIND := "portable_archive"
+const MAX_CONTAINER_BYTES := 16 * 1024 * 1024
+
+# This key keeps routine progression files opaque to casual editing. Since the
+# client is open source, it is intentionally not treated as a security secret.
+# Its original value is a stable file-format detail so released saves remain
+# readable after the Straywild rebrand.
+const CONTAINER_PASSPHRASE := (
+ "NETfishing progression container v1 / not an identity credential"
+)
+
+
+static func encode_local_save(
+ save_data: Dictionary,
+ scratch_path: String,
+) -> PackedByteArray:
+ return _encode_container(LOCAL_KIND, save_data, scratch_path)
+
+
+static func encode_archive(
+ save_data: Dictionary,
+ scratch_path: String,
+) -> PackedByteArray:
+ return _encode_container(ARCHIVE_KIND, save_data, scratch_path)
+
+
+static func read_local_save(
+ path: String,
+ allow_legacy_plaintext: bool = false,
+) -> Dictionary:
+ if allow_legacy_plaintext and not _looks_encrypted(path):
+ var legacy: Dictionary = _read_plaintext_dictionary(path)
+ if bool(legacy.get("ok", false)):
+ legacy["legacy_plaintext"] = true
+ return legacy
+ return _read_container(path, LOCAL_KIND)
+
+
+static func read_archive(path: String) -> Dictionary:
+ if not _looks_encrypted(path):
+ return {"ok": false, "message": "progression archive is not recognized."}
+ return _read_container(path, ARCHIVE_KIND)
+
+
+static func _encode_container(
+ kind: String,
+ save_data: Dictionary,
+ scratch_path: String,
+) -> PackedByteArray:
+ if save_data.is_empty() or scratch_path.is_empty():
+ return PackedByteArray()
+ var payload_json: String = JSON.stringify(save_data)
+ if payload_json.is_empty():
+ return PackedByteArray()
+ var payload_bytes: PackedByteArray = payload_json.to_utf8_buffer()
+ var envelope: Dictionary = {
+ "magic": MAGIC,
+ "format_version": FORMAT_VERSION,
+ "kind": kind,
+ "game_version": str(
+ ProjectSettings.get_setting("application/config/version", "")
+ ),
+ "created_at_unix": int(Time.get_unix_time_from_system()),
+ "payload_sha256": PortableFileGuard.hash_bytes(payload_bytes),
+ "payload_json": payload_json,
+ }
+ if DirAccess.make_dir_recursive_absolute(
+ scratch_path.get_base_dir()
+ ) != OK:
+ return PackedByteArray()
+ _remove_if_present(scratch_path)
+ var file: FileAccess = FileAccess.open_encrypted_with_pass(
+ scratch_path,
+ FileAccess.WRITE,
+ CONTAINER_PASSPHRASE,
+ )
+ if file == null:
+ return PackedByteArray()
+ file.store_string(JSON.stringify(envelope))
+ file.flush()
+ var write_ok: bool = file.get_error() == OK
+ file.close()
+ if not write_ok:
+ _remove_if_present(scratch_path)
+ return PackedByteArray()
+ var encoded: PackedByteArray = PortableFileGuard.read_bytes(
+ scratch_path,
+ MAX_CONTAINER_BYTES,
+ )
+ _remove_if_present(scratch_path)
+ return encoded
+
+
+static func _read_container(path: String, expected_kind: String) -> Dictionary:
+ if (
+ path.is_empty()
+ or not FileAccess.file_exists(path)
+ or FileAccess.get_size(path) > MAX_CONTAINER_BYTES
+ ):
+ return {"ok": false, "message": "progression file is unavailable."}
+ var envelope_text: String = _read_encrypted_text(path)
+ if envelope_text.is_empty():
+ return {"ok": false, "message": "progression file could not be opened."}
+ var envelope_json := JSON.new()
+ if (
+ envelope_json.parse(envelope_text) != OK
+ or typeof(envelope_json.data) != TYPE_DICTIONARY
+ ):
+ return {"ok": false, "message": "progression container is malformed."}
+ var envelope: Dictionary = envelope_json.data
+ var legacy_container: bool = envelope.get("magic") == LEGACY_MAGIC
+ if (
+ envelope.get("magic") not in [MAGIC, LEGACY_MAGIC]
+ or int(envelope.get("format_version", -1)) != FORMAT_VERSION
+ or str(envelope.get("kind", "")) != expected_kind
+ ):
+ return {"ok": false, "message": "progression container is unsupported."}
+ var payload_json: String = str(envelope.get("payload_json", ""))
+ var payload_bytes: PackedByteArray = payload_json.to_utf8_buffer()
+ if (
+ payload_json.is_empty()
+ or str(envelope.get("payload_sha256", ""))
+ != PortableFileGuard.hash_bytes(payload_bytes)
+ ):
+ return {"ok": false, "message": "progression container failed validation."}
+ var payload_parser := JSON.new()
+ if (
+ payload_parser.parse(payload_json) != OK
+ or typeof(payload_parser.data) != TYPE_DICTIONARY
+ ):
+ return {"ok": false, "message": "progression payload is malformed."}
+ return {
+ "ok": true,
+ "data": payload_parser.data,
+ "game_version": str(envelope.get("game_version", "")),
+ "created_at_unix": int(envelope.get("created_at_unix", 0)),
+ "legacy_plaintext": false,
+ "legacy_container": legacy_container,
+ }
+
+
+static func _read_encrypted_text(path: String) -> String:
+ var file: FileAccess = FileAccess.open_encrypted_with_pass(
+ path,
+ FileAccess.READ,
+ CONTAINER_PASSPHRASE,
+ )
+ if file == null:
+ return ""
+ var text: String = file.get_as_text()
+ file.close()
+ return text
+
+
+static func _read_plaintext_dictionary(path: String) -> Dictionary:
+ var bytes: PackedByteArray = PortableFileGuard.read_bytes(
+ path,
+ MAX_CONTAINER_BYTES,
+ )
+ if bytes.is_empty():
+ return {"ok": false}
+ var text: String = bytes.get_string_from_utf8()
+ if not text.strip_edges().begins_with("{"):
+ return {"ok": false}
+ var parser := JSON.new()
+ if parser.parse(text) != OK or typeof(parser.data) != TYPE_DICTIONARY:
+ return {"ok": false}
+ return {"ok": true, "data": parser.data}
+
+
+static func _looks_encrypted(path: String) -> bool:
+ if not FileAccess.file_exists(path) or FileAccess.get_size(path) < 4:
+ return false
+ var file := FileAccess.open(path, FileAccess.READ)
+ if file == null:
+ return false
+ var header: PackedByteArray = file.get_buffer(4)
+ file.close()
+ return header == PackedByteArray([0x47, 0x44, 0x45, 0x43])
+
+
+static func _remove_if_present(path: String) -> void:
+ if FileAccess.file_exists(path):
+ DirAccess.remove_absolute(path)
diff --git a/save/progression_save_codec.gd.uid b/save/progression_save_codec.gd.uid
new file mode 100644
index 0000000..6db5823
--- /dev/null
+++ b/save/progression_save_codec.gd.uid
@@ -0,0 +1 @@
+uid://lad8mvedktww
diff --git a/scripts/build_android_debug.sh b/scripts/build_android_debug.sh
index 8f856f8..f68eb32 100755
--- a/scripts/build_android_debug.sh
+++ b/scripts/build_android_debug.sh
@@ -4,7 +4,7 @@ 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 OUTPUT_PATH="${PROJECT_ROOT}/builds/android/straywild-debug.apk"
readonly GODOT_BIN="${GODOT_BIN:-godot}"
mkdir -p -- "$(dirname -- "${OUTPUT_PATH}")"
diff --git a/scripts/build_playtest.sh b/scripts/build_playtest.sh
index 7ddf4f9..d54ba70 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.6.5-alpha"
+readonly BUILD_ROOT="${PROJECT_ROOT}/builds/v0.20.2-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.6.5-alpha-windows-x86_64.zip"
-readonly LINUX_ZIP="${BUILD_ROOT}/NETfishing-v0.6.5-alpha-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/straywild"
+readonly WINDOWS_ZIP="${BUILD_ROOT}/straywild-v0.20.2-alpha-windows-x86_64.zip"
+readonly LINUX_ZIP="${BUILD_ROOT}/straywild-v0.20.2-alpha-linux-x86_64.zip"
readonly GODOT_BIN="${GODOT_BIN:-godot}"
if [[ ! -f "${PROJECT_ROOT}/project.godot" ]]; then
@@ -29,29 +31,64 @@ mkdir -p -- "${WINDOWS_DIR}" "${LINUX_DIR}"
--headless \
--path "${PROJECT_ROOT}" \
--export-release "Windows Desktop" \
- "${WINDOWS_DIR}/NETfishing.exe"
+ "${WINDOWS_DIR}/straywild.exe"
"${GODOT_BIN}" \
--headless \
--path "${PROJECT_ROOT}" \
--export-release "Linux Desktop" \
- "${LINUX_DIR}/NETfishing.x86_64"
+ "${LINUX_DIR}/straywild.x86_64"
for required_file in \
- "${WINDOWS_DIR}/NETfishing.exe" \
- "${WINDOWS_DIR}/NETfishing.pck" \
- "${LINUX_DIR}/NETfishing.x86_64" \
- "${LINUX_DIR}/NETfishing.pck"; do
+ "${WINDOWS_DIR}/straywild.exe" \
+ "${WINDOWS_DIR}/straywild.pck" \
+ "${LINUX_DIR}/straywild.x86_64" \
+ "${LINUX_DIR}/straywild.pck"; do
if [[ ! -s "${required_file}" ]]; then
echo "error: expected export output is missing: ${required_file}" >&2
exit 1
fi
done
-chmod +x -- "${LINUX_DIR}/NETfishing.x86_64"
+chmod +x -- "${LINUX_DIR}/straywild.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}/straywild-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
+ ;;
+ esac
shift
-fi
-if [[ $# -ne 0 ]]; then
- echo "Usage: $0 [--package-only]" >&2
+done
+if [[ ${HOTFIX} -eq 1 && ${PACKAGE_ONLY} -ne 1 ]]; then
+ echo "PortMaster hotfixes must use an existing pinned ARM64 export." >&2
+ echo "Run this script with both --package-only and --hotfix." >&2
exit 2
fi
@@ -28,20 +42,39 @@ 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 ARM64_EXECUTABLE="${ARM64_ROOT}/straywild.arm64"
+readonly ARM64_PCK="${ARM64_ROOT}/straywild.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 GAME_ROOT="${STAGE_ROOT}/straywild"
+readonly ARCHIVE="${RELEASE_ROOT}/straywild.zip"
+readonly OBSOLETE_VERSIONED_ARCHIVE="${RELEASE_ROOT}/straywild-${RELEASE_TAG}-portmaster-arm64.zip"
+readonly OBSOLETE_LOWERCASE_ARCHIVE="${RELEASE_ROOT}/straywild-${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
+if [[ ${HOTFIX} -eq 1 ]]; then
+ if ! git -C "${PROJECT_ROOT}" merge-base --is-ancestor \
+ "${TAG_COMMIT}" "${HEAD_COMMIT}"; then
+ echo "${RELEASE_TAG} is not an ancestor of HEAD. Refusing hotfix package." >&2
+ exit 1
+ fi
+
+ while IFS= read -r changed_path; do
+ case "${changed_path}" in
+ docs/PORTMASTER.md|scripts/build_portmaster.sh|scripts/portmaster/*)
+ ;;
+ *)
+ echo "Hotfix contains a non-PortMaster change: ${changed_path}" >&2
+ exit 1
+ ;;
+ esac
+ done < <(git -C "${PROJECT_ROOT}" diff --name-only \
+ "${TAG_COMMIT}..${HEAD_COMMIT}")
+elif [[ "${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
+if [[ "${straywild_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
@@ -54,7 +87,7 @@ if [[ "${NETFISHING_ALLOW_DIRTY_RELEASE:-0}" != "1" ]]; then
fi
fi
-for command_name in git file zip unzip sha256sum; do
+for command_name in git file zip unzip sha256sum rsync; do
command -v "${command_name}" >/dev/null 2>&1 || {
echo "Required command not found: ${command_name}" >&2
exit 1
@@ -63,11 +96,38 @@ done
mkdir -p -- "${ARM64_ROOT}" "${RELEASE_ROOT}"
if [[ ${PACKAGE_ONLY} -eq 0 ]]; then
+ readonly EXPORT_PROJECT_ROOT="$(
+ mktemp -d -t straywild-portmaster-export.XXXXXX
+ )"
+ cleanup_export_project() {
+ rm -rf -- "${EXPORT_PROJECT_ROOT}"
+ }
+ trap cleanup_export_project EXIT INT TERM
rm -f -- "${ARM64_EXECUTABLE}" "${ARM64_PCK}"
+ rsync -a \
+ --exclude '/.git/' \
+ --exclude '/.godot/' \
+ --exclude '/art/source/' \
+ --exclude '/art/exported/system_icons/straywild.ico' \
+ --exclude '/art/exported/system_icons/straywild_1024.png' \
+ --exclude '/art/exported/system_icons/straywild_1024.png.import' \
+ --exclude '/audio/ambience/' \
+ --exclude '/builds/' \
+ --exclude '/docs/design/' \
+ --exclude '/tests/' \
+ "${PROJECT_ROOT}/" "${EXPORT_PROJECT_ROOT}/"
+ "${ASSET_PROFILE_SCRIPT}" "${EXPORT_PROJECT_ROOT}"
"${GODOT_BIN}" \
--headless \
- --path "${PROJECT_ROOT}" \
- --export-release "Linux ARM64"
+ --path "${EXPORT_PROJECT_ROOT}" \
+ --import
+ "${GODOT_BIN}" \
+ --headless \
+ --path "${EXPORT_PROJECT_ROOT}" \
+ --export-release "Linux ARM64" \
+ "${ARM64_EXECUTABLE}"
+ cleanup_export_project
+ trap - EXIT INT TERM
fi
test -s "${ARM64_EXECUTABLE}"
@@ -75,73 +135,186 @@ test -s "${ARM64_PCK}"
file "${ARM64_EXECUTABLE}" | grep -q "ARM aarch64"
rm -rf -- "${STAGE_ROOT}"
-rm -f -- "${ARCHIVE}" "${LEGACY_ARCHIVE}"
+rm -f -- \
+ "${ARCHIVE}" \
+ "${OBSOLETE_VERSIONED_ARCHIVE}" \
+ "${OBSOLETE_LOWERCASE_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 0755 "${TEMPLATE_ROOT}/straywild.sh" "${STAGE_ROOT}/straywild.sh"
+install -m 0644 "${TEMPLATE_ROOT}/port.json" "${GAME_ROOT}/port.json"
+install -m 0644 "${TEMPLATE_ROOT}/straywild.gptk" \
+ "${GAME_ROOT}/straywild.gptk"
+install -m 0755 "${TEMPLATE_ROOT}/launch-straywild.sh" \
+ "${GAME_ROOT}/launch-straywild.sh"
+install -m 0755 "${ARM64_EXECUTABLE}" "${GAME_ROOT}/straywild.aarch64"
+install -m 0644 "${ARM64_PCK}" "${GAME_ROOT}/straywild.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 \
- "${TEMPLATE_ROOT}/licenses/CREDITS.md" \
- "${GAME_ROOT}/licenses/CREDITS.md"
+ "${PROJECT_ROOT}/docs/ATTRIBUTION.md" \
+ "${GAME_ROOT}/licenses/ATTRIBUTION.md"
install -m 0644 \
- "${TEMPLATE_ROOT}/licenses/Tuffy-LICENSE.txt" \
+ "${PROJECT_ROOT}/LICENSE" \
+ "${GAME_ROOT}/licenses/GPL-3.0-or-later.txt"
+install -m 0644 \
+ "${PROJECT_ROOT}/ASSET-LICENSE.md" \
+ "${GAME_ROOT}/licenses/straywild-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}/README.md" <"${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)"
+readonly EXPECTED_TOP_LEVELS="$(printf '%s\n' straywild straywild.sh)"
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
+grep -q '"version": 4' "${GAME_ROOT}/port.json"
+grep -q '"name": "straywild.zip"' "${GAME_ROOT}/port.json"
+grep -q '"rtr": true' "${GAME_ROOT}/port.json"
+if unzip -Z1 "${ARCHIVE}" | grep -qx 'port.json'; then
+ echo "port.json must be inside straywild/, not at the archive root." >&2
+ exit 1
+fi
+if grep -Eq '^[[:space:]]*#[[:space:]]*PORTMASTER:' \
+ "${STAGE_ROOT}/straywild.sh"; then
+ echo "The distributable launcher contains an installed PortMaster signature." >&2
+ exit 1
+fi
+grep -Fq 'PROFILE_PATH="$CONFDIR/performance_profile"' \
+ "${STAGE_ROOT}/straywild.sh"
+grep -Fq 'straywild_PERFORMANCE_PROFILE=light' \
+ "${STAGE_ROOT}/straywild.sh"
+grep -Fq 'CONTROLLER_MAPPING_FILE="$CONFDIR/cache/controller_mapping.txt"' \
+ "${STAGE_ROOT}/straywild.sh"
+grep -Fq 'SAVEDIR="$PORTS_ROOT/saves/straywild"' \
+ "${STAGE_ROOT}/straywild.sh"
+grep -Fq 'LEGACY_SAVEDIR="$PORTS_ROOT/saves/netfishing"' \
+ "${STAGE_ROOT}/straywild.sh"
+grep -Fq 'cp -a "$LEGACY_CONFDIR/." "$CONFDIR/"' \
+ "${STAGE_ROOT}/straywild.sh"
+grep -Fq '"straywild_DATA_DIR=$SAVEDIR"' \
+ "${STAGE_ROOT}/straywild.sh"
+grep -Fq '"straywild_CREATE_DATA_DIR=1"' \
+ "${STAGE_ROOT}/straywild.sh"
+grep -Fq 'printf '\''%s\n'\'' "$straywild_controllerconfig" > "$CONTROLLER_MAPPING_FILE"' \
+ "${STAGE_ROOT}/straywild.sh"
+grep -Fq '"$GAME_LAUNCHER"' "${STAGE_ROOT}/straywild.sh"
+grep -Fq '"$CONTROLLER_MAPPING_FILE"' "${STAGE_ROOT}/straywild.sh"
+if grep -Fq 'SDL_GAMECONTROLLERCONFIG="$straywild_controllerconfig"' \
+ "${STAGE_ROOT}/straywild.sh"; then
+ echo "Raw controller mappings must not be passed through westonwrap.sh." >&2
+ exit 1
+fi
+grep -Fq 'SDL_GAMECONTROLLERCONFIG="$(<"$CONTROLLER_MAPPING_FILE")"' \
+ "${GAME_ROOT}/launch-straywild.sh"
+grep -Fq 'export SDL_GAMECONTROLLERCONFIG' \
+ "${GAME_ROOT}/launch-straywild.sh"
+grep -Fq 'exec "$@"' "${GAME_ROOT}/launch-straywild.sh"
+grep -Fq 'GPTOKEYB_CONFIG="$GAMEDIR/straywild.gptk"' \
+ "${STAGE_ROOT}/straywild.sh"
+grep -Fq '$GPTOKEYB "straywild.aarch64" -c "$GPTOKEYB_CONFIG" &' \
+ "${STAGE_ROOT}/straywild.sh"
+grep -Fq 'pm_platform_helper "$GAME_EXECUTABLE"' \
+ "${STAGE_ROOT}/straywild.sh"
+grep -Fq 'pm_finish' "${STAGE_ROOT}/straywild.sh"
+if [[ "$(grep -Ec '^[[:space:]]*\$GPTOKEYB[[:space:]]' \
+ "${STAGE_ROOT}/straywild.sh")" -ne 1 ]]; then
+ echo "straywild must start exactly one GPTOKEYB exit handler." >&2
+ exit 1
+fi
+readonly GPTOKEYB_NOOP_KEYS=(
+ back guide start
+ a a_hk b b_hk x x_hk y y_hk
+ l1 l1_hk l2 l2_hk l3 r1 r1_hk r2 r2_hk r3
+ up down left right
+ left_analog_up left_analog_down left_analog_left left_analog_right
+ right_analog_up right_analog_down right_analog_left right_analog_right
+)
+for noop_key in "${GPTOKEYB_NOOP_KEYS[@]}"; do
+ if ! grep -Eq \
+ "^[[:space:]]*${noop_key}[[:space:]]*=[[:space:]]*\\\\\"[[:space:]]*$" \
+ "${GAME_ROOT}/straywild.gptk"; then
+ echo "GPTOKEYB no-op mapping is missing: ${noop_key}" >&2
+ exit 1
+ fi
+done
+if [[ "$(grep -Ec '^[[:space:]]*[a-z0-9_]+[[:space:]]*=' \
+ "${GAME_ROOT}/straywild.gptk")" -ne "${#GPTOKEYB_NOOP_KEYS[@]}" ]]; then
+ echo "GPTOKEYB no-op mapping contains an unexpected assignment." >&2
exit 1
fi
if unzip -Z1 "${ARCHIVE}" | grep -E \
diff --git a/scripts/generate_placeholder_animalese.gd b/scripts/generate_placeholder_animalese.gd
deleted file mode 100644
index 8119fd3..0000000
--- a/scripts/generate_placeholder_animalese.gd
+++ /dev/null
@@ -1,71 +0,0 @@
-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/import_animalese_voice.sh b/scripts/import_animalese_voice.sh
new file mode 100755
index 0000000..b9076ca
--- /dev/null
+++ b/scripts/import_animalese_voice.sh
@@ -0,0 +1,112 @@
+#!/usr/bin/env bash
+set -euo pipefail
+
+readonly TARGET_RMS_DBFS="-18.0"
+readonly PEAK_CEILING_DBFS="-3.0"
+readonly OUTPUT_SAMPLE_RATE="48000"
+
+usage() {
+ printf 'usage: %s SOURCE_DIRECTORY SAMPLE_SET_DIRECTORY\n' "$0" >&2
+ printf 'example: %s /path/to/voice_kim kim\n' "$0" >&2
+}
+
+if (( $# != 2 )); then
+ usage
+ exit 2
+fi
+
+if ! command -v sox >/dev/null 2>&1; then
+ printf 'error: sox is required to import animalese samples\n' >&2
+ exit 1
+fi
+
+readonly SCRIPT_DIRECTORY="$(cd -- "$(dirname -- "${BASH_SOURCE[0]}")" && pwd)"
+readonly PROJECT_ROOT="$(cd -- "$SCRIPT_DIRECTORY/.." && pwd)"
+readonly SOURCE_DIRECTORY="$(realpath -e -- "$1")"
+readonly SAMPLE_SET_DIRECTORY="$2"
+
+if [[ ! -d "$SOURCE_DIRECTORY" ]]; then
+ printf 'error: source is not a directory: %s\n' "$SOURCE_DIRECTORY" >&2
+ exit 1
+fi
+if [[ ! "$SAMPLE_SET_DIRECTORY" =~ ^[a-z0-9_-]+$ ]]; then
+ printf 'error: sample-set directory must use lowercase letters, numbers, underscores, or hyphens\n' >&2
+ exit 1
+fi
+
+readonly DESTINATION_DIRECTORY="$PROJECT_ROOT/sound/dialogue/animalese/$SAMPLE_SET_DIRECTORY"
+readonly WORK_DIRECTORY="$(mktemp -d -t straywild-animalese-import.XXXXXX)"
+
+cleanup() {
+ if [[ "$WORK_DIRECTORY" == /tmp/straywild-animalese-import.* ]]; then
+ find "$WORK_DIRECTORY" -depth -delete
+ fi
+}
+trap cleanup EXIT
+
+mkdir -p -- "$WORK_DIRECTORY/converted" "$WORK_DIRECTORY/normalized"
+mapfile -d '' SOURCE_FILES < <(
+ find "$SOURCE_DIRECTORY" -maxdepth 1 -type f -name '*.wav' -print0 \
+ | sort -z
+)
+if (( ${#SOURCE_FILES[@]} == 0 )); then
+ printf 'error: no .wav samples found in %s\n' "$SOURCE_DIRECTORY" >&2
+ exit 1
+fi
+
+for source_file in "${SOURCE_FILES[@]}"; do
+ filename="$(basename -- "$source_file")"
+ if [[ ! "$filename" =~ ^[a-z0-9_]+\.wav$ ]]; then
+ printf 'error: unsupported sample filename: %s\n' "$filename" >&2
+ exit 1
+ fi
+ converted_file="$WORK_DIRECTORY/converted/$filename"
+ normalized_file="$WORK_DIRECTORY/normalized/$filename"
+
+ # Measure the exact channel/rate conversion that will be shipped, then use
+ # the smaller of the RMS correction and peak-safe gain. Very short voice
+ # clips are not reliable inputs for program-loudness algorithms such as
+ # EBU R128, so this deterministic RMS/peak policy is intentional.
+ # Reserve headroom before high-quality resampling. Several supplied 8-bit
+ # clips sit near full scale and their interpolated waveform can otherwise
+ # clip before the normalization gain is calculated.
+ sox -D "$source_file" \
+ -r "$OUTPUT_SAMPLE_RATE" -c 1 -b 32 -e floating-point \
+ "$converted_file" gain -6 rate -v "$OUTPUT_SAMPLE_RATE"
+ statistics="$(sox "$converted_file" -n stat 2>&1)"
+ peak_amplitude="$(awk '/Maximum amplitude/ {print $3}' <<< "$statistics")"
+ rms_amplitude="$(awk '/RMS.*amplitude/ {print $3; exit}' <<< "$statistics")"
+ if ! awk -v peak="$peak_amplitude" -v rms="$rms_amplitude" \
+ 'BEGIN {exit !(peak > 0.0 && rms > 0.0)}'; then
+ printf 'error: sample is silent or unreadable: %s\n' "$source_file" >&2
+ exit 1
+ fi
+
+ gain_db="$(
+ awk \
+ -v peak="$peak_amplitude" \
+ -v rms="$rms_amplitude" \
+ -v target_rms="$TARGET_RMS_DBFS" \
+ -v peak_ceiling="$PEAK_CEILING_DBFS" \
+ 'BEGIN {
+ peak_db = 20.0 * log(peak) / log(10.0)
+ rms_db = 20.0 * log(rms) / log(10.0)
+ rms_gain = target_rms - rms_db
+ peak_gain = peak_ceiling - peak_db
+ gain = rms_gain < peak_gain ? rms_gain : peak_gain
+ printf "%.6f", gain
+ }'
+ )"
+ sox -D "$converted_file" \
+ -r "$OUTPUT_SAMPLE_RATE" -c 1 -b 16 -e signed-integer \
+ "$normalized_file" gain "$gain_db"
+ printf '%-16s %8s dB\n' "$filename" "$gain_db"
+done
+
+mkdir -p -- "$DESTINATION_DIRECTORY"
+for normalized_file in "$WORK_DIRECTORY"/normalized/*.wav; do
+ cp -- "$normalized_file" "$DESTINATION_DIRECTORY/$(basename -- "$normalized_file")"
+done
+
+printf 'imported %d normalized samples to %s\n' \
+ "${#SOURCE_FILES[@]}" "$DESTINATION_DIRECTORY"
diff --git a/scripts/normalize_texture_imports.gd b/scripts/normalize_texture_imports.gd
index 648ac80..e7b2406 100644
--- a/scripts/normalize_texture_imports.gd
+++ b/scripts/normalize_texture_imports.gd
@@ -10,7 +10,7 @@ const DEFAULT_PROFILE: Dictionary = {
"compress/hdr_compression": 1,
"compress/normal_map": 0,
"compress/channel_pack": 0,
- "mipmaps/generate": true,
+ "mipmaps/generate": false,
"mipmaps/limit": -1,
"roughness/mode": 0,
"roughness/src_normal": "",
@@ -27,6 +27,15 @@ const DEFAULT_PROFILE: Dictionary = {
"detect_3d/compress_to": 0,
},
}
+const FACIAL_FEATURE_IMPORT_PATH: String = (
+ "art/exported/characters/faces/"
+)
+const FACIAL_FEATURE_SIZE_LIMIT: int = 512
+const PROJECTED_TERRAIN_TEXTURE_IMPORTS: Array[String] = [
+ "res://world/generation/chunks/assets/chunk_0000_grass_lite.png.import",
+ "res://world/generation/chunks/assets/chunk_0001_sand.png.import",
+ "res://world/generation/chunks/assets/chunk_0031_dirt.png.import",
+]
var apply_changes: bool = false
var verbose: bool = false
@@ -86,11 +95,11 @@ func _parse_args() -> void:
quit(1)
return
i += 1
- roots.append(args[i])
- i += 1
- elif arg.begins_with("--root="):
- roots.append(arg.trim_prefix("--root="))
- 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
@@ -147,6 +156,10 @@ func _handle_import_file(import_path: String) -> void:
var section_data: Dictionary = DEFAULT_PROFILE[section_name]
for section_key: String in section_data.keys():
var desired: Variant = section_data[section_key]
+ if section_key == "process/size_limit":
+ desired = _size_limit_for(import_path)
+ elif section_key == "mipmaps/generate":
+ desired = _generates_mipmaps(import_path)
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)
@@ -174,6 +187,18 @@ func _handle_import_file(import_path: String) -> void:
updated += 1
+func _size_limit_for(import_path: String) -> int:
+ var normalized_path := import_path.replace("\\", "/")
+ if FACIAL_FEATURE_IMPORT_PATH in normalized_path:
+ return FACIAL_FEATURE_SIZE_LIMIT
+ return int(DEFAULT_PROFILE["params"]["process/size_limit"])
+
+
+func _generates_mipmaps(import_path: String) -> bool:
+ var normalized_path := import_path.replace("\\", "/")
+ return normalized_path in PROJECTED_TERRAIN_TEXTURE_IMPORTS
+
+
func print_summary() -> void:
if apply_changes:
_print(
diff --git a/scripts/normalize_texture_profiles.sh b/scripts/normalize_texture_profiles.sh
index 1483a61..0551efd 100755
--- a/scripts/normalize_texture_profiles.sh
+++ b/scripts/normalize_texture_profiles.sh
@@ -13,7 +13,7 @@ fi
readonly ROOT_PATH="$1"
-readonly WORK_ROOT="$(mktemp -d -t netfishing-godot-data.XXXXXX)"
+readonly WORK_ROOT="$(mktemp -d -t straywild-godot-data.XXXXXX)"
trap 'rm -rf -- "${WORK_ROOT}"' EXIT
cd "${PROJECT_ROOT}"
diff --git a/scripts/portmaster/NETfishing.sh b/scripts/portmaster/NETfishing.sh
deleted file mode 100755
index 58fbfbd..0000000
--- a/scripts/portmaster/NETfishing.sh
+++ /dev/null
@@ -1,120 +0,0 @@
-#!/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
index ef608bd..1b0a1c7 100755
--- a/scripts/portmaster/gameinfo.xml
+++ b/scripts/portmaster/gameinfo.xml
@@ -1,13 +1,13 @@
- ./NETfishing.sh
- NETfishing
+ ./straywild.sh
+ straywild
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
+ straywild project
Independent
adventure, simulation
- ./netfishing/screenshot.png
+ ./straywild/screenshot.png
diff --git a/scripts/portmaster/launch-straywild.sh b/scripts/portmaster/launch-straywild.sh
new file mode 100755
index 0000000..50f2ec9
--- /dev/null
+++ b/scripts/portmaster/launch-straywild.sh
@@ -0,0 +1,23 @@
+#!/bin/bash
+
+if [[ $# -lt 2 ]]; then
+ echo "Usage: $0 [arguments...]" >&2
+ exit 2
+fi
+
+CONTROLLER_MAPPING_FILE="$1"
+shift
+
+if [[ ! -r "$CONTROLLER_MAPPING_FILE" ]]; then
+ echo "Controller mapping file is not readable: $CONTROLLER_MAPPING_FILE" >&2
+ exit 1
+fi
+
+SDL_GAMECONTROLLERCONFIG="$(<"$CONTROLLER_MAPPING_FILE")"
+if [[ "$SDL_GAMECONTROLLERCONFIG" != *,*,* ]]; then
+ echo "Controller mapping file is malformed: $CONTROLLER_MAPPING_FILE" >&2
+ exit 1
+fi
+
+export SDL_GAMECONTROLLERCONFIG
+exec "$@"
diff --git a/scripts/portmaster/licenses/CREDITS.md b/scripts/portmaster/licenses/CREDITS.md
deleted file mode 100755
index 5b78a80..0000000
--- a/scripts/portmaster/licenses/CREDITS.md
+++ /dev/null
@@ -1,54 +0,0 @@
-# Credits
-
-## Project direction
-
-NETfishing is designed, directed, reviewed, and released by its project
-creator. A public-facing creator or studio credit has not yet been recorded in
-this repository.
-
-## Development assistance
-
-Development has used automated coding assistance for implementation,
-investigation, testing, documentation, and review. Design choices, supplied
-artwork, acceptance decisions, repository operations, and releases remain
-human-controlled.
-
-This disclosure is intentionally about process. Commit style, code style, or
-repository metadata cannot reliably prove whether a particular change was
-written with or without assistance.
-
-## Fonts
-
-- Tuffy: Thatcher Ulrich, Karoly Barta, and Michael Evans. The bundled license
- dedicates the work to the public domain; see `ui/fonts/Tuffy-LICENSE.txt`.
-- Seattle Avenue: bundled as `ui/fonts/seattle_avenue.otf`. Author and
- redistribution terms still need to be recorded before public distribution.
-
-## Music
-
-- `audio/music/title/as_in_four_wolves.ogg`: original title/ambient music by
- the project owner.
-
-## Sound effects
-
-- “Spinning reel.wav” by tosha73, used as the source for the fishing fight and
- manual-reeling loops. Freesound sound 509902, Creative Commons Zero (CC0):
- https://freesound.org/s/509902/
-- “Gentle Ocean Waves Loop” by kkenny101, used for the saltwater shoreline
- ambience. Freesound sound 852826, Creative Commons Zero (CC0):
- https://freesound.org/s/852826/
-- Dog_Bark.wav by ivolipa -- https://freesound.org/s/328729/ -- License:
- Creative Commons 0
-- Cat meow.m4a by Christyboy100 -- https://freesound.org/s/495694/ -- License:
- Attribution 3.0
-- “Quick Water Droplet” by qubodup, used for the bobber water-impact sound.
- Freesound sound 792931, Creative Commons Zero (CC0):
- https://freesound.org/s/792931/
-
-## Artwork
-
-- 2D artwork: Rheannon Eisworth, contributor.
-- 3D models: original work by the project owner.
-
-Asset categories and release-record requirements are listed in
-[`docs/ASSET-PROVENANCE.md`](docs/ASSET-PROVENANCE.md).
diff --git a/scripts/portmaster/licenses/Tuffy-LICENSE.txt b/scripts/portmaster/licenses/Tuffy-LICENSE.txt
deleted file mode 100755
index defced0..0000000
--- a/scripts/portmaster/licenses/Tuffy-LICENSE.txt
+++ /dev/null
@@ -1,11 +0,0 @@
-We, the copyright holders of this work, hereby release it into the
-public domain. This applies worldwide.
-
-In case this is not legally possible,
-
-We grant any entity the right to use this work for any purpose, without
-any conditions, unless such conditions are required by law.
-
-Thatcher Ulrich http://tulrich.com
-Karoly Barta bartakarcsi@gmail.com
-Michael Evans http://www.evertype.com
diff --git a/scripts/portmaster/port.json b/scripts/portmaster/port.json
index d14fa8f..4c36648 100644
--- a/scripts/portmaster/port.json
+++ b/scripts/portmaster/port.json
@@ -1,16 +1,15 @@
{
"version": 4,
- "name": "netfishing.zip",
+ "name": "straywild.zip",
"items": [
- "NETfishing.sh",
- "netfishing/"
+ "straywild.sh",
+ "straywild/"
],
"items_opt": null,
"attr": {
- "title": "NETfishing",
+ "title": "straywild",
"porter": [
- "Voyager",
- "asellite"
+ "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,
@@ -21,7 +20,7 @@
"simulation"
],
"image": {},
- "rtr": false,
+ "rtr": true,
"exp": false,
"runtime": [
"weston_pkg_0.2.squashfs"
diff --git a/scripts/portmaster/straywild.gptk b/scripts/portmaster/straywild.gptk
new file mode 100644
index 0000000..2e8602e
--- /dev/null
+++ b/scripts/portmaster/straywild.gptk
@@ -0,0 +1,41 @@
+# straywild reads the controller directly. GPTOKEYB is retained only for its
+# device-specific force-quit chord, so every keyboard and mouse binding is
+# disabled to prevent duplicate input.
+
+back = \"
+guide = \"
+start = \"
+
+a = \"
+a_hk = \"
+b = \"
+b_hk = \"
+x = \"
+x_hk = \"
+y = \"
+y_hk = \"
+
+l1 = \"
+l1_hk = \"
+l2 = \"
+l2_hk = \"
+l3 = \"
+r1 = \"
+r1_hk = \"
+r2 = \"
+r2_hk = \"
+r3 = \"
+
+up = \"
+down = \"
+left = \"
+right = \"
+
+left_analog_up = \"
+left_analog_down = \"
+left_analog_left = \"
+left_analog_right = \"
+right_analog_up = \"
+right_analog_down = \"
+right_analog_left = \"
+right_analog_right = \"
diff --git a/scripts/portmaster/straywild.sh b/scripts/portmaster/straywild.sh
new file mode 100755
index 0000000..d4e56a7
--- /dev/null
+++ b/scripts/portmaster/straywild.sh
@@ -0,0 +1,178 @@
+#!/bin/bash
+
+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
+
+source "$controlfolder/control.txt"
+[ -f "${controlfolder}/mod_${CFW_NAME}.txt" ] && source "${controlfolder}/mod_${CFW_NAME}.txt"
+get_controls
+
+GAMEDIR="/${directory}/ports/straywild"
+
+CONFDIR="$GAMEDIR/conf"
+PORTS_ROOT="${GAMEDIR%/straywild}"
+SAVEDIR="$PORTS_ROOT/saves/straywild"
+LEGACY_GAMEDIR="$PORTS_ROOT/netfishing"
+LEGACY_CONFDIR="$LEGACY_GAMEDIR/conf"
+LEGACY_SAVEDIR="$PORTS_ROOT/saves/netfishing"
+DATA_BOOTSTRAP_DIR="$CONFDIR/data/godot/app_userdata/straywild"
+LEGACY_DATA_BOOTSTRAP_DIR="$CONFDIR/data/godot/app_userdata/NETFISHING"
+GAME_EXECUTABLE="$GAMEDIR/straywild.aarch64"
+GAME_LAUNCHER="$GAMEDIR/launch-straywild.sh"
+GPTOKEYB_CONFIG="$GAMEDIR/straywild.gptk"
+CONTROLLER_MAPPING_FILE="$CONFDIR/cache/controller_mapping.txt"
+WESTON_DIR="/tmp/straywild-weston"
+WESTON_RUNTIME="weston_pkg_0.2"
+HARBOURMASTER="$controlfolder/harbourmaster"
+
+mkdir -p "$CONFDIR/data" "$CONFDIR/config" "$CONFDIR/cache" "$WESTON_DIR"
+chmod +x "$GAME_EXECUTABLE"
+
+# A renamed PortMaster entry installs beside the former NETfishing port. Copy
+# its device-local configuration once and continue using its established save
+# root when present. New installations use straywild paths exclusively.
+if [ -d "$LEGACY_CONFDIR" ] && [ ! -f "$CONFDIR/.netfishing-imported" ]; then
+ cp -a "$LEGACY_CONFDIR/." "$CONFDIR/"
+ touch "$CONFDIR/.netfishing-imported"
+fi
+if [ ! -f "$SAVEDIR/straywild_data.json" ] \
+ && [ ! -f "$SAVEDIR/netfishing_data.json" ] \
+ && { [ -f "$LEGACY_SAVEDIR/straywild_data.json" ] \
+ || [ -f "$LEGACY_SAVEDIR/netfishing_data.json" ]; }; then
+ SAVEDIR="$LEGACY_SAVEDIR"
+fi
+
+# straywild uses a predictable save directory beside the port instead of
+# presenting a folder picker on a small screen. Preserve an established
+# data-root choice when upgrading an existing installation.
+straywild_DATA_ENVIRONMENT=()
+if [ -f "$SAVEDIR/straywild_data.json" ] \
+ || [ -f "$SAVEDIR/netfishing_data.json" ] || {
+ [ ! -f "$DATA_BOOTSTRAP_DIR/data_root_bootstrap.json" ] &&
+ [ ! -f "$DATA_BOOTSTRAP_DIR/data_root_bootstrap.json.backup" ] &&
+ [ ! -f "$LEGACY_DATA_BOOTSTRAP_DIR/data_root_bootstrap.json" ] &&
+ [ ! -f "$LEGACY_DATA_BOOTSTRAP_DIR/data_root_bootstrap.json.backup" ]
+}; then
+ mkdir -p "$SAVEDIR"
+ straywild_DATA_ENVIRONMENT+=(
+ "straywild_DATA_DIR=$SAVEDIR"
+ "straywild_CREATE_DATA_DIR=1"
+ )
+fi
+
+> "$GAMEDIR/log.txt" && exec > >(tee "$GAMEDIR/log.txt") 2>&1
+
+if [ ! -f "$controlfolder/libs/${WESTON_RUNTIME}.squashfs" ]; then
+ if [ ! -x "$HARBOURMASTER" ]; then
+ pm_message "straywild 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
+ straywild_controllerconfig="$GODOT_MUOS_MAPPING"
+elif [ -n "$sdl_controllerconfig" ]; then
+ straywild_controllerconfig="$sdl_controllerconfig"
+else
+ straywild_controllerconfig="$GODOT_MUOS_MAPPING"
+fi
+printf '%s\n' "$straywild_controllerconfig" > "$CONTROLLER_MAPPING_FILE"
+
+straywild_GODOT_OPTIONS=()
+straywild_GAME_ENVIRONMENT=()
+PERFORMANCE_PROFILE="${straywild_PERFORMANCE_PROFILE:-}"
+PROFILE_PATH="$CONFDIR/performance_profile"
+if [[ -z "$PERFORMANCE_PROFILE" && -r "$PROFILE_PATH" ]]; then
+ IFS= read -r PERFORMANCE_PROFILE < "$PROFILE_PATH" || true
+ PERFORMANCE_PROFILE="${PERFORMANCE_PROFILE%$'\r'}"
+fi
+case "$PERFORMANCE_PROFILE" in
+ normal)
+ straywild_GAME_ENVIRONMENT+=(
+ "straywild_PERFORMANCE_PROFILE=normal"
+ )
+ ;;
+ light|"")
+ straywild_GAME_ENVIRONMENT+=(
+ "straywild_PERFORMANCE_PROFILE=light"
+ "straywild_LOW_END=1"
+ )
+ straywild_GODOT_OPTIONS+=(
+ --single-window
+ --disable-vsync
+ --max-fps 30
+ --audio-output-latency 40
+ )
+ ;;
+ *)
+ echo "Unknown performance profile '$PERFORMANCE_PROFILE'; using light." >&2
+ straywild_GAME_ENVIRONMENT+=(
+ "straywild_PERFORMANCE_PROFILE=light"
+ "straywild_LOW_END=1"
+ )
+ straywild_GODOT_OPTIONS+=(
+ --single-window
+ --disable-vsync
+ --max-fps 30
+ --audio-output-latency 40
+ )
+ ;;
+esac
+
+# straywild reads gameplay input natively. The explicit no-op map prevents
+# GPTOKEYB from injecting duplicate keyboard or mouse input while retaining
+# its device-specific force-quit chord, including on ROCKNIX.
+$GPTOKEYB "straywild.aarch64" -c "$GPTOKEYB_CONFIG" &
+pm_platform_helper "$GAME_EXECUTABLE"
+
+$ESUDO env CRUSTY_RESOLUTION="${DISPLAY_WIDTH}x${DISPLAY_HEIGHT}" \
+ "$WESTON_DIR/westonwrap.sh" headless noop kiosk crusty_x11egl \
+ XDG_DATA_HOME="$CONFDIR/data" \
+ XDG_CONFIG_HOME="$CONFDIR/config" \
+ XDG_CACHE_HOME="$CONFDIR/cache" \
+ GODOT_SILENCE_ROOT_WARNING=1 \
+ "${straywild_DATA_ENVIRONMENT[@]}" \
+ "${straywild_GAME_ENVIRONMENT[@]}" \
+ "$GAME_LAUNCHER" \
+ "$CONTROLLER_MAPPING_FILE" \
+ "$GAME_EXECUTABLE" \
+ "${straywild_GODOT_OPTIONS[@]}" \
+ --resolution "${DISPLAY_WIDTH}x${DISPLAY_HEIGHT}" \
+ --fullscreen \
+ --rendering-driver opengl3_es \
+ --audio-driver ALSA
+
+$ESUDO "$WESTON_DIR/westonwrap.sh" cleanup
+if [[ "$PM_CAN_MOUNT" != "N" ]]; then
+ $ESUDO umount "$WESTON_DIR" 2>/dev/null
+fi
+pm_finish
diff --git a/scripts/prepare_portmaster_assets.sh b/scripts/prepare_portmaster_assets.sh
new file mode 100755
index 0000000..0429f69
--- /dev/null
+++ b/scripts/prepare_portmaster_assets.sh
@@ -0,0 +1,96 @@
+#!/usr/bin/env bash
+
+set -euo pipefail
+
+if [[ $# -ne 1 ]]; then
+ echo "usage: scripts/prepare_portmaster_assets.sh " >&2
+ exit 2
+fi
+
+readonly PROJECT_ROOT="$1"
+readonly GENERAL_TEXTURE_LIMIT=128
+readonly GAMEPLAY_TEXTURE_LIMIT=64
+readonly AUDIO_SAMPLE_RATE=22050
+
+if [[ ! -f "${PROJECT_ROOT}/project.godot" ]]; then
+ echo "PortMaster asset project is missing project.godot: ${PROJECT_ROOT}" >&2
+ exit 1
+fi
+
+limit_texture_imports() {
+ local asset_root="$1"
+ local size_limit="$2"
+ local import_file
+ local updated=0
+
+ if [[ ! -d "${asset_root}" ]]; then
+ return
+ fi
+ while IFS= read -r -d '' import_file; do
+ if ! grep -q '^importer="texture"$' "${import_file}"; then
+ continue
+ fi
+ if ! grep -q '^process/size_limit=' "${import_file}"; then
+ continue
+ fi
+ sed -i \
+ -e 's|^compress/mode=.*$|compress/mode=2|' \
+ -e "s|^process/size_limit=.*$|process/size_limit=${size_limit}|" \
+ "${import_file}"
+ updated=$((updated + 1))
+ done < <(find "${asset_root}" -type f -name '*.import' -print0)
+ printf 'PortMaster texture limit %d px: %d imports under %s\n' \
+ "${size_limit}" "${updated}" "${asset_root#${PROJECT_ROOT}/}"
+}
+
+
+configure_audio_imports() {
+ local asset_root="$1"
+ local import_file
+ local updated=0
+
+ if [[ ! -d "${asset_root}" ]]; then
+ return
+ fi
+ while IFS= read -r -d '' import_file; do
+ if ! grep -q '^importer="wav"$' "${import_file}"; then
+ continue
+ fi
+ sed -i \
+ -e 's|^force/mono=.*$|force/mono=true|' \
+ -e 's|^force/max_rate=.*$|force/max_rate=true|' \
+ -e "s|^force/max_rate_hz=.*$|force/max_rate_hz=${AUDIO_SAMPLE_RATE}|" \
+ -e 's|^compress/mode=.*$|compress/mode=2|' \
+ "${import_file}"
+ updated=$((updated + 1))
+ done < <(find "${asset_root}" -type f -name '*.wav.import' -print0)
+ printf 'PortMaster audio profile %d Hz mono: %d imports under %s\n' \
+ "${AUDIO_SAMPLE_RATE}" "${updated}" \
+ "${asset_root#${PROJECT_ROOT}/}"
+}
+
+# The entire PortMaster project uses a deliberately small, VRAM-compressed
+# texture ceiling. Fonts are unaffected, preserving readable interface text
+# while raster artwork is reduced for one-gigabyte shared-memory devices.
+limit_texture_imports "${PROJECT_ROOT}" "${GENERAL_TEXTURE_LIMIT}"
+
+# These catalogs contain most of the authored high-resolution textures and
+# account for the largest runtime residency. At a 640x480 output, 64 pixels
+# remains sufficient for the deliberately low-fidelity light profile.
+for gameplay_root in \
+ "art/exported/characters" \
+ "art/exported/environment" \
+ "art/exported/items" \
+ "fish/species" \
+ "gathering" \
+ "items/icons" \
+ "world"; do
+ limit_texture_imports \
+ "${PROJECT_ROOT}/${gameplay_root}" \
+ "${GAMEPLAY_TEXTURE_LIMIT}"
+done
+
+# Short speech, call, and gameplay effects remain available, but use a small
+# mono QOA representation to reduce both package and resident sample memory.
+configure_audio_imports "${PROJECT_ROOT}/audio/sfx"
+configure_audio_imports "${PROJECT_ROOT}/sound/dialogue"
diff --git a/scripts/run_validations.sh b/scripts/run_validations.sh
index 8279bac..ff108a8 100755
--- a/scripts/run_validations.sh
+++ b/scripts/run_validations.sh
@@ -6,55 +6,110 @@ 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 RUN_ROOT="$(mktemp -d -t straywild-validations.XXXXXX)"
+
+export straywild_ISOLATED_VALIDATION_STORAGE=1
readonly -a QUICK_TESTS=(
+ "scripts/validate_animalese_samples.gd"
"tests/android_readiness_validation.gd"
+ "tests/camera_drag_validation.gd"
+ "tests/character_rig_validation.gd"
+ "tests/network_chat_protocol_validation.gd"
+ "tests/controller_focus_presentation_validation.gd"
+ "tests/controller_menu_accessibility_validation.gd"
+ "tests/controller_mapping_validation.gd"
+ "tests/controller_world_interaction_validation.gd"
+ "tests/controller_ui_navigation_validation.gd"
+ "tests/dedicated_server_config_validation.gd"
+ "tests/digging_prototype_validation.gd"
+ "tests/equipment_catalog_validation.gd"
+ "tests/exported_decal_hotfix_validation.gd"
+ "tests/file_dialog_controller_navigation_validation.gd"
"tests/fish_catalog_content_validation.gd"
"tests/fish_quality_validation.gd"
+ "tests/fishing_shop_controller_validation.gd"
"tests/fishing_audio_validation.gd"
"tests/fishing_surface_validation.gd"
+ "tests/friend_relationship_validation.gd"
+ "tests/fur_pattern_validation.gd"
+ "tests/generated_world_runtime_validation.gd"
+ "tests/gathering_marker_surface_validation.gd"
+ "tests/inventory_storage_validation.gd"
+ "tests/keyboard_mouse_mapping_validation.gd"
"tests/logbook_validation.gd"
+ "tests/network_player_animation_protocol_validation.gd"
+ "tests/net_attachment_validation.gd"
+ "tests/on_screen_keyboard_validation.gd"
+ "tests/player_gathering_animation_validation.gd"
"tests/player_experience_validation.gd"
"tests/shoreline_ambience_validation.gd"
"tests/surface_drawing_validation.gd"
+ "tests/surface_drawing_texture_renderer_validation.gd"
+ "tests/tackle_order_validation.gd"
+ "tests/terrain_biome_validation.gd"
"tests/terrain_blender_material_validation.gd"
+ "tests/terrain_chunk_generator_validation.gd"
+ "tests/texture_sampling_validation.gd"
+ "tests/tree_gathering_prototype_validation.gd"
+ "tests/unified_inventory_validation.gd"
"tests/world_time_validation.gd"
+ "tests/world_layout_validation.gd"
+ "tests/world_spawn_protocol_validation.gd"
"tests/world_weather_validation.gd"
)
readonly -a RUNTIME_TESTS=(
"tests/inventory_notepad_art_validation.gd"
+ "tests/light_performance_profile_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/gathering_showcase_validation.gd"
+ "tests/inventory_storage_persistence_validation.gd"
"tests/job_system_validation.gd"
+ "tests/progression_archive_validation.gd"
"tests/surface_drawing_runtime_validation.gd"
)
readonly -a NETWORK_TESTS=(
+ "tests/chat_privacy_multiplayer_validation.gd"
"tests/economy_regression_validation.gd"
"tests/fish_showcase_multiplayer_validation.gd"
+ "tests/friend_multiplayer_validation.gd"
"tests/fishing_multiplayer_validation.gd"
+ "tests/home_spawn_multiplayer_validation.gd"
"tests/job_multiplayer_validation.gd"
+ "tests/movement_multiplayer_validation.gd"
+ "tests/operator_multiplayer_validation.gd"
+ "tests/profile_multiplayer_validation.gd"
"tests/surface_drawing_multiplayer_validation.gd"
"tests/world_time_multiplayer_validation.gd"
+ "tests/world_layout_multiplayer_validation.gd"
+ "tests/world_spawn_multiplayer_validation.gd"
)
+readonly SESSION_SWITCH_NETWORK_TEST="tests/session_switch_multiplayer_validation.gd"
+readonly LATE_JOIN_NETWORK_TEST="tests/late_join_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"
+ printf \
+ 'Usage: %s {quick|full|host|network|session-switch|late-join|all|--list}\n' \
+ "$0"
}
prepare_root() {
@@ -78,6 +133,40 @@ run_test() {
--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
+ # A cold generated-world startup can take longer than ten seconds before
+ # NetworkSession is ready to bind. Keep the client launch tied to the actual
+ # listener instead of treating that normal startup work as a host failure.
+ for _attempt in {1..600}; 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/}"
@@ -93,7 +182,17 @@ run_network_test() {
--headless --path "${PROJECT_ROOT}" --script "${script}" -- host \
>"${host_root}/output.log" 2>&1 &
host_pid=$!
- sleep 0.75
+ 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" \
@@ -116,6 +215,150 @@ run_network_test() {
fi
}
+run_session_switch_network_test() {
+ local script="${SESSION_SWITCH_NETWORK_TEST}"
+ local name="${script#tests/}"
+ name="${name%.gd}"
+ local first_root second_root client_root
+ local first_pid second_pid first_status second_status client_status
+ local both_hosts_ready=0
+ first_root="$(prepare_root "${name}-first-host")"
+ second_root="$(prepare_root "${name}-second-host")"
+ client_root="$(prepare_root "${name}-client")"
+ printf '\n==> %s (two hosts + client)\n' "${script}"
+ XDG_DATA_HOME="${first_root}/data" \
+ XDG_CONFIG_HOME="${first_root}/config" \
+ XDG_CACHE_HOME="${first_root}/cache" \
+ timeout "${TEST_TIMEOUT_SECONDS}s" "${GODOT_BIN}" \
+ --headless --path "${PROJECT_ROOT}" --script "${script}" -- first_host \
+ >"${first_root}/output.log" 2>&1 &
+ first_pid=$!
+ XDG_DATA_HOME="${second_root}/data" \
+ XDG_CONFIG_HOME="${second_root}/config" \
+ XDG_CACHE_HOME="${second_root}/cache" \
+ timeout "${TEST_TIMEOUT_SECONDS}s" "${GODOT_BIN}" \
+ --headless --path "${PROJECT_ROOT}" --script "${script}" -- second_host \
+ >"${second_root}/output.log" 2>&1 &
+ second_pid=$!
+ for _attempt in {1..600}; do
+ if ! kill -0 "${first_pid}" 2>/dev/null \
+ || ! kill -0 "${second_pid}" 2>/dev/null; then
+ break
+ fi
+ if ! command -v ss >/dev/null 2>&1; then
+ sleep 2
+ both_hosts_ready=1
+ break
+ fi
+ if ss -H -lun "sport = :18142" | grep -q . \
+ && ss -H -lun "sport = :18143" | grep -q .; then
+ both_hosts_ready=1
+ break
+ fi
+ sleep 0.1
+ done
+ if ((both_hosts_ready == 0)); then
+ kill "${first_pid}" "${second_pid}" 2>/dev/null || true
+ wait "${first_pid}" 2>/dev/null || true
+ wait "${second_pid}" 2>/dev/null || true
+ printf '%s\n' '-- first host output --'
+ sed -n '1,240p' "${first_root}/output.log"
+ printf '%s\n' '-- second host output --'
+ sed -n '1,240p' "${second_root}/output.log"
+ printf '%s\n' 'error: session-switch hosts did not become ready' >&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 "${first_pid}"
+ first_status=$?
+ wait "${second_pid}"
+ second_status=$?
+ set -e
+ printf '%s\n' '-- first host output --'
+ sed -n '1,240p' "${first_root}/output.log"
+ printf '%s\n' '-- second host output --'
+ sed -n '1,240p' "${second_root}/output.log"
+ printf '%s\n' '-- client output --'
+ sed -n '1,240p' "${client_root}/output.log"
+ if ((first_status != 0 || second_status != 0 || client_status != 0)); then
+ printf 'error: hosts exited %d/%d; client exited %d\n' \
+ "${first_status}" "${second_status}" "${client_status}" >&2
+ return 1
+ fi
+}
+
+
+run_late_join_network_test() {
+ local script="${LATE_JOIN_NETWORK_TEST}"
+ local name="${script#tests/}"
+ name="${name%.gd}"
+ local host_root first_root late_root
+ local host_pid first_pid late_pid host_status first_status late_status
+ host_root="$(prepare_root "${name}-host")"
+ first_root="$(prepare_root "${name}-first")"
+ late_root="$(prepare_root "${name}-late")"
+ printf '\n==> %s (host + first client + late 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
+ XDG_DATA_HOME="${first_root}/data" \
+ XDG_CONFIG_HOME="${first_root}/config" \
+ XDG_CACHE_HOME="${first_root}/cache" \
+ timeout "${TEST_TIMEOUT_SECONDS}s" "${GODOT_BIN}" \
+ --headless --path "${PROJECT_ROOT}" --script "${script}" -- first \
+ >"${first_root}/output.log" 2>&1 &
+ first_pid=$!
+ sleep 4
+ XDG_DATA_HOME="${late_root}/data" \
+ XDG_CONFIG_HOME="${late_root}/config" \
+ XDG_CACHE_HOME="${late_root}/cache" \
+ timeout "${TEST_TIMEOUT_SECONDS}s" "${GODOT_BIN}" \
+ --headless --path "${PROJECT_ROOT}" --script "${script}" -- late \
+ >"${late_root}/output.log" 2>&1 &
+ late_pid=$!
+ set +e
+ wait "${late_pid}"
+ late_status=$?
+ wait "${first_pid}"
+ first_status=$?
+ wait "${host_pid}"
+ host_status=$?
+ set -e
+ printf '%s\n' '-- host output --'
+ sed -n '1,240p' "${host_root}/output.log"
+ printf '%s\n' '-- first client output --'
+ sed -n '1,240p' "${first_root}/output.log"
+ printf '%s\n' '-- late client output --'
+ sed -n '1,240p' "${late_root}/output.log"
+ if ((host_status != 0 || first_status != 0 || late_status != 0)); then
+ printf 'error: host exited %d; clients exited %d/%d\n' \
+ "${host_status}" "${first_status}" "${late_status}" >&2
+ return 1
+ fi
+}
+
run_quick() {
local test_script
for test_script in "${QUICK_TESTS[@]}"; do
@@ -143,6 +386,8 @@ run_network() {
for test_script in "${NETWORK_TESTS[@]}"; do
run_network_test "${test_script}"
done
+ run_late_join_network_test
+ run_session_switch_network_test
}
list_tests() {
@@ -154,6 +399,10 @@ list_tests() {
printf ' %s\n' "${HOST_TESTS[@]}"
printf '%s\n' 'Paired network tests:'
printf ' %s\n' "${NETWORK_TESTS[@]}"
+ printf '%s\n' 'Two-host session-switch network test:'
+ printf ' %s\n' "${SESSION_SWITCH_NETWORK_TEST}"
+ printf '%s\n' 'Three-peer late-join network test:'
+ printf ' %s\n' "${LATE_JOIN_NETWORK_TEST}"
}
case "${1:-}" in
@@ -169,6 +418,12 @@ case "${1:-}" in
network)
run_network
;;
+ session-switch)
+ run_session_switch_network_test
+ ;;
+ late-join)
+ run_late_join_network_test
+ ;;
all)
run_full
run_host
diff --git a/scripts/setup_android_build.sh b/scripts/setup_android_build.sh
index c1cebdc..bc96083 100755
--- a/scripts/setup_android_build.sh
+++ b/scripts/setup_android_build.sh
@@ -336,8 +336,8 @@ check_editor_settings() {
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"
+ echo " ${GODOT} --headless --path \"$PROJECT_ROOT\" --export-release \"Android\" builds/${PROJECT_VERSION}/android/straywild.apk"
+ echo " ${GODOT} --headless --path \"$PROJECT_ROOT\" --export-debug \"Android\" builds/android/straywild-debug.apk"
}
print_status INFO "Starting Android dependency preflight..."
diff --git a/scripts/test_player_data_root_paths.gd b/scripts/test_player_data_root_paths.gd
index 69a2485..f357b9a 100644
--- a/scripts/test_player_data_root_paths.gd
+++ b/scripts/test_player_data_root_paths.gd
@@ -1,36 +1,36 @@
extends SceneTree
-const PROJECT_PATH := "/tmp/netfishing"
-const INSTALL_PATH := "/opt/NETfishing"
+const PROJECT_PATH := "/tmp/straywild"
+const INSTALL_PATH := "/opt/straywild"
func _initialize() -> void:
- var root_to_create: String = OS.get_environment("NETFISHING_TEST_CREATE_ROOT")
+ var root_to_create: String = OS.get_environment("straywild_TEST_CREATE_ROOT")
if not root_to_create.is_empty():
_create_validation_root(root_to_create)
return
var failures: PackedStringArray = []
_expect(
"external temporary directory",
- "/tmp/NETfishing-test-data",
+ "/tmp/straywild-test-data",
PlayerDataRoot.PATH_ALLOWED,
failures,
)
_expect(
"external trailing slash",
- "/tmp/NETfishing-test-data/",
+ "/tmp/straywild-test-data/",
PlayerDataRoot.PATH_ALLOWED,
failures,
)
_expect(
"similar project-name sibling",
- "/tmp/netfishing-data",
+ "/tmp/straywild-data",
PlayerDataRoot.PATH_ALLOWED,
failures,
)
_expect(
"documents directory",
- "/home/player/Documents/NETfishing",
+ "/home/player/Documents/straywild",
PlayerDataRoot.PATH_ALLOWED,
failures,
)
@@ -55,7 +55,7 @@ func _initialize() -> void:
)
_expect_with_references(
"empty project reference",
- "/tmp/NETfishing-test-data",
+ "/tmp/straywild-test-data",
"",
INSTALL_PATH,
PlayerDataRoot.PATH_ALLOWED,
@@ -63,7 +63,7 @@ func _initialize() -> void:
)
_expect_with_references(
"root project reference",
- "/tmp/NETfishing-test-data",
+ "/tmp/straywild-test-data",
"/",
INSTALL_PATH,
PlayerDataRoot.PATH_ALLOWED,
@@ -71,9 +71,9 @@ func _initialize() -> void:
)
_expect_with_references(
"Windows component and case normalization",
- "C:\\Games\\NETfishing Data",
- "C:\\Games\\NETfishing",
- "C:\\Program Files\\NETfishing",
+ "C:\\Games\\straywild Data",
+ "C:\\Games\\straywild",
+ "C:\\Program Files\\straywild",
PlayerDataRoot.PATH_ALLOWED,
failures,
true,
diff --git a/scripts/validate_animalese_samples.gd b/scripts/validate_animalese_samples.gd
new file mode 100644
index 0000000..a4e7502
--- /dev/null
+++ b/scripts/validate_animalese_samples.gd
@@ -0,0 +1,73 @@
+extends SceneTree
+
+const AnimaleseVoiceType = preload("res://ui/animalese_voice.gd")
+const VoiceProfilesType = preload(
+ "res://player/animalese_voice_profiles.gd"
+)
+const EXPECTED_CHARACTERS := "abcdefghijklmnopqrstuvwxyz0123456789"
+const ROBOT_CHARACTERS := "abcdefghijklmnopqrstuvwxyz"
+
+var _failures: Array[String] = []
+
+
+func _initialize() -> void:
+ _expect(
+ AnimaleseVoiceType.SUPPORTED_CHARACTERS == EXPECTED_CHARACTERS,
+ "animalese supports every letter and digit",
+ )
+ _expect(
+ VoiceProfilesType.DEFAULT_SAMPLE_SET_ID == "kat",
+ "kat is the default animalese voice set",
+ )
+ _expect(
+ VoiceProfilesType.is_valid_sample_set("robot"),
+ "the original tones remain available as robot",
+ )
+ _expect(
+ VoiceProfilesType.is_valid_sample_set("kim"),
+ "kim is available as an animalese voice set",
+ )
+ _validate_set("kat", EXPECTED_CHARACTERS, false)
+ _validate_set("robot", ROBOT_CHARACTERS, true)
+ _validate_set("kim", EXPECTED_CHARACTERS, false)
+ if _failures.is_empty():
+ print("Animalese sample validation: PASS")
+ quit(0)
+ return
+ for failure: String in _failures:
+ push_error(failure)
+ quit(1)
+
+
+func _validate_set(
+ sample_set_id: String,
+ characters: String,
+ expects_fallback: bool,
+) -> void:
+ var sample_directory := VoiceProfilesType.sample_directory_for(
+ sample_set_id
+ )
+ _expect(
+ not sample_directory.is_empty(),
+ "%s has a sample directory" % sample_set_id,
+ )
+ for character_index: int in range(characters.length()):
+ var character := characters.substr(character_index, 1)
+ var sample_path := "%s/%s.wav" % [sample_directory, character]
+ _expect(
+ ResourceLoader.exists(sample_path, "AudioStreamWAV"),
+ "%s sample loads for %s" % [sample_set_id, character],
+ )
+ if expects_fallback:
+ _expect(
+ ResourceLoader.exists(
+ "%s/fallback.wav" % sample_directory,
+ "AudioStreamWAV",
+ ),
+ "%s supplies a fallback for digits and symbols" % sample_set_id,
+ )
+
+
+func _expect(condition: bool, message: String) -> void:
+ if not condition:
+ _failures.append(message)
diff --git a/scripts/generate_placeholder_animalese.gd.uid b/scripts/validate_animalese_samples.gd.uid
similarity index 100%
rename from scripts/generate_placeholder_animalese.gd.uid
rename to scripts/validate_animalese_samples.gd.uid
diff --git a/server/dedicated_server_config.gd b/server/dedicated_server_config.gd
index 457c382..3370457 100644
--- a/server/dedicated_server_config.gd
+++ b/server/dedicated_server_config.gd
@@ -1,18 +1,25 @@
class_name DedicatedServerConfig
extends RefCounted
-const DEFAULT_NAME: String = "NETfishing Dedicated Server"
+const DEFAULT_NAME: String = "straywild Dedicated Server"
const DEFAULT_BIND_ADDRESS: String = "*"
const DEFAULT_PORT: int = 7777
const DEFAULT_MAX_PLAYERS: int = 8
+const DEFAULT_WORLD_SEED: int = 13001
+const MAX_WORLD_SEED: int = 2147483646
+const MAX_OPERATORS: int = 64
var server_name: String = DEFAULT_NAME
var bind_address: String = DEFAULT_BIND_ADDRESS
var port: int = DEFAULT_PORT
var max_players: int = DEFAULT_MAX_PLAYERS
+var world_seed: int = DEFAULT_WORLD_SEED
var public_listing: bool = false
var discovery_url: String = ""
var data_directory: String = ""
+var chat_logging: bool = false
+var chat_log_path: String = ""
+var operator_fingerprints: PackedStringArray = PackedStringArray()
var error_message: String = ""
@@ -24,8 +31,10 @@ static func from_runtime() -> DedicatedServerConfig:
result.discovery_url = str(ProjectSettings.get_setting(
"network/discovery/base_url", ""
)).strip_edges()
- var config_path: String = OS.get_environment(
- "NETFISHING_SERVER_CONFIG"
+ var config_path: String = _environment_string(
+ "straywild_SERVER_CONFIG",
+ "NETFISHING_SERVER_CONFIG",
+ "",
).strip_edges()
for argument: String in OS.get_cmdline_user_args():
if argument.begins_with("--config="):
@@ -56,6 +65,7 @@ func _load_file(path: String) -> bool:
max_players = int(file.get_value(
"server", "max_players", max_players
))
+ world_seed = int(file.get_value("world", "seed", world_seed))
public_listing = bool(file.get_value(
"server", "public", public_listing
))
@@ -65,29 +75,59 @@ func _load_file(path: String) -> bool:
discovery_url = str(file.get_value(
"discovery", "url", discovery_url
))
+ chat_logging = bool(file.get_value(
+ "privacy", "chat_logging", chat_logging
+ ))
+ chat_log_path = str(file.get_value(
+ "privacy", "chat_log_path", chat_log_path
+ ))
+ 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
+ "straywild_SERVER_NAME", "NETFISHING_SERVER_NAME", server_name
)
bind_address = _environment_string(
- "NETFISHING_SERVER_BIND", bind_address
+ "straywild_SERVER_BIND", "NETFISHING_SERVER_BIND", bind_address
+ )
+ port = _environment_int(
+ "straywild_SERVER_PORT", "NETFISHING_SERVER_PORT", port
)
- port = _environment_int("NETFISHING_SERVER_PORT", port)
max_players = _environment_int(
- "NETFISHING_SERVER_MAX_PLAYERS", max_players
+ "straywild_SERVER_MAX_PLAYERS",
+ "NETFISHING_SERVER_MAX_PLAYERS",
+ max_players,
+ )
+ world_seed = _environment_int(
+ "straywild_WORLD_SEED", "NETFISHING_WORLD_SEED", world_seed
)
public_listing = _environment_bool(
- "NETFISHING_SERVER_PUBLIC", public_listing
+ "straywild_SERVER_PUBLIC", "NETFISHING_SERVER_PUBLIC", public_listing
)
discovery_url = _environment_string(
- "NETFISHING_DISCOVERY_URL", discovery_url
+ "straywild_DISCOVERY_URL", "NETFISHING_DISCOVERY_URL", discovery_url
)
data_directory = _environment_string(
- "NETFISHING_DATA_DIR", data_directory
+ "straywild_DATA_DIR", "NETFISHING_DATA_DIR", data_directory
)
+ chat_logging = _environment_bool(
+ "straywild_CHAT_LOGGING", "NETFISHING_CHAT_LOGGING", chat_logging
+ )
+ chat_log_path = _environment_string(
+ "straywild_CHAT_LOG_PATH", "NETFISHING_CHAT_LOG_PATH", chat_log_path
+ )
+ var operators_variable: String = _available_environment_variable(
+ "straywild_SERVER_OPERATORS",
+ "NETFISHING_SERVER_OPERATORS",
+ )
+ if not operators_variable.is_empty():
+ operator_fingerprints = _parse_fingerprint_list(
+ OS.get_environment(operators_variable)
+ )
func _apply_arguments(arguments: PackedStringArray) -> void:
@@ -102,14 +142,28 @@ func _apply_arguments(arguments: PackedStringArray) -> void:
max_players = _parse_int(
argument.trim_prefix("--max-players="), max_players
)
+ elif argument.begins_with("--world-seed="):
+ world_seed = _parse_int(
+ argument.trim_prefix("--world-seed="), world_seed
+ )
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.begins_with("--chat-log-path="):
+ chat_log_path = argument.trim_prefix("--chat-log-path=")
elif argument == "--public":
public_listing = true
elif argument == "--private":
public_listing = false
+ elif argument == "--chat-logging":
+ chat_logging = true
+ elif argument == "--no-chat-logging":
+ chat_logging = false
func _validate() -> void:
@@ -117,6 +171,10 @@ func _validate() -> void:
bind_address = bind_address.strip_edges()
discovery_url = discovery_url.strip_edges().trim_suffix("/")
data_directory = data_directory.strip_edges()
+ chat_log_path = chat_log_path.strip_edges()
+ if chat_logging and chat_log_path.is_empty() and not data_directory.is_empty():
+ chat_log_path = data_directory.path_join("logs/chat.jsonl")
+ operator_fingerprints = _normalized_fingerprints(operator_fingerprints)
if server_name.is_empty():
error_message = "Server name cannot be empty."
elif not _safe_text(server_name):
@@ -127,13 +185,32 @@ func _validate() -> void:
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 world_seed <= 0 or world_seed > MAX_WORLD_SEED:
+ error_message = "World seed must be between 1 and %d." % MAX_WORLD_SEED
elif not data_directory.is_empty() and not data_directory.is_absolute_path():
error_message = "Server data directory must be an absolute path."
+ elif chat_logging and (
+ chat_log_path.is_empty() or not chat_log_path.is_absolute_path()
+ ):
+ error_message = (
+ "Chat logging requires an absolute chat log 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:
@@ -144,23 +221,73 @@ static func _safe_text(value: String) -> bool:
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 _available_environment_variable(
+ name: String,
+ legacy_name: String,
+) -> String:
+ if OS.has_environment(name):
+ return name
+ return legacy_name if OS.has_environment(legacy_name) else ""
-static func _environment_int(name: String, fallback: int) -> int:
+static func _environment_string(
+ name: String,
+ legacy_name: String,
+ fallback: String,
+) -> String:
+ var variable: String = _available_environment_variable(name, legacy_name)
+ return OS.get_environment(variable) if not variable.is_empty() else fallback
+
+
+static func _environment_int(
+ name: String,
+ legacy_name: String,
+ fallback: int,
+) -> int:
+ var variable: String = _available_environment_variable(name, legacy_name)
return (
- _parse_int(OS.get_environment(name), fallback)
- if OS.has_environment(name) else fallback
+ _parse_int(OS.get_environment(variable), fallback)
+ if not variable.is_empty() else fallback
)
-static func _environment_bool(name: String, fallback: bool) -> bool:
- if not OS.has_environment(name):
+static func _environment_bool(
+ name: String,
+ legacy_name: String,
+ fallback: bool,
+) -> bool:
+ var variable: String = _available_environment_variable(name, legacy_name)
+ if variable.is_empty():
return fallback
- var value: String = OS.get_environment(name).strip_edges().to_lower()
+ var value: String = OS.get_environment(variable).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/settings/controller_mapping_manager.gd b/settings/controller_mapping_manager.gd
index 1690963..278022e 100644
--- a/settings/controller_mapping_manager.gd
+++ b/settings/controller_mapping_manager.gd
@@ -80,15 +80,15 @@ const ROLE_LABELS: Dictionary = {
ROLE_A: "jump / menu accept",
ROLE_B: "menu back",
ROLE_X: "player menu",
- ROLE_Y: "interact",
+ ROLE_Y: "interact / character call",
ROLE_LB: "focus chat or world",
ROLE_RB: "primary action",
ROLE_POINTER_MODIFIER: "virtual mouse modifier",
- ROLE_CAMERA_ZOOM: "camera zoom",
+ ROLE_CAMERA_ZOOM: "zoom modifier (+RT)",
ROLE_SELECT: "chat",
ROLE_START: "pause",
ROLE_LEFT_STICK_CLICK: "sprint",
- ROLE_RIGHT_STICK_CLICK: "unassigned stick click",
+ ROLE_RIGHT_STICK_CLICK: "sneak",
ROLE_DPAD_UP: "emote wheel",
ROLE_DPAD_DOWN: "quick menu",
ROLE_DPAD_LEFT: "previous hotbar slot",
@@ -96,7 +96,7 @@ const ROLE_LABELS: Dictionary = {
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",
+ ROLE_RIGHT_STICK_Y: "camera up / down (+RT to zoom)",
}
const ROLE_PROMPTS: Dictionary = {
@@ -106,8 +106,8 @@ const ROLE_PROMPTS: Dictionary = {
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_POINTER_MODIFIER: "press or squeeze LT",
+ ROLE_CAMERA_ZOOM: "press or squeeze RT",
ROLE_SELECT: "press select / back",
ROLE_START: "press start",
ROLE_LEFT_STICK_CLICK: "click the left stick",
@@ -139,11 +139,13 @@ const BUTTON_ACTION_ROLES: Dictionary = {
&"ui_cancel": ROLE_B,
&"open_backpack": ROLE_X,
&"interact": ROLE_Y,
+ &"character_call": ROLE_Y,
&"focus_gameplay": ROLE_LB,
&"fish_primary": ROLE_RB,
&"open_chat": ROLE_SELECT,
&"open_system_menu": ROLE_START,
&"sprint": ROLE_LEFT_STICK_CLICK,
+ &"sneak": ROLE_RIGHT_STICK_CLICK,
&"open_emotes": ROLE_DPAD_UP,
&"open_quick_actions": ROLE_DPAD_DOWN,
&"hotbar_previous": ROLE_DPAD_LEFT,
@@ -280,7 +282,7 @@ func _validated_profiles(raw_profiles: Dictionary) -> Dictionary:
):
complete = false
break
- if not complete:
+ if not complete or bindings_have_conflicts(bindings):
continue
validated[str(key_value)] = {
"controller_name": str(
@@ -400,6 +402,8 @@ func replace_active_bindings(bindings: Dictionary) -> bool:
return false
if not validate_binding(role, binding as Dictionary):
return false
+ if bindings_have_conflicts(bindings):
+ return false
var previous: Dictionary = _profiles.duplicate(true)
_profiles[_active_profile_key] = {
"controller_name": _active_controller_name,
@@ -482,6 +486,53 @@ func validate_binding(role: StringName, binding: Dictionary) -> bool:
)
+static func bindings_have_conflicts(bindings: Dictionary) -> bool:
+ for role_index: int in ROLE_ORDER.size():
+ var first_role: StringName = ROLE_ORDER[role_index]
+ var first_value: Variant = bindings.get(str(first_role), {})
+ if typeof(first_value) != TYPE_DICTIONARY:
+ continue
+ for other_index: int in range(role_index + 1, ROLE_ORDER.size()):
+ var second_role: StringName = ROLE_ORDER[other_index]
+ var second_value: Variant = bindings.get(str(second_role), {})
+ if typeof(second_value) != TYPE_DICTIONARY:
+ continue
+ if bindings_conflict(
+ first_role,
+ first_value as Dictionary,
+ second_role,
+ second_value as Dictionary,
+ ):
+ return true
+ return false
+
+
+static 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 TRIGGER_ROLES
+ and second_role in TRIGGER_ROLES
+ and signf(float(first.get("direction", 0.0)))
+ != signf(float(second.get("direction", 0.0)))
+ )
+ return not opposite_trigger_halves
+
+
func binding_label(binding: Dictionary) -> String:
if str(binding.get("kind", "")) == "button":
return _button_label(int(binding.get("button", -1)))
diff --git a/settings/keyboard_mouse_mapping_manager.gd b/settings/keyboard_mouse_mapping_manager.gd
new file mode 100644
index 0000000..54dacc8
--- /dev/null
+++ b/settings/keyboard_mouse_mapping_manager.gd
@@ -0,0 +1,472 @@
+class_name KeyboardMouseMappingManager
+extends Node
+
+signal mapping_changed
+
+const FORMAT_VERSION: int = 2
+const LEGACY_FORMAT_VERSION: int = 1
+const MAPPING_PATH: String = "user://keyboard_mouse_bindings.json"
+const MAPPING_TEMP_PATH: String = "user://keyboard_mouse_bindings.json.tmp"
+const MAPPING_BACKUP_PATH: String = (
+ "user://keyboard_mouse_bindings.json.backup"
+)
+const MAX_MAPPING_BYTES: int = 256 * 1024
+
+const ROLE_MOVE_FORWARD: StringName = &"move_forward"
+const ROLE_MOVE_BACKWARD: StringName = &"move_backward"
+const ROLE_MOVE_LEFT: StringName = &"move_left"
+const ROLE_MOVE_RIGHT: StringName = &"move_right"
+const ROLE_SPRINT: StringName = &"sprint"
+const ROLE_JUMP: StringName = &"jump"
+const ROLE_SNEAK: StringName = &"sneak"
+const ROLE_SLOW_WALK: StringName = &"slow_walk"
+const ROLE_INTERACT: StringName = &"interact"
+const ROLE_PRIMARY_ACTION: StringName = &"fish_primary"
+const ROLE_ALTERNATE_REEL: StringName = &"reel_alternate"
+const ROLE_CAMERA_DRAG: StringName = &"camera_drag"
+const ROLE_CAMERA_ZOOM_IN: StringName = &"camera_zoom_in"
+const ROLE_CAMERA_ZOOM_OUT: StringName = &"camera_zoom_out"
+const ROLE_PLAYER_MENU: StringName = &"open_backpack"
+const ROLE_TACKLE_BOX: StringName = &"open_tacklebox"
+const ROLE_PROP_BOOK: StringName = &"open_props"
+const ROLE_EMOTE_WHEEL: StringName = &"open_emotes"
+const ROLE_CHARACTER_CALL: StringName = &"character_call"
+const ROLE_CHAT: StringName = &"open_chat"
+const ROLE_FREE_CAMERA: StringName = &"toggle_free_camera"
+const ROLE_HIDE_HUD: StringName = &"toggle_hud"
+const ROLE_MENU_ACCEPT: StringName = &"ui_accept"
+const ROLE_MENU_BACK: StringName = &"ui_cancel"
+
+const ROLE_ORDER: Array[StringName] = [
+ ROLE_MOVE_FORWARD,
+ ROLE_MOVE_BACKWARD,
+ ROLE_MOVE_LEFT,
+ ROLE_MOVE_RIGHT,
+ ROLE_SPRINT,
+ ROLE_JUMP,
+ ROLE_SNEAK,
+ ROLE_SLOW_WALK,
+ ROLE_INTERACT,
+ ROLE_PRIMARY_ACTION,
+ ROLE_ALTERNATE_REEL,
+ ROLE_CAMERA_DRAG,
+ ROLE_CAMERA_ZOOM_IN,
+ ROLE_CAMERA_ZOOM_OUT,
+ ROLE_PLAYER_MENU,
+ ROLE_TACKLE_BOX,
+ ROLE_PROP_BOOK,
+ ROLE_EMOTE_WHEEL,
+ ROLE_CHARACTER_CALL,
+ ROLE_CHAT,
+ ROLE_FREE_CAMERA,
+ ROLE_HIDE_HUD,
+ &"hotbar_1",
+ &"hotbar_2",
+ &"hotbar_3",
+ &"hotbar_4",
+ &"hotbar_5",
+ &"hotbar_6",
+ &"hotbar_7",
+ &"hotbar_8",
+ &"hotbar_9",
+ ROLE_MENU_ACCEPT,
+ ROLE_MENU_BACK,
+]
+
+const ROLE_LABELS: Dictionary = {
+ ROLE_MOVE_FORWARD: "move forward",
+ ROLE_MOVE_BACKWARD: "move backward",
+ ROLE_MOVE_LEFT: "move left",
+ ROLE_MOVE_RIGHT: "move right",
+ ROLE_SPRINT: "sprint",
+ ROLE_JUMP: "jump",
+ ROLE_SNEAK: "sneak",
+ ROLE_SLOW_WALK: "slow walk",
+ ROLE_INTERACT: "interact",
+ ROLE_PRIMARY_ACTION: "primary action",
+ ROLE_ALTERNATE_REEL: "alternate reel",
+ ROLE_CAMERA_DRAG: "rotate camera",
+ ROLE_CAMERA_ZOOM_IN: "hotbar select (+SHIFT to zoom)",
+ ROLE_CAMERA_ZOOM_OUT: "hotbar select (+SHIFT to zoom)",
+ ROLE_PLAYER_MENU: "player menu",
+ ROLE_TACKLE_BOX: "tackle box",
+ ROLE_PROP_BOOK: "prop book",
+ ROLE_EMOTE_WHEEL: "emote wheel",
+ ROLE_CHARACTER_CALL: "character call",
+ ROLE_CHAT: "chat",
+ ROLE_FREE_CAMERA: "free camera",
+ ROLE_HIDE_HUD: "hide hud",
+ &"hotbar_1": "hotbar 1",
+ &"hotbar_2": "hotbar 2",
+ &"hotbar_3": "hotbar 3",
+ &"hotbar_4": "hotbar 4",
+ &"hotbar_5": "hotbar 5",
+ &"hotbar_6": "hotbar 6",
+ &"hotbar_7": "hotbar 7",
+ &"hotbar_8": "hotbar 8",
+ &"hotbar_9": "hotbar 9",
+ ROLE_MENU_ACCEPT: "menu accept",
+ ROLE_MENU_BACK: "menu back / pause",
+}
+
+var _default_events: Dictionary = {}
+var _default_bindings: Dictionary = {}
+var _bindings: Dictionary = {}
+
+
+func _ready() -> void:
+ _capture_project_defaults()
+ load_mapping()
+ _apply_active_mapping()
+
+
+func load_mapping() -> bool:
+ _recover_interrupted_write()
+ if not FileAccess.file_exists(MAPPING_PATH):
+ _bindings = {}
+ return true
+ var file := FileAccess.open(MAPPING_PATH, FileAccess.READ)
+ if file == null:
+ _bindings = {}
+ return false
+ if file.get_length() > MAX_MAPPING_BYTES:
+ file.close()
+ push_warning("Keyboard bindings are too large; using defaults.")
+ _bindings = {}
+ 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("Keyboard bindings are malformed; using defaults.")
+ _bindings = {}
+ return false
+ var data := json.data as Dictionary
+ var format_version: int = int(data.get("format_version", -1))
+ if format_version not in [LEGACY_FORMAT_VERSION, FORMAT_VERSION]:
+ push_warning("Keyboard binding version is unsupported; using defaults.")
+ _bindings = {}
+ return false
+ var raw_bindings: Variant = data.get("bindings", {})
+ if (
+ typeof(raw_bindings) != TYPE_DICTIONARY
+ ):
+ push_warning("Keyboard bindings are incomplete; using defaults.")
+ _bindings = {}
+ return false
+ var loaded_bindings := (raw_bindings as Dictionary).duplicate(true)
+ if format_version == LEGACY_FORMAT_VERSION:
+ loaded_bindings[str(ROLE_ALTERNATE_REEL)] = (
+ _default_bindings[str(ROLE_ALTERNATE_REEL)].duplicate(true)
+ )
+ if not _validate_complete_bindings(loaded_bindings):
+ push_warning("Keyboard bindings are incomplete; using defaults.")
+ _bindings = {}
+ return false
+ _bindings = loaded_bindings
+ return true
+
+
+func has_custom_mapping() -> bool:
+ return not _bindings.is_empty()
+
+
+func get_active_bindings() -> Dictionary:
+ if has_custom_mapping():
+ return _bindings.duplicate(true)
+ return _default_bindings.duplicate(true)
+
+
+func get_binding(role: StringName) -> Dictionary:
+ var binding: Variant = get_active_bindings().get(str(role), {})
+ if typeof(binding) != TYPE_DICTIONARY:
+ return {}
+ return (binding as Dictionary).duplicate(true)
+
+
+func get_binding_label(role: StringName) -> String:
+ return binding_label(get_binding(role))
+
+
+func get_role_label(role: StringName) -> String:
+ return str(ROLE_LABELS.get(role, str(role).replace("_", " ")))
+
+
+func binding_from_event(event: InputEvent) -> Dictionary:
+ var key_event := event as InputEventKey
+ if key_event != null:
+ if not key_event.pressed or key_event.echo:
+ return {}
+ if key_event.keycode in [KEY_SHIFT, KEY_CTRL, KEY_ALT, KEY_META]:
+ return {}
+ if key_event.keycode == KEY_NONE and (
+ key_event.physical_keycode == KEY_NONE
+ ):
+ return {}
+ return {
+ "kind": "key",
+ "keycode": int(key_event.keycode),
+ "physical_keycode": int(key_event.physical_keycode),
+ "alt": key_event.alt_pressed,
+ "shift": key_event.shift_pressed,
+ "ctrl": key_event.ctrl_pressed,
+ "meta": key_event.meta_pressed,
+ "location": int(key_event.location),
+ }
+ var mouse_event := event as InputEventMouseButton
+ if mouse_event == null or not mouse_event.pressed:
+ return {}
+ return {
+ "kind": "mouse_button",
+ "button": int(mouse_event.button_index),
+ }
+
+
+func validate_binding(binding: Dictionary) -> bool:
+ var kind: String = str(binding.get("kind", ""))
+ if kind == "mouse_button":
+ var button: int = int(binding.get("button", 0))
+ return (
+ button >= int(MOUSE_BUTTON_LEFT)
+ and button <= int(MOUSE_BUTTON_XBUTTON2)
+ )
+ if kind != "key":
+ return false
+ var keycode: int = int(binding.get("keycode", int(KEY_NONE)))
+ var physical_keycode: int = int(
+ binding.get("physical_keycode", int(KEY_NONE))
+ )
+ if keycode == int(KEY_NONE) and physical_keycode == int(KEY_NONE):
+ return false
+ for modifier: String in ["alt", "shift", "ctrl", "meta"]:
+ if typeof(binding.get(modifier, false)) != TYPE_BOOL:
+ return false
+ return true
+
+
+func set_binding(role: StringName, binding: Dictionary) -> bool:
+ if role not in ROLE_ORDER or not validate_binding(binding):
+ return false
+ var updated: Dictionary = get_active_bindings()
+ updated[str(role)] = binding.duplicate(true)
+ return replace_active_bindings(updated)
+
+
+func replace_active_bindings(bindings: Dictionary) -> bool:
+ if not _validate_complete_bindings(bindings):
+ return false
+ var previous: Dictionary = _bindings.duplicate(true)
+ _bindings = bindings.duplicate(true)
+ if not _save_mapping():
+ _bindings = previous
+ return false
+ _apply_active_mapping()
+ mapping_changed.emit()
+ return true
+
+
+func reset_mapping() -> bool:
+ var previous: Dictionary = _bindings.duplicate(true)
+ _bindings = {}
+ if not _remove_if_present(MAPPING_PATH):
+ _bindings = previous
+ return false
+ _remove_if_present(MAPPING_TEMP_PATH)
+ _remove_if_present(MAPPING_BACKUP_PATH)
+ _restore_project_defaults()
+ mapping_changed.emit()
+ return true
+
+
+func binding_label(binding: Dictionary) -> String:
+ if str(binding.get("kind", "")) == "mouse_button":
+ return _mouse_button_label(int(binding.get("button", 0)))
+ if str(binding.get("kind", "")) != "key":
+ return "unmapped"
+ var event := _event_from_binding(binding) as InputEventKey
+ if event == null:
+ return "unmapped"
+ var label: String = event.as_text_physical_keycode()
+ if label.is_empty():
+ label = event.as_text_keycode()
+ return label.to_lower() if not label.is_empty() else "unknown key"
+
+
+static func bindings_conflict(first: Dictionary, second: Dictionary) -> bool:
+ if str(first.get("kind", "")) != str(second.get("kind", "")):
+ return false
+ if str(first.get("kind", "")) == "mouse_button":
+ return int(first.get("button", 0)) == int(second.get("button", 0))
+ if str(first.get("kind", "")) != "key":
+ return false
+ return (
+ int(first.get("keycode", 0)) == int(second.get("keycode", 0))
+ and int(first.get("physical_keycode", 0))
+ == int(second.get("physical_keycode", 0))
+ and bool(first.get("alt", false)) == bool(second.get("alt", false))
+ and bool(first.get("shift", false)) == bool(second.get("shift", false))
+ and bool(first.get("ctrl", false)) == bool(second.get("ctrl", false))
+ and bool(first.get("meta", false)) == bool(second.get("meta", false))
+ )
+
+
+func _capture_project_defaults() -> void:
+ _default_events.clear()
+ _default_bindings.clear()
+ for role: StringName in ROLE_ORDER:
+ var action: StringName = role
+ var events: Array[InputEvent] = []
+ if InputMap.has_action(action):
+ for event: InputEvent in InputMap.action_get_events(action):
+ if not _is_keyboard_mouse_event(event):
+ continue
+ events.append(event.duplicate())
+ if not _default_bindings.has(str(role)):
+ var binding: Dictionary = binding_from_event(
+ _pressed_copy(event)
+ )
+ if not binding.is_empty():
+ _default_bindings[str(role)] = binding
+ _default_events[action] = events
+
+
+func _validate_complete_bindings(bindings: Dictionary) -> bool:
+ for role: StringName in ROLE_ORDER:
+ var raw_binding: Variant = bindings.get(str(role), {})
+ if (
+ typeof(raw_binding) != TYPE_DICTIONARY
+ or not validate_binding(raw_binding as Dictionary)
+ ):
+ return false
+ return true
+
+
+func _apply_active_mapping() -> void:
+ if not has_custom_mapping():
+ _restore_project_defaults()
+ return
+ _remove_managed_keyboard_mouse_events()
+ for role: StringName in ROLE_ORDER:
+ var binding: Variant = _bindings.get(str(role), {})
+ if typeof(binding) != TYPE_DICTIONARY:
+ continue
+ var event: InputEvent = _event_from_binding(binding as Dictionary)
+ if event != null:
+ InputMap.action_add_event(role, event)
+
+
+func _restore_project_defaults() -> void:
+ _remove_managed_keyboard_mouse_events()
+ for action_value: Variant in _default_events:
+ var action: StringName = StringName(action_value)
+ for event: InputEvent in _default_events[action]:
+ InputMap.action_add_event(action, event.duplicate())
+
+
+func _remove_managed_keyboard_mouse_events() -> void:
+ for action: StringName in ROLE_ORDER:
+ if not InputMap.has_action(action):
+ continue
+ for event: InputEvent in InputMap.action_get_events(action):
+ if _is_keyboard_mouse_event(event):
+ InputMap.action_erase_event(action, event)
+
+
+func _event_from_binding(binding: Dictionary) -> InputEvent:
+ if not validate_binding(binding):
+ return null
+ if str(binding.get("kind", "")) == "mouse_button":
+ var mouse_event := InputEventMouseButton.new()
+ mouse_event.button_index = int(binding.get("button", 0)) as MouseButton
+ return mouse_event
+ var key_event := InputEventKey.new()
+ key_event.keycode = int(binding.get("keycode", 0)) as Key
+ key_event.physical_keycode = int(
+ binding.get("physical_keycode", 0)
+ ) as Key
+ key_event.alt_pressed = bool(binding.get("alt", false))
+ key_event.shift_pressed = bool(binding.get("shift", false))
+ key_event.ctrl_pressed = bool(binding.get("ctrl", false))
+ key_event.meta_pressed = bool(binding.get("meta", false))
+ key_event.location = int(binding.get("location", 0)) as KeyLocation
+ return key_event
+
+
+static func _pressed_copy(event: InputEvent) -> InputEvent:
+ var result: InputEvent = event.duplicate()
+ if result is InputEventKey:
+ (result as InputEventKey).pressed = true
+ elif result is InputEventMouseButton:
+ (result as InputEventMouseButton).pressed = true
+ return result
+
+
+static func _is_keyboard_mouse_event(event: InputEvent) -> bool:
+ return event is InputEventKey or event is InputEventMouseButton
+
+
+static func _mouse_button_label(button: int) -> String:
+ var labels: Dictionary = {
+ int(MOUSE_BUTTON_LEFT): "mouse left",
+ int(MOUSE_BUTTON_RIGHT): "mouse right",
+ int(MOUSE_BUTTON_MIDDLE): "mouse middle",
+ int(MOUSE_BUTTON_WHEEL_UP): "wheel up",
+ int(MOUSE_BUTTON_WHEEL_DOWN): "wheel down",
+ int(MOUSE_BUTTON_WHEEL_LEFT): "wheel left",
+ int(MOUSE_BUTTON_WHEEL_RIGHT): "wheel right",
+ int(MOUSE_BUTTON_XBUTTON1): "mouse 4",
+ int(MOUSE_BUTTON_XBUTTON2): "mouse 5",
+ }
+ return str(labels.get(button, "mouse %d" % button))
+
+
+func _save_mapping() -> bool:
+ var file := FileAccess.open(MAPPING_TEMP_PATH, FileAccess.WRITE)
+ if file == null:
+ return false
+ file.store_string(JSON.stringify({
+ "format_version": FORMAT_VERSION,
+ "bindings": _bindings,
+ }, "\t"))
+ file.flush()
+ var write_error: Error = file.get_error()
+ file.close()
+ if write_error != OK:
+ _remove_if_present(MAPPING_TEMP_PATH)
+ return false
+ _remove_if_present(MAPPING_BACKUP_PATH)
+ var had_primary: bool = FileAccess.file_exists(MAPPING_PATH)
+ if had_primary and not _rename_file(MAPPING_PATH, MAPPING_BACKUP_PATH):
+ _remove_if_present(MAPPING_TEMP_PATH)
+ return false
+ if not _rename_file(MAPPING_TEMP_PATH, MAPPING_PATH):
+ if had_primary:
+ _rename_file(MAPPING_BACKUP_PATH, MAPPING_PATH)
+ return false
+ _remove_if_present(MAPPING_BACKUP_PATH)
+ return true
+
+
+func _recover_interrupted_write() -> void:
+ if FileAccess.file_exists(MAPPING_PATH):
+ _remove_if_present(MAPPING_TEMP_PATH)
+ _remove_if_present(MAPPING_BACKUP_PATH)
+ return
+ if FileAccess.file_exists(MAPPING_BACKUP_PATH):
+ _rename_file(MAPPING_BACKUP_PATH, MAPPING_PATH)
+ _remove_if_present(MAPPING_TEMP_PATH)
+
+
+static 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
+
+
+static func _remove_if_present(path: String) -> bool:
+ if not FileAccess.file_exists(path):
+ return true
+ return DirAccess.remove_absolute(
+ ProjectSettings.globalize_path(path)
+ ) == OK
diff --git a/settings/keyboard_mouse_mapping_manager.gd.uid b/settings/keyboard_mouse_mapping_manager.gd.uid
new file mode 100644
index 0000000..7c0679c
--- /dev/null
+++ b/settings/keyboard_mouse_mapping_manager.gd.uid
@@ -0,0 +1 @@
+uid://b7ih6m4nte705
diff --git a/settings/player_settings.gd b/settings/player_settings.gd
index a20a3e1..59099fc 100644
--- a/settings/player_settings.gd
+++ b/settings/player_settings.gd
@@ -15,6 +15,9 @@ 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 DEFAULT_CHAT_DOCK_RIGHT: bool = false
+const DEFAULT_CHAT_MOBILE_MODE: bool = false
+const DEFAULT_PAINT_DOCK_RIGHT: bool = true
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]
@@ -29,12 +32,14 @@ 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 swap_hotbar_camera_scroll: 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 chat_dock_right: bool = DEFAULT_CHAT_DOCK_RIGHT
+@export var chat_mobile_mode: bool = DEFAULT_CHAT_MOBILE_MODE
+@export var paint_dock_right: bool = DEFAULT_PAINT_DOCK_RIGHT
+@export var presentation_layout_customized: bool = false
@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
@@ -75,12 +80,14 @@ 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.swap_hotbar_camera_scroll = swap_hotbar_camera_scroll
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.presentation_layout_customized = presentation_layout_customized
result.fullscreen_enabled = fullscreen_enabled
result.master_volume = master_volume
result.music_volume = music_volume
@@ -91,6 +98,20 @@ func copy() -> PlayerSettings:
return result
+func normalize_implicit_presentation_layout() -> bool:
+ if presentation_layout_customized:
+ return false
+ var changed: bool = (
+ chat_dock_right != DEFAULT_CHAT_DOCK_RIGHT
+ or chat_mobile_mode != DEFAULT_CHAT_MOBILE_MODE
+ or paint_dock_right != DEFAULT_PAINT_DOCK_RIGHT
+ )
+ chat_dock_right = DEFAULT_CHAT_DOCK_RIGHT
+ chat_mobile_mode = DEFAULT_CHAT_MOBILE_MODE
+ paint_dock_right = DEFAULT_PAINT_DOCK_RIGHT
+ return changed
+
+
static func _is_valid_audio_volume(value: float) -> bool:
return (
is_finite(value)
diff --git a/settings/player_settings_manager.gd b/settings/player_settings_manager.gd
index 84b3f44..362abc9 100644
--- a/settings/player_settings_manager.gd
+++ b/settings/player_settings_manager.gd
@@ -6,6 +6,9 @@ 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
+const HEADLESS_WRITE_ENVIRONMENT: String = (
+ "straywild_ISOLATED_VALIDATION_STORAGE"
+)
signal settings_changed(settings: PlayerSettings)
@@ -51,6 +54,10 @@ func load_settings() -> bool:
if (
typeof(accessibility.get("auto_click_enabled")) != TYPE_BOOL
or typeof(camera.get("invert_vertical")) != TYPE_BOOL
+ or (
+ camera.has("swap_hotbar_camera_scroll")
+ and typeof(camera["swap_hotbar_camera_scroll"]) != TYPE_BOOL
+ )
or (
accessibility.has("on_screen_keyboard_enabled")
and typeof(accessibility["on_screen_keyboard_enabled"]) != TYPE_BOOL
@@ -79,6 +86,10 @@ func load_settings() -> bool:
presentation.has("paint_dock_right")
and typeof(presentation["paint_dock_right"]) != TYPE_BOOL
)
+ or (
+ presentation.has("layout_customized")
+ and typeof(presentation["layout_customized"]) != TYPE_BOOL
+ )
or (
presentation.has("fullscreen_enabled")
and typeof(presentation["fullscreen_enabled"]) != TYPE_BOOL
@@ -102,6 +113,9 @@ func load_settings() -> bool:
-1.0
)
loaded.invert_camera_y = camera["invert_vertical"]
+ loaded.swap_hotbar_camera_scroll = bool(
+ camera.get("swap_hotbar_camera_scroll", false)
+ )
loaded.on_screen_keyboard_enabled = bool(
accessibility.get("on_screen_keyboard_enabled", false)
)
@@ -132,6 +146,12 @@ func load_settings() -> bool:
presentation.get("chat_mobile_mode", false)
)
loaded.paint_dock_right = bool(presentation.get("paint_dock_right", true))
+ loaded.presentation_layout_customized = bool(
+ presentation.get("layout_customized", false)
+ )
+ var normalized_implicit_layout: bool = (
+ loaded.normalize_implicit_presentation_layout()
+ )
loaded.fullscreen_enabled = bool(
presentation.get("fullscreen_enabled", false)
)
@@ -144,7 +164,7 @@ func load_settings() -> bool:
if not loaded.is_valid():
return _use_defaults_after_corruption("Player settings values are invalid.")
current_settings = loaded
- _is_dirty = false
+ _is_dirty = normalized_implicit_layout
_apply_audio_settings(current_settings)
emit_signal("settings_changed", current_settings)
return true
@@ -154,7 +174,9 @@ func apply_settings(settings: PlayerSettings) -> bool:
if settings == null or not settings.is_valid():
return false
var previous: PlayerSettings = current_settings
- current_settings = settings.copy()
+ var normalized_settings := settings.copy()
+ normalized_settings.normalize_implicit_presentation_layout()
+ current_settings = normalized_settings
_is_dirty = true
if not save_now():
current_settings = previous
@@ -197,6 +219,15 @@ func save_chat_preferences() -> bool:
func save_now() -> bool:
+ if (
+ OS.has_feature("editor")
+ and DisplayServer.get_name() == "headless"
+ and not OS.has_environment(HEADLESS_WRITE_ENVIRONMENT)
+ ):
+ push_error(
+ "Refusing to write player settings from an unisolated headless editor run."
+ )
+ return false
if current_settings == null or not current_settings.is_valid():
return false
var data: Dictionary = {
@@ -213,6 +244,9 @@ func save_now() -> bool:
"mouse_sensitivity": current_settings.mouse_camera_sensitivity,
"controller_sensitivity": current_settings.controller_camera_sensitivity,
"invert_vertical": current_settings.invert_camera_y,
+ "swap_hotbar_camera_scroll": (
+ current_settings.swap_hotbar_camera_scroll
+ ),
},
"audio": {
"master": current_settings.master_volume,
@@ -228,6 +262,9 @@ func save_now() -> bool:
"chat_dock_right": current_settings.chat_dock_right,
"chat_mobile_mode": current_settings.chat_mobile_mode,
"paint_dock_right": current_settings.paint_dock_right,
+ "layout_customized": (
+ current_settings.presentation_layout_customized
+ ),
"fullscreen_enabled": current_settings.fullscreen_enabled,
},
}
diff --git a/sound/dialogue/animalese/alphanumeric/0.wav b/sound/dialogue/animalese/alphanumeric/0.wav
new file mode 100644
index 0000000..779e725
Binary files /dev/null and b/sound/dialogue/animalese/alphanumeric/0.wav differ
diff --git a/sound/dialogue/animalese/placeholder/a.wav.import b/sound/dialogue/animalese/alphanumeric/0.wav.import
similarity index 50%
rename from sound/dialogue/animalese/placeholder/a.wav.import
rename to sound/dialogue/animalese/alphanumeric/0.wav.import
index 86de7ca..19ac04b 100644
--- a/sound/dialogue/animalese/placeholder/a.wav.import
+++ b/sound/dialogue/animalese/alphanumeric/0.wav.import
@@ -2,13 +2,13 @@
importer="wav"
type="AudioStreamWAV"
-uid="uid://dvajjy4bwadio"
-path="res://.godot/imported/a.wav-9ab4b1a9a555e6fcf07951f399769b30.sample"
+uid="uid://cbn5ed728rsm"
+path="res://.godot/imported/0.wav-9378d7763e1ef87ae982ab57f6e81f2d.sample"
[deps]
-source_file="res://sound/dialogue/animalese/placeholder/a.wav"
-dest_files=["res://.godot/imported/a.wav-9ab4b1a9a555e6fcf07951f399769b30.sample"]
+source_file="res://sound/dialogue/animalese/alphanumeric/0.wav"
+dest_files=["res://.godot/imported/0.wav-9378d7763e1ef87ae982ab57f6e81f2d.sample"]
[params]
diff --git a/sound/dialogue/animalese/alphanumeric/1.wav b/sound/dialogue/animalese/alphanumeric/1.wav
new file mode 100644
index 0000000..db7dd62
Binary files /dev/null and b/sound/dialogue/animalese/alphanumeric/1.wav differ
diff --git a/sound/dialogue/animalese/alphanumeric/1.wav.import b/sound/dialogue/animalese/alphanumeric/1.wav.import
new file mode 100644
index 0000000..fa2905a
--- /dev/null
+++ b/sound/dialogue/animalese/alphanumeric/1.wav.import
@@ -0,0 +1,24 @@
+[remap]
+
+importer="wav"
+type="AudioStreamWAV"
+uid="uid://8ofrxxya8xo8"
+path="res://.godot/imported/1.wav-759e5f8cac5cef5d0e23605844f5032c.sample"
+
+[deps]
+
+source_file="res://sound/dialogue/animalese/alphanumeric/1.wav"
+dest_files=["res://.godot/imported/1.wav-759e5f8cac5cef5d0e23605844f5032c.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/alphanumeric/2.wav b/sound/dialogue/animalese/alphanumeric/2.wav
new file mode 100644
index 0000000..74dd315
Binary files /dev/null and b/sound/dialogue/animalese/alphanumeric/2.wav differ
diff --git a/sound/dialogue/animalese/alphanumeric/2.wav.import b/sound/dialogue/animalese/alphanumeric/2.wav.import
new file mode 100644
index 0000000..800b899
--- /dev/null
+++ b/sound/dialogue/animalese/alphanumeric/2.wav.import
@@ -0,0 +1,24 @@
+[remap]
+
+importer="wav"
+type="AudioStreamWAV"
+uid="uid://cmxvxip3pwuun"
+path="res://.godot/imported/2.wav-4b3515a4b79f8ae9ffc829c8a5e3c285.sample"
+
+[deps]
+
+source_file="res://sound/dialogue/animalese/alphanumeric/2.wav"
+dest_files=["res://.godot/imported/2.wav-4b3515a4b79f8ae9ffc829c8a5e3c285.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/alphanumeric/3.wav b/sound/dialogue/animalese/alphanumeric/3.wav
new file mode 100644
index 0000000..6f90835
Binary files /dev/null and b/sound/dialogue/animalese/alphanumeric/3.wav differ
diff --git a/sound/dialogue/animalese/alphanumeric/3.wav.import b/sound/dialogue/animalese/alphanumeric/3.wav.import
new file mode 100644
index 0000000..5430aea
--- /dev/null
+++ b/sound/dialogue/animalese/alphanumeric/3.wav.import
@@ -0,0 +1,24 @@
+[remap]
+
+importer="wav"
+type="AudioStreamWAV"
+uid="uid://c8017waek7ew8"
+path="res://.godot/imported/3.wav-ffb8f531dceaa153b492b74c4bbf7c7c.sample"
+
+[deps]
+
+source_file="res://sound/dialogue/animalese/alphanumeric/3.wav"
+dest_files=["res://.godot/imported/3.wav-ffb8f531dceaa153b492b74c4bbf7c7c.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/alphanumeric/4.wav b/sound/dialogue/animalese/alphanumeric/4.wav
new file mode 100644
index 0000000..061b5da
Binary files /dev/null and b/sound/dialogue/animalese/alphanumeric/4.wav differ
diff --git a/sound/dialogue/animalese/alphanumeric/4.wav.import b/sound/dialogue/animalese/alphanumeric/4.wav.import
new file mode 100644
index 0000000..65e2fa4
--- /dev/null
+++ b/sound/dialogue/animalese/alphanumeric/4.wav.import
@@ -0,0 +1,24 @@
+[remap]
+
+importer="wav"
+type="AudioStreamWAV"
+uid="uid://bbtypop1cmrw1"
+path="res://.godot/imported/4.wav-effad197373ffb9b48afe78e0fe6c95a.sample"
+
+[deps]
+
+source_file="res://sound/dialogue/animalese/alphanumeric/4.wav"
+dest_files=["res://.godot/imported/4.wav-effad197373ffb9b48afe78e0fe6c95a.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/alphanumeric/5.wav b/sound/dialogue/animalese/alphanumeric/5.wav
new file mode 100644
index 0000000..a6c5000
Binary files /dev/null and b/sound/dialogue/animalese/alphanumeric/5.wav differ
diff --git a/sound/dialogue/animalese/alphanumeric/5.wav.import b/sound/dialogue/animalese/alphanumeric/5.wav.import
new file mode 100644
index 0000000..cddbc1f
--- /dev/null
+++ b/sound/dialogue/animalese/alphanumeric/5.wav.import
@@ -0,0 +1,24 @@
+[remap]
+
+importer="wav"
+type="AudioStreamWAV"
+uid="uid://ddwhh6igah1ok"
+path="res://.godot/imported/5.wav-2cd0baa0afa291660575e1fc431a6b91.sample"
+
+[deps]
+
+source_file="res://sound/dialogue/animalese/alphanumeric/5.wav"
+dest_files=["res://.godot/imported/5.wav-2cd0baa0afa291660575e1fc431a6b91.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/alphanumeric/6.wav b/sound/dialogue/animalese/alphanumeric/6.wav
new file mode 100644
index 0000000..41154f8
Binary files /dev/null and b/sound/dialogue/animalese/alphanumeric/6.wav differ
diff --git a/sound/dialogue/animalese/alphanumeric/6.wav.import b/sound/dialogue/animalese/alphanumeric/6.wav.import
new file mode 100644
index 0000000..4cb51b2
--- /dev/null
+++ b/sound/dialogue/animalese/alphanumeric/6.wav.import
@@ -0,0 +1,24 @@
+[remap]
+
+importer="wav"
+type="AudioStreamWAV"
+uid="uid://cqsddej565w48"
+path="res://.godot/imported/6.wav-8c88e3785af1748d3cc8334b825655e1.sample"
+
+[deps]
+
+source_file="res://sound/dialogue/animalese/alphanumeric/6.wav"
+dest_files=["res://.godot/imported/6.wav-8c88e3785af1748d3cc8334b825655e1.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/alphanumeric/7.wav b/sound/dialogue/animalese/alphanumeric/7.wav
new file mode 100644
index 0000000..e732f42
Binary files /dev/null and b/sound/dialogue/animalese/alphanumeric/7.wav differ
diff --git a/sound/dialogue/animalese/alphanumeric/7.wav.import b/sound/dialogue/animalese/alphanumeric/7.wav.import
new file mode 100644
index 0000000..7ed72ee
--- /dev/null
+++ b/sound/dialogue/animalese/alphanumeric/7.wav.import
@@ -0,0 +1,24 @@
+[remap]
+
+importer="wav"
+type="AudioStreamWAV"
+uid="uid://de5lwv3vtq4jo"
+path="res://.godot/imported/7.wav-aa6620dad62f7a8fa68ac3dfe0e8bce0.sample"
+
+[deps]
+
+source_file="res://sound/dialogue/animalese/alphanumeric/7.wav"
+dest_files=["res://.godot/imported/7.wav-aa6620dad62f7a8fa68ac3dfe0e8bce0.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/alphanumeric/8.wav b/sound/dialogue/animalese/alphanumeric/8.wav
new file mode 100644
index 0000000..2779375
Binary files /dev/null and b/sound/dialogue/animalese/alphanumeric/8.wav differ
diff --git a/sound/dialogue/animalese/alphanumeric/8.wav.import b/sound/dialogue/animalese/alphanumeric/8.wav.import
new file mode 100644
index 0000000..4733d6c
--- /dev/null
+++ b/sound/dialogue/animalese/alphanumeric/8.wav.import
@@ -0,0 +1,24 @@
+[remap]
+
+importer="wav"
+type="AudioStreamWAV"
+uid="uid://lsklvocqcohu"
+path="res://.godot/imported/8.wav-ee305634b68bbc3c852777570ad90d81.sample"
+
+[deps]
+
+source_file="res://sound/dialogue/animalese/alphanumeric/8.wav"
+dest_files=["res://.godot/imported/8.wav-ee305634b68bbc3c852777570ad90d81.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/alphanumeric/9.wav b/sound/dialogue/animalese/alphanumeric/9.wav
new file mode 100644
index 0000000..644d326
Binary files /dev/null and b/sound/dialogue/animalese/alphanumeric/9.wav differ
diff --git a/sound/dialogue/animalese/alphanumeric/9.wav.import b/sound/dialogue/animalese/alphanumeric/9.wav.import
new file mode 100644
index 0000000..06be5cb
--- /dev/null
+++ b/sound/dialogue/animalese/alphanumeric/9.wav.import
@@ -0,0 +1,24 @@
+[remap]
+
+importer="wav"
+type="AudioStreamWAV"
+uid="uid://cuwg5n2w8trdf"
+path="res://.godot/imported/9.wav-2dbc1a5726fda3c5d3e5b8a5c940e14e.sample"
+
+[deps]
+
+source_file="res://sound/dialogue/animalese/alphanumeric/9.wav"
+dest_files=["res://.godot/imported/9.wav-2dbc1a5726fda3c5d3e5b8a5c940e14e.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/alphanumeric/a.wav b/sound/dialogue/animalese/alphanumeric/a.wav
new file mode 100644
index 0000000..cd50a8b
Binary files /dev/null and b/sound/dialogue/animalese/alphanumeric/a.wav differ
diff --git a/sound/dialogue/animalese/placeholder/x.wav.import b/sound/dialogue/animalese/alphanumeric/a.wav.import
similarity index 52%
rename from sound/dialogue/animalese/placeholder/x.wav.import
rename to sound/dialogue/animalese/alphanumeric/a.wav.import
index c91ffb4..f058d25 100644
--- a/sound/dialogue/animalese/placeholder/x.wav.import
+++ b/sound/dialogue/animalese/alphanumeric/a.wav.import
@@ -2,13 +2,13 @@
importer="wav"
type="AudioStreamWAV"
-uid="uid://b5al0m4de1m5x"
-path="res://.godot/imported/x.wav-b1beaddcc7d6663882ddd8b86e65db56.sample"
+uid="uid://dnnf2sfntra41"
+path="res://.godot/imported/a.wav-07b66f5c4c337a0412ac22c096725756.sample"
[deps]
-source_file="res://sound/dialogue/animalese/placeholder/x.wav"
-dest_files=["res://.godot/imported/x.wav-b1beaddcc7d6663882ddd8b86e65db56.sample"]
+source_file="res://sound/dialogue/animalese/alphanumeric/a.wav"
+dest_files=["res://.godot/imported/a.wav-07b66f5c4c337a0412ac22c096725756.sample"]
[params]
diff --git a/sound/dialogue/animalese/alphanumeric/b.wav b/sound/dialogue/animalese/alphanumeric/b.wav
new file mode 100644
index 0000000..56c49e2
Binary files /dev/null and b/sound/dialogue/animalese/alphanumeric/b.wav differ
diff --git a/sound/dialogue/animalese/alphanumeric/b.wav.import b/sound/dialogue/animalese/alphanumeric/b.wav.import
new file mode 100644
index 0000000..82c50e4
--- /dev/null
+++ b/sound/dialogue/animalese/alphanumeric/b.wav.import
@@ -0,0 +1,24 @@
+[remap]
+
+importer="wav"
+type="AudioStreamWAV"
+uid="uid://cxueylrkf0358"
+path="res://.godot/imported/b.wav-4eb4b2c71e0d5f50efbe3fc8793801e4.sample"
+
+[deps]
+
+source_file="res://sound/dialogue/animalese/alphanumeric/b.wav"
+dest_files=["res://.godot/imported/b.wav-4eb4b2c71e0d5f50efbe3fc8793801e4.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/alphanumeric/c.wav b/sound/dialogue/animalese/alphanumeric/c.wav
new file mode 100644
index 0000000..6531a4a
Binary files /dev/null and b/sound/dialogue/animalese/alphanumeric/c.wav differ
diff --git a/sound/dialogue/animalese/alphanumeric/c.wav.import b/sound/dialogue/animalese/alphanumeric/c.wav.import
new file mode 100644
index 0000000..5fc9844
--- /dev/null
+++ b/sound/dialogue/animalese/alphanumeric/c.wav.import
@@ -0,0 +1,24 @@
+[remap]
+
+importer="wav"
+type="AudioStreamWAV"
+uid="uid://cbwe5uie8fa6k"
+path="res://.godot/imported/c.wav-86a3dcc44eaabde775a8efc14c630f27.sample"
+
+[deps]
+
+source_file="res://sound/dialogue/animalese/alphanumeric/c.wav"
+dest_files=["res://.godot/imported/c.wav-86a3dcc44eaabde775a8efc14c630f27.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/alphanumeric/d.wav b/sound/dialogue/animalese/alphanumeric/d.wav
new file mode 100644
index 0000000..3838dd7
Binary files /dev/null and b/sound/dialogue/animalese/alphanumeric/d.wav differ
diff --git a/sound/dialogue/animalese/alphanumeric/d.wav.import b/sound/dialogue/animalese/alphanumeric/d.wav.import
new file mode 100644
index 0000000..c58cbf9
--- /dev/null
+++ b/sound/dialogue/animalese/alphanumeric/d.wav.import
@@ -0,0 +1,24 @@
+[remap]
+
+importer="wav"
+type="AudioStreamWAV"
+uid="uid://yyr410g00p5p"
+path="res://.godot/imported/d.wav-5577435bb675e68970dd9de887fff671.sample"
+
+[deps]
+
+source_file="res://sound/dialogue/animalese/alphanumeric/d.wav"
+dest_files=["res://.godot/imported/d.wav-5577435bb675e68970dd9de887fff671.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/alphanumeric/e.wav b/sound/dialogue/animalese/alphanumeric/e.wav
new file mode 100644
index 0000000..8ba7d24
Binary files /dev/null and b/sound/dialogue/animalese/alphanumeric/e.wav differ
diff --git a/sound/dialogue/animalese/alphanumeric/e.wav.import b/sound/dialogue/animalese/alphanumeric/e.wav.import
new file mode 100644
index 0000000..8d384a2
--- /dev/null
+++ b/sound/dialogue/animalese/alphanumeric/e.wav.import
@@ -0,0 +1,24 @@
+[remap]
+
+importer="wav"
+type="AudioStreamWAV"
+uid="uid://cp6stdrpayltv"
+path="res://.godot/imported/e.wav-6373ce27ee05eb89c9d510ca465a24d1.sample"
+
+[deps]
+
+source_file="res://sound/dialogue/animalese/alphanumeric/e.wav"
+dest_files=["res://.godot/imported/e.wav-6373ce27ee05eb89c9d510ca465a24d1.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/alphanumeric/f.wav b/sound/dialogue/animalese/alphanumeric/f.wav
new file mode 100644
index 0000000..0ec0644
Binary files /dev/null and b/sound/dialogue/animalese/alphanumeric/f.wav differ
diff --git a/sound/dialogue/animalese/placeholder/f.wav.import b/sound/dialogue/animalese/alphanumeric/f.wav.import
similarity index 50%
rename from sound/dialogue/animalese/placeholder/f.wav.import
rename to sound/dialogue/animalese/alphanumeric/f.wav.import
index dc85ee3..fdc83d0 100644
--- a/sound/dialogue/animalese/placeholder/f.wav.import
+++ b/sound/dialogue/animalese/alphanumeric/f.wav.import
@@ -2,13 +2,13 @@
importer="wav"
type="AudioStreamWAV"
-uid="uid://c0gv4cphnfcxf"
-path="res://.godot/imported/f.wav-8eb31f2de6240647a0d6d2520aadcd6a.sample"
+uid="uid://bc1lj4kba0fr1"
+path="res://.godot/imported/f.wav-637c7bb130969a55972a1c232962cb40.sample"
[deps]
-source_file="res://sound/dialogue/animalese/placeholder/f.wav"
-dest_files=["res://.godot/imported/f.wav-8eb31f2de6240647a0d6d2520aadcd6a.sample"]
+source_file="res://sound/dialogue/animalese/alphanumeric/f.wav"
+dest_files=["res://.godot/imported/f.wav-637c7bb130969a55972a1c232962cb40.sample"]
[params]
diff --git a/sound/dialogue/animalese/alphanumeric/g.wav b/sound/dialogue/animalese/alphanumeric/g.wav
new file mode 100644
index 0000000..c942d53
Binary files /dev/null and b/sound/dialogue/animalese/alphanumeric/g.wav differ
diff --git a/sound/dialogue/animalese/placeholder/g.wav.import b/sound/dialogue/animalese/alphanumeric/g.wav.import
similarity index 50%
rename from sound/dialogue/animalese/placeholder/g.wav.import
rename to sound/dialogue/animalese/alphanumeric/g.wav.import
index 1709852..f643d8d 100644
--- a/sound/dialogue/animalese/placeholder/g.wav.import
+++ b/sound/dialogue/animalese/alphanumeric/g.wav.import
@@ -2,13 +2,13 @@
importer="wav"
type="AudioStreamWAV"
-uid="uid://c7706p0fc8y7c"
-path="res://.godot/imported/g.wav-45b2cbd9653a31477e3f00c9432d560c.sample"
+uid="uid://6k1d81vrewm7"
+path="res://.godot/imported/g.wav-73da4ecac29d03a5e0c1bb2463bcb6cb.sample"
[deps]
-source_file="res://sound/dialogue/animalese/placeholder/g.wav"
-dest_files=["res://.godot/imported/g.wav-45b2cbd9653a31477e3f00c9432d560c.sample"]
+source_file="res://sound/dialogue/animalese/alphanumeric/g.wav"
+dest_files=["res://.godot/imported/g.wav-73da4ecac29d03a5e0c1bb2463bcb6cb.sample"]
[params]
diff --git a/sound/dialogue/animalese/alphanumeric/h.wav b/sound/dialogue/animalese/alphanumeric/h.wav
new file mode 100644
index 0000000..545915f
Binary files /dev/null and b/sound/dialogue/animalese/alphanumeric/h.wav differ
diff --git a/sound/dialogue/animalese/placeholder/h.wav.import b/sound/dialogue/animalese/alphanumeric/h.wav.import
similarity index 50%
rename from sound/dialogue/animalese/placeholder/h.wav.import
rename to sound/dialogue/animalese/alphanumeric/h.wav.import
index 96b2359..c33b3bf 100644
--- a/sound/dialogue/animalese/placeholder/h.wav.import
+++ b/sound/dialogue/animalese/alphanumeric/h.wav.import
@@ -2,13 +2,13 @@
importer="wav"
type="AudioStreamWAV"
-uid="uid://cl66wka105xmo"
-path="res://.godot/imported/h.wav-2da53a6638eebb75f0867eaf75dc24ba.sample"
+uid="uid://boatg77atys5a"
+path="res://.godot/imported/h.wav-44456bd4f004ee75088b13cb2069e3fb.sample"
[deps]
-source_file="res://sound/dialogue/animalese/placeholder/h.wav"
-dest_files=["res://.godot/imported/h.wav-2da53a6638eebb75f0867eaf75dc24ba.sample"]
+source_file="res://sound/dialogue/animalese/alphanumeric/h.wav"
+dest_files=["res://.godot/imported/h.wav-44456bd4f004ee75088b13cb2069e3fb.sample"]
[params]
diff --git a/sound/dialogue/animalese/alphanumeric/i.wav b/sound/dialogue/animalese/alphanumeric/i.wav
new file mode 100644
index 0000000..f464cbc
Binary files /dev/null and b/sound/dialogue/animalese/alphanumeric/i.wav differ
diff --git a/sound/dialogue/animalese/alphanumeric/i.wav.import b/sound/dialogue/animalese/alphanumeric/i.wav.import
new file mode 100644
index 0000000..51b5c36
--- /dev/null
+++ b/sound/dialogue/animalese/alphanumeric/i.wav.import
@@ -0,0 +1,24 @@
+[remap]
+
+importer="wav"
+type="AudioStreamWAV"
+uid="uid://cvru47ct1d7mj"
+path="res://.godot/imported/i.wav-8ce67b78d17a9ac111433177c3ed850b.sample"
+
+[deps]
+
+source_file="res://sound/dialogue/animalese/alphanumeric/i.wav"
+dest_files=["res://.godot/imported/i.wav-8ce67b78d17a9ac111433177c3ed850b.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/alphanumeric/j.wav b/sound/dialogue/animalese/alphanumeric/j.wav
new file mode 100644
index 0000000..1381026
Binary files /dev/null and b/sound/dialogue/animalese/alphanumeric/j.wav differ
diff --git a/sound/dialogue/animalese/placeholder/i.wav.import b/sound/dialogue/animalese/alphanumeric/j.wav.import
similarity index 52%
rename from sound/dialogue/animalese/placeholder/i.wav.import
rename to sound/dialogue/animalese/alphanumeric/j.wav.import
index 0c11fd6..83202fe 100644
--- a/sound/dialogue/animalese/placeholder/i.wav.import
+++ b/sound/dialogue/animalese/alphanumeric/j.wav.import
@@ -2,13 +2,13 @@
importer="wav"
type="AudioStreamWAV"
-uid="uid://jicgdbnyt6bm"
-path="res://.godot/imported/i.wav-1e8d7f13980a6f2f61d73f65a2efc91c.sample"
+uid="uid://dwi1wq2dyli5y"
+path="res://.godot/imported/j.wav-c07aa6b0f20fa8fcc89a706a3391c11c.sample"
[deps]
-source_file="res://sound/dialogue/animalese/placeholder/i.wav"
-dest_files=["res://.godot/imported/i.wav-1e8d7f13980a6f2f61d73f65a2efc91c.sample"]
+source_file="res://sound/dialogue/animalese/alphanumeric/j.wav"
+dest_files=["res://.godot/imported/j.wav-c07aa6b0f20fa8fcc89a706a3391c11c.sample"]
[params]
diff --git a/sound/dialogue/animalese/alphanumeric/k.wav b/sound/dialogue/animalese/alphanumeric/k.wav
new file mode 100644
index 0000000..3995b27
Binary files /dev/null and b/sound/dialogue/animalese/alphanumeric/k.wav differ
diff --git a/sound/dialogue/animalese/alphanumeric/k.wav.import b/sound/dialogue/animalese/alphanumeric/k.wav.import
new file mode 100644
index 0000000..8cbff42
--- /dev/null
+++ b/sound/dialogue/animalese/alphanumeric/k.wav.import
@@ -0,0 +1,24 @@
+[remap]
+
+importer="wav"
+type="AudioStreamWAV"
+uid="uid://6dkc31pv1ey1"
+path="res://.godot/imported/k.wav-c6fde0aff33080ec99703bdd4fa87e80.sample"
+
+[deps]
+
+source_file="res://sound/dialogue/animalese/alphanumeric/k.wav"
+dest_files=["res://.godot/imported/k.wav-c6fde0aff33080ec99703bdd4fa87e80.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/alphanumeric/l.wav b/sound/dialogue/animalese/alphanumeric/l.wav
new file mode 100644
index 0000000..e0f7ae7
Binary files /dev/null and b/sound/dialogue/animalese/alphanumeric/l.wav differ
diff --git a/sound/dialogue/animalese/alphanumeric/l.wav.import b/sound/dialogue/animalese/alphanumeric/l.wav.import
new file mode 100644
index 0000000..8c05113
--- /dev/null
+++ b/sound/dialogue/animalese/alphanumeric/l.wav.import
@@ -0,0 +1,24 @@
+[remap]
+
+importer="wav"
+type="AudioStreamWAV"
+uid="uid://d2o1hk6e4bbss"
+path="res://.godot/imported/l.wav-722141f795e13543172ffd960308b24c.sample"
+
+[deps]
+
+source_file="res://sound/dialogue/animalese/alphanumeric/l.wav"
+dest_files=["res://.godot/imported/l.wav-722141f795e13543172ffd960308b24c.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/alphanumeric/m.wav b/sound/dialogue/animalese/alphanumeric/m.wav
new file mode 100644
index 0000000..6ea69d4
Binary files /dev/null and b/sound/dialogue/animalese/alphanumeric/m.wav differ
diff --git a/sound/dialogue/animalese/placeholder/m.wav.import b/sound/dialogue/animalese/alphanumeric/m.wav.import
similarity index 50%
rename from sound/dialogue/animalese/placeholder/m.wav.import
rename to sound/dialogue/animalese/alphanumeric/m.wav.import
index 2a2439b..c5f005b 100644
--- a/sound/dialogue/animalese/placeholder/m.wav.import
+++ b/sound/dialogue/animalese/alphanumeric/m.wav.import
@@ -2,13 +2,13 @@
importer="wav"
type="AudioStreamWAV"
-uid="uid://dan4o17oreugv"
-path="res://.godot/imported/m.wav-aa394c4b1d5309d859ce73a368754264.sample"
+uid="uid://c588ew7t8dwse"
+path="res://.godot/imported/m.wav-2ad62bfb1ad90a0667f708c5154de5dc.sample"
[deps]
-source_file="res://sound/dialogue/animalese/placeholder/m.wav"
-dest_files=["res://.godot/imported/m.wav-aa394c4b1d5309d859ce73a368754264.sample"]
+source_file="res://sound/dialogue/animalese/alphanumeric/m.wav"
+dest_files=["res://.godot/imported/m.wav-2ad62bfb1ad90a0667f708c5154de5dc.sample"]
[params]
diff --git a/sound/dialogue/animalese/alphanumeric/n.wav b/sound/dialogue/animalese/alphanumeric/n.wav
new file mode 100644
index 0000000..636914e
Binary files /dev/null and b/sound/dialogue/animalese/alphanumeric/n.wav differ
diff --git a/sound/dialogue/animalese/placeholder/n.wav.import b/sound/dialogue/animalese/alphanumeric/n.wav.import
similarity index 50%
rename from sound/dialogue/animalese/placeholder/n.wav.import
rename to sound/dialogue/animalese/alphanumeric/n.wav.import
index f5e432c..455c90b 100644
--- a/sound/dialogue/animalese/placeholder/n.wav.import
+++ b/sound/dialogue/animalese/alphanumeric/n.wav.import
@@ -2,13 +2,13 @@
importer="wav"
type="AudioStreamWAV"
-uid="uid://dxup6pw80km61"
-path="res://.godot/imported/n.wav-84e9a6fa6664222868d647187a09f374.sample"
+uid="uid://mc1vnfuhpuwr"
+path="res://.godot/imported/n.wav-b995292a9ce9755047a5a6a6166a4a4c.sample"
[deps]
-source_file="res://sound/dialogue/animalese/placeholder/n.wav"
-dest_files=["res://.godot/imported/n.wav-84e9a6fa6664222868d647187a09f374.sample"]
+source_file="res://sound/dialogue/animalese/alphanumeric/n.wav"
+dest_files=["res://.godot/imported/n.wav-b995292a9ce9755047a5a6a6166a4a4c.sample"]
[params]
diff --git a/sound/dialogue/animalese/alphanumeric/o.wav b/sound/dialogue/animalese/alphanumeric/o.wav
new file mode 100644
index 0000000..0161399
Binary files /dev/null and b/sound/dialogue/animalese/alphanumeric/o.wav differ
diff --git a/sound/dialogue/animalese/alphanumeric/o.wav.import b/sound/dialogue/animalese/alphanumeric/o.wav.import
new file mode 100644
index 0000000..655e8fe
--- /dev/null
+++ b/sound/dialogue/animalese/alphanumeric/o.wav.import
@@ -0,0 +1,24 @@
+[remap]
+
+importer="wav"
+type="AudioStreamWAV"
+uid="uid://rkmixmwg8vgp"
+path="res://.godot/imported/o.wav-696f77252311afda46e416ece590eb55.sample"
+
+[deps]
+
+source_file="res://sound/dialogue/animalese/alphanumeric/o.wav"
+dest_files=["res://.godot/imported/o.wav-696f77252311afda46e416ece590eb55.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/alphanumeric/p.wav b/sound/dialogue/animalese/alphanumeric/p.wav
new file mode 100644
index 0000000..99bf4fd
Binary files /dev/null and b/sound/dialogue/animalese/alphanumeric/p.wav differ
diff --git a/sound/dialogue/animalese/placeholder/p.wav.import b/sound/dialogue/animalese/alphanumeric/p.wav.import
similarity index 50%
rename from sound/dialogue/animalese/placeholder/p.wav.import
rename to sound/dialogue/animalese/alphanumeric/p.wav.import
index a431c1f..96ae91b 100644
--- a/sound/dialogue/animalese/placeholder/p.wav.import
+++ b/sound/dialogue/animalese/alphanumeric/p.wav.import
@@ -2,13 +2,13 @@
importer="wav"
type="AudioStreamWAV"
-uid="uid://crubsdrd220eh"
-path="res://.godot/imported/p.wav-c32b759c822f258d8d947827adf8127a.sample"
+uid="uid://blbcb70gci0q2"
+path="res://.godot/imported/p.wav-bc58581e4a5d8761e1c8b91f069f951d.sample"
[deps]
-source_file="res://sound/dialogue/animalese/placeholder/p.wav"
-dest_files=["res://.godot/imported/p.wav-c32b759c822f258d8d947827adf8127a.sample"]
+source_file="res://sound/dialogue/animalese/alphanumeric/p.wav"
+dest_files=["res://.godot/imported/p.wav-bc58581e4a5d8761e1c8b91f069f951d.sample"]
[params]
diff --git a/sound/dialogue/animalese/alphanumeric/q.wav b/sound/dialogue/animalese/alphanumeric/q.wav
new file mode 100644
index 0000000..80562cc
Binary files /dev/null and b/sound/dialogue/animalese/alphanumeric/q.wav differ
diff --git a/sound/dialogue/animalese/placeholder/q.wav.import b/sound/dialogue/animalese/alphanumeric/q.wav.import
similarity index 50%
rename from sound/dialogue/animalese/placeholder/q.wav.import
rename to sound/dialogue/animalese/alphanumeric/q.wav.import
index 574c906..896c936 100644
--- a/sound/dialogue/animalese/placeholder/q.wav.import
+++ b/sound/dialogue/animalese/alphanumeric/q.wav.import
@@ -2,13 +2,13 @@
importer="wav"
type="AudioStreamWAV"
-uid="uid://ct0decr76agoq"
-path="res://.godot/imported/q.wav-d494b927c6f2b8bfbaa44d1f81887731.sample"
+uid="uid://bmu30s0vscbvg"
+path="res://.godot/imported/q.wav-b9d907f73d4a59dd6641ffe35c36aaac.sample"
[deps]
-source_file="res://sound/dialogue/animalese/placeholder/q.wav"
-dest_files=["res://.godot/imported/q.wav-d494b927c6f2b8bfbaa44d1f81887731.sample"]
+source_file="res://sound/dialogue/animalese/alphanumeric/q.wav"
+dest_files=["res://.godot/imported/q.wav-b9d907f73d4a59dd6641ffe35c36aaac.sample"]
[params]
diff --git a/sound/dialogue/animalese/alphanumeric/r.wav b/sound/dialogue/animalese/alphanumeric/r.wav
new file mode 100644
index 0000000..b38b3a8
Binary files /dev/null and b/sound/dialogue/animalese/alphanumeric/r.wav differ
diff --git a/sound/dialogue/animalese/placeholder/r.wav.import b/sound/dialogue/animalese/alphanumeric/r.wav.import
similarity index 50%
rename from sound/dialogue/animalese/placeholder/r.wav.import
rename to sound/dialogue/animalese/alphanumeric/r.wav.import
index 1f196b1..3007c69 100644
--- a/sound/dialogue/animalese/placeholder/r.wav.import
+++ b/sound/dialogue/animalese/alphanumeric/r.wav.import
@@ -2,13 +2,13 @@
importer="wav"
type="AudioStreamWAV"
-uid="uid://dpq7rin4pr1n3"
-path="res://.godot/imported/r.wav-d1d419e3246c0e918ff51a9dd662a742.sample"
+uid="uid://bgdec44ic6msd"
+path="res://.godot/imported/r.wav-d5ea52bfff9160d4d25d74dd94072208.sample"
[deps]
-source_file="res://sound/dialogue/animalese/placeholder/r.wav"
-dest_files=["res://.godot/imported/r.wav-d1d419e3246c0e918ff51a9dd662a742.sample"]
+source_file="res://sound/dialogue/animalese/alphanumeric/r.wav"
+dest_files=["res://.godot/imported/r.wav-d5ea52bfff9160d4d25d74dd94072208.sample"]
[params]
diff --git a/sound/dialogue/animalese/alphanumeric/s.wav b/sound/dialogue/animalese/alphanumeric/s.wav
new file mode 100644
index 0000000..8ba7d24
Binary files /dev/null and b/sound/dialogue/animalese/alphanumeric/s.wav differ
diff --git a/sound/dialogue/animalese/alphanumeric/s.wav.import b/sound/dialogue/animalese/alphanumeric/s.wav.import
new file mode 100644
index 0000000..c48dad9
--- /dev/null
+++ b/sound/dialogue/animalese/alphanumeric/s.wav.import
@@ -0,0 +1,24 @@
+[remap]
+
+importer="wav"
+type="AudioStreamWAV"
+uid="uid://draie1dh0muw"
+path="res://.godot/imported/s.wav-0dfb29a8b9a3bf4890d24e227cd0d0c4.sample"
+
+[deps]
+
+source_file="res://sound/dialogue/animalese/alphanumeric/s.wav"
+dest_files=["res://.godot/imported/s.wav-0dfb29a8b9a3bf4890d24e227cd0d0c4.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/alphanumeric/t.wav b/sound/dialogue/animalese/alphanumeric/t.wav
new file mode 100644
index 0000000..f34aa5d
Binary files /dev/null and b/sound/dialogue/animalese/alphanumeric/t.wav differ
diff --git a/sound/dialogue/animalese/alphanumeric/t.wav.import b/sound/dialogue/animalese/alphanumeric/t.wav.import
new file mode 100644
index 0000000..cd55195
--- /dev/null
+++ b/sound/dialogue/animalese/alphanumeric/t.wav.import
@@ -0,0 +1,24 @@
+[remap]
+
+importer="wav"
+type="AudioStreamWAV"
+uid="uid://ybmvcpv7prx"
+path="res://.godot/imported/t.wav-d4e7e7dbe4c04e54e9c7edb0d85af81a.sample"
+
+[deps]
+
+source_file="res://sound/dialogue/animalese/alphanumeric/t.wav"
+dest_files=["res://.godot/imported/t.wav-d4e7e7dbe4c04e54e9c7edb0d85af81a.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/alphanumeric/u.wav b/sound/dialogue/animalese/alphanumeric/u.wav
new file mode 100644
index 0000000..c41b3fc
Binary files /dev/null and b/sound/dialogue/animalese/alphanumeric/u.wav differ
diff --git a/sound/dialogue/animalese/placeholder/t.wav.import b/sound/dialogue/animalese/alphanumeric/u.wav.import
similarity index 52%
rename from sound/dialogue/animalese/placeholder/t.wav.import
rename to sound/dialogue/animalese/alphanumeric/u.wav.import
index ce35ba2..27b2e8b 100644
--- a/sound/dialogue/animalese/placeholder/t.wav.import
+++ b/sound/dialogue/animalese/alphanumeric/u.wav.import
@@ -2,13 +2,13 @@
importer="wav"
type="AudioStreamWAV"
-uid="uid://dr0dp5v8fdb8r"
-path="res://.godot/imported/t.wav-3a26c5be0f8e3007adf1daa05a6a1ea2.sample"
+uid="uid://dn0hxgm1ueiv7"
+path="res://.godot/imported/u.wav-b8a534783b71ec701bb0925308d513a2.sample"
[deps]
-source_file="res://sound/dialogue/animalese/placeholder/t.wav"
-dest_files=["res://.godot/imported/t.wav-3a26c5be0f8e3007adf1daa05a6a1ea2.sample"]
+source_file="res://sound/dialogue/animalese/alphanumeric/u.wav"
+dest_files=["res://.godot/imported/u.wav-b8a534783b71ec701bb0925308d513a2.sample"]
[params]
diff --git a/sound/dialogue/animalese/alphanumeric/v.wav b/sound/dialogue/animalese/alphanumeric/v.wav
new file mode 100644
index 0000000..78b81f9
Binary files /dev/null and b/sound/dialogue/animalese/alphanumeric/v.wav differ
diff --git a/sound/dialogue/animalese/alphanumeric/v.wav.import b/sound/dialogue/animalese/alphanumeric/v.wav.import
new file mode 100644
index 0000000..294b852
--- /dev/null
+++ b/sound/dialogue/animalese/alphanumeric/v.wav.import
@@ -0,0 +1,24 @@
+[remap]
+
+importer="wav"
+type="AudioStreamWAV"
+uid="uid://c5p70bkanhlua"
+path="res://.godot/imported/v.wav-0dc1396f424599662d6812e2e59a3b71.sample"
+
+[deps]
+
+source_file="res://sound/dialogue/animalese/alphanumeric/v.wav"
+dest_files=["res://.godot/imported/v.wav-0dc1396f424599662d6812e2e59a3b71.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/alphanumeric/w.wav b/sound/dialogue/animalese/alphanumeric/w.wav
new file mode 100644
index 0000000..26cbdc9
Binary files /dev/null and b/sound/dialogue/animalese/alphanumeric/w.wav differ
diff --git a/sound/dialogue/animalese/alphanumeric/w.wav.import b/sound/dialogue/animalese/alphanumeric/w.wav.import
new file mode 100644
index 0000000..5c13da4
--- /dev/null
+++ b/sound/dialogue/animalese/alphanumeric/w.wav.import
@@ -0,0 +1,24 @@
+[remap]
+
+importer="wav"
+type="AudioStreamWAV"
+uid="uid://dr2025tcuq5lq"
+path="res://.godot/imported/w.wav-caf9c9b3fa280c326a6be0c426672562.sample"
+
+[deps]
+
+source_file="res://sound/dialogue/animalese/alphanumeric/w.wav"
+dest_files=["res://.godot/imported/w.wav-caf9c9b3fa280c326a6be0c426672562.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/alphanumeric/x.wav b/sound/dialogue/animalese/alphanumeric/x.wav
new file mode 100644
index 0000000..fa020f7
Binary files /dev/null and b/sound/dialogue/animalese/alphanumeric/x.wav differ
diff --git a/sound/dialogue/animalese/alphanumeric/x.wav.import b/sound/dialogue/animalese/alphanumeric/x.wav.import
new file mode 100644
index 0000000..95a6a8e
--- /dev/null
+++ b/sound/dialogue/animalese/alphanumeric/x.wav.import
@@ -0,0 +1,24 @@
+[remap]
+
+importer="wav"
+type="AudioStreamWAV"
+uid="uid://c2c6lwmfogv1o"
+path="res://.godot/imported/x.wav-6dec8d5d6aa916173970737d9db8f509.sample"
+
+[deps]
+
+source_file="res://sound/dialogue/animalese/alphanumeric/x.wav"
+dest_files=["res://.godot/imported/x.wav-6dec8d5d6aa916173970737d9db8f509.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/alphanumeric/y.wav b/sound/dialogue/animalese/alphanumeric/y.wav
new file mode 100644
index 0000000..3acdc45
Binary files /dev/null and b/sound/dialogue/animalese/alphanumeric/y.wav differ
diff --git a/sound/dialogue/animalese/placeholder/y.wav.import b/sound/dialogue/animalese/alphanumeric/y.wav.import
similarity index 50%
rename from sound/dialogue/animalese/placeholder/y.wav.import
rename to sound/dialogue/animalese/alphanumeric/y.wav.import
index 2c7d8d3..72ebe0c 100644
--- a/sound/dialogue/animalese/placeholder/y.wav.import
+++ b/sound/dialogue/animalese/alphanumeric/y.wav.import
@@ -2,13 +2,13 @@
importer="wav"
type="AudioStreamWAV"
-uid="uid://c6hust77yamoe"
-path="res://.godot/imported/y.wav-5ef178912b2921bb7c790b12e2e82337.sample"
+uid="uid://csjurthefub6u"
+path="res://.godot/imported/y.wav-82e4c89a86bc31782eaca5f5ee737bb1.sample"
[deps]
-source_file="res://sound/dialogue/animalese/placeholder/y.wav"
-dest_files=["res://.godot/imported/y.wav-5ef178912b2921bb7c790b12e2e82337.sample"]
+source_file="res://sound/dialogue/animalese/alphanumeric/y.wav"
+dest_files=["res://.godot/imported/y.wav-82e4c89a86bc31782eaca5f5ee737bb1.sample"]
[params]
diff --git a/sound/dialogue/animalese/alphanumeric/z.wav b/sound/dialogue/animalese/alphanumeric/z.wav
new file mode 100644
index 0000000..779e725
Binary files /dev/null and b/sound/dialogue/animalese/alphanumeric/z.wav differ
diff --git a/sound/dialogue/animalese/alphanumeric/z.wav.import b/sound/dialogue/animalese/alphanumeric/z.wav.import
new file mode 100644
index 0000000..b089115
--- /dev/null
+++ b/sound/dialogue/animalese/alphanumeric/z.wav.import
@@ -0,0 +1,24 @@
+[remap]
+
+importer="wav"
+type="AudioStreamWAV"
+uid="uid://bujyv5lsxarhr"
+path="res://.godot/imported/z.wav-38cc5c83dca1b80b731d307651219c08.sample"
+
+[deps]
+
+source_file="res://sound/dialogue/animalese/alphanumeric/z.wav"
+dest_files=["res://.godot/imported/z.wav-38cc5c83dca1b80b731d307651219c08.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/kim/0.wav b/sound/dialogue/animalese/kim/0.wav
new file mode 100644
index 0000000..ed9af43
Binary files /dev/null and b/sound/dialogue/animalese/kim/0.wav differ
diff --git a/sound/dialogue/animalese/kim/0.wav.import b/sound/dialogue/animalese/kim/0.wav.import
new file mode 100644
index 0000000..cc27672
--- /dev/null
+++ b/sound/dialogue/animalese/kim/0.wav.import
@@ -0,0 +1,24 @@
+[remap]
+
+importer="wav"
+type="AudioStreamWAV"
+uid="uid://w8wvtbsovk1y"
+path="res://.godot/imported/0.wav-bbd392e8755139dca852d942c97afaa6.sample"
+
+[deps]
+
+source_file="res://sound/dialogue/animalese/kim/0.wav"
+dest_files=["res://.godot/imported/0.wav-bbd392e8755139dca852d942c97afaa6.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/kim/1.wav b/sound/dialogue/animalese/kim/1.wav
new file mode 100644
index 0000000..102b4f8
Binary files /dev/null and b/sound/dialogue/animalese/kim/1.wav differ
diff --git a/sound/dialogue/animalese/placeholder/s.wav.import b/sound/dialogue/animalese/kim/1.wav.import
similarity index 52%
rename from sound/dialogue/animalese/placeholder/s.wav.import
rename to sound/dialogue/animalese/kim/1.wav.import
index a794fdf..0169929 100644
--- a/sound/dialogue/animalese/placeholder/s.wav.import
+++ b/sound/dialogue/animalese/kim/1.wav.import
@@ -2,13 +2,13 @@
importer="wav"
type="AudioStreamWAV"
-uid="uid://c00meumj2blfa"
-path="res://.godot/imported/s.wav-15abb20075aacc8d5fe95cd206ae25ce.sample"
+uid="uid://cnny27n4wbk6s"
+path="res://.godot/imported/1.wav-5303efd25be6d603f73c2c2d77759fce.sample"
[deps]
-source_file="res://sound/dialogue/animalese/placeholder/s.wav"
-dest_files=["res://.godot/imported/s.wav-15abb20075aacc8d5fe95cd206ae25ce.sample"]
+source_file="res://sound/dialogue/animalese/kim/1.wav"
+dest_files=["res://.godot/imported/1.wav-5303efd25be6d603f73c2c2d77759fce.sample"]
[params]
diff --git a/sound/dialogue/animalese/kim/2.wav b/sound/dialogue/animalese/kim/2.wav
new file mode 100644
index 0000000..5559b55
Binary files /dev/null and b/sound/dialogue/animalese/kim/2.wav differ
diff --git a/sound/dialogue/animalese/kim/2.wav.import b/sound/dialogue/animalese/kim/2.wav.import
new file mode 100644
index 0000000..26bb35a
--- /dev/null
+++ b/sound/dialogue/animalese/kim/2.wav.import
@@ -0,0 +1,24 @@
+[remap]
+
+importer="wav"
+type="AudioStreamWAV"
+uid="uid://daw5c2h7p5yu6"
+path="res://.godot/imported/2.wav-a384b25ebb56567742354475fa551c97.sample"
+
+[deps]
+
+source_file="res://sound/dialogue/animalese/kim/2.wav"
+dest_files=["res://.godot/imported/2.wav-a384b25ebb56567742354475fa551c97.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/kim/3.wav b/sound/dialogue/animalese/kim/3.wav
new file mode 100644
index 0000000..158485a
Binary files /dev/null and b/sound/dialogue/animalese/kim/3.wav differ
diff --git a/sound/dialogue/animalese/kim/3.wav.import b/sound/dialogue/animalese/kim/3.wav.import
new file mode 100644
index 0000000..c4f479d
--- /dev/null
+++ b/sound/dialogue/animalese/kim/3.wav.import
@@ -0,0 +1,24 @@
+[remap]
+
+importer="wav"
+type="AudioStreamWAV"
+uid="uid://dn24y7on5811q"
+path="res://.godot/imported/3.wav-da7f408c6ee782136a7225f6a5b4c2da.sample"
+
+[deps]
+
+source_file="res://sound/dialogue/animalese/kim/3.wav"
+dest_files=["res://.godot/imported/3.wav-da7f408c6ee782136a7225f6a5b4c2da.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/kim/4.wav b/sound/dialogue/animalese/kim/4.wav
new file mode 100644
index 0000000..6d05ec7
Binary files /dev/null and b/sound/dialogue/animalese/kim/4.wav differ
diff --git a/sound/dialogue/animalese/kim/4.wav.import b/sound/dialogue/animalese/kim/4.wav.import
new file mode 100644
index 0000000..c6a70b3
--- /dev/null
+++ b/sound/dialogue/animalese/kim/4.wav.import
@@ -0,0 +1,24 @@
+[remap]
+
+importer="wav"
+type="AudioStreamWAV"
+uid="uid://cpbt1fduqk2bc"
+path="res://.godot/imported/4.wav-710dd9e6b25e0147dec2f21cfae1a894.sample"
+
+[deps]
+
+source_file="res://sound/dialogue/animalese/kim/4.wav"
+dest_files=["res://.godot/imported/4.wav-710dd9e6b25e0147dec2f21cfae1a894.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/kim/5.wav b/sound/dialogue/animalese/kim/5.wav
new file mode 100644
index 0000000..5671a38
Binary files /dev/null and b/sound/dialogue/animalese/kim/5.wav differ
diff --git a/sound/dialogue/animalese/kim/5.wav.import b/sound/dialogue/animalese/kim/5.wav.import
new file mode 100644
index 0000000..bc739df
--- /dev/null
+++ b/sound/dialogue/animalese/kim/5.wav.import
@@ -0,0 +1,24 @@
+[remap]
+
+importer="wav"
+type="AudioStreamWAV"
+uid="uid://chf4scjvimr1o"
+path="res://.godot/imported/5.wav-7adb723973f3005425a71be181ad28e9.sample"
+
+[deps]
+
+source_file="res://sound/dialogue/animalese/kim/5.wav"
+dest_files=["res://.godot/imported/5.wav-7adb723973f3005425a71be181ad28e9.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/kim/6.wav b/sound/dialogue/animalese/kim/6.wav
new file mode 100644
index 0000000..379199e
Binary files /dev/null and b/sound/dialogue/animalese/kim/6.wav differ
diff --git a/sound/dialogue/animalese/kim/6.wav.import b/sound/dialogue/animalese/kim/6.wav.import
new file mode 100644
index 0000000..3674eb9
--- /dev/null
+++ b/sound/dialogue/animalese/kim/6.wav.import
@@ -0,0 +1,24 @@
+[remap]
+
+importer="wav"
+type="AudioStreamWAV"
+uid="uid://phxil12i1qmt"
+path="res://.godot/imported/6.wav-1303e051aae59fdf169def7624379571.sample"
+
+[deps]
+
+source_file="res://sound/dialogue/animalese/kim/6.wav"
+dest_files=["res://.godot/imported/6.wav-1303e051aae59fdf169def7624379571.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/kim/7.wav b/sound/dialogue/animalese/kim/7.wav
new file mode 100644
index 0000000..ccfdd5f
Binary files /dev/null and b/sound/dialogue/animalese/kim/7.wav differ
diff --git a/sound/dialogue/animalese/kim/7.wav.import b/sound/dialogue/animalese/kim/7.wav.import
new file mode 100644
index 0000000..a6cee56
--- /dev/null
+++ b/sound/dialogue/animalese/kim/7.wav.import
@@ -0,0 +1,24 @@
+[remap]
+
+importer="wav"
+type="AudioStreamWAV"
+uid="uid://dn1pxla32ab23"
+path="res://.godot/imported/7.wav-cb4bd630248ebe745a1646dc7060d526.sample"
+
+[deps]
+
+source_file="res://sound/dialogue/animalese/kim/7.wav"
+dest_files=["res://.godot/imported/7.wav-cb4bd630248ebe745a1646dc7060d526.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/kim/8.wav b/sound/dialogue/animalese/kim/8.wav
new file mode 100644
index 0000000..d21e79c
Binary files /dev/null and b/sound/dialogue/animalese/kim/8.wav differ
diff --git a/sound/dialogue/animalese/kim/8.wav.import b/sound/dialogue/animalese/kim/8.wav.import
new file mode 100644
index 0000000..36a3e42
--- /dev/null
+++ b/sound/dialogue/animalese/kim/8.wav.import
@@ -0,0 +1,24 @@
+[remap]
+
+importer="wav"
+type="AudioStreamWAV"
+uid="uid://dw3bqqcoeoelf"
+path="res://.godot/imported/8.wav-5f912e71babee0f928483bcc9e2fa9f3.sample"
+
+[deps]
+
+source_file="res://sound/dialogue/animalese/kim/8.wav"
+dest_files=["res://.godot/imported/8.wav-5f912e71babee0f928483bcc9e2fa9f3.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/kim/9.wav b/sound/dialogue/animalese/kim/9.wav
new file mode 100644
index 0000000..70d4515
Binary files /dev/null and b/sound/dialogue/animalese/kim/9.wav differ
diff --git a/sound/dialogue/animalese/kim/9.wav.import b/sound/dialogue/animalese/kim/9.wav.import
new file mode 100644
index 0000000..8739b30
--- /dev/null
+++ b/sound/dialogue/animalese/kim/9.wav.import
@@ -0,0 +1,24 @@
+[remap]
+
+importer="wav"
+type="AudioStreamWAV"
+uid="uid://b3xwpno2ky226"
+path="res://.godot/imported/9.wav-92fe849d00f43baa22f6338601438d71.sample"
+
+[deps]
+
+source_file="res://sound/dialogue/animalese/kim/9.wav"
+dest_files=["res://.godot/imported/9.wav-92fe849d00f43baa22f6338601438d71.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/kim/a.wav b/sound/dialogue/animalese/kim/a.wav
new file mode 100644
index 0000000..78d2ae6
Binary files /dev/null and b/sound/dialogue/animalese/kim/a.wav differ
diff --git a/sound/dialogue/animalese/kim/a.wav.import b/sound/dialogue/animalese/kim/a.wav.import
new file mode 100644
index 0000000..c0c8c4b
--- /dev/null
+++ b/sound/dialogue/animalese/kim/a.wav.import
@@ -0,0 +1,24 @@
+[remap]
+
+importer="wav"
+type="AudioStreamWAV"
+uid="uid://c72cosdd78vhw"
+path="res://.godot/imported/a.wav-19230b3cafcf4c28b5dd1f5c6133e5a5.sample"
+
+[deps]
+
+source_file="res://sound/dialogue/animalese/kim/a.wav"
+dest_files=["res://.godot/imported/a.wav-19230b3cafcf4c28b5dd1f5c6133e5a5.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/kim/b.wav b/sound/dialogue/animalese/kim/b.wav
new file mode 100644
index 0000000..440592d
Binary files /dev/null and b/sound/dialogue/animalese/kim/b.wav differ
diff --git a/sound/dialogue/animalese/kim/b.wav.import b/sound/dialogue/animalese/kim/b.wav.import
new file mode 100644
index 0000000..495146b
--- /dev/null
+++ b/sound/dialogue/animalese/kim/b.wav.import
@@ -0,0 +1,24 @@
+[remap]
+
+importer="wav"
+type="AudioStreamWAV"
+uid="uid://caejv7ic8kc83"
+path="res://.godot/imported/b.wav-185deb7af67f5a2c71a0fcd8ad5c9ee8.sample"
+
+[deps]
+
+source_file="res://sound/dialogue/animalese/kim/b.wav"
+dest_files=["res://.godot/imported/b.wav-185deb7af67f5a2c71a0fcd8ad5c9ee8.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/kim/c.wav b/sound/dialogue/animalese/kim/c.wav
new file mode 100644
index 0000000..d7f2b03
Binary files /dev/null and b/sound/dialogue/animalese/kim/c.wav differ
diff --git a/sound/dialogue/animalese/kim/c.wav.import b/sound/dialogue/animalese/kim/c.wav.import
new file mode 100644
index 0000000..10afb6b
--- /dev/null
+++ b/sound/dialogue/animalese/kim/c.wav.import
@@ -0,0 +1,24 @@
+[remap]
+
+importer="wav"
+type="AudioStreamWAV"
+uid="uid://cpshexs36p2g0"
+path="res://.godot/imported/c.wav-bd1cbbf5862edfbaca6f9f8b0bb2990b.sample"
+
+[deps]
+
+source_file="res://sound/dialogue/animalese/kim/c.wav"
+dest_files=["res://.godot/imported/c.wav-bd1cbbf5862edfbaca6f9f8b0bb2990b.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/kim/d.wav b/sound/dialogue/animalese/kim/d.wav
new file mode 100644
index 0000000..51b694b
Binary files /dev/null and b/sound/dialogue/animalese/kim/d.wav differ
diff --git a/sound/dialogue/animalese/placeholder/d.wav.import b/sound/dialogue/animalese/kim/d.wav.import
similarity index 50%
rename from sound/dialogue/animalese/placeholder/d.wav.import
rename to sound/dialogue/animalese/kim/d.wav.import
index ffc5bb9..6654420 100644
--- a/sound/dialogue/animalese/placeholder/d.wav.import
+++ b/sound/dialogue/animalese/kim/d.wav.import
@@ -2,13 +2,13 @@
importer="wav"
type="AudioStreamWAV"
-uid="uid://yvha8eukw6l7"
-path="res://.godot/imported/d.wav-910bc02bc42bdb1b37561959a317c28a.sample"
+uid="uid://bqhge2ptlm71d"
+path="res://.godot/imported/d.wav-dd6ab85372a6bd92c069003d8685a7bf.sample"
[deps]
-source_file="res://sound/dialogue/animalese/placeholder/d.wav"
-dest_files=["res://.godot/imported/d.wav-910bc02bc42bdb1b37561959a317c28a.sample"]
+source_file="res://sound/dialogue/animalese/kim/d.wav"
+dest_files=["res://.godot/imported/d.wav-dd6ab85372a6bd92c069003d8685a7bf.sample"]
[params]
diff --git a/sound/dialogue/animalese/kim/e.wav b/sound/dialogue/animalese/kim/e.wav
new file mode 100644
index 0000000..cb2c1c8
Binary files /dev/null and b/sound/dialogue/animalese/kim/e.wav differ
diff --git a/sound/dialogue/animalese/placeholder/e.wav.import b/sound/dialogue/animalese/kim/e.wav.import
similarity index 50%
rename from sound/dialogue/animalese/placeholder/e.wav.import
rename to sound/dialogue/animalese/kim/e.wav.import
index 49de309..8a998e2 100644
--- a/sound/dialogue/animalese/placeholder/e.wav.import
+++ b/sound/dialogue/animalese/kim/e.wav.import
@@ -2,13 +2,13 @@
importer="wav"
type="AudioStreamWAV"
-uid="uid://8ujopf8aqs83"
-path="res://.godot/imported/e.wav-7da5f8fcd017bf931de2bd49dc00f652.sample"
+uid="uid://crfyuh2rii14l"
+path="res://.godot/imported/e.wav-08036c9672bbd5dd6268ec8812ae775f.sample"
[deps]
-source_file="res://sound/dialogue/animalese/placeholder/e.wav"
-dest_files=["res://.godot/imported/e.wav-7da5f8fcd017bf931de2bd49dc00f652.sample"]
+source_file="res://sound/dialogue/animalese/kim/e.wav"
+dest_files=["res://.godot/imported/e.wav-08036c9672bbd5dd6268ec8812ae775f.sample"]
[params]
diff --git a/sound/dialogue/animalese/kim/f.wav b/sound/dialogue/animalese/kim/f.wav
new file mode 100644
index 0000000..b1545ff
Binary files /dev/null and b/sound/dialogue/animalese/kim/f.wav differ
diff --git a/sound/dialogue/animalese/placeholder/b.wav.import b/sound/dialogue/animalese/kim/f.wav.import
similarity index 52%
rename from sound/dialogue/animalese/placeholder/b.wav.import
rename to sound/dialogue/animalese/kim/f.wav.import
index 4aea49c..03c1a4d 100644
--- a/sound/dialogue/animalese/placeholder/b.wav.import
+++ b/sound/dialogue/animalese/kim/f.wav.import
@@ -2,13 +2,13 @@
importer="wav"
type="AudioStreamWAV"
-uid="uid://cs30tdyuo6wtc"
-path="res://.godot/imported/b.wav-eed860af2033bda258bd172899061b45.sample"
+uid="uid://uk0jqhph802c"
+path="res://.godot/imported/f.wav-82aa6f7c781c80928450893a029aac45.sample"
[deps]
-source_file="res://sound/dialogue/animalese/placeholder/b.wav"
-dest_files=["res://.godot/imported/b.wav-eed860af2033bda258bd172899061b45.sample"]
+source_file="res://sound/dialogue/animalese/kim/f.wav"
+dest_files=["res://.godot/imported/f.wav-82aa6f7c781c80928450893a029aac45.sample"]
[params]
diff --git a/sound/dialogue/animalese/kim/g.wav b/sound/dialogue/animalese/kim/g.wav
new file mode 100644
index 0000000..a37ac01
Binary files /dev/null and b/sound/dialogue/animalese/kim/g.wav differ
diff --git a/sound/dialogue/animalese/kim/g.wav.import b/sound/dialogue/animalese/kim/g.wav.import
new file mode 100644
index 0000000..a9b73ba
--- /dev/null
+++ b/sound/dialogue/animalese/kim/g.wav.import
@@ -0,0 +1,24 @@
+[remap]
+
+importer="wav"
+type="AudioStreamWAV"
+uid="uid://1008bpcdr223"
+path="res://.godot/imported/g.wav-372aa2fc0531a484e3322ee3d3b48085.sample"
+
+[deps]
+
+source_file="res://sound/dialogue/animalese/kim/g.wav"
+dest_files=["res://.godot/imported/g.wav-372aa2fc0531a484e3322ee3d3b48085.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/kim/h.wav b/sound/dialogue/animalese/kim/h.wav
new file mode 100644
index 0000000..0c19076
Binary files /dev/null and b/sound/dialogue/animalese/kim/h.wav differ
diff --git a/sound/dialogue/animalese/kim/h.wav.import b/sound/dialogue/animalese/kim/h.wav.import
new file mode 100644
index 0000000..fb6ec91
--- /dev/null
+++ b/sound/dialogue/animalese/kim/h.wav.import
@@ -0,0 +1,24 @@
+[remap]
+
+importer="wav"
+type="AudioStreamWAV"
+uid="uid://bw0ua5pignh1m"
+path="res://.godot/imported/h.wav-f16878738745ac8ee0150078290ea7c5.sample"
+
+[deps]
+
+source_file="res://sound/dialogue/animalese/kim/h.wav"
+dest_files=["res://.godot/imported/h.wav-f16878738745ac8ee0150078290ea7c5.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/kim/i.wav b/sound/dialogue/animalese/kim/i.wav
new file mode 100644
index 0000000..cd097c2
Binary files /dev/null and b/sound/dialogue/animalese/kim/i.wav differ
diff --git a/sound/dialogue/animalese/kim/i.wav.import b/sound/dialogue/animalese/kim/i.wav.import
new file mode 100644
index 0000000..724e4f5
--- /dev/null
+++ b/sound/dialogue/animalese/kim/i.wav.import
@@ -0,0 +1,24 @@
+[remap]
+
+importer="wav"
+type="AudioStreamWAV"
+uid="uid://cyv3yum38oqdm"
+path="res://.godot/imported/i.wav-c720580ef88ff0c17cc9e0a9daec4ec2.sample"
+
+[deps]
+
+source_file="res://sound/dialogue/animalese/kim/i.wav"
+dest_files=["res://.godot/imported/i.wav-c720580ef88ff0c17cc9e0a9daec4ec2.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/kim/j.wav b/sound/dialogue/animalese/kim/j.wav
new file mode 100644
index 0000000..718d67e
Binary files /dev/null and b/sound/dialogue/animalese/kim/j.wav differ
diff --git a/sound/dialogue/animalese/kim/j.wav.import b/sound/dialogue/animalese/kim/j.wav.import
new file mode 100644
index 0000000..4cc5f64
--- /dev/null
+++ b/sound/dialogue/animalese/kim/j.wav.import
@@ -0,0 +1,24 @@
+[remap]
+
+importer="wav"
+type="AudioStreamWAV"
+uid="uid://b7hjrilpfkuxx"
+path="res://.godot/imported/j.wav-e5a60e91add3cfd5587ee2205e401fef.sample"
+
+[deps]
+
+source_file="res://sound/dialogue/animalese/kim/j.wav"
+dest_files=["res://.godot/imported/j.wav-e5a60e91add3cfd5587ee2205e401fef.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/kim/k.wav b/sound/dialogue/animalese/kim/k.wav
new file mode 100644
index 0000000..fa1748d
Binary files /dev/null and b/sound/dialogue/animalese/kim/k.wav differ
diff --git a/sound/dialogue/animalese/placeholder/z.wav.import b/sound/dialogue/animalese/kim/k.wav.import
similarity index 54%
rename from sound/dialogue/animalese/placeholder/z.wav.import
rename to sound/dialogue/animalese/kim/k.wav.import
index 12b1b54..171b8a0 100644
--- a/sound/dialogue/animalese/placeholder/z.wav.import
+++ b/sound/dialogue/animalese/kim/k.wav.import
@@ -2,13 +2,13 @@
importer="wav"
type="AudioStreamWAV"
-uid="uid://djvv63b0jlll"
-path="res://.godot/imported/z.wav-3e6ff24f53b1014b381cc80ee9fb1a84.sample"
+uid="uid://n4n7gg0bqbbm"
+path="res://.godot/imported/k.wav-82cc304c4627c5fcf78403e0d82b4066.sample"
[deps]
-source_file="res://sound/dialogue/animalese/placeholder/z.wav"
-dest_files=["res://.godot/imported/z.wav-3e6ff24f53b1014b381cc80ee9fb1a84.sample"]
+source_file="res://sound/dialogue/animalese/kim/k.wav"
+dest_files=["res://.godot/imported/k.wav-82cc304c4627c5fcf78403e0d82b4066.sample"]
[params]
diff --git a/sound/dialogue/animalese/kim/l.wav b/sound/dialogue/animalese/kim/l.wav
new file mode 100644
index 0000000..aea20f2
Binary files /dev/null and b/sound/dialogue/animalese/kim/l.wav differ
diff --git a/sound/dialogue/animalese/kim/l.wav.import b/sound/dialogue/animalese/kim/l.wav.import
new file mode 100644
index 0000000..1845e6d
--- /dev/null
+++ b/sound/dialogue/animalese/kim/l.wav.import
@@ -0,0 +1,24 @@
+[remap]
+
+importer="wav"
+type="AudioStreamWAV"
+uid="uid://nle3bfrffs5t"
+path="res://.godot/imported/l.wav-4b9d875920578dd42b0265fdc42a4c0d.sample"
+
+[deps]
+
+source_file="res://sound/dialogue/animalese/kim/l.wav"
+dest_files=["res://.godot/imported/l.wav-4b9d875920578dd42b0265fdc42a4c0d.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/kim/m.wav b/sound/dialogue/animalese/kim/m.wav
new file mode 100644
index 0000000..68a5ace
Binary files /dev/null and b/sound/dialogue/animalese/kim/m.wav differ
diff --git a/sound/dialogue/animalese/kim/m.wav.import b/sound/dialogue/animalese/kim/m.wav.import
new file mode 100644
index 0000000..5a81393
--- /dev/null
+++ b/sound/dialogue/animalese/kim/m.wav.import
@@ -0,0 +1,24 @@
+[remap]
+
+importer="wav"
+type="AudioStreamWAV"
+uid="uid://wia38ojr60e6"
+path="res://.godot/imported/m.wav-f7d07c9e641beaa268666b6d6b350fb0.sample"
+
+[deps]
+
+source_file="res://sound/dialogue/animalese/kim/m.wav"
+dest_files=["res://.godot/imported/m.wav-f7d07c9e641beaa268666b6d6b350fb0.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/kim/n.wav b/sound/dialogue/animalese/kim/n.wav
new file mode 100644
index 0000000..b3996d0
Binary files /dev/null and b/sound/dialogue/animalese/kim/n.wav differ
diff --git a/sound/dialogue/animalese/kim/n.wav.import b/sound/dialogue/animalese/kim/n.wav.import
new file mode 100644
index 0000000..63058b2
--- /dev/null
+++ b/sound/dialogue/animalese/kim/n.wav.import
@@ -0,0 +1,24 @@
+[remap]
+
+importer="wav"
+type="AudioStreamWAV"
+uid="uid://ci70fsfe28t2"
+path="res://.godot/imported/n.wav-19567bad745c477e3e8f72b9737adf99.sample"
+
+[deps]
+
+source_file="res://sound/dialogue/animalese/kim/n.wav"
+dest_files=["res://.godot/imported/n.wav-19567bad745c477e3e8f72b9737adf99.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/kim/o.wav b/sound/dialogue/animalese/kim/o.wav
new file mode 100644
index 0000000..66dd126
Binary files /dev/null and b/sound/dialogue/animalese/kim/o.wav differ
diff --git a/sound/dialogue/animalese/kim/o.wav.import b/sound/dialogue/animalese/kim/o.wav.import
new file mode 100644
index 0000000..4ba4147
--- /dev/null
+++ b/sound/dialogue/animalese/kim/o.wav.import
@@ -0,0 +1,24 @@
+[remap]
+
+importer="wav"
+type="AudioStreamWAV"
+uid="uid://droru2bo6m2ih"
+path="res://.godot/imported/o.wav-d2b3f3d92542aa1643abd9b106b457de.sample"
+
+[deps]
+
+source_file="res://sound/dialogue/animalese/kim/o.wav"
+dest_files=["res://.godot/imported/o.wav-d2b3f3d92542aa1643abd9b106b457de.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/kim/p.wav b/sound/dialogue/animalese/kim/p.wav
new file mode 100644
index 0000000..25cbb55
Binary files /dev/null and b/sound/dialogue/animalese/kim/p.wav differ
diff --git a/sound/dialogue/animalese/kim/p.wav.import b/sound/dialogue/animalese/kim/p.wav.import
new file mode 100644
index 0000000..ec826e3
--- /dev/null
+++ b/sound/dialogue/animalese/kim/p.wav.import
@@ -0,0 +1,24 @@
+[remap]
+
+importer="wav"
+type="AudioStreamWAV"
+uid="uid://d2gyv7ugi1yam"
+path="res://.godot/imported/p.wav-f16816387176c36971c1a3eb52f1742e.sample"
+
+[deps]
+
+source_file="res://sound/dialogue/animalese/kim/p.wav"
+dest_files=["res://.godot/imported/p.wav-f16816387176c36971c1a3eb52f1742e.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/kim/q.wav b/sound/dialogue/animalese/kim/q.wav
new file mode 100644
index 0000000..887f7d6
Binary files /dev/null and b/sound/dialogue/animalese/kim/q.wav differ
diff --git a/sound/dialogue/animalese/kim/q.wav.import b/sound/dialogue/animalese/kim/q.wav.import
new file mode 100644
index 0000000..0e794ee
--- /dev/null
+++ b/sound/dialogue/animalese/kim/q.wav.import
@@ -0,0 +1,24 @@
+[remap]
+
+importer="wav"
+type="AudioStreamWAV"
+uid="uid://ci6h5d2oag4an"
+path="res://.godot/imported/q.wav-729a36d49b5b3bd23f1badb802667428.sample"
+
+[deps]
+
+source_file="res://sound/dialogue/animalese/kim/q.wav"
+dest_files=["res://.godot/imported/q.wav-729a36d49b5b3bd23f1badb802667428.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/kim/r.wav b/sound/dialogue/animalese/kim/r.wav
new file mode 100644
index 0000000..1fa9937
Binary files /dev/null and b/sound/dialogue/animalese/kim/r.wav differ
diff --git a/sound/dialogue/animalese/kim/r.wav.import b/sound/dialogue/animalese/kim/r.wav.import
new file mode 100644
index 0000000..84c2887
--- /dev/null
+++ b/sound/dialogue/animalese/kim/r.wav.import
@@ -0,0 +1,24 @@
+[remap]
+
+importer="wav"
+type="AudioStreamWAV"
+uid="uid://c03fj374ceaqc"
+path="res://.godot/imported/r.wav-74003e87654a77c6a0afc0d80fca9cf3.sample"
+
+[deps]
+
+source_file="res://sound/dialogue/animalese/kim/r.wav"
+dest_files=["res://.godot/imported/r.wav-74003e87654a77c6a0afc0d80fca9cf3.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/kim/s.wav b/sound/dialogue/animalese/kim/s.wav
new file mode 100644
index 0000000..962fd46
Binary files /dev/null and b/sound/dialogue/animalese/kim/s.wav differ
diff --git a/sound/dialogue/animalese/kim/s.wav.import b/sound/dialogue/animalese/kim/s.wav.import
new file mode 100644
index 0000000..c39714d
--- /dev/null
+++ b/sound/dialogue/animalese/kim/s.wav.import
@@ -0,0 +1,24 @@
+[remap]
+
+importer="wav"
+type="AudioStreamWAV"
+uid="uid://d0yb6lesxgm8j"
+path="res://.godot/imported/s.wav-627d8bd9f4cee8c43ac9237705df784f.sample"
+
+[deps]
+
+source_file="res://sound/dialogue/animalese/kim/s.wav"
+dest_files=["res://.godot/imported/s.wav-627d8bd9f4cee8c43ac9237705df784f.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/kim/t.wav b/sound/dialogue/animalese/kim/t.wav
new file mode 100644
index 0000000..c5fcc51
Binary files /dev/null and b/sound/dialogue/animalese/kim/t.wav differ
diff --git a/sound/dialogue/animalese/kim/t.wav.import b/sound/dialogue/animalese/kim/t.wav.import
new file mode 100644
index 0000000..b6d0ae9
--- /dev/null
+++ b/sound/dialogue/animalese/kim/t.wav.import
@@ -0,0 +1,24 @@
+[remap]
+
+importer="wav"
+type="AudioStreamWAV"
+uid="uid://bcvrvl4mli82i"
+path="res://.godot/imported/t.wav-aec336b52646bd870831f9effe598c92.sample"
+
+[deps]
+
+source_file="res://sound/dialogue/animalese/kim/t.wav"
+dest_files=["res://.godot/imported/t.wav-aec336b52646bd870831f9effe598c92.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/kim/u.wav b/sound/dialogue/animalese/kim/u.wav
new file mode 100644
index 0000000..56caa56
Binary files /dev/null and b/sound/dialogue/animalese/kim/u.wav differ
diff --git a/sound/dialogue/animalese/kim/u.wav.import b/sound/dialogue/animalese/kim/u.wav.import
new file mode 100644
index 0000000..7724a93
--- /dev/null
+++ b/sound/dialogue/animalese/kim/u.wav.import
@@ -0,0 +1,24 @@
+[remap]
+
+importer="wav"
+type="AudioStreamWAV"
+uid="uid://mpw8xt0uxm5d"
+path="res://.godot/imported/u.wav-02b540f73efcb10ad4943d4f37bdf90b.sample"
+
+[deps]
+
+source_file="res://sound/dialogue/animalese/kim/u.wav"
+dest_files=["res://.godot/imported/u.wav-02b540f73efcb10ad4943d4f37bdf90b.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/kim/v.wav b/sound/dialogue/animalese/kim/v.wav
new file mode 100644
index 0000000..4add64f
Binary files /dev/null and b/sound/dialogue/animalese/kim/v.wav differ
diff --git a/sound/dialogue/animalese/placeholder/v.wav.import b/sound/dialogue/animalese/kim/v.wav.import
similarity index 50%
rename from sound/dialogue/animalese/placeholder/v.wav.import
rename to sound/dialogue/animalese/kim/v.wav.import
index ff876a8..fb772de 100644
--- a/sound/dialogue/animalese/placeholder/v.wav.import
+++ b/sound/dialogue/animalese/kim/v.wav.import
@@ -2,13 +2,13 @@
importer="wav"
type="AudioStreamWAV"
-uid="uid://y3g1s3pw3bn0"
-path="res://.godot/imported/v.wav-5a9986e91dbdd0da4f0583b35a986b1c.sample"
+uid="uid://n0r81bxuadfo"
+path="res://.godot/imported/v.wav-d66f5caffb359fad446ec9ddbacdf071.sample"
[deps]
-source_file="res://sound/dialogue/animalese/placeholder/v.wav"
-dest_files=["res://.godot/imported/v.wav-5a9986e91dbdd0da4f0583b35a986b1c.sample"]
+source_file="res://sound/dialogue/animalese/kim/v.wav"
+dest_files=["res://.godot/imported/v.wav-d66f5caffb359fad446ec9ddbacdf071.sample"]
[params]
diff --git a/sound/dialogue/animalese/kim/w.wav b/sound/dialogue/animalese/kim/w.wav
new file mode 100644
index 0000000..9020a84
Binary files /dev/null and b/sound/dialogue/animalese/kim/w.wav differ
diff --git a/sound/dialogue/animalese/placeholder/w.wav.import b/sound/dialogue/animalese/kim/w.wav.import
similarity index 50%
rename from sound/dialogue/animalese/placeholder/w.wav.import
rename to sound/dialogue/animalese/kim/w.wav.import
index 36f4e0f..b6f0a30 100644
--- a/sound/dialogue/animalese/placeholder/w.wav.import
+++ b/sound/dialogue/animalese/kim/w.wav.import
@@ -2,13 +2,13 @@
importer="wav"
type="AudioStreamWAV"
-uid="uid://5ml2iuf8cpgt"
-path="res://.godot/imported/w.wav-d3e9035a1fc5e7946be782a0d4f95e64.sample"
+uid="uid://dm742m0d4b8o7"
+path="res://.godot/imported/w.wav-46b37c23c004fe793f00e82c587efbcf.sample"
[deps]
-source_file="res://sound/dialogue/animalese/placeholder/w.wav"
-dest_files=["res://.godot/imported/w.wav-d3e9035a1fc5e7946be782a0d4f95e64.sample"]
+source_file="res://sound/dialogue/animalese/kim/w.wav"
+dest_files=["res://.godot/imported/w.wav-46b37c23c004fe793f00e82c587efbcf.sample"]
[params]
diff --git a/sound/dialogue/animalese/kim/x.wav b/sound/dialogue/animalese/kim/x.wav
new file mode 100644
index 0000000..f147ef3
Binary files /dev/null and b/sound/dialogue/animalese/kim/x.wav differ
diff --git a/sound/dialogue/animalese/kim/x.wav.import b/sound/dialogue/animalese/kim/x.wav.import
new file mode 100644
index 0000000..4fcd581
--- /dev/null
+++ b/sound/dialogue/animalese/kim/x.wav.import
@@ -0,0 +1,24 @@
+[remap]
+
+importer="wav"
+type="AudioStreamWAV"
+uid="uid://cn2my2uukyd8u"
+path="res://.godot/imported/x.wav-90efe4fb55c5c2061c2e9dbc89bfb532.sample"
+
+[deps]
+
+source_file="res://sound/dialogue/animalese/kim/x.wav"
+dest_files=["res://.godot/imported/x.wav-90efe4fb55c5c2061c2e9dbc89bfb532.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/kim/y.wav b/sound/dialogue/animalese/kim/y.wav
new file mode 100644
index 0000000..7f818c8
Binary files /dev/null and b/sound/dialogue/animalese/kim/y.wav differ
diff --git a/sound/dialogue/animalese/kim/y.wav.import b/sound/dialogue/animalese/kim/y.wav.import
new file mode 100644
index 0000000..0cfb028
--- /dev/null
+++ b/sound/dialogue/animalese/kim/y.wav.import
@@ -0,0 +1,24 @@
+[remap]
+
+importer="wav"
+type="AudioStreamWAV"
+uid="uid://djhq5e17gcqv1"
+path="res://.godot/imported/y.wav-0170ee159f2f8405596f47206d01cf8f.sample"
+
+[deps]
+
+source_file="res://sound/dialogue/animalese/kim/y.wav"
+dest_files=["res://.godot/imported/y.wav-0170ee159f2f8405596f47206d01cf8f.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/kim/z.wav b/sound/dialogue/animalese/kim/z.wav
new file mode 100644
index 0000000..ed9af43
Binary files /dev/null and b/sound/dialogue/animalese/kim/z.wav differ
diff --git a/sound/dialogue/animalese/kim/z.wav.import b/sound/dialogue/animalese/kim/z.wav.import
new file mode 100644
index 0000000..4a3b319
--- /dev/null
+++ b/sound/dialogue/animalese/kim/z.wav.import
@@ -0,0 +1,24 @@
+[remap]
+
+importer="wav"
+type="AudioStreamWAV"
+uid="uid://bhhaol0imq60f"
+path="res://.godot/imported/z.wav-6daaf8e93675aba3840dbf16e9c93ebf.sample"
+
+[deps]
+
+source_file="res://sound/dialogue/animalese/kim/z.wav"
+dest_files=["res://.godot/imported/z.wav-6daaf8e93675aba3840dbf16e9c93ebf.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/a.wav b/sound/dialogue/animalese/placeholder/a.wav
deleted file mode 100644
index ca652d4..0000000
Binary files a/sound/dialogue/animalese/placeholder/a.wav and /dev/null differ
diff --git a/sound/dialogue/animalese/placeholder/b.wav b/sound/dialogue/animalese/placeholder/b.wav
deleted file mode 100644
index 71ceb88..0000000
Binary files a/sound/dialogue/animalese/placeholder/b.wav and /dev/null differ
diff --git a/sound/dialogue/animalese/placeholder/c.wav b/sound/dialogue/animalese/placeholder/c.wav
deleted file mode 100644
index 8b5eb64..0000000
Binary files a/sound/dialogue/animalese/placeholder/c.wav and /dev/null differ
diff --git a/sound/dialogue/animalese/placeholder/d.wav b/sound/dialogue/animalese/placeholder/d.wav
deleted file mode 100644
index 978009a..0000000
Binary files a/sound/dialogue/animalese/placeholder/d.wav and /dev/null differ
diff --git a/sound/dialogue/animalese/placeholder/e.wav b/sound/dialogue/animalese/placeholder/e.wav
deleted file mode 100644
index 76f7b8c..0000000
Binary files a/sound/dialogue/animalese/placeholder/e.wav and /dev/null differ
diff --git a/sound/dialogue/animalese/placeholder/f.wav b/sound/dialogue/animalese/placeholder/f.wav
deleted file mode 100644
index bec92c1..0000000
Binary files a/sound/dialogue/animalese/placeholder/f.wav and /dev/null differ
diff --git a/sound/dialogue/animalese/placeholder/fallback.wav b/sound/dialogue/animalese/placeholder/fallback.wav
deleted file mode 100644
index 63a759e..0000000
Binary files a/sound/dialogue/animalese/placeholder/fallback.wav and /dev/null differ
diff --git a/sound/dialogue/animalese/placeholder/fallback.wav.import b/sound/dialogue/animalese/placeholder/fallback.wav.import
deleted file mode 100644
index e116a3c..0000000
--- a/sound/dialogue/animalese/placeholder/fallback.wav.import
+++ /dev/null
@@ -1,24 +0,0 @@
-[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
deleted file mode 100644
index 39dccf1..0000000
Binary files a/sound/dialogue/animalese/placeholder/g.wav and /dev/null differ
diff --git a/sound/dialogue/animalese/placeholder/h.wav b/sound/dialogue/animalese/placeholder/h.wav
deleted file mode 100644
index acebfed..0000000
Binary files a/sound/dialogue/animalese/placeholder/h.wav and /dev/null differ
diff --git a/sound/dialogue/animalese/placeholder/i.wav b/sound/dialogue/animalese/placeholder/i.wav
deleted file mode 100644
index fbd10e1..0000000
Binary files a/sound/dialogue/animalese/placeholder/i.wav and /dev/null differ
diff --git a/sound/dialogue/animalese/placeholder/j.wav b/sound/dialogue/animalese/placeholder/j.wav
deleted file mode 100644
index 0087db5..0000000
Binary files a/sound/dialogue/animalese/placeholder/j.wav and /dev/null differ
diff --git a/sound/dialogue/animalese/placeholder/j.wav.import b/sound/dialogue/animalese/placeholder/j.wav.import
deleted file mode 100644
index 46a0baf..0000000
--- a/sound/dialogue/animalese/placeholder/j.wav.import
+++ /dev/null
@@ -1,24 +0,0 @@
-[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
deleted file mode 100644
index c1b5e69..0000000
Binary files a/sound/dialogue/animalese/placeholder/k.wav and /dev/null differ
diff --git a/sound/dialogue/animalese/placeholder/l.wav b/sound/dialogue/animalese/placeholder/l.wav
deleted file mode 100644
index fe2624b..0000000
Binary files a/sound/dialogue/animalese/placeholder/l.wav and /dev/null differ
diff --git a/sound/dialogue/animalese/placeholder/m.wav b/sound/dialogue/animalese/placeholder/m.wav
deleted file mode 100644
index 4660d96..0000000
Binary files a/sound/dialogue/animalese/placeholder/m.wav and /dev/null differ
diff --git a/sound/dialogue/animalese/placeholder/n.wav b/sound/dialogue/animalese/placeholder/n.wav
deleted file mode 100644
index 97e5457..0000000
Binary files a/sound/dialogue/animalese/placeholder/n.wav and /dev/null differ
diff --git a/sound/dialogue/animalese/placeholder/o.wav b/sound/dialogue/animalese/placeholder/o.wav
deleted file mode 100644
index bee6bc1..0000000
Binary files a/sound/dialogue/animalese/placeholder/o.wav and /dev/null differ
diff --git a/sound/dialogue/animalese/placeholder/p.wav b/sound/dialogue/animalese/placeholder/p.wav
deleted file mode 100644
index 29fdd60..0000000
Binary files a/sound/dialogue/animalese/placeholder/p.wav and /dev/null differ
diff --git a/sound/dialogue/animalese/placeholder/q.wav b/sound/dialogue/animalese/placeholder/q.wav
deleted file mode 100644
index df184ca..0000000
Binary files a/sound/dialogue/animalese/placeholder/q.wav and /dev/null differ
diff --git a/sound/dialogue/animalese/placeholder/r.wav b/sound/dialogue/animalese/placeholder/r.wav
deleted file mode 100644
index 41fda60..0000000
Binary files a/sound/dialogue/animalese/placeholder/r.wav and /dev/null differ
diff --git a/sound/dialogue/animalese/placeholder/s.wav b/sound/dialogue/animalese/placeholder/s.wav
deleted file mode 100644
index f9c542a..0000000
Binary files a/sound/dialogue/animalese/placeholder/s.wav and /dev/null differ
diff --git a/sound/dialogue/animalese/placeholder/t.wav b/sound/dialogue/animalese/placeholder/t.wav
deleted file mode 100644
index 76e0948..0000000
Binary files a/sound/dialogue/animalese/placeholder/t.wav and /dev/null differ
diff --git a/sound/dialogue/animalese/placeholder/u.wav b/sound/dialogue/animalese/placeholder/u.wav
deleted file mode 100644
index 81ebbe5..0000000
Binary files a/sound/dialogue/animalese/placeholder/u.wav and /dev/null differ
diff --git a/sound/dialogue/animalese/placeholder/u.wav.import b/sound/dialogue/animalese/placeholder/u.wav.import
deleted file mode 100644
index 8ccd096..0000000
--- a/sound/dialogue/animalese/placeholder/u.wav.import
+++ /dev/null
@@ -1,24 +0,0 @@
-[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
deleted file mode 100644
index d5c0d12..0000000
Binary files a/sound/dialogue/animalese/placeholder/v.wav and /dev/null differ
diff --git a/sound/dialogue/animalese/placeholder/w.wav b/sound/dialogue/animalese/placeholder/w.wav
deleted file mode 100644
index 5122f9c..0000000
Binary files a/sound/dialogue/animalese/placeholder/w.wav and /dev/null differ
diff --git a/sound/dialogue/animalese/placeholder/x.wav b/sound/dialogue/animalese/placeholder/x.wav
deleted file mode 100644
index e00f085..0000000
Binary files a/sound/dialogue/animalese/placeholder/x.wav and /dev/null differ
diff --git a/sound/dialogue/animalese/placeholder/y.wav b/sound/dialogue/animalese/placeholder/y.wav
deleted file mode 100644
index 07b45ef..0000000
Binary files a/sound/dialogue/animalese/placeholder/y.wav and /dev/null differ
diff --git a/sound/dialogue/animalese/placeholder/z.wav b/sound/dialogue/animalese/placeholder/z.wav
deleted file mode 100644
index 8f94654..0000000
Binary files a/sound/dialogue/animalese/placeholder/z.wav and /dev/null differ
diff --git a/sound/dialogue/animalese/robot/a.wav b/sound/dialogue/animalese/robot/a.wav
new file mode 100644
index 0000000..0e434f7
Binary files /dev/null and b/sound/dialogue/animalese/robot/a.wav differ
diff --git a/sound/dialogue/animalese/robot/a.wav.import b/sound/dialogue/animalese/robot/a.wav.import
new file mode 100644
index 0000000..6d33ed2
--- /dev/null
+++ b/sound/dialogue/animalese/robot/a.wav.import
@@ -0,0 +1,24 @@
+[remap]
+
+importer="wav"
+type="AudioStreamWAV"
+uid="uid://cg8vgijwjen1h"
+path="res://.godot/imported/a.wav-912469cf287b9f82df9e0084967c3138.sample"
+
+[deps]
+
+source_file="res://sound/dialogue/animalese/robot/a.wav"
+dest_files=["res://.godot/imported/a.wav-912469cf287b9f82df9e0084967c3138.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/robot/b.wav b/sound/dialogue/animalese/robot/b.wav
new file mode 100644
index 0000000..8e24102
Binary files /dev/null and b/sound/dialogue/animalese/robot/b.wav differ
diff --git a/sound/dialogue/animalese/robot/b.wav.import b/sound/dialogue/animalese/robot/b.wav.import
new file mode 100644
index 0000000..d858d56
--- /dev/null
+++ b/sound/dialogue/animalese/robot/b.wav.import
@@ -0,0 +1,24 @@
+[remap]
+
+importer="wav"
+type="AudioStreamWAV"
+uid="uid://dbn3ilqo34j1l"
+path="res://.godot/imported/b.wav-482bec9aa59b1274ddc061acf0928cc3.sample"
+
+[deps]
+
+source_file="res://sound/dialogue/animalese/robot/b.wav"
+dest_files=["res://.godot/imported/b.wav-482bec9aa59b1274ddc061acf0928cc3.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/robot/c.wav b/sound/dialogue/animalese/robot/c.wav
new file mode 100644
index 0000000..4eb4028
Binary files /dev/null and b/sound/dialogue/animalese/robot/c.wav differ
diff --git a/sound/dialogue/animalese/robot/c.wav.import b/sound/dialogue/animalese/robot/c.wav.import
new file mode 100644
index 0000000..a32613d
--- /dev/null
+++ b/sound/dialogue/animalese/robot/c.wav.import
@@ -0,0 +1,24 @@
+[remap]
+
+importer="wav"
+type="AudioStreamWAV"
+uid="uid://6tmnptljtqms"
+path="res://.godot/imported/c.wav-88fe8cecfa21a5d5cf845d6b8679d13c.sample"
+
+[deps]
+
+source_file="res://sound/dialogue/animalese/robot/c.wav"
+dest_files=["res://.godot/imported/c.wav-88fe8cecfa21a5d5cf845d6b8679d13c.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/robot/d.wav b/sound/dialogue/animalese/robot/d.wav
new file mode 100644
index 0000000..eba38a4
Binary files /dev/null and b/sound/dialogue/animalese/robot/d.wav differ
diff --git a/sound/dialogue/animalese/robot/d.wav.import b/sound/dialogue/animalese/robot/d.wav.import
new file mode 100644
index 0000000..3732bc7
--- /dev/null
+++ b/sound/dialogue/animalese/robot/d.wav.import
@@ -0,0 +1,24 @@
+[remap]
+
+importer="wav"
+type="AudioStreamWAV"
+uid="uid://btrj6qtm84855"
+path="res://.godot/imported/d.wav-ef76f071f3356e954019c5494b963533.sample"
+
+[deps]
+
+source_file="res://sound/dialogue/animalese/robot/d.wav"
+dest_files=["res://.godot/imported/d.wav-ef76f071f3356e954019c5494b963533.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/robot/e.wav b/sound/dialogue/animalese/robot/e.wav
new file mode 100644
index 0000000..4b54f04
Binary files /dev/null and b/sound/dialogue/animalese/robot/e.wav differ
diff --git a/sound/dialogue/animalese/robot/e.wav.import b/sound/dialogue/animalese/robot/e.wav.import
new file mode 100644
index 0000000..ce7a9c8
--- /dev/null
+++ b/sound/dialogue/animalese/robot/e.wav.import
@@ -0,0 +1,24 @@
+[remap]
+
+importer="wav"
+type="AudioStreamWAV"
+uid="uid://esdsylelog50"
+path="res://.godot/imported/e.wav-e44b4d22c43894915ef0134813dd6ec8.sample"
+
+[deps]
+
+source_file="res://sound/dialogue/animalese/robot/e.wav"
+dest_files=["res://.godot/imported/e.wav-e44b4d22c43894915ef0134813dd6ec8.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/robot/f.wav b/sound/dialogue/animalese/robot/f.wav
new file mode 100644
index 0000000..e30b889
Binary files /dev/null and b/sound/dialogue/animalese/robot/f.wav differ
diff --git a/sound/dialogue/animalese/robot/f.wav.import b/sound/dialogue/animalese/robot/f.wav.import
new file mode 100644
index 0000000..c9a3d2f
--- /dev/null
+++ b/sound/dialogue/animalese/robot/f.wav.import
@@ -0,0 +1,24 @@
+[remap]
+
+importer="wav"
+type="AudioStreamWAV"
+uid="uid://xb01jed4d7xl"
+path="res://.godot/imported/f.wav-06750a71b9dfd09a72792c85901ef2e0.sample"
+
+[deps]
+
+source_file="res://sound/dialogue/animalese/robot/f.wav"
+dest_files=["res://.godot/imported/f.wav-06750a71b9dfd09a72792c85901ef2e0.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/robot/fallback.wav b/sound/dialogue/animalese/robot/fallback.wav
new file mode 100644
index 0000000..54ca7ca
Binary files /dev/null and b/sound/dialogue/animalese/robot/fallback.wav differ
diff --git a/sound/dialogue/animalese/robot/fallback.wav.import b/sound/dialogue/animalese/robot/fallback.wav.import
new file mode 100644
index 0000000..a62bdeb
--- /dev/null
+++ b/sound/dialogue/animalese/robot/fallback.wav.import
@@ -0,0 +1,24 @@
+[remap]
+
+importer="wav"
+type="AudioStreamWAV"
+uid="uid://cebc27kmdv7bt"
+path="res://.godot/imported/fallback.wav-70adb8cf4fa8834ef769dc92854a5f89.sample"
+
+[deps]
+
+source_file="res://sound/dialogue/animalese/robot/fallback.wav"
+dest_files=["res://.godot/imported/fallback.wav-70adb8cf4fa8834ef769dc92854a5f89.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/robot/g.wav b/sound/dialogue/animalese/robot/g.wav
new file mode 100644
index 0000000..d659358
Binary files /dev/null and b/sound/dialogue/animalese/robot/g.wav differ
diff --git a/sound/dialogue/animalese/robot/g.wav.import b/sound/dialogue/animalese/robot/g.wav.import
new file mode 100644
index 0000000..1c619ad
--- /dev/null
+++ b/sound/dialogue/animalese/robot/g.wav.import
@@ -0,0 +1,24 @@
+[remap]
+
+importer="wav"
+type="AudioStreamWAV"
+uid="uid://cxnixxqcol7cf"
+path="res://.godot/imported/g.wav-bab051202eead7ae86eb50d16fe7ae4d.sample"
+
+[deps]
+
+source_file="res://sound/dialogue/animalese/robot/g.wav"
+dest_files=["res://.godot/imported/g.wav-bab051202eead7ae86eb50d16fe7ae4d.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/robot/h.wav b/sound/dialogue/animalese/robot/h.wav
new file mode 100644
index 0000000..3363245
Binary files /dev/null and b/sound/dialogue/animalese/robot/h.wav differ
diff --git a/sound/dialogue/animalese/robot/h.wav.import b/sound/dialogue/animalese/robot/h.wav.import
new file mode 100644
index 0000000..c7a868b
--- /dev/null
+++ b/sound/dialogue/animalese/robot/h.wav.import
@@ -0,0 +1,24 @@
+[remap]
+
+importer="wav"
+type="AudioStreamWAV"
+uid="uid://cyssmnjalcjh1"
+path="res://.godot/imported/h.wav-82eb606f12a9a3898f9adc04f47242bc.sample"
+
+[deps]
+
+source_file="res://sound/dialogue/animalese/robot/h.wav"
+dest_files=["res://.godot/imported/h.wav-82eb606f12a9a3898f9adc04f47242bc.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/robot/i.wav b/sound/dialogue/animalese/robot/i.wav
new file mode 100644
index 0000000..c0b5ce8
Binary files /dev/null and b/sound/dialogue/animalese/robot/i.wav differ
diff --git a/sound/dialogue/animalese/robot/i.wav.import b/sound/dialogue/animalese/robot/i.wav.import
new file mode 100644
index 0000000..94f36d8
--- /dev/null
+++ b/sound/dialogue/animalese/robot/i.wav.import
@@ -0,0 +1,24 @@
+[remap]
+
+importer="wav"
+type="AudioStreamWAV"
+uid="uid://cvng0omshxqb1"
+path="res://.godot/imported/i.wav-efaf75378a992fa714224dedfe31ed34.sample"
+
+[deps]
+
+source_file="res://sound/dialogue/animalese/robot/i.wav"
+dest_files=["res://.godot/imported/i.wav-efaf75378a992fa714224dedfe31ed34.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/robot/j.wav b/sound/dialogue/animalese/robot/j.wav
new file mode 100644
index 0000000..f5f2e24
Binary files /dev/null and b/sound/dialogue/animalese/robot/j.wav differ
diff --git a/sound/dialogue/animalese/robot/j.wav.import b/sound/dialogue/animalese/robot/j.wav.import
new file mode 100644
index 0000000..b9a9adf
--- /dev/null
+++ b/sound/dialogue/animalese/robot/j.wav.import
@@ -0,0 +1,24 @@
+[remap]
+
+importer="wav"
+type="AudioStreamWAV"
+uid="uid://b8roqgoeyo3l8"
+path="res://.godot/imported/j.wav-822afb34d9e86d0e6fb2f2e871c11a99.sample"
+
+[deps]
+
+source_file="res://sound/dialogue/animalese/robot/j.wav"
+dest_files=["res://.godot/imported/j.wav-822afb34d9e86d0e6fb2f2e871c11a99.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/robot/k.wav b/sound/dialogue/animalese/robot/k.wav
new file mode 100644
index 0000000..f9c6b5c
Binary files /dev/null and b/sound/dialogue/animalese/robot/k.wav differ
diff --git a/sound/dialogue/animalese/placeholder/l.wav.import b/sound/dialogue/animalese/robot/k.wav.import
similarity index 52%
rename from sound/dialogue/animalese/placeholder/l.wav.import
rename to sound/dialogue/animalese/robot/k.wav.import
index 8193ecf..ca3086f 100644
--- a/sound/dialogue/animalese/placeholder/l.wav.import
+++ b/sound/dialogue/animalese/robot/k.wav.import
@@ -2,13 +2,13 @@
importer="wav"
type="AudioStreamWAV"
-uid="uid://bixgnaq4idkhj"
-path="res://.godot/imported/l.wav-b21006c3b048e0c797d6baf19ab0f663.sample"
+uid="uid://b2x5ijnb38tnv"
+path="res://.godot/imported/k.wav-253c9eda1dc82ce4e496727f8805ed63.sample"
[deps]
-source_file="res://sound/dialogue/animalese/placeholder/l.wav"
-dest_files=["res://.godot/imported/l.wav-b21006c3b048e0c797d6baf19ab0f663.sample"]
+source_file="res://sound/dialogue/animalese/robot/k.wav"
+dest_files=["res://.godot/imported/k.wav-253c9eda1dc82ce4e496727f8805ed63.sample"]
[params]
diff --git a/sound/dialogue/animalese/robot/l.wav b/sound/dialogue/animalese/robot/l.wav
new file mode 100644
index 0000000..cc1d512
Binary files /dev/null and b/sound/dialogue/animalese/robot/l.wav differ
diff --git a/sound/dialogue/animalese/robot/l.wav.import b/sound/dialogue/animalese/robot/l.wav.import
new file mode 100644
index 0000000..7f77dac
--- /dev/null
+++ b/sound/dialogue/animalese/robot/l.wav.import
@@ -0,0 +1,24 @@
+[remap]
+
+importer="wav"
+type="AudioStreamWAV"
+uid="uid://dtobkfled545r"
+path="res://.godot/imported/l.wav-74470cbddad2ea89893332702c74ae8d.sample"
+
+[deps]
+
+source_file="res://sound/dialogue/animalese/robot/l.wav"
+dest_files=["res://.godot/imported/l.wav-74470cbddad2ea89893332702c74ae8d.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/robot/m.wav b/sound/dialogue/animalese/robot/m.wav
new file mode 100644
index 0000000..7359a06
Binary files /dev/null and b/sound/dialogue/animalese/robot/m.wav differ
diff --git a/sound/dialogue/animalese/robot/m.wav.import b/sound/dialogue/animalese/robot/m.wav.import
new file mode 100644
index 0000000..cc0ebdf
--- /dev/null
+++ b/sound/dialogue/animalese/robot/m.wav.import
@@ -0,0 +1,24 @@
+[remap]
+
+importer="wav"
+type="AudioStreamWAV"
+uid="uid://x53vy8vfb0v7"
+path="res://.godot/imported/m.wav-3282c0735e81b1924994c0f641854897.sample"
+
+[deps]
+
+source_file="res://sound/dialogue/animalese/robot/m.wav"
+dest_files=["res://.godot/imported/m.wav-3282c0735e81b1924994c0f641854897.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/robot/n.wav b/sound/dialogue/animalese/robot/n.wav
new file mode 100644
index 0000000..a505906
Binary files /dev/null and b/sound/dialogue/animalese/robot/n.wav differ
diff --git a/sound/dialogue/animalese/robot/n.wav.import b/sound/dialogue/animalese/robot/n.wav.import
new file mode 100644
index 0000000..3122cfb
--- /dev/null
+++ b/sound/dialogue/animalese/robot/n.wav.import
@@ -0,0 +1,24 @@
+[remap]
+
+importer="wav"
+type="AudioStreamWAV"
+uid="uid://cktk3r2ociatv"
+path="res://.godot/imported/n.wav-38bfd5021f583b1a472e0c265ddc01e0.sample"
+
+[deps]
+
+source_file="res://sound/dialogue/animalese/robot/n.wav"
+dest_files=["res://.godot/imported/n.wav-38bfd5021f583b1a472e0c265ddc01e0.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/robot/o.wav b/sound/dialogue/animalese/robot/o.wav
new file mode 100644
index 0000000..7e8cb61
Binary files /dev/null and b/sound/dialogue/animalese/robot/o.wav differ
diff --git a/sound/dialogue/animalese/robot/o.wav.import b/sound/dialogue/animalese/robot/o.wav.import
new file mode 100644
index 0000000..58596f6
--- /dev/null
+++ b/sound/dialogue/animalese/robot/o.wav.import
@@ -0,0 +1,24 @@
+[remap]
+
+importer="wav"
+type="AudioStreamWAV"
+uid="uid://exnucx2mi3nk"
+path="res://.godot/imported/o.wav-c2146198bb8a83542393735ccb97a983.sample"
+
+[deps]
+
+source_file="res://sound/dialogue/animalese/robot/o.wav"
+dest_files=["res://.godot/imported/o.wav-c2146198bb8a83542393735ccb97a983.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/robot/p.wav b/sound/dialogue/animalese/robot/p.wav
new file mode 100644
index 0000000..770c2ea
Binary files /dev/null and b/sound/dialogue/animalese/robot/p.wav differ
diff --git a/sound/dialogue/animalese/placeholder/c.wav.import b/sound/dialogue/animalese/robot/p.wav.import
similarity index 52%
rename from sound/dialogue/animalese/placeholder/c.wav.import
rename to sound/dialogue/animalese/robot/p.wav.import
index bbd8074..5b0c010 100644
--- a/sound/dialogue/animalese/placeholder/c.wav.import
+++ b/sound/dialogue/animalese/robot/p.wav.import
@@ -2,13 +2,13 @@
importer="wav"
type="AudioStreamWAV"
-uid="uid://2inox7x31xam"
-path="res://.godot/imported/c.wav-61dc49533634805d70c71d509b63e37b.sample"
+uid="uid://jgnbqcbt0t64"
+path="res://.godot/imported/p.wav-b5d1c85200b8e57e540534fa6782857b.sample"
[deps]
-source_file="res://sound/dialogue/animalese/placeholder/c.wav"
-dest_files=["res://.godot/imported/c.wav-61dc49533634805d70c71d509b63e37b.sample"]
+source_file="res://sound/dialogue/animalese/robot/p.wav"
+dest_files=["res://.godot/imported/p.wav-b5d1c85200b8e57e540534fa6782857b.sample"]
[params]
diff --git a/sound/dialogue/animalese/robot/q.wav b/sound/dialogue/animalese/robot/q.wav
new file mode 100644
index 0000000..f7ec879
Binary files /dev/null and b/sound/dialogue/animalese/robot/q.wav differ
diff --git a/sound/dialogue/animalese/robot/q.wav.import b/sound/dialogue/animalese/robot/q.wav.import
new file mode 100644
index 0000000..a624666
--- /dev/null
+++ b/sound/dialogue/animalese/robot/q.wav.import
@@ -0,0 +1,24 @@
+[remap]
+
+importer="wav"
+type="AudioStreamWAV"
+uid="uid://ycgqs14iexxe"
+path="res://.godot/imported/q.wav-27ccb3c315837cff45c092f2c543d9c3.sample"
+
+[deps]
+
+source_file="res://sound/dialogue/animalese/robot/q.wav"
+dest_files=["res://.godot/imported/q.wav-27ccb3c315837cff45c092f2c543d9c3.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/robot/r.wav b/sound/dialogue/animalese/robot/r.wav
new file mode 100644
index 0000000..9a3b293
Binary files /dev/null and b/sound/dialogue/animalese/robot/r.wav differ
diff --git a/sound/dialogue/animalese/robot/r.wav.import b/sound/dialogue/animalese/robot/r.wav.import
new file mode 100644
index 0000000..a64a6b2
--- /dev/null
+++ b/sound/dialogue/animalese/robot/r.wav.import
@@ -0,0 +1,24 @@
+[remap]
+
+importer="wav"
+type="AudioStreamWAV"
+uid="uid://bun5h3babtwrw"
+path="res://.godot/imported/r.wav-7370fd302d45d0a79de1eb3caf1fa0e2.sample"
+
+[deps]
+
+source_file="res://sound/dialogue/animalese/robot/r.wav"
+dest_files=["res://.godot/imported/r.wav-7370fd302d45d0a79de1eb3caf1fa0e2.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/robot/s.wav b/sound/dialogue/animalese/robot/s.wav
new file mode 100644
index 0000000..0847120
Binary files /dev/null and b/sound/dialogue/animalese/robot/s.wav differ
diff --git a/sound/dialogue/animalese/robot/s.wav.import b/sound/dialogue/animalese/robot/s.wav.import
new file mode 100644
index 0000000..a65ebe9
--- /dev/null
+++ b/sound/dialogue/animalese/robot/s.wav.import
@@ -0,0 +1,24 @@
+[remap]
+
+importer="wav"
+type="AudioStreamWAV"
+uid="uid://ds02uafo1fulk"
+path="res://.godot/imported/s.wav-d021f184e6f257c8c38e99166409779b.sample"
+
+[deps]
+
+source_file="res://sound/dialogue/animalese/robot/s.wav"
+dest_files=["res://.godot/imported/s.wav-d021f184e6f257c8c38e99166409779b.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/robot/t.wav b/sound/dialogue/animalese/robot/t.wav
new file mode 100644
index 0000000..fef9bf0
Binary files /dev/null and b/sound/dialogue/animalese/robot/t.wav differ
diff --git a/sound/dialogue/animalese/robot/t.wav.import b/sound/dialogue/animalese/robot/t.wav.import
new file mode 100644
index 0000000..86242c0
--- /dev/null
+++ b/sound/dialogue/animalese/robot/t.wav.import
@@ -0,0 +1,24 @@
+[remap]
+
+importer="wav"
+type="AudioStreamWAV"
+uid="uid://ouvxq1dsnqsc"
+path="res://.godot/imported/t.wav-12b718527178b570cfd5eedb8c04297e.sample"
+
+[deps]
+
+source_file="res://sound/dialogue/animalese/robot/t.wav"
+dest_files=["res://.godot/imported/t.wav-12b718527178b570cfd5eedb8c04297e.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/robot/u.wav b/sound/dialogue/animalese/robot/u.wav
new file mode 100644
index 0000000..d2d148d
Binary files /dev/null and b/sound/dialogue/animalese/robot/u.wav differ
diff --git a/sound/dialogue/animalese/robot/u.wav.import b/sound/dialogue/animalese/robot/u.wav.import
new file mode 100644
index 0000000..2703c24
--- /dev/null
+++ b/sound/dialogue/animalese/robot/u.wav.import
@@ -0,0 +1,24 @@
+[remap]
+
+importer="wav"
+type="AudioStreamWAV"
+uid="uid://dd3ece0ndg16y"
+path="res://.godot/imported/u.wav-06fb03902c67d2949aa71cdb8e1f8bf0.sample"
+
+[deps]
+
+source_file="res://sound/dialogue/animalese/robot/u.wav"
+dest_files=["res://.godot/imported/u.wav-06fb03902c67d2949aa71cdb8e1f8bf0.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/robot/v.wav b/sound/dialogue/animalese/robot/v.wav
new file mode 100644
index 0000000..32d8341
Binary files /dev/null and b/sound/dialogue/animalese/robot/v.wav differ
diff --git a/sound/dialogue/animalese/robot/v.wav.import b/sound/dialogue/animalese/robot/v.wav.import
new file mode 100644
index 0000000..a82ae8a
--- /dev/null
+++ b/sound/dialogue/animalese/robot/v.wav.import
@@ -0,0 +1,24 @@
+[remap]
+
+importer="wav"
+type="AudioStreamWAV"
+uid="uid://cn1reodu8saiy"
+path="res://.godot/imported/v.wav-d3ebbf2d472bce20fe0792395ed4d049.sample"
+
+[deps]
+
+source_file="res://sound/dialogue/animalese/robot/v.wav"
+dest_files=["res://.godot/imported/v.wav-d3ebbf2d472bce20fe0792395ed4d049.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/robot/w.wav b/sound/dialogue/animalese/robot/w.wav
new file mode 100644
index 0000000..68d06c2
Binary files /dev/null and b/sound/dialogue/animalese/robot/w.wav differ
diff --git a/sound/dialogue/animalese/placeholder/o.wav.import b/sound/dialogue/animalese/robot/w.wav.import
similarity index 52%
rename from sound/dialogue/animalese/placeholder/o.wav.import
rename to sound/dialogue/animalese/robot/w.wav.import
index b094a9f..76cbec4 100644
--- a/sound/dialogue/animalese/placeholder/o.wav.import
+++ b/sound/dialogue/animalese/robot/w.wav.import
@@ -2,13 +2,13 @@
importer="wav"
type="AudioStreamWAV"
-uid="uid://lntkug4avr2k"
-path="res://.godot/imported/o.wav-6c18faa9701875d7753d91afcd1b09e6.sample"
+uid="uid://bfbipoalvo37y"
+path="res://.godot/imported/w.wav-fa8b04d920c43f155d3f4a909a2ee5e6.sample"
[deps]
-source_file="res://sound/dialogue/animalese/placeholder/o.wav"
-dest_files=["res://.godot/imported/o.wav-6c18faa9701875d7753d91afcd1b09e6.sample"]
+source_file="res://sound/dialogue/animalese/robot/w.wav"
+dest_files=["res://.godot/imported/w.wav-fa8b04d920c43f155d3f4a909a2ee5e6.sample"]
[params]
diff --git a/sound/dialogue/animalese/robot/x.wav b/sound/dialogue/animalese/robot/x.wav
new file mode 100644
index 0000000..24e8604
Binary files /dev/null and b/sound/dialogue/animalese/robot/x.wav differ
diff --git a/sound/dialogue/animalese/robot/x.wav.import b/sound/dialogue/animalese/robot/x.wav.import
new file mode 100644
index 0000000..d54fe68
--- /dev/null
+++ b/sound/dialogue/animalese/robot/x.wav.import
@@ -0,0 +1,24 @@
+[remap]
+
+importer="wav"
+type="AudioStreamWAV"
+uid="uid://e4bx33654fbn"
+path="res://.godot/imported/x.wav-ce5e1553c073860351f5d4de3da6af28.sample"
+
+[deps]
+
+source_file="res://sound/dialogue/animalese/robot/x.wav"
+dest_files=["res://.godot/imported/x.wav-ce5e1553c073860351f5d4de3da6af28.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/robot/y.wav b/sound/dialogue/animalese/robot/y.wav
new file mode 100644
index 0000000..414a176
Binary files /dev/null and b/sound/dialogue/animalese/robot/y.wav differ
diff --git a/sound/dialogue/animalese/robot/y.wav.import b/sound/dialogue/animalese/robot/y.wav.import
new file mode 100644
index 0000000..023e8ae
--- /dev/null
+++ b/sound/dialogue/animalese/robot/y.wav.import
@@ -0,0 +1,24 @@
+[remap]
+
+importer="wav"
+type="AudioStreamWAV"
+uid="uid://b3ns0qyyvxqgt"
+path="res://.godot/imported/y.wav-f5e15ad92e6316d22a5263c8296dff17.sample"
+
+[deps]
+
+source_file="res://sound/dialogue/animalese/robot/y.wav"
+dest_files=["res://.godot/imported/y.wav-f5e15ad92e6316d22a5263c8296dff17.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/robot/z.wav b/sound/dialogue/animalese/robot/z.wav
new file mode 100644
index 0000000..243bab2
Binary files /dev/null and b/sound/dialogue/animalese/robot/z.wav differ
diff --git a/sound/dialogue/animalese/robot/z.wav.import b/sound/dialogue/animalese/robot/z.wav.import
new file mode 100644
index 0000000..1ee68f8
--- /dev/null
+++ b/sound/dialogue/animalese/robot/z.wav.import
@@ -0,0 +1,24 @@
+[remap]
+
+importer="wav"
+type="AudioStreamWAV"
+uid="uid://cu8vaak45mcna"
+path="res://.godot/imported/z.wav-bde1bea473ae6ddcfb32432d5206e265.sample"
+
+[deps]
+
+source_file="res://sound/dialogue/animalese/robot/z.wav"
+dest_files=["res://.godot/imported/z.wav-bde1bea473ae6ddcfb32432d5206e265.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
index b383202..7472063 100644
--- a/tests/android_readiness_validation.gd
+++ b/tests/android_readiness_validation.gd
@@ -21,6 +21,7 @@ func _init() -> void:
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(&"character_call", 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
diff --git a/tests/art_tools_validation.gd b/tests/art_tools_validation.gd
index 874cc4e..146fb4e 100644
--- a/tests/art_tools_validation.gd
+++ b/tests/art_tools_validation.gd
@@ -19,54 +19,147 @@ func _run() -> void:
for _frame: int in 8:
await process_frame
assert(bool(main.get("_application_initialized")))
+ assert(bool(main.call(
+ "_apply_world", WorldLayout.STARTER_ISLAND, 1, true
+ )))
+ assert(bool(main.call(
+ "_prepare_host_world", WorldLayout.STARTER_ISLAND, 1
+ )))
assert(bool(main.call("_prepare_private_host")))
var save_manager := main.get("_save_manager") as PlayerSaveManager
- assert(save_manager.initialize_new_game())
+ assert(save_manager.initialize_new_game(1, WorldLayout.STARTER_ISLAND))
main.call("_enter_gameplay")
await physics_frame
await physics_frame
var player := main.get("_player") as Player
+ var session := main.get_node("%NetworkSession") as NetworkSession
+ 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
var game_ui := main.get_node("%GameUI") as GameUI
+ var keyboard_mapping := main.get_node(
+ "%KeyboardMouseMappingManager"
+ ) as KeyboardMouseMappingManager
var toolbar := game_ui.get_node(
"%SurfaceDrawingToolbar"
) as SurfaceDrawingToolbar
var chat_ui := game_ui.get_node("%ChatUI") as ChatUI
assert(player != null and service != null and toolbar != null)
assert(chat_ui != null)
+ assert(keyboard_mapping != null)
+ assert(keyboard_mapping.reset_mapping())
+ await process_frame
+ var shop_prompt_key := game_ui.get_node("%ShopPromptKey") as Label
+ assert(shop_prompt_key.text == "E")
+ var remapped_interact := InputEventKey.new()
+ remapped_interact.physical_keycode = KEY_F
+ remapped_interact.pressed = true
+ assert(keyboard_mapping.set_binding(
+ KeyboardMouseMappingManager.ROLE_INTERACT,
+ keyboard_mapping.binding_from_event(remapped_interact),
+ ))
+ await process_frame
+ assert(shop_prompt_key.text == "F")
+ assert(keyboard_mapping.reset_mapping())
+ await process_frame
+ assert(toolbar.get_parent() == chat_ui.get_parent())
+ assert(
+ toolbar.get_index() > chat_ui.get_index(),
+ "The art-kit toolbar must receive overlapping pointer input before Chat.",
+ )
assert(bool(game_ui.call("_can_start_virtual_mouse")))
+ var virtual_pointer_states: Array[bool] = []
+ game_ui.virtual_pointer_mode_changed.connect(
+ func(active: bool) -> void: virtual_pointer_states.append(active)
+ )
game_ui.call("_begin_virtual_mouse", 0)
var virtual_cursor := game_ui.get_node(
"%ControllerVirtualCursor"
) as ControllerVirtualCursor
assert(virtual_cursor.visible)
+ assert(virtual_pointer_states == [true])
+ # PopupMenu subwindows can consume the physical shoulder release. The
+ # polled primary-button state must still release the synthetic mouse click.
+ game_ui.set("_virtual_mouse_button_mask", MOUSE_BUTTON_MASK_LEFT)
+ game_ui.call("_sync_virtual_mouse_primary_button", false)
+ assert(int(game_ui.get("_virtual_mouse_button_mask")) == 0)
assert(not player.is_camera_input_enabled())
game_ui.call("_end_virtual_mouse")
assert(not virtual_cursor.visible)
+ assert(virtual_pointer_states == [true, false])
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)
+ var chat_dock_selector := settings_panel.get_node(
+ "%ChatDockSelector"
+ ) as OptionButton
+ var chat_mode_selector := settings_panel.get_node(
+ "%ChatModeSelector"
+ ) as OptionButton
+ var paint_dock_selector := settings_panel.get_node(
+ "%PaintDockSelector"
+ ) as OptionButton
+ assert(chat_dock_selector != null)
+ assert(chat_mode_selector != null)
+ assert(paint_dock_selector != null)
+ assert(chat_dock_selector.custom_minimum_size.x == 300.0)
+ assert(chat_mode_selector.custom_minimum_size.x == 300.0)
+ assert(paint_dock_selector.custom_minimum_size.x == 300.0)
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
+ settings_panel.find_children(
+ "*", "BubbleButton", true, false
+ ).is_empty()
)
assert(not chat_ui.is_docked_right())
assert(toolbar.is_docked_right())
@@ -80,11 +173,25 @@ func _run() -> void:
changed_docks.paint_dock_right = false
assert(settings_manager.apply_settings(changed_docks))
await process_frame
+ assert(not chat_ui.is_docked_right())
+ assert(not chat_ui.is_mobile_mode())
+ assert(toolbar.is_docked_right())
+ changed_docks.presentation_layout_customized = true
+ 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)
@@ -94,13 +201,48 @@ func _run() -> void:
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)
+ assert(reloaded_settings.current_settings.presentation_layout_customized)
reloaded_settings.queue_free()
+ var legacy_data: Dictionary = JSON.parse_string(
+ FileAccess.get_file_as_string(PlayerSettingsManager.SETTINGS_PATH)
+ ) as Dictionary
+ var legacy_presentation := legacy_data.get("presentation", {}) as Dictionary
+ legacy_presentation.erase("layout_customized")
+ legacy_data["presentation"] = legacy_presentation
+ var legacy_file := FileAccess.open(
+ PlayerSettingsManager.SETTINGS_PATH,
+ FileAccess.WRITE,
+ )
+ assert(legacy_file != null)
+ legacy_file.store_string(JSON.stringify(legacy_data, "\t"))
+ legacy_file.close()
+ var migrated_legacy_settings := PlayerSettingsManager.new()
+ root.add_child(migrated_legacy_settings)
+ assert(migrated_legacy_settings.load_settings())
+ assert(not migrated_legacy_settings.current_settings.chat_dock_right)
+ assert(not migrated_legacy_settings.current_settings.chat_mobile_mode)
+ assert(migrated_legacy_settings.current_settings.paint_dock_right)
+ assert(
+ not migrated_legacy_settings.current_settings.presentation_layout_customized
+ )
+ migrated_legacy_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())
@@ -109,6 +251,7 @@ func _run() -> void:
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(
@@ -120,7 +263,60 @@ func _run() -> void:
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)
+ var ui_pixelation := main.get("_ui_pixelation") as UIPixelationPresenter
+ assert(ui_pixelation != null)
+ var on_screen_keyboard := ui_pixelation.get(
+ "_on_screen_keyboard"
+ ) as OnScreenKeyboard
+ assert(on_screen_keyboard != null)
+ if DisplayServer.has_feature(DisplayServer.FEATURE_VIRTUAL_KEYBOARD):
+ assert(typed_chat_entry.virtual_keyboard_enabled)
+ assert(not on_screen_keyboard.is_open())
+ await process_frame
+ assert(typed_chat_entry.has_focus())
+ else:
+ assert(not typed_chat_entry.virtual_keyboard_enabled)
+ assert(on_screen_keyboard.is_open())
+
+ # A bite temporarily owns gameplay input without discarding an in-progress
+ # message. The draft and insertion point return only after fishing releases
+ # that ownership, and any controller keyboard follows the restored field.
+ var chat_fishing_spot := main.get_node("%FishingSpot") as FishingSpot
+ assert(chat_fishing_spot != null)
+ var interrupted_draft := "finish this message after fishing"
+ var interrupted_caret: int = 12
+ typed_chat_entry.text = interrupted_draft
+ typed_chat_entry.caret_column = interrupted_caret
+ chat_fishing_spot.call("_set_fishing_input_priority", true)
+ await process_frame
+ assert(not chat_ui.is_open())
+ assert(not bool(chat_ui.get("_input_lock_applied")))
+ assert(chat_ui.has_fishing_resume_pending())
+ assert(not on_screen_keyboard.is_open())
+ chat_ui.open_chat()
+ assert(not chat_ui.is_open())
+ chat_fishing_spot.call("_set_fishing_input_priority", false)
+ for _frame: int in 4:
+ await process_frame
+ assert(chat_ui.is_open())
+ assert(typed_chat_entry.text == interrupted_draft)
+ assert(typed_chat_entry.caret_column == interrupted_caret)
+ assert(bool(chat_ui.get("_input_lock_applied")))
+ if not DisplayServer.has_feature(DisplayServer.FEATURE_VIRTUAL_KEYBOARD):
+ assert(on_screen_keyboard.is_open())
+ on_screen_keyboard.call("_close_keyboard", true)
+ assert(typed_chat_entry.has_focus())
+
+ # A deferred Standby bite has not started the fishing minigame yet. It must
+ # release fishing's input ownership so chat remains available until the
+ # player confirms the bite.
+ chat_fishing_spot.call("_set_fishing_input_priority", true)
+ chat_fishing_spot.call("_set_bite_confirmation_pending", true)
+ for _frame: int in 4:
+ await process_frame
+ assert(not chat_fishing_spot.is_fishing_input_priority_active())
+ assert(chat_ui.is_open())
+ chat_fishing_spot.call("_set_bite_confirmation_pending", false)
var left_bumper := InputEventJoypadButton.new()
left_bumper.button_index = JOY_BUTTON_LEFT_SHOULDER
left_bumper.pressed = true
@@ -129,16 +325,162 @@ func _run() -> void:
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(chat_ui.is_open())
- 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()
+ chat_ui.open_chat()
+ assert(chat_ui.is_open())
+ typed_chat_entry.text = "validation message"
+ chat_ui.call("_send")
+ assert(not chat_ui.is_open())
+ assert(not typed_chat_entry.has_focus())
+ assert(not bool(chat_ui.get("_input_lock_applied")))
+ assert(not chat_status.visible)
+ assert(chat_status.text.is_empty())
+ 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 == [&"chat", &"freecam", &"hud"])
+ game_ui.call("_on_quick_action_selected", &"freecam")
+ assert(player.is_free_camera_active())
+ var free_camera := player.get_active_gameplay_camera()
+ assert(free_camera.name == &"FreeCamera")
+ var free_camera_yaw_before: float = free_camera.global_rotation.y
+ var mapping_manager := main.get(
+ "_controller_mapping_manager"
+ ) as ControllerMappingManager
+ var right_stick_motion := InputEventJoypadMotion.new()
+ right_stick_motion.device = mapping_manager.get_active_device_id()
+ right_stick_motion.axis = JOY_AXIS_RIGHT_X
+ right_stick_motion.axis_value = 0.9
+ Input.parse_input_event(right_stick_motion)
+ await create_timer(0.1).timeout
+ assert(
+ absf(angle_difference(
+ free_camera.global_rotation.y,
+ free_camera_yaw_before,
+ )) > 0.02
+ )
+ right_stick_motion.axis_value = 0.0
+ Input.parse_input_event(right_stick_motion)
+ await process_frame
+
+ var free_camera_body := player.get("_free_camera_body") as CharacterBody3D
+ assert(free_camera_body != null)
+ # A released fishing cast locks only the avatar. The detached camera must
+ # continue to accept translation while that movement lock is active.
+ player.set_movement_enabled(false)
+ var cast_locked_camera_position: Vector3 = free_camera_body.global_position
+ Input.action_press("move_forward")
+ for _frame: int in 3:
+ await physics_frame
+ Input.action_release("move_forward")
+ assert(not free_camera_body.global_position.is_equal_approx(
+ cast_locked_camera_position
+ ))
+ player.set_movement_enabled(true)
+
+ # Chat owns all movement while text entry is active, including developer
+ # freecam movement which reads the same WASD actions as the player.
+ chat_ui.open_chat()
+ assert(chat_ui.is_open())
+ var free_camera_position_before: Vector3 = free_camera_body.global_position
+ Input.action_press("move_forward")
+ for _frame: int in 3:
+ await physics_frame
+ Input.action_release("move_forward")
+ assert(free_camera_body.global_position.is_equal_approx(
+ free_camera_position_before
+ ))
+ chat_ui.refocus_gameplay()
+
+ # Typewriter speech cycles the shared open-mouth faceplates, then restores
+ # the player's authored mouth. World speech also follows the active freecam,
+ # not the inactive normal gameplay camera.
+ var normal_camera := player.get_gameplay_camera()
+ var normal_camera_transform: Transform3D = normal_camera.global_transform
+ var chat_anchor: Vector3 = player.get_chat_anchor_position()
+ normal_camera.look_at(
+ normal_camera.global_position
+ - (chat_anchor - normal_camera.global_position),
+ Vector3.UP,
+ )
+ assert(normal_camera.is_position_behind(chat_anchor))
+ assert(not free_camera.is_position_behind(chat_anchor))
+ var shop_prompt := game_ui.get_node("%ShopPrompt") as Control
+ var storage_prompt := game_ui.get_node("%StoragePrompt") as Control
+ game_ui.set_shop_prompt_visible(true, chat_anchor)
+ game_ui.set_storage_prompt_visible(true, chat_anchor)
+ assert(shop_prompt.visible)
+ assert(storage_prompt.visible)
+ chat_ui.show_local_speech("hello there friend")
+ await process_frame
+ assert(bool(player.get("_speech_mouth_active")))
+ var first_speech_mouth: String = str(player.get("_speech_mouth_id"))
+ await create_timer(0.12).timeout
+ var second_speech_mouth: String = str(player.get("_speech_mouth_id"))
+ assert(not first_speech_mouth.is_empty())
+ assert(not second_speech_mouth.is_empty())
+ assert(first_speech_mouth != second_speech_mouth)
+ chat_ui.call("_update_speech")
+ var speech: Dictionary = chat_ui.get("_speech")
+ var local_speech: Dictionary = speech.get(session.get_local_peer_id(), {})
+ var speech_bubble := local_speech.get("bubble") as PanelContainer
+ assert(speech_bubble != null and speech_bubble.visible)
+ normal_camera.global_transform = normal_camera_transform
+ await create_timer(0.6).timeout
+ assert(not bool(player.get("_speech_mouth_active")))
+ chat_ui.call("_on_peer_removed", session.get_local_peer_id())
+
+ 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)
+ var hud_fishing_spot := main.get_node("%FishingSpot") as FishingSpot
+ assert(hud_fishing_spot != null)
+ hud_fishing_spot.state = FishingSpot.FishingState.SHOWING_CATCH
+ game_ui.call("_refresh_gameplay_hud_visibility")
+ assert((
+ game_ui.get_node("%GameplayTransientHUD") as Control
+ ).visible)
+ assert(not (
+ game_ui.get_node("%ExperiencePresentation") as Control
+ ).visible)
+ assert(chat_ui.is_hud_hidden())
+ hud_fishing_spot.state = FishingSpot.FishingState.READY
+ game_ui.call("_refresh_gameplay_hud_visibility")
+ assert(not (
+ game_ui.get_node("%GameplayTransientHUD") 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
@@ -147,26 +489,41 @@ func _run() -> void:
start_button.button_index = JOY_BUTTON_START
start_button.pressed = true
assert(bool(main.call("_is_pause_open_request", start_button)))
+ await _validate_pause_browser_transition(game_ui, player)
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()
- for _frame: int in 12:
- await process_frame
+ 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)
+ chat_ui.open_chat()
+ assert(chat_ui.is_open())
+ assert(not service.is_active() and not toolbar.visible)
+ chat_ui.close_chat()
+ assert(not chat_ui.is_open())
+ assert(service.is_active() and toolbar.visible)
var ui_root := game_ui.get_node("%UIRoot") as Control
assert(toolbar.get_parent() == ui_root)
assert(is_zero_approx(toolbar.position.y))
@@ -180,6 +537,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))
@@ -204,7 +563,8 @@ 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
+ var export_button := toolbar.get_node("%ExportButton") as Button
+ var stamp_button := toolbar.get_node("%StampButton") as Button
assert(
mode_button != null
and eraser_button != null
@@ -212,36 +572,234 @@ func _run() -> void:
and hide_button != null
and restore_button != null
and finalize_button != null
- and close_button != null
+ and export_button != null
+ and stamp_button != null
)
+ assert(export_button.text == "png")
+ assert(export_button.tooltip_text == "aim at artwork, then click to export PNG")
+ assert(stamp_button.text == "stamp")
+ assert(stamp_button.disabled)
+ 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,
+ export_button,
+ stamp_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(export_button.get_index() > finalize_button.get_index())
+ assert(stamp_button.get_index() > export_button.get_index())
+ assert(mode_button.get_index() > stamp_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)
+ for pointer_only_control: Control in [
+ brush_option,
+ grid_option,
+ mode_button,
+ eraser_button,
+ undo_button,
+ hide_button,
+ restore_button,
+ finalize_button,
+ export_button,
+ stamp_button,
+ ]:
+ assert(pointer_only_control.focus_mode == Control.FOCUS_NONE)
+ assert(brush_option.get_popup().unfocusable)
+ assert(grid_option.get_popup().unfocusable)
+ brush_option.get_popup().popup(Rect2i(100, 100, 220, 220))
+ await process_frame
+ game_ui.call("_begin_virtual_mouse", 0)
+ game_ui.call("_update_virtual_cursor_position", Vector2(160.0, 160.0))
+ var brush_popup_cursor := toolbar.get(
+ "_brush_popup_cursor"
+ ) as ControllerVirtualCursor
+ assert(brush_popup_cursor != null and brush_popup_cursor.visible)
+ assert(brush_popup_cursor.get_parent() == brush_option.get_popup())
+ assert(brush_popup_cursor.z_index == RenderingServer.CANVAS_ITEM_Z_MAX)
+ assert(not virtual_cursor.visible)
+ brush_option.get_popup().hide()
+ game_ui.call("_update_virtual_cursor_position", Vector2(160.0, 160.0))
+ assert(not brush_popup_cursor.visible)
+ assert(virtual_cursor.visible)
+ game_ui.call("_end_virtual_mouse")
+ var expected_brush_icons: Array[String] = [
+ "art_kit_marker_tip_fine.png",
+ "art_kit_marker_tip_thin.png",
+ "art_kit_marker_tip_mid.png",
+ "art_kit_marker_tip_thick.png",
+ ]
+ var expected_grid_icons: Array[String] = [
+ "art_kit_grid_small_light.png",
+ "art_kit_grid_medium_light.png",
+ "art_kit_grid_large_light.png",
+ "art_kit_grid_xl_light.png",
+ ]
+ for index: int in 4:
+ assert(brush_option.get_item_text(index).is_empty())
+ assert(grid_option.get_item_text(index).is_empty())
+ assert(brush_option.get_item_icon(index) != null)
+ assert(grid_option.get_item_icon(index) != null)
+ var brush_unlocked: bool = index == 0
+ var grid_unlocked: bool = index == 0
+ if brush_unlocked:
+ assert(
+ str(brush_option.get_item_icon(index).get_meta(
+ &"channel_mask_source", ""
+ )).ends_with(
+ expected_brush_icons[index]
+ )
+ )
+ else:
+ assert(bool(brush_option.get_item_icon(index).get_meta(
+ &"locked_content_icon", false
+ )))
+ if grid_unlocked:
+ assert(
+ grid_option.get_item_icon(index).resource_path.ends_with(
+ expected_grid_icons[index]
+ )
+ )
+ else:
+ assert(bool(grid_option.get_item_icon(index).get_meta(
+ &"locked_content_icon", false
+ )))
+ var expected_lock_alpha: float = 1.0 if index == 0 else 0.72
+ assert(is_equal_approx(
+ brush_option.get_popup().get_item_icon_modulate(index).a,
+ expected_lock_alpha,
+ ))
+ assert(is_equal_approx(
+ grid_option.get_popup().get_item_icon_modulate(index).a,
+ expected_lock_alpha,
+ ))
assert(not brush_option.get_popup().is_item_disabled(0))
assert(brush_option.get_popup().is_item_disabled(1))
assert(not grid_option.get_popup().is_item_disabled(0))
assert(grid_option.get_popup().is_item_disabled(1))
var color_buttons: Dictionary = toolbar.get("_color_buttons")
assert(color_buttons.size() == SurfaceDrawingPalette.COLORS.size())
+ for color_button: Button in color_buttons.values():
+ assert(color_button.focus_mode == Control.FOCUS_NONE)
assert(not (color_buttons[&"chalk_white"] as Button).disabled)
assert((color_buttons[&"ocean_teal"] as Button).disabled)
+ var base_color_lock := (
+ (color_buttons[&"chalk_white"] as Button).get_node(
+ "UnlockStateIcon"
+ ) as TextureRect
+ )
+ var locked_color_lock := (
+ (color_buttons[&"ocean_teal"] as Button).get_node(
+ "UnlockStateIcon"
+ ) as TextureRect
+ )
+ assert(not base_color_lock.visible)
+ assert(locked_color_lock.visible)
+ assert(locked_color_lock.size == Vector2(18.0, 18.0))
+ assert(is_equal_approx(locked_color_lock.modulate.a, 0.34))
+ var popup_lock_icon := toolbar.get("_popup_lock_icon") as Texture2D
+ assert(popup_lock_icon.get_width() == 40)
+ assert(popup_lock_icon.get_height() == 40)
+ assert(int(popup_lock_icon.get_meta(
+ &"locked_content_icon_size", 0
+ )) == 24)
for product_id: StringName in [
- &"marker_ocean_teal", &"brush_4x", &"grid_128x",
+ &"marker_ocean_teal",
+ &"brush_4x",
+ &"grid_128x",
+ PlayerArtUnlocks.STAMP_PRODUCT_ID,
]:
assert(player.art_unlocks.unlock_product(product_id))
await process_frame
assert(not (color_buttons[&"ocean_teal"] as Button).disabled)
assert(not brush_option.get_popup().is_item_disabled(3))
assert(not grid_option.get_popup().is_item_disabled(3))
+ assert(not stamp_button.disabled)
+ var stamp_library_panel := toolbar.get_node(
+ "%StampLibraryPanel"
+ ) as PanelContainer
+ var stamp_library_close := toolbar.get_node(
+ "%StampLibraryClose"
+ ) as Button
+ assert(stamp_library_panel != null and not stamp_library_panel.visible)
+ stamp_button.pressed.emit()
+ assert(stamp_library_panel.visible)
+ var library_pointer := InputEventMouseMotion.new()
+ library_pointer.position = stamp_library_panel.get_global_rect().get_center()
+ assert(toolbar.owns_pointer_event(library_pointer))
+ stamp_library_close.pressed.emit()
+ assert(not stamp_library_panel.visible)
+ assert(not locked_color_lock.visible)
+ assert(str(brush_option.get_item_icon(3).get_meta(
+ &"channel_mask_source", ""
+ )).ends_with(expected_brush_icons[3]))
+ assert(grid_option.get_item_icon(3).resource_path.ends_with(
+ expected_grid_icons[3]
+ ))
+ assert(is_equal_approx(
+ brush_option.get_popup().get_item_icon_modulate(3).a, 1.0
+ ))
+ assert(is_equal_approx(
+ grid_option.get_popup().get_item_icon_modulate(3).a, 1.0
+ ))
assert(service.set_color_id(&"ocean_teal"))
assert(service.set_brush_size(4))
assert(service.set_grid_size(128))
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(brush_option.material == null)
+ assert(grid_option.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)
@@ -256,64 +814,214 @@ 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))
- player.hotbar.select_slot(0)
var fishing_spot := main.get_node("%FishingSpot") as FishingSpot
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)
- player.hotbar.select_slot(1)
+ 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)
- assert(service.handle_input(paint_key, true))
+ hotbar_ui.set_swap_hotbar_camera_scroll(true)
+ hotbar_ui._unhandled_input(wheel_down)
+ await process_frame
+ assert(player.hotbar.get_selected_slot() == 1)
+ var shifted_wheel_down := InputEventMouseButton.new()
+ shifted_wheel_down.button_index = MOUSE_BUTTON_WHEEL_DOWN
+ shifted_wheel_down.pressed = true
+ shifted_wheel_down.shift_pressed = true
+ hotbar_ui._unhandled_input(shifted_wheel_down)
+ await process_frame
+ assert(player.hotbar.get_selected_slot() == 2)
+ hotbar_ui.set_swap_hotbar_camera_scroll(false)
+
+ 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)
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()
+
+
+func _validate_pause_browser_transition(
+ game_ui: GameUI,
+ player: Player,
+) -> void:
+ var pause_menu := game_ui.get_pause_menu()
+ var root_page := pause_menu.get_node("%RootPage") as Control
+ var join_page := pause_menu.get_node("%JoinGamePage") as JoinGamePage
+ pause_menu.open_menu()
+ while bool(pause_menu.get("_root_transition_active")):
+ await process_frame
+ assert(pause_menu.visible and root_page.visible)
+ assert(not bool(player.call("_is_movement_input_enabled")))
+
+ pause_menu.call("_open_join_game")
+ while not join_page.visible:
+ await process_frame
+ assert(bool(pause_menu.get("_root_transition_active")))
+ pause_menu.call("_close_join_game")
+ assert(not join_page.visible)
+ # This is the issue #73 input: Escape arrives while the root is returning.
+ assert(pause_menu.handle_escape())
+ while bool(pause_menu.get("_root_transition_active")):
+ await process_frame
+ assert(pause_menu.visible and root_page.visible)
+ assert(not bool(player.call("_is_movement_input_enabled")))
+
+ assert(pause_menu.handle_escape())
+ while pause_menu.visible:
+ await process_frame
+ assert(player.is_movement_enabled())
+
+ # Fishing can complete while pause owns local input. Closing pause must only
+ # release its own lock, not restore the stale movement value from open time.
+ player.set_movement_enabled(false)
+ pause_menu.open_menu()
+ while bool(pause_menu.get("_root_transition_active")):
+ await process_frame
+ assert(not bool(player.call("_is_movement_input_enabled")))
+ player.set_movement_enabled(true)
+ assert(player.is_movement_enabled())
+ assert(not bool(player.call("_is_movement_input_enabled")))
+ pause_menu.close_menu(PauseMenu.CloseReason.USER_RETURN)
+ assert(player.is_movement_enabled())
+ assert(bool(player.call("_is_movement_input_enabled")))
+
+ # Opening pause during a right-click camera drag must not snapshot and later
+ # restore the drag's transient captured mouse mode after the drag is cancelled.
+ assert(
+ pause_menu.call(
+ "_get_restorable_mouse_mode",
+ Input.MOUSE_MODE_CAPTURED,
+ ) == Input.MOUSE_MODE_VISIBLE
+ )
+ Input.mouse_mode = Input.MOUSE_MODE_VISIBLE
+ var drag_press := InputEventMouseButton.new()
+ drag_press.button_index = MOUSE_BUTTON_RIGHT
+ drag_press.button_mask = MOUSE_BUTTON_MASK_RIGHT
+ drag_press.pressed = true
+ player.call("_input", drag_press)
+ assert(bool(player.get("_camera_dragging")))
+ var camera_yaw := player.get_node("%CameraYaw") as Node3D
+ var yaw_before_drag: float = camera_yaw.rotation.y
+ var drag_motion := InputEventMouseMotion.new()
+ drag_motion.button_mask = MOUSE_BUTTON_MASK_RIGHT
+ drag_motion.relative = Vector2(24.0, -8.0)
+ drag_motion.screen_relative = drag_motion.relative
+ player.call("_input", drag_motion)
+ assert(not is_equal_approx(camera_yaw.rotation.y, yaw_before_drag))
+ await process_frame
+ assert(bool(player.get("_camera_dragging")))
+ if DisplayServer.get_name() != "headless":
+ assert(Input.mouse_mode == Input.MOUSE_MODE_CAPTURED)
+ var drag_release := InputEventMouseButton.new()
+ drag_release.button_index = MOUSE_BUTTON_RIGHT
+ drag_release.button_mask = 0
+ drag_release.pressed = false
+ player.call("_input", drag_release)
+ assert(not bool(player.get("_camera_dragging")))
+ player.call("_input", drag_press)
+ assert(bool(player.get("_camera_dragging")))
+ pause_menu.open_menu()
+ while bool(pause_menu.get("_root_transition_active")):
+ await process_frame
+ assert(not bool(player.get("_camera_dragging")))
+ assert(Input.mouse_mode == Input.MOUSE_MODE_VISIBLE)
+ pause_menu.close_menu(PauseMenu.CloseReason.USER_RETURN)
+ assert(Input.mouse_mode == Input.MOUSE_MODE_VISIBLE)
diff --git a/tests/camera_drag_validation.gd b/tests/camera_drag_validation.gd
new file mode 100644
index 0000000..a2dd410
--- /dev/null
+++ b/tests/camera_drag_validation.gd
@@ -0,0 +1,80 @@
+extends SceneTree
+
+const PlayerScene: PackedScene = preload("res://player/player.tscn")
+
+
+func _initialize() -> void:
+ call_deferred("_run")
+
+
+func _run() -> void:
+ root.size = Vector2i(1280, 720)
+ var player := PlayerScene.instantiate() as Player
+ root.add_child(player)
+ await process_frame
+ player.local_control_enabled = true
+ player.set_camera_input_enabled(true)
+ Input.mouse_mode = Input.MOUSE_MODE_VISIBLE
+
+ var press := InputEventMouseButton.new()
+ press.button_index = MOUSE_BUTTON_RIGHT
+ press.button_mask = MOUSE_BUTTON_MASK_RIGHT
+ press.pressed = true
+ Input.parse_input_event(press)
+ await process_frame
+ assert(bool(player.get("_camera_dragging")))
+ if DisplayServer.get_name() != "headless":
+ assert(Input.mouse_mode == Input.MOUSE_MODE_CAPTURED)
+
+ var yaw := player.get_node("%CameraYaw") as Node3D
+ var previous_yaw: float = yaw.rotation.y
+ var motion := InputEventMouseMotion.new()
+ motion.button_mask = MOUSE_BUTTON_MASK_RIGHT
+ motion.relative = Vector2(18.0, -4.0)
+ motion.screen_relative = motion.relative
+ Input.parse_input_event(motion)
+ await process_frame
+ assert(not is_equal_approx(yaw.rotation.y, previous_yaw))
+ assert(bool(player.get("_camera_dragging")))
+ if DisplayServer.get_name() != "headless":
+ assert(Input.mouse_mode == Input.MOUSE_MODE_CAPTURED)
+
+ var release := InputEventMouseButton.new()
+ release.button_index = MOUSE_BUTTON_RIGHT
+ release.pressed = false
+ Input.parse_input_event(release)
+ await process_frame
+ assert(not bool(player.get("_camera_dragging")))
+ if DisplayServer.get_name() != "headless":
+ assert(Input.mouse_mode == Input.MOUSE_MODE_VISIBLE)
+
+ Input.parse_input_event(press)
+ await process_frame
+ assert(bool(player.get("_camera_dragging")))
+ player.notification(NOTIFICATION_APPLICATION_FOCUS_OUT)
+ assert(not bool(player.get("_camera_dragging")))
+
+ var plain_wheel_down := InputEventMouseButton.new()
+ plain_wheel_down.button_index = MOUSE_BUTTON_WHEEL_DOWN
+ plain_wheel_down.pressed = true
+ var shifted_wheel_down := InputEventMouseButton.new()
+ shifted_wheel_down.button_index = MOUSE_BUTTON_WHEEL_DOWN
+ shifted_wheel_down.pressed = true
+ shifted_wheel_down.shift_pressed = true
+ player.apply_camera_settings(0.005, 2.5, false, false)
+ var default_zoom: float = float(player.get("_target_zoom"))
+ player._unhandled_input(plain_wheel_down)
+ assert(is_equal_approx(float(player.get("_target_zoom")), default_zoom))
+ player._unhandled_input(shifted_wheel_down)
+ assert(float(player.get("_target_zoom")) > default_zoom)
+ player.apply_camera_settings(0.005, 2.5, false, true)
+ var swapped_zoom: float = float(player.get("_target_zoom"))
+ player._unhandled_input(shifted_wheel_down)
+ assert(is_equal_approx(float(player.get("_target_zoom")), swapped_zoom))
+ player._unhandled_input(plain_wheel_down)
+ assert(float(player.get("_target_zoom")) > swapped_zoom)
+
+ player.queue_free()
+ await process_frame
+ print("Camera drag validation: PASS")
+ quit()
diff --git a/tests/camera_drag_validation.gd.uid b/tests/camera_drag_validation.gd.uid
new file mode 100644
index 0000000..a37fad9
--- /dev/null
+++ b/tests/camera_drag_validation.gd.uid
@@ -0,0 +1 @@
+uid://dku3kyarg3fxu
diff --git a/tests/character_rig_validation.gd b/tests/character_rig_validation.gd
new file mode 100644
index 0000000..2aece2b
--- /dev/null
+++ b/tests/character_rig_validation.gd
@@ -0,0 +1,139 @@
+extends SceneTree
+
+
+const CHARACTER_SCENE: PackedScene = preload(
+ "res://art/exported/characters/base/straywild_base_character.glb"
+)
+
+const EXPECTED_ANIMATIONS: Array[StringName] = [
+ &"casting",
+ &"casting_sit",
+ &"draw",
+ &"fighting",
+ &"fighting_sit",
+ &"fishing",
+ &"fishing_sit",
+ &"idle",
+ &"idle_show",
+ &"idle_sit",
+ &"idle_sit_show",
+ &"idle_sneak",
+ &"pocket_idle_idle",
+ &"pocket_idle_show",
+ &"pocket_show_idle",
+ &"pocket_show_show",
+ &"pocket_sit_idle_idle",
+ &"pocket_sit_idle_show",
+ &"pocket_sit_show_idle",
+ &"pocket_sit_show_show",
+ &"pocket_walking_idle_idle",
+ &"pocket_walking_idle_show",
+ &"pocket_walking_show_idle",
+ &"pocket_walking_show_show",
+ &"release",
+ &"release_sit",
+ &"retract",
+ &"retract_sit",
+ &"running",
+ &"running_show",
+ &"sneaking",
+ &"strike",
+ &"walking",
+ &"walking_show",
+]
+
+
+func _initialize() -> void:
+ call_deferred("_run")
+
+
+func _run() -> void:
+ var character := CHARACTER_SCENE.instantiate()
+ root.add_child(character)
+ var skeleton := _find_first(character, "Skeleton3D") as Skeleton3D
+ var animation_player := _find_first(
+ character,
+ "AnimationPlayer",
+ ) as AnimationPlayer
+ assert(skeleton != null)
+ assert(animation_player != null)
+
+ var socket_index := skeleton.find_bone("rod_socket")
+ assert(socket_index >= 0)
+ var socket_direction_index := skeleton.find_bone("rod_socket.001")
+ assert(socket_direction_index >= 0)
+ assert(skeleton.get_bone_parent(socket_direction_index) == socket_index)
+ var animation_names := animation_player.get_animation_list()
+ assert(animation_names.size() == EXPECTED_ANIMATIONS.size())
+ for expected in EXPECTED_ANIMATIONS:
+ assert(expected in animation_names, "Missing animation: %s" % expected)
+ var animation := animation_player.get_animation(expected)
+ if expected in [&"casting", &"draw", &"idle_sneak", &"sneaking", &"strike"]:
+ var has_socket_track: bool = false
+ for track_index in animation.get_track_count():
+ has_socket_track = has_socket_track or String(
+ animation.track_get_path(track_index)
+ ).contains("rod_socket")
+ assert(
+ has_socket_track,
+ "Animation %s lost its authored rod socket pose." % expected,
+ )
+
+ var arms := character.find_child(
+ "body_arms",
+ true,
+ false,
+ ) as MeshInstance3D
+ assert(arms != null)
+ var blended_arm_vertices := _validate_mesh_weights(arms)
+ assert(blended_arm_vertices > 0, "body_arms has no blended joint weights.")
+ var body := character.find_child(
+ "body_main",
+ true,
+ false,
+ ) as MeshInstance3D
+ assert(body != null)
+ _validate_mesh_weights(body)
+
+ print(
+ "Character rig validation: PASS "
+ + "(animations=%d, blended arm vertices=%d)"
+ % [EXPECTED_ANIMATIONS.size(), blended_arm_vertices]
+ )
+ character.queue_free()
+ quit()
+
+
+func _validate_mesh_weights(mesh_instance: MeshInstance3D) -> int:
+ var blended_vertices := 0
+ for surface_index in mesh_instance.mesh.get_surface_count():
+ var arrays := mesh_instance.mesh.surface_get_arrays(surface_index)
+ var weights: PackedFloat32Array = arrays[Mesh.ARRAY_WEIGHTS]
+ var vertices: PackedVector3Array = arrays[Mesh.ARRAY_VERTEX]
+ assert(not weights.is_empty())
+ assert(not vertices.is_empty())
+ var components_per_vertex := weights.size() / vertices.size()
+ assert(components_per_vertex == 4 or components_per_vertex == 8)
+ for offset in range(0, weights.size(), components_per_vertex):
+ var total := 0.0
+ var influence_count := 0
+ for component in range(components_per_vertex):
+ var weight := weights[offset + component]
+ total += weight
+ if weight > 0.000001:
+ influence_count += 1
+ assert(absf(total - 1.0) <= 0.0001)
+ assert(influence_count <= 2)
+ if influence_count == 2:
+ blended_vertices += 1
+ return blended_vertices
+
+
+func _find_first(parent: Node, class_name_to_find: String) -> Node:
+ if parent.is_class(class_name_to_find):
+ return parent
+ for child in parent.get_children():
+ var found := _find_first(child, class_name_to_find)
+ if found != null:
+ return found
+ return null
diff --git a/tests/character_rig_validation.gd.uid b/tests/character_rig_validation.gd.uid
new file mode 100644
index 0000000..f7bda09
--- /dev/null
+++ b/tests/character_rig_validation.gd.uid
@@ -0,0 +1 @@
+uid://di6r1gphajq8u
diff --git a/tests/character_scale_validation.gd b/tests/character_scale_validation.gd
index 1cc988c..213f64f 100644
--- a/tests/character_scale_validation.gd
+++ b/tests/character_scale_validation.gd
@@ -47,6 +47,10 @@ func _run() -> void:
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))
diff --git a/tests/chat_privacy_multiplayer_validation.gd b/tests/chat_privacy_multiplayer_validation.gd
new file mode 100644
index 0000000..e1d6f87
--- /dev/null
+++ b/tests/chat_privacy_multiplayer_validation.gd
@@ -0,0 +1,124 @@
+extends SceneTree
+
+const MainScene: PackedScene = preload("res://main/main.tscn")
+const TEST_PORT: int = 18152
+const PREJOIN_MESSAGE: String = "private prejoin message"
+const LIVE_MESSAGE: String = "live message after join"
+
+
+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("Chat privacy 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(bool(main.call(
+ "_apply_world", WorldLayout.STARTER_ISLAND, 1, true
+ )))
+ assert(session.set_host_world(WorldLayout.STARTER_ISLAND, 1))
+ 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 chat_service := main.get_node(
+ "%NetworkChatService"
+ ) as NetworkChatService
+ assert(chat_service.send_local_message(PREJOIN_MESSAGE))
+ var remote_peer_id: int = await _wait_for_remote_peer(session)
+ assert(remote_peer_id > 1)
+ await create_timer(1.0).timeout
+ assert(chat_service.send_local_message(LIVE_MESSAGE))
+ 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("Chat privacy multiplayer host validation: PASS")
+ await _session_cleanup(main, session)
+
+
+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 chat_service := main.get_node(
+ "%NetworkChatService"
+ ) as NetworkChatService
+ await create_timer(0.5).timeout
+ assert(not _history_contains(chat_service, PREJOIN_MESSAGE))
+ var live_deadline: int = Time.get_ticks_msec() + 8000
+ while (
+ Time.get_ticks_msec() < live_deadline
+ and not _history_contains(chat_service, LIVE_MESSAGE)
+ ):
+ await process_frame
+ assert(_history_contains(chat_service, LIVE_MESSAGE))
+ assert(not _history_contains(chat_service, PREJOIN_MESSAGE))
+ print("Chat privacy multiplayer client validation: PASS")
+ await _session_cleanup(main, session)
+
+
+func _history_contains(service: NetworkChatService, body: String) -> bool:
+ return service.get_history().any(
+ func(message: Dictionary) -> bool:
+ return str(message.get("body", "")) == body
+ )
+
+
+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 _session_cleanup(main: Node, session: NetworkSession) -> void:
+ session.disconnect_session("")
+ main.queue_free()
+ for _frame: int in 4:
+ await process_frame
+ await create_timer(0.1).timeout
+ quit()
diff --git a/tests/chat_privacy_multiplayer_validation.gd.uid b/tests/chat_privacy_multiplayer_validation.gd.uid
new file mode 100644
index 0000000..33b210d
--- /dev/null
+++ b/tests/chat_privacy_multiplayer_validation.gd.uid
@@ -0,0 +1 @@
+uid://ivtqrnppnmli
diff --git a/tests/controller_focus_presentation_validation.gd b/tests/controller_focus_presentation_validation.gd
index 638e0cc..7f59f70 100644
--- a/tests/controller_focus_presentation_validation.gd
+++ b/tests/controller_focus_presentation_validation.gd
@@ -3,6 +3,9 @@ extends SceneTree
const FocusPresentationType = preload(
"res://ui/controller_focus_presentation.gd"
)
+const FishBatchSelectionType = preload(
+ "res://ui/fish_batch_selection.gd"
+)
var _failures: Array[String] = []
@@ -12,6 +15,8 @@ func _initialize() -> void:
func _run() -> void:
+ _validate_controller_cooler_focus()
+ root.size = Vector2i(1280, 720)
var stage := Control.new()
stage.set_anchors_and_offsets_preset(Control.PRESET_FULL_RECT)
root.add_child(stage)
@@ -19,45 +24,383 @@ func _run() -> void:
stage.add_child(presentation)
var standard_button := Button.new()
standard_button.text = "standard"
+ standard_button.position = Vector2(80.0, 60.0)
standard_button.custom_minimum_size = Vector2(120.0, 60.0)
+ var normal_style := _button_style(Color("123f4e"))
+ var old_hover_style := _button_style(Color("58c6d4"))
+ var old_focus_style := _button_style(Color("238697"))
+ standard_button.add_theme_stylebox_override("normal", normal_style)
+ standard_button.add_theme_stylebox_override("hover", old_hover_style)
+ standard_button.add_theme_stylebox_override("focus", old_focus_style)
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)
+ var second_button := Button.new()
+ second_button.text = "second"
+ second_button.position = Vector2(260.0, 60.0)
+ second_button.custom_minimum_size = Vector2(120.0, 60.0)
+ # Legacy opt-outs must not prevent the new universal cursor from appearing.
+ second_button.set_meta(&"controller_focus_inversion_disabled", true)
+ stage.add_child(second_button)
await process_frame
+ standard_button.grab_focus()
+ await process_frame
+ _expect(
+ root.gui_get_focus_owner() == null,
+ "a menu's programmatic first focus is visible before navigation input",
+ )
+ _expect(
+ presentation.get("_neutral_focus_seed") == standard_button,
+ "the neutral menu focus seed did not remember the first action",
+ )
+ var player_menu_tab := InputEventKey.new()
+ player_menu_tab.physical_keycode = KEY_TAB
+ player_menu_tab.pressed = true
+ presentation._input(player_menu_tab)
+ await process_frame
+ _expect(
+ root.gui_get_focus_owner() == null,
+ "the default player-menu Tab was consumed as focus navigation",
+ )
+ _expect(
+ presentation.get("_neutral_focus_seed") == standard_button,
+ "the default player-menu Tab discarded the neutral focus seed",
+ )
+ var first_navigation := InputEventJoypadButton.new()
+ first_navigation.button_index = JOY_BUTTON_DPAD_DOWN
+ first_navigation.pressed = true
+ presentation._input(first_navigation)
+ await process_frame
+ _expect(
+ root.gui_get_focus_owner() == standard_button,
+ "the first directional input did not activate the neutral focus seed",
+ )
+
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
+ var focus_arrow := presentation.get("_focus_arrow") as TextureRect
+ var focus_shadow := presentation.get("_focus_arrow_shadow") as TextureRect
+ _expect(focus_arrow != null, "controller focus arrow was not created")
+ _expect(focus_shadow != null, "controller focus shadow was not created")
+ _expect(focus_arrow.visible, "controller focus arrow is not visible")
+ _expect(focus_shadow.visible, "controller focus shadow is not visible")
_expect(
- standard_button.material != null,
- "ordinary controller focus receives inversion",
+ focus_arrow.texture == FocusPresentationType.FOCUS_ARROW_TEXTURE,
+ "controller focus arrow uses the wrong artwork",
+ )
+ _expect(
+ focus_shadow.texture == focus_arrow.texture,
+ "controller focus shadow does not follow the cursor silhouette",
+ )
+ _expect(
+ focus_shadow.position.is_equal_approx(
+ focus_arrow.position
+ + FocusPresentationType.FOCUS_ARROW_SHADOW_OFFSET
+ ),
+ "controller focus shadow uses the wrong offset",
+ )
+ _expect(
+ is_equal_approx(
+ focus_arrow.rotation_degrees,
+ FocusPresentationType.FOCUS_ARROW_ROTATION_DEGREES,
+ ),
+ "controller focus arrow does not point toward five o'clock",
)
-
- authored_selector.grab_focus()
- await process_frame
_expect(
standard_button.material == null,
- "inversion clears when focus moves",
+ "controller focus still applies material inversion",
)
_expect(
- authored_selector.material == null,
- "authored selector backgrounds opt out of inversion",
+ standard_button.get_theme_stylebox("focus") == normal_style,
+ "native focus highlighting was not neutralized",
+ )
+ _expect(
+ _arrow_marks_top_left(focus_arrow, standard_button.get_global_rect()),
+ "controller focus arrow is not in the control's top-left corner",
+ )
+ stage.scale = Vector2.ONE * 0.5
+ await process_frame
+ _expect(
+ focus_arrow.size.is_equal_approx(
+ FocusPresentationType.FOCUS_ARROW_SIZE * 0.5
+ ),
+ "controller focus arrow does not follow canonical UI stage scale",
+ )
+ _expect(
+ _arrow_marks_top_left(focus_arrow, standard_button.get_global_rect()),
+ "scaled controller focus arrow left its control's top-left corner",
+ )
+ stage.scale = Vector2.ONE
+ await process_frame
+ var hover_motion := InputEventMouseMotion.new()
+ hover_motion.position = standard_button.get_global_rect().get_center()
+ hover_motion.global_position = hover_motion.position
+ root.push_input(hover_motion, true)
+ await process_frame
+ _expect(
+ root.gui_get_hovered_control() == standard_button,
+ "the pointer did not reach the hover-suppression test button",
+ )
+ presentation._input(first_navigation)
+ presentation._input(controller_event)
+ await process_frame
+ _expect(
+ standard_button.get_theme_stylebox("hover") == normal_style,
+ "controller use leaves a stale mouse-hover highlight visible",
)
- standard_button.grab_focus()
- await process_frame
- standard_button.focus_mode = Control.FOCUS_NONE
+ second_button.grab_focus()
await process_frame
_expect(
- standard_button.material == null,
- "inversion clears when a focused control is defocused",
+ focus_arrow.visible,
+ "legacy inversion metadata incorrectly hides the focus arrow",
+ )
+ _expect(
+ _arrow_marks_top_left(focus_arrow, second_button.get_global_rect()),
+ "controller focus arrow did not follow the focused control",
+ )
+ var bubble_button := BubbleButton.new()
+ bubble_button.text = "bubble"
+ bubble_button.position = Vector2(440.0, 60.0)
+ bubble_button.size = Vector2(120.0, 120.0)
+ stage.add_child(bubble_button)
+ await process_frame
+ bubble_button.grab_focus()
+ await process_frame
+ _expect(
+ _arrow_overlaps_bubble_edge(focus_arrow, bubble_button),
+ "controller focus arrow does not meet the visible bubble edge",
+ )
+
+ var pointer_motion := InputEventMouseMotion.new()
+ pointer_motion.position = Vector2(4.0, 4.0)
+ presentation._input(pointer_motion)
+ await process_frame
+ _expect(
+ not focus_arrow.visible,
+ "mouse motion does not clear controller focus presentation",
+ )
+ _expect(
+ not focus_shadow.visible,
+ "mouse motion leaves the controller focus shadow visible",
+ )
+ _expect(
+ root.gui_get_focus_owner() == null,
+ "mouse motion leaves a stale menu action focused",
+ )
+ _expect(
+ standard_button.get_theme_stylebox("focus") == old_focus_style,
+ "native theme overrides were not restored after controller use",
+ )
+ _expect(
+ standard_button.get_theme_stylebox("hover") == old_hover_style,
+ "mouse hover styling was not restored after controller use",
+ )
+ var item_list := ItemList.new()
+ item_list.position = Vector2(80.0, 160.0)
+ item_list.size = Vector2(240.0, 150.0)
+ item_list.max_columns = 1
+ for index: int in 4:
+ item_list.add_item("list item %d" % index)
+ stage.add_child(item_list)
+ await process_frame
+ item_list.select(0)
+ item_list.grab_focus()
+ await process_frame
+ presentation._input(first_navigation)
+ presentation._input(controller_event)
+ await process_frame
+ var first_item_arrow_y: float = focus_arrow.position.y
+ item_list.select(2)
+ await process_frame
+ _expect(
+ focus_arrow.position.y > first_item_arrow_y,
+ "focus arrow does not follow the selected ItemList row",
+ )
+ _expect(
+ item_list.get_theme_stylebox("selected") is StyleBoxEmpty,
+ "ItemList still draws a selected-row cursor highlight",
+ )
+ _expect(
+ item_list.get_theme_color("font_selected_color")
+ == item_list.get_theme_color("font_color"),
+ "ItemList still recolors the selected-row cursor text",
+ )
+
+ var tree := Tree.new()
+ tree.position = Vector2(700.0, 160.0)
+ tree.size = Vector2(240.0, 150.0)
+ tree.hide_root = true
+ var tree_root: TreeItem = tree.create_item()
+ var first_tree_item: TreeItem = tree.create_item(tree_root)
+ first_tree_item.set_text(0, "first folder")
+ var second_tree_item: TreeItem = tree.create_item(tree_root)
+ second_tree_item.set_text(0, "second folder")
+ stage.add_child(tree)
+ await process_frame
+ first_tree_item.select(0)
+ tree.grab_focus()
+ await process_frame
+ var first_tree_arrow_y: float = focus_arrow.position.y
+ second_tree_item.select(0)
+ await process_frame
+ _expect(
+ focus_arrow.position.y > first_tree_arrow_y,
+ "focus arrow does not follow the selected Tree row",
+ )
+
+ var popup := PopupMenu.new()
+ for index: int in 5:
+ popup.add_item("popup item %d" % index)
+ root.add_child(popup)
+ popup.popup(Rect2i(500, 300, 0, 0))
+ popup.set_focused_item(0)
+ for _frame: int in 2:
+ await process_frame
+ _expect(
+ presentation.get("_focused_popup") == popup,
+ "focus arrow did not enter an open PopupMenu",
+ )
+ _expect(
+ presentation.get("_focus_layer").custom_viewport == popup,
+ "focus arrow is not rendered inside the PopupMenu viewport",
+ )
+ _expect(
+ popup.get_theme_stylebox("hover") is StyleBoxEmpty,
+ "PopupMenu still draws its native focus highlight",
+ )
+ _expect(
+ _arrow_marks_top_left(
+ focus_arrow,
+ presentation.call("_popup_focus_target_rect", popup) as Rect2,
+ ),
+ "focus arrow does not mark the focused PopupMenu item",
+ )
+ var first_popup_arrow_y: float = focus_arrow.position.y
+ popup.set_focused_item(2)
+ await process_frame
+ _expect(
+ focus_arrow.position.y > first_popup_arrow_y,
+ "focus arrow does not follow PopupMenu item navigation",
+ )
+ popup.hide()
+ popup.queue_free()
+ await process_frame
+ item_list.grab_focus()
+ await process_frame
+
+ var dialog := FileDialog.new()
+ dialog.file_mode = FileDialog.FILE_MODE_OPEN_DIR
+ dialog.access = FileDialog.ACCESS_FILESYSTEM
+ dialog.size = Vector2i(720, 480)
+ root.add_child(dialog)
+ dialog.popup_centered()
+ for _frame: int in 2:
+ await process_frame
+ var dialog_cancel := dialog.get_cancel_button()
+ dialog_cancel.grab_focus()
+ for _frame: int in 2:
+ await process_frame
+ _expect(
+ focus_arrow.visible,
+ "focus arrow is hidden for an embedded dialog control",
+ )
+ _expect(
+ presentation.get("_focus_layer").custom_viewport
+ == dialog_cancel.get_viewport(),
+ "focus arrow is not rendered inside the dialog viewport",
+ )
+ _expect(
+ _arrow_marks_top_left(
+ focus_arrow,
+ presentation.call("_focus_target_rect", dialog_cancel) as Rect2,
+ ),
+ "focus arrow does not follow an embedded dialog control",
+ )
+ dialog.hide()
+ dialog.queue_free()
+ await process_frame
+ item_list.grab_focus()
+ await process_frame
+
+ var scroll := ScrollContainer.new()
+ scroll.position = Vector2(420.0, 160.0)
+ scroll.size = Vector2(180.0, 90.0)
+ scroll.horizontal_scroll_mode = ScrollContainer.SCROLL_MODE_DISABLED
+ stage.add_child(scroll)
+ var list := VBoxContainer.new()
+ list.custom_minimum_size.x = 160.0
+ scroll.add_child(list)
+ var last_scroll_button: Button
+ for index: int in 8:
+ var scroll_button := Button.new()
+ scroll_button.text = "scroll option %d" % index
+ scroll_button.custom_minimum_size = Vector2(160.0, 40.0)
+ list.add_child(scroll_button)
+ last_scroll_button = scroll_button
+ await process_frame
+ last_scroll_button.grab_focus()
+ for _frame: int in 2:
+ await process_frame
+ _expect(
+ scroll.scroll_vertical > 0,
+ "controller focus does not reveal an off-screen selection",
+ )
+ _expect(
+ root.gui_get_focus_owner() == last_scroll_button,
+ "scroll following does not preserve the focused control",
+ )
+ _expect(
+ _arrow_marks_top_left(
+ focus_arrow,
+ last_scroll_button.get_global_rect(),
+ ),
+ "focus arrow does not follow a scrolled control",
+ )
+
+ last_scroll_button.focus_mode = Control.FOCUS_NONE
+ await process_frame
+ _expect(
+ not focus_arrow.visible,
+ "focus arrow remains after its control becomes unfocusable",
+ )
+ var event_focused_button := Button.new()
+ event_focused_button.text = "event focused"
+ event_focused_button.position = Vector2(700.0, 360.0)
+ event_focused_button.custom_minimum_size = Vector2(160.0, 50.0)
+ stage.add_child(event_focused_button)
+ await process_frame
+ presentation._input(first_navigation)
+ event_focused_button.grab_focus()
+ await process_frame
+ _expect(
+ root.gui_get_focus_owner() == event_focused_button,
+ (
+ "a menu focus established by the current directional event was "
+ + "mistaken for an automatic default"
+ ),
+ )
+
+ presentation.set_virtual_pointer_active(true)
+ await process_frame
+ _expect(
+ not focus_arrow.visible,
+ "focus arrow competes with active virtual-pointer presentation",
+ )
+ presentation._input(controller_event)
+ await process_frame
+ _expect(
+ not focus_arrow.visible,
+ "controller input stole presentation from the held virtual pointer",
+ )
+ presentation.set_virtual_pointer_active(false)
+ presentation._input(controller_event)
+ await process_frame
+ _expect(
+ focus_arrow.visible,
+ "controller focus presentation did not resume after virtual-pointer use",
)
stage.queue_free()
@@ -70,6 +413,66 @@ func _run() -> void:
quit(1)
+func _validate_controller_cooler_focus() -> void:
+ var selection := FishBatchSelectionType.new()
+ selection.set_visible_order([&"first", &"second"])
+ selection.select_only(&"first")
+ selection.focus_only(&"second")
+ _expect(
+ selection.get_focused_id() == &"second",
+ "cooler focus does not follow controller navigation",
+ )
+ _expect(
+ selection.get_selected_ids() == [&"first"],
+ "moving cooler focus unexpectedly changes the selected catch set",
+ )
+
+
+func _button_style(color: Color) -> StyleBoxFlat:
+ var style := StyleBoxFlat.new()
+ style.bg_color = color
+ style.set_corner_radius_all(8)
+ return style
+
+
+func _arrow_marks_top_left(arrow: Control, target: Rect2) -> bool:
+ var arrow_pivot: Vector2 = arrow.position + arrow.size * 0.5
+ var arrow_scale: Vector2 = arrow.size / FocusPresentationType.FOCUS_ARROW_SIZE
+ var tip_from_pivot: Vector2 = (
+ (FocusPresentationType.FOCUS_ARROW_TIP_UV - Vector2.ONE * 0.5)
+ * arrow.size
+ ).rotated(deg_to_rad(
+ FocusPresentationType.FOCUS_ARROW_ROTATION_DEGREES
+ ))
+ var actual_tip: Vector2 = arrow_pivot + tip_from_pivot
+ var expected_tip: Vector2 = (
+ target.position
+ + FocusPresentationType.FOCUS_ARROW_TARGET_OVERLAP * arrow_scale
+ )
+ return actual_tip.distance_to(expected_tip) <= 0.5
+
+
+func _arrow_overlaps_bubble_edge(
+ arrow: Control,
+ bubble: BubbleButton,
+) -> bool:
+ var arrow_pivot: Vector2 = arrow.position + arrow.size * 0.5
+ var tip_from_pivot: Vector2 = (
+ (FocusPresentationType.FOCUS_ARROW_TIP_UV - Vector2.ONE * 0.5)
+ * arrow.size
+ ).rotated(deg_to_rad(
+ FocusPresentationType.FOCUS_ARROW_ROTATION_DEGREES
+ ))
+ var actual_tip: Vector2 = arrow_pivot + tip_from_pivot
+ var local_tip: Vector2 = (
+ bubble.get_global_transform_with_canvas().affine_inverse()
+ * actual_tip
+ )
+ var radius: Vector2 = bubble.size * 0.5
+ var normalized: Vector2 = (local_tip - radius) / radius
+ return normalized.length() >= 0.75 and normalized.length() <= 1.0
+
+
func _expect(condition: bool, message: String) -> void:
if not condition:
_failures.append(message)
diff --git a/tests/controller_focus_recovery_validation.gd b/tests/controller_focus_recovery_validation.gd
deleted file mode 100644
index e34afaa..0000000
--- a/tests/controller_focus_recovery_validation.gd
+++ /dev/null
@@ -1,88 +0,0 @@
-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
-
- var controller_event := InputEventJoypadButton.new()
- controller_event.button_index = JOY_BUTTON_A
- controller_event.pressed = true
- recovery._input(controller_event)
- original.grab_focus()
- await process_frame
- _expect(root.gui_get_focus_owner() == original, "original option receives 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 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",
- )
-
- 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_mapping_validation.gd b/tests/controller_mapping_validation.gd
index 16c9ad0..a5938d6 100644
--- a/tests/controller_mapping_validation.gd
+++ b/tests/controller_mapping_validation.gd
@@ -14,6 +14,22 @@ func _init() -> void:
func _run() -> void:
+ if (
+ ControllerMappingManagerType.ROLE_LABELS[
+ ControllerMappingManagerType.ROLE_CAMERA_ZOOM
+ ] != "zoom modifier (+RT)"
+ ):
+ push_error("controller zoom modifier label is misleading")
+ quit(1)
+ return
+ if (
+ ControllerMappingManagerType.ROLE_LABELS[
+ ControllerMappingManagerType.ROLE_RIGHT_STICK_Y
+ ] != "camera up / down (+RT to zoom)"
+ ):
+ push_error("controller vertical camera label omits the RT zoom chord")
+ quit(1)
+ return
var manager := ControllerMappingManagerType.new()
root.add_child(manager)
await process_frame
@@ -94,6 +110,32 @@ func _validate_manager(manager: ControllerMappingManagerType) -> String:
ControllerMappingManagerType.ROLE_ORDER.size(),
str(defaults.keys()),
]
+ if ControllerMappingManagerType.ROLE_LABELS.get(
+ ControllerMappingManagerType.ROLE_RIGHT_STICK_CLICK, ""
+ ) != "sneak":
+ return "right-stick click is not presented as sneak"
+ if ControllerMappingManagerType.BUTTON_ACTION_ROLES.get(
+ &"sneak", &""
+ ) != ControllerMappingManagerType.ROLE_RIGHT_STICK_CLICK:
+ return "right-stick click is not assigned to the sneak action"
+ if ControllerMappingManagerType.ROLE_LABELS.get(
+ ControllerMappingManagerType.ROLE_Y, ""
+ ) != "interact / character call":
+ return "Y does not present its contextual gameplay actions"
+ if ControllerMappingManagerType.BUTTON_ACTION_ROLES.get(
+ &"interact", &""
+ ) != ControllerMappingManagerType.ROLE_Y:
+ return "Y is not assigned to the interact action"
+ if ControllerMappingManagerType.BUTTON_ACTION_ROLES.get(
+ &"character_call", &""
+ ) != ControllerMappingManagerType.ROLE_Y:
+ return "Y is not assigned to the character-call action"
+ if not _has_joy_button(&"character_call", JOY_BUTTON_Y):
+ return "character call does not default to Y"
+ if not _has_joy_button(&"interact", JOY_BUTTON_Y):
+ return "interact does not default to Y"
+ if not _has_joy_button(&"sneak", JOY_BUTTON_RIGHT_STICK):
+ return "sneak does not default to right-stick click"
var trigger_button := InputEventJoypadButton.new()
trigger_button.device = manager.get_active_device_id()
trigger_button.button_index = JOY_BUTTON_MISC1
@@ -126,6 +168,10 @@ func _validate_manager(manager: ControllerMappingManagerType) -> String:
"kind": "button",
"button": int(JOY_BUTTON_X),
}
+ custom[str(ControllerMappingManagerType.ROLE_X)] = {
+ "kind": "button",
+ "button": int(JOY_BUTTON_A),
+ }
custom[str(ControllerMappingManagerType.ROLE_POINTER_MODIFIER)] = (
trigger_binding
)
@@ -133,6 +179,41 @@ func _validate_manager(manager: ControllerMappingManagerType) -> String:
return "valid custom mapping could not be saved"
if not manager.has_custom_mapping():
return "saved custom mapping did not become active"
+ var duplicate: Dictionary = custom.duplicate(true)
+ duplicate[str(ControllerMappingManagerType.ROLE_X)] = (
+ duplicate[str(ControllerMappingManagerType.ROLE_A)] as Dictionary
+ ).duplicate(true)
+ if manager.replace_active_bindings(duplicate):
+ return "duplicate controller buttons were accepted"
+ var shared_trigger_axis: Dictionary = custom.duplicate(true)
+ shared_trigger_axis[
+ str(ControllerMappingManagerType.ROLE_POINTER_MODIFIER)
+ ] = {
+ "kind": "axis",
+ "axis": int(JOY_AXIS_TRIGGER_LEFT),
+ "direction": 1.0,
+ "rest": 0.0,
+ }
+ shared_trigger_axis[
+ str(ControllerMappingManagerType.ROLE_CAMERA_ZOOM)
+ ] = {
+ "kind": "axis",
+ "axis": int(JOY_AXIS_TRIGGER_LEFT),
+ "direction": -1.0,
+ "rest": 0.0,
+ }
+ if ControllerMappingManagerType.bindings_have_conflicts(
+ shared_trigger_axis
+ ):
+ return "opposite halves of a shared trigger axis conflict"
+ var malformed_profiles: Dictionary = manager._validated_profiles({
+ "name:controller": {
+ "controller_name": "controller",
+ "bindings": duplicate,
+ },
+ })
+ if not malformed_profiles.is_empty():
+ return "a stored controller profile with duplicate buttons was accepted"
if _keyboard_event_count(&"jump") != keyboard_events_before:
return "controller remapping changed keyboard bindings"
var active_button := InputEventJoypadButton.new()
@@ -197,7 +278,7 @@ func _validate_manager(manager: ControllerMappingManagerType) -> String:
func _validate_portmaster_launcher() -> String:
- const launcher_path: String = "res://scripts/portmaster/NETfishing.sh"
+ const launcher_path: String = "res://scripts/portmaster/straywild.sh"
if not FileAccess.file_exists(launcher_path):
return "PortMaster launcher template is missing"
var launcher: String = FileAccess.get_file_as_string(launcher_path)
@@ -210,21 +291,72 @@ func _validate_portmaster_launcher() -> String:
"guide:b8,start:b7,back:b6",
"leftstick:b9",
"rightstick:b12",
- "netfishing_controllerconfig=\"$GODOT_MUOS_MAPPING\"",
- "SDL_GAMECONTROLLERCONFIG=\"$netfishing_controllerconfig\" \\",
+ "straywild_controllerconfig=\"$GODOT_MUOS_MAPPING\"",
+ "GPTOKEYB_CONFIG=\"$GAMEDIR/straywild.gptk\"",
+ "$GPTOKEYB \"straywild.aarch64\" -c \"$GPTOKEYB_CONFIG\" &",
+ "pm_platform_helper \"$GAME_EXECUTABLE\"",
+ "\"$GAME_LAUNCHER\"",
+ "\"$CONTROLLER_MAPPING_FILE\"",
+ "pm_finish",
]:
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"
+ const game_launcher_path: String = (
+ "res://scripts/portmaster/launch-straywild.sh"
+ )
+ if not FileAccess.file_exists(game_launcher_path):
+ return "PortMaster game launcher wrapper is missing"
+ var game_launcher: String = FileAccess.get_file_as_string(
+ game_launcher_path
+ )
+ for expected_fragment: String in [
+ "SDL_GAMECONTROLLERCONFIG=\"$(<\"$CONTROLLER_MAPPING_FILE\")\"",
+ "export SDL_GAMECONTROLLERCONFIG",
+ "exec \"$@\"",
+ ]:
+ if expected_fragment not in game_launcher:
+ return "PortMaster game launcher omitted " + expected_fragment
return ""
func _validate_auto_map(manager: ControllerMappingManagerType) -> String:
+ if manager.get_role_prompt(ControllerMappingManagerType.ROLE_LT) != (
+ "press or squeeze LT"
+ ):
+ return "auto-map does not identify the LT control"
+ if manager.get_role_prompt(ControllerMappingManagerType.ROLE_RT) != (
+ "press or squeeze RT"
+ ):
+ return "auto-map does not identify the RT control"
var panel := ControllerMappingPanelType.new()
root.add_child(panel)
panel.setup(manager)
panel.open_panel()
+ var mapping_scrolls: Array[Node] = panel.find_children(
+ "", "ScrollContainer", true, false
+ )
+ if mapping_scrolls.is_empty():
+ return "controller mapper does not contain its mapping list"
+ if not (mapping_scrolls[0] as ScrollContainer).follow_focus:
+ return "controller mapper does not scroll to follow controller focus"
+ var first_binding := panel._binding_buttons.get(
+ ControllerMappingManagerType.ROLE_ORDER.front()
+ ) as Button
+ var last_binding := panel._binding_buttons.get(
+ ControllerMappingManagerType.ROLE_ORDER.back()
+ ) as Button
+ if first_binding.get_node(first_binding.focus_neighbor_top) != first_binding:
+ return "controller mapper loses focus above its first row"
+ if last_binding.get_node(last_binding.focus_neighbor_bottom) != (
+ panel._close_button
+ ):
+ return "controller mapper cannot reach Done from its last row"
+ if panel._close_button.get_node(
+ panel._close_button.focus_neighbor_top
+ ) != last_binding:
+ return "controller mapper cannot return from Done to its last row"
panel._begin_auto_map()
for role: StringName in ControllerMappingManagerType.ROLE_ORDER:
panel._process(ControllerMappingPanelType.CAPTURE_NEUTRAL_SECONDS)
@@ -232,12 +364,6 @@ func _validate_auto_map(manager: ControllerMappingManagerType) -> String:
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()
@@ -258,24 +384,12 @@ func _validate_auto_map(manager: ControllerMappingManagerType) -> String:
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(
+ var duplicate_saved: bool = manager.set_binding(
ControllerMappingManagerType.ROLE_B,
- ControllerMappingManagerType.default_bindings()[
- str(ControllerMappingManagerType.ROLE_B)
- ] as Dictionary,
+ manager.get_binding(ControllerMappingManagerType.ROLE_A),
)
+ if duplicate_saved:
+ return "manual mapping accepted a controller button already in use"
panel._begin_manual_capture(
ControllerMappingManagerType.ROLE_POINTER_MODIFIER
)
@@ -386,3 +500,11 @@ func _keyboard_event_count(action: StringName) -> int:
if event is InputEventKey or event is InputEventMouseButton:
count += 1
return count
+
+
+func _has_joy_button(action: StringName, button_index: JoyButton) -> bool:
+ for event: InputEvent in InputMap.action_get_events(action):
+ var button := event as InputEventJoypadButton
+ if button != null and button.button_index == button_index:
+ return true
+ return false
diff --git a/tests/controller_menu_accessibility_validation.gd b/tests/controller_menu_accessibility_validation.gd
new file mode 100644
index 0000000..37e43d5
--- /dev/null
+++ b/tests/controller_menu_accessibility_validation.gd
@@ -0,0 +1,2054 @@
+extends SceneTree
+
+const JoinGamePageScene = preload(
+ "res://ui/network/join_game_page.tscn"
+)
+const TitleScreenScene = preload("res://ui/title_screen.tscn")
+const PauseMenuScene = preload("res://ui/pause_menu.tscn")
+const SettingsPanelScene = preload("res://ui/settings_panel.tscn")
+const SaveSlotsPageScene = preload("res://ui/save_slots_page.tscn")
+const BubbleConfirmationScene = preload(
+ "res://ui/components/bubble_menu/bubble_confirmation_page.tscn"
+)
+const TitleConfirmationScene = preload(
+ "res://ui/title_confirmation_bubble_page.tscn"
+)
+const MailPageType = preload("res://ui/mail_page.gd")
+const LogbookPageType = preload("res://ui/logbook_page.gd")
+const ProfilePageType = preload("res://ui/profile_page.gd")
+const VoiceProfilesType = preload(
+ "res://player/animalese_voice_profiles.gd"
+)
+const PlayerMenuScene = preload("res://ui/player_menu.tscn")
+const PlayerMenuType = preload("res://ui/player_menu.gd")
+const PlayerHotbarType = preload("res://inventory/player_hotbar.gd")
+const ItemCatalogResource: ItemCatalog = preload(
+ "res://items/catalog/item_catalog.tres"
+)
+const PlayersPageType = preload("res://ui/players_page.gd")
+const TheNetPageType = preload("res://ui/the_net_page.gd")
+const ControllerMappingManagerType = preload(
+ "res://settings/controller_mapping_manager.gd"
+)
+const DialogControllerNavigationType = preload(
+ "res://ui/file_dialog_controller_navigation.gd"
+)
+
+var _failures: Array[String] = []
+
+
+func _initialize() -> void:
+ _run.call_deferred()
+
+
+func _run() -> void:
+ root.size = Vector2i(1280, 720)
+ await _validate_primary_menu_navigation()
+ await _validate_save_slots_navigation()
+ await _validate_join_game_navigation()
+ await _validate_data_settings_navigation()
+ await _validate_settings_adjustment_navigation()
+ await _validate_settings_presentation_requires_apply()
+ await _validate_mail_navigation()
+ await _validate_profile_confirmation_focus()
+ await _validate_profile_voice_navigation()
+ await _validate_player_menu_toggle_queue()
+ await _validate_inventory_tab_zone_transitions()
+ await _validate_player_menu_nested_controller_back()
+ await _validate_confirmation_dialog_navigation()
+ await _validate_bubble_confirmation_navigation()
+ _validate_mapping_capture_contract()
+ if _failures.is_empty():
+ print("Controller menu accessibility validation: PASS")
+ quit(0)
+ return
+ for failure: String in _failures:
+ push_error(failure)
+ quit(1)
+
+
+func _validate_player_menu_toggle_queue() -> void:
+ var menu := PlayerMenuScene.instantiate() as Control
+ root.add_child(menu)
+ await process_frame
+ menu.visible = true
+ menu.set("_transitioning", true)
+ var toggle := InputEventAction.new()
+ toggle.action = &"open_backpack"
+ toggle.pressed = true
+ menu.call("_input", toggle)
+ _expect(
+ bool(menu.get("_backpack_toggle_queued")),
+ "Player menu discarded TAB during its entry transition.",
+ )
+ menu.call("_input", toggle)
+ _expect(
+ not bool(menu.get("_backpack_toggle_queued")),
+ "Repeated TAB presses did not preserve toggle parity.",
+ )
+ menu.call("_input", toggle)
+ menu.call(
+ "_finish_menu_entry",
+ int(menu.get("_transition_generation")),
+ )
+ await create_timer(0.5).timeout
+ _expect(
+ not menu.visible,
+ "A queued TAB press did not close the menu after entry.",
+ )
+ menu.queue_free()
+ await process_frame
+
+
+func _validate_primary_menu_navigation() -> void:
+ var title := TitleScreenScene.instantiate() as Control
+ root.add_child(title)
+ await process_frame
+ (title.get_node("%ButtonCenter") as Control).show()
+ (title.get_node("%BubbleField") as Control).show()
+ title.call("_update_title_layout")
+ title.call("_set_title_bubbles_interactive", true)
+ var title_controls: Array[Control] = []
+ for candidate: Variant in title.call("_get_title_buttons"):
+ var control := candidate as Control
+ if control is BaseButton:
+ (control as BaseButton).disabled = false
+ title_controls.append(control)
+ _expect(
+ title_controls.size() == 5,
+ "Title menu does not expose its five primary actions.",
+ )
+ var play_button := title.get_node("%PlayButton") as Button
+ _expect(
+ play_button.icon != null
+ and play_button.icon.resource_path
+ == "res://ui/icons/new_menu/play.png"
+ and play_button.text.is_empty(),
+ "The Play action is not presented as the large square icon button.",
+ )
+ _expect(
+ is_equal_approx(play_button.size.x, play_button.size.y),
+ "The Play action is not square.",
+ )
+ var online_button := title.get_node("%JoinGameButton") as Button
+ var settings_button := title.get_node("%SettingsButton") as Button
+ var credits_button := title.get_node("%CreditsButton") as Button
+ var close_button := title.get_node("%QuitButton") as Button
+ for secondary_button: Button in [
+ online_button,
+ settings_button,
+ credits_button,
+ close_button,
+ ]:
+ _expect(
+ is_equal_approx(
+ secondary_button.size.x,
+ secondary_button.size.y,
+ ),
+ "A secondary title action is not square.",
+ )
+ _expect(
+ is_equal_approx(
+ close_button.position.y + close_button.size.y
+ - online_button.position.y,
+ play_button.size.y,
+ ),
+ "The secondary title-action stack does not match Play's height.",
+ )
+ _expect(
+ not (title.get_node("%NewGameButton") as Control).visible
+ and not (title.get_node("%DeleteSaveButton") as Control).visible,
+ "Legacy New/Delete title bubbles are still visible.",
+ )
+ _assert_neighbor(
+ play_button,
+ &"focus_neighbor_right",
+ title.get_node("%JoinGameButton") as Control,
+ )
+ _assert_neighbor(
+ title.get_node("%JoinGameButton") as Control,
+ &"focus_neighbor_left",
+ play_button,
+ )
+ _assert_neighbor(
+ title.get_node("%JoinGameButton") as Control,
+ &"focus_neighbor_bottom",
+ title.get_node("%SettingsButton") as Control,
+ )
+ _assert_neighbor(
+ title.get_node("%SettingsButton") as Control,
+ &"focus_neighbor_bottom",
+ title.get_node("%CreditsButton") as Control,
+ )
+ _assert_neighbor(
+ title.get_node("%CreditsButton") as Control,
+ &"focus_neighbor_bottom",
+ title.get_node("%QuitButton") as Control,
+ )
+ for title_control: Control in title_controls:
+ _assert_directionally_reachable(title_control, title_controls)
+ title.queue_free()
+ await process_frame
+
+ var pause := PauseMenuScene.instantiate() as Control
+ root.add_child(pause)
+ pause.show()
+ var root_page := pause.get_node("%RootPage") as Control
+ root_page.call("show_page", false)
+ for _frame: int in 2:
+ await process_frame
+ var pause_controls: Array[Control] = [
+ pause.get_node("%ResumeButton") as Control,
+ pause.get_node("%SaveButton") as Control,
+ pause.get_node("%JoinGameButton") as Control,
+ pause.get_node("%SettingsButton") as Control,
+ pause.get_node("%ReturnToTitleButton") as Control,
+ pause.get_node("%ResetProgressButton") as Control,
+ pause.get_node("%QuitButton") as Control,
+ ]
+ _assert_directionally_reachable(pause_controls.front(), pause_controls)
+ pause.queue_free()
+ await process_frame
+
+
+func _validate_save_slots_navigation() -> void:
+ var page := SaveSlotsPageScene.instantiate() as SaveSlotsPage
+ root.add_child(page)
+ await process_frame
+ page.open_page()
+ for _frame: int in 2:
+ await process_frame
+ await create_timer(0.25).timeout
+ var saves_tab := page.get_node("%SavesTab") as Button
+ var new_tab := page.get_node("%NewSlotTab") as Button
+ var content_panel := page.get_node("%ContentPanel") as PanelContainer
+ var tab_overlap: float = (
+ saves_tab.get_global_rect().end.y
+ - content_panel.get_global_rect().position.y
+ )
+ _expect(
+ is_equal_approx(tab_overlap, saves_tab.size.y * 0.5),
+ "Save-slot content does not cover the lower half of its organizer tabs.",
+ )
+ _expect(
+ page.get_active_page_id() == &"new",
+ "An empty save catalog does not open directly to New Slot.",
+ )
+ _assert_neighbor(saves_tab, &"focus_neighbor_right", new_tab)
+ _assert_neighbor(new_tab, &"focus_neighbor_left", saves_tab)
+ var new_controls: Array[Control] = [
+ saves_tab,
+ new_tab,
+ page.get_node("%NewSlotName") as Control,
+ page.get_node("%GeneratedButton") as Control,
+ page.get_node("%StarterButton") as Control,
+ page.get_node("%RandomSeedButton") as Control,
+ page.get_node("%CustomSeedButton") as Control,
+ page.get_node("%CreateSlotButton") as Control,
+ page.get_node("%BackButton") as Control,
+ ]
+ _assert_directionally_reachable(new_tab, new_controls)
+ page.call("_select_page", &"saves", false)
+ await process_frame
+ var import_button := page.get_node("%ImportSlotButton") as Button
+ var actions := page.get_node("%Actions") as VBoxContainer
+ var secondary_actions := page.get_node("%SecondaryActions") as GridContainer
+ var play_slot := page.get_node("%PlaySlotButton") as Button
+ var rename_slot := page.get_node("%RenameSlotButton") as Button
+ var duplicate_slot := page.get_node("%DuplicateSlotButton") as Button
+ var export_slot := page.get_node("%ExportSlotButton") as Button
+ var delete_slot := page.get_node("%DeleteSlotButton") as Button
+ _expect(
+ saves_tab.find_valid_focus_neighbor(SIDE_BOTTOM) == import_button,
+ "An empty Save Slots page does not lead from its tab to Import Save.",
+ )
+ _expect(
+ import_button.find_valid_focus_neighbor(SIDE_BOTTOM)
+ == page.get_node("%BackButton"),
+ "An empty Save Slots page does not lead from Import Save to Back.",
+ )
+ _expect(
+ is_equal_approx(play_slot.size.x, actions.size.x),
+ "Save-slot Play does not span the full action width.",
+ )
+ _expect(
+ secondary_actions.columns == 2
+ and is_equal_approx(rename_slot.size.x, duplicate_slot.size.x)
+ and is_equal_approx(export_slot.size.x, delete_slot.size.x)
+ and is_equal_approx(rename_slot.position.y, duplicate_slot.position.y)
+ and is_equal_approx(export_slot.position.y, delete_slot.position.y)
+ and export_slot.position.y > rename_slot.position.y,
+ "Save-slot secondary actions are not arranged as a 2 by 2 grid.",
+ )
+ var play_style := play_slot.get_theme_stylebox("normal") as StyleBoxFlat
+ _expect(
+ play_style != null and play_style.bg_color == UtilityPageStyle.GREEN,
+ "Save-slot Play does not use the green primary-action style.",
+ )
+ _assert_neighbor(play_slot, &"focus_neighbor_bottom", rename_slot)
+ _assert_neighbor(rename_slot, &"focus_neighbor_right", duplicate_slot)
+ _assert_neighbor(rename_slot, &"focus_neighbor_bottom", export_slot)
+ _assert_neighbor(duplicate_slot, &"focus_neighbor_bottom", delete_slot)
+ _assert_neighbor(export_slot, &"focus_neighbor_right", delete_slot)
+ page.queue_free()
+ await process_frame
+
+
+func _validate_join_game_navigation() -> void:
+ var page := JoinGamePageScene.instantiate() as Control
+ root.add_child(page)
+ await process_frame
+ page.show()
+ await process_frame
+ var discover := page.get_node("%DiscoverButton") as Button
+ var friends := page.get_node("%FriendsButton") as Button
+ var direct := page.get_node("%DirectButton") as Button
+ var saved := page.get_node("%SavedButton") as Button
+ var recent := page.get_node("%RecentButton") as Button
+ var address := page.get_node("%Address") as LineEdit
+ var name_edit := page.get_node("%NameEdit") as LineEdit
+ var server_list := page.get_node("%ServerList") as ItemList
+ var presence := page.get_node("%PresenceButton") as Button
+ var room_open := page.get_node("%RoomOpenButton") as Button
+ var room_listing := page.get_node("%RoomListingButton") as Button
+ var refresh := page.get_node("%RefreshButton") as Button
+ var join := page.get_node("%JoinButton") as Button
+ var save := page.get_node("%SaveButton") as Button
+ var edit := page.get_node("%EditButton") as Button
+ var favorite := page.get_node("%FavoriteButton") as Button
+ var delete := page.get_node("%DeleteButton") as Button
+ var cancel := page.get_node("%CancelButton") as Button
+ var back := page.get_node("%BackButton") as Button
+ var direct_content := page.get_node("%DirectContent") as Control
+ var list_content := page.get_node("%ListContent") as Control
+ var content_panel := page.get_node("%ContentPanel") as PanelContainer
+ var actions := page.get_node("%Actions") as HBoxContainer
+ var modes: Array[Control] = [discover, friends, direct, saved, recent]
+ var tab_overlap: float = (
+ discover.get_global_rect().end.y
+ - content_panel.get_global_rect().position.y
+ )
+ _expect(
+ is_equal_approx(tab_overlap, discover.size.y * 0.5),
+ "Join-game content does not cover the lower half of its organizer tabs.",
+ )
+
+ direct_content.hide()
+ list_content.show()
+ address.hide()
+ name_edit.hide()
+ server_list.show()
+ _set_button_state(refresh, true)
+ _set_button_state(join, true)
+ _set_button_state(save, false)
+ _set_button_state(edit, false)
+ _set_button_state(favorite, false)
+ _set_button_state(delete, false)
+ _set_button_state(cancel, false)
+ _set_button_state(back, true)
+ page.set("_mode", JoinGamePage.Mode.DISCOVER)
+ page.call("_configure_controller_navigation")
+ await process_frame
+ _assert_neighbor(discover, &"focus_neighbor_bottom", server_list)
+ _assert_neighbor(server_list, &"focus_neighbor_top", discover)
+ _assert_neighbor(server_list, &"focus_neighbor_bottom", refresh)
+ _assert_neighbor(refresh, &"focus_neighbor_right", join)
+ _assert_neighbor(join, &"focus_neighbor_bottom", back)
+ _assert_neighbor(back, &"focus_neighbor_top", join)
+ _expect(
+ is_equal_approx(
+ join.get_global_rect().end.x,
+ actions.get_global_rect().end.x,
+ ),
+ "Discover Refresh and Join are not right-justified.",
+ )
+ var discover_controls: Array[Control] = modes.duplicate()
+ discover_controls.append_array([server_list, refresh, join, back])
+ _assert_directionally_reachable(discover, discover_controls)
+ room_open.show()
+ room_listing.show()
+ _set_button_state(room_open, true)
+ _set_button_state(room_listing, true)
+ page.call("_configure_controller_navigation")
+ await process_frame
+ _assert_neighbor(room_open, &"focus_neighbor_right", room_listing)
+ _assert_neighbor(room_open, &"focus_neighbor_left", room_open)
+ _assert_neighbor(room_listing, &"focus_neighbor_left", room_open)
+ _assert_neighbor(room_listing, &"focus_neighbor_bottom", server_list)
+ var hosted_discover_controls: Array[Control] = modes.duplicate()
+ hosted_discover_controls.append_array([
+ room_open,
+ room_listing,
+ server_list,
+ refresh,
+ join,
+ back,
+ ])
+ _assert_directionally_reachable(discover, hosted_discover_controls)
+ var rooms: Array[Dictionary] = [
+ {
+ "room_id": "controller-default-room",
+ "room_name": "Controller default room",
+ "current_players": 1,
+ "max_players": 8,
+ },
+ {
+ "room_id": "controller-second-room",
+ "room_name": "Controller second room",
+ "current_players": 2,
+ "max_players": 8,
+ },
+ ]
+ page.call("_on_discovery_rooms_updated", rooms)
+ _expect(
+ int(page.get("_selected_discovery_index")) == 0,
+ "Discovery should select its first room as soon as results arrive.",
+ )
+ _expect(
+ server_list.is_selected(0),
+ "Discovery's visible cursor and selected room should agree immediately.",
+ )
+
+ page.set("_mode", JoinGamePage.Mode.FRIENDS)
+ (page.get_node("%OnlineControls") as Control).hide()
+ presence.show()
+ page.call("_configure_controller_navigation")
+ await process_frame
+ _assert_neighbor(friends, &"focus_neighbor_bottom", server_list)
+ _assert_neighbor(server_list, &"focus_neighbor_top", friends)
+ _assert_neighbor(server_list, &"focus_neighbor_bottom", presence)
+ _assert_neighbor(presence, &"focus_neighbor_right", refresh)
+ _assert_neighbor(refresh, &"focus_neighbor_right", join)
+ _expect(
+ is_equal_approx(
+ presence.get_global_rect().position.x,
+ actions.get_global_rect().position.x,
+ ),
+ "Friend presence is not left-justified in the action row.",
+ )
+ _expect(
+ is_equal_approx(
+ join.get_global_rect().end.x,
+ actions.get_global_rect().end.x,
+ ),
+ "Friends Refresh and Join are not right-justified.",
+ )
+ var friend_controls: Array[Control] = modes.duplicate()
+ friend_controls.append_array([server_list, presence, refresh, join, back])
+ _assert_directionally_reachable(friends, friend_controls)
+
+ list_content.hide()
+ direct_content.show()
+ presence.hide()
+ address.show()
+ address.editable = true
+ server_list.hide()
+ _set_button_state(refresh, false)
+ _set_button_state(join, true)
+ _set_button_state(save, true)
+ page.set("_mode", JoinGamePage.Mode.DIRECT)
+ page.call("_configure_controller_navigation")
+ await process_frame
+ _assert_neighbor(direct, &"focus_neighbor_bottom", address)
+ _assert_neighbor(address, &"focus_neighbor_top", direct)
+ _assert_neighbor(address, &"focus_neighbor_bottom", join)
+ _assert_neighbor(save, &"focus_neighbor_bottom", back)
+ _assert_neighbor(back, &"focus_neighbor_top", save)
+ var direct_controls: Array[Control] = modes.duplicate()
+ direct_controls.append_array([address, join, save, back])
+ _assert_directionally_reachable(direct, direct_controls)
+
+ name_edit.show()
+ name_edit.editable = true
+ page.set("_name_entry_active", true)
+ page.call("_configure_controller_navigation")
+ await process_frame
+ _assert_neighbor(address, &"focus_neighbor_bottom", name_edit)
+ _assert_neighbor(name_edit, &"focus_neighbor_top", address)
+ page.call("request_back")
+ _expect(
+ not bool(page.get("_name_entry_active")),
+ "Join-game Back should leave the server-name edit substate.",
+ )
+ _expect(
+ page.visible,
+ "Join-game Back should not close the browser from an edit substate.",
+ )
+ await process_frame
+
+ var saved_entries: Array[SavedServerEntry] = []
+ server_list.clear()
+ for index: int in 3:
+ var entry := SavedServerEntry.new()
+ entry.entry_id = "controller-saved-%d" % index
+ entry.display_name = "Saved server %d" % index
+ saved_entries.append(entry)
+ server_list.add_item(entry.display_name)
+ page.set("_mode", JoinGamePage.Mode.SAVED)
+ page.set("_visible_entries", saved_entries)
+ direct_content.hide()
+ list_content.show()
+ server_list.show()
+ page.call("_configure_controller_navigation")
+ page.call("_restore_entry_selection_and_focus", 1)
+ await process_frame
+ _expect(
+ server_list.is_selected(1)
+ and root.gui_get_focus_owner() == server_list,
+ "Saved-server refresh did not restore the same list position.",
+ )
+ saved_entries.remove_at(1)
+ server_list.remove_item(1)
+ page.set("_visible_entries", saved_entries)
+ page.call("_restore_entry_selection_and_focus", 1)
+ await process_frame
+ _expect(
+ server_list.is_selected(1)
+ and (
+ page.get("_selected_entry") as SavedServerEntry
+ ).entry_id == "controller-saved-2",
+ "Deleting a saved server did not select the row that replaced it.",
+ )
+ saved_entries.clear()
+ server_list.clear()
+ page.set("_visible_entries", saved_entries)
+ page.call("_restore_entry_selection_and_focus", 0)
+ await process_frame
+ _expect(
+ root.gui_get_focus_owner() == saved,
+ "An empty saved-server list did not return focus to its mode tab.",
+ )
+
+ page.queue_free()
+ await process_frame
+
+
+func _validate_data_settings_navigation() -> void:
+ var main_source: String = FileAccess.get_file_as_string("res://main/main.gd")
+ var data_root_source: String = FileAccess.get_file_as_string(
+ "res://network/player_data_root.gd"
+ )
+ var settings_source: String = FileAccess.get_file_as_string(
+ "res://ui/settings_panel.gd"
+ )
+ _expect(
+ not main_source.contains("OS.shell_open")
+ and not main_source.contains(
+ "status_changed.connect(_on_data_root_status)"
+ )
+ and not data_root_source.contains("OS.shell_open")
+ and settings_source.count("OS.shell_open") == 1,
+ (
+ "Opening the data folder is not owned exclusively by the Settings "
+ + "Data action."
+ ),
+ )
+ var panel := SettingsPanelScene.instantiate() as SettingsPanel
+ root.add_child(panel)
+ await process_frame
+ panel.show()
+ for _frame: int in 2:
+ await process_frame
+ var data_tab := panel.get_node("%DataTab") as Button
+ var open_data_folder := panel.get_node("%OpenDataFolder") as Button
+ var content_panel := panel.get_node("%ContentPanel") as PanelContainer
+ var tab_overlap: float = (
+ data_tab.get_global_rect().end.y
+ - content_panel.get_global_rect().position.y
+ )
+ _expect(
+ is_equal_approx(tab_overlap, data_tab.size.y * 0.5),
+ "Settings content does not cover the lower half of its organizer tabs.",
+ )
+ var open_activation := {"count": 0}
+ open_data_folder.pressed.connect(func() -> void:
+ open_activation["count"] = int(open_activation["count"]) + 1
+ )
+ data_tab.grab_focus()
+ var accept_press := InputEventJoypadButton.new()
+ accept_press.button_index = JOY_BUTTON_A
+ accept_press.pressed = true
+ Input.parse_input_event(accept_press)
+ await process_frame
+ var accept_release := InputEventJoypadButton.new()
+ accept_release.button_index = JOY_BUTTON_A
+ accept_release.pressed = false
+ Input.parse_input_event(accept_release)
+ for _frame: int in 2:
+ await process_frame
+ _expect(
+ panel.get_active_page_id() == &"data",
+ "Selecting the Data tab did not open the Data page.",
+ )
+ _expect(
+ int(open_activation["count"]) == 0,
+ "Selecting the Data tab also activated Open Data Folder.",
+ )
+ _expect(
+ root.gui_get_focus_owner() == data_tab,
+ "Selecting the Data tab transferred focus into its actions.",
+ )
+ open_data_folder.grab_focus()
+ _expect(
+ root.gui_get_focus_owner() == open_data_folder,
+ "Open Data Folder did not accept controller focus.",
+ )
+ _expect(
+ not open_data_folder.disabled,
+ "Open Data Folder is unexpectedly disabled.",
+ )
+ var action_press := InputEventJoypadButton.new()
+ action_press.button_index = JOY_BUTTON_A
+ action_press.pressed = true
+ Input.parse_input_event(action_press)
+ await process_frame
+ var action_release := InputEventJoypadButton.new()
+ action_release.button_index = JOY_BUTTON_A
+ action_release.pressed = false
+ Input.parse_input_event(action_release)
+ for _frame: int in 2:
+ await process_frame
+ _expect(
+ int(open_activation["count"]) == 1,
+ (
+ "Explicitly accepting Open Data Folder pressed it %d times."
+ % int(open_activation["count"])
+ ),
+ )
+ _expect(
+ panel.find_children(
+ "*", "ConfirmationDialog", true, false
+ ).is_empty(),
+ "Open Data Folder left a controller-activatable dialog behind.",
+ )
+ var change_data_folder := panel.get_node("%ChangeDataFolder") as Button
+ var copy_fingerprint := panel.get_node("%CopyPlayerFingerprint") as Button
+ var export_player := panel.get_node("%ExportPlayerIdentity") as Button
+ var import_player := panel.get_node("%ImportPlayerIdentity") as Button
+ var export_host := panel.get_node("%ExportHostIdentity") as Button
+ var import_host := panel.get_node("%ImportHostIdentity") as Button
+ var controls: Array[Control] = [
+ data_tab,
+ open_data_folder,
+ change_data_folder,
+ copy_fingerprint,
+ export_player,
+ import_player,
+ export_host,
+ import_host,
+ panel.get_node("%ApplySettingsButton") as Control,
+ panel.get_node("%SettingsBackButton") as Control,
+ ]
+ for control: Control in controls:
+ _expect(
+ control.focus_mode == Control.FOCUS_ALL,
+ "Data & Identity control %s is not controller-focusable."
+ % control.name,
+ )
+ _assert_neighbor(data_tab, &"focus_neighbor_bottom", open_data_folder)
+ _assert_neighbor(
+ open_data_folder,
+ &"focus_neighbor_right",
+ change_data_folder,
+ )
+ _assert_neighbor(
+ open_data_folder,
+ &"focus_neighbor_bottom",
+ copy_fingerprint,
+ )
+ _assert_neighbor(
+ change_data_folder,
+ &"focus_neighbor_left",
+ open_data_folder,
+ )
+ _assert_neighbor(
+ change_data_folder,
+ &"focus_neighbor_bottom",
+ copy_fingerprint,
+ )
+ _assert_neighbor(
+ copy_fingerprint,
+ &"focus_neighbor_top",
+ open_data_folder,
+ )
+ _assert_neighbor(
+ copy_fingerprint,
+ &"focus_neighbor_bottom",
+ export_player,
+ )
+ _assert_neighbor(export_player, &"focus_neighbor_right", import_player)
+ _assert_neighbor(export_player, &"focus_neighbor_bottom", export_host)
+ _assert_neighbor(import_player, &"focus_neighbor_left", export_player)
+ _assert_neighbor(import_player, &"focus_neighbor_bottom", import_host)
+ _assert_neighbor(export_host, &"focus_neighbor_top", export_player)
+ _assert_neighbor(export_host, &"focus_neighbor_right", import_host)
+ _assert_neighbor(import_host, &"focus_neighbor_top", import_player)
+ _assert_neighbor(import_host, &"focus_neighbor_left", export_host)
+ _assert_directionally_reachable(controls.front(), controls)
+ await process_frame
+ open_data_folder.grab_focus()
+ panel.call("_select_page", &"sound", true)
+ _expect(
+ panel.get_active_page_id() == &"sound",
+ "Settings did not leave the Data page.",
+ )
+ _expect(
+ root.gui_get_focus_owner() == panel.get_node("%SoundTab"),
+ "Leaving Data retained focus on a hidden Data action.",
+ )
+ var sound_down: Control = (
+ (panel.get_node("%SoundTab") as Control).find_valid_focus_neighbor(
+ SIDE_BOTTOM
+ )
+ )
+ _expect(
+ sound_down != null and not panel.get_node("%DataPage").is_ancestor_of(
+ sound_down
+ ),
+ "Sound navigation still pointed into the hidden Data page.",
+ )
+ var hidden_data_activation_count: int = int(open_activation["count"])
+ Input.parse_input_event(action_press)
+ await process_frame
+ Input.parse_input_event(action_release)
+ for _frame: int in 2:
+ await process_frame
+ _expect(
+ int(open_activation["count"]) == hidden_data_activation_count,
+ "Controller A activated Open Data Folder from another settings page.",
+ )
+ open_data_folder.pressed.emit()
+ await process_frame
+ _expect(
+ panel.find_children(
+ "*", "ConfirmationDialog", true, false
+ ).is_empty(),
+ "A hidden Data action created a folder dialog.",
+ )
+ panel.call("_select_page", &"data", true)
+ await process_frame
+ copy_fingerprint.grab_focus()
+ var feedback := panel.get_node("%SettingsFeedback") as Label
+ feedback.text = "activation boundary intact"
+ open_data_folder.pressed.emit()
+ await process_frame
+ _expect(
+ feedback.text == "activation boundary intact",
+ (
+ "Open Data Folder crossed its activation boundary without owning "
+ + "controller focus."
+ ),
+ )
+ panel.hide()
+ await process_frame
+ var hidden_focus_owner: Control = root.gui_get_focus_owner()
+ _expect(
+ hidden_focus_owner == null
+ or not panel.is_ancestor_of(hidden_focus_owner),
+ "Hiding Settings retained focus on one of its controls.",
+ )
+ _expect(
+ panel.find_children("*", "BubbleButton", true, false).is_empty(),
+ "Settings children still contain bubble controls.",
+ )
+ panel.queue_free()
+ await process_frame
+
+
+func _validate_settings_adjustment_navigation() -> void:
+ var panel := SettingsPanelScene.instantiate() as SettingsPanel
+ root.add_child(panel)
+ await process_frame
+ panel.show()
+ panel.call("_select_page", &"sound", false)
+ for _frame: int in 2:
+ await process_frame
+ var sound_controls: Array[Control] = [
+ panel.get_node("%SoundTab") as Control,
+ panel.get_node("%MasterVolumeSlider") as Control,
+ panel.get_node("%MusicVolumeSlider") as Control,
+ panel.get_node("%EffectsVolumeSlider") as Control,
+ panel.get_node("%EnvironmentVolumeSlider") as Control,
+ panel.get_node("%ApplySettingsButton") as Control,
+ panel.get_node("%SettingsBackButton") as Control,
+ ]
+ _assert_directionally_reachable(sound_controls.front(), sound_controls)
+ panel.call("_select_page", &"controls", false)
+ for _frame: int in 2:
+ await process_frame
+ var mouse_slider := panel.get_node("%MouseSensitivitySlider") as HSlider
+ var controller_slider := panel.get_node(
+ "%ControllerSensitivitySlider"
+ ) as HSlider
+ var on_screen_keyboard := panel.get_node(
+ "%OnScreenKeyboardToggle"
+ ) as Button
+ var swap_scroll := panel.get_node("%SwapScrollToggle") as Button
+ var invert_y := panel.get_node("%InvertYToggle") as Button
+ var controller_binds := panel.get_node("%ControllerMapping") as Button
+ var keyboard_binds := panel.get_node("%KeyboardMapping") as Button
+ _expect(
+ mouse_slider.focus_mode == Control.FOCUS_ALL
+ and controller_slider.focus_mode == Control.FOCUS_ALL,
+ "Standard sensitivity sliders are not controller-focusable.",
+ )
+ _expect(
+ is_equal_approx(mouse_slider.step, 0.0005)
+ and is_equal_approx(controller_slider.step, 0.1),
+ "Sensitivity sliders do not retain their authored increments.",
+ )
+ _assert_neighbor(
+ invert_y,
+ &"focus_neighbor_bottom",
+ swap_scroll,
+ )
+ _assert_neighbor(
+ swap_scroll,
+ &"focus_neighbor_top",
+ invert_y,
+ )
+ _assert_neighbor(
+ swap_scroll,
+ &"focus_neighbor_bottom",
+ on_screen_keyboard,
+ )
+ _assert_neighbor(
+ on_screen_keyboard,
+ &"focus_neighbor_top",
+ swap_scroll,
+ )
+ _assert_neighbor(
+ on_screen_keyboard,
+ &"focus_neighbor_bottom",
+ controller_binds,
+ )
+ _assert_neighbor(
+ controller_binds,
+ &"focus_neighbor_top",
+ on_screen_keyboard,
+ )
+ _assert_neighbor(
+ controller_binds,
+ &"focus_neighbor_right",
+ keyboard_binds,
+ )
+ _assert_neighbor(
+ keyboard_binds,
+ &"focus_neighbor_top",
+ on_screen_keyboard,
+ )
+ _assert_neighbor(
+ keyboard_binds,
+ &"focus_neighbor_left",
+ controller_binds,
+ )
+ var control_inputs: Array[Control] = [
+ panel.get_node("%ControlsTab") as Control,
+ mouse_slider,
+ controller_slider,
+ invert_y,
+ swap_scroll,
+ on_screen_keyboard,
+ controller_binds,
+ keyboard_binds,
+ panel.get_node("%ApplySettingsButton") as Control,
+ panel.get_node("%SettingsBackButton") as Control,
+ ]
+ _assert_directionally_reachable(control_inputs.front(), control_inputs)
+ panel.queue_free()
+ await process_frame
+
+
+func _validate_settings_presentation_requires_apply() -> void:
+ var panel := SettingsPanelScene.instantiate() as SettingsPanel
+ var settings_manager := PlayerSettingsManager.new()
+ root.add_child(settings_manager)
+ root.add_child(panel)
+ await process_frame
+ panel.open_panel(settings_manager)
+ await process_frame
+ var chat_dock := panel.get_node("%ChatDockSelector") as OptionButton
+ var chat_mode := panel.get_node("%ChatModeSelector") as OptionButton
+ var paint_dock := panel.get_node("%PaintDockSelector") as OptionButton
+ chat_dock.select(1)
+ chat_dock.item_selected.emit(1)
+ chat_mode.select(1)
+ chat_mode.item_selected.emit(1)
+ paint_dock.select(0)
+ paint_dock.item_selected.emit(0)
+ _expect(
+ not settings_manager.current_settings.chat_dock_right
+ and not settings_manager.current_settings.chat_mobile_mode
+ and settings_manager.current_settings.paint_dock_right
+ and not settings_manager.current_settings.presentation_layout_customized,
+ (
+ "Navigating presentation choices changed saved docking settings "
+ + "before Apply."
+ ),
+ )
+ panel.call("_apply_settings")
+ _expect(
+ settings_manager.current_settings.chat_dock_right
+ and settings_manager.current_settings.chat_mobile_mode
+ and not settings_manager.current_settings.paint_dock_right
+ and settings_manager.current_settings.presentation_layout_customized,
+ "Applying deliberate presentation choices did not retain them.",
+ )
+ panel.close_panel(true)
+ panel.queue_free()
+ settings_manager.queue_free()
+ await process_frame
+
+
+func _validate_mail_navigation() -> void:
+ var page := MailPageType.new() as Control
+ root.add_child(page)
+ await process_frame
+ page.call("activate")
+ page.call("set_interactive", true)
+ var inbox_list := page.get("_inbox_list") as VBoxContainer
+ var entry := Button.new()
+ entry.text = "test letter"
+ entry.custom_minimum_size = Vector2(1008.0, 54.0)
+ inbox_list.add_child(entry)
+ await process_frame
+ page.call("_refresh_controller_navigation")
+ var archive_view := page.get("_archive_view_button") as Button
+ var send_mail := page.get("_send_mail_button") as Button
+ _assert_neighbor(
+ archive_view, &"focus_neighbor_right", send_mail
+ )
+ _assert_neighbor(archive_view, &"focus_neighbor_bottom", entry)
+ _assert_neighbor(send_mail, &"focus_neighbor_bottom", entry)
+ _assert_neighbor(entry, &"focus_neighbor_top", archive_view)
+ _assert_directionally_reachable(
+ archive_view, [archive_view, send_mail, entry]
+ )
+
+ var inbox := page.get("_inbox") as Control
+ var compose := page.get("_compose") as Control
+ var letter := page.get("_letter") as Control
+ inbox.hide()
+ compose.hide()
+ letter.show()
+ var accept := page.get("_accept") as Button
+ var decline := page.get("_decline") as Button
+ var close := page.get("_letter_close") as Button
+ var archive := page.get("_archive") as Button
+ var delete := page.get("_delete") as Button
+ accept.show()
+ decline.show()
+ delete.disabled = false
+ page.call("_refresh_controller_navigation")
+ _assert_neighbor(accept, &"focus_neighbor_bottom", decline)
+ _assert_neighbor(decline, &"focus_neighbor_bottom", delete)
+ _assert_neighbor(delete, &"focus_neighbor_left", archive)
+ _assert_neighbor(archive, &"focus_neighbor_left", close)
+ _assert_neighbor(close, &"focus_neighbor_top", close)
+ _assert_directionally_reachable(
+ accept, [accept, decline, close, archive, delete]
+ )
+ page.queue_free()
+ await process_frame
+
+
+func _validate_profile_confirmation_focus() -> void:
+ var page := ProfilePageType.new() as Control
+ root.add_child(page)
+ await process_frame
+ page.call("activate")
+ page.call("set_interactive", true)
+ page.call("reset_controller_zone")
+ var name_edit := page.get("_name_edit") as LineEdit
+ name_edit.grab_focus()
+ page.call("_on_name_changed", "V")
+ _expect(
+ root.gui_get_focus_owner() == name_edit,
+ "Typing a profile name released the active name field.",
+ )
+ var suggestions := page.get("_suggestions") as HBoxContainer
+ var suggestion := Button.new()
+ suggestion.text = "alternate name"
+ suggestions.add_child(suggestion)
+ page.call("_apply_controller_zone_focus")
+ var account_controls: Array[Control] = []
+ for item: Variant in page.call("_account_controller_controls"):
+ account_controls.append(item as Control)
+ var preview := page.get("_preview") as Control
+ var reset_view := page.get("_reset_view_button") as Button
+ var reset_view_hint := page.get("_reset_view_controller_hint") as Label
+ _expect(
+ suggestion in account_controls,
+ "Profile name-conflict choices are outside the account zone.",
+ )
+ _expect(
+ suggestion.focus_mode == Control.FOCUS_ALL,
+ "Profile name-conflict choices are not controller-focusable.",
+ )
+ _expect(
+ preview.focus_mode == Control.FOCUS_NONE,
+ "The profile preview must not take controller focus.",
+ )
+ _expect(
+ reset_view.focus_mode == Control.FOCUS_NONE,
+ "The profile reset-view button must not take controller focus.",
+ )
+ _expect(
+ reset_view_hint != null
+ and reset_view_hint.text == "Reset View: RS"
+ and reset_view_hint.focus_mode == Control.FOCUS_NONE,
+ "The profile preview does not present its right-stick reset hint.",
+ )
+ preview.set("_camera_yaw", 1.0)
+ preview.set("_camera_pitch", 0.4)
+ preview.set("_camera_distance", 2.5)
+ var reset_view_event := InputEventJoypadButton.new()
+ reset_view_event.button_index = JOY_BUTTON_RIGHT_STICK
+ reset_view_event.pressed = true
+ _expect(
+ bool(page.call("handle_controller_input", reset_view_event)),
+ "Profile customization did not consume right-stick reset.",
+ )
+ _expect(
+ is_equal_approx(float(preview.get("_camera_yaw")), 0.0)
+ and is_equal_approx(
+ float(preview.get("_camera_pitch")),
+ ProfilePreview.DEFAULT_CAMERA_PITCH,
+ )
+ and is_equal_approx(
+ float(preview.get("_camera_distance")),
+ ProfilePreview.DEFAULT_CAMERA_DISTANCE,
+ ),
+ "Right-stick click did not restore the default profile preview view.",
+ )
+ page.call("_show_confirmation", "defaults")
+ var confirmation := page.get("_discard_confirmation") as Control
+ var confirm := page.get("_confirmation_confirm") as Button
+ var keep_editing := page.get("_keep_editing_button") as Button
+ _expect(confirmation.visible, "The profile confirmation did not open.")
+ _expect(
+ confirm.focus_mode == Control.FOCUS_ALL,
+ "The profile confirmation action is not controller-focusable.",
+ )
+ _expect(
+ keep_editing.focus_mode == Control.FOCUS_ALL,
+ "The profile confirmation cancel action is not controller-focusable.",
+ )
+ for control: Control in account_controls:
+ _expect(
+ control.focus_mode == Control.FOCUS_NONE,
+ "Profile confirmation leaked focus to %s." % control.name,
+ )
+ var cancel_event := InputEventAction.new()
+ cancel_event.action = &"ui_cancel"
+ cancel_event.pressed = true
+ _expect(
+ bool(page.call("handle_controller_input", cancel_event)),
+ "Profile confirmation did not consume controller Back.",
+ )
+ _expect(
+ not confirmation.visible,
+ "Controller Back did not close the profile confirmation.",
+ )
+ for control: Control in account_controls:
+ _expect(
+ control.focus_mode == Control.FOCUS_ALL,
+ "Profile account focus was not restored after confirmation.",
+ )
+ page.call("_enter_controller_customization")
+ for _frame: int in 2:
+ await process_frame
+ var categories: Array[Control] = []
+ var category_list := page.get("_category_list") as VBoxContainer
+ for item: Variant in page.call("_controls_under", category_list):
+ categories.append(item as Control)
+ var initial_category_focus := root.gui_get_focus_owner() as Control
+ _expect(
+ initial_category_focus in categories,
+ "Customize did not focus the appearance feature list.",
+ )
+ var down := InputEventAction.new()
+ down.action = &"ui_down"
+ down.pressed = true
+ Input.parse_input_event(down)
+ for _frame: int in 2:
+ await process_frame
+ var next_category_focus := root.gui_get_focus_owner() as Control
+ _expect(
+ next_category_focus in categories,
+ "Moving down dropped focus from the appearance feature list.",
+ )
+ _expect(
+ next_category_focus != initial_category_focus,
+ "Moving down did not advance through appearance features.",
+ )
+ down.pressed = false
+ Input.parse_input_event(down)
+ var accept_event := InputEventAction.new()
+ accept_event.action = &"ui_accept"
+ accept_event.pressed = true
+ _expect(
+ bool(page.call("handle_controller_input", accept_event)),
+ "Appearance feature selection did not consume controller Accept.",
+ )
+ for _frame: int in 3:
+ await process_frame
+ var option_groups: Array = page.call("_controller_option_groups")
+ var option_controls: Array[Control] = []
+ if not option_groups.is_empty():
+ for item: Variant in option_groups.front():
+ var option_control := item as Control
+ if option_control != null:
+ option_controls.append(option_control)
+ _expect(
+ root.gui_get_focus_owner() in option_controls,
+ "Selecting an appearance feature did not focus its options.",
+ )
+ var rebuilt_option := root.gui_get_focus_owner() as BaseButton
+ if rebuilt_option != null:
+ rebuilt_option.pressed.emit()
+ for _frame: int in 3:
+ await process_frame
+ option_groups = page.call("_controller_option_groups")
+ option_controls.clear()
+ if not option_groups.is_empty():
+ for item: Variant in option_groups.front():
+ var option_control := item as Control
+ if option_control != null:
+ option_controls.append(option_control)
+ _expect(
+ root.gui_get_focus_owner() in option_controls,
+ "Rebuilding appearance options dropped controller focus.",
+ )
+ root.gui_release_focus()
+ var controller_down := InputEventJoypadButton.new()
+ controller_down.button_index = JOY_BUTTON_DPAD_DOWN
+ controller_down.pressed = true
+ page.call("handle_controller_input", controller_down)
+ _expect(
+ root.gui_get_focus_owner() in option_controls,
+ "Appearance options did not recover a lost controller focus owner.",
+ )
+ var cancel_options := InputEventAction.new()
+ cancel_options.action = &"ui_cancel"
+ cancel_options.pressed = true
+ _expect(
+ bool(page.call("handle_controller_input", cancel_options)),
+ "Appearance options did not consume controller Back.",
+ )
+ for _frame: int in 2:
+ await process_frame
+ _expect(
+ root.gui_get_focus_owner() in categories,
+ "Controller Back did not return to the appearance feature list.",
+ )
+ page.queue_free()
+ await process_frame
+
+
+func _validate_profile_voice_navigation() -> void:
+ var page := ProfilePageType.new() as Control
+ page.set_anchors_and_offsets_preset(Control.PRESET_FULL_RECT)
+ root.add_child(page)
+ await process_frame
+ page.call("activate")
+ page.call("set_interactive", true)
+ page.call("_select_category", "voice")
+ await process_frame
+ page.set(
+ "_controller_zone",
+ ProfilePageType.ControllerZone.OPTIONS,
+ )
+ page.set("_controller_option_depth", 0)
+ page.call("_apply_controller_zone_focus")
+ page.call("_focus_controller_zone")
+ await process_frame
+ var sample_grid := page.find_child(
+ "VoiceSampleSetGrid", true, false
+ ) as GridContainer
+ var pitch_grid := page.find_child(
+ "VoicePitchGrid", true, false
+ ) as GridContainer
+ var speed_grid := page.find_child(
+ "VoiceSpeedGrid", true, false
+ ) as GridContainer
+ var call_grid := page.find_child(
+ "VoiceCallGrid", true, false
+ ) as GridContainer
+ _expect(
+ is_instance_valid(sample_grid)
+ and is_instance_valid(pitch_grid)
+ and is_instance_valid(speed_grid)
+ and is_instance_valid(call_grid),
+ "The voice page is missing a settings section.",
+ )
+ if (
+ not is_instance_valid(sample_grid)
+ or not is_instance_valid(pitch_grid)
+ or not is_instance_valid(speed_grid)
+ or not is_instance_valid(call_grid)
+ ):
+ page.queue_free()
+ await process_frame
+ return
+ var sample_buttons: Array[Control] = []
+ for item: Variant in page.call("_controls_under", sample_grid):
+ sample_buttons.append(item as Control)
+ _expect(
+ sample_buttons.size() == 3,
+ "The voice-set row must expose kat, robot, and kim.",
+ )
+ if sample_buttons.size() != 3:
+ page.queue_free()
+ await process_frame
+ return
+ var kat_button := sample_grid.get_node("VoiceSet_kat") as Button
+ var robot_button := sample_grid.get_node("VoiceSet_robot") as Button
+ var kim_button := sample_grid.get_node("VoiceSet_kim") as Button
+ _expect(kat_button.text == "kat", "The default voice set is not labeled kat.")
+ _expect(robot_button.text == "robot", "The tone voice set is not labeled robot.")
+ _expect(kim_button.text == "kim", "The kim voice set is not labeled kim.")
+ _expect(kat_button.button_pressed, "The kat voice set is not selected by default.")
+ var option_controls: Array[Control] = []
+ var option_groups: Array = page.call("_controller_option_groups")
+ for item: Variant in option_groups[0]:
+ option_controls.append(item as Control)
+ _expect(
+ option_controls.size()
+ == (
+ VoiceProfilesType.SAMPLE_SET_OPTIONS.size()
+ + VoiceProfilesType.OPTIONS.size()
+ + VoiceProfilesType.SPEED_OPTIONS.size()
+ + VoiceProfilesType.CALL_OPTIONS.size()
+ ),
+ "The controller voice zone does not contain every voice setting.",
+ )
+ _assert_directionally_reachable(kat_button, option_controls)
+ var kat_down := kat_button.get_node_or_null(
+ kat_button.focus_neighbor_bottom
+ ) as Control
+ _expect(
+ kat_down != null and pitch_grid.is_ancestor_of(kat_down),
+ "Down from the voice-set row does not enter the pitch row.",
+ )
+ if OS.has_environment("straywild_PROFILE_VOICE_CAPTURE"):
+ await RenderingServer.frame_post_draw
+ var capture := root.get_viewport().get_texture().get_image()
+ _expect(
+ capture.save_png(OS.get_environment(
+ "straywild_PROFILE_VOICE_CAPTURE"
+ )) == OK,
+ "The profile voice-page capture could not be saved.",
+ )
+ robot_button.grab_focus()
+ robot_button.button_pressed = true
+ robot_button.pressed.emit()
+ await process_frame
+ _expect(
+ root.gui_get_focus_owner() == robot_button,
+ "Selecting robot moved controller focus out of the voice-set row.",
+ )
+ _expect(
+ str(page.get("_draft_sample_set_id")) == "robot",
+ "Selecting robot did not update the profile draft.",
+ )
+ kim_button.grab_focus()
+ kim_button.button_pressed = true
+ kim_button.pressed.emit()
+ await process_frame
+ _expect(
+ root.gui_get_focus_owner() == kim_button,
+ "Selecting kim moved controller focus out of the voice-set row.",
+ )
+ _expect(
+ str(page.get("_draft_sample_set_id")) == "kim",
+ "Selecting kim did not update the profile draft.",
+ )
+ var category_controls: Array[Control] = []
+ for item: Variant in page.call(
+ "_controls_under", page.get("_category_list")
+ ):
+ category_controls.append(item as Control)
+ for category_control: Control in category_controls:
+ _expect(
+ category_control.focus_mode == Control.FOCUS_NONE,
+ "The voice options zone leaked focus into profile categories.",
+ )
+ page.queue_free()
+ await process_frame
+
+
+func _validate_player_menu_nested_controller_back() -> void:
+ var menu := PlayerMenuScene.instantiate() as Control
+ root.add_child(menu)
+ await process_frame
+ menu.visible = true
+ menu.set("_current_section", PlayerMenuType.Section.PROFILE)
+ var profile_page := menu.get("_profile_page") as Control
+ profile_page.call("activate")
+ profile_page.call("set_interactive", true)
+ profile_page.call("_enter_controller_customization")
+ for _frame: int in 2:
+ await process_frame
+ var categories: Array[Control] = []
+ var category_list := profile_page.get(
+ "_category_list"
+ ) as VBoxContainer
+ for item: Variant in profile_page.call(
+ "_controls_under", category_list
+ ):
+ categories.append(item as Control)
+ var initial_focus := root.gui_get_focus_owner() as Control
+ menu.call("_reserve_visible_secondary_navigation")
+ await process_frame
+ _expect(
+ root.gui_get_focus_owner() in categories,
+ "Player-menu focus reservation disabled appearance features.",
+ )
+ var down := InputEventAction.new()
+ down.action = &"ui_down"
+ down.pressed = true
+ Input.parse_input_event(down)
+ for _frame: int in 2:
+ await process_frame
+ _expect(
+ root.gui_get_focus_owner() in categories,
+ "Player-menu Down dropped appearance feature focus.",
+ )
+ _expect(
+ root.gui_get_focus_owner() != initial_focus,
+ "Player-menu Down did not advance appearance feature focus.",
+ )
+ down.pressed = false
+ Input.parse_input_event(down)
+ _expect(
+ bool(menu.call("consume_escape")),
+ "Global player-menu Back did not consume the Profile category zone.",
+ )
+ _expect(
+ menu.visible,
+ "Global player-menu Back closed Profile from a nested zone.",
+ )
+ _expect(
+ int(profile_page.get("_controller_zone"))
+ == ProfilePageType.ControllerZone.ACCOUNT,
+ "Global player-menu Back did not return Profile to its account zone.",
+ )
+
+ var logbook_page := menu.get("_catalog_logbook") as Control
+ logbook_page.set("_active", true)
+ logbook_page.set("_interactive", true)
+ logbook_page.set(
+ "_controller_zone",
+ LogbookPageType.ControllerZone.DETAILS,
+ )
+ menu.set("_current_section", PlayerMenuType.Section.LOGBOOK)
+ _expect(
+ bool(menu.call("consume_escape")),
+ "Global player-menu Back did not consume the Logbook detail zone.",
+ )
+ _expect(
+ menu.visible,
+ "Global player-menu Back closed Logbook from a nested zone.",
+ )
+ _expect(
+ int(logbook_page.get("_controller_zone"))
+ == LogbookPageType.ControllerZone.ENTRIES,
+ "Global player-menu Back did not return Logbook to creature selection.",
+ )
+ logbook_page.call("_set_index_open", true, false)
+ logbook_page.set(
+ "_controller_zone",
+ LogbookPageType.ControllerZone.INDEX,
+ )
+ _expect(
+ bool(menu.call("consume_escape")),
+ "Global player-menu Back did not consume the Logbook index zone.",
+ )
+ _expect(
+ menu.visible and not bool(logbook_page.get("_index_open")),
+ "Global player-menu Back did not return from the Logbook index page.",
+ )
+ _expect(
+ int(logbook_page.get("_controller_zone"))
+ == LogbookPageType.ControllerZone.TABS,
+ "Global player-menu Back did not return Logbook index focus to tabs.",
+ )
+
+ var net_page := menu.get("_the_net_page") as Control
+ net_page.set("_active", true)
+ net_page.set("_interactive", true)
+ net_page.set(
+ "_controller_zone",
+ TheNetPageType.ControllerZone.CLAIMS,
+ )
+ menu.set("_current_section", PlayerMenuType.Section.NET)
+ _expect(
+ bool(menu.call("consume_escape")),
+ "Global player-menu Back did not consume the Fishnet claims zone.",
+ )
+ _expect(
+ menu.visible,
+ "Global player-menu Back closed Fishnet from a nested zone.",
+ )
+ _expect(
+ int(net_page.get("_controller_zone"))
+ == TheNetPageType.ControllerZone.TABS,
+ "Global player-menu Back did not return Fishnet to its tabs.",
+ )
+
+ var mail_page := menu.get("_mail_page") as Control
+ mail_page.set("_active", true)
+ mail_page.set("_interactive", true)
+ (mail_page.get("_inbox") as Control).hide()
+ (mail_page.get("_compose") as Control).show()
+ (mail_page.get("_letter") as Control).hide()
+ menu.set("_current_section", PlayerMenuType.Section.MAIL)
+ _expect(
+ bool(menu.call("consume_escape")),
+ "Global player-menu Back did not consume the Mail compose zone.",
+ )
+ _expect(
+ menu.visible,
+ "Global player-menu Back closed Mail while composing.",
+ )
+ _expect(
+ (mail_page.get("_inbox") as Control).visible,
+ "Global player-menu Back did not return Mail to the inbox.",
+ )
+
+ var mapping_manager := ControllerMappingManagerType.new()
+ var bindings: Dictionary = ControllerMappingManagerType.default_bindings()
+ bindings[str(ControllerMappingManagerType.ROLE_B)] = {
+ "kind": "button",
+ "button": int(JOY_BUTTON_Y),
+ }
+ mapping_manager.set("_profiles", {
+ "default": {
+ "controller_name": "controller",
+ "bindings": bindings,
+ },
+ })
+ menu.call("setup_controller_mapping", mapping_manager)
+ var players_page := menu.get("_players_page") as Control
+ players_page.set("_active", true)
+ players_page.set("_interactive", true)
+ players_page.set(
+ "_controller_zone", PlayersPageType.ControllerZone.BODY
+ )
+ menu.set("_current_section", PlayerMenuType.Section.PLAYERS)
+ _expect(
+ bool(menu.call("consume_escape")),
+ "Global player-menu Back did not consume the Online body zone.",
+ )
+ _expect(
+ menu.visible,
+ "Global player-menu Back closed Online from its body zone.",
+ )
+ _expect(
+ int(players_page.get("_controller_zone"))
+ == PlayersPageType.ControllerZone.TABS,
+ "Global player-menu Back did not return Online to its tabs.",
+ )
+ players_page.set(
+ "_controller_zone", PlayersPageType.ControllerZone.BODY
+ )
+ var mapped_back := InputEventJoypadButton.new()
+ mapped_back.device = 0
+ mapped_back.button_index = JOY_BUTTON_Y
+ mapped_back.pressed = true
+ _expect(
+ bool(menu.call(
+ "_handle_controller_ownership_input", mapped_back
+ )),
+ "Player menu did not consume a mapped controller Back press.",
+ )
+ _expect(
+ int(players_page.get("_controller_zone"))
+ == PlayersPageType.ControllerZone.TABS,
+ "Mapped controller Back skipped the previous player-page zone.",
+ )
+ menu.queue_free()
+ await process_frame
+ mapping_manager.free()
+
+
+func _validate_inventory_tab_zone_transitions() -> void:
+ var menu := PlayerMenuScene.instantiate() as Control
+ root.add_child(menu)
+ await process_frame
+ var inventory_state := Node.new()
+ root.add_child(inventory_state)
+ var bag := PlayerBag.new()
+ var catches := FishInventory.new()
+ var storage_capacity := PlayerCoolerCapacity.new()
+ var layout := PlayerInventoryLayout.new()
+ var hotbar := PlayerHotbarType.new()
+ for node: Node in [bag, catches, storage_capacity, layout, hotbar]:
+ inventory_state.add_child(node)
+ bag.setup(ItemCatalogResource)
+ layout.setup(bag, catches, ItemCatalogResource, storage_capacity)
+ assert(layout.restore_backpack_level(1))
+ bag.set_inventory_layout(layout)
+ catches.set_inventory_layout(layout)
+ hotbar.setup(bag, ItemCatalogResource, catches, layout)
+ assert(bag.add_item(&"basic_fishing_rod", 1))
+ assert(bag.add_item(&"coffee", 1))
+ assert(bag.add_item(&"worms", 1))
+ assert(layout.move_entry(
+ PlayerInventoryLayout.EntryKind.ITEM,
+ &"basic_fishing_rod",
+ PlayerInventoryLayout.InventoryContainer.INVENTORY,
+ 9,
+ ))
+ assert(hotbar.assign_item(0, &"coffee"))
+ menu.set("_bag", bag)
+ menu.set("_inventory", catches)
+ menu.set("_hotbar", hotbar)
+ menu.set("_inventory_layout", layout)
+ menu.set("_item_catalog", ItemCatalogResource)
+ var inventory_grid := menu.get("_general_inventory_grid") as GeneralInventoryGrid
+ inventory_grid.setup(
+ layout,
+ bag,
+ catches,
+ hotbar,
+ ItemCatalogResource,
+ PlayerInventoryLayout.InventoryContainer.INVENTORY,
+ )
+ menu.visible = true
+ menu.call(
+ "_show_section_immediate",
+ PlayerMenuType.Section.BAG,
+ )
+ menu.call("_set_content_interactive", true)
+ menu.call("_enter_inventory_tabs_zone")
+ for _frame: int in 2:
+ await process_frame
+ var inventory_tab := menu.get_node("%BagSubTab") as Button
+ var tackle_tab := menu.get_node("%TackleSubTab") as Button
+ _expect(
+ root.gui_get_focus_owner() == inventory_tab,
+ "Inventory tab zone did not begin on the unified Inventory tab.",
+ )
+ _expect(inventory_tab.text == "Inventory", "Unified tab is not labelled Inventory.")
+ _expect(
+ not (menu.get_node("%CoolerSubTab") as Button).visible
+ and not (menu.get_node("%ItemsSubTab") as Button).visible,
+ "Legacy Cooler or Items tabs remain visible.",
+ )
+
+ var right := InputEventAction.new()
+ right.action = &"ui_right"
+ right.pressed = true
+ _expect(
+ bool(menu.call("_handle_controller_ownership_input", right)),
+ "Inventory tab zone did not consume controller Right.",
+ )
+ await _wait_for_player_menu_page_transition(menu)
+ _expect(
+ menu.get("_current_section") == PlayerMenuType.Section.TACKLE_BOX,
+ "Controller Right did not switch from Inventory to Tackle.",
+ )
+ _expect(
+ menu.get("_controller_ownership")
+ == PlayerMenuType.ControllerOwnership.INVENTORY_TABS,
+ "Changing Inventory tabs entered the item-content zone without A.",
+ )
+ _expect(
+ root.gui_get_focus_owner() == tackle_tab,
+ "Changing Inventory tabs did not keep focus on the selected tab.",
+ )
+ var down := InputEventAction.new()
+ down.action = &"ui_down"
+ down.pressed = true
+ _expect(
+ bool(menu.call("_handle_controller_ownership_input", down)),
+ "Inventory tab zone did not consume controller Down.",
+ )
+ await process_frame
+ _expect(
+ root.gui_get_focus_owner() == tackle_tab,
+ "Controller Down escaped the Inventory tab zone before A.",
+ )
+ var left := InputEventAction.new()
+ left.action = &"ui_left"
+ left.pressed = true
+ _expect(
+ bool(menu.call("_handle_controller_ownership_input", left)),
+ "Tackle-to-Inventory navigation did not consume controller Left.",
+ )
+ _expect(
+ menu.get("_current_section") == PlayerMenuType.Section.BAG
+ or bool(menu.get("_page_transitioning")),
+ "Tackle-to-Inventory input did not start the Inventory transition.",
+ )
+ await _wait_for_player_menu_page_transition(menu)
+ _expect(
+ root.gui_get_focus_owner() == inventory_tab,
+ "Returning to Inventory did not retain tab focus before A.",
+ )
+ menu.call("_set_content_interactive", true)
+ menu.call("_configure_bag_item_focus")
+ var active_slots := inventory_grid.get_slots()
+ for slot: GeneralInventorySlot in active_slots:
+ _expect(
+ slot.focus_mode == Control.FOCUS_NONE,
+ "Inventory slots remained focusable while tabs owned the controller.",
+ )
+
+ var accept := InputEventJoypadButton.new()
+ accept.button_index = JOY_BUTTON_A
+ accept.pressed = true
+ _expect(
+ bool(menu.call("_handle_controller_ownership_input", accept)),
+ "Inventory tab zone did not consume controller A.",
+ )
+ _expect(
+ menu.get("_controller_ownership")
+ == PlayerMenuType.ControllerOwnership.ITEM_LIST,
+ "Controller A did not explicitly enter Inventory contents.",
+ )
+ for _frame: int in 2:
+ await process_frame
+ _expect(
+ menu.get("_current_section") == PlayerMenuType.Section.BAG,
+ "Entering Inventory contents changed section to %s."
+ % menu.get("_current_section"),
+ )
+ var first_item := active_slots[0] as Control
+ var second_item := active_slots[1] as Control
+ for slot: GeneralInventorySlot in active_slots:
+ _expect(
+ slot.focus_mode == Control.FOCUS_ALL,
+ "Accepting Inventory did not enable its content focus zone.",
+ )
+ _expect(
+ root.gui_get_focus_owner() == first_item,
+ "Entering Inventory did not focus its first slot.",
+ )
+ _expect(
+ first_item.focus_neighbor_right == first_item.get_path_to(second_item),
+ "Inventory slots do not provide horizontal controller navigation.",
+ )
+ Input.parse_input_event(right)
+ for _frame: int in 2:
+ await process_frame
+ _expect(
+ menu.get("_current_section") == PlayerMenuType.Section.BAG,
+ "Inventory slot navigation changed section to %s."
+ % menu.get("_current_section"),
+ )
+ _expect(
+ root.gui_get_focus_owner() == second_item,
+ "Controller Right did not move between Inventory slots.",
+ )
+ var right_release := InputEventAction.new()
+ right_release.action = &"ui_right"
+ right_release.pressed = false
+ Input.parse_input_event(right_release)
+ _expect(
+ bool(menu.call("consume_escape")),
+ "Global player-menu Back did not consume Inventory contents.",
+ )
+ _expect(
+ menu.visible,
+ "Global player-menu Back closed Inventory from its content zone.",
+ )
+ _expect(
+ menu.get("_controller_ownership")
+ == PlayerMenuType.ControllerOwnership.INVENTORY_TABS,
+ "Global player-menu Back did not return Inventory contents to tabs.",
+ )
+ for _frame: int in 2:
+ await process_frame
+ _expect(
+ menu.get("_current_section") == PlayerMenuType.Section.BAG,
+ "Returning from Inventory contents changed section to %s."
+ % menu.get("_current_section"),
+ )
+ _expect(
+ bool(menu.call("_handle_controller_ownership_input", accept)),
+ "Inventory tab zone did not re-enter Inventory contents.",
+ )
+ for _frame: int in 2:
+ await process_frame
+ var notepad_source := active_slots[9]
+ notepad_source.grab_focus()
+ var context_press := InputEventJoypadButton.new()
+ context_press.button_index = JOY_BUTTON_Y
+ context_press.pressed = true
+ _expect(
+ bool(menu.call(
+ "_handle_controller_ownership_input", context_press
+ )),
+ "Controller Y did not open the selected Inventory notepad.",
+ )
+ _expect(
+ menu.get("_controller_ownership")
+ == PlayerMenuType.ControllerOwnership.NOTEPAD_ACTIONS,
+ "Inventory notepad did not become the active controller zone.",
+ )
+ _expect(
+ (menu.get_node("%BagDetailConstellation") as Control).visible,
+ "Inventory notepad remained hidden after controller Y.",
+ )
+ _expect(
+ bool(menu.call("consume_escape")),
+ "Controller B did not close the Inventory notepad.",
+ )
+ _expect(
+ not (menu.get_node("%BagDetailConstellation") as Control).visible
+ and menu.get("_controller_ownership")
+ == PlayerMenuType.ControllerOwnership.ITEM_LIST,
+ "Closing the Inventory notepad did not restore the item zone.",
+ )
+ for _frame: int in 2:
+ await process_frame
+ var accept_release := InputEventJoypadButton.new()
+ accept_release.button_index = JOY_BUTTON_A
+ accept_release.pressed = false
+ notepad_source.grab_focus()
+ menu.call("_handle_controller_ownership_input", accept)
+ menu.call("_handle_controller_ownership_input", accept_release)
+ _expect(
+ str(menu.get("_inventory_move_identity")) == "basic_fishing_rod",
+ "Controller A did not pick up the focused Inventory item.",
+ )
+ var move_target := active_slots[10]
+ move_target.grab_focus()
+ menu.call("_handle_controller_ownership_input", accept)
+ menu.call("_handle_controller_ownership_input", accept_release)
+ _expect(
+ layout.get_key_at(
+ PlayerInventoryLayout.InventoryContainer.INVENTORY, 10
+ ) == PlayerInventoryLayout.item_key(&"basic_fishing_rod"),
+ "Controller A did not place the picked-up item in its target slot.",
+ )
+ var management_requests: Array[int] = [0]
+ menu.controller_hotbar_management_requested.connect(
+ func(_initial_slot: int) -> void:
+ management_requests[0] += 1
+ )
+ menu.set(
+ "_controller_ownership",
+ PlayerMenuType.ControllerOwnership.ITEM_LIST,
+ )
+ menu.call("_apply_inventory_controller_zone_focus_modes")
+ menu.call("_configure_bag_item_focus")
+ var hotbar_source := active_slots[10]
+ _expect(
+ menu.get("_current_section") == PlayerMenuType.Section.BAG,
+ "Hotbar navigation test left the unified Inventory section (%s)."
+ % menu.get("_current_section"),
+ )
+ _expect(
+ hotbar_source.focus_mode == Control.FOCUS_ALL,
+ "Final active Inventory row was not controller-focusable.",
+ )
+ hotbar_source.grab_focus()
+ _expect(
+ root.gui_get_focus_owner() == hotbar_source,
+ "Final Inventory row did not accept controller focus before Hotbar entry.",
+ )
+ _expect(
+ bool(menu.call("_controller_focus_is_on_last_inventory_row")),
+ "Focused Inventory slot was not recognized as part of the final row.",
+ )
+ _expect(
+ bool(menu.call("_handle_controller_ownership_input", down)),
+ "Down from the final Inventory row did not enter the hotbar zone.",
+ )
+ _expect(
+ menu.get("_controller_ownership")
+ == PlayerMenuType.ControllerOwnership.HOTBAR_MANAGEMENT,
+ "Equipment-to-hotbar navigation did not transfer controller ownership.",
+ )
+ _expect(
+ management_requests[0] == 1,
+ "Equipment-to-hotbar navigation did not open hotbar management.",
+ )
+ var up := InputEventAction.new()
+ up.action = &"ui_up"
+ up.pressed = true
+ _expect(
+ bool(menu.call("_handle_controller_ownership_input", up)),
+ "Up from hotbar management was not consumed.",
+ )
+ _expect(
+ menu.get("_controller_ownership")
+ == PlayerMenuType.ControllerOwnership.ITEM_LIST,
+ "Up from hotbar management did not return to Inventory contents.",
+ )
+ for _frame: int in 2:
+ await process_frame
+ _expect(
+ root.gui_get_focus_owner() == hotbar_source,
+ "Up from hotbar management did not restore the source item focus.",
+ )
+ _expect(
+ bool(menu.call("_handle_controller_ownership_input", down)),
+ "Inventory contents could not re-enter hotbar management.",
+ )
+ _expect(
+ management_requests[0] == 2,
+ "Re-entering the hotbar did not reopen hotbar management.",
+ )
+ _expect(
+ bool(menu.call("_handle_controller_ownership_input", accept)),
+ "Hotbar management did not consume controller A.",
+ )
+ _expect(
+ hotbar.get_item_id(0).is_empty(),
+ "Controller A did not return the selected hotbar item to Inventory.",
+ )
+ _expect(layout.is_item_in_inventory(&"coffee"), "Cleared hotbar item was lost.")
+ _expect(
+ bool(menu.call("consume_escape")),
+ "Global player-menu Back did not consume hotbar management.",
+ )
+ _expect(
+ menu.visible,
+ "Global player-menu Back closed Inventory from hotbar management.",
+ )
+ _expect(
+ menu.get("_controller_ownership")
+ == PlayerMenuType.ControllerOwnership.ITEM_LIST,
+ "Global player-menu Back did not return the hotbar to Inventory contents.",
+ )
+ for _frame: int in 2:
+ await process_frame
+
+ menu.call("_show_section_immediate", PlayerMenuType.Section.TACKLE_BOX)
+ menu.call("_set_content_interactive", true)
+ menu.set(
+ "_controller_ownership",
+ PlayerMenuType.ControllerOwnership.ITEM_LIST,
+ )
+ menu.call("_apply_inventory_controller_zone_focus_modes")
+ menu.call("_configure_tackle_item_focus")
+ for _frame: int in 2:
+ await process_frame
+ var tackle_buttons: Dictionary = menu.get("_tackle_item_buttons")
+ var worms_button := tackle_buttons.get(&"worms") as Button
+ _expect(worms_button != null, "Tackle test could not find unlocked worms.")
+ if worms_button != null:
+ worms_button.grab_focus()
+ menu.call("_handle_controller_ownership_input", accept)
+ menu.call("_handle_controller_ownership_input", accept_release)
+ _expect(
+ StringName(menu.get("_selected_tackle_item_id")).is_empty(),
+ "Controller A still activates a bait or lure inventory action.",
+ )
+ _expect(
+ not (menu.get_node("%TackleDetailPanel") as Control).visible,
+ "Controller A opened the tackle notepad reserved for Y.",
+ )
+ _expect(
+ bool(menu.call(
+ "_handle_controller_ownership_input", context_press
+ )),
+ "Controller Y did not open the bait/lure equip notepad.",
+ )
+ _expect(
+ menu.get("_controller_ownership")
+ == PlayerMenuType.ControllerOwnership.NOTEPAD_ACTIONS,
+ "Tackle notepad did not take controller ownership after Y.",
+ )
+ _expect(
+ (menu.get_node("%TackleDetailPanel") as Control).visible,
+ "Controller Y left the bait/lure equip notepad hidden.",
+ )
+ menu.queue_free()
+ inventory_state.queue_free()
+ await process_frame
+
+
+func _wait_for_player_menu_page_transition(menu: Control) -> void:
+ var frames_waited: int = 0
+ while bool(menu.get("_page_transitioning")) and frames_waited < 120:
+ await process_frame
+ frames_waited += 1
+ # Same-section Equipment/Items changes do not tween, but they restore the
+ # active tab focus with a deferred call just like completed page changes.
+ for _frame: int in 2:
+ await process_frame
+ _expect(
+ not bool(menu.get("_page_transitioning")),
+ "Inventory page transition did not finish within 120 frames.",
+ )
+
+
+func _validate_mapping_capture_contract() -> void:
+ var title_source: String = FileAccess.get_file_as_string(
+ "res://ui/title_screen.gd"
+ )
+ var game_ui_source: String = FileAccess.get_file_as_string(
+ "res://ui/game_ui.gd"
+ )
+ var chat_source: String = FileAccess.get_file_as_string(
+ "res://ui/chat_ui.gd"
+ )
+ var main_source: String = FileAccess.get_file_as_string(
+ "res://main/main.gd"
+ )
+ _expect(
+ title_source.contains(
+ "_settings_panel.is_input_mapping_capturing()"
+ ),
+ "Title input does not respect every active binding capture.",
+ )
+ _expect(
+ game_ui_source.contains("or is_input_mapping_capturing()"),
+ "Controller menu scrolling does not respect every binding capture.",
+ )
+ _expect(
+ chat_source.contains("func _configure_controller_focus()"),
+ "Chat does not author controller routes for its exterior controls.",
+ )
+ _expect(
+ chat_source.contains(
+ "Control.FOCUS_ALL if _opened else Control.FOCUS_NONE"
+ ),
+ "Passive chat controls can steal world controller focus.",
+ )
+ _expect(
+ main_source.contains("func _configure_popup_dialog("),
+ "Runtime popup dialogs do not receive authored controller routes.",
+ )
+
+
+func _validate_confirmation_dialog_navigation() -> void:
+ var dialog := ConfirmationDialog.new()
+ dialog.ok_button_text = "continue"
+ var fields := VBoxContainer.new()
+ var first := LineEdit.new()
+ first.placeholder_text = "passphrase"
+ first.custom_minimum_size = Vector2(420.0, 42.0)
+ fields.add_child(first)
+ var second := LineEdit.new()
+ second.placeholder_text = "confirm passphrase"
+ second.custom_minimum_size = Vector2(420.0, 42.0)
+ fields.add_child(second)
+ dialog.add_child(fields)
+ root.add_child(dialog)
+ dialog.popup_centered(Vector2i(560, 300))
+ for _frame: int in 2:
+ await process_frame
+ DialogControllerNavigationType.configure_scope(dialog, first)
+ await process_frame
+ var controls: Array[Control] = (
+ DialogControllerNavigationType.interactive_controls(dialog)
+ )
+ _expect(
+ controls.has(first) and controls.has(second),
+ "Confirmation-dialog text fields are not controller reachable.",
+ )
+ _expect(
+ controls.has(dialog.get_ok_button())
+ and controls.has(dialog.get_cancel_button()),
+ "Confirmation-dialog actions are not controller reachable.",
+ )
+ _assert_directionally_reachable(first, controls)
+ _expect(
+ dialog.gui_get_focus_owner() == first,
+ "Confirmation dialog did not focus its requested entry control.",
+ )
+ dialog.hide()
+ await process_frame
+ root.gui_release_focus()
+ dialog.popup_centered(Vector2i(560, 300))
+ await process_frame
+ DialogControllerNavigationType.configure_scope(dialog, first)
+ await process_frame
+ _expect(
+ dialog.gui_get_focus_owner() == first,
+ "A reopened dialog did not restore controller focus.",
+ )
+ dialog.queue_free()
+ await process_frame
+
+
+func _validate_bubble_confirmation_navigation() -> void:
+ for scene: PackedScene in [
+ BubbleConfirmationScene,
+ TitleConfirmationScene,
+ ]:
+ var page := scene.instantiate() as Control
+ root.add_child(page)
+ await process_frame
+ page.show()
+ page.call("_set_interactive", true)
+ var confirm := page.get_node("BubbleCluster/ConfirmButton") as Button
+ var cancel := page.get_node("BubbleCluster/CancelButton") as Button
+ _assert_neighbor(confirm, &"focus_neighbor_left", cancel)
+ _assert_neighbor(confirm, &"focus_neighbor_right", cancel)
+ _assert_neighbor(confirm, &"focus_neighbor_top", confirm)
+ _assert_neighbor(confirm, &"focus_neighbor_bottom", confirm)
+ _assert_neighbor(cancel, &"focus_neighbor_left", confirm)
+ _assert_neighbor(cancel, &"focus_neighbor_right", confirm)
+ _assert_neighbor(cancel, &"focus_neighbor_top", cancel)
+ _assert_neighbor(cancel, &"focus_neighbor_bottom", cancel)
+ page.queue_free()
+ await process_frame
+func _set_button_state(button: Button, shown: bool) -> void:
+ button.visible = shown
+ button.disabled = false
+
+
+func _assert_neighbor(
+ origin: Control,
+ property: StringName,
+ expected: Control,
+) -> void:
+ var path: NodePath = origin.get(property)
+ _expect(
+ not path.is_empty(),
+ "%s has no %s neighbor." % [origin.name, property],
+ )
+ if path.is_empty():
+ return
+ var actual := origin.get_node_or_null(path) as Control
+ _expect(
+ actual == expected,
+ "%s points %s to %s instead of %s."
+ % [
+ origin.name,
+ property,
+ actual.name if actual != null else "nothing",
+ expected.name,
+ ],
+ )
+
+
+func _assert_directionally_reachable(
+ start: Control,
+ controls: Array[Control],
+) -> void:
+ var expected: Dictionary[int, bool] = {}
+ for control: Control in controls:
+ expected[control.get_instance_id()] = true
+ var visited: Dictionary[int, bool] = {start.get_instance_id(): true}
+ var pending: Array[Control] = [start]
+ while not pending.is_empty():
+ var current: Control = pending.pop_front()
+ for side: Side in [SIDE_LEFT, SIDE_RIGHT, SIDE_TOP, SIDE_BOTTOM]:
+ var neighbor: Control = current.find_valid_focus_neighbor(side)
+ if (
+ neighbor == null
+ or not expected.has(neighbor.get_instance_id())
+ or visited.has(neighbor.get_instance_id())
+ ):
+ continue
+ visited[neighbor.get_instance_id()] = true
+ pending.append(neighbor)
+ _expect(
+ visited.size() == expected.size(),
+ "Only %d of %d controls are directionally reachable from %s."
+ % [visited.size(), expected.size(), start.name],
+ )
+
+
+func _expect(condition: bool, message: String) -> void:
+ if not condition:
+ _failures.append(message)
diff --git a/tests/controller_menu_accessibility_validation.gd.uid b/tests/controller_menu_accessibility_validation.gd.uid
new file mode 100644
index 0000000..a8b5c11
--- /dev/null
+++ b/tests/controller_menu_accessibility_validation.gd.uid
@@ -0,0 +1 @@
+uid://cdvy3ln6d3b1v
diff --git a/tests/controller_ui_navigation_validation.gd b/tests/controller_ui_navigation_validation.gd
index 7cecbab..24f39f9 100644
--- a/tests/controller_ui_navigation_validation.gd
+++ b/tests/controller_ui_navigation_validation.gd
@@ -6,6 +6,9 @@ const ControllerFocusNavigationType = preload(
const UIReferencePresentationType = preload(
"res://ui/ui_reference_presentation.gd"
)
+const FullscreenMenuPresentationType = preload(
+ "res://ui/fullscreen_menu_presentation.gd"
+)
func _initialize() -> void:
@@ -14,7 +17,13 @@ func _initialize() -> void:
func _run() -> void:
_validate_spatial_navigation()
+ _validate_strict_directional_navigation()
+ _validate_disabled_control_exclusion()
+ _validate_traversal_cycle()
_validate_controller_hierarchy_contract()
+ _validate_player_page_zone_contracts()
+ _validate_shop_navigation_contract()
+ _validate_settings_navigation_contract()
_validate_four_by_three_centering()
_validate_low_end_profile_contract()
print("Controller UI navigation validation: PASS")
@@ -38,34 +47,252 @@ func _validate_spatial_navigation() -> void:
host.queue_free()
+func _validate_strict_directional_navigation() -> void:
+ var host := Control.new()
+ root.add_child(host)
+ var origin := _make_button(host, "origin", Vector2(200.0, 200.0))
+ var mostly_below := _make_button(
+ host, "mostly_below", Vector2(250.0, 400.0)
+ )
+ var controls: Array[Control] = [origin, mostly_below]
+ ControllerFocusNavigationType.configure_spatial_neighbors(controls)
+ assert(origin.get_node(origin.focus_neighbor_right) == origin)
+ assert(origin.get_node(origin.focus_neighbor_top) == origin)
+ assert(origin.get_node(origin.focus_neighbor_bottom) == mostly_below)
+ host.queue_free()
+
+
+func _validate_disabled_control_exclusion() -> void:
+ var host := Control.new()
+ root.add_child(host)
+ var left := _make_button(host, "left", Vector2(50.0, 100.0))
+ var disabled := _make_button(host, "disabled", Vector2(200.0, 100.0))
+ var right := _make_button(host, "right", Vector2(350.0, 100.0))
+ disabled.disabled = true
+ disabled.focus_next = disabled.get_path_to(right)
+ ControllerFocusNavigationType.configure_spatial_neighbors(
+ [left, disabled, right]
+ )
+ assert(left.get_node(left.focus_neighbor_right) == right)
+ assert(right.get_node(right.focus_neighbor_left) == left)
+ assert(disabled.focus_next.is_empty())
+ host.queue_free()
+
+
+func _validate_traversal_cycle() -> void:
+ var host := Control.new()
+ root.add_child(host)
+ var first := _make_button(host, "first", Vector2(50.0, 50.0))
+ var second := _make_button(host, "second", Vector2(200.0, 50.0))
+ var third := _make_button(host, "third", Vector2(50.0, 150.0))
+ var controls: Array[Control] = [third, first, second]
+ ControllerFocusNavigationType.configure_spatial_neighbors(controls)
+ assert(first.get_node(first.focus_next) == second)
+ assert(second.get_node(second.focus_next) == third)
+ assert(third.get_node(third.focus_next) == first)
+ assert(first.get_node(first.focus_previous) == third)
+ 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(not source.contains("_handle_controller_secondary_switch"))
+ assert(source.contains("ControllerOwnership.INVENTORY_TABS"))
+ assert(source.contains("ControllerOwnership.SORT_FILTER"))
+ assert(source.contains("_reset_controller_zone_for_section"))
+ assert(source.contains("preserve_tab_zone"))
+ assert(source.contains("active_tab.call_deferred(\"grab_focus\")"))
+ assert(source.contains("CONTROLLER_PICKUP_HOLD_SECONDS"))
assert(source.contains("_reserve_main_navigation_for_page_switching"))
+ assert(source.contains(
+ "if not _is_inventory_section(_current_section):\n\t\treturn"
+ ))
+ assert(source.contains(
+ "_dispatch_active_page_controller_input(cancel_event)"
+ ))
+ assert(source.contains("configure_spatial_neighbors(candidates)"))
+
+
+func _validate_player_page_zone_contracts() -> void:
+ for path: String in [
+ "res://ui/logbook_page.gd",
+ "res://ui/the_net_page.gd",
+ "res://ui/mail_page.gd",
+ "res://ui/profile_page.gd",
+ "res://ui/players_page.gd",
+ ]:
+ var source: String = FileAccess.get_file_as_string(path)
+ assert(source.contains("func reset_controller_zone()"))
+ assert(source.contains("func handle_controller_input(event: InputEvent)"))
+ var profile_source: String = FileAccess.get_file_as_string(
+ "res://ui/profile_page.gd"
+ )
+ assert(profile_source.contains("ControllerZone.COLOR_PICKER"))
+ assert(profile_source.contains("_adjust_controller_color_gamut"))
+ assert(profile_source.contains("_customize_button.text = \"customize\""))
+ assert(profile_source.contains("_enter_controller_customization"))
+ assert(not profile_source.contains(
+ "if event.is_action_pressed(\"ui_down\"):\n"
+ + "\t\t\t_controller_zone = ControllerZone.CATEGORIES"
+ ))
+ assert(profile_source.contains(
+ "_reset_view_button.focus_mode = Control.FOCUS_NONE"
+ ))
+ var preview_source: String = FileAccess.get_file_as_string(
+ "res://ui/profile_preview.gd"
+ )
+ assert(preview_source.contains("focus_mode = Control.FOCUS_NONE"))
+ assert(not preview_source.contains("grab_focus()"))
+ var mail_source: String = FileAccess.get_file_as_string(
+ "res://ui/mail_page.gd"
+ )
+ assert(mail_source.contains("_configure_compose_controller_navigation"))
+ assert(mail_source.contains("_set_compose_neighbors"))
+ var net_source: String = FileAccess.get_file_as_string(
+ "res://ui/the_net_page.gd"
+ )
+ var game_ui_source: String = FileAccess.get_file_as_string(
+ "res://ui/game_ui.gd"
+ )
+ assert(not net_source.contains("ROLE_RIGHT_STICK_Y"))
+ assert(game_ui_source.contains("_update_controller_menu_scroll"))
+
+
+func _validate_shop_navigation_contract() -> void:
+ var source: String = FileAccess.get_file_as_string(
+ "res://ui/fishing_shop.gd"
+ )
+ assert(source.contains("ROLE_LB"))
+ assert(source.contains("ROLE_RB"))
+ assert(source.contains("_configure_controller_focus"))
+
+
+func _validate_settings_navigation_contract() -> void:
+ var settings_scene := load("res://ui/settings_panel.tscn") as PackedScene
+ var settings := settings_scene.instantiate() as SettingsPanel
+ root.add_child(settings)
+ var accessibility_tab := settings.get_node(
+ "%AccessibilityTab"
+ ) as Button
+ var auto_click_toggle := settings.get_node("%AutoClickToggle") as Button
+ var interval_slider := settings.get_node(
+ "%AutoClickIntervalSlider"
+ ) as HSlider
+ var apply_button := settings.get_node("%ApplySettingsButton") as Button
+ var back_button := settings.get_node("%SettingsBackButton") as Button
+ assert(
+ accessibility_tab.get_node(accessibility_tab.focus_neighbor_bottom)
+ == auto_click_toggle
+ )
+ assert(
+ auto_click_toggle.get_node(auto_click_toggle.focus_neighbor_bottom)
+ == interval_slider
+ )
+ assert(
+ interval_slider.get_node(interval_slider.focus_neighbor_top)
+ == auto_click_toggle
+ )
+ assert(
+ interval_slider.get_node(interval_slider.focus_neighbor_bottom)
+ == apply_button
+ )
+ assert(apply_button.get_node(apply_button.focus_neighbor_right) == back_button)
+ var reset_scene := load(
+ "res://ui/pixelation_reset_overlay.tscn"
+ ) as PackedScene
+ var reset_overlay := reset_scene.instantiate() as PixelationResetOverlay
+ root.add_child(reset_overlay)
+ settings.show()
+ reset_overlay.set_settings_open(true)
+ var reset_focus_requested: Array[bool] = [false]
+ settings.crisp_reset_focus_requested.connect(func() -> void:
+ reset_focus_requested[0] = true
+ )
+ settings.crisp_reset_focus_requested.connect(
+ reset_overlay.focus_reset_button
+ )
+ var reset_cursor_presentation := reset_overlay.get_node(
+ "ControllerFocusPresentation"
+ ) as ControllerFocusPresentation
+ var controller_right := InputEventJoypadButton.new()
+ controller_right.button_index = JOY_BUTTON_DPAD_RIGHT
+ controller_right.pressed = true
+ reset_cursor_presentation._input(controller_right)
+ var focus_reset_event := InputEventAction.new()
+ focus_reset_event.action = &"ui_right"
+ focus_reset_event.pressed = true
+ settings.call("_on_back_button_gui_input", focus_reset_event)
+ assert(reset_focus_requested[0])
+ assert(
+ root.gui_get_focus_owner()
+ == reset_overlay.get_node("%ResetPixelationButton")
+ )
+ assert(
+ (reset_cursor_presentation.get("_focus_arrow") as TextureRect).visible
+ )
+ reset_overlay.return_to_settings_requested.connect(
+ settings.focus_back_button
+ )
+ var return_event := InputEventAction.new()
+ return_event.action = &"ui_left"
+ return_event.pressed = true
+ reset_overlay.call("_on_reset_button_gui_input", return_event)
+ assert(root.gui_get_focus_owner() == back_button)
+ reset_overlay.queue_free()
+ settings.queue_free()
func _validate_four_by_three_centering() -> void:
- var stage_position: Vector2 = (
- UIReferencePresentationType.get_stage_position(Vector2(640.0, 480.0))
+ var display_size := Vector2(640.0, 480.0)
+ var stage_position: Vector2 = UIReferencePresentationType.get_stage_position(
+ display_size
)
assert(stage_position.is_equal_approx(Vector2(0.0, 120.0)))
+ assert(FullscreenMenuPresentationType.uses_compact_profile(display_size))
+ assert(is_equal_approx(
+ FullscreenMenuPresentationType.get_profile_scale(display_size),
+ 4.0 / 3.0,
+ ))
+ assert(FullscreenMenuPresentationType.get_profile_position(
+ display_size
+ ).is_equal_approx(Vector2(-640.0 / 3.0, -120.0)))
+ assert(FullscreenMenuPresentationType.get_source_rect(display_size)
+ == Rect2(160.0, 0.0, 960.0, 720.0))
func _validate_low_end_profile_contract() -> void:
var launcher: String = FileAccess.get_file_as_string(
- "res://scripts/portmaster/NETfishing.sh"
+ "res://scripts/portmaster/straywild.sh"
)
- assert(launcher.contains("allwinner,h616"))
- assert(launcher.contains("sun50iw9p1"))
- assert(launcher.contains("NETFISHING_LOW_END=1"))
+ assert(launcher.contains("straywild_PERFORMANCE_PROFILE:-"))
+ assert(launcher.contains("$CONFDIR/performance_profile"))
+ assert(launcher.contains("normal)"))
+ assert(launcher.contains("light|\"\")"))
+ assert(launcher.contains("straywild_PERFORMANCE_PROFILE=normal"))
+ assert(launcher.contains("straywild_PERFORMANCE_PROFILE=light"))
+ assert(launcher.contains("straywild_LOW_END=1"))
assert(launcher.contains("--max-fps 30"))
assert(launcher.contains("--audio-output-latency 40"))
+ var builder: String = FileAccess.get_file_as_string(
+ "res://scripts/build_portmaster.sh"
+ )
+ assert(builder.contains("prepare_portmaster_assets.sh"))
+ assert(builder.contains("straywild-portmaster-export"))
+ var asset_profile: String = FileAccess.get_file_as_string(
+ "res://scripts/prepare_portmaster_assets.sh"
+ )
+ assert(asset_profile.contains("GENERAL_TEXTURE_LIMIT=128"))
+ assert(asset_profile.contains("GAMEPLAY_TEXTURE_LIMIT=64"))
+ assert(asset_profile.contains("AUDIO_SAMPLE_RATE=22050"))
+ assert(asset_profile.contains("compress/mode=2"))
+ assert(asset_profile.contains("fish/species"))
+ assert(asset_profile.contains("art/exported/characters"))
+ assert(builder.contains("--exclude '/audio/ambience/'"))
+ assert(not builder.contains("--exclude '/audio/music/'"))
func _make_button(
diff --git a/tests/controller_world_interaction_validation.gd b/tests/controller_world_interaction_validation.gd
new file mode 100644
index 0000000..3d684cb
--- /dev/null
+++ b/tests/controller_world_interaction_validation.gd
@@ -0,0 +1,145 @@
+extends SceneTree
+
+const MainScene = preload("res://main/main.tscn")
+
+var _failures: Array[String] = []
+
+
+func _initialize() -> void:
+ _run.call_deferred()
+
+
+func _run() -> void:
+ root.size = Vector2i(1280, 720)
+ var main := 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
+ _expect(
+ bool(main.get("_application_initialized")),
+ "application initialization failed",
+ )
+ var save_manager := main.get("_save_manager") as PlayerSaveManager
+ var world_layout: StringName = WorldLayout.GENERATED
+ var world_seed: int = PlayerSaveManager.DEFAULT_WORLD_SEED
+ _expect(
+ bool(main.call("_apply_world", world_layout, world_seed, true)),
+ "generated-world preparation failed",
+ )
+ _expect(
+ bool(main.call("_prepare_host_world", world_layout, world_seed)),
+ "host-world preparation failed",
+ )
+ _expect(
+ bool(main.call("_prepare_private_host")),
+ "private host preparation failed",
+ )
+ _expect(
+ save_manager != null
+ and save_manager.initialize_new_game(world_seed, world_layout),
+ "new-game initialization failed",
+ )
+ main.call("_enter_gameplay")
+ for _frame: int in 8:
+ await process_frame
+
+ var player := main.get("_player") as Player
+ var interaction := main.get("_shop_interaction") as FishingShopInteraction
+ var game_ui := main.get("_game_ui") as GameUI
+ var chat_service := main.get_node(
+ "%NetworkChatService"
+ ) as NetworkChatService
+ _expect(player != null, "local player is unavailable")
+ _expect(interaction != null, "shop interaction is unavailable")
+ _expect(game_ui != null, "game UI is unavailable")
+ _expect(chat_service != null, "network chat service is unavailable")
+ if (
+ player != null
+ and interaction != null
+ and game_ui != null
+ and chat_service != null
+ ):
+ player.global_position = interaction.global_position
+ for _frame: int in 6:
+ await physics_frame
+ _expect(
+ interaction.is_local_player_in_range(),
+ "local player did not enter the shop interaction range",
+ )
+ var character_calls: Array[String] = []
+ var capture_call := func(
+ _peer_id: int,
+ call_id: String,
+ _pitch_scale: float,
+ ) -> void:
+ character_calls.append(call_id)
+ chat_service.character_call_received.connect(capture_call)
+ var press := InputEventJoypadButton.new()
+ press.device = 0
+ press.button_index = JOY_BUTTON_Y
+ press.pressed = true
+ _expect(
+ press.is_action_pressed("interact"),
+ "Y does not resolve to interact",
+ )
+ _expect(
+ press.is_action_pressed("character_call"),
+ "Y does not resolve to character call",
+ )
+ game_ui.call("_input", press)
+ main.call("_unhandled_input", press)
+ await process_frame
+ _expect(
+ game_ui.get_fishing_shop().visible,
+ "Y did not open the shop while the player was in range",
+ )
+ _expect(
+ character_calls.is_empty(),
+ "Y played a character call instead of opening the shop",
+ )
+ player.global_position = interaction.global_position + Vector3(20.0, 0.0, 0.0)
+ for _frame: int in 6:
+ await physics_frame
+ for _frame: int in 60:
+ if not game_ui.get_fishing_shop().visible:
+ break
+ await process_frame
+ _expect(
+ not interaction.is_local_player_in_range(),
+ "local player did not leave the shop interaction range",
+ )
+ _expect(
+ not game_ui.get_fishing_shop().visible,
+ "shop did not close after leaving its interaction range",
+ )
+ game_ui.call("_input", press)
+ await process_frame
+ _expect(
+ character_calls.size() == 1,
+ "Y did not play a character call away from an interaction",
+ )
+ chat_service.character_call_received.disconnect(capture_call)
+
+ var session := main.get_node("%NetworkSession") as NetworkSession
+ if session != null:
+ session.disconnect_session("Controller interaction validation complete.")
+ main.queue_free()
+ for _frame: int in 4:
+ await process_frame
+ await create_timer(0.1).timeout
+ if _failures.is_empty():
+ print("Controller world interaction 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_world_interaction_validation.gd.uid b/tests/controller_world_interaction_validation.gd.uid
new file mode 100644
index 0000000..8315ee5
--- /dev/null
+++ b/tests/controller_world_interaction_validation.gd.uid
@@ -0,0 +1 @@
+uid://b6fg46sad1ma6
diff --git a/tests/dedicated_host_session_validation.gd b/tests/dedicated_host_session_validation.gd
index 6f1de7d..2d098bd 100644
--- a/tests/dedicated_host_session_validation.gd
+++ b/tests/dedicated_host_session_validation.gd
@@ -1,6 +1,9 @@
extends SceneTree
const TEST_PORT: int = 35777
+const OPERATOR_FINGERPRINT: String = (
+ "0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef"
+)
func _init() -> void:
@@ -35,12 +38,18 @@ func _run() -> void:
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.")
diff --git a/tests/dedicated_server_config_validation.gd b/tests/dedicated_server_config_validation.gd
index 6548d62..2125220 100644
--- a/tests/dedicated_server_config_validation.gd
+++ b/tests/dedicated_server_config_validation.gd
@@ -1,6 +1,12 @@
extends SceneTree
const ConfigType = preload("res://server/dedicated_server_config.gd")
+const OPERATOR_A: String = (
+ "0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef"
+)
+const OPERATOR_B: String = (
+ "fedcba9876543210fedcba9876543210fedcba9876543210fedcba9876543210"
+)
func _initialize() -> void:
@@ -9,10 +15,16 @@ func _initialize() -> void:
func _run() -> void:
var defaults := ConfigType.new()
- defaults.set("data_directory", "/tmp/netfishing-server")
+ defaults.set("data_directory", "/tmp/straywild-server")
defaults.call("_validate")
assert(defaults.is_valid())
assert(not bool(defaults.get("public_listing")))
+ assert(not bool(defaults.get("chat_logging")))
+ assert(str(defaults.get("chat_log_path")).is_empty())
+ assert(NetworkChatService.BURST_COUNT == 3)
+ assert(NetworkChatService.WINDOW_COUNT == 5)
+ assert(is_equal_approx(NetworkChatService.WINDOW_SECONDS, 10.0))
+ assert(NetworkChatService.CALL_COOLDOWN_MILLISECONDS == 90)
var path: String = ProjectSettings.globalize_path(
"user://dedicated-server-validation.cfg"
@@ -22,9 +34,19 @@ func _run() -> void:
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("world", "seed", 928374)
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("discovery", "url", "https://discovery.straywild.io/")
+ file.set_value("privacy", "chat_logging", true)
+ file.set_value(
+ "privacy",
+ "chat_log_path",
+ "/tmp/configured-server/logs/chat.jsonl",
+ )
+ file.set_value(
+ "moderation", "operators", PackedStringArray([OPERATOR_B, OPERATOR_A])
+ )
assert(file.save(path) == OK)
var configured := ConfigType.new()
@@ -35,18 +57,148 @@ func _run() -> void:
assert(str(configured.get("bind_address")) == "127.0.0.1")
assert(int(configured.get("port")) == 17777)
assert(int(configured.get("max_players")) == 12)
+ assert(int(configured.get("world_seed")) == 928374)
assert(bool(configured.get("public_listing")))
- assert(str(configured.get("discovery_url")) == "https://discovery.netfishing.org")
+ assert(str(configured.get("discovery_url")) == "https://discovery.straywild.io")
+ assert(bool(configured.get("chat_logging")))
+ assert(
+ str(configured.get("chat_log_path"))
+ == "/tmp/configured-server/logs/chat.jsonl"
+ )
+ 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 invalid_seed := ConfigType.new()
+ invalid_seed.set("world_seed", 0)
+ invalid_seed.set("data_directory", "/tmp/invalid-seed-server")
+ invalid_seed.call("_validate")
+ assert(not invalid_seed.is_valid())
+
+ var default_chat_path := ConfigType.new()
+ default_chat_path.set("data_directory", "/tmp/default-chat-path-server")
+ default_chat_path.set("chat_logging", true)
+ default_chat_path.call("_validate")
+ assert(default_chat_path.is_valid())
+ assert(
+ str(default_chat_path.get("chat_log_path"))
+ == "/tmp/default-chat-path-server/logs/chat.jsonl"
+ )
+
+ var invalid_chat_path := ConfigType.new()
+ invalid_chat_path.set("data_directory", "/tmp/invalid-chat-path-server")
+ invalid_chat_path.set("chat_logging", true)
+ invalid_chat_path.set("chat_log_path", "relative/chat.jsonl")
+ invalid_chat_path.call("_validate")
+ assert(not invalid_chat_path.is_valid())
+
+ var chat_log_validation_path: String = ProjectSettings.globalize_path(
+ "user://dedicated-chat-log-validation.jsonl"
+ )
+ var chat_service := NetworkChatService.new()
+ assert(not chat_service.configure_dedicated_history(
+ true,
+ "relative/chat.jsonl",
+ ))
+ assert(chat_service.configure_dedicated_history(
+ true,
+ chat_log_validation_path,
+ ))
+ assert(FileAccess.file_exists(chat_log_validation_path))
+ chat_service.call("_append_chat_log", {
+ "sender_display_name": "Privacy Tester",
+ "body": "explicitly retained message",
+ })
+ var logged_message: Variant = JSON.parse_string(
+ FileAccess.get_file_as_string(chat_log_validation_path).strip_edges()
+ )
+ assert(typeof(logged_message) == TYPE_DICTIONARY)
+ assert(str(logged_message.get("recorded_at_utc", "")).ends_with("Z"))
+ assert(logged_message.get("sender_display_name") == "Privacy Tester")
+ assert(logged_message.get("body") == "explicitly retained message")
+ assert(chat_service.configure_dedicated_history(false, ""))
+ assert(DirAccess.remove_absolute(chat_log_validation_path) == OK)
+ chat_service.free()
+
var discovery := DiscoveryClient.new()
- assert(bool(discovery.call("_valid_public_room", {
+ 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",
@@ -57,7 +209,71 @@ func _run() -> void:
"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/digging_prototype_validation.gd b/tests/digging_prototype_validation.gd
new file mode 100644
index 0000000..95917a0
--- /dev/null
+++ b/tests/digging_prototype_validation.gd
@@ -0,0 +1,161 @@
+extends SceneTree
+
+const StarterIslandScene = preload(
+ "res://world/regions/starter_island_region.tscn"
+)
+const ShovelAttachmentScene = preload("res://player/shovel_attachment.tscn")
+const ItemCatalogResource: ItemCatalog = preload(
+ "res://items/catalog/item_catalog.tres"
+)
+const Gatherables: GatherableCatalog = preload(
+ "res://gathering/catalog/gatherable_catalog.tres"
+)
+
+class PrimaryTerrainProvider:
+ extends Node3D
+
+ var primary_meshes: Array[MeshInstance3D] = []
+
+
+ func get_primary_terrain_meshes() -> Array[MeshInstance3D]:
+ return primary_meshes
+
+
+func _initialize() -> void:
+ call_deferred("_run")
+
+
+func _run() -> void:
+ _validate_catalog_content()
+ _validate_flat_shovel()
+ await _validate_beach_authoring()
+ _validate_primary_terrain_provider()
+ print("Digging prototype validation: PASS")
+ quit()
+
+
+func _validate_catalog_content() -> void:
+ var shovel: ItemData = ItemCatalogResource.get_available_item_by_id(
+ &"standard_shovel"
+ )
+ assert(shovel != null)
+ assert(shovel.category == ItemData.Category.TOOL)
+ assert(shovel.equippable and shovel.hotbar_allowed)
+ var clam: GatherableData = Gatherables.get_entry(&"clam_manila")
+ assert(clam != null and clam.is_available())
+ assert(clam.required_tool_id == shovel.item_id)
+ assert(clam.diggable_area_id == &"starter_beach")
+ assert(clam.presentation_mode == GatherableData.PresentationMode.WATER_SPURT)
+ assert(not clam.requires_sneaking)
+ assert(is_equal_approx(clam.active_lifetime_seconds, 10.0))
+
+
+func _validate_flat_shovel() -> void:
+ var attachment := ShovelAttachmentScene.instantiate() as BoneAttachment3D
+ assert(attachment != null)
+ var shovel := attachment.get_node("Shovel") as Node3D
+ assert(shovel != null)
+ var meshes: Array[MeshInstance3D] = []
+ _collect_meshes(shovel, meshes)
+ assert(meshes.size() == 5)
+ for mesh_instance: MeshInstance3D in meshes:
+ assert(
+ mesh_instance.cast_shadow
+ == GeometryInstance3D.SHADOW_CASTING_SETTING_OFF
+ )
+ var material := mesh_instance.mesh.surface_get_material(0) as StandardMaterial3D
+ assert(material != null)
+ assert(material.shading_mode == BaseMaterial3D.SHADING_MODE_UNSHADED)
+ assert(material.transparency == BaseMaterial3D.TRANSPARENCY_DISABLED)
+ assert(material.albedo_texture == null)
+ assert(is_equal_approx(material.albedo_color.a, 1.0))
+ attachment.free()
+
+
+func _validate_beach_authoring() -> void:
+ var region := StarterIslandScene.instantiate() as WorldRegion
+ root.add_child(region)
+ await process_frame
+ var area: DiggableArea3D = region.get_diggable_area(&"starter_beach")
+ assert(area != null)
+ assert(area.terrain_source == NodePath("../../Terrain/Visual"))
+ assert(area.surface_materials.size() == 1)
+ assert(area.surface_materials[0] == &"sand")
+ var triangles: Array[PackedVector3Array] = area.get_surface_triangles()
+ assert(not triangles.is_empty())
+ for triangle: PackedVector3Array in triangles:
+ assert(triangle.size() == 3)
+ var center := (triangle[0] + triangle[1] + triangle[2]) / 3.0
+ assert(area.generation_bounds.has_point(Vector2(center.x, center.z)))
+ assert(center.y <= area.maximum_global_y + 0.001)
+ region.queue_free()
+
+
+func _validate_primary_terrain_provider() -> void:
+ var fixture := Node3D.new()
+ fixture.name = "PrimaryTerrainFixture"
+ root.add_child(fixture)
+ var provider := PrimaryTerrainProvider.new()
+ provider.name = "Provider"
+ fixture.add_child(provider)
+ var clear_sand := _flat_surface_triangle(0.0, 0.0, &"sand")
+ var covered_sand := _flat_surface_triangle(10.0, 0.0, &"sand")
+ var covering_grass := _flat_surface_triangle(10.0, 0.01, &"grass_lite")
+ for mesh_instance: MeshInstance3D in [
+ clear_sand,
+ covered_sand,
+ covering_grass,
+ ]:
+ provider.add_child(mesh_instance)
+ provider.primary_meshes.append(mesh_instance)
+ var buried_base := _flat_surface_triangle(20.0, 0.0, &"sand")
+ buried_base.name = "BuriedSandBase"
+ provider.add_child(buried_base)
+ var area := DiggableArea3D.new()
+ area.name = "DiggableArea"
+ area.area_id = &"provider_test"
+ area.terrain_source = NodePath("../Provider")
+ area.surface_materials = [&"sand"]
+ area.generation_bounds = Rect2(-20.0, -20.0, 40.0, 40.0)
+ fixture.add_child(area)
+ var triangles := area.get_surface_triangles()
+ assert(triangles.size() == 2)
+ var clear_center := Vector3(1.0 / 3.0, 0.0, 1.0 / 3.0)
+ var covered_center := Vector3(10.0 + 1.0 / 3.0, 0.0, 1.0 / 3.0)
+ assert(area.is_surface_point_valid(clear_center))
+ assert(not area.is_surface_point_valid(covered_center))
+ fixture.free()
+
+
+func _flat_surface_triangle(
+ x_offset: float,
+ height: float,
+ material_name: StringName,
+) -> MeshInstance3D:
+ var arrays: Array = []
+ arrays.resize(Mesh.ARRAY_MAX)
+ arrays[Mesh.ARRAY_VERTEX] = PackedVector3Array([
+ Vector3(x_offset, height, 0.0),
+ Vector3(x_offset, height, 1.0),
+ Vector3(x_offset + 1.0, height, 0.0),
+ ])
+ arrays[Mesh.ARRAY_INDEX] = PackedInt32Array([0, 1, 2])
+ var mesh := ArrayMesh.new()
+ mesh.add_surface_from_arrays(Mesh.PRIMITIVE_TRIANGLES, arrays)
+ var material := StandardMaterial3D.new()
+ material.resource_name = str(material_name)
+ mesh.surface_set_material(0, material)
+ var mesh_instance := MeshInstance3D.new()
+ mesh_instance.name = "Primary%s" % str(material_name).capitalize()
+ mesh_instance.mesh = mesh
+ return mesh_instance
+
+
+func _collect_meshes(
+ root_node: Node,
+ result: Array[MeshInstance3D],
+) -> void:
+ for child: Node in root_node.get_children():
+ if child is MeshInstance3D:
+ result.append(child as MeshInstance3D)
+ _collect_meshes(child, result)
diff --git a/tests/digging_prototype_validation.gd.uid b/tests/digging_prototype_validation.gd.uid
new file mode 100644
index 0000000..182ab02
--- /dev/null
+++ b/tests/digging_prototype_validation.gd.uid
@@ -0,0 +1 @@
+uid://c1dt44ieacnvb
diff --git a/tests/economy_regression_validation.gd b/tests/economy_regression_validation.gd
index 9b40e74..0728d82 100644
--- a/tests/economy_regression_validation.gd
+++ b/tests/economy_regression_validation.gd
@@ -31,9 +31,14 @@ func _run() -> void:
for _frame: int in 8:
await process_frame
assert(bool(main.get("_application_initialized")))
+ var session := main.get_node("%NetworkSession") as NetworkSession
+ assert(bool(main.call(
+ "_apply_world", WorldLayout.STARTER_ISLAND, 1, true
+ )))
+ assert(session.set_host_world(WorldLayout.STARTER_ISLAND, 1))
assert(bool(main.call("_prepare_private_host")))
var save_manager := main.get("_save_manager") as PlayerSaveManager
- assert(save_manager.initialize_new_game())
+ assert(save_manager.initialize_new_game(1, WorldLayout.STARTER_ISLAND))
main.call("_enter_gameplay")
for _frame: int in 8:
await process_frame
@@ -42,13 +47,13 @@ func _run() -> void:
var catalog := main.get("fish_catalog") as FishPool
var sale_service := main.get_node("%NetworkSaleService") as NetworkSaleService
var shop_service := main.get_node("%NetworkShopService") as NetworkShopService
- var session := main.get_node("%NetworkSession") as NetworkSession
var reservations := (
main.get_node("%PlayerAssetReservationService")
as PlayerAssetReservationService
)
assert(player != null)
- assert(catalog != null and catalog.candidates.size() == 53)
+ assert(catalog != null and catalog.candidates.size() == 316)
+ assert(LogbookCatalog.ordered_species(catalog.candidates).size() == 314)
assert(sale_service != null)
assert(shop_service != null)
assert(session != null and session.is_host())
@@ -61,7 +66,6 @@ func _run() -> void:
_test_multi_sale(player, catalog, sale_service)
_test_reservations(player, catalog, sale_service, reservations)
_test_anywhere_sale(player, catalog, sale_service)
- await _test_player_menu_sale(main, player, catalog, sale_service)
await _test_host_shop_sale(main, player, catalog, sale_service)
assert(session.set_host_open(true))
@@ -72,31 +76,43 @@ func _run() -> void:
assert(session.set_host_open(false))
await _test_host_shop_purchase(main, player, shop_service)
+ await _test_host_rod_purchase(player, shop_service)
await _test_host_art_shop_purchase(main, player, shop_service)
await _test_fishing_shop_sale_ui(
main, player, catalog, sale_service, reservations
)
+ _test_host_backpack_purchase(player, shop_service)
+ await _test_host_home_upgrade(main, player, shop_service)
assert(not sale_service.is_local_sale_pending())
assert(not shop_service.is_local_purchase_pending())
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()
func _run_multiplayer_host() -> void:
root.size = Vector2i(1280, 720)
var main: Node = await _create_initialized_main()
+ var session := main.get_node("%NetworkSession") as NetworkSession
+ assert(bool(main.call(
+ "_apply_world", WorldLayout.STARTER_ISLAND, 1, true
+ )))
+ assert(session.set_host_world(WorldLayout.STARTER_ISLAND, 1))
assert(bool(main.call("_prepare_private_host")))
var save_manager := main.get("_save_manager") as PlayerSaveManager
assert(save_manager.initialize_new_game())
main.call("_enter_gameplay")
- 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:
@@ -118,7 +134,10 @@ func _run_multiplayer_host() -> void:
var interaction := main.get("_shop_interaction") as FishingShopInteraction
assert(interaction != null)
remote_avatar.global_position = interaction.global_position
- var completion_deadline: int = Time.get_ticks_msec() + 30000
+ # The client can finish loading its local presentation well after the host
+ # has authenticated it, especially during a cold import. Keep the authority
+ # alive until the client completes the transaction sequence or disconnects.
+ var completion_deadline: int = Time.get_ticks_msec() + 90000
while Time.get_ticks_msec() < completion_deadline:
await process_frame
if session.get_authenticated_peer_ids().size() < 2:
@@ -126,7 +145,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()
@@ -151,11 +172,21 @@ func _run_multiplayer_client() -> void:
var shop_service := main.get_node("%NetworkShopService") as NetworkShopService
sale_service.local_sale_finished.connect(_on_sale_finished)
shop_service.local_purchase_finished.connect(_on_shop_finished)
+ assert(player.inventory_layout.restore_backpack_level(
+ PlayerInventoryLayout.MAX_BACKPACK_LEVEL
+ ))
+ player.inventory.set_inventory_layout(null)
var catch_ids: Array[StringName] = []
for fish: FishData in catalog.candidates:
+ if not fish.active:
+ continue
var fish_catch := _make_catch(fish)
- player.inventory.add_catch(fish_catch)
+ assert(player.inventory.add_catch(fish_catch))
catch_ids.append(fish_catch.catch_id)
+ if catch_ids.size() == NetworkSaleProtocol.MAX_CATCHES_PER_REQUEST:
+ break
+ assert(catch_ids.size() == NetworkSaleProtocol.MAX_CATCHES_PER_REQUEST)
+ player.inventory.set_inventory_layout(player.inventory_layout)
await create_timer(1.0).timeout
_sale_result.clear()
assert(not sale_service.request_local_sale(catch_ids).is_empty())
@@ -169,6 +200,7 @@ func _run_multiplayer_client() -> void:
for catch_id: StringName in catch_ids:
assert(not player.inventory.contains_catch_id(catch_id))
assert(not sale_service.is_local_sale_pending())
+ assert(player.inventory_layout.restore_backpack_level(0))
var interaction := main.get("_shop_interaction") as FishingShopInteraction
player.global_position = interaction.global_position
@@ -206,6 +238,17 @@ func _run_multiplayer_client() -> void:
print("Client shop result: ", _shop_result)
assert(not _shop_result.is_empty() and bool(_shop_result[1]))
assert(not shop_service.is_local_purchase_pending())
+ var backpack_cost := player.inventory_layout.get_next_backpack_cost()
+ if player.wallet.get_balance() < backpack_cost:
+ assert(player.wallet.credit(
+ backpack_cost - player.wallet.get_balance()
+ ))
+ _shop_result.clear()
+ assert(not shop_service.request_backpack_capacity_upgrade().is_empty())
+ while _shop_result.is_empty():
+ await process_frame
+ assert(bool(_shop_result[1]))
+ assert(player.inventory_layout.get_inventory_capacity() == 18)
var required_art_balance: int = (
ArtShopStock.ART_KIT_PRICE + ArtShopStock.UPGRADE_PRICE
)
@@ -228,7 +271,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()
@@ -251,6 +296,8 @@ func _test_each_species(
sale_service: NetworkSaleService,
) -> void:
for fish: FishData in catalog.candidates:
+ if not fish.active:
+ continue
assert(fish != null)
var fish_catch := _make_catch(fish)
player.inventory.add_catch(fish_catch)
@@ -362,91 +409,62 @@ func _test_host_shop_sale(
)
var species_ids: Array[StringName] = []
var expected_payout: int = 0
+ var species_balance_before: int = player.wallet.get_balance()
for fish: FishData in catalog.candidates:
+ if not fish.active:
+ continue
var fish_catch := _make_catch(fish)
- player.inventory.add_catch(fish_catch)
+ assert(player.inventory.add_catch(fish_catch))
species_ids.append(fish_catch.catch_id)
expected_payout += fish_catch.sale_value
- var species_balance_before: int = player.wallet.get_balance()
- _assert_sale(
- player,
- sale_service,
- species_ids,
- true,
- true,
- NetworkSaleService.MAIN_SHOP_BUYER_ID,
- )
+ if species_ids.size() >= 5:
+ _assert_sale(
+ player,
+ sale_service,
+ species_ids,
+ true,
+ true,
+ NetworkSaleService.MAIN_SHOP_BUYER_ID,
+ )
+ species_ids.clear()
+ if not species_ids.is_empty():
+ _assert_sale(
+ player,
+ sale_service,
+ species_ids,
+ true,
+ true,
+ NetworkSaleService.MAIN_SHOP_BUYER_ID,
+ )
assert(
player.wallet.get_balance()
== species_balance_before + expected_payout
)
-func _test_player_menu_sale(
- main: Node,
- player: Player,
- catalog: FishPool,
- sale_service: NetworkSaleService,
-) -> void:
- var game_ui := main.get_node("%GameUI") as GameUI
- var player_menu := game_ui.get_node("%PlayerMenu") as PlayerMenu
- var fish_catch := _make_catch(catalog.candidates[6])
- player.inventory.add_catch(fish_catch)
- player.global_position = Vector3(0.0, 3.95, 13.0)
- player_menu.open_menu()
- await create_timer(2.2).timeout
- player_menu.call("_on_catch_card_pressed", fish_catch.catch_id)
- await process_frame
- var sell_action := player_menu.get_node("%SellBubble") as Button
- var confirmation := player_menu.get_node("%SaleConfirmation") as Control
- var confirm_button := player_menu.get_node("%ConfirmSaleButton") as Button
- var ui_viewport := main.get_node(
- "UIPresentation/UIViewport"
- ) as SubViewport
- assert(sell_action.visible and not sell_action.disabled)
- 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 process_frame
- assert(confirmation.visible)
- assert(confirm_button.visible and not confirm_button.disabled)
- assert(
- confirmation.z_index
- > (player_menu.get_node("%CoolerOuterWall") as Control).z_index
- )
- _sale_result.clear()
- await _activate_focused_button(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(not sale_service.is_local_sale_pending())
- player_menu.close_menu()
- await create_timer(2.2).timeout
- assert(not player_menu.visible)
- player_menu.open_menu()
- await create_timer(2.2).timeout
- assert(player_menu.visible)
- assert(
- not sell_action.disabled
- or player.inventory.get_all_catches().is_empty()
- )
- player_menu.close_menu()
- 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
@@ -481,7 +499,7 @@ func _test_host_art_shop_purchase(
for _frame: int in 4:
await physics_frame
var required_balance: int = (
- ArtShopStock.ART_KIT_PRICE + ArtShopStock.UPGRADE_PRICE * 3
+ ArtShopStock.ART_KIT_PRICE + ArtShopStock.UPGRADE_PRICE * 4
)
if player.wallet.get_balance() < required_balance:
assert(player.wallet.credit(required_balance - player.wallet.get_balance()))
@@ -495,9 +513,14 @@ 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",
+ &"marker_ocean_teal",
+ &"brush_2x",
+ &"grid_32x",
+ PlayerArtUnlocks.STAMP_PRODUCT_ID,
]:
_shop_result.clear()
assert(not shop_service.request_art_upgrade(product_id).is_empty())
@@ -506,6 +529,88 @@ func _test_host_art_shop_purchase(
assert(not shop_service.is_local_purchase_pending())
+func _test_host_rod_purchase(
+ player: Player,
+ shop_service: NetworkShopService,
+) -> void:
+ assert(player.bag.remove_item(&"basic_fishing_rod", 1))
+ _shop_result.clear()
+ assert(not shop_service.request_rod(&"basic_fishing_rod").is_empty())
+ assert(not _shop_result.is_empty() and bool(_shop_result[1]))
+ assert(player.bag.owns_item(&"basic_fishing_rod"))
+ assert(not shop_service.is_local_purchase_pending())
+
+
+func _test_host_backpack_purchase(
+ player: Player,
+ shop_service: NetworkShopService,
+) -> void:
+ var layout := player.inventory_layout
+ assert(layout.get_inventory_capacity() == 9)
+ var total_cost: int = 0
+ for cost: int in PlayerInventoryLayout.BACKPACK_EXPANSION_COSTS:
+ total_cost += cost
+ if player.wallet.get_balance() < total_cost:
+ assert(player.wallet.credit(total_cost - player.wallet.get_balance()))
+ var balance_before := player.wallet.get_balance()
+ for expected_capacity: int in [18, 27, 36]:
+ _shop_result.clear()
+ assert(not shop_service.request_backpack_capacity_upgrade().is_empty())
+ assert(
+ not _shop_result.is_empty() and bool(_shop_result[1]),
+ "Backpack purchase failed: %s" % [_shop_result],
+ )
+ assert(layout.get_inventory_capacity() == expected_capacity)
+ assert(player.wallet.get_balance() == balance_before - total_cost)
+ assert(layout.get_next_backpack_cost() == -1)
+
+
+func _test_host_home_upgrade(
+ main: Node,
+ player: Player,
+ shop_service: NetworkShopService,
+) -> void:
+ var interaction := main.get(
+ "_rv_upgrade_shop_interaction"
+ ) as RVUpgradeInteraction
+ var home_state := main.get("_home_state") as PlayerHomeState
+ var capacity := player.cooler_capacity
+ assert(interaction != null and home_state != null)
+ player.global_position = interaction.global_position
+ for _frame: int in 4:
+ await physics_frame
+ assert(interaction.is_avatar_in_range(player))
+ assert(home_state.get_tier() == PlayerHomeState.STARTER_TIER)
+ assert(
+ interaction.get_dialogue_text(home_state.get_tier())
+ == RVUpgradeInteraction.TENT_DIALOGUE_TEXT
+ )
+ assert(capacity.get_capacity() == 9)
+ var upgrade_cost: int = home_state.get_next_upgrade_cost()
+ assert(upgrade_cost == PlayerHomeState.HOME_UPGRADE_COSTS.front())
+ assert(upgrade_cost == 50)
+ if player.wallet.get_balance() < upgrade_cost:
+ assert(player.wallet.credit(
+ upgrade_cost - player.wallet.get_balance()
+ ))
+ var balance_before := player.wallet.get_balance()
+ _shop_result.clear()
+ assert(not shop_service.request_home_upgrade().is_empty())
+ assert(
+ not _shop_result.is_empty() and bool(_shop_result[1]),
+ "Home purchase failed: %s" % [_shop_result],
+ )
+ assert(home_state.get_tier() == PlayerHomeState.STARTER_RV_TIER)
+ assert(
+ interaction.get_dialogue_text(home_state.get_tier())
+ == RVUpgradeInteraction.RV_DIALOGUE_TEXT
+ )
+ assert(capacity.get_capacity() == 18)
+ assert(player.wallet.get_balance() == balance_before - upgrade_cost)
+ assert(home_state.get_next_upgrade_cost() == -1)
+ assert(capacity.get_next_cost() == -1)
+
+
func _test_fishing_shop_sale_ui(
main: Node,
player: Player,
@@ -528,6 +633,57 @@ func _test_fishing_shop_sale_ui(
and shop_backdrop != null
and ui_viewport != null
)
+ var item_catalog := main.get("item_catalog") as ItemCatalog
+ var worms: ItemData = item_catalog.get_item_by_id(&"worms")
+ assert(worms != null and worms.max_stack == 10)
+ if player.bag.get_quantity(worms.item_id) == 0:
+ assert(player.bag.add_item(worms.item_id, 4))
+ assert(player.equip_bait(worms))
+ await process_frame
+ var expected_bait_supply := UtilityPageStyle.supply_quantity_text(
+ player.bag.get_quantity(worms.item_id),
+ worms.max_stack,
+ )
+ var hud_bait_quantity := game_ui.get_node(
+ "%ActiveBaitQuantity"
+ ) as Label
+ assert(hud_bait_quantity.text == expected_bait_supply)
+ var hud_bait_badge := hud_bait_quantity.get_parent() as Panel
+ assert(
+ is_equal_approx(
+ hud_bait_badge.rotation_degrees,
+ UtilityPageStyle.SUPPLY_BADGE_ROTATION_DEGREES,
+ )
+ )
+ player_menu.call("_refresh_tackle_box")
+ await process_frame
+ var tackle_bait_button: Button
+ for child: Node in player_menu.get_node("%BaitItemList").get_children():
+ var candidate := child as Button
+ if (
+ candidate != null
+ and candidate.get_meta(&"controller_tackle_item_id", StringName())
+ == worms.item_id
+ ):
+ tackle_bait_button = candidate
+ break
+ assert(tackle_bait_button != null)
+ var tackle_quantity := tackle_bait_button.get_node(
+ "QuantityBadge/Quantity"
+ ) as Label
+ assert(tackle_quantity.text == expected_bait_supply)
+ assert(
+ str(tackle_bait_button.get_meta(
+ &"inventory_context_text", ""
+ )).contains(expected_bait_supply)
+ )
+ var tackle_badge := tackle_quantity.get_parent() as Panel
+ assert(
+ is_equal_approx(
+ tackle_badge.rotation_degrees,
+ UtilityPageStyle.SUPPLY_BADGE_ROTATION_DEGREES,
+ )
+ )
player.global_position = interaction.global_position
for _frame: int in 4:
await physics_frame
@@ -543,7 +699,8 @@ func _test_fishing_shop_sale_ui(
}))
var balance_before: int = player.wallet.get_balance()
assert(shop.open_shop())
- await process_frame
+ for _frame: int in 2:
+ await process_frame
assert(shop_backdrop.visible)
assert(shop_backdrop.material is ShaderMaterial)
assert(shop.has_node("InputBlocker") and not shop.has_node("Dimmer"))
@@ -552,67 +709,344 @@ 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(
+ upgrade_grid.get_theme_constant("h_separation")
+ == FishingShop.SHOP_SLOT_SEPARATION
+ )
+ assert(
+ upgrade_grid.get_theme_constant("v_separation")
+ == FishingShop.SHOP_SLOT_SEPARATION
+ )
+ 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",
+ "BackpackPurchase",
+ ]:
+ 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"))
+ for placeholder_name: String in ["CoolerPurchase", "BackpackPurchase"]:
+ var placeholder_button := shop.get_node(
+ "%%%s" % placeholder_name
+ ) as Button
+ assert(
+ placeholder_button.icon.resource_path.ends_with(
+ "/pictograms/x_light.png"
+ )
+ )
+ 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", "BackpackCost"
+ ]:
+ 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")
+ var equipment_tab := shop_tabs[3] as Button
+ assert(equipment_tab != null and equipment_tab.text == "Equipment")
+ var supplies_list := shop.get_node("%SuppliesList") as VBoxContainer
+ var bait_tab := shop_tabs[1] as Button
+ assert(bait_tab != null and bait_tab.text == "Bait and Lures")
+ await _activate_pointer_control(bait_tab, ui_viewport)
+ await process_frame
+ var worms_button := supplies_list.find_child(
+ "Supply_worms", true, false
+ ) as Button
+ var snails_button := supplies_list.find_child(
+ "Supply_snails", true, false
+ ) as Button
+ assert(worms_button != null and snails_button != null)
+ var bait_grid := worms_button.get_parent().get_parent() as GridContainer
+ assert(bait_grid != null)
+ assert(
+ bait_grid.get_theme_constant("h_separation")
+ == FishingShop.SHOP_SLOT_SEPARATION
+ )
+ assert(
+ bait_grid.get_theme_constant("v_separation")
+ == FishingShop.SHOP_SLOT_SEPARATION
+ )
+ var shop_bait_quantity := worms_button.get_node(
+ "SupplyQuantityBadge/Quantity"
+ ) as Label
+ assert(shop_bait_quantity.text == expected_bait_supply)
+ var shop_bait_badge := shop_bait_quantity.get_parent() as Panel
+ assert(shop_bait_badge.position.y == FishingShop.BAIT_SUPPLY_BADGE_Y)
+ assert(
+ is_equal_approx(
+ shop_bait_badge.rotation_degrees,
+ UtilityPageStyle.SUPPLY_BADGE_ROTATION_DEGREES,
+ )
+ )
+ assert(
+ shop_bait_badge.position.y + shop_bait_badge.size.y
+ <= FishingShop.SUPPLY_PRICE_Y
+ )
+ var worms_unit_price: int = FishingShopStock.get_price(worms.item_id)
+ assert(
+ worms_button.tooltip_text.contains(
+ "%d fish coin%s per bait piece" % [
+ worms_unit_price,
+ "" if worms_unit_price == 1 else "s",
+ ]
+ )
+ )
+ assert(not worms_button.tooltip_text.contains("owned"))
+ var worms_restock_quantity: int = FishingShopStock.get_purchase_quantity(
+ worms.item_id,
+ player.bag.get_quantity(worms.item_id),
+ worms.max_stack,
+ )
+ var worms_restock_cost: int = FishingShopStock.get_purchase_cost(
+ worms.item_id,
+ worms_restock_quantity,
+ player.bag.is_bait_unlocked(worms.item_id),
+ )
+ var worms_price := worms_button.get_parent().get_node(
+ "PriceBubble/CurrencyAmount/Price"
+ ) as Label
+ assert(worms_price.text == str(maxi(worms_restock_cost, 0)))
+ var bait_receipt: String = shop.call(
+ "_purchase_success_feedback",
+ worms.item_id,
+ NetworkShopProtocol.ProductCategory.SUPPLY,
+ maxi(worms_restock_quantity, 1),
+ maxi(worms_restock_cost, worms_unit_price),
+ )
+ assert(bait_receipt.begins_with("Bait Restocked\n"))
+ assert(bait_receipt.contains(CurrencyPresentation.ICON_PATH))
+ assert(bait_receipt.contains(" spent on "))
+ assert(bait_receipt.ends_with(" worms"))
+ assert(snails_button.get_node_or_null("SupplyQuantityBadge") == null)
+ assert(worms_button.get_node_or_null("UnlockStateBadge") == null)
+ var snails_lock_icon := snails_button.get_node(
+ "UnlockStateBadge/UnlockStateIcon"
+ ) as TextureRect
+ assert(not bool(snails_lock_icon.get_meta(&"unlocked")))
+ assert(snails_lock_icon.texture.resource_path.ends_with(
+ "/lock_light.png"
+ ))
+ assert(is_equal_approx(snails_lock_icon.modulate.a, 0.88))
+ await _activate_pointer_control(equipment_tab, ui_viewport)
+ await process_frame
+ var equipment_sections: Array[String] = []
+ for child: Node in shop.get_node("%SuppliesList").get_children():
+ if child is Label:
+ equipment_sections.append((child as Label).text)
+ assert(equipment_sections == ["rods", "equipment"])
+ var rod_carousel := supplies_list.get_node("RodCarousel") as HBoxContainer
+ assert(rod_carousel != null)
+ var rod_cards := rod_carousel.get_node("RodScroll/RodCards") as HBoxContainer
+ assert(rod_cards != null and rod_cards.get_child_count() == 1)
+ assert(
+ rod_cards.get_theme_constant("separation")
+ == FishingShop.SHOP_SLOT_SEPARATION
+ )
+ var basic_rod_button := rod_cards.find_child(
+ "Rod_basic_fishing_rod", true, false
+ ) as Button
+ assert(basic_rod_button != null)
+ assert(basic_rod_button.custom_minimum_size == Vector2(144.0, 144.0))
+ assert(basic_rod_button.icon != null and basic_rod_button.disabled)
+ assert(basic_rod_button.get_node_or_null("UnlockStateBadge") == null)
+ assert(rod_cards.find_child("Rod_aurora_rod", true, false) == null)
+ 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", "stamps",
+ ])
+ 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 expected_art_upgrade_icons: Dictionary[StringName, String] = {
+ &"brush_2x": "art_kit_marker_tip_thin.png",
+ &"brush_3x": "art_kit_marker_tip_mid.png",
+ &"brush_4x": "art_kit_marker_tip_thick.png",
+ &"grid_32x": "art_kit_grid_medium_light.png",
+ &"grid_64x": "art_kit_grid_large_light.png",
+ &"grid_128x": "art_kit_grid_xl_light.png",
+ }
+ var found_art_upgrade_icons: int = 0
+ for upgrade_node: Node in shop.find_children("*", "Button", true, false):
+ var upgrade_button := upgrade_node as Button
+ if not upgrade_button.has_meta(&"art_product_id"):
+ continue
+ var product_id := StringName(
+ str(upgrade_button.get_meta(&"art_product_id"))
+ )
+ if not expected_art_upgrade_icons.has(product_id):
+ continue
+ found_art_upgrade_icons += 1
+ assert(upgrade_button.text.is_empty())
+ assert(upgrade_button.icon != null)
+ var product_unlocked: bool = (
+ player.art_unlocks.owns_product(product_id)
+ )
+ var state_badge := upgrade_button.get_node_or_null(
+ "UnlockStateBadge"
+ ) as Panel
+ var state_icon := (
+ state_badge.get_node("UnlockStateIcon") as TextureRect
+ if state_badge != null
+ else null
+ ) as TextureRect
+ assert((state_icon == null) == product_unlocked)
+ if state_icon != null:
+ assert(state_icon.texture.resource_path.ends_with("/lock_light.png"))
+ assert(
+ upgrade_button.icon.resource_path.ends_with(
+ expected_art_upgrade_icons[product_id]
+ )
+ )
+ assert(found_art_upgrade_icons == expected_art_upgrade_icons.size())
+ var stamp_button: Button
+ for upgrade_node: Node in shop.find_children("*", "Button", true, false):
+ var upgrade_button := upgrade_node as Button
+ if StringName(str(upgrade_button.get_meta(&"art_product_id", ""))) == (
+ PlayerArtUnlocks.STAMP_PRODUCT_ID
+ ):
+ stamp_button = upgrade_button
+ break
+ assert(stamp_button != null)
+ assert(stamp_button.text == "stamp")
+ assert(stamp_button.icon == null)
+ 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)
- var mounted_cooler := player_menu.get("_cooler_page") as Control
- assert(mounted_cooler != null and mounted_cooler.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 sell_inventory := shop.get("_sell_inventory") as ShopSellInventory
+ assert(sell_inventory != null and sell_inventory.visible)
+ var sell_tray_grid := sell_inventory.get("_tray_grid") as GridContainer
assert(
- mounted_cooler.get_parent() == shop.get_node("%ShopCoolerMount")
+ sell_tray_grid.columns == PlayerInventoryLayout.INVENTORY_COLUMNS
)
- var cooler_outer_wall := player_menu.get("_cooler_outer_wall") as Control
- 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)
assert(
- StringName(
- (player_menu.get("_sale_buyer_override") as FishBuyerProfile).id
- ) == NetworkSaleService.MAIN_SHOP_BUYER_ID
+ sell_tray_grid.get_theme_constant("h_separation")
+ == GeneralInventoryGrid.DEFAULT_SLOT_SEPARATION
)
- var fish_nodes: Dictionary = player_menu.get("_fish_nodes")
- var fish_button := fish_nodes.get(fish_catch.catch_id) as Button
- 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)
- 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 process_frame
- var confirmation := player_menu.get("_sale_confirmation") as Control
- var confirm_button := player_menu.get("_confirm_sale_button") as Button
- assert(confirmation.visible)
assert(
- confirmation.z_index
- > cooler_outer_wall.z_index
+ sell_inventory.get_parent() == shop.get_node("%ShopCoolerMount")
)
+ var staged: Dictionary = sell_inventory.get("_staged")
+ sell_inventory.call(
+ "_stage",
+ PlayerInventoryLayout.EntryKind.CATCH,
+ reserved_catch.catch_id,
+ )
+ assert(staged.is_empty())
+ var sell_button := sell_inventory.get("_sell_button") as Button
+ var all_button := sell_inventory.get("_all_button") as Button
+ assert(sell_button != null and sell_button.text == "sell")
+ assert(all_button != null and all_button.text == "all")
+ assert(sell_button.get_parent() == all_button.get_parent())
+ assert(
+ sell_button.size_flags_horizontal == Control.SIZE_EXPAND_FILL
+ and all_button.size_flags_horizontal == Control.SIZE_EXPAND_FILL
+ )
+ sell_inventory.call("_stage_all_eligible")
+ assert(staged.has(PlayerInventoryLayout.catch_key(fish_catch.catch_id)))
+ assert(not staged.has(
+ PlayerInventoryLayout.catch_key(reserved_catch.catch_id)
+ ))
+ assert(all_button.disabled)
+ assert((sell_inventory.get("_feedback") as Label).text.is_empty())
+ var source_grid := sell_inventory.get("_inventory_grid") as GeneralInventoryGrid
+ var staged_source_found := false
+ for source_slot: GeneralInventorySlot in source_grid.get_slots():
+ if source_slot.entry_identity == fish_catch.catch_id:
+ staged_source_found = bool(source_slot.get("_staged"))
+ break
+ assert(staged_source_found)
+ var expected_all_payout := int(sell_inventory.call("_calculate_total"))
+ assert(expected_all_payout >= fish_catch.sale_value)
+ var feedback := sell_inventory.get("_feedback") as Label
+ var total_label := sell_inventory.get("_total_label") as Label
+ var sale_buttons := sell_button.get_parent() as HBoxContainer
+ assert(feedback.get_index() < total_label.get_parent().get_index())
+ assert(total_label.get_parent().get_index() < sale_buttons.get_index())
_sale_result.clear()
- await _activate_focused_button(confirm_button, ui_viewport)
+ sell_inventory.call("_submit_sale")
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(player.wallet.get_balance() == balance_before + expected_all_payout)
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())
+ assert(not sell_inventory.is_visible_in_tree())
shop.close_shop()
- await process_frame
+ await shop.menu_visibility_changed
+ assert(not shop.visible)
func _assert_sale(
@@ -633,7 +1067,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/equipment_catalog_validation.gd b/tests/equipment_catalog_validation.gd
new file mode 100644
index 0000000..bc549aa
--- /dev/null
+++ b/tests/equipment_catalog_validation.gd
@@ -0,0 +1,109 @@
+extends SceneTree
+
+const FishingShopStockType = preload("res://economy/fishing_shop_stock.gd")
+const FishingShopType = preload("res://ui/fishing_shop.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 PlayerWalletType = preload("res://economy/player_wallet.gd")
+
+const ItemCatalogResource: ItemCatalogType = preload(
+ "res://items/catalog/item_catalog.tres"
+)
+
+
+func _initialize() -> void:
+ var shop := FishingShopType.new()
+ assert(shop.call("_unlock_status", false) == "locked")
+ assert(shop.call("_unlock_status", true) == "unlocked")
+ var locked_button := Button.new()
+ shop.call(
+ "_add_unlock_state_icon", locked_button, false, Vector2(72.0, 72.0)
+ )
+ var locked_badge := locked_button.get_node(
+ "UnlockStateBadge"
+ ) as Panel
+ var locked_icon := locked_badge.get_node(
+ "UnlockStateIcon"
+ ) as TextureRect
+ assert(locked_badge.position == Vector2(4.0, 4.0))
+ assert(locked_badge.size == Vector2(30.0, 30.0))
+ assert(locked_icon.texture.resource_path.ends_with("/lock_light.png"))
+ assert(locked_icon.size == Vector2(18.0, 18.0))
+ assert(locked_icon.position == Vector2(6.0, 6.0))
+ assert(is_equal_approx(locked_icon.modulate.a, 0.88))
+ assert(not bool(locked_icon.get_meta(&"unlocked")))
+ locked_button.free()
+ var unlocked_button := Button.new()
+ shop.call(
+ "_add_unlock_state_icon", unlocked_button, true, Vector2(72.0, 72.0)
+ )
+ assert(unlocked_button.get_node_or_null("UnlockStateBadge") == null)
+ unlocked_button.free()
+ shop.free()
+
+ var magnet: ItemDataType = ItemCatalogResource.get_available_item_by_id(
+ &"magnet"
+ )
+ assert(magnet != null)
+ assert(magnet.category == ItemDataType.Category.TOOL)
+ assert(magnet.icon == null)
+ assert(not magnet.usable)
+ assert(not magnet.equippable)
+ assert(not magnet.hotbar_allowed)
+ assert(FishingShopStockType.get_price(&"magnet") == 250)
+ assert(FishingShopStockType.get_stock_item_ids().has(&"magnet"))
+ assert(FishingShopStockType.is_permanent_unlock(&"magnet", magnet))
+ var fishing_net: ItemDataType = (
+ ItemCatalogResource.get_available_item_by_id(&"fishing_net")
+ )
+ assert(fishing_net != null)
+ assert(fishing_net.category == ItemDataType.Category.TOOL)
+ assert(fishing_net.icon == null)
+ assert(not fishing_net.usable)
+ assert(not fishing_net.equippable)
+ assert(not fishing_net.hotbar_allowed)
+ assert(FishingShopStockType.get_price(&"fishing_net") == 250)
+ assert(FishingShopStockType.get_stock_item_ids().has(&"fishing_net"))
+ assert(FishingShopStockType.is_permanent_unlock(
+ &"fishing_net", fishing_net
+ ))
+ assert(not FishingShopStockType.is_gathering_tool(&"fishing_net"))
+ var crab_net: ItemDataType = ItemCatalogResource.get_available_item_by_id(
+ &"crab_net"
+ )
+ assert(crab_net != null)
+ assert(crab_net.icon != null)
+ assert(crab_net.icon.resource_path.ends_with("/equipment/net.png"))
+ var shovel: ItemDataType = ItemCatalogResource.get_available_item_by_id(
+ &"standard_shovel"
+ )
+ assert(shovel != null)
+ assert(shovel.category == ItemDataType.Category.TOOL)
+ assert(shovel.icon != null)
+ assert(shovel.icon.resource_path.ends_with("/equipment/shovel.png"))
+ assert(shovel.equippable)
+ assert(shovel.hotbar_allowed)
+ assert(FishingShopStockType.get_price(&"standard_shovel") == 75)
+ assert(FishingShopStockType.get_stock_item_ids().has(&"standard_shovel"))
+ assert(FishingShopStockType.is_permanent_unlock(&"standard_shovel", shovel))
+ assert(FishingShopStockType.is_gathering_tool(&"crab_net"))
+ assert(FishingShopStockType.is_gathering_tool(&"standard_shovel"))
+ assert(not FishingShopStockType.is_gathering_tool(&"basic_fishing_rod"))
+
+ var wallet := PlayerWalletType.new()
+ wallet.current_balance = 250
+ var bag := PlayerBagType.new()
+ bag.setup(ItemCatalogResource)
+ assert(FishingShopStockType.purchase_one(
+ &"magnet", wallet, bag, ItemCatalogResource
+ ))
+ assert(bag.owns_item(&"magnet"))
+ assert(wallet.get_balance() == 0)
+ assert(not FishingShopStockType.purchase_one(
+ &"magnet", wallet, bag, ItemCatalogResource
+ ))
+ bag.free()
+ wallet.free()
+ print("Equipment catalog validation: PASS")
+ quit()
diff --git a/tests/equipment_catalog_validation.gd.uid b/tests/equipment_catalog_validation.gd.uid
new file mode 100644
index 0000000..b26406d
--- /dev/null
+++ b/tests/equipment_catalog_validation.gd.uid
@@ -0,0 +1 @@
+uid://1f2k3yrth7qn
diff --git a/tests/exported_decal_hotfix_validation.gd b/tests/exported_decal_hotfix_validation.gd
index be70325..6560dd6 100644
--- a/tests/exported_decal_hotfix_validation.gd
+++ b/tests/exported_decal_hotfix_validation.gd
@@ -1,11 +1,20 @@
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",
+}
+const FACIAL_FEATURE_SIZE_LIMIT: int = 512
+
func _initialize() -> void:
call_deferred("_run")
func _run() -> void:
+ CharacterCustomizationCatalog.refresh_feature_assets()
+ assert(CharacterCustomizationCatalog._feature_textures.is_empty())
assert(
CharacterCustomizationCatalog._canonical_feature_filename(
"eyes_alligator_eyes.png"
@@ -27,21 +36,128 @@ func _run() -> void:
).is_empty()
)
- var expected_counts := {"eyes": 22, "mouth": 17, "nose": 17}
- for category_id: String in expected_counts:
+ var load_candidates: Array[Dictionary] = []
+ for category_id: String in FEATURE_ASSET_ROOTS:
var options := CharacterCustomizationCatalog.options_for(category_id)
- assert(options.size() == int(expected_counts[category_id]) + 1)
+ 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(
+ var texture_path := (
+ CharacterCustomizationCatalog.feature_texture_path(
category_id, option_id
- ) != null
+ )
)
+ assert(not texture_path.is_empty())
+ assert(ResourceLoader.exists(texture_path, "Texture2D"))
+ _validate_feature_import(texture_path)
+ load_candidates.append({
+ "category_id": category_id,
+ "option_id": option_id,
+ })
+ for expected_id: String in expected_ids:
+ assert(seen_ids.has(expected_id))
+ assert(CharacterCustomizationCatalog._feature_textures.is_empty())
+ assert(
+ load_candidates.size()
+ > CharacterCustomizationCatalog.FEATURE_TEXTURE_CACHE_LIMIT
+ )
+ for candidate_index: int in range(
+ CharacterCustomizationCatalog.FEATURE_TEXTURE_CACHE_LIMIT + 1
+ ):
+ var candidate: Dictionary = load_candidates[candidate_index]
+ assert(CharacterCustomizationCatalog.texture_for(
+ str(candidate["category_id"]),
+ str(candidate["option_id"]),
+ ) != null)
+ assert(
+ CharacterCustomizationCatalog._feature_textures.size()
+ == CharacterCustomizationCatalog.FEATURE_TEXTURE_CACHE_LIMIT
+ )
+ for mouth_id: String in ["open_ah", "open_oh", "open_smile"]:
+ assert(CharacterCustomizationCatalog.texture_for(
+ "mouth", mouth_id
+ ) != null)
+ assert(
+ CharacterCustomizationCatalog._feature_textures.size()
+ <= CharacterCustomizationCatalog.FEATURE_TEXTURE_CACHE_LIMIT
+ )
+
+ _validate_linux_arm64_texture_format()
+ await _validate_incremental_profile_previews()
print("Exported decal hotfix validation: PASS")
quit()
+
+
+func _validate_feature_import(texture_path: String) -> void:
+ var import_config := ConfigFile.new()
+ assert(import_config.load(texture_path + ".import") == OK)
+ assert(int(import_config.get_value("params", "compress/mode", -1)) == 2)
+ assert(not bool(import_config.get_value(
+ "params", "mipmaps/generate", true
+ )))
+ assert(int(import_config.get_value(
+ "params", "process/size_limit", 0
+ )) == FACIAL_FEATURE_SIZE_LIMIT)
+ assert(int(import_config.get_value(
+ "params", "detect_3d/compress_to", -1
+ )) == 0)
+
+
+func _validate_linux_arm64_texture_format() -> void:
+ var export_config := ConfigFile.new()
+ assert(export_config.load("res://export_presets.cfg") == OK)
+ assert(not bool(export_config.get_value(
+ "preset.3.options", "texture_format/s3tc_bptc", true
+ )))
+ assert(bool(export_config.get_value(
+ "preset.3.options", "texture_format/etc2_astc", false
+ )))
+
+
+func _validate_incremental_profile_previews() -> void:
+ var profile_page := ProfilePage.new()
+ root.add_child(profile_page)
+ await process_frame
+ for category_id: String in CharacterCustomizationCatalog.FEATURE_CATEGORIES:
+ var cached_before: int = (
+ (profile_page.get("_feature_preview_cache") as Dictionary).size()
+ )
+ profile_page.call("_select_category", category_id)
+ var initial_requests: int = (
+ (profile_page.get("_feature_preview_requests") as Array).size()
+ )
+ assert(initial_requests > 3)
+ for _frame: int in 4:
+ await process_frame
+ var cached_after: int = (
+ (profile_page.get("_feature_preview_cache") as Dictionary).size()
+ )
+ assert(cached_after > cached_before)
+ assert(cached_after - cached_before < initial_requests)
+ profile_page.queue_free()
+ for _frame: int in 8:
+ await process_frame
+
+
+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/file_dialog_controller_navigation_validation.gd b/tests/file_dialog_controller_navigation_validation.gd
new file mode 100644
index 0000000..7ac8df9
--- /dev/null
+++ b/tests/file_dialog_controller_navigation_validation.gd
@@ -0,0 +1,348 @@
+extends SceneTree
+
+const FileDialogControllerNavigationType = preload(
+ "res://ui/file_dialog_controller_navigation.gd"
+)
+const InterfaceFontControllerType = preload(
+ "res://ui/interface_font_controller.gd"
+)
+const OnScreenKeyboardType = preload("res://ui/on_screen_keyboard.gd")
+
+
+func _initialize() -> void:
+ call_deferred("_run")
+
+
+func _run() -> void:
+ root.size = Vector2i(1280, 720)
+ var dialog := FileDialog.new()
+ dialog.file_mode = FileDialog.FILE_MODE_OPEN_DIR
+ dialog.access = FileDialog.ACCESS_FILESYSTEM
+ dialog.use_native_dialog = false
+ root.add_child(dialog)
+ dialog.current_dir = "/tmp"
+ dialog.popup_centered(Vector2i(900, 540))
+ for _frame: int in 3:
+ await process_frame
+ FileDialogControllerNavigationType.configure(dialog)
+ await process_frame
+
+ var root_scope: Window = (
+ FileDialogControllerNavigationType.active_scope(dialog)
+ )
+ assert(root_scope == dialog)
+ var root_controls: Array[Control] = (
+ FileDialogControllerNavigationType.interactive_controls(root_scope)
+ )
+ var directory_list: ItemList
+ var parent_button: Button
+ var create_folder_button: Button
+ var path_edit: LineEdit
+ var drive_button: MenuButton
+ var refresh_button: Button
+ var favorite_button: Button
+ var hidden_button: Button
+ var grid_button: Button
+ var list_button: Button
+ var filter_button: Button
+ var sort_button: MenuButton
+ var cancel_button: Button
+ var select_button: Button
+ for control: Control in root_controls:
+ if (
+ control is ItemList
+ and control.accessibility_name == "Directories & Files:"
+ ):
+ directory_list = control as ItemList
+ if control is LineEdit and control.accessibility_name == "Path:":
+ path_edit = control as LineEdit
+ var button := control as Button
+ if button != null:
+ parent_button = (
+ button
+ if button.tooltip_text == "Go to parent folder."
+ else parent_button
+ )
+ create_folder_button = (
+ button
+ if button.tooltip_text == "Create a new folder."
+ else create_folder_button
+ )
+ refresh_button = (
+ button
+ if button.tooltip_text == "Refresh files."
+ else refresh_button
+ )
+ favorite_button = (
+ button
+ if button.tooltip_text == "(Un)favorite current folder."
+ else favorite_button
+ )
+ hidden_button = (
+ button
+ if button.tooltip_text == (
+ "Toggle the visibility of hidden files."
+ )
+ else hidden_button
+ )
+ grid_button = (
+ button
+ if button.tooltip_text == (
+ "View items as a grid of thumbnails."
+ )
+ else grid_button
+ )
+ list_button = (
+ button
+ if button.tooltip_text == "View items as a list."
+ else list_button
+ )
+ filter_button = (
+ button
+ if button.tooltip_text == (
+ "Toggle the visibility of the filter for file names."
+ )
+ else filter_button
+ )
+ cancel_button = button if button.text == "Cancel" else cancel_button
+ select_button = (
+ button
+ if button.text == "Select Current Folder"
+ else select_button
+ )
+ var menu := control as MenuButton
+ if menu != null:
+ drive_button = menu if menu.accessibility_name == "Drive" else drive_button
+ sort_button = (
+ menu if menu.tooltip_text == "Sort files" else sort_button
+ )
+ assert(directory_list != null)
+ assert(parent_button != null)
+ assert(path_edit != null)
+ assert(create_folder_button != null)
+ assert(drive_button != null)
+ assert(refresh_button != null)
+ assert(favorite_button != null)
+ assert(hidden_button != null)
+ assert(grid_button != null)
+ assert(list_button != null)
+ assert(filter_button != null)
+ assert(sort_button != null)
+ assert(cancel_button != null)
+ assert(select_button != null)
+ assert(dialog.gui_get_focus_owner() == directory_list)
+ _assert_neighbor(path_edit, &"focus_neighbor_left", parent_button)
+ _assert_neighbor(path_edit, &"focus_neighbor_right", drive_button)
+ _assert_neighbor(drive_button, &"focus_neighbor_left", path_edit)
+ _assert_neighbor(refresh_button, &"focus_neighbor_left", drive_button)
+ _assert_neighbor(favorite_button, &"focus_neighbor_left", refresh_button)
+ _assert_neighbor(
+ create_folder_button, &"focus_neighbor_left", favorite_button
+ )
+ _assert_neighbor(hidden_button, &"focus_neighbor_right", grid_button)
+ _assert_neighbor(grid_button, &"focus_neighbor_right", list_button)
+ _assert_neighbor(list_button, &"focus_neighbor_right", filter_button)
+ _assert_neighbor(filter_button, &"focus_neighbor_right", sort_button)
+ _assert_neighbor(sort_button, &"focus_neighbor_top", create_folder_button)
+ _assert_neighbor(
+ create_folder_button, &"focus_neighbor_bottom", sort_button
+ )
+ _assert_neighbor(directory_list, &"focus_neighbor_top", hidden_button)
+ _assert_neighbor(directory_list, &"focus_neighbor_bottom", select_button)
+ _assert_neighbor(cancel_button, &"focus_neighbor_right", select_button)
+ _assert_neighbor(select_button, &"focus_neighbor_left", cancel_button)
+ assert(favorite_button.accessibility_name == "favorite current folder")
+ assert(create_folder_button.accessibility_name == "new folder")
+ assert(filter_button.accessibility_name == "filter file names")
+ _assert_directionally_reachable(directory_list, root_controls)
+ directory_list.clear()
+ directory_list.add_item("test folder")
+ directory_list.select(0)
+ directory_list.grab_focus()
+ assert(
+ FileDialogControllerNavigationType.move_from_item_list(
+ directory_list, Vector2.UP
+ )
+ )
+ assert(dialog.gui_get_focus_owner() == hidden_button)
+ directory_list.grab_focus()
+ assert(
+ FileDialogControllerNavigationType.move_from_item_list(
+ directory_list, Vector2.DOWN
+ )
+ )
+ assert(dialog.gui_get_focus_owner() == select_button)
+ sort_button.grab_focus()
+ var accept_press := InputEventAction.new()
+ accept_press.action = &"ui_accept"
+ accept_press.pressed = true
+ Input.parse_input_event(accept_press)
+ var accept_release := InputEventAction.new()
+ accept_release.action = &"ui_accept"
+ accept_release.pressed = false
+ Input.parse_input_event(accept_release)
+ await process_frame
+ assert(sort_button.get_popup().visible)
+ sort_button.get_popup().hide()
+ await process_frame
+
+ create_folder_button.pressed.emit()
+ for _frame: int in 3:
+ await process_frame
+ FileDialogControllerNavigationType.configure(dialog)
+ await process_frame
+ var folder_scope: Window = (
+ FileDialogControllerNavigationType.active_scope(dialog)
+ )
+ assert(folder_scope != null and folder_scope != dialog)
+ var folder_controls: Array[Control] = (
+ FileDialogControllerNavigationType.interactive_controls(folder_scope)
+ )
+ var folder_name_edit: LineEdit
+ var folder_cancel: Button
+ var folder_ok: Button
+ for control: Control in folder_controls:
+ if control is LineEdit:
+ folder_name_edit = control as LineEdit
+ var button := control as Button
+ if button != null and button.text == "Cancel":
+ folder_cancel = button
+ if button != null and button.text == "OK":
+ folder_ok = button
+ assert(folder_name_edit != null)
+ assert(folder_cancel != null)
+ assert(folder_ok != null)
+ assert(folder_scope.gui_get_focus_owner() == folder_name_edit)
+ _assert_directionally_reachable(folder_name_edit, folder_controls)
+
+ var keyboard := OnScreenKeyboardType.new()
+ root.add_child(keyboard)
+ keyboard.set_enabled(true)
+ folder_name_edit.grab_focus()
+ assert(keyboard.request_for_control(folder_name_edit))
+ assert(keyboard.is_open())
+ assert(keyboard.get_parent() == folder_scope)
+ keyboard.call("_close_keyboard", true)
+
+ var main_source: String = FileAccess.get_file_as_string(
+ "res://main/main.gd"
+ )
+ assert(main_source.contains("picker_scope != _data_folder_dialog"))
+ assert(main_source.contains("picker_scope.hide()"))
+ assert(main_source.contains("is_controller_text_entry_open()"))
+
+ dialog.queue_free()
+ keyboard.queue_free()
+ await process_frame
+ await _validate_compact_dialog()
+ print("File dialog controller navigation validation: PASS")
+ quit()
+
+
+func _validate_compact_dialog() -> void:
+ root.size = Vector2i(640, 480)
+ var font_controller := InterfaceFontControllerType.new()
+ root.add_child(font_controller)
+ var keyboard := OnScreenKeyboardType.new()
+ root.add_child(keyboard)
+ await process_frame
+ keyboard.set_enabled(false)
+ font_controller.set_controller_text_entry_request(
+ Callable(keyboard, "request_for_control"),
+ Callable(keyboard, "is_open"),
+ )
+ var dialog := FileDialog.new()
+ dialog.file_mode = FileDialog.FILE_MODE_OPEN_DIR
+ dialog.access = FileDialog.ACCESS_FILESYSTEM
+ dialog.use_native_dialog = false
+ root.add_child(dialog)
+ dialog.current_dir = "/tmp"
+ font_controller.popup_file_dialog(dialog)
+ for _frame: int in 4:
+ await process_frame
+ assert(dialog.size.x <= 616 and dialog.size.y <= 456)
+ var scope: Window = FileDialogControllerNavigationType.active_scope(dialog)
+ var controls: Array[Control] = (
+ FileDialogControllerNavigationType.interactive_controls(scope)
+ )
+ var directory_list: ItemList
+ var path_edit: LineEdit
+ var select_button: Button
+ for control: Control in controls:
+ if (
+ control is ItemList
+ and control.accessibility_name == "Directories & Files:"
+ ):
+ directory_list = control as ItemList
+ if control is LineEdit and control.accessibility_name == "Path:":
+ path_edit = control as LineEdit
+ if control is Button and (control as Button).text == (
+ "Select Current Folder"
+ ):
+ select_button = control as Button
+ assert(directory_list != null)
+ assert(path_edit != null)
+ assert(select_button != null)
+ _assert_directionally_reachable(directory_list, controls)
+ directory_list.clear()
+ directory_list.add_item("test folder")
+ directory_list.select(0)
+ directory_list.grab_focus()
+ var down_event := InputEventJoypadButton.new()
+ down_event.button_index = JOY_BUTTON_DPAD_DOWN
+ down_event.pressed = true
+ var dialog_controller := font_controller.get(
+ "_file_dialog_controller"
+ ) as FileDialogController
+ dialog_controller.call("_input", down_event)
+ assert(scope.gui_get_focus_owner() == select_button)
+ path_edit.grab_focus()
+ var accept_event := InputEventJoypadButton.new()
+ accept_event.button_index = JOY_BUTTON_A
+ accept_event.pressed = true
+ dialog_controller.call("_input", accept_event)
+ assert(keyboard.is_open())
+ keyboard.call("_close_keyboard", true)
+ dialog.queue_free()
+ font_controller.queue_free()
+ keyboard.queue_free()
+ await process_frame
+
+
+func _assert_directionally_reachable(
+ start: Control,
+ controls: Array[Control],
+) -> void:
+ var expected: Dictionary[int, bool] = {}
+ for control: Control in controls:
+ expected[control.get_instance_id()] = true
+ var visited: Dictionary[int, bool] = {start.get_instance_id(): true}
+ var pending: Array[Control] = [start]
+ while not pending.is_empty():
+ var current: Control = pending.pop_front()
+ for path: NodePath in [
+ current.focus_neighbor_left,
+ current.focus_neighbor_right,
+ current.focus_neighbor_top,
+ current.focus_neighbor_bottom,
+ ]:
+ var neighbor := current.get_node_or_null(path) as Control
+ if (
+ neighbor == null
+ or not expected.has(neighbor.get_instance_id())
+ or visited.has(neighbor.get_instance_id())
+ ):
+ continue
+ visited[neighbor.get_instance_id()] = true
+ pending.append(neighbor)
+ assert(visited.size() == expected.size())
+
+
+func _assert_neighbor(
+ origin: Control,
+ property_name: StringName,
+ expected: Control,
+) -> void:
+ var neighbor_path: NodePath = origin.get(property_name)
+ var neighbor := origin.get_node_or_null(neighbor_path) as Control
+ assert(neighbor == expected)
diff --git a/tests/file_dialog_controller_navigation_validation.gd.uid b/tests/file_dialog_controller_navigation_validation.gd.uid
new file mode 100644
index 0000000..b2156bf
--- /dev/null
+++ b/tests/file_dialog_controller_navigation_validation.gd.uid
@@ -0,0 +1 @@
+uid://bgnku8bs7oxx7
diff --git a/tests/fish_catalog_content_validation.gd b/tests/fish_catalog_content_validation.gd
index 0b7235b..82f5604 100644
--- a/tests/fish_catalog_content_validation.gd
+++ b/tests/fish_catalog_content_validation.gd
@@ -15,6 +15,7 @@ const NetworkFishingServiceType = preload(
"res://network/network_fishing_service.gd"
)
const FishingSpotType = preload("res://fishing/fishing_spot.gd")
+const CalendarSeasonType = preload("res://world/calendar_season.gd")
const Catalog: FishPoolType = preload("res://fish/pools/fish_catalog.tres")
const PondPool: FishPoolType = preload(
@@ -23,10 +24,39 @@ const PondPool: FishPoolType = preload(
const OceanPool: FishPoolType = preload(
"res://fish/pools/starter_ocean_pool.tres"
)
+const GeneratedPondPool: FishPoolType = preload(
+ "res://fish/pools/generated_pond_pool.tres"
+)
+const GeneratedLakePool: FishPoolType = preload(
+ "res://fish/pools/generated_lake_pool.tres"
+)
+const GeneratedRiverPool: FishPoolType = preload(
+ "res://fish/pools/generated_river_pool.tres"
+)
const PelicanBuyer = preload("res://economy/buyers/pelicans.tres")
const StarterRegionScene = preload(
"res://world/regions/starter_island_region.tscn"
)
+const SharedFishPlaceholder: Texture2D = preload(
+ "res://art/exported/creatures/fish_placeholder.png"
+)
+const FINAL_CREATURE_CATALOG_NUMBERS: Array[int] = [
+ 101, 102, 103, 104, 105, 106, 107, 201, 403, 701, 704, 801,
+ 802, 805, 806, 808, 2502, 2504, 2505, 2508, 2601, 2801, 2802,
+ 2803, 2804, 2806, 2807, 3002, 3102, 3103, 3104, 3107, 3201,
+ 3401, 3902, 3906, 4101, 4401, 4701, 5001, 5702, 5901, 5902,
+ 5903, 5904, 6001, 6103, 6406, 6601, 6801, 6804, 6807, 6901,
+ 6902, 6904, 6905, 7101, 7403, 7506, 7508,
+]
+
+const ACTIVE_CATALOG_COUNT: int = 314
+const INACTIVE_CATALOG_COUNT: int = 2
+const FISHING_SPECIES_COUNT: int = 310
+const GENERATED_POND_COUNT: int = 33
+const GENERATED_LAKE_COUNT: int = 20
+const GENERATED_RIVER_COUNT: int = 53
+const STARTER_POND_COUNT: int = 106
+const OCEAN_COUNT: int = 204
const ORIGINAL_IDS: Array[StringName] = [
&"bluegill", &"bass", &"carp", &"sunfish",
@@ -69,15 +99,16 @@ 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",
+ &"bowfin", &"sturgeon_lake", &"gar_longnose", &"paddlefish",
+ &"sturgeon_shovelnose", &"gar_spotted", &"lungfish_west_african",
]
-
-
func _initialize() -> void:
call_deferred("_run")
func _run() -> void:
_validate_catalog_and_pools()
+ _validate_generated_habitat_pools()
_validate_weight_based_display_scale()
_validate_starter_water_bodies()
_validate_authoritative_water_filter()
@@ -86,12 +117,50 @@ func _run() -> void:
quit()
+func _validate_generated_habitat_pools() -> void:
+ var expected_counts: Dictionary = {
+ GeneratedPondPool: GENERATED_POND_COUNT,
+ GeneratedLakePool: GENERATED_LAKE_COUNT,
+ GeneratedRiverPool: GENERATED_RIVER_COUNT,
+ }
+ var freshwater_ids: Dictionary[StringName, bool] = {}
+ for pool: FishPoolType in expected_counts:
+ assert(pool.candidates.size() == int(expected_counts[pool]))
+ for fish: FishDataType in pool.candidates:
+ assert(fish != null and fish.is_fishable())
+ assert(not freshwater_ids.has(fish.id))
+ freshwater_ids[fish.id] = true
+ assert(fish.is_allowed_in_water(WaterType.Type.FRESH_WATER))
+ assert(freshwater_ids.size() == STARTER_POND_COUNT)
+ assert(PondPool.candidates.size() == STARTER_POND_COUNT)
+ for fish: FishDataType in PondPool.candidates:
+ assert(freshwater_ids.has(fish.id))
+ assert(OceanPool.candidates.size() == OCEAN_COUNT)
+ var ocean_ids: Dictionary[StringName, bool] = {}
+ for fish: FishDataType in OceanPool.candidates:
+ assert(fish != null and fish.is_fishable())
+ assert(not ocean_ids.has(fish.id))
+ ocean_ids[fish.id] = true
+ assert(fish.is_allowed_in_water(WaterType.Type.SALT_WATER))
+ for fish: FishDataType in Catalog.candidates:
+ if not fish.is_fishable():
+ continue
+ assert(freshwater_ids.has(fish.id) != ocean_ids.has(fish.id))
+ assert(freshwater_ids.size() + ocean_ids.size() == FISHING_SPECIES_COUNT)
+ assert(GeneratedPondPool.get_fish_by_id(&"gar_longnose") == null)
+ assert(GeneratedRiverPool.get_fish_by_id(&"gar_longnose") != null)
+ assert(GeneratedRiverPool.get_fish_by_id(&"goby_round") == null)
+ assert(GeneratedLakePool.get_fish_by_id(&"goby_round") != null)
+
+
func _validate_weight_based_display_scale() -> void:
var previous_scale: float = 0.0
for fish: FishDataType in Catalog.candidates:
+ if not fish.active:
+ continue
assert(fish.is_selectable())
assert(fish.weight_min_lb > 0.0)
- assert(fish.weight_max_lb <= 1000.0)
+ assert(fish.weight_max_lb <= 500000.0)
var minimum_scale: float = fish.get_display_scale_for_weight(
fish.weight_min_lb
)
@@ -119,9 +188,111 @@ func _validate_weight_based_display_scale() -> void:
func _validate_catalog_and_pools() -> void:
- assert(Catalog.candidates.size() == 53)
- assert(PondPool.candidates.size() == 19)
- assert(OceanPool.candidates.size() == 34)
+ assert(Catalog.candidates.size() == 316)
+ assert(PondPool.candidates.size() == STARTER_POND_COUNT)
+ assert(OceanPool.candidates.size() == OCEAN_COUNT)
+ var active_count: int = 0
+ var inactive_count: int = 0
+ var fishing_count: int = 0
+ var final_art_count: int = 0
+ var placeholder_fishing_count: int = 0
+ var catalog_numbers: Dictionary[int, bool] = {}
+ for fish: FishDataType in Catalog.candidates:
+ assert(fish != null and not fish.id.is_empty())
+ assert(fish.catalog_number > 0)
+ assert(not fish.collection_group.is_empty())
+ assert(not catalog_numbers.has(fish.catalog_number))
+ catalog_numbers[fish.catalog_number] = true
+ assert(not fish.logbook_fact.strip_edges().is_empty())
+ assert(CalendarSeasonType.is_valid_mask(fish.available_seasons))
+ assert(not fish.get_season_text().is_empty())
+ if fish.active:
+ active_count += 1
+ assert(fish.is_selectable())
+ assert(fish.display_texture != null)
+ else:
+ inactive_count += 1
+ assert(not fish.is_selectable())
+ if fish.catalog_number not in FINAL_CREATURE_CATALOG_NUMBERS:
+ assert(fish.display_texture == null)
+ if fish.catalog_number in FINAL_CREATURE_CATALOG_NUMBERS:
+ var expected_path := (
+ "res://art/exported/creatures/%d.png"
+ % fish.catalog_number
+ )
+ var expected_texture: Texture2D = load(
+ expected_path
+ ) as Texture2D
+ assert(expected_texture != null)
+ assert(fish.display_texture == expected_texture)
+ final_art_count += 1
+ if fish.collection_method == FishDataType.CollectionMethod.FISHING:
+ fishing_count += 1
+ assert(fish.active)
+ if fish.catalog_number not in FINAL_CREATURE_CATALOG_NUMBERS:
+ assert(fish.display_texture == SharedFishPlaceholder)
+ placeholder_fishing_count += 1
+ assert(active_count == ACTIVE_CATALOG_COUNT)
+ assert(inactive_count == INACTIVE_CATALOG_COUNT)
+ assert(fishing_count == FISHING_SPECIES_COUNT)
+ assert(final_art_count == FINAL_CREATURE_CATALOG_NUMBERS.size())
+ assert(placeholder_fishing_count == 253)
+ var chum: FishDataType = Catalog.get_fish_by_id(&"salmon_chum")
+ assert(chum != null)
+ assert(chum.get_season_text() == "fall")
+ assert(
+ chum.is_available_in_season(CalendarSeasonType.Season.FALL)
+ )
+ assert(
+ not chum.is_available_in_season(CalendarSeasonType.Season.SUMMER)
+ )
+ var chum_pool := FishPoolType.new()
+ chum_pool.candidates = [chum]
+ var seasonal_collection := CollectionLogType.new()
+ var seasonal_selector := FishSelectorType.new()
+ seasonal_selector.use_deterministic_test_seed = true
+ var summer_context := FishingContextType.new()
+ summer_context.water_type = WaterType.Type.SALT_WATER
+ summer_context.season = CalendarSeasonType.Season.SUMMER
+ seasonal_selector.begin_roll()
+ assert(
+ seasonal_selector.select_fish(
+ chum_pool,
+ summer_context,
+ seasonal_collection,
+ ) == null
+ )
+ var fall_context := FishingContextType.new()
+ fall_context.water_type = WaterType.Type.SALT_WATER
+ fall_context.season = CalendarSeasonType.Season.FALL
+ seasonal_selector.begin_roll()
+ assert(
+ seasonal_selector.select_fish(
+ chum_pool,
+ fall_context,
+ seasonal_collection,
+ ) == chum
+ )
+ seasonal_collection.free()
+ var inactive_fish: FishDataType = Catalog.get_fish_by_id(&"crab_dungeness")
+ assert(inactive_fish != null and not inactive_fish.active)
+ var inactive_pool := FishPoolType.new()
+ inactive_pool.candidates = [inactive_fish]
+ var inactive_context := FishingContextType.new()
+ inactive_context.water_type = WaterType.Type.SALT_WATER
+ var inactive_collection := CollectionLogType.new()
+ var inactive_selector := FishSelectorType.new()
+ inactive_selector.use_deterministic_test_seed = true
+ inactive_selector.begin_roll()
+ assert(
+ inactive_selector.select_fish(
+ inactive_pool,
+ inactive_context,
+ inactive_collection,
+ ) == null
+ )
+ assert(inactive_selector.create_catch(inactive_fish) == null)
+ inactive_collection.free()
for fish_id: StringName in ORIGINAL_IDS:
var original_fish: FishDataType = Catalog.get_fish_by_id(fish_id)
assert(original_fish != null)
@@ -132,28 +303,45 @@ func _validate_catalog_and_pools() -> void:
assert(OceanPool.get_fish_by_id(fish_id) == null)
assert(fresh_fish.is_allowed_in_water(WaterType.Type.FRESH_WATER))
assert(not fresh_fish.is_allowed_in_water(WaterType.Type.SALT_WATER))
- assert(LogbookCatalog.category_for(fresh_fish) == WaterType.Type.FRESH_WATER)
+ assert(
+ LogbookCatalog.category_for(fresh_fish)
+ == LogbookCatalog.Category.FRESH_WATER
+ )
for fish_id: StringName in SALT_WATER_IDS:
var salt_fish: FishDataType = Catalog.get_fish_by_id(fish_id)
assert(OceanPool.get_fish_by_id(fish_id) == salt_fish)
assert(PondPool.get_fish_by_id(fish_id) == null)
assert(salt_fish.is_allowed_in_water(WaterType.Type.SALT_WATER))
assert(not salt_fish.is_allowed_in_water(WaterType.Type.FRESH_WATER))
- assert(LogbookCatalog.category_for(salt_fish) == WaterType.Type.SALT_WATER)
+ assert(
+ LogbookCatalog.category_for(salt_fish)
+ == LogbookCatalog.Category.SALT_WATER
+ )
for fish_id: StringName in CATFISH_IDS:
var fish: FishDataType = Catalog.get_fish_by_id(fish_id)
assert(fish != null)
assert(fish.is_selectable())
assert(PondPool.get_fish_by_id(fish_id) == fish)
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)
+ var expected_tags: Array[StringName] = []
+ if fish_id in [&"catfish_channel", &"catfish_white"]:
+ expected_tags.assign([&"starter_pond", &"pond"])
+ else:
+ expected_tags.append(&"river")
+ assert(fish.availability.allowed_location_tags == expected_tags)
+ assert(
+ LogbookCatalog.category_for(fish)
+ == LogbookCatalog.Category.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)
+ assert(
+ LogbookCatalog.category_for(fish)
+ == LogbookCatalog.Category.FRESH_WATER
+ )
var expected_values: Dictionary[StringName, Array] = {
&"catfish_blue": [1, 1.25, 3.0, 12.0, 6, 9],
@@ -211,17 +399,25 @@ func _validate_catalog_and_pools() -> void:
var pond_context := FishingContextType.new()
pond_context.location_tags = [&"starter_pond"]
pond_context.water_type = WaterType.Type.FRESH_WATER
- var night_pond_context := FishingContextType.new()
- night_pond_context.location_tags = [&"starter_pond"]
- night_pond_context.water_type = WaterType.Type.FRESH_WATER
- night_pond_context.is_night = true
var ocean_context := FishingContextType.new()
ocean_context.location_tags = [&"coast", &"ocean"]
ocean_context.water_type = WaterType.Type.SALT_WATER
for fish_id: StringName in CATFISH_IDS:
var fish: FishDataType = Catalog.get_fish_by_id(fish_id)
- assert(not fish.availability.is_available(pond_context))
- assert(fish.availability.is_available(night_pond_context))
+ var day_habitat_context := FishingContextType.new()
+ if fish_id in [&"catfish_channel", &"catfish_white"]:
+ day_habitat_context.location_tags.append(&"pond")
+ else:
+ day_habitat_context.location_tags.append(&"river")
+ day_habitat_context.water_type = WaterType.Type.FRESH_WATER
+ var night_habitat_context := FishingContextType.new()
+ night_habitat_context.location_tags = (
+ day_habitat_context.location_tags.duplicate()
+ )
+ night_habitat_context.water_type = WaterType.Type.FRESH_WATER
+ night_habitat_context.is_night = true
+ assert(not fish.availability.is_available(day_habitat_context))
+ assert(fish.availability.is_available(night_habitat_context))
assert(not fish.availability.is_available(ocean_context))
var single_species_pool := FishPoolType.new()
single_species_pool.candidates = [fish]
@@ -231,7 +427,7 @@ func _validate_catalog_and_pools() -> void:
selector.begin_roll()
assert(
selector.select_fish(
- single_species_pool, night_pond_context, collection
+ single_species_pool, night_habitat_context, collection
) == fish
)
collection.free()
@@ -269,29 +465,45 @@ func _validate_authoritative_water_filter() -> void:
ocean_region.location_tags = [&"coast", &"ocean"]
var stale_salt_pool_region := FishableWaterRegion.new()
stale_salt_pool_region.water_type = WaterType.Type.FRESH_WATER
- stale_salt_pool_region.fish_pool = OceanPool
+ var salt_only_pool := FishPoolType.new()
+ salt_only_pool.candidates = [Catalog.get_fish_by_id(&"bass")]
+ stale_salt_pool_region.fish_pool = salt_only_pool
stale_salt_pool_region.location_tags = [&"starter_pond"]
var stale_fresh_pool_region := FishableWaterRegion.new()
stale_fresh_pool_region.water_type = WaterType.Type.SALT_WATER
- stale_fresh_pool_region.fish_pool = PondPool
+ var fresh_only_pool := FishPoolType.new()
+ fresh_only_pool.candidates = [Catalog.get_fish_by_id(&"bluegill")]
+ stale_fresh_pool_region.fish_pool = fresh_only_pool
stale_fresh_pool_region.location_tags = [&"coast", &"ocean"]
var evidence := {"discovered_fish_ids": []}
assert(
- service.call("_select_authoritative_fish", pond_region, evidence, null)
- != null
- )
- assert(
- service.call("_select_authoritative_fish", ocean_region, evidence, null)
+ service.call(
+ "_select_authoritative_fish", pond_region, evidence, null, null
+ )
!= null
)
assert(
service.call(
- "_select_authoritative_fish", stale_salt_pool_region, evidence, null
+ "_select_authoritative_fish", ocean_region, evidence, null, null
+ )
+ != null
+ )
+ assert(
+ service.call(
+ "_select_authoritative_fish",
+ stale_salt_pool_region,
+ evidence,
+ null,
+ null,
) == null
)
assert(
service.call(
- "_select_authoritative_fish", stale_fresh_pool_region, evidence, null
+ "_select_authoritative_fish",
+ stale_fresh_pool_region,
+ evidence,
+ null,
+ null,
) == null
)
service.free()
@@ -313,7 +525,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
@@ -342,11 +559,14 @@ func _validate_catches_and_authoritative_sale() -> void:
}
sale_service.set("_buyers", sale_buyers)
- for index: int in Catalog.candidates.size():
- var fish: FishDataType = Catalog.candidates[index]
+ var catch_sequence: int = 0
+ for fish: FishDataType in Catalog.candidates:
+ if not fish.active:
+ continue
+ catch_sequence += 1
var fish_catch: FishCatch = selector.create_catch(fish)
assert(fish_catch != null)
- fish_catch.catch_sequence = index + 1
+ fish_catch.catch_sequence = catch_sequence
assert(fish_catch.fish.display_texture == fish.display_texture)
var loaded: FishCatch = FishCatchType.from_save_dict(
@@ -380,8 +600,9 @@ func _validate_catches_and_authoritative_sale() -> void:
var sale_result: Dictionary = sale_service.call(
"_build_authoritative_result",
1,
- "catalog_sale_%d" % index,
+ "catalog_sale_%d" % catch_sequence,
[loaded.to_network_dict()],
+ [],
PelicanBuyer,
)
assert(bool(sale_result.get("accepted", false)))
diff --git a/tests/fish_hotbar_showcase_validation.gd b/tests/fish_hotbar_showcase_validation.gd
index ec802b0..af3644b 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:
@@ -40,7 +39,6 @@ func _run() -> void:
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)
@@ -58,7 +56,7 @@ func _run() -> void:
)
fish_catch.ensure_identity()
player.inventory.add_catch(fish_catch)
- player.collection_log.mark_quality_discovered(
+ player.collection_log.record_catch(
fish_catch.fish_id,
fish_catch.quality,
)
@@ -83,48 +81,47 @@ 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()
+ world_weather.get_persistent_weather()
)
- var saved_weather_seconds: float = world_weather.get_seconds_remaining()
+ var saved_weather_seconds: float = (
+ world_weather.get_persistent_seconds_remaining()
+ )
+ var saved_time_hours: float = world_time.get_persistent_time_hours()
assert(save_manager.save_now())
var save_path: String = str(save_manager.get("_save_path"))
- var save_file := FileAccess.open(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 hotbar_data: Dictionary = (parsed as Dictionary)["hotbar"]
+ var decoded: Dictionary = ProgressionSaveCodec.read_local_save(save_path)
+ assert(bool(decoded.get("ok", false)))
+ var parsed: Dictionary = decoded["data"]
+ var hotbar_data: Dictionary = parsed["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"]) == 7)
+ assert(int(parsed["save_version"]) == 11)
+ assert(not PlayerHomeState.sanitize_save_data(
+ parsed.get("home", {})
+ ).is_empty())
assert(
- int((parsed as Dictionary)["experience"]["total_experience"])
+ int(parsed["experience"]["total_experience"])
== 125
)
assert(absf(
- float((parsed as Dictionary)["world"]["time_hours"]) - 19.75
+ float(parsed["world"]["time_hours"]) - saved_time_hours
) < 0.01)
assert(
- int((parsed as Dictionary)["world"]["weather"])
+ int(parsed["world"]["weather"])
== int(saved_weather)
)
assert(absf(
float(
- (parsed as Dictionary)["world"][
+ parsed["world"][
"weather_seconds_remaining"
]
) - saved_weather_seconds
@@ -138,6 +135,19 @@ func _run() -> void:
int(saved_masks[String(fish.id)])
== FishQuality.bit_for(FishQuality.Tier.EXCEPTIONAL)
)
+ var saved_catch_counts: Dictionary = (
+ (parsed as Dictionary)["collection"]["catch_counts"]
+ )
+ assert(int(saved_catch_counts[String(fish.id)]) == 1)
+ var legacy_without_counts: Dictionary = parsed.duplicate(true)
+ (legacy_without_counts["collection"] as Dictionary).erase("catch_counts")
+ var legacy_snapshot: RefCounted = save_manager.call(
+ "_build_load_snapshot",
+ legacy_without_counts,
+ )
+ assert(legacy_snapshot != null)
+ var recovered_counts: Dictionary = legacy_snapshot.get("catch_counts")
+ assert(int(recovered_counts.get(fish.id, 0)) == 1)
assert(player.hotbar.clear_slot(1))
assert(player.experience.restore_total_experience(0))
@@ -149,10 +159,12 @@ func _run() -> void:
)
assert(save_manager.load_player_data())
assert(player.experience.get_total_experience() == 125)
- assert(absf(world_time.get_time_hours() - 19.75) < 0.01)
- assert(world_weather.get_weather() == saved_weather)
+ # Loading a save must not replace the active RTC-authoritative clock.
+ assert(absf(world_time.get_time_hours() - 8.0) < 0.01)
+ assert(world_weather.get_persistent_weather() == saved_weather)
assert(absf(
- world_weather.get_seconds_remaining() - saved_weather_seconds
+ world_weather.get_persistent_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)
@@ -163,14 +175,14 @@ func _run() -> void:
FishQuality.Tier.EXCEPTIONAL,
)
)
- assert(not service.is_local_showcase_visible())
- assert(service.toggle_selected_fish())
+ assert(player.collection_log.get_catch_count(fish.id) == 1)
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",
@@ -188,8 +200,8 @@ 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.ENET_CHANNEL_COUNT == 10)
+ assert(NetworkProtocol.PROTOCOL_VERSION == 12)
+ assert(NetworkProtocol.ENET_CHANNEL_COUNT == 18)
assert(
NetworkProtocol.FISH_QUALITY_CAPABILITY
== "fish_quality_v1"
@@ -202,5 +214,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 366128f..99a2169 100644
--- a/tests/fish_quality_validation.gd
+++ b/tests/fish_quality_validation.gd
@@ -25,13 +25,60 @@ func _run() -> void:
_validate_mail_round_trip()
_validate_collection_mastery()
_validate_version_four_migration()
- assert(NetworkProtocol.PROTOCOL_VERSION == 3)
- assert(NetworkProtocol.ENET_CHANNEL_COUNT == 10)
+ _validate_fishing_protocol_v2()
+ assert(NetworkProtocol.PROTOCOL_VERSION == 12)
+ assert(NetworkProtocol.ENET_CHANNEL_COUNT == 18)
+ assert(NetworkProtocol.MOVEMENT_ANIMATION_CHANNEL == 11)
+ assert(
+ NetworkProtocol.MOVEMENT_RECONCILIATION_CAPABILITY
+ == "movement_reconciliation_v2"
+ )
+ assert(
+ NetworkProtocol.FISHING_REPLICATION_CAPABILITY
+ == "fishing_replication_v2"
+ )
assert(NetworkProtocol.FISH_QUALITY_CAPABILITY == "fish_quality_v1")
print("Fish quality validation: PASS")
quit()
+func _validate_fishing_protocol_v2() -> void:
+ var request: Dictionary = {
+ "request_id": "cast-1",
+ "session_id": "session-1",
+ "origin": [0.0, 1.0, 0.0],
+ "target": [0.0, 0.0, -4.0],
+ "charge": 0.5,
+ "rod_id": "basic_fishing_rod",
+ "reel_speed": 0.2,
+ "barrier_damage": 1,
+ "bite_multiplier": 1.0,
+ "rarity_multipliers": [],
+ "discovered_fish_ids": [],
+ "capacity_available": true,
+ "movement_sequence": 17,
+ }
+ assert(NetworkFishingProtocol.validate_cast_request(request).is_empty())
+ var missing_sequence: Dictionary = request.duplicate(true)
+ missing_sequence.erase("movement_sequence")
+ assert(
+ NetworkFishingProtocol.validate_cast_request(missing_sequence)
+ == "Malformed fishing request."
+ )
+ var invalid_sequence: Dictionary = request.duplicate(true)
+ invalid_sequence["movement_sequence"] = -1
+ assert(
+ NetworkFishingProtocol.validate_cast_request(invalid_sequence)
+ == "Fishing request values are outside allowed limits."
+ )
+ assert(NetworkFishingProtocol.OBSERVER_SNAPSHOT_CHANNEL == 10)
+ assert(NetworkWorldSpawnProtocol.SNAPSHOT_CHANNEL == 15)
+ assert(
+ NetworkWorldSpawnProtocol.SNAPSHOT_CHANNEL
+ != NetworkFishingProtocol.SNAPSHOT_CHANNEL
+ )
+
+
func _validate_tiers_and_distribution() -> void:
assert(FishQualityType.TIER_COUNT == 5)
assert(FishQualityType.display_name(0) == "boring")
@@ -83,40 +130,68 @@ func _validate_barrier_challenge_curve() -> void:
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)
- var impressive_low_input: int = FishQualityType.barrier_health_for_catch(
- FishQualityType.Tier.IMPRESSIVE,
- 0,
- 0.0,
- 1.0,
- )
- var impressive_high_input: int = FishQualityType.barrier_health_for_catch(
- FishQualityType.Tier.IMPRESSIVE,
- 4,
- 1.0,
- 1.0,
- )
- assert(impressive_low_input >= 50 and impressive_low_input <= 100)
- assert(impressive_high_input > impressive_low_input)
- assert(impressive_high_input <= 100)
assert(
- FishQualityType.barrier_health_for_catch(
- FishQualityType.Tier.BORING,
- 4,
- 1.0,
- 1.0,
- ) <= 9
+ FishQualityType.barrier_health_range(-1)
+ == Vector2i(1, 9)
)
var profile := CatchDifficultyProfile.new()
profile.barrier_count_min = 1
profile.barrier_count_max = 1
- profile.barrier_health_min = 8
- profile.barrier_health_max = 8
profile.first_barrier_margin = 0.2
profile.final_barrier_margin = 0.2
profile.minimum_barrier_spacing = 0.1
@@ -141,30 +216,53 @@ func _validate_barrier_challenge_curve() -> void:
assert(health <= expected_maximums[quality])
controller.queue_free()
- var shiny_health: int = FishQualityType.barrier_health_for_catch(
- FishQualityType.Tier.SHINY,
- 4,
- 1.0,
- 1.0,
- )
+ var shiny_health: int = FishQualityType.barrier_health_range(
+ FishQualityType.Tier.SHINY
+ ).y
var base_power_clicks: int = ceili(float(shiny_health) / 1.0)
+ var maximum_barrier_damage: int = (
+ PlayerFishingUpgrades.get_barrier_damage_for_level(
+ PlayerFishingUpgrades.MAX_BARRIER_POWER_LEVEL
+ )
+ )
var max_power_clicks: int = ceili(
float(shiny_health)
- / float(PlayerFishingUpgrades.MAX_BARRIER_POWER_LEVEL + 1)
+ / float(maximum_barrier_damage)
)
assert(base_power_clicks == 400)
- assert(max_power_clicks == 100)
+ assert(maximum_barrier_damage == 128)
+ assert(
+ NetworkFishingProtocol.MAX_BARRIER_DAMAGE
+ == maximum_barrier_damage
+ )
+ assert(max_power_clicks == 4)
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_DELAY, 1.5))
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(
+ PlayerFishingUpgrades.BARRIER_POWER_COSTS.size()
+ == PlayerFishingUpgrades.MAX_BARRIER_POWER_LEVEL
+ )
+ for barrier_level: int in range(
+ PlayerFishingUpgrades.MAX_BARRIER_POWER_LEVEL + 1
+ ):
+ assert(upgrades.restore_levels(0, barrier_level))
+ assert(upgrades.get_barrier_damage() == 1 << barrier_level)
+ if barrier_level < PlayerFishingUpgrades.MAX_BARRIER_POWER_LEVEL:
+ assert(
+ upgrades.get_next_barrier_power_cost()
+ == PlayerFishingUpgrades.BARRIER_POWER_COSTS[barrier_level]
+ )
+ else:
+ assert(upgrades.get_next_barrier_power_cost() == -1)
assert(
upgrades.restore_levels(
PlayerFishingUpgrades.MAX_REEL_SPEED_LEVEL,
@@ -254,6 +352,7 @@ func _validate_catch_round_trip_and_sale() -> void:
1,
"quality_sale",
[network_data],
+ [],
PelicanBuyer,
)
assert(bool(accepted.get("accepted", false)))
@@ -285,6 +384,7 @@ func _validate_catch_round_trip_and_sale() -> void:
1,
"quality_sale_forged",
[forged],
+ [],
PelicanBuyer,
)
assert(not bool(rejected.get("accepted", false)))
@@ -348,6 +448,11 @@ func _validate_collection_mastery() -> void:
collection.get_quality_mask(&"bluegill")
== FishQualityType.ALL_TIERS_MASK
)
+ assert(collection.get_catch_count(&"bluegill") == 0)
+ collection.record_catch(&"bluegill", FishQualityType.Tier.BORING)
+ collection.record_catch(&"bluegill", FishQualityType.Tier.SHINY)
+ assert(collection.get_catch_count(&"bluegill") == 2)
+ assert(int(collection.get_catch_counts()[&"bluegill"]) == 2)
collection.queue_free()
@@ -380,7 +485,7 @@ func _validate_version_four_migration() -> void:
version_four,
4,
)
- assert(int(migrated.get("save_version", -1)) == 7)
+ assert(int(migrated.get("save_version", -1)) == 11)
assert(int((migrated["experience"] as Dictionary)["total_experience"]) == 0)
assert(
is_equal_approx(
@@ -388,6 +493,10 @@ func _validate_version_four_migration() -> void:
8.0,
)
)
+ assert(
+ int((migrated["world"] as Dictionary)["seed"])
+ == PlayerSaveManager.DEFAULT_WORLD_SEED
+ )
var catches: Array = migrated["inventory"]["catches"]
assert(int((catches[0] as Dictionary)["quality"]) == 0)
var collection: Dictionary = migrated["collection"]
@@ -400,4 +509,7 @@ func _validate_version_four_migration() -> void:
assert(
PlayerJobService.validate_save_data(migrated.get("jobs", {}))
)
+ assert(not PlayerHomeState.sanitize_save_data(
+ migrated.get("home", {})
+ ).is_empty())
manager.queue_free()
diff --git a/tests/fish_showcase_multiplayer_validation.gd b/tests/fish_showcase_multiplayer_validation.gd
index 23f02ef..5161377 100644
--- a/tests/fish_showcase_multiplayer_validation.gd
+++ b/tests/fish_showcase_multiplayer_validation.gd
@@ -24,6 +24,10 @@ func _run() -> void:
func _run_host() -> void:
var main: Node = await _create_initialized_main()
var session := main.get_node("%NetworkSession") as NetworkSession
+ assert(bool(main.call(
+ "_apply_world", WorldLayout.STARTER_ISLAND, 1, true
+ )))
+ assert(session.set_host_world(WorldLayout.STARTER_ISLAND, 1))
assert(session.start_private_host(TEST_PORT))
var save_manager := main.get("_save_manager") as PlayerSaveManager
assert(save_manager.initialize_new_game())
@@ -35,8 +39,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
@@ -66,7 +70,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()
@@ -118,8 +124,8 @@ func _run_client() -> 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((player.get("_held_fish_display") 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())
var held_display := player.get("_held_fish_display") as Node3D
@@ -132,7 +138,9 @@ func _run_client() -> void:
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()
@@ -166,3 +174,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_authority_validation.gd b/tests/fishing_authority_validation.gd
index dc79c44..b3c17dd 100644
--- a/tests/fishing_authority_validation.gd
+++ b/tests/fishing_authority_validation.gd
@@ -19,6 +19,12 @@ func _run() -> void:
for _frame: int in 8:
await process_frame
assert(bool(main.get("_application_initialized")))
+ assert(bool(main.call(
+ "_apply_world",
+ WorldLayout.GENERATED,
+ PlayerSaveManager.DEFAULT_WORLD_SEED,
+ true,
+ )))
assert(bool(main.call("_prepare_private_host")))
var save_manager := main.get("_save_manager") as PlayerSaveManager
assert(save_manager.initialize_new_game())
@@ -31,18 +37,48 @@ 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
+ var item_catalog := main.get("item_catalog") as ItemCatalog
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 worms: ItemData = item_catalog.get_item_by_id(&"worms")
+ assert(worms != null)
+ if player.bag.get_quantity(&"worms") == 0:
+ assert(player.bag.add_item(&"worms", 2))
+ assert(player.equip_bait(worms))
+ var bait_quantity_before: int = player.bag.get_quantity(&"worms")
+ var fresh_root := main.get_node(
+ "TestWorld/Regions/GeneratedWorldRegion/WaterBodies/FreshWaterBodies"
+ ) as Node3D
+ var pond: WaterBodyAuthoring
+ for child: Node in fresh_root.get_children():
+ var candidate := child as WaterBodyAuthoring
+ if candidate != null and &"pond" in candidate.location_tags:
+ pond = candidate
+ break
+ assert(pond != null)
+ var pond_region := pond.get_node("FishingRegion") as FishableWaterRegion
+ assert(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_transform * Vector3(
+ pond.surface_size.x * 0.5 + 0.8,
+ 1.44,
+ 0.0,
+ )
+ var pond_direction := pond.global_position - player.global_position
+ pond_direction.y = 0.0
+ pond_direction = pond_direction.normalized()
var visuals := player.get_node("Visuals") as Node3D
- visuals.rotation.y = PI * 0.5
+ visuals.global_rotation.y = atan2(-pond_direction.x, -pond_direction.z)
for _frame: int in 4:
await physics_frame
- assert(player.get_facing_direction().dot(Vector3.LEFT) > 0.99)
+ assert(player.get_facing_direction().dot(pond_direction) > 0.99)
fishing_spot.call("_begin_aiming", player)
assert(fishing_spot.state == FishingSpotType.FishingState.AIMING_CAST)
@@ -50,8 +86,10 @@ 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))
+ var aimed_direction := aimed_target - player.global_position
+ aimed_direction.y = 0.0
+ assert(aimed_direction.normalized().dot(pond_direction) > 0.99)
+ 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 +103,14 @@ func _run() -> void:
await process_frame
assert(fishing_spot.state == FishingSpotType.FishingState.WAITING_FOR_BITE)
assert(service.has_local_attempt())
+ assert(player.bag.get_quantity(&"worms") == bait_quantity_before)
+ 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)
@@ -96,9 +138,10 @@ func _run() -> void:
assert(fishing_spot.state == FishingSpotType.FishingState.READY)
assert(player.is_movement_enabled())
assert(not bobber.visible)
+ assert(player.bag.get_quantity(&"worms") == bait_quantity_before)
# A private host rolls and retains the authoritative catch before the fight
- # so its quality can scale the same barriers clients receive in snapshots.
+ # 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)
@@ -113,9 +156,18 @@ func _run() -> void:
attempts = service.get("_attempts")
attempt = attempts.get(session.get_local_peer_id())
assert(attempt != null)
+ assert(player.bag.get_quantity(&"worms") == bait_quantity_before)
+ var fishing_priority_transitions: Array[bool] = []
+ fishing_spot.fishing_input_priority_changed.connect(
+ func(active: bool) -> void:
+ fishing_priority_transitions.append(active)
+ )
service.call("_start_bite", attempt)
await process_frame
assert(attempt.phase == NetworkFishingAttempt.Phase.FIGHTING)
+ assert(fishing_spot.is_fishing_input_priority_active())
+ assert(fishing_priority_transitions == [true])
+ assert(player.bag.get_quantity(&"worms") == bait_quantity_before - 1)
var catalog: FishPool = main.get("fish_catalog") as FishPool
assert(catalog != null)
var fish: FishData = catalog.get_fish_by_id(attempt.fish_id)
@@ -124,35 +176,111 @@ func _run() -> void:
fish,
)
assert(fish_catch != null and fish_catch.is_valid())
- var baseline_controller := CatchController.new()
- root.add_child(baseline_controller)
- baseline_controller.start_authoritative_encounter(
+ 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,
- FishQuality.Tier.BORING,
+ fish_catch.quality,
)
var quality_barriers: Array = attempt.controller.get("_barriers")
- var baseline_barriers: Array = baseline_controller.get("_barriers")
- assert(quality_barriers.size() == baseline_barriers.size())
+ 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 baseline_barrier: RefCounted = baseline_barriers[barrier_index]
+ var reference_barrier: RefCounted = reference_barriers[barrier_index]
assert(
- int(quality_barrier.get("maximum_health"))
- == FishQuality.apply_barrier_health(
- int(baseline_barrier.get("maximum_health")),
- fish_catch.quality,
+ is_equal_approx(
+ float(quality_barrier.get("position")),
+ float(reference_barrier.get("position")),
)
)
- baseline_controller.queue_free()
- service.call("_cancel_attempt", session.get_local_peer_id(), "")
+ 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_spot.is_fishing_input_priority_active())
+ assert(fishing_status.text.is_empty())
+ assert(not fishing_status.visible)
+ assert(not fishing_panel.visible)
+ var reset_test_deadline: int = Time.get_ticks_msec() + 3000
+ while (
+ Time.get_ticks_msec() < reset_test_deadline
+ and fishing_spot.state != FishingSpotType.FishingState.READY
+ ):
+ await process_frame
+ assert(fishing_spot.state == FishingSpotType.FishingState.READY)
+ assert(not fishing_spot.is_fishing_input_priority_active())
+ assert(fishing_priority_transitions == [true, false])
+
+ # Leaving a session during the cast presentation must release every local
+ # action and equipment lock. This is the same cleanup path used by an
+ # in-game server switch or a lost connection.
+ 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")
+ assert(fishing_spot.state == FishingSpotType.FishingState.CASTING)
+ assert(not player.is_movement_enabled())
+ fishing_spot.call(
+ "_on_network_session_state_changed",
+ NetworkSession.State.DISCONNECTING,
+ )
+ assert(fishing_spot.state == FishingSpotType.FishingState.READY)
+ assert(player.is_movement_enabled())
+ assert(fishing_spot.can_change_hotbar_selection())
+ assert(fishing_spot.can_open_fishing_shop())
+
+ # Escape can put the rod into RETURNING immediately before deep-water
+ # recovery begins. Recovery must finish that return before snapshotting its
+ # movement state, otherwise the respawn restores a permanent cast lock.
+ var water_recovery := main.get_node("%WaterRecovery") as WaterRecoveryController
+ assert(water_recovery != null)
+ var pause_menu: PauseMenu = game_ui.get_pause_menu()
+ pause_menu.open_menu()
+ assert(pause_menu.visible)
+ player.set_movement_enabled(false)
+ fishing_spot.set("_active_player", player)
+ fishing_spot.state = FishingSpotType.FishingState.RETURNING
+ water_recovery.call(
+ "_on_recovery_requested",
+ player,
+ player.global_position.y,
+ )
+ assert(fishing_spot.state == FishingSpotType.FishingState.READY)
+ assert(bool(water_recovery.get("_prior_movement_enabled")))
+ assert(bool(water_recovery.get("_prior_camera_input_enabled")))
+ assert(not pause_menu.visible)
+ var test_world := main.get_node("TestWorld") as TestWorld
+ assert(test_world != null)
+ var recovery_entry := water_recovery.get("_entry_position") as Vector3
+ var resolved_recovery := water_recovery.call(
+ "_resolve_recovery_position"
+ ) as Vector3
+ assert(resolved_recovery.is_equal_approx(
+ test_world.get_water_recovery_position(recovery_entry)
+ ))
+ water_recovery.call("_finish_recovery")
+ assert(player.is_movement_enabled())
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 2dae5e3..9d7ef7f 100644
--- a/tests/fishing_multiplayer_validation.gd
+++ b/tests/fishing_multiplayer_validation.gd
@@ -2,6 +2,7 @@ extends SceneTree
const MainScene = preload("res://main/main.tscn")
const FishingSpotType = preload("res://fishing/fishing_spot.gd")
+const TEST_SEED: int = 864209
func _initialize() -> void:
@@ -22,9 +23,23 @@ func _run() -> void:
func _run_host() -> void:
var main: Node = await _create_initialized_main()
+ assert(bool(main.call(
+ "_apply_world",
+ WorldLayout.STARTER_ISLAND,
+ TEST_SEED,
+ true,
+ )))
+ assert(bool(main.call(
+ "_prepare_host_world",
+ WorldLayout.STARTER_ISLAND,
+ TEST_SEED,
+ )))
assert(bool(main.call("_prepare_private_host")))
var save_manager := main.get("_save_manager") as PlayerSaveManager
- assert(save_manager.initialize_new_game())
+ assert(save_manager.initialize_new_game(
+ TEST_SEED,
+ WorldLayout.STARTER_ISLAND,
+ ))
main.call("_enter_gameplay")
var session := main.get_node("%NetworkSession") as NetworkSession
assert(session.set_host_open(true))
@@ -32,7 +47,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
@@ -48,14 +63,40 @@ func _run_host() -> void:
var spawn_service := main.get_node(
"%PlayerSpawnService"
) as PlayerSpawnService
+ var network_home := main.get_node(
+ "%NetworkHomeService"
+ ) as NetworkHomeService
+ var home_world := main.get_node("%HomeWorldService") as HomeWorldService
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
+ var remote_record := session.get_peer_record(remote_peer_id)
+ assert(remote_record != null)
+ assert(await _wait_for_home(
+ network_home, remote_record.identity_fingerprint
+ ))
+ var exterior_spawn: Transform3D = home_world.get_exterior_spawn_transform(
+ remote_record.identity_fingerprint
+ )
+ var exterior_deadline: int = Time.get_ticks_msec() + 5000
+ while (
+ Time.get_ticks_msec() < exterior_deadline
+ and remote_avatar.global_position.distance_to(exterior_spawn.origin) > 0.25
+ ):
+ await physics_frame
+ assert(remote_avatar.global_position.distance_to(exterior_spawn.origin) <= 0.25)
+ # Give the client enough local frames to observe the initial RV-door spawn
+ # before this test publishes its separate fishing-position teleport.
+ for _frame: int in 30:
+ await physics_frame
+ var cast_setup: Dictionary = await _find_clear_fishing_setup(
+ main, remote_avatar
+ )
+ assert(not cast_setup.is_empty(), "no clear fishing setup was found")
+ var expected_position: Vector3 = cast_setup["position"]
+ var expected_direction: Vector3 = cast_setup["direction"]
session.publish_authoritative_teleport(remote_peer_id)
- var aiming_deadline: int = Time.get_ticks_msec() + 5000
+ var aiming_deadline: int = Time.get_ticks_msec() + 15000
while (
Time.get_ticks_msec() < aiming_deadline
and int(remote_avatar.get("_fishing_visual_phase"))
@@ -67,7 +108,7 @@ func _run_host() -> void:
== Player.FishingVisualPhase.CASTING
)
var remote_position_error := (
- remote_avatar.global_position - Vector3(-0.5, 3.95, 2.1)
+ remote_avatar.global_position - expected_position
)
remote_position_error.y = 0.0
assert(
@@ -75,7 +116,7 @@ func _run_host() -> void:
"remote casting position drifted: %s" % remote_avatar.global_position
)
assert(
- remote_avatar.get_facing_direction().dot(Vector3.LEFT) > 0.99,
+ remote_avatar.get_facing_direction().dot(expected_direction) > 0.99,
"remote casting facing drifted: %s"
% remote_avatar.get_facing_direction()
)
@@ -110,7 +151,7 @@ func _run_host() -> void:
Player.FishingVisualPhase.FISHING,
]
)
- var sitting_deadline: int = Time.get_ticks_msec() + 3000
+ 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())
@@ -139,18 +180,31 @@ func _run_host() -> void:
int(remote_avatar.get("_fishing_visual_phase"))
== Player.FishingVisualPhase.RETRACT
)
- assert(remote_animation_player.current_animation == &"retract_sit")
-
- var completion_deadline: int = Time.get_ticks_msec() + 12000
+ 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")
+ var retract_completion_deadline: int = Time.get_ticks_msec() + 5000
+ while (
+ Time.get_ticks_msec() < retract_completion_deadline
+ and not remote_avatar.is_retract_visual_complete()
+ ):
+ await process_frame
+ assert(remote_avatar.is_retract_visual_complete())
+ # Give the client that owns the same presentation a frame boundary to finish
+ # local cleanup before the host intentionally tears down this test session.
+ for _frame: int in 4:
+ await physics_frame
+
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()
@@ -168,6 +222,23 @@ func _run_client() -> void:
joined = true
break
assert(joined)
+ var network_home := main.get_node(
+ "%NetworkHomeService"
+ ) as NetworkHomeService
+ var home_world := main.get_node("%HomeWorldService") as HomeWorldService
+ var local_fingerprint: String = session.get_local_identity_fingerprint()
+ assert(await _wait_for_home(network_home, local_fingerprint))
+ var player := main.get("_player") as Player
+ var exterior_spawn: Transform3D = (
+ home_world.get_exterior_spawn_transform(local_fingerprint)
+ )
+ var home_spawn_deadline: int = Time.get_ticks_msec() + 5000
+ while (
+ Time.get_ticks_msec() < home_spawn_deadline
+ and player.global_position.distance_to(exterior_spawn.origin) > 0.25
+ ):
+ await physics_frame
+ assert(player.global_position.distance_to(exterior_spawn.origin) <= 0.25)
var service := main.get_node(
"%NetworkFishingService"
@@ -178,7 +249,6 @@ func _run_client() -> 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:
@@ -189,29 +259,39 @@ func _run_client() -> void:
"Visuals/CharacterRig/AnimationPlayer"
) as AnimationPlayer
assert(character_animation_player.has_animation(&"retract_sit"))
- var placement_deadline: int = Time.get_ticks_msec() + 5000
+ # The host chooses and publishes a clear target after both RV manifests have
+ # settled. Waiting for that authoritative teleport also coordinates the
+ # animation-observation window between these two independent processes.
+ var placement_deadline: int = Time.get_ticks_msec() + 10000
while (
Time.get_ticks_msec() < placement_deadline
- and player.global_position.distance_to(Vector3(-0.5, 3.95, 2.1))
- > 0.25
+ and player.global_position.distance_to(exterior_spawn.origin) <= 1.0
):
await physics_frame
- 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)
+ assert(player.global_position.distance_to(exterior_spawn.origin) > 1.0)
+ for _frame: int in 2:
+ await physics_frame
+ var expected_position: Vector3 = player.global_position
+ var expected_direction: Vector3 = player.get_facing_direction()
player.set_casting_visual()
assert(
int(player.get("_fishing_visual_phase"))
== Player.FishingVisualPhase.CASTING
)
- for _frame: int in 30:
+ # Keep the transient pull-back pose visible long enough for the separate host
+ # process to observe it even when a cold validation run is CPU-bound.
+ for _frame: int in 120:
await physics_frame
player.set_fishing_visual(false)
- player.call("_set_sitting", true)
+ player.call("_set_sitting", true, true)
assert(player.is_sitting())
for _frame: int in 15:
await physics_frame
+ assert(fishing_spot.state == FishingSpotType.FishingState.READY)
+ assert(fishing_spot.get("_active_player") == null)
fishing_spot.call("_begin_aiming", player)
+ assert(fishing_spot.state == FishingSpotType.FishingState.AIMING_CAST)
assert(player.is_movement_enabled())
assert(
int(player.get("_fishing_visual_phase"))
@@ -220,6 +300,7 @@ func _run_client() -> void:
var cast_charge: float = 0.0
var target: Vector3 = Vector3.ZERO
var cast_origin: Vector3 = fishing_spot.get("_cast_origin_position")
+ var target_diagnostics: Array[String] = []
for charge_step: int in range(1, 21):
var candidate_charge := float(charge_step) / 20.0
fishing_spot.set("_cast_charge", candidate_charge)
@@ -228,14 +309,33 @@ func _run_client() -> void:
var candidate_surface := fishing_spot.get(
"_aim_surface_sample"
) as FishingSurfaceSample
+ target_diagnostics.append(
+ "charge=%.2f surface=%s path=%s target=%s" % [
+ candidate_charge,
+ candidate_surface.is_fishable(),
+ fishing_spot.get("_cast_path_is_clear"),
+ candidate_target,
+ ]
+ )
if (
candidate_surface.is_fishable()
- and fishing_spot.is_cast_path_clear(cast_origin, candidate_target)
+ and bool(fishing_spot.get("_cast_path_is_clear"))
):
cast_charge = candidate_charge
target = candidate_target
break
- assert(cast_charge > 0.0, "no clear fishable test target was found")
+ assert(
+ cast_charge > 0.0,
+ (
+ "no clear fishable test target was found; position=%s facing=%s "
+ + "expected=%s diagnostics=%s"
+ ) % [
+ player.global_position,
+ player.get_facing_direction(),
+ expected_direction,
+ target_diagnostics,
+ ],
+ )
var evidence: Dictionary = fishing_spot.call("_build_network_evidence")
fishing_spot.set("state", FishingSpotType.FishingState.CASTING)
player.set_movement_enabled(false)
@@ -272,7 +372,9 @@ func _run_client() -> void:
]
)
assert(service.has_local_attempt())
- assert(player.bag.get_quantity(&"worms") == worms_before_cast - 1)
+ # Bait remains available while the bobber is waiting. It is consumed only
+ # once the authoritative bite begins.
+ assert(player.bag.get_quantity(&"worms") == worms_before_cast)
var fishing_deadline: int = Time.get_ticks_msec() + 5000
while (
Time.get_ticks_msec() < fishing_deadline
@@ -301,6 +403,12 @@ func _run_client() -> void:
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"
@@ -323,9 +431,18 @@ func _run_client() -> void:
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()
@@ -341,3 +458,79 @@ func _create_initialized_main() -> Node:
await process_frame
assert(bool(main.get("_application_initialized")))
return main
+
+
+func _wait_for_home(
+ network_home: NetworkHomeService,
+ fingerprint: String,
+) -> bool:
+ var deadline: int = Time.get_ticks_msec() + 10000
+ while Time.get_ticks_msec() < deadline:
+ await process_frame
+ var manifests: Dictionary = network_home.get(
+ "_manifests_by_fingerprint"
+ )
+ if manifests.has(fingerprint):
+ return true
+ return false
+
+
+func _find_clear_fishing_setup(main: Node, player: Player) -> Dictionary:
+ var world := main.get_node("TestWorld") as TestWorld
+ var fishing_spot := main.get_node("%FishingSpot") as FishingSpotType
+ var regions: Array[FishableWaterRegion] = world.get_fishable_water_regions()
+ for region: FishableWaterRegion in regions:
+ var body := region.get_parent() as WaterBodyAuthoring
+ if body == null or body.surface_size.x <= 0.0 or body.surface_size.y <= 0.0:
+ continue
+ var center: Vector3 = region.global_position
+ var half_size: Vector2 = body.surface_size * 0.5
+ var candidate_offsets: Array[Vector3] = [
+ Vector3(half_size.x + 0.8, 0.0, 0.0),
+ Vector3(-half_size.x - 0.8, 0.0, 0.0),
+ Vector3(0.0, 0.0, half_size.y + 0.8),
+ Vector3(0.0, 0.0, -half_size.y - 0.8),
+ ]
+ for offset: Vector3 in candidate_offsets:
+ var position := Vector3(
+ center.x + offset.x,
+ region.get_surface_height() + 1.44,
+ center.z + offset.z,
+ )
+ var direction: Vector3 = center - position
+ direction.y = 0.0
+ direction = direction.normalized()
+ player.global_position = position
+ var visuals := player.get_node("Visuals") as Node3D
+ visuals.global_rotation.y = atan2(-direction.x, -direction.z)
+ for _frame: int in 2:
+ await physics_frame
+ var cast_origin: Vector3 = player.get_cast_origin_position()
+ var launch_position: Vector3 = player.get_fishing_rod_tip().global_position
+ for charge_step: int in range(1, 21):
+ var cast_charge := float(charge_step) / 20.0
+ var cast_distance := lerpf(
+ fishing_spot.minimum_cast_distance,
+ fishing_spot.maximum_cast_distance,
+ cast_charge,
+ )
+ var query_position := cast_origin + direction * cast_distance
+ var sample: FishingSurfaceSample = (
+ fishing_spot.resolve_fishing_surface(
+ query_position, cast_origin.y
+ )
+ )
+ var target: Vector3 = sample.get_bobber_position(
+ fishing_spot.solid_bobber_clearance
+ )
+ if (
+ sample.is_fishable()
+ and fishing_spot.is_cast_path_clear(
+ launch_position, target
+ )
+ ):
+ return {
+ "position": player.global_position,
+ "direction": direction,
+ }
+ return {}
diff --git a/tests/fishing_shop_controller_validation.gd b/tests/fishing_shop_controller_validation.gd
new file mode 100644
index 0000000..488a01e
--- /dev/null
+++ b/tests/fishing_shop_controller_validation.gd
@@ -0,0 +1,105 @@
+extends SceneTree
+
+const FishingShopScene: PackedScene = preload(
+ "res://ui/fishing_shop.tscn"
+)
+
+
+func _initialize() -> void:
+ call_deferred("_run")
+
+
+func _run() -> void:
+ root.size = Vector2i(1280, 720)
+ var shop := FishingShopScene.instantiate() as FishingShop
+ root.add_child(shop)
+ shop.show()
+ await process_frame
+
+ var tabs: Array = shop.get("_shop_tabs") as Array
+ assert(tabs.size() == FishingShop.ShopSection.size())
+ var supplies_list := shop.get_node("%SuppliesList") as VBoxContainer
+ var dummy_stock := Button.new()
+ dummy_stock.name = "ControllerTestStock"
+ dummy_stock.text = "test stock"
+ supplies_list.add_child(dummy_stock)
+ var activations: Array[int] = [0]
+ dummy_stock.pressed.connect(
+ func() -> void:
+ activations[0] += 1
+ )
+
+ var accept := InputEventJoypadButton.new()
+ accept.button_index = JOY_BUTTON_A
+ accept.pressed = true
+ var accept_release := InputEventJoypadButton.new()
+ accept_release.button_index = JOY_BUTTON_A
+ accept_release.pressed = false
+ var cancel := InputEventJoypadButton.new()
+ cancel.button_index = JOY_BUTTON_B
+ cancel.pressed = true
+
+ for section_index: int in range(FishingShop.ShopSection.ART_SUPPLIES + 1):
+ shop.set("_shop_section", section_index)
+ shop.call("_select_shop_section", section_index, false)
+ shop.call("_enter_shop_tabs_zone")
+ for _frame: int in 2:
+ await process_frame
+ var tab := tabs[section_index] as Button
+ assert(
+ root.gui_get_focus_owner() == tab,
+ "section %d tab focus mismatch: focused=%s tab=%s visible=%s mode=%d"
+ % [
+ section_index,
+ root.gui_get_focus_owner(),
+ tab,
+ tab.is_visible_in_tree(),
+ tab.focus_mode,
+ ],
+ )
+ assert(
+ shop.get("_controller_zone")
+ == FishingShop.ControllerZone.TABS
+ )
+ shop.call("_input", accept)
+ for _frame: int in 2:
+ await process_frame
+ assert(int(shop.get("_shop_section")) == section_index)
+ assert(
+ shop.get("_controller_zone")
+ == FishingShop.ControllerZone.CONTENT
+ )
+ assert(not _array_contains_control(
+ tabs,
+ root.gui_get_focus_owner(),
+ ))
+ if section_index == FishingShop.ShopSection.BAIT:
+ assert(root.gui_get_focus_owner() == dummy_stock)
+ Input.parse_input_event(accept)
+ await process_frame
+ Input.parse_input_event(accept_release)
+ await process_frame
+ assert(activations[0] == 1)
+ shop.call("_input", cancel)
+ for _frame: int in 2:
+ await process_frame
+ assert(shop.visible)
+ assert(
+ shop.get("_controller_zone")
+ == FishingShop.ControllerZone.TABS
+ )
+ assert(root.gui_get_focus_owner() == tab)
+
+ shop.hide()
+ shop.queue_free()
+ await process_frame
+ print("Fishing shop controller validation: PASS")
+ quit()
+
+
+func _array_contains_control(values: Array, target: Control) -> bool:
+ for value: Variant in values:
+ var control := value as Control
+ if control == target:
+ return true
+ return false
diff --git a/tests/fishing_shop_controller_validation.gd.uid b/tests/fishing_shop_controller_validation.gd.uid
new file mode 100644
index 0000000..228bf9b
--- /dev/null
+++ b/tests/fishing_shop_controller_validation.gd.uid
@@ -0,0 +1 @@
+uid://duwikmb08papi
diff --git a/tests/fishing_surface_validation.gd b/tests/fishing_surface_validation.gd
index 15cc428..8248c36 100644
--- a/tests/fishing_surface_validation.gd
+++ b/tests/fishing_surface_validation.gd
@@ -51,6 +51,8 @@ func _validate_resolver_layers() -> void:
_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_solid_box(world, Vector3(1.0, 1.0, 1.0), Vector3(0.0, 1.49, 2.0))
+ _add_solid_box(world, Vector3(1.0, 1.0, 1.0), Vector3(0.0, 1.5, -2.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
@@ -73,6 +75,21 @@ func _validate_resolver_layers() -> void:
)
assert(water_under_overhang.is_fishable())
assert(is_equal_approx(water_under_overhang.position.y, 2.0))
+ var shallow_water: FishingSurfaceSampleType = resolver.resolve_surface(
+ space_state,
+ Vector3(0.0, 2.0, 2.0),
+ 4.0,
+ 0.04,
+ )
+ assert(shallow_water.is_fishable())
+ var exact_shore_contact: FishingSurfaceSampleType = resolver.resolve_surface(
+ space_state,
+ Vector3(0.0, 2.0, -2.0),
+ 4.0,
+ 0.04,
+ )
+ assert(exact_shore_contact.has_surface)
+ assert(not exact_shore_contact.is_fishable())
var covered_water: FishingSurfaceSampleType = resolver.resolve_surface(
space_state,
@@ -134,6 +151,12 @@ func _validate_portable_water_body() -> void:
water_body.set("surface_size", Vector2(12.0, 8.0))
water_body.set("fishing_depth", 6.0)
water_body.set("recovery_depth", 7.0)
+ water_body.set(
+ "recovery_entry_height_reference",
+ PlayerWaterTrigger.EntryHeightReference.PLAYER_ORIGIN,
+ )
+ water_body.set("recovery_entry_depth_threshold", 0.1)
+ water_body.set("recovery_entry_confirmation_seconds", 0.2)
water_body.set("fish_pool", PondPool)
root.add_child(water_body)
await process_frame
@@ -152,16 +175,138 @@ func _validate_portable_water_body() -> void:
assert(is_equal_approx(fishing_region.get_surface_height(), 7.0))
assert(is_equal_approx(fishing_shape_node.position.y, -3.0))
assert(fishing_shape.size.is_equal_approx(Vector3(12.0, 6.0, 8.0)))
- var recovery_region := water_body.get_node("RecoveryRegion") as Area3D
+ var recovery_region := (
+ water_body.get_node("RecoveryRegion") as PlayerWaterTrigger
+ )
assert(is_equal_approx(recovery_region.position.y, -3.5))
+ assert(
+ recovery_region.entry_height_reference
+ == PlayerWaterTrigger.EntryHeightReference.PLAYER_ORIGIN
+ )
+ assert(is_equal_approx(recovery_region.entry_depth_threshold, 0.1))
+ assert(is_equal_approx(recovery_region.entry_confirmation_seconds, 0.2))
+
+ var recovery_player := PlayerScene.instantiate() as Player
+ assert(recovery_player != null)
+ root.add_child(recovery_player)
+ recovery_player.set_process(false)
+ recovery_player.set_physics_process(false)
+ var recovery_requests: Array[float] = []
+ recovery_region.recovery_requested.connect(
+ func(_player: Player, requested_surface_height: float) -> void:
+ recovery_requests.append(requested_surface_height)
+ )
+ recovery_region.call("_on_body_entered", recovery_player)
+ recovery_player.global_position.y = 6.8
+ recovery_region.call("_physics_process", 0.1)
+ assert(recovery_requests.is_empty())
+ # Breaking the submersion resets confirmation rather than carrying a stale
+ # partial fall into a later jump.
+ recovery_player.global_position.y = 7.0
+ recovery_region.call("_physics_process", 0.1)
+ recovery_player.global_position.y = 6.8
+ recovery_region.call("_physics_process", 0.11)
+ assert(recovery_requests.is_empty())
+ recovery_region.call("_physics_process", 0.1)
+ assert(recovery_requests == [7.0])
+ recovery_region.call("_on_body_exited", recovery_player)
+ recovery_player.queue_free()
+ await process_frame
water_body.queue_free()
await process_frame
+ var mesh_derived_body := WaterBodyScene.instantiate() as WaterBodyAuthoring
+ mesh_derived_body.position = Vector3(-4.0, 3.0, 6.0)
+ mesh_derived_body.derive_coverage_from_visual_mesh = true
+ mesh_derived_body.fishing_depth = 3.0
+ mesh_derived_body.fish_pool = PondPool
+ var derived_visual := mesh_derived_body.get_node(
+ "VisualWater"
+ ) as MeshInstance3D
+ var derived_mesh := derived_visual.mesh as PlaneMesh
+ derived_mesh.size = Vector2(18.0, 11.0)
+ root.add_child(mesh_derived_body)
+ await process_frame
+
+ var derived_shape_node := mesh_derived_body.get_node(
+ "FishingRegion/Shape"
+ ) as CollisionShape3D
+ var derived_shape := derived_shape_node.shape as BoxShape3D
+ assert(derived_shape.size.is_equal_approx(Vector3(18.0, 3.0, 11.0)))
+ assert(derived_shape_node.position.is_equal_approx(Vector3(0.0, -1.5, 0.0)))
+ assert(derived_mesh.size.is_equal_approx(Vector2(18.0, 11.0)))
+
+ mesh_derived_body.queue_free()
+ await process_frame
+
+ var polygon_body := WaterBodyScene.instantiate() as WaterBodyAuthoring
+ polygon_body.position = Vector3(30.0, 5.0, 20.0)
+ polygon_body.surface_polygon = PackedVector2Array([
+ Vector2(-4.0, -4.0),
+ Vector2(4.0, -4.0),
+ Vector2(4.0, 4.0),
+ Vector2(0.0, 1.0),
+ Vector2(-4.0, 4.0),
+ ])
+ polygon_body.visual_surface_enabled = false
+ polygon_body.fish_pool = PondPool
+ root.add_child(polygon_body)
+ await physics_frame
+ await physics_frame
+
+ var polygon_fishing_region := polygon_body.get_node(
+ "FishingRegion"
+ ) as FishableWaterRegionType
+ var polygon_recovery_region := polygon_body.get_node(
+ "RecoveryRegion"
+ ) as Area3D
+ assert(polygon_fishing_region.get_child_count() == 3)
+ assert(polygon_recovery_region.get_child_count() == 3)
+ for child: Node in polygon_fishing_region.get_children():
+ var polygon_shape_node := child as CollisionShape3D
+ assert(polygon_shape_node != null)
+ assert(polygon_shape_node.shape is ConvexPolygonShape3D)
+
+ var polygon_resolver := FishingSurfaceResolverType.new()
+ var polygon_space_state := (
+ polygon_body.get_world_3d().direct_space_state
+ )
+ var polygon_inside: FishingSurfaceSampleType = (
+ polygon_resolver.resolve_surface(
+ polygon_space_state,
+ Vector3(30.0, 0.0, 20.0),
+ 8.0,
+ )
+ )
+ assert(polygon_inside.is_fishable())
+ assert(is_equal_approx(polygon_inside.position.y, 5.0))
+ var polygon_notch: FishingSurfaceSampleType = (
+ polygon_resolver.resolve_surface(
+ polygon_space_state,
+ Vector3(30.0, 0.0, 23.0),
+ 8.0,
+ )
+ )
+ assert(not polygon_notch.has_surface)
+
+ polygon_body.queue_free()
+ await process_frame
+
func _validate_starter_region_surfaces() -> void:
var region := StarterRegionScene.instantiate() as Node3D
root.add_child(region)
+ var ocean_body := region.get_node(
+ "WaterBodies/Ocean"
+ ) as WaterBodyAuthoring
+ assert(ocean_body != null)
+ assert(ocean_body.derive_coverage_from_visual_mesh)
+ assert(not ocean_body.manage_recovery_coverage)
+ var ocean_region := region.get_node(
+ "WaterBodies/Ocean/FishingRegions/OceanFishingRegion"
+ ) as FishableWaterRegionType
+ assert(ocean_region.get_child_count() == 1)
var fishing_spot := FishingSpotScene.instantiate() as FishingSpotType
root.add_child(fishing_spot)
await physics_frame
@@ -179,6 +324,38 @@ func _validate_starter_region_surfaces() -> void:
)
assert(ocean.is_fishable())
assert(is_equal_approx(ocean.position.y, -0.45))
+ var ocean_visual := region.get_node(
+ "WaterBodies/Ocean/VisualWater"
+ ) as WaterSurfaceMotion
+ assert(ocean_visual != null)
+ assert(ocean_visual.is_processing())
+ assert(
+ absf(ocean_visual.global_position.y - ocean.position.y)
+ <= ocean_visual.amplitude + 0.0001
+ )
+ var ocean_shapes := region.get_node(
+ "WaterBodies/Ocean/FishingRegions/OceanFishingRegion"
+ ).get_children()
+ assert(ocean_shapes.size() == 1)
+ var ocean_shape := ocean_shapes[0] as CollisionShape3D
+ assert(ocean_shape != null)
+ var ocean_box := ocean_shape.shape as BoxShape3D
+ assert(ocean_box != null)
+ var ocean_mesh := ocean_visual.mesh as PlaneMesh
+ assert(ocean_mesh != null)
+ assert(ocean_box.size.is_equal_approx(Vector3(
+ ocean_mesh.size.x,
+ 2.0,
+ ocean_mesh.size.y,
+ )))
+ 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),
@@ -189,6 +366,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
@@ -266,11 +464,47 @@ func _validate_bite_wait_distribution() -> void:
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 >= 45.0)
- assert(average_wait_seconds <= 49.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
@@ -285,7 +519,7 @@ func _validate_remote_presentation() -> void:
await process_frame
var origin: Vector3 = player.get_fishing_rod_tip().global_position
var target: Vector3 = origin + Vector3(-4.0, -0.5, 0.0)
- presentation.show_cast(origin, target)
+ presentation.show_cast(origin, target, "cast-attempt")
var bobber := presentation.get("_bobber") as MeshInstance3D
assert(bobber.visible)
assert(presentation.get("_cast_tween") != null)
@@ -295,6 +529,22 @@ func _validate_remote_presentation() -> void:
await create_timer(0.2).timeout
assert(not is_equal_approx(bobber.global_position.y, first_bob_y))
+ # A client joining after the reliable cast event reconstructs the current
+ # remote fishing state from the observer summary instead of waiting for the
+ # next cast. This also covers a bite/fighting transition received by an
+ # already active observer.
+ presentation.cleanup()
+ presentation.synchronize_active("late-attempt", target, false)
+ assert(bobber.visible)
+ assert(
+ int(player.get("_fishing_visual_phase"))
+ == Player.FishingVisualPhase.FISHING
+ )
+ presentation.synchronize_active("late-attempt", target, true)
+ assert(bool(player.get("_fighting_visual_active")))
+ presentation.synchronize_active("late-attempt", target, false)
+ assert(not bool(player.get("_fighting_visual_active")))
+
var fish_catch := FishCatchType.new()
var fish: FishData = PondPool.candidates.front()
fish_catch.fish = fish
@@ -305,11 +555,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.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/friend_multiplayer_validation.gd b/tests/friend_multiplayer_validation.gd
new file mode 100644
index 0000000..f8ef64d
--- /dev/null
+++ b/tests/friend_multiplayer_validation.gd
@@ -0,0 +1,161 @@
+extends SceneTree
+
+const MainScene: PackedScene = preload("res://main/main.tscn")
+const TEST_PORT: int = 18196
+
+
+func _initialize() -> void:
+ call_deferred("_run")
+
+
+func _run() -> void:
+ var arguments := OS.get_cmdline_user_args()
+ if arguments.has("host"):
+ await _run_host()
+ return
+ if arguments.has("client"):
+ await _run_client()
+ return
+ push_error("Friend multiplayer validation needs host or client mode.")
+ quit(1)
+
+
+func _run_host() -> void:
+ var main := await _create_initialized_main()
+ var session := main.get_node("%NetworkSession") as NetworkSession
+ var save_manager := main.get("_save_manager") as PlayerSaveManager
+ assert(bool(main.call(
+ "_apply_world", WorldLayout.STARTER_ISLAND, 1, true
+ )))
+ assert(session.set_host_world(WorldLayout.STARTER_ISLAND, 1))
+ 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 := await _wait_for_remote_peer(session)
+ assert(remote_peer_id > 1)
+ assert(session.peer_supports_capability(
+ remote_peer_id, NetworkProtocol.FRIENDS_CAPABILITY
+ ))
+ var remote_record := session.get_peer_record(remote_peer_id)
+ assert(remote_record != null)
+ var game_ui := main.get_node("%GameUI") as GameUI
+ var prompt_deadline := Time.get_ticks_msec() + 8000
+ while (
+ Time.get_ticks_msec() < prompt_deadline
+ and not game_ui.is_social_prompt_open()
+ ):
+ await process_frame
+ assert(game_ui.is_social_prompt_open())
+ await create_timer(0.15).timeout
+ game_ui.call("_accept_social_prompt")
+
+ var relationships := main.get_node(
+ "%PlayerRelationshipStore"
+ ) as PlayerRelationshipStore
+ var accepted_deadline := Time.get_ticks_msec() + 8000
+ while (
+ Time.get_ticks_msec() < accepted_deadline
+ and not relationships.is_friend(remote_record.identity_fingerprint)
+ ):
+ await process_frame
+ assert(relationships.is_friend(remote_record.identity_fingerprint))
+ var friend := relationships.get_friend_record(
+ remote_record.identity_fingerprint
+ )
+ assert(not str(friend.get("remote_presence_channel", "")).is_empty())
+ assert(not str(friend.get("remote_invite_token", "")).is_empty())
+
+ var disconnect_deadline := 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("Friend multiplayer host validation: PASS")
+ await _cleanup(main, session)
+
+
+func _run_client() -> void:
+ var main := 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 := 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())
+ assert(session.supports_server_capability(
+ NetworkProtocol.FRIENDS_CAPABILITY
+ ))
+ var host_record := session.get_peer_record(1)
+ assert(host_record != null)
+ var service := main.get_node(
+ "%NetworkPlayerListService"
+ ) as NetworkPlayerListService
+ assert(service.send_friend_request(
+ 1,
+ host_record.identity_fingerprint,
+ host_record.display_name,
+ ))
+
+ var relationships := main.get_node(
+ "%PlayerRelationshipStore"
+ ) as PlayerRelationshipStore
+ var accepted_deadline := Time.get_ticks_msec() + 8000
+ while (
+ Time.get_ticks_msec() < accepted_deadline
+ and not relationships.is_friend(host_record.identity_fingerprint)
+ ):
+ await process_frame
+ assert(relationships.is_friend(host_record.identity_fingerprint))
+ var friend := relationships.get_friend_record(
+ host_record.identity_fingerprint
+ )
+ assert(not str(friend.get("remote_presence_channel", "")).is_empty())
+ assert(not str(friend.get("remote_invite_token", "")).is_empty())
+ print("Friend multiplayer client validation: PASS")
+ await _cleanup(main, session)
+
+
+func _create_initialized_main() -> Node:
+ root.size = Vector2i(1280, 720)
+ var main := 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 := 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 _cleanup(main: Node, session: NetworkSession) -> void:
+ session.disconnect_session("")
+ main.queue_free()
+ for _frame: int in 4:
+ await process_frame
+ await create_timer(0.1).timeout
+ quit()
diff --git a/tests/friend_multiplayer_validation.gd.uid b/tests/friend_multiplayer_validation.gd.uid
new file mode 100644
index 0000000..d243a6b
--- /dev/null
+++ b/tests/friend_multiplayer_validation.gd.uid
@@ -0,0 +1 @@
+uid://cxltqyom4yt3j
diff --git a/tests/friend_relationship_validation.gd b/tests/friend_relationship_validation.gd
new file mode 100644
index 0000000..ce6bd90
--- /dev/null
+++ b/tests/friend_relationship_validation.gd
@@ -0,0 +1,76 @@
+extends SceneTree
+
+const FRIEND_FINGERPRINT := (
+ "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"
+)
+
+
+func _initialize() -> void:
+ call_deferred("_run")
+
+
+func _run() -> void:
+ var data_root := PlayerDataRoot.new()
+ root.add_child(data_root)
+ var portable_path := ProjectSettings.globalize_path(
+ "user://friend-relationship-validation"
+ )
+ var created := data_root.create_unbound_root(portable_path)
+ assert(bool(created.get("ok", false)))
+ assert(data_root.activate_process_root(portable_path))
+
+ var store := PlayerRelationshipStore.new()
+ root.add_child(store)
+ store.configure_storage(
+ data_root.path_for(&"player_relationships"), data_root
+ )
+ var local_capabilities := store.create_friend_capabilities()
+ var remote_capabilities := store.create_friend_capabilities()
+ assert(not local_capabilities.is_empty())
+ assert(not remote_capabilities.is_empty())
+ assert(store.add_friend(
+ FRIEND_FINGERPRINT,
+ "Pond Friend",
+ local_capabilities,
+ {
+ "presence_channel": remote_capabilities["presence_channel"],
+ "invite_token": remote_capabilities["invite_token"],
+ },
+ ))
+ assert(store.is_friend(FRIEND_FINGERPRINT))
+
+ var reloaded := PlayerRelationshipStore.new()
+ root.add_child(reloaded)
+ reloaded.configure_storage(
+ data_root.path_for(&"player_relationships"), data_root
+ )
+ assert(reloaded.is_friend(FRIEND_FINGERPRINT))
+ var friend := reloaded.get_friend_record(FRIEND_FINGERPRINT)
+ assert(
+ str(friend["remote_presence_channel"])
+ == str(remote_capabilities["presence_channel"])
+ )
+ assert(
+ str(friend["remote_invite_token"])
+ == str(remote_capabilities["invite_token"])
+ )
+ assert(reloaded.set_blocked(FRIEND_FINGERPRINT, "Pond Friend", true))
+ assert(reloaded.is_blocked(FRIEND_FINGERPRINT))
+ assert(reloaded.is_muted(FRIEND_FINGERPRINT))
+ assert(not reloaded.is_friend(FRIEND_FINGERPRINT))
+ assert(reloaded.set_blocked(FRIEND_FINGERPRINT, "Pond Friend", false))
+ assert(not reloaded.is_blocked(FRIEND_FINGERPRINT))
+ assert(not reloaded.is_muted(FRIEND_FINGERPRINT))
+ assert(not reloaded.is_friend(FRIEND_FINGERPRINT))
+
+ var hello := NetworkProtocol.make_client_hello(
+ "profile",
+ "Pond Friend",
+ "nonce",
+ )
+ assert(
+ NetworkProtocol.FRIENDS_CAPABILITY
+ in PackedStringArray(hello.get("capability_flags", []))
+ )
+ print("Friend relationship validation: PASS")
+ quit()
diff --git a/tests/friend_relationship_validation.gd.uid b/tests/friend_relationship_validation.gd.uid
new file mode 100644
index 0000000..aa2acaf
--- /dev/null
+++ b/tests/friend_relationship_validation.gd.uid
@@ -0,0 +1 @@
+uid://d3v4e1xtu8ytq
diff --git a/tests/fur_pattern_validation.gd b/tests/fur_pattern_validation.gd
new file mode 100644
index 0000000..a795d53
--- /dev/null
+++ b/tests/fur_pattern_validation.gd
@@ -0,0 +1,437 @@
+extends SceneTree
+
+
+func _initialize() -> void:
+ call_deferred("_run")
+
+
+func _run() -> void:
+ CharacterCustomizationCatalog.refresh_fur_pattern_assets()
+ var legacy_snapshot := CharacterCustomizationCatalog.default_snapshot()
+ legacy_snapshot[CharacterCustomizationCatalog.FUR_STYLE_ID] = "spots_bengal"
+ for added_id: String in [
+ CharacterCustomizationCatalog.FUR_STYLE_ARMS_ID,
+ CharacterCustomizationCatalog.FUR_STYLE_HEAD_ID,
+ CharacterCustomizationCatalog.FUR_STYLE_EARS_ID,
+ CharacterCustomizationCatalog.FUR_STYLE_TAIL_ID,
+ ]:
+ legacy_snapshot.erase(added_id)
+ var migrated := CharacterCustomizationCatalog.sanitized_snapshot(
+ legacy_snapshot
+ )
+ assert(CharacterCustomizationCatalog.validate_snapshot(migrated))
+ for style_field: String in CharacterCustomizationCatalog.FUR_STYLE_IDS:
+ assert(migrated[style_field] == "bengal")
+
+ 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 custom_color := Color("2b91c7")
+ var custom_color_id := CharacterCustomizationCatalog.custom_fur_color_id(
+ custom_color
+ )
+ assert(custom_color_id == "custom_2b91c7")
+ assert(
+ CharacterCustomizationCatalog.custom_fur_color_id(
+ Color(custom_color.r, custom_color.g, custom_color.b, 0.2)
+ )
+ == custom_color_id
+ )
+ for color_field: String in CharacterCustomizationCatalog.FUR_COLOR_IDS:
+ assert(CharacterCustomizationCatalog.is_valid_option(
+ color_field,
+ custom_color_id,
+ ))
+ assert(
+ CharacterCustomizationCatalog.option_color(
+ color_field, custom_color_id
+ ).is_equal_approx(custom_color)
+ )
+ assert(not CharacterCustomizationCatalog.is_valid_option(
+ "fur_pattern", "custom_2b91cg"
+ ))
+ assert(not CharacterCustomizationCatalog.is_valid_option(
+ "fur_pattern", "custom_2b91c7ff"
+ ))
+
+ var pattern_ids: Array[String] = []
+ for option: Dictionary in CharacterCustomizationCatalog.options_for(
+ CharacterCustomizationCatalog.FUR_STYLE_ID
+ ):
+ pattern_ids.append(str(option.get("id", "")))
+ assert(pattern_ids == [
+ "solid",
+ "bengal",
+ "calico",
+ "fox",
+ "paws",
+ "stripes",
+ "tiger",
+ "tummy",
+ ])
+
+ var patterned := CharacterCustomizationCatalog.default_snapshot()
+ patterned["species"] = "round"
+ patterned["ears"] = "pointy_short"
+ patterned["tail"] = "fox"
+ patterned["fur_pattern"] = "orange"
+ patterned[CharacterCustomizationCatalog.FUR_STYLE_ID] = "calico"
+ patterned[CharacterCustomizationCatalog.FUR_STYLE_ARMS_ID] = "paws"
+ patterned[CharacterCustomizationCatalog.FUR_STYLE_HEAD_ID] = "tiger"
+ patterned[CharacterCustomizationCatalog.FUR_STYLE_EARS_ID] = "fox"
+ patterned[CharacterCustomizationCatalog.FUR_STYLE_TAIL_ID] = "fox"
+ patterned["fur_color_2"] = "cream"
+ patterned["fur_color_3"] = "brown"
+ patterned["fur_color_4"] = "red"
+ assert(CharacterCustomizationCatalog.validate_snapshot(patterned))
+
+ _assert_option_ids(
+ CharacterCustomizationCatalog.fur_pattern_options_for_field(
+ CharacterCustomizationCatalog.FUR_STYLE_ID, patterned
+ ),
+ ["solid", "bengal", "calico", "fox", "paws", "stripes", "tiger", "tummy"],
+ )
+ _assert_option_ids(
+ CharacterCustomizationCatalog.fur_pattern_options_for_field(
+ CharacterCustomizationCatalog.FUR_STYLE_ARMS_ID, patterned
+ ),
+ ["solid", "bengal", "fox", "paws"],
+ )
+ _assert_option_ids(
+ CharacterCustomizationCatalog.fur_pattern_options_for_field(
+ CharacterCustomizationCatalog.FUR_STYLE_HEAD_ID, patterned
+ ),
+ ["solid", "bengal", "fox", "tiger"],
+ )
+ _assert_option_ids(
+ CharacterCustomizationCatalog.fur_pattern_options_for_field(
+ CharacterCustomizationCatalog.FUR_STYLE_EARS_ID, patterned
+ ),
+ ["solid", "fox"],
+ )
+ _assert_option_ids(
+ CharacterCustomizationCatalog.fur_pattern_options_for_field(
+ CharacterCustomizationCatalog.FUR_STYLE_TAIL_ID, patterned
+ ),
+ ["solid", "fox"],
+ )
+ var pointy_snapshot := patterned.duplicate(true)
+ pointy_snapshot["species"] = "pointy"
+ _assert_option_ids(
+ CharacterCustomizationCatalog.fur_pattern_options_for_field(
+ CharacterCustomizationCatalog.FUR_STYLE_HEAD_ID, pointy_snapshot
+ ),
+ ["solid", "fox"],
+ )
+ var no_accessories := patterned.duplicate(true)
+ no_accessories["ears"] = "none"
+ no_accessories["tail"] = "none"
+ _assert_option_ids(
+ CharacterCustomizationCatalog.fur_pattern_options_for_field(
+ CharacterCustomizationCatalog.FUR_STYLE_EARS_ID, no_accessories
+ ),
+ ["solid"],
+ )
+ _assert_option_ids(
+ CharacterCustomizationCatalog.fur_pattern_options_for_field(
+ CharacterCustomizationCatalog.FUR_STYLE_TAIL_ID, no_accessories
+ ),
+ ["solid"],
+ )
+
+ var body_texture := _assert_pattern_texture("calico", "body_main")
+ var arms_texture := _assert_pattern_texture("paws", "body_arms")
+ var head_texture := _assert_pattern_texture("tiger", "head_round")
+ var ears_texture := _assert_pattern_texture("fox", "ears_pointy_short")
+ var tail_texture := _assert_pattern_texture("fox", "tails_fox")
+ _assert_pattern_texture("tummy", "body_main")
+ _assert_pattern_texture("stripes", "body_main")
+ _assert_pattern_texture("fox", "head_pointy")
+
+ 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 body_material := _assert_mesh_pattern(
+ skeleton, "body_main", body_texture
+ )
+ var arms_material := _assert_mesh_pattern(
+ skeleton, "body_arms", arms_texture
+ )
+ var head_material := _assert_mesh_pattern(
+ skeleton, "head_round", head_texture
+ )
+ var ears_material := _assert_mesh_pattern(
+ skeleton, "ears_pointy_short", ears_texture
+ )
+ var tail_material := _assert_mesh_pattern(
+ skeleton, "tails_fox", tail_texture
+ )
+ for material: ShaderMaterial in [
+ body_material,
+ arms_material,
+ head_material,
+ ears_material,
+ tail_material,
+ ]:
+ assert(material.shader == preload("res://player/fur_pattern.gdshader"))
+ 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 recolored := patterned.duplicate(true)
+ recolored["fur_color_3"] = "teal"
+ PlayerVisualPresenter.apply_appearance(visuals, recolored)
+ for material: ShaderMaterial in [
+ body_material,
+ arms_material,
+ head_material,
+ ears_material,
+ tail_material,
+ ]:
+ assert(material.get_shader_parameter("green_zone_color") == Color("3a8790"))
+ assert(
+ NetworkProfileProtocol.signature_fields({"appearance": patterned})
+ != NetworkProfileProtocol.signature_fields({"appearance": recolored})
+ )
+ var other_arms := patterned.duplicate(true)
+ other_arms[CharacterCustomizationCatalog.FUR_STYLE_ARMS_ID] = "fox"
+ assert(
+ NetworkProfileProtocol.signature_fields({"appearance": patterned})
+ != NetworkProfileProtocol.signature_fields({"appearance": other_arms})
+ )
+ var custom_snapshot := patterned.duplicate(true)
+ custom_snapshot["fur_pattern"] = custom_color_id
+ assert(CharacterCustomizationCatalog.validate_snapshot(custom_snapshot))
+ PlayerVisualPresenter.apply_appearance(visuals, custom_snapshot)
+ for material: ShaderMaterial in [
+ body_material,
+ arms_material,
+ head_material,
+ ears_material,
+ tail_material,
+ ]:
+ assert(
+ (material.get_shader_parameter("base_color") as Color).is_equal_approx(
+ custom_color
+ )
+ )
+ assert(
+ NetworkProfileProtocol.signature_fields({"appearance": patterned})
+ != NetworkProfileProtocol.signature_fields({"appearance": custom_snapshot})
+ )
+
+ var solid := CharacterCustomizationCatalog.default_snapshot()
+ PlayerVisualPresenter.apply_appearance(visuals, solid)
+ assert(body.material_override is StandardMaterial3D)
+ await _validate_fur_color_ui(patterned)
+
+ print("Fur pattern validation: PASS")
+ visuals.queue_free()
+ await process_frame
+ quit()
+
+
+func _validate_fur_color_ui(snapshot: Dictionary) -> void:
+ var profile_page := ProfilePage.new()
+ profile_page.size = Vector2(1280.0, 720.0)
+ root.add_child(profile_page)
+ await process_frame
+ profile_page.set("_draft_appearance", snapshot.duplicate(true))
+ profile_page.call("_select_category", "fur_pattern")
+ profile_page.call("_select_fur_section", "colors")
+ await process_frame
+ await process_frame
+ var color_panel := profile_page.find_child(
+ "FurColorOptionsPanel", true, false
+ ) as PanelContainer
+ var channel_grid := profile_page.find_child(
+ "FurColorChannelGrid", true, false
+ ) as GridContainer
+ var palette_grid := profile_page.find_child(
+ "FurPaletteGrid", true, false
+ ) as GridContainer
+ var picker := profile_page.find_child(
+ "FurCustomColorPicker", true, false
+ ) as ColorPickerButton
+ var picker_popup := picker.get_popup()
+ var picker_control := picker.get_picker()
+ var option_list := profile_page.get("_option_list") as Control
+ assert(color_panel != null and option_list != null)
+ assert(color_panel.size.x > 0.0)
+ assert(color_panel.size.x <= option_list.size.x + 0.5)
+ assert(channel_grid != null and channel_grid.columns == 2)
+ assert(channel_grid.get_child_count() == 4)
+ assert(palette_grid != null and palette_grid.columns == 6)
+ assert(palette_grid.get_child_count() == 30)
+ for option_button: Button in palette_grid.get_children():
+ assert(
+ option_button.custom_minimum_size
+ == Vector2.ONE * ProfilePage.FUR_PALETTE_SWATCH_SIZE
+ )
+ for color_index: int in CharacterCustomizationCatalog.FUR_COLOR_IDS.size():
+ var category_id: String = (
+ CharacterCustomizationCatalog.FUR_COLOR_IDS[color_index]
+ )
+ var channel_button := profile_page.find_child(
+ "FurColorSlot%d" % (color_index + 1), true, false
+ ) as Button
+ assert(channel_button != null)
+ var swatch := channel_button.get_node(
+ "ChannelContents/ActiveColorSwatch"
+ ) as Panel
+ var channel_label := channel_button.get_node(
+ "ChannelContents/ChannelNumber"
+ ) as Label
+ assert(swatch.size.is_equal_approx(
+ Vector2.ONE * ProfilePage.FUR_CHANNEL_SWATCH_SIZE
+ ))
+ assert(swatch.size.y <= channel_label.size.y)
+ var swatch_style := swatch.get_theme_stylebox(
+ "panel"
+ ) as StyleBoxFlat
+ assert(swatch_style != null)
+ assert(
+ swatch_style.corner_radius_top_left
+ == roundi(ProfilePage.FUR_CHANNEL_SWATCH_SIZE * 0.5)
+ )
+ assert(swatch_style.bg_color.is_equal_approx(
+ CharacterCustomizationCatalog.option_color(
+ category_id,
+ str(snapshot[category_id]),
+ )
+ ))
+ var picker_style := picker.get_theme_stylebox("normal") as StyleBoxFlat
+ assert(picker_style != null and picker_style.corner_radius_top_left == 14)
+ var picker_window_size: Vector2i = profile_page.get_window().size
+ var expected_picker_size := Vector2i(
+ mini(
+ ProfilePage.FUR_COLOR_PICKER_POPUP_SIZE.x,
+ maxi(picker_window_size.x - 32, 420),
+ ),
+ mini(
+ ProfilePage.FUR_COLOR_PICKER_POPUP_SIZE.y,
+ maxi(picker_window_size.y - 32, 340),
+ ),
+ )
+ assert(picker_popup.min_size == expected_picker_size)
+ assert(picker_popup.theme.default_font == UtilityPageStyle.TuffyFont)
+ var popup_style := picker_popup.get_theme_stylebox(
+ "panel"
+ ) as StyleBoxFlat
+ assert(popup_style != null)
+ assert(popup_style.bg_color.a == 1.0)
+ assert(popup_style.corner_radius_top_left == 20)
+ assert(
+ picker_control.get_theme_constant("sv_width")
+ == mini(
+ ProfilePage.FUR_COLOR_PICKER_SV_SIZE.x,
+ expected_picker_size.x - 110,
+ )
+ )
+ assert(
+ picker_control.get_theme_constant("sv_height")
+ == mini(
+ ProfilePage.FUR_COLOR_PICKER_SV_SIZE.y,
+ expected_picker_size.y - 180,
+ )
+ )
+ assert(not picker_control.edit_alpha)
+ assert(not picker_control.edit_intensity)
+ var picker_display := picker.get_node("FurCustomColorDisplay") as Panel
+ var display_style := picker_display.get_theme_stylebox(
+ "panel"
+ ) as StyleBoxFlat
+ assert(display_style != null and display_style.corner_radius_top_left == 11)
+ var live_custom_color := Color("2b91c7")
+ profile_page.call(
+ "_select_custom_fur_color",
+ live_custom_color,
+ CharacterCustomizationCatalog.FUR_COLOR_IDS[0],
+ )
+ var first_swatch := profile_page.find_child(
+ "FurColorSlot1", true, false
+ ).get_node("ChannelContents/ActiveColorSwatch") as Panel
+ var first_swatch_style := first_swatch.get_theme_stylebox(
+ "panel"
+ ) as StyleBoxFlat
+ assert(first_swatch_style.bg_color.is_equal_approx(live_custom_color))
+ picker_popup.popup_centered()
+ for _frame: int in 2:
+ await process_frame
+ picker_popup.hide()
+ for _frame: int in 3:
+ await process_frame
+ assert(is_instance_valid(picker))
+ assert(picker.is_inside_tree())
+ assert(
+ profile_page.find_child(
+ "FurCustomColorPicker", true, false
+ ) == picker
+ )
+ profile_page.free()
+ await process_frame
+
+
+func _assert_option_ids(options: Array, expected: Array[String]) -> void:
+ var actual: Array[String] = []
+ for option: Dictionary in options:
+ actual.append(str(option.get("id", "")))
+ assert(actual == expected)
+
+
+func _assert_pattern_texture(
+ pattern_id: String,
+ component_id: String,
+) -> Texture2D:
+ var texture := CharacterCustomizationCatalog.fur_pattern_texture(
+ pattern_id, component_id
+ )
+ assert(texture != null)
+ assert(texture.get_width() == 1024)
+ assert(texture.get_height() == 1024)
+ return texture
+
+
+func _assert_mesh_pattern(
+ skeleton: Skeleton3D,
+ mesh_name: String,
+ expected_texture: Texture2D,
+) -> ShaderMaterial:
+ var mesh := skeleton.get_node_or_null(mesh_name) as MeshInstance3D
+ assert(mesh != null)
+ var material := mesh.material_override as ShaderMaterial
+ assert(material != null)
+ assert(material.get_shader_parameter("pattern_texture") == expected_texture)
+ return material
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/gathering_marker_surface_validation.gd b/tests/gathering_marker_surface_validation.gd
new file mode 100644
index 0000000..72f15f0
--- /dev/null
+++ b/tests/gathering_marker_surface_validation.gd
@@ -0,0 +1,101 @@
+extends SceneTree
+
+const PlayerScene: PackedScene = preload("res://player/player.tscn")
+const GatheringControllerType = preload(
+ "res://gathering/gathering_controller.gd"
+)
+
+
+func _initialize() -> void:
+ call_deferred("_run")
+
+
+func _run() -> void:
+ var player := PlayerScene.instantiate() as Player
+ root.add_child(player)
+ player.set_process(false)
+ player.set_physics_process(false)
+
+ var controller := GatheringControllerType.new()
+ root.add_child(controller)
+ controller.set("_player", player)
+
+ var wall := _make_surface(
+ Vector3(2.0, 2.0, 0.1),
+ Vector3(0.0, 0.9, -1.1),
+ )
+ root.add_child(wall)
+ await physics_frame
+ controller.call("_update_marker_target")
+ var marker := controller.get("_marker") as MeshInstance3D
+ assert(marker != null)
+ var disc := marker.mesh as CylinderMesh
+ assert(disc != null)
+ assert(is_equal_approx(disc.top_radius, controller.marker_radius))
+ assert(is_equal_approx(disc.bottom_radius, controller.marker_radius))
+ var invalid_material := controller.get(
+ "_marker_invalid_material"
+ ) as StandardMaterial3D
+ var valid_material := controller.get(
+ "_marker_valid_material"
+ ) as StandardMaterial3D
+ assert(invalid_material != null)
+ assert(valid_material != null)
+ assert(invalid_material.albedo_color == Color.WHITE)
+ assert(is_equal_approx(invalid_material.albedo_color.a, 1.0))
+ assert(is_equal_approx(valid_material.albedo_color.a, 1.0))
+ assert(invalid_material.no_depth_test)
+ assert(valid_material.no_depth_test)
+ assert(
+ invalid_material.shading_mode
+ == BaseMaterial3D.SHADING_MODE_UNSHADED
+ )
+ assert(
+ valid_material.shading_mode
+ == BaseMaterial3D.SHADING_MODE_UNSHADED
+ )
+ assert(bool(controller.get("_marker_has_surface")))
+ assert(marker.global_basis.y.dot(Vector3.BACK) > 0.99)
+
+ wall.queue_free()
+ await process_frame
+ var floor := _make_surface(
+ Vector3(10.0, 0.2, 10.0),
+ Vector3(0.0, -0.1, -1.7),
+ )
+ root.add_child(floor)
+ await physics_frame
+ controller.call("_update_marker_target")
+ assert(bool(controller.get("_marker_has_surface")))
+ assert(marker.global_basis.y.dot(Vector3.UP) > 0.99)
+ assert(is_equal_approx(absf(marker.global_basis.determinant()), 1.0))
+ var horizontal_marker_offset := Vector2(
+ marker.global_position.x - player.global_position.x,
+ marker.global_position.z - player.global_position.z,
+ ).length()
+ assert(
+ is_equal_approx(
+ horizontal_marker_offset,
+ controller.marker_distance,
+ )
+ )
+
+ floor.queue_free()
+ controller.queue_free()
+ player.queue_free()
+ await process_frame
+ print("Gathering marker surface validation: PASS")
+ quit()
+
+
+func _make_surface(size: Vector3, position: Vector3) -> StaticBody3D:
+ var body := StaticBody3D.new()
+ body.position = position
+ body.collision_layer = 1
+ body.collision_mask = 0
+ var collision := CollisionShape3D.new()
+ var shape := BoxShape3D.new()
+ shape.size = size
+ collision.shape = shape
+ body.add_child(collision)
+ return body
diff --git a/tests/gathering_marker_surface_validation.gd.uid b/tests/gathering_marker_surface_validation.gd.uid
new file mode 100644
index 0000000..044defc
--- /dev/null
+++ b/tests/gathering_marker_surface_validation.gd.uid
@@ -0,0 +1 @@
+uid://ddsf88kuxgsqp
diff --git a/tests/gathering_showcase_validation.gd b/tests/gathering_showcase_validation.gd
new file mode 100644
index 0000000..51d6f38
--- /dev/null
+++ b/tests/gathering_showcase_validation.gd
@@ -0,0 +1,143 @@
+extends SceneTree
+
+const MainScene: PackedScene = preload("res://main/main.tscn")
+const TEST_PORT: int = 18142
+
+
+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(bool(main.call(
+ "_apply_world", WorldLayout.STARTER_ISLAND, 1, true
+ )))
+ assert(session.set_host_world(WorldLayout.STARTER_ISLAND, 1))
+ assert(session.start_private_host(TEST_PORT))
+ var save_manager := main.get("_save_manager") as PlayerSaveManager
+ assert(save_manager.initialize_new_game(1, WorldLayout.STARTER_ISLAND))
+ main.call("_enter_gameplay")
+ for _frame: int in 8:
+ await process_frame
+
+ var player := main.get("_player") as Player
+ var fishing_spot := main.get_node("%FishingSpot") as FishingSpot
+ var game_ui := main.get_node("%GameUI") as GameUI
+ var catalog := main.get("fish_catalog") as FishPool
+ assert(
+ player != null
+ and fishing_spot != null
+ and game_ui != null
+ and catalog != null
+ )
+ fishing_spot.minimum_showcase_duration = 1.0
+ assert(player.bag.add_item(&"crab_net"))
+ assert(player.hotbar.assign_item(0, &"crab_net"))
+ assert(player.hotbar.select_slot(0))
+ await process_frame
+ assert(player.hotbar.get_selected_item_id() == &"crab_net")
+ assert(bool(player.get("_active_item_is_net")))
+
+ var crab: FishData = catalog.get_fish_by_id(&"crab_brown")
+ assert(crab != null and crab.is_selectable())
+ var crab_catch := FishCatch.new()
+ crab_catch.fish = crab
+ crab_catch.fish_id = crab.id
+ crab_catch.weight_lb = crab.get_minimum_weight()
+ crab_catch.display_scale = crab.get_display_scale_for_weight(
+ crab_catch.weight_lb
+ )
+ crab_catch.sale_value = crab.get_sale_value_for_weight(
+ crab_catch.weight_lb
+ )
+ crab_catch.ensure_identity()
+ player.inventory.add_catch(crab_catch)
+ player.collection_log.mark_discovered(crab.id)
+
+ assert(fishing_spot.present_external_catch(crab_catch))
+ assert(fishing_spot.state == FishingSpot.FishingState.SHOWING_CATCH)
+ assert(not player.is_movement_enabled())
+ # Network attempt cleanup may restore the base movement flag before the
+ # presentation finishes. The showcase owns a separate local-input lock.
+ player.set_movement_enabled(true)
+ assert(not bool(player.call("_is_movement_input_enabled")))
+ assert(not bool(player.call("_is_camera_input_enabled")))
+ Input.action_release("fish_primary")
+ await process_frame
+ assert(not bool(fishing_spot.get("_put_away_press_armed")))
+
+ var pocket_event := InputEventAction.new()
+ pocket_event.action = &"fish_primary"
+ pocket_event.pressed = true
+ Input.parse_input_event(pocket_event)
+ await process_frame
+ assert(fishing_spot.get("_pending_catch") == crab_catch)
+ Input.action_release("fish_primary")
+ fishing_spot.set("_showcase_input_lock_remaining", 0.0)
+ await process_frame
+ assert(bool(fishing_spot.get("_put_away_press_armed")))
+ Input.parse_input_event(pocket_event)
+ await process_frame
+ assert(fishing_spot.get("_pending_catch") == null)
+
+ # A radial menu can open after the showcase panel disappears but before the
+ # pocket animation restores gameplay. Its camera lock must not overwrite the
+ # showcase's underlying camera state when the two finish in either order.
+ Input.action_press("open_emotes")
+ var emote_press := InputEventAction.new()
+ emote_press.action = &"open_emotes"
+ emote_press.pressed = true
+ game_ui.call("_input", emote_press)
+ var emote_menu := game_ui.get_node("%EmoteRadialMenu") as EmoteRadialMenu
+ assert(emote_menu.is_open())
+ assert(not bool(player.call("_is_camera_input_enabled")))
+
+ var pocket_deadline: int = Time.get_ticks_msec() + 6000
+ while (
+ Time.get_ticks_msec() < pocket_deadline
+ and fishing_spot.state != FishingSpot.FishingState.READY
+ ):
+ await process_frame
+ assert(fishing_spot.state == FishingSpot.FishingState.READY)
+ assert(player.is_movement_enabled())
+ assert(bool(player.call("_is_movement_input_enabled")))
+ assert(not bool(player.call("_is_camera_input_enabled")))
+ Input.action_release("open_emotes")
+ var emote_release := InputEventAction.new()
+ emote_release.action = &"open_emotes"
+ emote_release.pressed = false
+ game_ui.call("_input", emote_release)
+ assert(not emote_menu.is_open())
+ assert(bool(player.call("_is_camera_input_enabled")))
+
+ # A focused chat field or popup can consume the wheel's release event. The
+ # polled action state must still close the radial and release its camera lock.
+ Input.action_press("open_emotes")
+ game_ui.call("_input", emote_press)
+ assert(emote_menu.is_open())
+ Input.action_release("open_emotes")
+ await process_frame
+ assert(not emote_menu.is_open())
+ assert(bool(player.call("_is_camera_input_enabled")))
+ assert(player.inventory.contains_catch_id(crab_catch.catch_id))
+ assert(bool(player.get("_active_item_is_net")))
+ print("Gathering showcase validation: PASS")
+
+ session.disconnect_session("")
+ main.queue_free()
+ for _frame: int in 4:
+ await process_frame
+ await create_timer(0.1).timeout
+ quit()
diff --git a/tests/gathering_showcase_validation.gd.uid b/tests/gathering_showcase_validation.gd.uid
new file mode 100644
index 0000000..a0266d4
--- /dev/null
+++ b/tests/gathering_showcase_validation.gd.uid
@@ -0,0 +1 @@
+uid://cedarlvsw4bpm
diff --git a/tests/generated_world_runtime_validation.gd b/tests/generated_world_runtime_validation.gd
new file mode 100644
index 0000000..076f1c9
--- /dev/null
+++ b/tests/generated_world_runtime_validation.gd
@@ -0,0 +1,1811 @@
+extends SceneTree
+
+const RegionScene: PackedScene = preload(
+ "res://world/generation/generated_world_region.tscn"
+)
+const GeneratedPondPool: FishPool = preload(
+ "res://fish/pools/generated_pond_pool.tres"
+)
+const GeneratedLakePool: FishPool = preload(
+ "res://fish/pools/generated_lake_pool.tres"
+)
+const GeneratedRiverPool: FishPool = preload(
+ "res://fish/pools/generated_river_pool.tres"
+)
+const Gatherables: GatherableCatalog = preload(
+ "res://gathering/catalog/gatherable_catalog.tres"
+)
+const PrecipitationOcclusionType = preload(
+ "res://world/environment/precipitation_occlusion.gd"
+)
+const FIRST_SEED := 13001
+const SECOND_SEED := 13002
+const RIVER_FALLBACK_SEED := 13012
+const EXPECTED_PROP_IDS: Array[StringName] = [
+ &"prop_bridge",
+ &"prop_dock",
+ &"prop_log",
+ &"prop_log_post_1",
+ &"prop_log_post_2",
+ &"prop_mushroom",
+ &"prop_palm",
+ &"prop_pine",
+ &"prop_pine_large",
+ &"prop_timber_1",
+ &"prop_timber_2",
+ &"prop_tree_1",
+ &"prop_tree_2",
+ &"prop_tree_3",
+ &"prop_tree_large",
+]
+const EXPECTED_BIOME_IDS: Array[StringName] = [
+ &"biome_plains",
+ &"biome_forest",
+ &"biome_pine_forest",
+ &"biome_coast",
+]
+const PROCEDURAL_PROP_IDS: Array[StringName] = [
+ &"prop_log",
+ &"prop_mushroom",
+ &"prop_palm",
+ &"prop_pine",
+ &"prop_pine_large",
+ &"prop_tree_1",
+ &"prop_tree_2",
+ &"prop_tree_3",
+ &"prop_tree_large",
+]
+
+
+func _initialize() -> void:
+ call_deferred(&"_run")
+
+
+func _run() -> void:
+ var region := RegionScene.instantiate() as GeneratedWorldRegion
+ assert(region != null)
+ var configured_generator := region.get_node(
+ "Terrain/TerrainChunkGenerator"
+ ) as TerrainChunkGenerator
+ assert(configured_generator != null)
+ configured_generator.elevated_cliff_third_level_chance = 1.0
+ configured_generator.elevated_cliff_third_tier_base_chance = 0.0
+ configured_generator.elevated_cliff_third_tier_stack_chance = 1.0
+ configured_generator.elevated_cliff_double_third_tier_chance = 0.0
+ root.add_child(region)
+ await process_frame
+ assert(region.generate_world(FIRST_SEED))
+ await physics_frame
+
+ var generator := region.get_node(
+ "Terrain/TerrainChunkGenerator"
+ ) as TerrainChunkGenerator
+ assert(generator != null)
+ assert(region.get_generation_seed() == FIRST_SEED)
+ _validate_generated_region(region, generator)
+
+ var first_layout := generator.placement_keys()
+ var first_stacked_layout := generator.stacked_elevated_placement_keys()
+ var first_biomes := _biome_signature(region, generator)
+ var first_decorations := _decoration_signature(region)
+ assert(region.generate_world(FIRST_SEED))
+ assert(generator.placement_keys() == first_layout)
+ assert(_biome_signature(region, generator) == first_biomes)
+ assert(_decoration_signature(region) == first_decorations)
+ generator._elevated_feature_center = Vector2i(-1, -1)
+ assert(generator.generate_from_placement_keys(first_layout))
+ assert(generator.stacked_elevated_placement_keys() == first_stacked_layout)
+
+ configured_generator.elevated_cliff_third_tier_base_chance = 1.0
+ configured_generator.elevated_cliff_third_tier_stack_chance = 0.0
+ assert(region.generate_world(SECOND_SEED))
+ await process_frame
+ await physics_frame
+ await physics_frame
+ _validate_generated_region(region, generator)
+ assert(generator.placement_keys() != first_layout)
+ assert(_biome_signature(region, generator) != first_biomes)
+ var second_layout := generator.placement_keys()
+
+ configured_generator.elevated_cliff_double_third_tier_chance = 1.0
+ assert(region.generate_world(RIVER_FALLBACK_SEED))
+ await process_frame
+ await physics_frame
+ await physics_frame
+ _validate_generated_region(region, generator)
+ assert(generator.placement_keys() != first_layout)
+ assert(generator.placement_keys() != second_layout)
+ assert(generator._river_feature_candidate_index >= 0)
+
+ region.queue_free()
+ await process_frame
+ print("Generated world runtime validation: PASS")
+ quit()
+
+
+func _validate_generated_region(
+ region: GeneratedWorldRegion,
+ generator: TerrainChunkGenerator,
+) -> void:
+ var placements := generator.placement_keys()
+ var expected_chunk_count := generator.grid_size.x * generator.grid_size.y
+ var center := Vector2i(
+ generator.grid_size.x / 2,
+ generator.grid_size.y / 2,
+ )
+ var center_index := center.y * generator.grid_size.x + center.x
+ assert(generator.grid_size == Vector2i(20, 20))
+ assert(generator._lake_feature_footprint_size.x >= 7)
+ assert(
+ generator._lake_feature_footprint_size.x
+ == generator._lake_feature_footprint_size.y
+ )
+ assert(maxi(
+ generator._river_feature_footprint_size.x,
+ generator._river_feature_footprint_size.y,
+ ) >= 4)
+ assert(mini(
+ generator._river_feature_footprint_size.x,
+ generator._river_feature_footprint_size.y,
+ ) >= 4)
+ assert(generator._river_feature_flow_direction in [
+ Vector2i.RIGHT,
+ Vector2i.DOWN,
+ Vector2i.LEFT,
+ Vector2i.UP,
+ ])
+ assert(generator._river_feature_outlet_direction in [
+ Vector2i.RIGHT,
+ Vector2i.DOWN,
+ Vector2i.LEFT,
+ Vector2i.UP,
+ ])
+ assert(
+ generator._river_feature_flow_direction.x
+ * generator._river_feature_outlet_direction.x
+ + generator._river_feature_flow_direction.y
+ * generator._river_feature_outlet_direction.y
+ == 0
+ )
+ assert(generator.elevated_cliff_coastal_feature_required)
+ assert(placements.size() == expected_chunk_count)
+ assert(placements[center_index].begins_with("chunk_spawn@"))
+ assert(_placement_count(placements, "chunk_spawn") == 1)
+ assert(_placement_count(placements, "chunk_0001") >= 1)
+ var stream_count := (
+ _placement_count(placements, "chunk_0003")
+ + _placement_count(placements, "chunk_0016")
+ )
+ assert(stream_count >= 1 and stream_count <= 3)
+ assert(_placement_count(placements, "chunk_0004") == 2)
+ var beach_count := _placement_count(placements, "chunk_0002")
+ var grass_coast_count := _placement_count(placements, "chunk_0005")
+ var grass_corner_count := _placement_count(placements, "chunk_0007")
+ var beach_corner_count := _placement_count(placements, "chunk_0008")
+ var has_secondary_elevation := (
+ generator._secondary_elevated_feature_center.x >= 0
+ )
+ var has_third_tier_base := (
+ _placement_count(placements, "chunk_0028") > 0
+ )
+ var coastline_edge_count := (
+ 2 * (generator.grid_size.x - 2)
+ + 2 * (generator.grid_size.y - 2)
+ )
+ assert(
+ beach_count
+ == coastline_edge_count - (4 if has_secondary_elevation else 0) - 2
+ and grass_coast_count == 0
+ )
+ assert(
+ beach_corner_count == (3 if has_secondary_elevation else 4)
+ and grass_corner_count == 0
+ )
+ assert(
+ _placement_count(placements, "chunk_0009")
+ == (
+ (1 if has_secondary_elevation else 0)
+ + (0 if has_third_tier_base else 9)
+ )
+ )
+ assert(
+ _placement_count(placements, "chunk_0010")
+ == (
+ (1 if has_secondary_elevation else 0)
+ + (0 if has_third_tier_base else 4)
+ )
+ )
+ assert(
+ _placement_count(placements, "chunk_0011")
+ + _placement_count(placements, "chunk_0012")
+ == (
+ (2 if has_secondary_elevation else 0)
+ + (0 if has_third_tier_base else 12)
+ )
+ )
+ assert(
+ _placement_count(placements, "chunk_0012")
+ == (
+ (1 if has_secondary_elevation else 0)
+ + (0 if has_third_tier_base else 1)
+ )
+ )
+ assert(
+ _placement_count(placements, "chunk_0028")
+ == (9 if has_third_tier_base else 0)
+ )
+ assert(
+ _placement_count(placements, "chunk_0027")
+ == (4 if has_third_tier_base else 0)
+ )
+ assert(
+ _placement_count(placements, "chunk_0026")
+ == (10 if has_third_tier_base else 0)
+ )
+ assert(_placement_count(placements, "chunk_0029") == 1)
+ assert(_placement_count(placements, "chunk_0030") == 1)
+ assert(_placement_count(placements, "chunk_0031") == 1)
+ assert(_placement_count(placements, "chunk_0032") == 1)
+ assert(_placement_count(placements, "chunk_0033") == 1)
+ assert(_placement_count(placements, "chunk_0034") == 1)
+ assert(_placement_count(placements, "chunk_0035") == 1)
+ assert(_placement_count(placements, "chunk_0036") == 1)
+ assert(_placement_count(placements, "chunk_0038") == 2)
+ assert(
+ _placement_count(placements, "chunk_0014")
+ == (2 if has_secondary_elevation else 0)
+ )
+ assert(
+ _placement_count(placements, "chunk_0015")
+ == (1 if has_secondary_elevation else 0)
+ )
+ assert(_placement_count(placements, "chunk_0018") == 0)
+ assert(_placement_count(placements, "chunk_0020") == 4)
+ assert(_placement_count(placements, "chunk_0022") == 4)
+ assert(_placement_count(placements, "chunk_0023") == 4)
+ var lake_edge_count := _placement_count(placements, "chunk_0017")
+ var lake_edge_variant_count := _placement_count(placements, "chunk_0037")
+ var lake_fill_count := _placement_count(placements, "chunk_0019")
+ var lake_narrow_count := _placement_count(placements, "chunk_0021")
+ var lake_size := generator._lake_feature_footprint_size.x
+ assert(lake_size in [7, 8])
+ assert(lake_fill_count == (lake_size - 2) * (lake_size - 2))
+ assert(lake_edge_count + lake_edge_variant_count == (lake_size - 6) * 4)
+ assert(lake_edge_count > 0)
+ assert(lake_edge_variant_count > 0)
+ assert(lake_narrow_count == 8)
+ assert(_placement_count(placements, "chunk_9999") == 0)
+ assert(_placement_count(placements, "chunk_9998") == 0)
+ assert(
+ _placement_count(placements, "chunk_9997")
+ == (1 if has_secondary_elevation else 0)
+ )
+ assert(
+ _placement_count(placements, "chunk_9996")
+ == (1 if has_secondary_elevation else 0)
+ )
+ assert(_placement_count(placements, "chunk_9995") == 0)
+ var river_coordinate_count := generator._river_feature_coordinates.size()
+ var river_edge_count := (
+ _placement_count(placements, "chunk_0024")
+ + _placement_count(placements, "chunk_0025")
+ + _placement_count(placements, "chunk_0038")
+ )
+ assert(river_coordinate_count >= 12)
+ assert(generator._river_feature_placements.size() == river_coordinate_count)
+ assert(river_edge_count == river_coordinate_count - 8)
+ var stone_bridge_specs: Array[Dictionary] = []
+ for spec: Dictionary in generator._river_feature_placements:
+ if spec.get("id", &"") == &"chunk_0038":
+ stone_bridge_specs.append(spec)
+ assert(stone_bridge_specs.size() == 2)
+ var first_bridge_coordinate := stone_bridge_specs[0].get(
+ "coordinate",
+ Vector2i(-1, -1),
+ ) as Vector2i
+ var second_bridge_coordinate := stone_bridge_specs[1].get(
+ "coordinate",
+ Vector2i(-1, -1),
+ ) as Vector2i
+ var bridge_delta := second_bridge_coordinate - first_bridge_coordinate
+ assert(absi(bridge_delta.x) + absi(bridge_delta.y) == 1)
+ assert(posmod(
+ int(stone_bridge_specs[1].get("turns", -1))
+ - int(stone_bridge_specs[0].get("turns", -1)),
+ 4,
+ ) == 2)
+ var river_source_coordinates := generator._river_source_coordinates(
+ generator._river_feature_origin,
+ generator._river_feature_footprint_size,
+ generator._river_feature_flow_direction,
+ )
+ assert(river_source_coordinates.size() == 2)
+ for source_coordinate: Vector2i in river_source_coordinates:
+ var source_index := (
+ source_coordinate.y * generator.grid_size.x + source_coordinate.x
+ )
+ assert(
+ placements[source_index].begins_with("chunk_0029@")
+ or placements[source_index].begins_with("chunk_0030@")
+ )
+ var source_offset := source_coordinate - generator._elevated_feature_center
+ assert(
+ maxi(absi(source_offset.x), absi(source_offset.y))
+ == TerrainChunkGenerator.ELEVATED_FEATURE_RADIUS
+ )
+ var outlet_coordinates: Array[Vector2i] = []
+ for index: int in placements.size():
+ if (
+ placements[index].begins_with("chunk_0031@")
+ or placements[index].begins_with("chunk_0032@")
+ ):
+ var outlet_coordinate := Vector2i(
+ index % generator.grid_size.x,
+ index / generator.grid_size.x,
+ )
+ outlet_coordinates.append(outlet_coordinate)
+ assert(
+ not generator._coordinate_is_inside_grid(
+ outlet_coordinate
+ + generator._river_feature_outlet_direction
+ )
+ )
+ assert(outlet_coordinates.size() == 2)
+ var outlet_delta := outlet_coordinates[1] - outlet_coordinates[0]
+ assert(absi(outlet_delta.x) + absi(outlet_delta.y) == 1)
+ for beach_coordinate: Vector2i in [
+ outlet_coordinates[0] - outlet_delta,
+ outlet_coordinates[1] + outlet_delta,
+ ]:
+ assert(generator._coordinate_is_inside_grid(beach_coordinate))
+ assert(generator._coordinate_is_on_boundary(beach_coordinate))
+ var beach_index := (
+ beach_coordinate.y * generator.grid_size.x + beach_coordinate.x
+ )
+ assert(placements[beach_index].begins_with("chunk_0002@"))
+ for index: int in placements.size():
+ var coordinate := Vector2i(
+ index % generator.grid_size.x,
+ index / generator.grid_size.x,
+ )
+ if placements[index].begins_with("chunk_0001@"):
+ assert(generator._distance_from_map_boundary(coordinate) <= 1)
+ elif placements[index].begins_with("chunk_0013@"):
+ assert(generator._distance_from_map_boundary(coordinate) <= 2)
+ assert(_placement_count(placements, "chunk_0013") >= 1)
+ assert(
+ generator.get_generated_chunks_root().get_child_count()
+ == expected_chunk_count
+ )
+ var generated_root := generator.get_generated_chunks_root()
+ var terrain_collision_batches := generated_root.find_children(
+ "TerrainCollisionBatch_*",
+ "StaticBody3D",
+ true,
+ false,
+ )
+ assert(not terrain_collision_batches.is_empty())
+ assert(
+ terrain_collision_batches.size()
+ <= ceili(float(generator.grid_size.x) / generator.collision_batch_size)
+ * ceili(float(generator.grid_size.y) / generator.collision_batch_size)
+ )
+ assert(
+ generated_root.find_children(
+ "TerrainCollision",
+ "StaticBody3D",
+ true,
+ false,
+ ).is_empty()
+ )
+ assert(
+ generator.get_primary_terrain_meshes().size()
+ == (
+ expected_chunk_count
+ + generator.stacked_elevated_placement_keys().size()
+ )
+ )
+ _validate_elevated_cliff_feature(region, generator)
+
+ var shop := region.get_node("Interactables/FishingShopWorld") as Node3D
+ var storage := region.get_node("Interactables/PlayerStorageBox") as Node3D
+ assert(shop != null and shop.has_node("Shopkeeper"))
+ assert(storage != null and storage.has_node("InteractionArea"))
+ var spawn_chunk_center := generator.chunk_position(center)
+ var shop_offset := shop.position - spawn_chunk_center
+ var storage_offset := storage.position - spawn_chunk_center
+ assert(absf(shop_offset.x) < 5.0 and absf(shop_offset.z) < 5.0)
+ assert(absf(storage_offset.x) < 5.0 and absf(storage_offset.z) < 5.0)
+ assert(region.get_fishing_shop() != null)
+ assert(region.get_player_storage() != null)
+ assert(region.get_player_spawn_transform().origin.y > 0.0)
+ _validate_prop_catalog(region)
+ _validate_biome_catalog(region, generator)
+
+ var ocean := region.get_node("WaterBodies/OceanWater") as WaterBodyAuthoring
+ var fresh_root := region.get_node("WaterBodies/FreshWaterBodies") as Node3D
+ assert(ocean != null and fresh_root != null)
+ assert(ocean.water_type == WaterType.Type.SALT_WATER)
+ assert(is_equal_approx(ocean.position.y, GeneratedWorldRegion.WATER_HEIGHT))
+ var ocean_recovery := ocean.get_node("RecoveryRegion") as PlayerWaterTrigger
+ assert(
+ ocean_recovery.entry_height_reference
+ == PlayerWaterTrigger.EntryHeightReference.BODY_CENTER
+ )
+ assert(is_equal_approx(ocean_recovery.entry_depth_threshold, 0.35))
+ var fresh_placement_count := 0
+ var river_placement_count := 0
+ var polygon_sizes_by_coordinate: Dictionary[Vector2i, int] = {}
+ for record: Dictionary in generator.placement_records():
+ var tags: PackedStringArray = record.get("tags", PackedStringArray())
+ if "coast" in tags:
+ _validate_ocean_facing_record(record, generator.grid_size)
+ if "fresh_water" in tags:
+ fresh_placement_count += 1
+ if "river" in tags:
+ river_placement_count += 1
+ assert(not "pond" in tags)
+ assert(not "lake" in tags)
+ var surface_polygon: PackedVector2Array = record.get(
+ "water_surface_polygon",
+ PackedVector2Array(),
+ )
+ if surface_polygon.size() >= 3:
+ polygon_sizes_by_coordinate[
+ record.get("coordinate", Vector2i.ZERO)
+ ] = surface_polygon.size()
+ _validate_complete_coastline(generator)
+ assert(fresh_root.get_child_count() == fresh_placement_count)
+ assert(river_placement_count == river_coordinate_count)
+ assert(
+ polygon_sizes_by_coordinate.size()
+ == 20 + river_placement_count
+ )
+ var river_body_count := 0
+ for child: Node in fresh_root.get_children():
+ var fresh := child as WaterBodyAuthoring
+ assert(fresh != null)
+ assert(fresh.water_type == WaterType.Type.FRESH_WATER)
+ assert(is_equal_approx(fresh.position.y, GeneratedWorldRegion.WATER_HEIGHT))
+ assert(fresh.visible)
+ assert(fresh.surface_size.x <= 10.0 and fresh.surface_size.y <= 10.0)
+ assert(not fresh.visual_surface_enabled)
+ assert(not (fresh.get_node("VisualWater") as MeshInstance3D).visible)
+ var generated_bed := fresh.get_node_or_null(
+ "GeneratedBed",
+ ) as MeshInstance3D
+ assert(generated_bed != null and generated_bed.mesh != null)
+ assert(
+ is_equal_approx(
+ generated_bed.global_position.y,
+ GeneratedWorldRegion.GENERATED_FRESH_WATER_BED_HEIGHT,
+ )
+ )
+ assert(
+ generated_bed.material_override != null
+ and generated_bed.material_override.resource_name == "dirt"
+ )
+ var fresh_recovery := (
+ fresh.get_node("RecoveryRegion") as PlayerWaterTrigger
+ )
+ assert(
+ fresh_recovery.entry_height_reference
+ == PlayerWaterTrigger.EntryHeightReference.PLAYER_ORIGIN
+ )
+ assert(is_equal_approx(fresh_recovery.entry_depth_threshold, 0.1))
+ assert(is_equal_approx(
+ fresh_recovery.entry_confirmation_seconds,
+ 0.2,
+ ))
+ var coordinate_tokens := child.name.trim_prefix("FreshWater_").split("_")
+ assert(coordinate_tokens.size() == 2)
+ var coordinate := Vector2i(
+ int(coordinate_tokens[0]),
+ int(coordinate_tokens[1]),
+ )
+ if polygon_sizes_by_coordinate.has(coordinate):
+ assert(
+ fresh.surface_polygon.size()
+ == polygon_sizes_by_coordinate[coordinate]
+ )
+ assert(
+ fresh.get_node("FishingRegion").get_child_count() > 1
+ )
+ else:
+ assert(fresh.surface_polygon.is_empty())
+ if &"river" in fresh.location_tags:
+ river_body_count += 1
+ assert(fresh.fish_pool == GeneratedRiverPool)
+ elif &"lake" in fresh.location_tags:
+ assert(fresh.fish_pool == GeneratedLakePool)
+ else:
+ assert(&"pond" in fresh.location_tags)
+ assert(fresh.fish_pool == GeneratedPondPool)
+ assert(river_body_count == river_placement_count)
+
+ _validate_authored_chunk_surfaces(region, generator)
+ _validate_projected_terrain_materials(
+ generator.get_generated_chunks_root()
+ )
+
+ var decorations := region.get_node("Decorations") as Node3D
+ var grass_prop_surface_triangles := region.get_spawn_surface_triangles(
+ [&"grass_lite"],
+ -INF,
+ 0.35,
+ )
+ var sand_prop_surface_triangles := region.get_spawn_surface_triangles(
+ [&"sand"],
+ -INF,
+ 0.35,
+ )
+ _validate_crab_spawn_surfaces(region)
+ _validate_water_recovery_positions(
+ region,
+ grass_prop_surface_triangles,
+ sand_prop_surface_triangles,
+ )
+ var anchors := region.get_node(
+ "GatherableAnchors/ReachableTreeTrunks"
+ ) as GatherableAnchorSet3D
+ assert(decorations != null and decorations.get_child_count() > 0)
+ assert(anchors != null)
+ assert(anchors.get_spawn_positions().size() > 0)
+ _validate_tree_gatherable_anchors(region, decorations, anchors)
+ var palm_clusters: Dictionary[int, Array] = {}
+ var tree_scales: Array[float] = []
+ var tree_yaws: Dictionary[float, bool] = {}
+ var ocean_edges_by_coordinate: Dictionary[Vector2i, int] = {}
+ for record: Dictionary in generator.placement_records():
+ var record_edges := int(record.get("ocean_facing_edges", 0))
+ if record_edges != 0:
+ ocean_edges_by_coordinate[
+ record.get("coordinate", Vector2i.ZERO)
+ ] = record_edges
+ for child: Node in decorations.get_children():
+ var prop_id := StringName(child.get_meta(&"terrain_prop_id", &""))
+ assert(PROCEDURAL_PROP_IDS.has(prop_id))
+ var definition := region.get_prop_catalog().definition_for_id(prop_id)
+ assert(definition != null)
+ if definition.procedural_group in [&"grass_tree", &"sand_tree"]:
+ assert(
+ _has_precipitation_occlusion_layer(child),
+ "Precipitation occlusion layer missing from %s (%s)."
+ % [child.name, prop_id],
+ )
+ var biome_id := StringName(
+ child.get_meta(&"terrain_biome_id", &"")
+ )
+ var biome := region.get_biome_catalog().definition_for_id(biome_id)
+ assert(biome != null)
+ var biome_rule := biome.prop_rule_for_group(
+ definition.procedural_group
+ )
+ assert(biome_rule != null and biome_rule.allows_prop(definition))
+ var coordinate: Vector2i = child.get_meta(
+ &"terrain_chunk_coordinate",
+ Vector2i.ZERO,
+ )
+ var spawn_coordinate := Vector2i(
+ generator.grid_size.x / 2,
+ generator.grid_size.y / 2,
+ )
+ var spawn_distance := (
+ absi(coordinate.x - spawn_coordinate.x)
+ + absi(coordinate.y - spawn_coordinate.y)
+ )
+ assert(spawn_distance >= definition.minimum_spawn_chunk_distance)
+ _validate_decoration_transform(
+ region,
+ child as Node3D,
+ definition,
+ grass_prop_surface_triangles,
+ sand_prop_surface_triangles,
+ )
+ if prop_id == &"prop_palm":
+ var cluster_id := int(
+ child.get_meta(&"terrain_prop_cluster_id", -1)
+ )
+ var members: Array = palm_clusters.get(cluster_id, [])
+ members.append(child)
+ palm_clusters[cluster_id] = members
+ var ocean_direction := _ocean_direction(
+ ocean_edges_by_coordinate.get(coordinate, 0)
+ )
+ if ocean_direction.is_zero_approx():
+ ocean_direction = _nearest_ocean_direction(
+ region,
+ generator,
+ (child as Node3D).position,
+ )
+ var local_overhang := Vector3(
+ definition.local_overhang_direction.x,
+ 0.0,
+ definition.local_overhang_direction.y,
+ ).normalized()
+ var actual_overhang := local_overhang.rotated(
+ Vector3.UP,
+ (child as Node3D).rotation.y,
+ )
+ assert(
+ actual_overhang.dot(ocean_direction)
+ >= cos(deg_to_rad(definition.ocean_facing_spread_degrees))
+ - 0.001
+ )
+ elif prop_id in [
+ &"prop_tree_1",
+ &"prop_tree_2",
+ &"prop_tree_3",
+ &"prop_tree_large",
+ &"prop_pine",
+ &"prop_pine_large",
+ ]:
+ tree_scales.append(
+ float(child.get_meta(&"terrain_prop_visual_scale", 1.0))
+ )
+ tree_yaws[snappedf((child as Node3D).rotation.y, 0.01)] = true
+ if prop_id in [
+ &"prop_tree_1",
+ &"prop_tree_2",
+ &"prop_tree_3",
+ &"prop_tree_large",
+ ]:
+ assert(
+ _has_material_variant_override(
+ child.get_node_or_null("Visual"),
+ definition.material_variants,
+ )
+ )
+ assert(
+ _has_material_variant_override(
+ child.get_node_or_null("Visual"),
+ definition.secondary_material_variants,
+ )
+ )
+ assert(not palm_clusters.is_empty())
+ for members: Array in palm_clusters.values():
+ assert(members.size() >= 1 and members.size() <= 3)
+ for first_index: int in members.size():
+ for second_index: int in range(first_index + 1, members.size()):
+ var first := members[first_index] as Node3D
+ var second := members[second_index] as Node3D
+ var separation := Vector2(
+ first.position.x - second.position.x,
+ first.position.z - second.position.z,
+ ).length()
+ assert(separation > 0.5 and separation < 6.1)
+ assert(tree_scales.max() - tree_scales.min() > 0.25)
+ assert(tree_yaws.size() >= 8)
+ assert(_decoration_group_count(region, &"grass_tree") > 0)
+ assert(_decoration_group_count(region, &"sand_tree") > 0)
+ assert(
+ _decoration_biome_group_count(
+ region,
+ &"biome_forest",
+ &"grass_tree",
+ ) >= 4
+ )
+ assert(
+ _decoration_biome_group_count(
+ region,
+ &"biome_pine_forest",
+ &"grass_tree",
+ ) >= 4
+ )
+
+
+func _validate_elevated_cliff_feature(
+ region: GeneratedWorldRegion,
+ generator: TerrainChunkGenerator,
+) -> void:
+ var elevated_coordinates: Dictionary[Vector2i, StringName] = {}
+ var top_coordinates: Array[Vector2i] = []
+ var ramp_records: Array[Dictionary] = []
+ var coastal_ids: Array[StringName] = [
+ &"chunk_0014",
+ &"chunk_0015",
+ &"chunk_9999",
+ &"chunk_9998",
+ &"chunk_9997",
+ &"chunk_9996",
+ ]
+ var has_coastal_feature := false
+ for record: Dictionary in generator.placement_records():
+ var stable_id: StringName = record.get("stable_id", &"")
+ if stable_id not in [
+ &"chunk_0009",
+ &"chunk_0010",
+ &"chunk_0011",
+ &"chunk_0012",
+ &"chunk_0026",
+ &"chunk_0027",
+ &"chunk_0028",
+ &"chunk_0029",
+ &"chunk_0030",
+ &"chunk_0014",
+ &"chunk_0015",
+ &"chunk_9999",
+ &"chunk_9998",
+ &"chunk_9997",
+ &"chunk_9996",
+ ]:
+ continue
+ var coordinate: Vector2i = record.get("coordinate", Vector2i.ZERO)
+ if stable_id in coastal_ids:
+ has_coastal_feature = true
+ assert(
+ coordinate.x in [0, generator.grid_size.x - 1]
+ or coordinate.y in [0, generator.grid_size.y - 1]
+ )
+ else:
+ assert(coordinate.x > 0 and coordinate.x < generator.grid_size.x - 1)
+ assert(coordinate.y > 0 and coordinate.y < generator.grid_size.y - 1)
+ elevated_coordinates[coordinate] = stable_id
+ if stable_id in [&"chunk_0009", &"chunk_0028"]:
+ top_coordinates.append(coordinate)
+ elif stable_id == &"chunk_0012":
+ ramp_records.append(record)
+ assert(elevated_coordinates.size() == (34 if has_coastal_feature else 25))
+ var feature_center := generator._elevated_feature_center
+ assert(feature_center != Vector2i(-1, -1))
+ var center_index := feature_center.y * generator.grid_size.x + feature_center.x
+ assert(center_index >= 0 and center_index < generator.placement_keys().size())
+ var primary_top_id := StringName(
+ generator.placement_keys()[center_index].get_slice("@", 0)
+ )
+ assert(primary_top_id in [&"chunk_0009", &"chunk_0028"])
+ var primary_is_third_tier := primary_top_id == &"chunk_0028"
+ assert(
+ top_coordinates.size()
+ == (9 + (1 if has_coastal_feature else 0))
+ )
+ assert(
+ ramp_records.size()
+ == (
+ (0 if primary_is_third_tier else 1)
+ + (1 if has_coastal_feature else 0)
+ )
+ )
+ var detected_feature_center := Vector2i(-1, -1)
+ for candidate: Vector2i in top_coordinates:
+ var neighboring_top_count := 0
+ for row_offset: int in range(-1, 2):
+ for column_offset: int in range(-1, 2):
+ neighboring_top_count += int(
+ elevated_coordinates.get(
+ candidate + Vector2i(column_offset, row_offset),
+ &"",
+ ) == primary_top_id
+ )
+ if neighboring_top_count == 9:
+ detected_feature_center = candidate
+ break
+ assert(detected_feature_center == feature_center)
+ for row_offset: int in range(-2, 3):
+ for column_offset: int in range(-2, 3):
+ assert(
+ elevated_coordinates.has(
+ feature_center + Vector2i(column_offset, row_offset)
+ )
+ )
+ if has_coastal_feature:
+ var secondary_top := generator._secondary_elevated_feature_center
+ assert(secondary_top != Vector2i(-1, -1))
+ for row_offset: int in range(-1, 2):
+ for column_offset: int in range(-1, 2):
+ assert(
+ elevated_coordinates.has(
+ secondary_top + Vector2i(column_offset, row_offset)
+ )
+ )
+ var found_primary_ramp := false
+ for ramp_record: Dictionary in ramp_records:
+ var ramp_coordinate: Vector2i = ramp_record.get(
+ "coordinate", Vector2i(-1, -1)
+ )
+ var ramp_turns := int(ramp_record.get("rotation_quarters", 0))
+ var high_edge := TerrainChunkTopology.rotated_edge(
+ TerrainChunkTopology.Edge.WEST,
+ ramp_turns,
+ )
+ var high_offset := TerrainChunkTopology.grid_offset(high_edge)
+ assert(
+ elevated_coordinates.get(ramp_coordinate + high_offset, &"")
+ == &"chunk_0009"
+ )
+ found_primary_ramp = (
+ found_primary_ramp
+ or elevated_coordinates.get(
+ ramp_coordinate + high_offset * 2,
+ &"",
+ ) == &"chunk_0009"
+ )
+ var low_coordinate := ramp_coordinate - high_offset
+ var low_index := (
+ low_coordinate.y * generator.grid_size.x + low_coordinate.x
+ )
+ assert(
+ generator.placement_keys()[low_index].begins_with("chunk_0000@")
+ or generator.placement_keys()[low_index].begins_with("chunk_spawn@")
+ )
+ assert(found_primary_ramp == not primary_is_third_tier)
+ var primary_surface_height := (
+ generator.elevated_cliff_level_height
+ * (2.0 if primary_is_third_tier else 1.0)
+ )
+ var primary_surface_position := generator.to_global(
+ generator.chunk_position(feature_center)
+ + Vector3.UP * primary_surface_height
+ )
+ assert(
+ region.is_home_exterior_position_accessible(primary_surface_position)
+ == not primary_is_third_tier
+ )
+ assert(region.is_home_exterior_position_accessible(generator.to_global(
+ generator.chunk_position(feature_center)
+ )))
+ # A higher surface sharing a ramp-accessible base cell is still invalid
+ # unless that elevation has its own authored ramp connection.
+ assert(not region.is_home_exterior_position_accessible(generator.to_global(
+ generator.chunk_position(feature_center)
+ + Vector3.UP * (generator.elevated_cliff_level_height * 2.0)
+ )))
+
+ var generated := generator.get_generated_chunks_root()
+ assert(generated != null)
+ var layered_count := 0
+ for chunk_root: Node in generated.get_children():
+ var stable_id := StringName(
+ chunk_root.get_meta(&"terrain_chunk_id", &"")
+ )
+ if stable_id not in [
+ &"chunk_0009",
+ &"chunk_0010",
+ &"chunk_0011",
+ &"chunk_0012",
+ &"chunk_0026",
+ &"chunk_0027",
+ &"chunk_0028",
+ &"chunk_0029",
+ &"chunk_0030",
+ &"chunk_0014",
+ &"chunk_0015",
+ &"chunk_9999",
+ &"chunk_9998",
+ &"chunk_9997",
+ &"chunk_9996",
+ ]:
+ continue
+ if stable_id in [&"chunk_0014", &"chunk_0015"]:
+ assert(chunk_root.get_node_or_null("TerrainBaseLayer") == null)
+ continue
+ if stable_id in [&"chunk_0029", &"chunk_0030"]:
+ assert(chunk_root.get_node_or_null("TerrainBaseLayer") == null)
+ var source_mesh := TerrainChunkAnalyzer.find_primary_mesh(
+ chunk_root,
+ stable_id,
+ )
+ assert(source_mesh != null and source_mesh.mesh != null)
+ continue
+ layered_count += 1
+ var base_layer := chunk_root.get_node_or_null("TerrainBaseLayer")
+ var overlay := chunk_root.get_node_or_null("TerrainOverlay")
+ assert(base_layer != null and overlay != null)
+ var transition_base_mesh := &"chunk_0000"
+ if stable_id in [&"chunk_9999", &"chunk_9998"]:
+ transition_base_mesh = &"chunk_0007"
+ elif stable_id in [&"chunk_9997", &"chunk_9996"]:
+ transition_base_mesh = &"chunk_0008"
+ var base_mesh := TerrainChunkAnalyzer.find_primary_mesh(
+ base_layer,
+ transition_base_mesh,
+ )
+ var overlay_mesh := TerrainChunkAnalyzer.find_primary_mesh(
+ overlay,
+ (
+ &"chunk_0010"
+ if stable_id in [
+ &"chunk_9999",
+ &"chunk_9998",
+ &"chunk_9997",
+ &"chunk_9996",
+ ]
+ else stable_id
+ ),
+ )
+ assert(base_mesh != null and base_mesh.mesh != null)
+ assert(overlay_mesh != null and overlay_mesh.mesh != null)
+ assert(layered_count == (29 if has_coastal_feature else 23))
+ var stacked_keys := generator.stacked_elevated_placement_keys()
+ assert(stacked_keys.size() in [0, 4])
+ if stacked_keys.is_empty():
+ return
+ for key: String in stacked_keys:
+ assert(
+ key.begins_with("chunk_0010@")
+ or key.begins_with("chunk_0027@")
+ )
+ var stacked_count := 0
+ for chunk_root: Node in generated.get_children():
+ for child: Node in chunk_root.get_children():
+ if not bool(child.get_meta(&"terrain_stacked_elevation", false)):
+ continue
+ stacked_count += 1
+ var stacked_id := StringName(
+ child.get_meta(&"terrain_chunk_id", &"")
+ )
+ assert(stacked_id in [&"chunk_0010", &"chunk_0027"])
+ var expected_height := (
+ generator.elevated_cliff_level_height * 2.0
+ if primary_is_third_tier
+ else generator.elevated_cliff_level_height
+ )
+ assert(
+ is_equal_approx(
+ (child as Node3D).position.y,
+ expected_height,
+ )
+ )
+ var stacked_mesh := TerrainChunkAnalyzer.find_primary_mesh(
+ child,
+ stacked_id,
+ )
+ assert(stacked_mesh != null and stacked_mesh.mesh != null)
+ assert(stacked_count == 4)
+
+
+func _validate_ocean_facing_record(
+ record: Dictionary,
+ grid_size: Vector2i,
+) -> void:
+ var coordinate: Vector2i = record.get("coordinate", Vector2i.ZERO)
+ var ocean_edges := int(record.get("ocean_facing_edges", 0))
+ assert(ocean_edges != 0)
+ for edge_value: int in TerrainChunkTopology.Edge.values():
+ if (ocean_edges & (1 << edge_value)) == 0:
+ continue
+ var ocean_coordinate := (
+ coordinate
+ + TerrainChunkTopology.grid_offset(
+ edge_value as TerrainChunkTopology.Edge
+ )
+ )
+ assert(
+ ocean_coordinate.x < 0
+ or ocean_coordinate.y < 0
+ or ocean_coordinate.x >= grid_size.x
+ or ocean_coordinate.y >= grid_size.y
+ )
+
+
+func _validate_complete_coastline(generator: TerrainChunkGenerator) -> void:
+ for record: Dictionary in generator.placement_records():
+ var coordinate: Vector2i = record.get("coordinate", Vector2i.ZERO)
+ var ocean_edges := int(record.get("ocean_facing_edges", 0))
+ for edge_value: int in TerrainChunkTopology.Edge.values():
+ var neighbor := (
+ coordinate
+ + TerrainChunkTopology.grid_offset(
+ edge_value as TerrainChunkTopology.Edge
+ )
+ )
+ var outside := (
+ neighbor.x < 0
+ or neighbor.y < 0
+ or neighbor.x >= generator.grid_size.x
+ or neighbor.y >= generator.grid_size.y
+ )
+ assert(
+ ((ocean_edges & (1 << edge_value)) != 0) == outside
+ )
+
+
+func _ocean_direction(ocean_edges: int) -> Vector3:
+ var result := Vector3.ZERO
+ for edge_value: int in TerrainChunkTopology.Edge.values():
+ if (ocean_edges & (1 << edge_value)) != 0:
+ result += TerrainChunkTopology.edge_normal(
+ edge_value as TerrainChunkTopology.Edge
+ )
+ return result.normalized()
+
+
+func _nearest_ocean_direction(
+ region: GeneratedWorldRegion,
+ generator: TerrainChunkGenerator,
+ position: Vector3,
+) -> Vector3:
+ var half_extents := region.get_playable_half_extents()
+ var distance_x := half_extents.x - absf(position.x)
+ var distance_z := half_extents.y - absf(position.z)
+ var direction_x := Vector3.RIGHT if position.x >= 0.0 else Vector3.LEFT
+ var direction_z := Vector3.BACK if position.z >= 0.0 else Vector3.FORWARD
+ if absf(distance_x - distance_z) <= generator.catalog.chunk_size * 0.35:
+ return (direction_x + direction_z).normalized()
+ return direction_x if distance_x < distance_z else direction_z
+
+
+func _validate_prop_catalog(region: GeneratedWorldRegion) -> void:
+ var catalog := region.get_prop_catalog()
+ assert(catalog != null)
+ assert(catalog.validation_errors().is_empty())
+ assert(catalog.definitions.size() == EXPECTED_PROP_IDS.size())
+ for stable_id: StringName in EXPECTED_PROP_IDS:
+ var definition := catalog.definition_for_id(stable_id)
+ assert(definition != null and definition.packed_scene != null)
+ var instance := definition.packed_scene.instantiate() as Node3D
+ assert(instance != null)
+ assert(instance.position.is_zero_approx())
+ assert(_find_mesh_instance(instance) != null)
+ instance.free()
+ if definition.has_gatherable_surface():
+ assert(
+ definition.gatherable_surface_clearance >= 0.12
+ )
+ var mushroom := catalog.definition_for_id(&"prop_mushroom")
+ assert(mushroom != null and mushroom.has_collision())
+ assert(mushroom.visual_offset.y < 0.0)
+ for tree_id: StringName in [
+ &"prop_tree_1",
+ &"prop_tree_2",
+ &"prop_tree_3",
+ &"prop_tree_large",
+ ]:
+ var tree := catalog.definition_for_id(tree_id)
+ assert(tree != null)
+ assert(tree.minimum_visual_scale < tree.maximum_visual_scale)
+ assert(tree.material_variants.size() >= 3)
+ assert(not tree.variant_material_slot_names.is_empty())
+ assert(tree.secondary_material_variants.size() >= 3)
+ assert(not tree.secondary_variant_material_slot_names.is_empty())
+ var pine := catalog.definition_for_id(&"prop_pine")
+ assert(pine != null)
+ assert(pine.minimum_visual_scale < pine.maximum_visual_scale)
+ assert(pine.material_variants.is_empty())
+ assert(not pine.has_gatherable_surface())
+ var large_pine := catalog.definition_for_id(&"prop_pine_large")
+ assert(large_pine != null)
+ assert(large_pine.minimum_visual_scale < large_pine.maximum_visual_scale)
+ assert(large_pine.material_variants.is_empty())
+ assert(not large_pine.has_gatherable_surface())
+ var palm := catalog.definition_for_id(&"prop_palm")
+ assert(palm != null)
+ assert(is_equal_approx(palm.minimum_visual_scale, 0.5))
+ assert(is_equal_approx(palm.maximum_visual_scale, 1.0))
+ assert(palm.minimum_cluster_size == 2)
+ assert(palm.maximum_cluster_size == 3)
+ assert(palm.minimum_cluster_radius > palm.clearance_radius)
+ assert(palm.maximum_cluster_radius > palm.minimum_cluster_radius)
+ assert(palm.prefer_ocean_facing)
+ assert(not palm.local_overhang_direction.is_zero_approx())
+ var aligned_tree_collisions: Dictionary[StringName, Vector2] = {
+ &"prop_tree_1": Vector2(0.08, 0.01),
+ &"prop_tree_2": Vector2(0.1, 0.075),
+ &"prop_tree_3": Vector2(-0.1, 0.18),
+ &"prop_tree_large": Vector2(0.084, 0.504),
+ &"prop_pine": Vector2(-0.044, 0.083),
+ &"prop_pine_large": Vector2(-0.04, 0.075),
+ &"prop_palm": Vector2(0.15, -0.1),
+ }
+ for tree_id: StringName in aligned_tree_collisions:
+ var aligned_tree := catalog.definition_for_id(tree_id)
+ assert(aligned_tree != null and aligned_tree.has_cylinder_collision())
+ var collision_center := Vector2(
+ aligned_tree.collision_offset.x,
+ aligned_tree.collision_offset.z,
+ )
+ assert(collision_center.is_equal_approx(
+ aligned_tree_collisions[tree_id]
+ ))
+ var forest := region.get_biome_catalog().definition_for_id(&"biome_forest")
+ var forest_rule := forest.prop_rule_for_group(&"grass_tree")
+ var large_tree := catalog.definition_for_id(&"prop_tree_large")
+ var small_tree_weight := (
+ catalog.definition_for_id(&"prop_tree_1").selection_weight
+ + catalog.definition_for_id(&"prop_tree_2").selection_weight
+ + catalog.definition_for_id(&"prop_tree_3").selection_weight
+ )
+ assert(forest_rule.allows_prop(large_tree))
+ assert(large_tree.selection_weight <= small_tree_weight)
+ var pine_forest := region.get_biome_catalog().definition_for_id(
+ &"biome_pine_forest"
+ )
+ var pine_rule := pine_forest.prop_rule_for_group(&"grass_tree")
+ assert(pine_rule.allows_prop(large_pine))
+ assert(
+ is_equal_approx(large_pine.selection_weight, pine.selection_weight)
+ )
+ assert(forest_rule.minimum_placements == 4)
+ assert(forest_rule.placement_attempts_per_chunk == 4)
+ assert(pine_rule.minimum_placements == 4)
+ assert(pine_rule.placement_attempts_per_chunk == 4)
+
+
+func _validate_biome_catalog(
+ region: GeneratedWorldRegion,
+ generator: TerrainChunkGenerator,
+) -> void:
+ var catalog := region.get_biome_catalog()
+ assert(catalog != null)
+ assert(catalog.validation_errors().is_empty())
+ assert(catalog.definitions.size() == EXPECTED_BIOME_IDS.size())
+ var counts: Dictionary[StringName, int] = {}
+ for record: Dictionary in generator.placement_records():
+ var coordinate: Vector2i = record.get("coordinate", Vector2i.ZERO)
+ var biome_id := region.get_biome_at(coordinate)
+ var tags: PackedStringArray = record.get("tags", PackedStringArray())
+ if "mixed_surface" in tags:
+ assert(biome_id == &"")
+ elif "grass" in tags or "sand" in tags:
+ assert(biome_id != &"")
+ var biome := catalog.definition_for_id(biome_id)
+ assert(biome != null and biome.supports_chunk_tags(tags))
+ counts[biome_id] = counts.get(biome_id, 0) + 1
+ else:
+ assert(biome_id == &"")
+ for biome_id: StringName in EXPECTED_BIOME_IDS:
+ assert(counts.get(biome_id, 0) > 0)
+ var center := Vector2i(
+ generator.grid_size.x / 2,
+ generator.grid_size.y / 2,
+ )
+ assert(region.get_biome_at(center) == &"biome_plains")
+ for child: Node in generator.get_generated_chunks_root().get_children():
+ var coordinate: Vector2i = child.get_meta(
+ &"terrain_chunk_coordinate",
+ Vector2i.ZERO,
+ )
+ assert(
+ StringName(child.get_meta(&"terrain_biome_id", &""))
+ == region.get_biome_at(coordinate)
+ )
+
+
+func _validate_decoration_transform(
+ region: GeneratedWorldRegion,
+ prop: Node3D,
+ definition: TerrainPropDefinition,
+ grass_surface_triangles: Array[PackedVector3Array],
+ sand_surface_triangles: Array[PackedVector3Array],
+) -> void:
+ assert(prop != null)
+ assert(prop.scale.is_equal_approx(Vector3.ONE))
+ var visual_root := prop.get_node_or_null("Visual") as Node3D
+ var visual := _find_mesh_instance(visual_root)
+ var collision := prop.get_node_or_null(
+ "TrunkCollision/CollisionShape"
+ ) as CollisionShape3D
+ assert(visual_root != null)
+ assert(visual_root.position.is_equal_approx(definition.visual_offset))
+ var visual_scale := float(
+ prop.get_meta(&"terrain_prop_visual_scale", 1.0)
+ )
+ assert(visual_scale >= definition.minimum_visual_scale - 0.001)
+ assert(visual_scale <= definition.maximum_visual_scale + 0.001)
+ assert(
+ visual_root.scale.is_equal_approx(Vector3.ONE * visual_scale)
+ )
+ assert(visual != null and visual.mesh != null)
+ if definition.has_collision():
+ assert(collision != null)
+ assert(
+ collision.shape is CylinderShape3D
+ or collision.shape is BoxShape3D
+ )
+ assert(collision.global_basis.get_scale().is_equal_approx(Vector3.ONE))
+ if collision.shape is CylinderShape3D:
+ var cylinder := collision.shape as CylinderShape3D
+ assert(is_equal_approx(
+ cylinder.radius,
+ definition.collision_radius * visual_scale,
+ ))
+ assert(is_equal_approx(
+ cylinder.height,
+ definition.collision_height * visual_scale,
+ ))
+ assert(collision.position.is_equal_approx(
+ definition.collision_offset * visual_scale
+ + Vector3.UP * cylinder.height * 0.5
+ ))
+ else:
+ assert(collision == null)
+ var query := PhysicsRayQueryParameters3D.create(
+ # Stay below neighboring cliff overhangs while still beginning above the
+ # authored walkable surface. Regional collision batches intentionally
+ # keep every original shape on one body, so excluding an overhang body
+ # would also exclude the ground beneath the prop.
+ prop.global_position + Vector3.UP * 0.1,
+ prop.global_position + Vector3.DOWN * 8.0,
+ 1,
+ )
+ var excluded_colliders: Array[RID] = []
+ var decorations := region.get_node("Decorations") as Node3D
+ for decoration: Node in decorations.get_children():
+ var prop_body := decoration.get_node_or_null(
+ "TrunkCollision",
+ ) as CollisionObject3D
+ if prop_body != null:
+ excluded_colliders.append(prop_body.get_rid())
+ query.exclude = excluded_colliders
+ var hit := region.get_world_3d().direct_space_state.intersect_ray(query)
+ # Stacked cliff walls can overhang a neighboring cell. Match the placement
+ # sampler by ignoring steep faces until the ray reaches walkable terrain.
+ while (
+ not hit.is_empty()
+ and absf((hit.get("normal", Vector3.ZERO) as Vector3).dot(Vector3.UP))
+ < 0.35
+ ):
+ var steep_collider := hit.get("collider") as CollisionObject3D
+ assert(steep_collider != null)
+ excluded_colliders.append(steep_collider.get_rid())
+ query.exclude = excluded_colliders
+ hit = region.get_world_3d().direct_space_state.intersect_ray(query)
+ assert(
+ not hit.is_empty(),
+ "No terrain collision below %s (%s) at %s."
+ % [prop.name, definition.stable_id, prop.global_position],
+ )
+ var ground_position: Vector3 = hit["position"]
+ assert(
+ absf(ground_position.y - prop.global_position.y) <= 0.01,
+ "Terrain height %s does not match %s (%s) at %s."
+ % [
+ ground_position.y,
+ prop.name,
+ definition.stable_id,
+ "%s in chunk %s; hit %s" % [
+ prop.global_position,
+ prop.get_meta(&"terrain_chunk_coordinate", Vector2i(-1, -1)),
+ (hit.get("collider") as Node).get_path(),
+ ],
+ ],
+ )
+ var required_surface_triangles: Array[PackedVector3Array] = (
+ sand_surface_triangles
+ if definition.procedural_group == &"sand_tree"
+ else grass_surface_triangles
+ )
+ var required_surface_height := _triangle_surface_height_at(
+ prop.global_position,
+ required_surface_triangles,
+ )
+ assert(
+ required_surface_height > -INF
+ and absf(required_surface_height - prop.global_position.y) <= 0.01,
+ "%s (%s) is not rooted on its required terrain material at %s."
+ % [prop.name, definition.stable_id, prop.global_position],
+ )
+
+
+func _validate_crab_spawn_surfaces(region: GeneratedWorldRegion) -> void:
+ var brown := Gatherables.get_entry(&"crab_brown")
+ var blue := Gatherables.get_entry(&"crab_blue")
+ assert(brown != null and blue != null)
+ assert(is_equal_approx(
+ region.get_saltwater_surface_height(),
+ GeneratedWorldRegion.WATER_HEIGHT,
+ ))
+ var brown_minimum := maxf(
+ brown.minimum_surface_y,
+ GeneratedWorldRegion.WATER_HEIGHT + brown.waterline_margin,
+ )
+ var blue_maximum := minf(
+ blue.maximum_surface_y,
+ GeneratedWorldRegion.WATER_HEIGHT - blue.waterline_margin,
+ )
+ assert(brown_minimum > blue_maximum)
+ var brown_triangles := region.get_spawn_surface_triangles(
+ brown.surface_materials,
+ brown_minimum,
+ 0.6,
+ brown.maximum_surface_y,
+ )
+ var blue_triangles := region.get_spawn_surface_triangles(
+ blue.surface_materials,
+ blue.minimum_surface_y,
+ 0.6,
+ blue_maximum,
+ )
+ assert(not brown_triangles.is_empty())
+ assert(not blue_triangles.is_empty())
+ assert(
+ brown.target_population_for_surface_area(
+ _surface_triangle_area(brown_triangles)
+ ) > brown.population
+ )
+ assert(
+ blue.target_population_for_surface_area(
+ _surface_triangle_area(blue_triangles)
+ ) > blue.population
+ )
+
+
+func _surface_triangle_area(triangles: Array[PackedVector3Array]) -> float:
+ var area := 0.0
+ for triangle: PackedVector3Array in triangles:
+ area += (triangle[1] - triangle[0]).cross(
+ triangle[2] - triangle[0]
+ ).length() * 0.5
+ return area
+
+
+func _triangle_surface_height_at(
+ position: Vector3,
+ triangles: Array[PackedVector3Array],
+) -> float:
+ var highest := -INF
+ var segment_start := position + Vector3.UP * 100.0
+ var segment_end := position + Vector3.DOWN * 100.0
+ for triangle: PackedVector3Array in triangles:
+ if triangle.size() != 3:
+ continue
+ var hit: Variant = Geometry3D.segment_intersects_triangle(
+ segment_start,
+ segment_end,
+ triangle[0],
+ triangle[1],
+ triangle[2],
+ )
+ if hit is Vector3:
+ highest = maxf(highest, (hit as Vector3).y)
+ return highest
+
+
+func _validate_water_recovery_positions(
+ region: GeneratedWorldRegion,
+ grass_triangles: Array[PackedVector3Array],
+ sand_triangles: Array[PackedVector3Array],
+) -> void:
+ var fresh_water_root := region.get_node(
+ "WaterBodies/FreshWaterBodies"
+ ) as Node3D
+ assert(fresh_water_root != null and fresh_water_root.get_child_count() > 0)
+ var fresh_water := fresh_water_root.get_child(0) as WaterBodyAuthoring
+ assert(fresh_water != null)
+ var entry_position := fresh_water.global_position
+ var fallback_position := region.get_player_spawn_transform().origin
+ var recovery_position := region.get_water_recovery_position(
+ entry_position,
+ fallback_position,
+ )
+ assert(recovery_position.is_finite())
+ assert(
+ _horizontal_distance_squared(entry_position, recovery_position)
+ < _horizontal_distance_squared(entry_position, fallback_position),
+ "Fresh-water recovery returned the distant generated-world spawn.",
+ )
+ assert(
+ recovery_position.y
+ > GeneratedWorldRegion.WATER_HEIGHT
+ + GeneratedWorldRegion.WATER_RECOVERY_MINIMUM_GROUND_CLEARANCE
+ )
+ var recovery_triangles: Array[PackedVector3Array] = []
+ recovery_triangles.append_array(grass_triangles)
+ recovery_triangles.append_array(sand_triangles)
+ var center_height := _triangle_surface_height_at(
+ recovery_position,
+ recovery_triangles,
+ )
+ assert(center_height > -INF)
+ assert(absf(center_height - recovery_position.y) <= 0.01)
+ for direction: Vector2 in GeneratedWorldRegion.PROP_SURFACE_SAMPLE_DIRECTIONS:
+ var footprint_position := recovery_position + Vector3(
+ direction.x * GeneratedWorldRegion.WATER_RECOVERY_FOOTPRINT_RADIUS,
+ 0.0,
+ direction.y * GeneratedWorldRegion.WATER_RECOVERY_FOOTPRINT_RADIUS,
+ )
+ var footprint_height := _triangle_surface_height_at(
+ footprint_position,
+ recovery_triangles,
+ )
+ assert(footprint_height > -INF)
+ assert(
+ absf(footprint_height - recovery_position.y)
+ <= GeneratedWorldRegion.PROP_MAXIMUM_SUPPORT_HEIGHT_DIFFERENCE
+ )
+ assert(
+ region.get_water_recovery_position(
+ entry_position,
+ fallback_position,
+ ).is_equal_approx(recovery_position)
+ )
+ assert(
+ region.get_water_recovery_position(
+ Vector3(INF, 0.0, 0.0),
+ fallback_position,
+ ).is_equal_approx(fallback_position)
+ )
+
+
+func _horizontal_distance_squared(a: Vector3, b: Vector3) -> float:
+ return Vector2(a.x - b.x, a.z - b.z).length_squared()
+
+
+func _validate_tree_gatherable_anchors(
+ region: GeneratedWorldRegion,
+ decorations: Node3D,
+ anchors: GatherableAnchorSet3D,
+) -> void:
+ var eligible_props: Dictionary[StringName, Node3D] = {}
+ var expected_anchor_count := 0
+ for child: Node in decorations.get_children():
+ var prop := child as Node3D
+ if prop == null:
+ continue
+ var prop_id := StringName(prop.get_meta(&"terrain_prop_id", &""))
+ var definition := region.get_prop_catalog().definition_for_id(prop_id)
+ var socket_count := _count_authored_beetle_sockets(prop)
+ if (
+ definition != null
+ and definition.has_gatherable_surface()
+ and socket_count > 0
+ ):
+ eligible_props[prop.name] = prop
+ expected_anchor_count += socket_count
+ var positions := anchors.get_spawn_positions()
+ assert(positions.size() == expected_anchor_count)
+ assert(positions.size() >= 12)
+ for child: Node in anchors.get_children():
+ var anchor := child as Marker3D
+ assert(anchor != null)
+ assert(bool(anchor.get_meta(&"authored_beetle_socket", false)))
+ var prop_name := StringName(anchor.get_meta(&"terrain_prop_name", &""))
+ assert(eligible_props.has(prop_name))
+ var prop: Node3D = eligible_props[prop_name]
+ var prop_id := StringName(prop.get_meta(&"terrain_prop_id", &""))
+ var definition := region.get_prop_catalog().definition_for_id(prop_id)
+ assert(definition != null and definition.has_gatherable_surface())
+ var local_anchor := prop.to_local(anchor.global_position)
+ assert(prop_id != &"prop_palm")
+ assert(local_anchor.y >= 0.25 and local_anchor.y <= 2.0)
+
+
+func _count_authored_beetle_sockets(root_node: Node) -> int:
+ var result := 0
+ if String(root_node.name).to_lower().contains("beetle_socket"):
+ result += 1
+ for child: Node in root_node.get_children():
+ result += _count_authored_beetle_sockets(child)
+ return result
+
+
+func _find_mesh_instance(root_node: Node) -> MeshInstance3D:
+ if root_node is MeshInstance3D:
+ return root_node as MeshInstance3D
+ if root_node == null:
+ return null
+ for child: Node in root_node.get_children():
+ var mesh_instance := _find_mesh_instance(child)
+ if mesh_instance != null:
+ return mesh_instance
+ return null
+
+
+func _has_precipitation_occlusion_layer(root_node: Node) -> bool:
+ var mesh_instance := root_node as MeshInstance3D
+ if (
+ mesh_instance != null
+ and mesh_instance.get_layer_mask_value(
+ PrecipitationOcclusionType.RENDER_LAYER_NUMBER
+ )
+ ):
+ return true
+ for child: Node in root_node.get_children():
+ if _has_precipitation_occlusion_layer(child):
+ return true
+ return false
+
+
+func _has_material_variant_override(
+ root_node: Node,
+ variants: Array[Material],
+) -> bool:
+ if root_node == null:
+ return false
+ var mesh_instance := root_node as MeshInstance3D
+ if mesh_instance != null and mesh_instance.mesh != null:
+ for surface_index: int in mesh_instance.mesh.get_surface_count():
+ var override := mesh_instance.get_surface_override_material(
+ surface_index
+ )
+ if override != null and override in variants:
+ return true
+ for child: Node in root_node.get_children():
+ if _has_material_variant_override(child, variants):
+ return true
+ return false
+
+
+func _validate_authored_chunk_surfaces(
+ region: GeneratedWorldRegion,
+ generator: TerrainChunkGenerator,
+) -> void:
+ var generated := generator.get_generated_chunks_root()
+ var found_beach := false
+ var found_stream := false
+ var found_pond := false
+ var found_grass_ocean_edge := false
+ var found_grass_beach_transition := false
+ var found_grass_ocean_corner := false
+ var found_beach_ocean_corner := false
+ var found_grass_sand_diagonal := false
+ var beach_family := (
+ _placement_count(generator.placement_keys(), "chunk_0002") > 0
+ )
+ for chunk_root: Node in generated.get_children():
+ if chunk_root.name.begins_with("chunk_0002r"):
+ var beach := chunk_root.find_child(
+ "chunk_0002", true, false
+ ) as MeshInstance3D
+ assert(beach != null and beach.mesh != null)
+ var material := beach.get_active_material(0)
+ assert(material != null and material.resource_name == "sand")
+ found_beach = true
+ elif (
+ chunk_root.name.begins_with("chunk_0003r")
+ or chunk_root.name.begins_with("chunk_0016r")
+ ):
+ var stream_id := (
+ &"chunk_0016"
+ if chunk_root.name.begins_with("chunk_0016r")
+ else &"chunk_0003"
+ )
+ var stream := chunk_root.find_child(
+ String(stream_id), true, false
+ ) as MeshInstance3D
+ assert(stream != null and stream.mesh != null)
+ var stream_materials := _material_names(stream)
+ assert("dirt" in stream_materials)
+ assert("grass_lite" in stream_materials)
+ assert("dirt_wall" in stream_materials)
+ if stream_id == &"chunk_0003":
+ found_stream = true
+ elif chunk_root.name.begins_with("chunk_0004r"):
+ var pond := chunk_root.find_child(
+ "chunk_0004", true, false
+ ) as MeshInstance3D
+ assert(pond != null and pond.mesh != null)
+ for surface_index: int in pond.mesh.get_surface_count():
+ var arrays := pond.mesh.surface_get_arrays(surface_index)
+ var normals := arrays[Mesh.ARRAY_NORMAL] as PackedVector3Array
+ for normal: Vector3 in normals:
+ assert(normal.y >= -0.001)
+ _validate_pond_collision(region, pond)
+ found_pond = true
+ elif chunk_root.name.begins_with("chunk_0005r"):
+ var grass_ocean_edge := chunk_root.find_child(
+ "chunk_0005", true, false
+ ) as MeshInstance3D
+ assert(grass_ocean_edge != null and grass_ocean_edge.mesh != null)
+ var edge_materials := _material_names(grass_ocean_edge)
+ assert("grass_lite" in edge_materials)
+ assert("cliff_wall" in edge_materials)
+ found_grass_ocean_edge = true
+ elif chunk_root.name.begins_with("chunk_0006r"):
+ var grass_beach_transition := chunk_root.find_child(
+ "chunk_0006", true, false
+ ) as MeshInstance3D
+ assert(
+ grass_beach_transition != null
+ and grass_beach_transition.mesh != null
+ )
+ var transition_materials := _material_names(
+ grass_beach_transition
+ )
+ assert("sand" in transition_materials)
+ assert("grass_lite" in transition_materials)
+ assert("cliff_wall" in transition_materials)
+ found_grass_beach_transition = true
+ elif chunk_root.name.begins_with("chunk_0007r"):
+ var grass_ocean_corner := chunk_root.find_child(
+ "chunk_0007", true, false
+ ) as MeshInstance3D
+ assert(grass_ocean_corner != null and grass_ocean_corner.mesh != null)
+ var corner_materials := _material_names(grass_ocean_corner)
+ assert("grass_lite" in corner_materials)
+ assert("cliff_wall" in corner_materials)
+ found_grass_ocean_corner = true
+ elif chunk_root.name.begins_with("chunk_0008r"):
+ var beach_ocean_corner := chunk_root.find_child(
+ "chunk_0008", true, false
+ ) as MeshInstance3D
+ assert(beach_ocean_corner != null and beach_ocean_corner.mesh != null)
+ var beach_corner_materials := _material_names(beach_ocean_corner)
+ assert("sand" in beach_corner_materials)
+ found_beach_ocean_corner = true
+ elif chunk_root.name.begins_with("chunk_0013r"):
+ var grass_sand_diagonal := chunk_root.find_child(
+ "chunk_0013", true, false
+ ) as MeshInstance3D
+ assert(grass_sand_diagonal != null and grass_sand_diagonal.mesh != null)
+ var diagonal_materials := _material_names(grass_sand_diagonal)
+ assert("grass_lite" in diagonal_materials)
+ assert("sand" in diagonal_materials)
+ found_grass_sand_diagonal = true
+ assert(found_stream)
+ assert(found_pond)
+ assert(found_grass_sand_diagonal)
+ if beach_family:
+ assert(found_beach)
+ assert(found_beach_ocean_corner)
+ assert(not found_grass_ocean_edge)
+ assert(not found_grass_ocean_corner)
+ else:
+ assert(not found_beach)
+ assert(not found_beach_ocean_corner)
+ assert(found_grass_ocean_edge)
+ assert(found_grass_ocean_corner)
+ assert(not found_grass_beach_transition)
+
+
+func _material_names(mesh_instance: MeshInstance3D) -> PackedStringArray:
+ var result := PackedStringArray()
+ for surface_index: int in mesh_instance.mesh.get_surface_count():
+ var material := mesh_instance.get_active_material(surface_index)
+ if material != null:
+ result.append(material.resource_name)
+ return result
+
+
+func _validate_projected_terrain_materials(root: Node) -> void:
+ var expected_sizes := {
+ "grass_lite": 1.75,
+ "sand": 2.6,
+ "dirt": 2.5,
+ }
+ var surface_counts := {
+ "grass_lite": 0,
+ "sand": 0,
+ "dirt": 0,
+ }
+ _validate_projected_material_node(root, expected_sizes, surface_counts)
+ for material_name: String in expected_sizes:
+ assert(surface_counts[material_name] > 0)
+
+
+func _validate_projected_material_node(
+ root: Node,
+ expected_sizes: Dictionary,
+ surface_counts: Dictionary,
+) -> void:
+ var mesh_instance := root as MeshInstance3D
+ if mesh_instance != null and mesh_instance.mesh != null:
+ for surface_index: int in mesh_instance.mesh.get_surface_count():
+ var material := mesh_instance.get_active_material(surface_index)
+ if material == null or not expected_sizes.has(material.resource_name):
+ continue
+ var shader_material := material as ShaderMaterial
+ assert(shader_material != null and shader_material.shader != null)
+ 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"
+ )),
+ float(expected_sizes[material.resource_name]),
+ )
+ )
+ surface_counts[material.resource_name] += 1
+ for child: Node in root.get_children():
+ _validate_projected_material_node(
+ child,
+ expected_sizes,
+ surface_counts,
+ )
+
+
+func _validate_pond_collision(
+ region: GeneratedWorldRegion,
+ pond: MeshInstance3D,
+) -> void:
+ var best_centroid := Vector3.ZERO
+ var best_height := -INF
+ for surface_index: int in pond.mesh.get_surface_count():
+ var arrays := pond.mesh.surface_get_arrays(surface_index)
+ var vertices := arrays[Mesh.ARRAY_VERTEX] as PackedVector3Array
+ var normals := arrays[Mesh.ARRAY_NORMAL] as PackedVector3Array
+ var indices := arrays[Mesh.ARRAY_INDEX] as PackedInt32Array
+ var triangle_count := (
+ indices.size() / 3 if not indices.is_empty() else vertices.size() / 3
+ )
+ for triangle_index: int in triangle_count:
+ var offset := triangle_index * 3
+ var index_a: int = indices[offset] if not indices.is_empty() else offset
+ var index_b: int = (
+ indices[offset + 1] if not indices.is_empty() else offset + 1
+ )
+ var index_c: int = (
+ indices[offset + 2] if not indices.is_empty() else offset + 2
+ )
+ var a: Vector3 = vertices[index_a]
+ var b: Vector3 = vertices[index_b]
+ var c: Vector3 = vertices[index_c]
+ var normal := (
+ (normals[index_a] + normals[index_b] + normals[index_c]) / 3.0
+ ).normalized()
+ var centroid := (a + b + c) / 3.0
+ if normal.y > 0.5 and centroid.y > best_height:
+ best_height = centroid.y
+ best_centroid = centroid
+ assert(best_height > -INF)
+ var target := pond.global_transform * best_centroid
+ var query := PhysicsRayQueryParameters3D.create(
+ target + Vector3.UP * 2.0,
+ target + Vector3.DOWN * 2.0,
+ 1,
+ )
+ var hit := region.get_world_3d().direct_space_state.intersect_ray(query)
+ assert(not hit.is_empty())
+ var collider := hit.get("collider") as Node
+ assert(
+ collider != null
+ and collider.has_meta(&"terrain_collision_batch")
+ )
+
+
+func _decoration_group_count(
+ region: GeneratedWorldRegion,
+ group: StringName,
+) -> int:
+ var count := 0
+ var decorations := region.get_node("Decorations") as Node3D
+ for child: Node in decorations.get_children():
+ var stable_id := StringName(child.get_meta(&"terrain_prop_id", &""))
+ var definition := region.get_prop_catalog().definition_for_id(stable_id)
+ if definition != null and definition.procedural_group == group:
+ count += 1
+ return count
+
+
+func _decoration_biome_group_count(
+ region: GeneratedWorldRegion,
+ biome_id: StringName,
+ group: StringName,
+) -> int:
+ var count := 0
+ var decorations := region.get_node("Decorations") as Node3D
+ for child: Node in decorations.get_children():
+ if StringName(child.get_meta(&"terrain_biome_id", &"")) != biome_id:
+ continue
+ var prop_id := StringName(child.get_meta(&"terrain_prop_id", &""))
+ var definition := region.get_prop_catalog().definition_for_id(prop_id)
+ if definition != null and definition.procedural_group == group:
+ count += 1
+ return count
+
+
+func _biome_signature(
+ region: GeneratedWorldRegion,
+ generator: TerrainChunkGenerator,
+) -> PackedStringArray:
+ var result := PackedStringArray()
+ for row: int in generator.grid_size.y:
+ for column: int in generator.grid_size.x:
+ result.append(
+ String(region.get_biome_at(Vector2i(column, row)))
+ )
+ return result
+
+
+func _placement_count(keys: PackedStringArray, stable_id: String) -> int:
+ var count := 0
+ for key: String in keys:
+ if key.begins_with(stable_id + "@"):
+ count += 1
+ return count
+
+
+func _decoration_signature(region: GeneratedWorldRegion) -> PackedStringArray:
+ var result := PackedStringArray()
+ var decorations := region.get_node("Decorations") as Node3D
+ for child: Node in decorations.get_children():
+ var prop := child as Node3D
+ result.append("%s:%s:%s" % [
+ prop.name,
+ prop.position,
+ prop.rotation,
+ ])
+ return result
diff --git a/tests/generated_world_runtime_validation.gd.uid b/tests/generated_world_runtime_validation.gd.uid
new file mode 100644
index 0000000..060cb22
--- /dev/null
+++ b/tests/generated_world_runtime_validation.gd.uid
@@ -0,0 +1 @@
+uid://opamwsr0t7px
diff --git a/tests/home_spawn_multiplayer_validation.gd b/tests/home_spawn_multiplayer_validation.gd
new file mode 100644
index 0000000..2855eb2
--- /dev/null
+++ b/tests/home_spawn_multiplayer_validation.gd
@@ -0,0 +1,201 @@
+extends SceneTree
+
+const MainScene: PackedScene = preload("res://main/main.tscn")
+const TEST_PORT: int = 18222
+
+
+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("Home spawn 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 network_home := main.get_node(
+ "%NetworkHomeService"
+ ) as NetworkHomeService
+ var home_world := main.get_node("%HomeWorldService") as HomeWorldService
+ var spawn_service := main.get_node(
+ "%PlayerSpawnService"
+ ) as PlayerSpawnService
+ var save_manager := main.get("_save_manager") as PlayerSaveManager
+ assert(bool(main.call(
+ "_apply_world", WorldLayout.STARTER_ISLAND, 1, true
+ )))
+ assert(bool(main.call(
+ "_prepare_host_world", WorldLayout.STARTER_ISLAND, 1
+ )))
+ assert(save_manager.initialize_new_game(1, WorldLayout.STARTER_ISLAND))
+ assert(session.start_private_host(TEST_PORT))
+ main.call("_enter_gameplay")
+ assert(session.set_host_open(true))
+
+ var host_fingerprint: String = session.get_local_identity_fingerprint()
+ assert(await _wait_for_home(network_home, host_fingerprint))
+ var host_avatar: Player = spawn_service.get_avatar(1)
+ var host_spawn: Transform3D = home_world.get_exterior_spawn_transform(
+ host_fingerprint
+ )
+ assert(_same_door_position(host_avatar.global_position, host_spawn.origin))
+ assert(_faces_away_from_home(host_avatar, home_world, host_fingerprint))
+ assert(network_home.request_enter(host_fingerprint))
+ await physics_frame
+ host_avatar.global_position = home_world.get_interior_exit_position(
+ host_fingerprint
+ )
+ assert(network_home.request_exit())
+ await physics_frame
+ assert(_same_door_position(host_avatar.global_position, host_spawn.origin))
+ assert(_faces_away_from_home(host_avatar, home_world, host_fingerprint))
+
+ var remote_peer_id: int = await _wait_for_remote_peer(session)
+ assert(remote_peer_id > 1)
+ var remote_record := session.get_peer_record(remote_peer_id)
+ var remote_fingerprint: String = remote_record.identity_fingerprint
+ assert(await _wait_for_home(network_home, remote_fingerprint))
+ var remote_avatar: Player = spawn_service.get_avatar(remote_peer_id)
+ var remote_spawn: Transform3D = home_world.get_exterior_spawn_transform(
+ remote_fingerprint
+ )
+ assert(_same_door_position(
+ remote_avatar.global_position, remote_spawn.origin
+ ))
+ assert(_faces_away_from_home(
+ remote_avatar, home_world, remote_fingerprint
+ ))
+
+ 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("Home spawn multiplayer host validation: PASS")
+ await _cleanup(main, session)
+
+
+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 network_home := main.get_node(
+ "%NetworkHomeService"
+ ) as NetworkHomeService
+ var home_world := main.get_node("%HomeWorldService") as HomeWorldService
+ 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")):
+ break
+ assert(session.is_joined_client())
+ var fingerprint: String = session.get_local_identity_fingerprint()
+ assert(await _wait_for_home(network_home, fingerprint))
+ var player := main.get("_player") as Player
+ var exterior_spawn: Transform3D = (
+ home_world.get_exterior_spawn_transform(fingerprint)
+ )
+ deadline = Time.get_ticks_msec() + 5000
+ while (
+ Time.get_ticks_msec() < deadline
+ and not _same_door_position(
+ player.global_position, exterior_spawn.origin
+ )
+ ):
+ await physics_frame
+ assert(_same_door_position(
+ player.global_position, exterior_spawn.origin
+ ))
+ assert(_faces_away_from_home(player, home_world, fingerprint))
+ print("Home spawn multiplayer client validation: PASS")
+ await _cleanup(main, session)
+
+
+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_home(
+ network_home: NetworkHomeService,
+ fingerprint: String,
+) -> bool:
+ var deadline: int = Time.get_ticks_msec() + 10000
+ while Time.get_ticks_msec() < deadline:
+ await process_frame
+ var manifests: Dictionary = network_home.get(
+ "_manifests_by_fingerprint"
+ )
+ if manifests.has(fingerprint):
+ return true
+ return false
+
+
+func _faces_away_from_home(
+ avatar: Player,
+ home_world: HomeWorldService,
+ fingerprint: String,
+) -> bool:
+ var exterior_basis: Basis = home_world.get_exterior_transform(
+ fingerprint
+ ).basis
+ var outward_direction := exterior_basis.z
+ outward_direction.y = 0.0
+ if outward_direction.is_zero_approx():
+ return false
+ return avatar.get_facing_direction().dot(
+ outward_direction.normalized()
+ ) >= 0.99
+
+
+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 _same_door_position(first: Vector3, second: Vector3) -> bool:
+ return (
+ Vector2(first.x, first.z).distance_to(Vector2(second.x, second.z))
+ < 0.08
+ and absf(first.y - second.y) < 0.3
+ )
+
+
+func _cleanup(main: Node, session: NetworkSession) -> void:
+ session.disconnect_session("")
+ main.queue_free()
+ for _frame: int in 4:
+ await process_frame
+ await create_timer(0.1).timeout
+ quit()
diff --git a/tests/home_spawn_multiplayer_validation.gd.uid b/tests/home_spawn_multiplayer_validation.gd.uid
new file mode 100644
index 0000000..06150f2
--- /dev/null
+++ b/tests/home_spawn_multiplayer_validation.gd.uid
@@ -0,0 +1 @@
+uid://bndtkip5p6xma
diff --git a/tests/inventory_notepad_art_validation.gd b/tests/inventory_notepad_art_validation.gd
index cbe3414..d871b49 100644
--- a/tests/inventory_notepad_art_validation.gd
+++ b/tests/inventory_notepad_art_validation.gd
@@ -1,14 +1,22 @@
extends SceneTree
const PlayerMenuScene = preload("res://ui/player_menu.tscn")
+const HotbarScene = preload("res://ui/hotbar.tscn")
const UIReferencePresentationType = preload(
"res://ui/ui_reference_presentation.gd"
)
+const FishSaleResultType = preload("res://economy/fish_sale_result.gd")
+const CurrencyPresentationType = preload(
+ "res://ui/currency_presentation.gd"
+)
+const PelicanBuyer = preload("res://economy/buyers/pelicans.tres")
const EXPECTED_HOST_SIZE := Vector2(278.0, 484.0)
const EXPECTED_ART_SIZE := Vector2(306.28125, 580.8)
const EXPECTED_ART_POSITION := Vector2(-4.140625, -28.4)
-const INVENTORY_PANEL_RECT := Rect2(54.0, 166.0, 882.0, 484.0)
-const NOTEPAD_HOST_RECT := Rect2(952.0, 166.0, 278.0, 484.0)
+const LEGACY_COOLER_PANEL_RECT := Rect2(54.0, 166.0, 882.0, 484.0)
+const LEGACY_COOLER_NOTEPAD_RECT := Rect2(952.0, 166.0, 278.0, 484.0)
+const INVENTORY_PANEL_RECT := Rect2(199.0, 166.0, 882.0, 484.0)
+const NOTEPAD_HOST_RECT := Rect2(501.0, 166.0, 278.0, 484.0)
const NOTEPAD_ART_RECT := Rect2(
NOTEPAD_HOST_RECT.position + EXPECTED_ART_POSITION,
EXPECTED_ART_SIZE,
@@ -57,12 +65,22 @@ func _run() -> void:
+ InventoryNotepad.NOTEPAD_ART_OFFSET
))
_validate_shared_inventory_geometry(player_menu)
+ _validate_first_open_inventory(player_menu)
+ await _validate_inventory_grid_centering(player_menu, presentation_stage)
+ _validate_utility_page_geometry(player_menu)
+ await _validate_profile_content_bounds(player_menu)
+ await _validate_hotbar_centering(presentation_stage)
_validate_inventory_layering(player_menu)
+ _validate_sale_confirmation_copy(player_menu)
+ _validate_tackle_to_items_transition(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()
@@ -99,34 +117,341 @@ func _apply_stage_layout(stage: Control, display_size: Vector2) -> void:
func _validate_shared_inventory_geometry(player_menu: PlayerMenu) -> void:
for node_name: StringName in [
- &"DetailConstellation",
+ &"BagOuterWall",
+ &"TackleMainPanel",
+ ]:
+ var main_panel := player_menu.get_node("%%%s" % node_name) as Control
+ assert(main_panel != null)
+ assert(main_panel.position.is_equal_approx(INVENTORY_PANEL_RECT.position))
+ assert(
+ main_panel.size.is_equal_approx(INVENTORY_PANEL_RECT.size),
+ "%s was %s, expected %s" % [
+ node_name,
+ main_panel.size,
+ INVENTORY_PANEL_RECT.size,
+ ],
+ )
+ var cooler_panel := player_menu.get_node("%CoolerOuterWall") as Control
+ var cooler_notepad := player_menu.get_node("%DetailConstellation") as Control
+ assert(cooler_panel.position.is_equal_approx(
+ LEGACY_COOLER_PANEL_RECT.position
+ ))
+ assert(cooler_panel.size.is_equal_approx(LEGACY_COOLER_PANEL_RECT.size))
+ assert(cooler_notepad.position.is_equal_approx(
+ LEGACY_COOLER_NOTEPAD_RECT.position
+ ))
+ assert(cooler_notepad.size.is_equal_approx(
+ LEGACY_COOLER_NOTEPAD_RECT.size
+ ))
+ for node_name: StringName in [
&"BagDetailConstellation",
&"TackleDetailPanel",
]:
var host := player_menu.get_node("%%%s" % node_name) as Control
assert(host != null)
- assert(host.position.is_equal_approx(Vector2(952.0, 166.0)))
+ assert(host.position.is_equal_approx(NOTEPAD_HOST_RECT.position))
assert(host.size.is_equal_approx(EXPECTED_HOST_SIZE))
+ assert(is_equal_approx(INVENTORY_PANEL_RECT.get_center().x, 640.0))
+
+
+func _validate_first_open_inventory(player_menu: PlayerMenu) -> void:
+ var legacy_slots: Array = player_menu.get("_bag_slot_nodes") as Array
+ assert(legacy_slots.is_empty())
+ var old_slot_nodes: Array[Node] = player_menu.find_children(
+ "*", "BagStorageSlot", true, false
+ )
+ assert(old_slot_nodes.is_empty())
+
+
+func _validate_inventory_grid_centering(
+ player_menu: PlayerMenu,
+ stage: Control,
+) -> void:
+ var grid := player_menu.get("_general_inventory_grid") as Control
+ assert(grid != null)
+ grid.custom_minimum_size = Vector2(782.0, 342.0)
+ grid.size = grid.custom_minimum_size
+ player_menu.call("_layout_general_inventory_grid")
+ await process_frame
+ assert(is_equal_approx(grid.position.y, 39.0))
+ assert(
+ is_equal_approx(
+ grid.get_global_rect().get_center().x,
+ stage.get_global_rect().get_center().x,
+ ),
+ "inventory center %s did not match stage center %s" % [
+ grid.get_global_rect().get_center().x,
+ stage.get_global_rect().get_center().x,
+ ],
+ )
+
+
+func _validate_utility_page_geometry(player_menu: PlayerMenu) -> void:
+ for page_name: StringName in [
+ &"TheNetPage",
+ &"MailPage",
+ &"ProfilePage",
+ &"PlayersPage",
+ ]:
+ var page := player_menu.get_node("%%%s" % page_name) as Control
+ assert(page != null)
+ var shell := page.find_child(
+ "UtilityMainBox", true, false
+ ) as Control
+ assert(shell != null)
+ assert(shell.position.is_equal_approx(UtilityPageStyle.LAPTOP_RECT.position))
+ assert(shell.size.is_equal_approx(UtilityPageStyle.LAPTOP_RECT.size))
+ assert(is_equal_approx(shell.get_rect().get_center().x, 640.0))
+
+
+func _validate_profile_content_bounds(player_menu: PlayerMenu) -> void:
+ var profile := player_menu.get_node("%ProfilePage") as ProfilePage
+ assert(profile != null)
+ var menu_was_visible: bool = player_menu.visible
+ var profile_was_visible: bool = profile.visible
+ player_menu.visible = true
+ profile.visible = true
+ await process_frame
+ await process_frame
+ var option_list := profile.get("_option_list") as Control
+ var body := option_list.get_parent() as Control
+ var preview := profile.get("_preview") as Control
+ var shell := profile.find_child("UtilityMainBox", true, false) as Control
+ var content_zone := profile.find_child(
+ "ProfileContentZone", true, false
+ ) as Control
+ var profile_layout := profile.find_child(
+ "ProfileLayout", true, false
+ ) as Control
+ var account_row := profile.find_child(
+ "ProfileAccountRow", true, false
+ ) as Control
+ var body_panel := profile.find_child(
+ "ProfileBodyPanel", true, false
+ ) as Control
+ assert(option_list != null and body != null and preview != null)
+ assert(shell != null and content_zone != null and profile_layout != null)
+ assert(account_row != null and body_panel != null)
+ var content_frame_size := Vector2(
+ UtilityPageStyle.LAPTOP_RECT.size.x - 40.0,
+ UtilityPageStyle.LAPTOP_RECT.size.y - 42.0,
+ )
+ assert(
+ content_zone.size.is_equal_approx(content_frame_size),
+ (
+ "profile content zone changed size: %s != %s; "
+ + "layout min=%s account min=%s body min=%s"
+ ) % [
+ content_zone.size,
+ content_frame_size,
+ profile_layout.get_combined_minimum_size(),
+ account_row.get_combined_minimum_size(),
+ body_panel.get_combined_minimum_size(),
+ ],
+ )
+ assert(
+ profile_layout.size.is_equal_approx(
+ UtilityPageStyle.LAPTOP_CONTENT_SIZE
+ ),
+ "profile layout changed size: %s != %s" % [
+ profile_layout.size,
+ UtilityPageStyle.LAPTOP_CONTENT_SIZE,
+ ],
+ )
+ assert(
+ profile_layout.get_combined_minimum_size().x
+ <= profile_layout.size.x,
+ "profile layout exceeded shared content width: %s > %s" % [
+ profile_layout.get_combined_minimum_size().x,
+ profile_layout.size.x,
+ ],
+ )
+ var content_rect: Rect2 = profile_layout.get_global_rect()
+ for section: Control in [account_row, body_panel]:
+ var section_rect: Rect2 = section.get_global_rect()
+ assert(section_rect.position.x >= content_rect.position.x - 0.5)
+ assert(section_rect.end.x <= content_rect.end.x + 0.5)
+ var category_ids: Array[String] = []
+ for category_id: String in CharacterCustomizationCatalog.CATEGORY_IDS:
+ category_ids.append(category_id)
+ category_ids.append("voice")
+ for category_id: String in category_ids:
+ profile.call("_select_category", category_id)
+ await process_frame
+ await process_frame
+ assert(
+ shell.position.is_equal_approx(
+ UtilityPageStyle.LAPTOP_RECT.position
+ ),
+ "%s profile shell position changed: %s" % [
+ category_id,
+ shell.position,
+ ],
+ )
+ assert(
+ shell.size.is_equal_approx(UtilityPageStyle.LAPTOP_RECT.size),
+ "%s profile shell size changed: %s != %s" % [
+ category_id,
+ shell.size,
+ UtilityPageStyle.LAPTOP_RECT.size,
+ ],
+ )
+ assert(
+ content_zone.size.is_equal_approx(content_frame_size),
+ "%s profile content zone changed size: %s != %s" % [
+ category_id,
+ content_zone.size,
+ content_frame_size,
+ ],
+ )
+ assert(
+ profile_layout.size.is_equal_approx(
+ UtilityPageStyle.LAPTOP_CONTENT_SIZE
+ ),
+ "%s profile layout changed size: %s != %s" % [
+ category_id,
+ profile_layout.size,
+ UtilityPageStyle.LAPTOP_CONTENT_SIZE,
+ ],
+ )
+ assert(
+ profile_layout.get_combined_minimum_size().x
+ <= profile_layout.size.x,
+ "%s profile layout exceeded shared content width: %s > %s" % [
+ category_id,
+ profile_layout.get_combined_minimum_size().x,
+ profile_layout.size.x,
+ ],
+ )
+ var category_body_rect: Rect2 = body_panel.get_global_rect()
+ assert(
+ category_body_rect.end.x <= content_rect.end.x + 0.5,
+ "%s body overflowed shared content: %s > %s" % [
+ category_id,
+ category_body_rect.end.x,
+ content_rect.end.x,
+ ],
+ )
+ assert(
+ category_body_rect.end.y <= content_rect.end.y + 0.5,
+ "%s body overflowed shared content vertically: %s > %s" % [
+ category_id,
+ category_body_rect.end.y,
+ content_rect.end.y,
+ ],
+ )
+ assert(
+ body.get_combined_minimum_size().x <= body.size.x,
+ "%s customization content overflowed its body: %s > %s" % [
+ category_id,
+ body.get_combined_minimum_size().x,
+ body.size.x,
+ ],
+ )
+ assert(
+ preview.get_global_rect().end.x
+ <= content_rect.end.x + 0.5,
+ "%s preview overflowed the utility content area" % category_id,
+ )
+ profile.call("_select_category", "fur_pattern")
+ profile.call("_select_fur_section", "colors")
+ await process_frame
+ await process_frame
+ var color_panel := profile.find_child(
+ "FurColorOptionsPanel", true, false
+ ) as Control
+ var color_scroll := profile.find_child(
+ "FurColorScroll", true, false
+ ) as ScrollContainer
+ assert(color_panel != null and color_scroll != null)
+ assert(
+ color_panel.get_global_rect().end.y
+ <= body_panel.get_global_rect().end.y + 0.5,
+ "fur color panel overflowed profile body: %s > %s" % [
+ color_panel.get_global_rect().end.y,
+ body_panel.get_global_rect().end.y,
+ ],
+ )
+ assert(
+ profile_layout.get_combined_minimum_size().y
+ <= profile_layout.size.y,
+ "fur colors expanded profile layout vertically: %s > %s" % [
+ profile_layout.get_combined_minimum_size().y,
+ profile_layout.size.y,
+ ],
+ )
+ profile.visible = profile_was_visible
+ player_menu.visible = menu_was_visible
+
+
+func _validate_hotbar_centering(parent: Control) -> void:
+ var hotbar := HotbarScene.instantiate() as HotbarUI
+ parent.add_child(hotbar)
+ await process_frame
+ var presentation := hotbar.get_node(
+ "%HotbarPresentationScaleRoot"
+ ) as Control
+ var field := hotbar.get_node("%BubbleField") as Control
+ hotbar.set_player_menu_context(true)
+ var displayed_center_x: float = (
+ presentation.position.x
+ + field.get_rect().get_center().x * presentation.scale.x
+ )
+ assert(is_equal_approx(displayed_center_x, 640.0))
+ var displayed_top: float = (
+ presentation.position.y + field.position.y * presentation.scale.y
+ )
+ var displayed_bottom: float = (
+ displayed_top + field.size.y * presentation.scale.y
+ )
+ assert(displayed_top < INVENTORY_PANEL_RECT.end.y)
+ assert(displayed_bottom > INVENTORY_PANEL_RECT.end.y)
+ hotbar.queue_free()
func _validate_inventory_layering(player_menu: PlayerMenu) -> void:
- var bag_filters := player_menu.get_node("%BagFilterTabs") as Control
var bag_panel := player_menu.get_node("%BagOuterWall") as Control
var sale_confirmation := player_menu.get_node("%SaleConfirmation") as Control
var cooler_panel := player_menu.get_node("%CoolerOuterWall") as Control
var tackle_panel := player_menu.get_node("%TackleMainPanel") as Control
- assert(bag_filters != null)
+ var inventory_tabs := player_menu.get_node("%InventorySubTabs") as Control
+ var items_tab := player_menu.get_node("%ItemsSubTab") as Button
+ var bait_list := player_menu.get_node("%BaitItemList") as GridContainer
+ var lure_list := player_menu.get_node("%LureItemList") as GridContainer
assert(bag_panel != null)
assert(sale_confirmation != null)
assert(cooler_panel != null)
assert(tackle_panel != null)
- assert(bag_filters.z_index > bag_panel.z_index)
+ assert(inventory_tabs != null)
+ assert(items_tab != null and not items_tab.visible)
+ assert(not (player_menu.get_node("%CoolerSubTab") as Button).visible)
+ assert((player_menu.get_node("%BagSubTab") as Button).text == "Inventory")
+ assert(bait_list != null and bait_list.columns == 4)
+ assert(lure_list != null and lure_list.columns == 4)
+ assert(bait_list.get_theme_constant("h_separation") == 28)
+ assert(lure_list.get_theme_constant("h_separation") == 28)
assert(sale_confirmation.z_index > cooler_panel.z_index)
assert(sale_confirmation.z_index > tackle_panel.z_index)
+ assert(
+ (player_menu.get_node("%BagDetailConstellation") as Control).z_index
+ > bag_panel.z_index
+ )
+ assert(
+ (player_menu.get_node("%BagModalBlocker") as Control).z_index
+ > bag_panel.z_index
+ )
+ assert(
+ (player_menu.get_node("%BagDetailConstellation") as Control).z_index
+ > (player_menu.get_node("%BagModalBlocker") as Control).z_index
+ )
+ assert(
+ (player_menu.get_node("%TackleDetailPanel") as Control).z_index
+ > tackle_panel.z_index
+ )
# 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 +463,112 @@ func _validate_inventory_layering(player_menu: PlayerMenu) -> void:
)
+func _validate_sale_confirmation_copy(player_menu: PlayerMenu) -> void:
+ var preview := FishSaleResultType.new()
+ preview.fish_count = 1
+ preview.payout = 3
+ preview.base_value = 12
+ var message: String = str(player_menu.call(
+ "_sale_confirmation_text",
+ preview,
+ PelicanBuyer,
+ preview.base_value,
+ ))
+ assert(message == (
+ "[center]You can sell this fish to the pelicans now for %s, "
+ + "but the shop is willing to pay %s![/center]"
+ ) % [
+ CurrencyPresentationType.bbcode_amount(preview.payout, 22),
+ CurrencyPresentationType.bbcode_amount(preview.base_value, 22),
+ ])
+ assert((player_menu.get_node("%ConfirmSaleButton") as Button).text == "sell now")
+ assert((player_menu.get_node("%CancelSaleButton") as Button).text == "nevermind")
+
+
+func _validate_tackle_to_items_transition(player_menu: PlayerMenu) -> void:
+ var inventory_notepad := player_menu.get_node(
+ "%BagDetailBubble"
+ ) as InventoryNotepad
+ assert(inventory_notepad != null)
+ assert(inventory_notepad.title_text == "inventory notes")
+ player_menu.call(
+ "_show_section_immediate", PlayerMenu.Section.TACKLE_BOX
+ )
+ player_menu.call("_show_inventory_tab", 0)
+ player_menu.call("_cancel_page_tween")
+ player_menu.call("_show_section_immediate", PlayerMenu.Section.BAG)
+ player_menu.call("_update_bag_detail")
+ assert(player_menu.get("_current_section") == PlayerMenu.Section.BAG)
+ assert(not (player_menu.get_node("%BagDetailConstellation") as Control).visible)
+ assert(
+ (player_menu.get_node("%BagSpriteDetailData") as Label).text
+ == "select an item for details."
+ )
+ player_menu.visible = true
+ player_menu.call("_set_content_interactive", true)
+ var no_actions: Array[BaseButton] = []
+ player_menu.call(
+ "_open_inventory_notepad",
+ PlayerMenu.Section.BAG,
+ StringName("modal-test"),
+ no_actions,
+ )
+ assert((player_menu.get_node("%BagDetailConstellation") as Control).visible)
+ assert((player_menu.get_node("%BagModalBlocker") as Control).visible)
+ assert(
+ (player_menu.get_node("%BagModalBlocker") as Control).mouse_filter
+ == Control.MOUSE_FILTER_STOP
+ )
+ assert(
+ (player_menu.get_node("%InventoryTab") as Button).focus_mode
+ == Control.FOCUS_NONE
+ )
+ assert(
+ (player_menu.get_node("%BagSpriteDetailData") as Label).get_theme_font(
+ "font"
+ ) == InventoryNotepad.NOTEPAD_FONT
+ )
+ player_menu.call(
+ "_release_controller_ownership", false, false
+ )
+ assert(not (player_menu.get_node("%BagModalBlocker") as Control).visible)
+ player_menu.call("_set_content_interactive", false)
+ player_menu.visible = false
+ player_menu.call("_cancel_page_tween")
+
+
+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
+ assert(sort_choice != null)
+ assert(sort_direction != null)
+ assert(empty_selection != null)
+ assert(favorite != null and sell != null)
+ assert(player_menu.get_node_or_null("%SellAllBubble") == 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)
+
+
func _validate_resolution_matrix() -> void:
for target_size: Vector2 in TARGET_SIZES:
var stage_rect: Rect2 = UIReferencePresentationType.get_rect(
@@ -235,28 +666,30 @@ func _to_screen_rect(
func _capture_inventory_pages(player_menu: PlayerMenu) -> void:
- if not OS.has_environment("NETFISHING_NOTEPAD_CAPTURE"):
+ if not OS.has_environment("straywild_NOTEPAD_CAPTURE"):
return
player_menu.visible = true
var sections: Array[PlayerMenu.Section] = [
- PlayerMenu.Section.COOLER,
- PlayerMenu.Section.TACKLE_BOX,
PlayerMenu.Section.BAG,
+ PlayerMenu.Section.TACKLE_BOX,
]
- var suffixes: Array[String] = ["cooler", "tackle", "equipment"]
+ var suffixes: Array[String] = ["inventory", "tackle"]
for index: int in sections.size():
player_menu.call("_show_section_immediate", sections[index])
await process_frame
await process_frame
await _save_capture(suffixes[index])
- player_menu.call("_show_section_immediate", PlayerMenu.Section.COOLER)
+ player_menu.call("_show_section_immediate", PlayerMenu.Section.BAG)
var sale_confirmation := player_menu.get_node("%SaleConfirmation") as Control
var confirmation_message := player_menu.get_node(
"%ConfirmationMessage"
) as Label
assert(sale_confirmation != null)
assert(confirmation_message != null)
- confirmation_message.text = "sell selected fish?"
+ confirmation_message.text = (
+ "You can sell this fish to the pelicans now, "
+ + "but the shop will pay more!"
+ )
sale_confirmation.visible = true
await process_frame
await process_frame
@@ -267,7 +700,7 @@ func _capture_inventory_pages(player_menu: PlayerMenu) -> void:
func _save_capture(suffix: String) -> void:
var image: Image = root.get_texture().get_image()
var path: String = "%s-%s.png" % [
- OS.get_environment("NETFISHING_NOTEPAD_CAPTURE"),
+ OS.get_environment("straywild_NOTEPAD_CAPTURE"),
suffix,
]
assert(image.save_png(path) == OK)
diff --git a/tests/inventory_storage_persistence_validation.gd b/tests/inventory_storage_persistence_validation.gd
new file mode 100644
index 0000000..794f438
--- /dev/null
+++ b/tests/inventory_storage_persistence_validation.gd
@@ -0,0 +1,83 @@
+extends SceneTree
+
+const MainScene: PackedScene = preload("res://main/main.tscn")
+const ItemCatalogResource: ItemCatalog = preload(
+ "res://items/catalog/item_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 save_manager := main.get("_save_manager") as PlayerSaveManager
+ var player := main.get("_player") as Player
+ assert(save_manager != null and player != null)
+ const TEST_WORLD_SEED := 918273
+ assert(save_manager.initialize_new_game(
+ TEST_WORLD_SEED,
+ WorldLayout.STARTER_ISLAND,
+ ))
+ save_manager.set_autosave_enabled(true)
+ assert(player.bag.add_item(&"art_kit"))
+ assert(player.bag.add_item(&"coffee"))
+ assert(player.bag.add_item(&"worms"))
+ assert(player.bag.add_item(&"the_standby"))
+ assert(player.equip_bait(ItemCatalogResource.get_item_by_id(&"worms")))
+ assert(player.equip_lure(ItemCatalogResource.get_item_by_id(&"the_standby")))
+ assert(player.bag.move_item_to_storage_slot(&"basic_fishing_rod", 14))
+ assert(player.bag.move_item_to_storage_slot(&"coffee", 12))
+ assert(save_manager.save_now())
+
+ var decoded: Dictionary = ProgressionSaveCodec.read_local_save(
+ str(save_manager.get("_save_path"))
+ )
+ assert(bool(decoded.get("ok", false)))
+ var parsed: Dictionary = decoded["data"]
+ var records: Array = parsed["bag"]["items"]
+ assert(str(parsed["world"]["layout"]) == String(WorldLayout.STARTER_ISLAND))
+ assert(int(parsed["world"]["seed"]) == TEST_WORLD_SEED)
+ assert(_saved_slot(records, &"basic_fishing_rod") == 14)
+ assert(_saved_slot(records, &"coffee") == 12)
+
+ assert(player.bag.move_item_to_storage_slot(&"basic_fishing_rod", 0))
+ assert(player.bag.move_item_to_storage_slot(&"coffee", 0))
+ player.unequip_bait()
+ player.unequip_lure()
+ assert(save_manager.load_player_data())
+ assert(save_manager.get_world_layout() == WorldLayout.STARTER_ISLAND)
+ assert(save_manager.get_world_seed() == TEST_WORLD_SEED)
+ assert(player.bag.get_storage_slot(&"basic_fishing_rod") == 14)
+ assert(player.bag.get_storage_slot(&"coffee") == 12)
+ assert(player.active_bait_id == &"worms")
+ assert(player.active_lure_id == &"the_standby")
+
+ main.queue_free()
+ for _frame: int in 4:
+ await process_frame
+ await create_timer(0.1).timeout
+ print("Inventory storage persistence validation: PASS")
+ quit()
+
+
+func _saved_slot(records: Array, item_id: StringName) -> int:
+ for value: Variant in records:
+ if (
+ typeof(value) == TYPE_DICTIONARY
+ and str((value as Dictionary).get("item_id", ""))
+ == String(item_id)
+ ):
+ return int((value as Dictionary).get("storage_slot", -1))
+ return -1
diff --git a/tests/inventory_storage_persistence_validation.gd.uid b/tests/inventory_storage_persistence_validation.gd.uid
new file mode 100644
index 0000000..6edda3b
--- /dev/null
+++ b/tests/inventory_storage_persistence_validation.gd.uid
@@ -0,0 +1 @@
+uid://bwp2jhs116hmw
diff --git a/tests/inventory_storage_validation.gd b/tests/inventory_storage_validation.gd
new file mode 100644
index 0000000..eccb66d
--- /dev/null
+++ b/tests/inventory_storage_validation.gd
@@ -0,0 +1,127 @@
+extends SceneTree
+
+const Catalog: ItemCatalog = preload("res://items/catalog/item_catalog.tres")
+const Bluegill: FishData = preload("res://fish/species/bluegill/bluegill.tres")
+
+
+func _initialize() -> void:
+ call_deferred("_run")
+
+
+func _run() -> void:
+ var state := Node.new()
+ root.add_child(state)
+ var bag := PlayerBag.new()
+ var catches := FishInventory.new()
+ var storage_capacity := PlayerCoolerCapacity.new()
+ var layout := PlayerInventoryLayout.new()
+ var hotbar := PlayerHotbar.new()
+ for node: Node in [bag, catches, storage_capacity, layout, hotbar]:
+ state.add_child(node)
+ bag.setup(Catalog)
+ layout.setup(bag, catches, Catalog, storage_capacity)
+ bag.set_inventory_layout(layout)
+ catches.set_inventory_layout(layout)
+ hotbar.setup(bag, Catalog, catches, layout)
+
+ assert(bag.add_item(&"basic_fishing_rod", 1))
+ assert(bag.add_item(&"coffee", 3))
+ var fish_catch := _make_bluegill()
+ assert(catches.add_catch(fish_catch))
+
+ var inventory_grid := GeneralInventoryGrid.new()
+ inventory_grid.set_slot_presentation(Vector2(78.0, 78.0), 10)
+ root.add_child(inventory_grid)
+ inventory_grid.setup(
+ layout,
+ bag,
+ catches,
+ hotbar,
+ Catalog,
+ PlayerInventoryLayout.InventoryContainer.INVENTORY,
+ )
+ var storage_grid := GeneralInventoryGrid.new()
+ root.add_child(storage_grid)
+ storage_grid.setup(
+ layout,
+ bag,
+ catches,
+ hotbar,
+ Catalog,
+ PlayerInventoryLayout.InventoryContainer.STORAGE,
+ )
+ await process_frame
+
+ var inventory_slots: Array = inventory_grid.get("_slots")
+ var storage_slots: Array = storage_grid.get("_slots")
+ assert(inventory_slots.size() == 36)
+ assert(inventory_grid.get_slots().size() == 9)
+ assert(inventory_grid.custom_minimum_size == Vector2(782.0, 342.0))
+ assert(storage_capacity.get_capacity() == 9)
+ assert(storage_slots.size() == 72)
+ assert(storage_grid.get_slots().size() == 9)
+ assert((storage_slots[0] as Control).custom_minimum_size == Vector2(52.0, 52.0))
+ assert((storage_slots[9] as GeneralInventorySlot).disabled)
+
+ var coffee_slot := _find_slot(inventory_slots, &"coffee")
+ assert(coffee_slot != null)
+ (inventory_slots[7] as GeneralInventorySlot).call(
+ "_drop_data",
+ Vector2.ZERO,
+ {"kind": "bag_item", "item_id": "coffee"},
+ )
+ assert(int(layout.get_entry(
+ PlayerInventoryLayout.EntryKind.ITEM, &"coffee"
+ ).get("slot", -1)) == 7)
+ assert(coffee_slot.entry_identity.is_empty())
+
+ (storage_slots[0] as GeneralInventorySlot).call(
+ "_drop_data",
+ Vector2.ZERO,
+ {"kind": "cooler_fish", "catch_id": String(fish_catch.catch_id)},
+ )
+ assert(layout.get_container(
+ PlayerInventoryLayout.EntryKind.CATCH, fish_catch.catch_id
+ ) == PlayerInventoryLayout.InventoryContainer.STORAGE)
+
+ assert(hotbar.assign_item(0, &"basic_fishing_rod"))
+ assert(not layout.is_item_in_inventory(&"basic_fishing_rod"))
+ (inventory_slots[8] as GeneralInventorySlot).call(
+ "_drop_data",
+ Vector2.ZERO,
+ {"kind": "hotbar_slot", "slot_index": 0},
+ )
+ assert(hotbar.get_item_id(0).is_empty())
+ assert(layout.is_item_in_inventory(&"basic_fishing_rod"))
+ assert(int(layout.get_entry(
+ PlayerInventoryLayout.EntryKind.ITEM, &"basic_fishing_rod"
+ ).get("slot", -1)) == 8)
+
+ print("Inventory storage validation: PASS")
+ state.free()
+ inventory_grid.free()
+ storage_grid.free()
+ quit()
+
+
+func _make_bluegill() -> FishCatch:
+ var fish_catch := FishCatch.new()
+ fish_catch.fish = Bluegill
+ fish_catch.fish_id = Bluegill.id
+ fish_catch.catch_id = &"bluegill:inventory_storage_test"
+ fish_catch.weight_lb = Bluegill.get_minimum_weight()
+ fish_catch.display_scale = Bluegill.get_display_scale_for_weight(
+ fish_catch.weight_lb
+ )
+ fish_catch.sale_value = Bluegill.get_sale_value_for_weight(
+ fish_catch.weight_lb
+ )
+ return fish_catch
+
+
+func _find_slot(slots: Array, identity: StringName) -> GeneralInventorySlot:
+ for candidate: Variant in slots:
+ var slot := candidate as GeneralInventorySlot
+ if slot != null and slot.entry_identity == identity:
+ return slot
+ return null
diff --git a/tests/inventory_storage_validation.gd.uid b/tests/inventory_storage_validation.gd.uid
new file mode 100644
index 0000000..1c8717c
--- /dev/null
+++ b/tests/inventory_storage_validation.gd.uid
@@ -0,0 +1 @@
+uid://w6e06q8yfwp6
diff --git a/tests/job_multiplayer_validation.gd b/tests/job_multiplayer_validation.gd
index 5994eae..6c0fa5b 100644
--- a/tests/job_multiplayer_validation.gd
+++ b/tests/job_multiplayer_validation.gd
@@ -23,13 +23,16 @@ func _run() -> void:
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
var jobs := main.get_node("%PlayerJobService") as PlayerJobService
- assert(session.start_private_host(TEST_PORT))
- assert(save_manager.initialize_new_game())
- main.call("_enter_gameplay")
+ assert(bool(main.call(
+ "_apply_world", WorldLayout.STARTER_ISLAND, 1, true
+ )))
+ assert(session.set_host_world(WorldLayout.STARTER_ISLAND, 1))
+ 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))
@@ -56,7 +59,9 @@ func _run_host() -> void:
print("Job 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()
@@ -85,6 +90,20 @@ func _run_client() -> void:
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
@@ -93,7 +112,9 @@ func _run_client() -> void:
print("Job 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/job_system_validation.gd b/tests/job_system_validation.gd
index 24cad9d..65b7164 100644
--- a/tests/job_system_validation.gd
+++ b/tests/job_system_validation.gd
@@ -21,13 +21,17 @@ func _run() -> void:
assert(bool(main.get("_application_initialized")))
var session := main.get_node("%NetworkSession") as NetworkSession
assert(session != null)
+ assert(bool(main.call(
+ "_apply_world", WorldLayout.STARTER_ISLAND, 1, true
+ )))
+ assert(session.set_host_world(WorldLayout.STARTER_ISLAND, 1))
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())
+ assert(save_manager.initialize_new_game(1, WorldLayout.STARTER_ISLAND))
main.call("_enter_gameplay")
await physics_frame
await physics_frame
@@ -49,6 +53,7 @@ func _run() -> void:
_validate_weather_guarantees(board)
_validate_late_board_weather_fallback()
_validate_remote_tamper_rejection(jobs, board)
+ _validate_creature_jobs(jobs)
var sell_job: Dictionary = _find_job(
jobs.get_daily_jobs(), JobCatalog.Kind.SELL_TOTAL
@@ -85,6 +90,11 @@ func _run() -> void:
(player_menu.get_node("%NavigationCluster") as Control).get_child_count()
== 7
)
+ net_page.call("_select_view", TheNetPage.View.LIFETIME)
+ await process_frame
+ _validate_fishnet_bounds(net_page)
+ await _validate_compact_value_tooltips(net_page)
+ await _validate_unavailable_fishnet_layout()
var wallet_before: int = player.wallet.get_balance()
var experience_before: int = player.experience.get_total_experience()
@@ -162,10 +172,31 @@ func _run() -> void:
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
@@ -173,23 +204,238 @@ func _run() -> void:
assert(session.set_host_open(false))
assert(save_manager.save_now())
- var save_file := FileAccess.open(
- str(save_manager.get("_save_path")), FileAccess.READ
+ var decoded: Dictionary = ProgressionSaveCodec.read_local_save(
+ str(save_manager.get("_save_path"))
)
- 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(bool(decoded.get("ok", false)))
+ var save_data: Dictionary = decoded["data"]
+ assert(int(save_data.get("save_version", -1)) == 11)
assert(PlayerJobService.validate_save_data(save_data.get("jobs", {})))
+ assert(not PlayerHomeState.sanitize_save_data(
+ save_data.get("home", {})
+ ).is_empty())
+ _validate_pause_session_switch(main, session)
main.queue_free()
- await process_frame
+ 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_fishnet_bounds(page: TheNetPage) -> void:
+ var laptop := page.get_node("UtilityMainBox") as PanelContainer
+ var forecast_column := page.find_child(
+ "ForecastColumn", true, false
+ ) as VBoxContainer
+ var refresh_label := page.find_child(
+ "DailyRefreshLabel", true, false
+ ) as Label
+ var jobs_scroll := page.find_child("JobsScroll", true, false) as ScrollContainer
+ assert(laptop != null)
+ assert(forecast_column != null and refresh_label != null)
+ assert(jobs_scroll != null)
+ var laptop_rect: Rect2 = laptop.get_global_rect()
+ assert(forecast_column.get_global_rect().end.x <= laptop_rect.end.x)
+ assert(refresh_label.get_global_rect().end.x <= laptop_rect.end.x)
+ assert(refresh_label.get_global_rect().end.y <= laptop_rect.end.y)
+ assert(jobs_scroll.horizontal_scroll_mode == ScrollContainer.SCROLL_MODE_DISABLED)
+ var list := page.get("_list") as VBoxContainer
+ assert(list != null)
+ for row: Control in list.get_children():
+ assert(row.size.x <= jobs_scroll.size.x + 0.5)
+
+
+func _validate_compact_value_tooltips(page: TheNetPage) -> void:
+ assert(str(page.call("_compact_integer", 2400)) == "2.4k")
+ assert(str(page.call("_compact_integer", 2500)) == "2.5k")
+ var list := page.get("_list") as VBoxContainer
+ assert(list != null)
+ var jobs: Array[Dictionary] = [{
+ "title": "compact tooltip check",
+ "description": "compact values keep their exact values on hover",
+ "target": 2500,
+ "progress": 2400,
+ "fish_coin": 1200,
+ "experience": 3400,
+ }]
+ page.call("_build_job_rows", jobs, "")
+ await process_frame
+ var row := list.get_child(list.get_child_count() - 1) as PanelContainer
+ assert(row != null)
+ var progress_bar: ProgressBar
+ var progress_count: Label
+ var reward: VBoxContainer
+ for child: Node in row.find_children("*", "ProgressBar", true, false):
+ progress_bar = child as ProgressBar
+ break
+ for child: Node in row.find_children("*", "Label", true, false):
+ var label := child as Label
+ if label != null and label.text == "2.4k / 2.5k":
+ progress_count = label
+ break
+ for child: Node in row.find_children("*", "VBoxContainer", true, false):
+ var candidate := child as VBoxContainer
+ if candidate != null and candidate.tooltip_text == (
+ "1200 fish coins · 3400 xp"
+ ):
+ reward = candidate
+ break
+ assert(progress_bar != null)
+ assert(progress_bar.tooltip_text == "2400 / 2500")
+ assert(progress_bar.mouse_filter == Control.MOUSE_FILTER_STOP)
+ assert(progress_count != null)
+ assert(progress_count.tooltip_text == "2400 / 2500")
+ assert(progress_count.mouse_filter == Control.MOUSE_FILTER_STOP)
+ assert(reward != null)
+ assert(reward.mouse_filter == Control.MOUSE_FILTER_STOP)
+ row.queue_free()
+ await process_frame
+
+
+func _validate_creature_jobs(jobs: PlayerJobService) -> void:
+ var fish_count: int = 0
+ var insect_count: int = 0
+ var shellfish_count: int = 0
+ var candidates: Array[FishData] = []
+ for fish: FishData in Catalog.candidates:
+ if fish == null or not fish.is_selectable():
+ continue
+ candidates.append(fish)
+ match fish.get_creature_group():
+ FishData.CreatureGroup.FISH:
+ fish_count += 1
+ FishData.CreatureGroup.INSECT:
+ insect_count += 1
+ FishData.CreatureGroup.SHELLFISH:
+ shellfish_count += 1
+ assert(fish_count > 0 and insect_count > 0 and shellfish_count > 0)
+
+ var lifetime: Array[Dictionary] = jobs.get_lifetime_jobs()
+ var fish_discovery: Dictionary = _find_job(
+ lifetime, JobCatalog.Kind.DISCOVER_SPECIES
+ )
+ assert(int(fish_discovery.get("target", -1)) == fish_count)
+ for creature_group: int in [
+ FishData.CreatureGroup.INSECT,
+ FishData.CreatureGroup.SHELLFISH,
+ ]:
+ assert(not _find_creature_job(
+ lifetime,
+ JobCatalog.Kind.CATCH_CREATURE_GROUP,
+ creature_group,
+ ).is_empty())
+ assert(not _find_creature_job(
+ lifetime,
+ JobCatalog.Kind.DISCOVER_CREATURE_GROUP,
+ creature_group,
+ ).is_empty())
+ assert(not _find_creature_job(
+ lifetime,
+ JobCatalog.Kind.MASTER_CREATURE_GROUP,
+ creature_group,
+ ).is_empty())
+
+ var daily_groups: Dictionary[int, bool] = {}
+ for seed_index: int in 64:
+ for job: Dictionary in JobCatalog.generate_daily_jobs(
+ "creature-job-validation-%d" % seed_index,
+ candidates,
+ true,
+ ):
+ if int(job.get("kind", -1)) == JobCatalog.Kind.CATCH_CREATURE_GROUP:
+ daily_groups[int(job.get("creature_group", -1))] = true
+ assert(daily_groups.has(FishData.CreatureGroup.INSECT))
+ assert(daily_groups.has(FishData.CreatureGroup.SHELLFISH))
+
+ var beetle: FishData = Catalog.get_fish_by_id(&"beetle_stag_common")
+ assert(beetle != null)
+ var beetle_catch := _make_test_catch(beetle, &"job-beetle")
+ jobs.call("_on_authoritative_catch", beetle_catch)
+ var statistics: Dictionary = jobs.to_save_data().get("statistics", {})
+ assert(int(statistics.get("fish_caught", -1)) == 0)
+ assert(int(statistics.get("insects_caught", -1)) == 1)
+
+
+func _find_creature_job(
+ jobs: Array[Dictionary],
+ kind: JobCatalog.Kind,
+ creature_group: int,
+) -> Dictionary:
+ for job: Dictionary in jobs:
+ if (
+ int(job.get("kind", -1)) == kind
+ and int(job.get("creature_group", -1)) == creature_group
+ ):
+ return job
+ return {}
+
+
+func _make_test_catch(fish: FishData, catch_id: StringName) -> FishCatch:
+ var fish_catch := FishCatch.new()
+ fish_catch.fish = fish
+ fish_catch.fish_id = fish.id
+ fish_catch.catch_id = catch_id
+ fish_catch.catch_sequence = 1
+ fish_catch.weight_lb = fish.get_minimum_weight()
+ fish_catch.display_scale = fish.get_display_scale_for_weight(
+ fish_catch.weight_lb
+ )
+ fish_catch.quality = FishQuality.Tier.BORING
+ fish_catch.sale_value = fish.get_sale_value_for_weight(fish_catch.weight_lb)
+ assert(fish_catch.is_valid())
+ return fish_catch
+
+
func _validate_weather_guarantees(board: Dictionary) -> void:
var jobs: Array = board.get("jobs", [])
var schedule: Array = board.get("weather_schedule", [])
diff --git a/tests/keyboard_mouse_mapping_validation.gd b/tests/keyboard_mouse_mapping_validation.gd
new file mode 100644
index 0000000..1b95e01
--- /dev/null
+++ b/tests/keyboard_mouse_mapping_validation.gd
@@ -0,0 +1,274 @@
+extends SceneTree
+
+const KeyboardMouseMappingManagerType = preload(
+ "res://settings/keyboard_mouse_mapping_manager.gd"
+)
+const KeyboardMouseMappingPanelType = preload(
+ "res://ui/keyboard_mouse_mapping_panel.gd"
+)
+const SettingsPanelScene = preload("res://ui/settings_panel.tscn")
+const FishingSpotScene = preload("res://fishing/fishing_spot.tscn")
+const FishingSpotType = preload("res://fishing/fishing_spot.gd")
+const CatchDifficultyProfileType = preload(
+ "res://fishing/catch_difficulty_profile.gd"
+)
+const PlayerType = preload("res://player/player.gd")
+
+
+func _initialize() -> void:
+ _run.call_deferred()
+
+
+func _run() -> void:
+ var manager := KeyboardMouseMappingManagerType.new()
+ root.add_child(manager)
+ await process_frame
+ var defaults: Dictionary = manager.get_active_bindings()
+ assert(
+ defaults.size()
+ == KeyboardMouseMappingManagerType.ROLE_ORDER.size()
+ )
+ assert(
+ not manager.get_binding(
+ KeyboardMouseMappingManagerType.ROLE_INTERACT
+ ).is_empty()
+ )
+ assert(
+ manager.get_binding_label(
+ KeyboardMouseMappingManagerType.ROLE_INTERACT
+ ) == "e"
+ )
+ assert(
+ KeyboardMouseMappingManagerType.ROLE_LABELS[
+ KeyboardMouseMappingManagerType.ROLE_CAMERA_ZOOM_IN
+ ] == "hotbar select (+SHIFT to zoom)"
+ )
+ assert(
+ KeyboardMouseMappingManagerType.ROLE_LABELS[
+ KeyboardMouseMappingManagerType.ROLE_CAMERA_ZOOM_OUT
+ ] == "hotbar select (+SHIFT to zoom)"
+ )
+ assert(
+ str(defaults[
+ str(KeyboardMouseMappingManagerType.ROLE_PRIMARY_ACTION)
+ ].get("kind", "")) == "mouse_button"
+ )
+ assert(
+ str(defaults[
+ str(KeyboardMouseMappingManagerType.ROLE_ALTERNATE_REEL)
+ ].get("kind", "")) == "key"
+ )
+ assert(_has_physical_key(&"reel_alternate", KEY_QUOTELEFT))
+ assert(_event_count(&"reel_alternate", true) == 0)
+ assert(
+ str(defaults[
+ str(KeyboardMouseMappingManagerType.ROLE_CHAT)
+ ].get("kind", "")) == "key"
+ )
+
+ var joypad_events_before: int = _event_count(&"jump", true)
+ var rebind_key := InputEventKey.new()
+ rebind_key.physical_keycode = KEY_R
+ rebind_key.pressed = true
+ var jump_binding: Dictionary = manager.binding_from_event(rebind_key)
+ assert(manager.validate_binding(jump_binding))
+ assert(manager.set_binding(
+ KeyboardMouseMappingManagerType.ROLE_JUMP,
+ jump_binding,
+ ))
+ assert(manager.has_custom_mapping())
+ assert(FileAccess.file_exists(
+ KeyboardMouseMappingManagerType.MAPPING_PATH
+ ))
+ assert(_has_physical_key(&"jump", KEY_R))
+ assert(_event_count(&"jump", true) == joypad_events_before)
+
+ var mouse_event := InputEventMouseButton.new()
+ mouse_event.button_index = MOUSE_BUTTON_XBUTTON1
+ mouse_event.pressed = true
+ var mouse_binding: Dictionary = manager.binding_from_event(mouse_event)
+ assert(manager.set_binding(
+ KeyboardMouseMappingManagerType.ROLE_PRIMARY_ACTION,
+ mouse_binding,
+ ))
+ assert(_has_mouse_button(&"fish_primary", MOUSE_BUTTON_XBUTTON1))
+ assert(not _has_mouse_button(&"fish_primary", MOUSE_BUTTON_LEFT))
+ var alternate_reel_key := InputEventKey.new()
+ alternate_reel_key.physical_keycode = KEY_G
+ alternate_reel_key.pressed = true
+ assert(manager.set_binding(
+ KeyboardMouseMappingManagerType.ROLE_ALTERNATE_REEL,
+ manager.binding_from_event(alternate_reel_key),
+ ))
+ assert(_has_physical_key(&"reel_alternate", KEY_G))
+ assert(_event_count(&"reel_alternate", true) == 0)
+
+ var joypad_event := InputEventJoypadButton.new()
+ joypad_event.button_index = JOY_BUTTON_A
+ joypad_event.pressed = true
+ assert(manager.binding_from_event(joypad_event).is_empty())
+
+ var panel := KeyboardMouseMappingPanelType.new()
+ root.add_child(panel)
+ panel.setup(manager)
+ panel.open_panel()
+ await process_frame
+ assert(_has_label_text(panel, "keyboard binds"))
+ var scrolls: Array[Node] = panel.find_children(
+ "", "ScrollContainer", true, false
+ )
+ assert(not scrolls.is_empty())
+ assert((scrolls.front() as ScrollContainer).follow_focus)
+ var first_binding := panel._binding_buttons.get(
+ KeyboardMouseMappingManagerType.ROLE_ORDER.front()
+ ) as Button
+ var last_binding := panel._binding_buttons.get(
+ KeyboardMouseMappingManagerType.ROLE_ORDER.back()
+ ) as Button
+ assert(first_binding.get_node(
+ first_binding.focus_neighbor_top
+ ) == first_binding)
+ assert(last_binding.get_node(
+ last_binding.focus_neighbor_bottom
+ ) == panel._close_button)
+ assert(panel._close_button.get_node(
+ panel._close_button.focus_neighbor_top
+ ) == last_binding)
+ panel._begin_capture(KeyboardMouseMappingManagerType.ROLE_INTERACT)
+ var interact_key := InputEventKey.new()
+ interact_key.physical_keycode = KEY_F
+ interact_key.pressed = true
+ panel._input(interact_key)
+ assert(_has_physical_key(&"interact", KEY_F))
+ assert(
+ manager.get_binding_label(
+ KeyboardMouseMappingManagerType.ROLE_INTERACT
+ ) == "f"
+ )
+ assert(not panel.is_capturing())
+
+ var settings_panel := SettingsPanelScene.instantiate() as SettingsPanel
+ root.add_child(settings_panel)
+ await process_frame
+ var controller_bubble := settings_panel.get_node(
+ "%ControllerMapping"
+ ) as Button
+ var keyboard_bubble := settings_panel.get_node("%KeyboardMapping") as Button
+ assert(controller_bubble.text == "controller binds")
+ assert(keyboard_bubble.text == "keyboard binds")
+
+ assert(manager.reset_mapping())
+ assert(not manager.has_custom_mapping())
+ assert(not FileAccess.file_exists(
+ KeyboardMouseMappingManagerType.MAPPING_PATH
+ ))
+ assert(_has_physical_key(&"jump", KEY_SPACE))
+ assert(_has_mouse_button(&"fish_primary", MOUSE_BUTTON_LEFT))
+ assert(_has_physical_key(&"reel_alternate", KEY_QUOTELEFT))
+ assert(_event_count(&"reel_alternate", true) == 0)
+ assert(_has_physical_key(&"open_chat", KEY_T))
+ assert(_event_count(&"jump", true) == joypad_events_before)
+
+ var legacy_bindings: Dictionary = manager.get_active_bindings()
+ legacy_bindings.erase(str(
+ KeyboardMouseMappingManagerType.ROLE_ALTERNATE_REEL
+ ))
+ legacy_bindings[str(KeyboardMouseMappingManagerType.ROLE_JUMP)] = (
+ manager.binding_from_event(rebind_key)
+ )
+ var legacy_file := FileAccess.open(
+ KeyboardMouseMappingManagerType.MAPPING_PATH,
+ FileAccess.WRITE,
+ )
+ assert(legacy_file != null)
+ legacy_file.store_string(JSON.stringify({
+ "format_version": KeyboardMouseMappingManagerType.LEGACY_FORMAT_VERSION,
+ "bindings": legacy_bindings,
+ }))
+ legacy_file.close()
+ var migrated_manager := KeyboardMouseMappingManagerType.new()
+ root.add_child(migrated_manager)
+ await process_frame
+ assert(migrated_manager.has_custom_mapping())
+ assert(_has_physical_key(&"jump", KEY_R))
+ assert(_has_physical_key(&"reel_alternate", KEY_QUOTELEFT))
+ assert(migrated_manager.reset_mapping())
+
+ var fishing_spot := FishingSpotScene.instantiate() as FishingSpotType
+ root.add_child(fishing_spot)
+ await process_frame
+ var local_player := PlayerType.new()
+ fishing_spot.set("_local_player", local_player)
+ fishing_spot.set("_gameplay_input_enabled", true)
+ fishing_spot.state = FishingSpotType.FishingState.FIGHTING
+ var catch_controller := fishing_spot.get_node("%CatchController")
+ var catch_profile := CatchDifficultyProfileType.new()
+ catch_profile.barrier_count_min = 0
+ catch_profile.barrier_count_max = 0
+ catch_controller.start_encounter(catch_profile, 1.0, 1)
+ var alternate_echo := InputEventKey.new()
+ alternate_echo.physical_keycode = KEY_QUOTELEFT
+ alternate_echo.pressed = true
+ alternate_echo.echo = true
+ assert(alternate_echo.is_action(&"reel_alternate"))
+ fishing_spot._unhandled_input(alternate_echo)
+ assert(not bool(catch_controller.get("_reel_input_held")))
+ var alternate_key_press := InputEventKey.new()
+ alternate_key_press.physical_keycode = KEY_QUOTELEFT
+ alternate_key_press.pressed = true
+ assert(alternate_key_press.is_action(&"reel_alternate"))
+ fishing_spot._unhandled_input(alternate_key_press)
+ assert(bool(catch_controller.get("_reel_input_held")))
+ catch_controller.set_reel_input(false)
+ var alternate_press := InputEventAction.new()
+ alternate_press.action = &"reel_alternate"
+ alternate_press.pressed = true
+ fishing_spot._unhandled_input(alternate_press)
+ assert(bool(catch_controller.get("_reel_input_held")))
+ var alternate_release := InputEventAction.new()
+ alternate_release.action = &"reel_alternate"
+ alternate_release.pressed = false
+ fishing_spot._unhandled_input(alternate_release)
+ assert(not bool(catch_controller.get("_reel_input_held")))
+ fishing_spot.free()
+ local_player.free()
+
+ settings_panel.queue_free()
+ panel.queue_free()
+ migrated_manager.queue_free()
+ manager.queue_free()
+ print("Keyboard and mouse mapping validation: PASS")
+ quit(0)
+
+
+func _event_count(action: StringName, joypad: bool) -> int:
+ var result: int = 0
+ for event: InputEvent in InputMap.action_get_events(action):
+ if joypad == (
+ event is InputEventJoypadButton or event is InputEventJoypadMotion
+ ):
+ result += 1
+ return result
+
+
+func _has_physical_key(action: StringName, key: Key) -> bool:
+ for event: InputEvent in InputMap.action_get_events(action):
+ var key_event := event as InputEventKey
+ if key_event != null and key_event.physical_keycode == key:
+ return true
+ return false
+
+
+func _has_mouse_button(action: StringName, button: MouseButton) -> bool:
+ for event: InputEvent in InputMap.action_get_events(action):
+ var mouse_event := event as InputEventMouseButton
+ if mouse_event != null and mouse_event.button_index == button:
+ return true
+ return false
+
+
+func _has_label_text(parent: Node, expected: String) -> bool:
+ for node: Node in parent.find_children("", "Label", true, false):
+ if (node as Label).text == expected:
+ return true
+ return false
diff --git a/tests/keyboard_mouse_mapping_validation.gd.uid b/tests/keyboard_mouse_mapping_validation.gd.uid
new file mode 100644
index 0000000..4d21769
--- /dev/null
+++ b/tests/keyboard_mouse_mapping_validation.gd.uid
@@ -0,0 +1 @@
+uid://br6aeelrpa64k
diff --git a/tests/late_join_multiplayer_validation.gd b/tests/late_join_multiplayer_validation.gd
new file mode 100644
index 0000000..6ea544f
--- /dev/null
+++ b/tests/late_join_multiplayer_validation.gd
@@ -0,0 +1,369 @@
+extends SceneTree
+
+const MainScene: PackedScene = preload("res://main/main.tscn")
+const TEST_PORT: int = 18194
+const LATE_MESSAGE: String = "late join visibility check"
+const CLEAR_MESSAGE: String = "late join animation clear check"
+const ACTIVE_ACTION: StringName = &"draw"
+
+
+func _initialize() -> void:
+ call_deferred("_run")
+
+
+func _run() -> void:
+ var arguments: PackedStringArray = OS.get_cmdline_user_args()
+ if arguments.has("host"):
+ await _run_host()
+ elif arguments.has("first"):
+ await _run_first_client()
+ elif arguments.has("late"):
+ await _run_late_client()
+ else:
+ push_error("Late-join validation needs host, first, or late 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(bool(main.call(
+ "_apply_world",
+ WorldLayout.STARTER_ISLAND,
+ NetworkProtocol.DEFAULT_WORLD_SEED,
+ true,
+ )))
+ assert(session.set_host_world(
+ WorldLayout.STARTER_ISLAND,
+ NetworkProtocol.DEFAULT_WORLD_SEED,
+ ))
+ assert(session.start_private_host(TEST_PORT))
+ assert(save_manager.initialize_new_game(
+ NetworkProtocol.DEFAULT_WORLD_SEED,
+ WorldLayout.STARTER_ISLAND,
+ ))
+ main.call("_enter_gameplay")
+ for _frame: int in 4:
+ await physics_frame
+ assert(session.set_host_open(true))
+ assert(await _wait_for_registry_size(session, 3))
+ var spawn := main.get_node("%PlayerSpawnService") as PlayerSpawnService
+ assert(spawn.get_peer_ids().size() == 3)
+ var action_peer_id: int = await _wait_for_remote_action(
+ spawn,
+ ACTIVE_ACTION,
+ )
+ assert(action_peer_id > 1)
+ assert(await _wait_for_avatar_bool(
+ spawn,
+ action_peer_id,
+ &"_active_item_is_net",
+ true,
+ ))
+ assert(await _wait_for_avatar_sitting(spawn, action_peer_id, true))
+ assert(await _wait_for_avatar_action(
+ spawn,
+ action_peer_id,
+ &"",
+ ))
+ assert(await _wait_for_avatar_sitting(spawn, action_peer_id, false))
+ var chat := main.get_node("%NetworkChatService") as NetworkChatService
+ assert(await _wait_for_history_message(chat, CLEAR_MESSAGE))
+ print("Late-join multiplayer host validation: PASS")
+ await _cleanup(main, session)
+
+
+func _run_first_client() -> void:
+ var main: Node = await _create_initialized_main()
+ var session := await _join(main)
+ var profile := main.get_node("%NetworkProfileService") as NetworkProfileService
+ var preview: Dictionary = profile.get_persisted_appearance().duplicate(true)
+ preview["scale"] = 0.9
+ assert(profile.preview_appearance(preview))
+ var player := main.get("_player") as Player
+ assert(player != null)
+ var floor_deadline: int = Time.get_ticks_msec() + 5000
+ while Time.get_ticks_msec() < floor_deadline and not player.is_on_floor():
+ await physics_frame
+ assert(player.is_on_floor())
+ player.toggle_sitting()
+ assert(player.is_sitting())
+ assert(player.bag.add_item(&"crab_net"))
+ # Slot zero begins selected with the starter rod. Replacing that slot does
+ # not emit a selection change, so use the next slot to exercise the same
+ # equipped-item path a player uses in normal gameplay.
+ assert(player.hotbar.assign_item(1, &"crab_net"))
+ assert(player.hotbar.select_slot(1))
+ assert(await _wait_for_local_net(main, player))
+ assert(player.begin_animation_action(ACTIVE_ACTION))
+ await create_timer(0.5).timeout
+ assert(StringName(str(player.get("_animation_action_id"))) == ACTIVE_ACTION)
+ var chat := main.get_node("%NetworkChatService") as NetworkChatService
+ var deadline: int = Time.get_ticks_msec() + 15000
+ while Time.get_ticks_msec() < deadline:
+ await process_frame
+ if (
+ session.get_authenticated_peer_ids().size() == 3
+ and _history_contains(chat, LATE_MESSAGE)
+ ):
+ break
+ assert(session.get_authenticated_peer_ids().size() == 3)
+ assert(_history_contains(chat, LATE_MESSAGE))
+ var spawn := main.get_node("%PlayerSpawnService") as PlayerSpawnService
+ assert(spawn.get_peer_ids().size() == 3)
+ # Keep the action active until the late peer has joined and announced that
+ # it has received the lifecycle snapshot. This exercises client -> host ->
+ # existing client/late client replication, including the join-in-progress
+ # state carried by the spawn envelope.
+ await create_timer(1.0).timeout
+ player.end_animation_action()
+ player.toggle_sitting()
+ assert(not player.is_sitting())
+ assert(await _wait_for_history_message(chat, CLEAR_MESSAGE))
+ print("Late-join first-client validation: PASS")
+ await _cleanup(main, session)
+
+
+func _run_late_client() -> void:
+ var main: Node = await _create_initialized_main()
+ var session := await _join(main)
+ var deadline: int = Time.get_ticks_msec() + 8000
+ while (
+ Time.get_ticks_msec() < deadline
+ and session.get_authenticated_peer_ids().size() < 3
+ ):
+ await process_frame
+ assert(session.get_authenticated_peer_ids().size() == 3)
+ var spawn := main.get_node("%PlayerSpawnService") as PlayerSpawnService
+ assert(spawn.get_peer_ids().size() == 3)
+ var action_peer_id: int = await _wait_for_remote_action(
+ spawn,
+ ACTIVE_ACTION,
+ )
+ assert(action_peer_id > 1)
+ assert(await _wait_for_avatar_bool(
+ spawn,
+ action_peer_id,
+ &"_active_item_is_net",
+ true,
+ ))
+ assert(await _wait_for_avatar_sitting(spawn, action_peer_id, true))
+ var chat := main.get_node("%NetworkChatService") as NetworkChatService
+ assert(chat.send_local_message(LATE_MESSAGE))
+ assert(await _wait_for_avatar_action(
+ spawn,
+ action_peer_id,
+ &"",
+ ))
+ assert(await _wait_for_avatar_sitting(spawn, action_peer_id, false))
+ assert(chat.send_local_message(CLEAR_MESSAGE))
+ await create_timer(0.5).timeout
+ print("Late-join late-client validation: PASS")
+ await _cleanup(main, session)
+
+
+func _join(main: Node) -> NetworkSession:
+ main.call("_on_title_join_game_requested", "127.0.0.1:%d" % TEST_PORT)
+ var session := main.get_node("%NetworkSession") as NetworkSession
+ 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")):
+ return session
+ assert(false, "Client did not join in time.")
+ return session
+
+
+func _wait_for_registry_size(session: NetworkSession, expected: int) -> bool:
+ var deadline: int = Time.get_ticks_msec() + 25000
+ while Time.get_ticks_msec() < deadline:
+ await process_frame
+ if session.get_authenticated_peer_ids().size() == expected:
+ return true
+ return false
+
+
+func _wait_for_local_net(main: Node, player: Player) -> bool:
+ var deadline: int = Time.get_ticks_msec() + 10000
+ while Time.get_ticks_msec() < deadline:
+ # A joined client can finish its world transition one frame after the
+ # hotbar selection signal. Refresh through the production path until the
+ # ready fishing state accepts the equipped item.
+ main.call("_refresh_active_hotbar_item")
+ await process_frame
+ if bool(player.get("_active_item_is_net")):
+ return true
+ var fishing_spot: Node = main.get_node("%FishingSpot")
+ print(
+ "net equip wait timed out: ",
+ {
+ "fishing_state": int(fishing_spot.get("state")),
+ "selected_slot": player.hotbar.get_selected_slot(),
+ "selected_item": String(player.hotbar.get_selected_item_id()),
+ "owns_net": player.bag.owns_item(&"crab_net"),
+ },
+ )
+ return false
+
+
+func _history_contains(service: NetworkChatService, body: String) -> bool:
+ return service.get_history().any(
+ func(message: Dictionary) -> bool:
+ return str(message.get("body", "")) == body
+ )
+
+
+func _wait_for_history_message(
+ service: NetworkChatService,
+ body: String,
+) -> bool:
+ var deadline: int = Time.get_ticks_msec() + 10000
+ while Time.get_ticks_msec() < deadline:
+ await process_frame
+ if _history_contains(service, body):
+ return true
+ return false
+
+
+func _wait_for_remote_action(
+ spawn_service: PlayerSpawnService,
+ action_id: StringName,
+) -> int:
+ var deadline: int = Time.get_ticks_msec() + 10000
+ while Time.get_ticks_msec() < deadline:
+ await process_frame
+ for peer_id: int in spawn_service.get_peer_ids():
+ var avatar: Player = spawn_service.get_avatar(peer_id)
+ if (
+ avatar != null
+ and _observed_action_id(avatar) == action_id
+ ):
+ return peer_id
+ print(
+ "animation wait timed out: ",
+ _action_debug_snapshot(spawn_service),
+ )
+ return 0
+
+
+func _wait_for_avatar_action(
+ spawn_service: PlayerSpawnService,
+ peer_id: int,
+ action_id: StringName,
+) -> bool:
+ var deadline: int = Time.get_ticks_msec() + 10000
+ while Time.get_ticks_msec() < deadline:
+ await process_frame
+ var avatar: Player = spawn_service.get_avatar(peer_id)
+ if (
+ avatar != null
+ and _observed_action_id(avatar) == action_id
+ ):
+ return true
+ print(
+ "animation clear wait timed out: ",
+ _action_debug_snapshot(spawn_service),
+ )
+ return false
+
+
+func _wait_for_avatar_bool(
+ spawn_service: PlayerSpawnService,
+ peer_id: int,
+ property_name: StringName,
+ expected: bool,
+) -> bool:
+ var deadline: int = Time.get_ticks_msec() + 10000
+ while Time.get_ticks_msec() < deadline:
+ await process_frame
+ var avatar: Player = spawn_service.get_avatar(peer_id)
+ if avatar != null and bool(avatar.get(property_name)) == expected:
+ return true
+ return false
+
+
+func _wait_for_avatar_sitting(
+ spawn_service: PlayerSpawnService,
+ peer_id: int,
+ expected: bool,
+) -> bool:
+ var deadline: int = Time.get_ticks_msec() + 10000
+ while Time.get_ticks_msec() < deadline:
+ await process_frame
+ var avatar: Player = spawn_service.get_avatar(peer_id)
+ if avatar != null and avatar.is_sitting() == expected:
+ return true
+ var avatar: Player = spawn_service.get_avatar(peer_id)
+ if avatar != null:
+ print(
+ "sitting wait timed out: ",
+ {
+ "peer_id": peer_id,
+ "expected": expected,
+ "sitting": avatar.is_sitting(),
+ "sit_after_landing": bool(avatar.get("_sit_after_landing")),
+ "on_floor": avatar.is_on_floor(),
+ "position": avatar.global_position,
+ "velocity": avatar.velocity,
+ "authoritative": bool(avatar.get(
+ "_network_authoritative_simulation"
+ )),
+ "last_input": int(avatar.get(
+ "_last_network_input_sequence"
+ )),
+ },
+ )
+ return false
+
+
+func _observed_action_id(avatar: Player) -> StringName:
+ return StringName(str(avatar.get(
+ "_animation_action_id"
+ if bool(avatar.get("_network_authoritative_simulation"))
+ else "_network_target_animation_action_id"
+ )))
+
+
+func _action_debug_snapshot(spawn_service: PlayerSpawnService) -> Dictionary:
+ var result: Dictionary = {}
+ for peer_id: int in spawn_service.get_peer_ids():
+ var avatar: Player = spawn_service.get_avatar(peer_id)
+ if avatar == null:
+ continue
+ result[peer_id] = {
+ "authoritative": bool(avatar.get(
+ "_network_authoritative_simulation"
+ )),
+ "source": str(avatar.get("_animation_action_id")),
+ "target": str(avatar.get(
+ "_network_target_animation_action_id"
+ )),
+ "last_input": int(avatar.get("_last_network_input_sequence")),
+ }
+ return result
+
+
+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 _cleanup(main: Node, session: NetworkSession) -> void:
+ session.disconnect_session("")
+ main.queue_free()
+ for _frame: int in 4:
+ await process_frame
+ await create_timer(0.1).timeout
+ quit()
diff --git a/tests/late_join_multiplayer_validation.gd.uid b/tests/late_join_multiplayer_validation.gd.uid
new file mode 100644
index 0000000..5d33548
--- /dev/null
+++ b/tests/late_join_multiplayer_validation.gd.uid
@@ -0,0 +1 @@
+uid://dnuwxkoeeykt3
diff --git a/tests/light_performance_profile_validation.gd b/tests/light_performance_profile_validation.gd
new file mode 100644
index 0000000..6a654f1
--- /dev/null
+++ b/tests/light_performance_profile_validation.gd
@@ -0,0 +1,293 @@
+extends SceneTree
+
+const MainScene: PackedScene = preload("res://main/main.tscn")
+const RuntimePerformanceProfileType = preload(
+ "res://main/runtime_performance_profile.gd"
+)
+func _initialize() -> void:
+ call_deferred("_run")
+
+
+func _run() -> void:
+ _validate_profile_resolution()
+ OS.set_environment(
+ RuntimePerformanceProfileType.PROFILE_ENVIRONMENT_VARIABLE,
+ "light",
+ )
+ root.size = Vector2i(640, 480)
+ 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")))
+ assert(bool(main.call(
+ "_apply_world",
+ WorldLayout.GENERATED,
+ PlayerSaveManager.DEFAULT_WORLD_SEED,
+ true,
+ )))
+ _validate_main_profile(main)
+ _validate_minimal_weather(main)
+ _stop_audio_players(main)
+ main.queue_free()
+ for _frame: int in 8:
+ await process_frame
+ OS.unset_environment(
+ RuntimePerformanceProfileType.PROFILE_ENVIRONMENT_VARIABLE
+ )
+ var normal_main: Node = MainScene.instantiate()
+ root.add_child(normal_main)
+ for _frame: int in 4:
+ await process_frame
+ if not bool(normal_main.get("_application_initialized")):
+ normal_main.call("_activate_selected_data_path", "", true)
+ for _frame: int in 8:
+ await process_frame
+ assert(bool(normal_main.get("_application_initialized")))
+ assert(bool(normal_main.call(
+ "_apply_world",
+ WorldLayout.GENERATED,
+ PlayerSaveManager.DEFAULT_WORLD_SEED,
+ true,
+ )))
+ _validate_normal_profile(normal_main)
+ _validate_new_game_music_transition(normal_main)
+ _stop_audio_players(normal_main)
+ normal_main.queue_free()
+ for _frame: int in 8:
+ await process_frame
+ print("Light performance profile validation: PASS")
+ quit()
+
+
+func _validate_profile_resolution() -> void:
+ var normal := RuntimePerformanceProfileType.from_name(&"normal")
+ var light := RuntimePerformanceProfileType.from_name(&"light")
+ var legacy := RuntimePerformanceProfileType.from_name(&"", true)
+ assert(not normal.is_light())
+ assert(is_equal_approx(normal.get_world_render_scale(), 1.0))
+ assert(light.is_light())
+ assert(is_equal_approx(light.get_world_render_scale(), 0.375))
+ assert(legacy.is_light())
+
+
+func _validate_main_profile(main: Node) -> void:
+ var profile: RuntimePerformanceProfile = main.get("_performance_profile")
+ assert(profile != null and profile.is_light())
+ assert(is_equal_approx(root.scaling_3d_scale, 0.375))
+ var pixelation: Node = main.get_node("%WorldPixelationPostprocess")
+ assert(bool(pixelation.call("is_light_performance_profile")))
+ pixelation.call("set_gameplay_active", true)
+ var screen_grid := pixelation.get_node("ScreenGrid") as ColorRect
+ assert(screen_grid != null and not screen_grid.visible)
+
+ var pond := _first_fresh_water_visual(main)
+ var ocean := main.get_node(
+ "TestWorld/Regions/GeneratedWorldRegion/WaterBodies/OceanWater/VisualWater"
+ ) as MeshInstance3D
+ assert(not pond.visible)
+ assert(ocean.material_override is StandardMaterial3D)
+ assert(not ocean.material_override is ShaderMaterial)
+ _validate_ocean_fishing_coverage(main, ocean)
+ var region := main.get_node(
+ "TestWorld/Regions/GeneratedWorldRegion"
+ ) as GeneratedWorldRegion
+ assert(region != null)
+ assert(region.get_node("Decorations").get_child_count() > 0)
+
+ var title_background := main.get_node("%TitleBackground") as ColorRect
+ var title_material := title_background.material as ShaderMaterial
+ assert(title_material != null)
+ assert(not bool(title_material.get_shader_parameter(
+ "animation_enabled"
+ )))
+ for backdrop_name: StringName in [
+ &"PlayerMenuBackdrop",
+ &"ShopBackdrop",
+ ]:
+ var backdrop := main.get_node("%%%s" % backdrop_name) as ColorRect
+ var backdrop_material := backdrop.material as ShaderMaterial
+ assert(backdrop_material != null)
+ assert(not bool(backdrop_material.get_shader_parameter(
+ "animation_enabled"
+ )))
+ assert(
+ backdrop_material.get_shader_parameter(
+ "scroll_velocity_pixels"
+ ) == Vector2.ZERO
+ )
+ var game_ui := main.get_node("%GameUI") as GameUI
+ var title_screen := game_ui.get_title_screen()
+ assert(title_screen != null)
+ var player_menu := game_ui.get("_player_menu") as PlayerMenu
+ assert(player_menu != null)
+ assert(not player_menu.is_cooler_water_effect_enabled())
+ assert((main.get_node("%TitleMusic") as AudioStreamPlayer).stream != null)
+ assert((main.get_node("%NewGameMusic") as AudioStreamPlayer).stream != null)
+ assert((main.get_node("%DuskMusic") as AudioStreamPlayer).stream != null)
+ assert(
+ (main.get_node("%WavesAudio") as AudioStreamPlayer).stream == null
+ )
+ assert(
+ (main.get_node("RainAmbience") as AudioStreamPlayer).stream == null
+ )
+
+
+func _validate_normal_profile(main: Node) -> void:
+ var profile: RuntimePerformanceProfile = main.get("_performance_profile")
+ assert(profile != null and not profile.is_light())
+ assert(is_equal_approx(root.scaling_3d_scale, 1.0))
+ var pixelation: Node = main.get_node("%WorldPixelationPostprocess")
+ assert(not bool(pixelation.call("is_light_performance_profile")))
+ var pond := _first_fresh_water_visual(main)
+ var ocean := main.get_node(
+ "TestWorld/Regions/GeneratedWorldRegion/WaterBodies/OceanWater/VisualWater"
+ ) as MeshInstance3D
+ assert(not pond.visible)
+ assert(ocean.material_override is ShaderMaterial)
+ _validate_ocean_fishing_coverage(main, ocean)
+ var region := main.get_node(
+ "TestWorld/Regions/GeneratedWorldRegion"
+ ) as GeneratedWorldRegion
+ assert(region != null)
+ assert(region.get_node("Decorations").get_child_count() > 0)
+ var game_ui := main.get_node("%GameUI") as GameUI
+ var title_screen := game_ui.get_title_screen()
+ assert(title_screen != null)
+ var player_menu := game_ui.get("_player_menu") as PlayerMenu
+ assert(player_menu != null)
+ assert(player_menu.is_cooler_water_effect_enabled())
+
+ var title_background := main.get_node("%TitleBackground") as ColorRect
+ var title_material := title_background.material as ShaderMaterial
+ assert(title_material != null)
+ assert(bool(title_material.get_shader_parameter("animation_enabled")))
+ for backdrop_name: StringName in [
+ &"PlayerMenuBackdrop",
+ &"ShopBackdrop",
+ ]:
+ var backdrop := main.get_node("%%%s" % backdrop_name) as ColorRect
+ var backdrop_material := backdrop.material as ShaderMaterial
+ assert(backdrop_material != null)
+ assert(bool(backdrop_material.get_shader_parameter(
+ "animation_enabled"
+ )))
+ assert(
+ backdrop_material.get_shader_parameter(
+ "scroll_velocity_pixels"
+ ) != Vector2.ZERO
+ )
+ var visuals: WorldTimeVisualController = main.get_node(
+ "%WorldTimeVisualController"
+ )
+ var rain := visuals.get_node("LocalRain") as GPUParticles3D
+ assert(rain.amount == WorldTimeVisualController.RAIN_PARTICLE_AMOUNT)
+ assert(rain.fixed_fps == 30)
+ var clouds := visuals.get_node(
+ "LocalStormClouds"
+ ) as LocalStormCloudLayer
+ assert(clouds.get_patch_count() == 81)
+ assert(clouds.get_node_or_null("CloudField") is MultiMeshInstance3D)
+ assert(clouds.get_node_or_null("CloudCeiling") == null)
+ assert((main.get_node("%TitleMusic") as AudioStreamPlayer).stream != null)
+ assert((main.get_node("%NewGameMusic") as AudioStreamPlayer).stream != null)
+ assert((main.get_node("%DuskMusic") as AudioStreamPlayer).stream != null)
+ assert(
+ (main.get_node("%WavesAudio") as AudioStreamPlayer).stream != null
+ )
+ assert(
+ (main.get_node("RainAmbience") as AudioStreamPlayer).stream != null
+ )
+
+
+func _validate_new_game_music_transition(main: Node) -> void:
+ var title_music := main.get_node("%TitleMusic") as AudioStreamPlayer
+ var new_game_music := main.get_node("%NewGameMusic") as AudioStreamPlayer
+ assert(title_music.playing)
+ main.call("_start_new_game_music")
+ assert(not title_music.playing)
+ assert(new_game_music.playing)
+ main.call("_show_title_music", true)
+ assert(not new_game_music.playing)
+ assert(title_music.playing)
+
+
+func _first_fresh_water_visual(main: Node) -> MeshInstance3D:
+ var root := main.get_node(
+ "TestWorld/Regions/GeneratedWorldRegion/WaterBodies/FreshWaterBodies"
+ ) as Node3D
+ assert(root != null and root.get_child_count() > 0)
+ return root.get_child(0).get_node("VisualWater") as MeshInstance3D
+
+
+func _validate_ocean_fishing_coverage(
+ main: Node,
+ ocean: MeshInstance3D,
+) -> void:
+ var ocean_mesh := ocean.mesh as PlaneMesh
+ assert(ocean_mesh != null)
+ var shape_node := main.get_node(
+ "TestWorld/Regions/GeneratedWorldRegion/WaterBodies/OceanWater/"
+ + "FishingRegion/Shape"
+ ) as CollisionShape3D
+ assert(shape_node != null)
+ var fishing_shape := shape_node.shape as BoxShape3D
+ assert(fishing_shape != null)
+ assert(fishing_shape.size.is_equal_approx(Vector3(
+ ocean_mesh.size.x,
+ 4.0,
+ ocean_mesh.size.y,
+ )))
+
+
+func _stop_audio_players(root_node: Node) -> void:
+ if root_node is AudioStreamPlayer:
+ (root_node as AudioStreamPlayer).stop()
+ elif root_node is AudioStreamPlayer2D:
+ (root_node as AudioStreamPlayer2D).stop()
+ elif root_node is AudioStreamPlayer3D:
+ (root_node as AudioStreamPlayer3D).stop()
+ for child: Node in root_node.get_children():
+ _stop_audio_players(child)
+
+
+func _validate_minimal_weather(main: Node) -> void:
+ var visuals: WorldTimeVisualController = main.get_node(
+ "%WorldTimeVisualController"
+ )
+ var rain := visuals.get_node("LocalRain") as GPUParticles3D
+ assert(rain.amount == WorldTimeVisualController.LIGHT_RAIN_PARTICLE_AMOUNT)
+ assert(rain.fixed_fps == WorldTimeVisualController.LIGHT_RAIN_FIXED_FPS)
+ assert(rain.amount == 48)
+ assert(rain.fixed_fps == 8)
+ var clouds := visuals.get_node("LocalStormClouds") as LocalStormCloudLayer
+ assert(clouds.get_patch_count() == 1)
+ var ceiling := clouds.get_node("CloudCeiling") as MeshInstance3D
+ assert(ceiling != null and ceiling.mesh is PlaneMesh)
+ assert(ceiling.material_override == null)
+ assert((ceiling.mesh as PlaneMesh).material is StandardMaterial3D)
+
+ visuals.apply_weather_immediately(WorldWeatherService.Weather.RAINY)
+ assert(rain.emitting and rain.amount_ratio > 0.99)
+ assert(clouds.visible and clouds.get_storm_amount() > 0.87)
+ var cloud_material := (
+ (ceiling.mesh as PlaneMesh).material as StandardMaterial3D
+ )
+ assert(cloud_material.albedo_color.a < 0.65)
+ var test_world := main.get_node("TestWorld") as TestWorld
+ var runtime_environment := (
+ test_world.get_world_environment().environment as Environment
+ )
+ var sky_material := (
+ runtime_environment.sky.sky_material as ShaderMaterial
+ )
+ assert(is_zero_approx(float(
+ sky_material.get_shader_parameter("cloud_coverage")
+ )))
+ assert(is_zero_approx(float(
+ sky_material.get_shader_parameter("cloud_opacity")
+ )))
diff --git a/tests/light_performance_profile_validation.gd.uid b/tests/light_performance_profile_validation.gd.uid
new file mode 100644
index 0000000..3495021
--- /dev/null
+++ b/tests/light_performance_profile_validation.gd.uid
@@ -0,0 +1 @@
+uid://c6xqcslinxd1a
diff --git a/tests/logbook_runtime_validation.gd b/tests/logbook_runtime_validation.gd
index 77280ea..e33e551 100644
--- a/tests/logbook_runtime_validation.gd
+++ b/tests/logbook_runtime_validation.gd
@@ -2,6 +2,7 @@ extends SceneTree
const MainScene = preload("res://main/main.tscn")
const FishCatchType = preload("res://fish/fish_catch.gd")
+const MENU_TRANSITION_SETTLE_SECONDS := 3.0
func _initialize() -> void:
@@ -36,12 +37,16 @@ func _run() -> void:
var logbook := player_menu.get_node("%CatalogLogbook") as LogbookPage
var backdrop := main.get_node("%PlayerMenuBackdrop") as ColorRect
var hotbar := game_ui.get_node("%Hotbar") as Control
+ var fishing_spot := main.get("_fishing_spot") as FishingSpot
+ assert(logbook.get("_fishing_spot") == fishing_spot)
+ assert(logbook.get("_world_time") == main.get("_world_time"))
+ assert(logbook.get("_world_weather") == main.get("_world_weather"))
var shortcut := InputEventKey.new()
shortcut.pressed = true
shortcut.physical_keycode = KEY_L
assert(bool(player_menu.call("_handle_direct_page_shortcut", shortcut)))
- await create_timer(2.2).timeout
+ await create_timer(MENU_TRANSITION_SETTLE_SECONDS).timeout
assert(player_menu.visible)
assert(logbook.visible)
assert(backdrop.visible)
@@ -49,40 +54,73 @@ func _run() -> void:
assert(not hotbar.visible)
var entry_buttons: Dictionary = logbook.get("_entry_buttons")
- assert(entry_buttons.size() == 19)
+ assert(entry_buttons.size() == 108)
await _capture_if_requested("-unknown")
+ logbook.call("_set_index_open", true, false)
+ await process_frame
+ assert(bool(logbook.get("_index_open")))
+ assert((logbook.get("_index_body") as Control).visible)
+ await _capture_if_requested("-index")
+ logbook.set_compact_presentation(true)
+ await process_frame
+ await _capture_if_requested("-index-compact")
+ logbook.set_compact_presentation(false)
+ await process_frame
+ logbook.call("_set_index_open", false, false)
+ await process_frame
logbook.call(
"_select_category", WaterType.Type.FRESH_WATER
)
await create_timer(0.25).timeout
- assert((logbook.get("_entry_buttons") as Dictionary).size() == 19)
+ assert((logbook.get("_entry_buttons") as Dictionary).size() == 108)
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() == 34)
+ assert((logbook.get("_entry_buttons") as Dictionary).size() == 202)
logbook.call("_select_category", WaterType.Type.FRESH_WATER)
await create_timer(0.25).timeout
- assert((logbook.get("_entry_buttons") as Dictionary).size() == 19)
+ assert((logbook.get("_entry_buttons") as Dictionary).size() == 108)
player.collection_log.mark_discovered(&"bluegill")
await process_frame
logbook.call("_select_entry", &"bluegill", &"bluegill")
await create_timer(0.2).timeout
await _capture_if_requested("-known")
+ var bluegill := (
+ main.get("fish_catalog") as FishPool
+ ).get_fish_by_id(&"bluegill")
+ var bluegill_available_now := fishing_spot.is_species_available_now(bluegill)
+ var index_options := logbook.get("_index_options") as Dictionary
+ index_options["availability"] = (
+ LogbookCatalog.AvailabilityFilter.AVAILABLE_NOW
+ )
+ logbook.notify_availability_context_changed()
+ await process_frame
+ assert(
+ (logbook.get("_entry_buttons") as Dictionary).has(&"bluegill")
+ == bluegill_available_now
+ )
+ logbook.call("_reset_index")
+ await process_frame
assert(bool(player_menu.call("_handle_direct_page_shortcut", shortcut)))
- await create_timer(2.2).timeout
+ await create_timer(MENU_TRANSITION_SETTLE_SECONDS).timeout
assert(not player_menu.visible)
assert(bool(player_menu.call("_handle_direct_page_shortcut", shortcut)))
- await create_timer(2.2).timeout
+ await create_timer(MENU_TRANSITION_SETTLE_SECONDS).timeout
player_menu.call(
"_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 +128,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,9 +141,40 @@ 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() == 53)
+ assert(catalog.candidates.size() == 316)
+ var active_species := LogbookCatalog.ordered_species(catalog.candidates)
+ assert(active_species.size() == 314)
+ # Keep this runtime save fixture within maximum player capacity while the
+ # focused catalog validation exercises every active species individually.
+ var round_trip_species: Array[FishData] = []
+ for fish: FishData in active_species:
+ if round_trip_species.size() >= 59:
+ break
+ round_trip_species.append(fish)
+ for fish_id: StringName in [
+ &"catfish_blue",
+ &"catfish_channel",
+ &"catfish_flathead",
+ &"catfish_white",
+ ]:
+ var catfish: FishData = catalog.get_fish_by_id(fish_id)
+ if catfish not in round_trip_species:
+ round_trip_species.append(catfish)
+ assert(round_trip_species.size() == 63)
+ player.inventory_layout.restore_backpack_level(
+ PlayerInventoryLayout.MAX_BACKPACK_LEVEL
+ )
+ var home_state := main.get("_home_state") as PlayerHomeState
+ assert(home_state != null)
+ assert(home_state.upgrade_to_next_tier())
+ assert(
+ player.inventory_layout.get_backpack_level()
+ == PlayerInventoryLayout.MAX_BACKPACK_LEVEL
+ )
+ assert(player.cooler_capacity.get_capacity() == 18)
+ player.inventory.set_inventory_layout(null)
for index: int in 4:
- _add_test_catch(player, catalog.candidates[index])
+ _add_test_catch(player, round_trip_species[index])
assert(save_manager.save_now())
var no_catches: Array[FishCatch] = []
var no_discoveries: Array[StringName] = []
@@ -112,18 +183,21 @@ func _validate_save_round_trip(
assert(save_manager.load_player_data())
assert(player.inventory.get_all_catches().size() == 4)
for index: int in 4:
- var original_fish: FishData = catalog.candidates[index]
+ var original_fish: FishData = round_trip_species[index]
assert(player.inventory.get_count(original_fish.id) == 1)
assert(player.collection_log.has_discovered(original_fish.id))
- for index: int in range(4, catalog.candidates.size()):
- _add_test_catch(player, catalog.candidates[index])
+ for index: int in range(4, round_trip_species.size()):
+ _add_test_catch(player, round_trip_species[index])
assert(save_manager.save_now())
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() == 53)
- for fish: FishData in catalog.candidates:
+ assert(
+ player.inventory.get_all_catches().size()
+ == round_trip_species.size()
+ )
+ for fish: FishData in round_trip_species:
assert(player.inventory.get_count(fish.id) == 1)
assert(player.collection_log.has_discovered(fish.id))
for fish_id: StringName in [
@@ -142,6 +216,7 @@ func _validate_save_round_trip(
assert(catch_sprite != null)
assert(catch_sprite.texture == fish_catch.fish.display_texture)
player.end_catch_showcase(Callable(), true)
+ player.inventory.set_inventory_layout(player.inventory_layout)
func _add_test_catch(player: Player, fish: FishData) -> void:
@@ -161,13 +236,13 @@ func _add_test_catch(player: Player, fish: FishData) -> void:
func _capture_if_requested(suffix: String) -> void:
- if not OS.has_environment("NETFISHING_LOGBOOK_CAPTURE"):
+ if not OS.has_environment("straywild_LOGBOOK_CAPTURE"):
return
await process_frame
await process_frame
var image: Image = root.get_texture().get_image()
var path: String = (
- OS.get_environment("NETFISHING_LOGBOOK_CAPTURE")
+ OS.get_environment("straywild_LOGBOOK_CAPTURE")
+ suffix
+ ".png"
)
diff --git a/tests/logbook_validation.gd b/tests/logbook_validation.gd
index 38b7ade..67747a5 100644
--- a/tests/logbook_validation.gd
+++ b/tests/logbook_validation.gd
@@ -5,6 +5,7 @@ const FishInventoryType = preload("res://inventory/fish_inventory.gd")
const FishPoolType = preload("res://fish/fish_pool.gd")
const CollectionLogType = preload("res://collection/collection_log.gd")
const FishDataType = preload("res://fish/fish_data.gd")
+const FishQualityType = preload("res://fish/fish_quality.gd")
const LogbookPortraitType = preload(
"res://ui/components/logbook_portrait.gd"
)
@@ -16,17 +17,19 @@ const CatalogResource: FishPoolType = preload(
"res://fish/pools/fish_catalog.tres"
)
+var _query_available_id: StringName
+
func _initialize() -> void:
call_deferred("_run")
func _run() -> void:
- if OS.has_environment("NETFISHING_TEST_CREATE_ROOT"):
+ if OS.has_environment("straywild_TEST_CREATE_ROOT"):
var data_root := PlayerDataRoot.new()
root.add_child(data_root)
var result: Dictionary = data_root.create_unbound_root(
- OS.get_environment("NETFISHING_TEST_CREATE_ROOT")
+ OS.get_environment("straywild_TEST_CREATE_ROOT")
)
assert(bool(result.get("ok", false)))
data_root.queue_free()
@@ -34,131 +37,382 @@ func _run() -> void:
quit()
return
_validate_catalog()
+ _validate_catalog_query()
await _validate_page()
print("Logbook validation: PASS")
quit()
func _validate_catalog() -> void:
- var expected_ids: Array[StringName] = [
- &"bluegill",
- &"bass",
- &"carp",
- &"sunfish",
- &"catfish_blue",
- &"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(CatalogResource.candidates.size() == 316)
+ var ordered := LogbookCatalog.ordered_species(CatalogResource.candidates)
+ assert(ordered.size() == 314)
+ var previous_number: int = 0
+ var catalog_numbers: Dictionary[int, bool] = {}
+ for fish: FishDataType in CatalogResource.candidates:
assert(fish != null)
- assert(not LogbookCatalog.facts_for(fish.id).is_empty())
+ assert(fish.catalog_number > 0)
+ assert(not catalog_numbers.has(fish.catalog_number))
+ catalog_numbers[fish.catalog_number] = true
+ assert(not LogbookCatalog.facts_for(fish).is_empty())
assert(
- LogbookCatalog.facts_for(fish.id)
- == LogbookCatalog.facts_for(fish.id).to_lower()
+ LogbookCatalog.facts_for(fish)
+ == LogbookCatalog.facts_for(fish).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",
- &"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(
- LogbookCatalog.category_for(fish)
- == expected_category
- )
- assert(LogbookCatalog.catalog_number(fish.id) == index + 1)
+ assert(LogbookCatalog.facts_for(fish) != "unknown")
+ for fish: FishDataType in ordered:
+ assert(fish.active)
+ assert(fish.catalog_number > previous_number)
+ assert(LogbookCatalog.catalog_number(fish) == fish.catalog_number)
+ previous_number = fish.catalog_number
assert(
- LogbookCatalog.empty_state(WaterType.Type.SALT_WATER)
+ LogbookCatalog.empty_state(LogbookCatalog.Category.SALT_WATER)
== "No saltwater catches cataloged yet."
)
+func _validate_catalog_query() -> void:
+ var collection := CollectionLogType.new()
+ var inventory := FishInventoryType.new()
+ var fresh_species: Array[FishDataType] = []
+ for fish: FishDataType in LogbookCatalog.ordered_species(
+ CatalogResource.candidates
+ ):
+ if LogbookCatalog.category_for(fish) == LogbookCatalog.Category.FRESH_WATER:
+ fresh_species.append(fish)
+ assert(fresh_species.size() == 108)
+ var first := fresh_species[0]
+ var second := fresh_species[1]
+ var unknown := fresh_species[2]
+ for quality: int in FishQualityType.TIER_COUNT:
+ collection.record_catch(first.id, quality)
+ collection.record_catch(second.id, FishQualityType.Tier.BORING)
+ var held := _make_test_catch(first, true)
+ assert(inventory.add_catch(held))
+
+ var options := _default_query_options()
+ options["show"] = LogbookCatalog.DiscoveryFilter.CAUGHT
+ var filtered := LogbookCatalog.filtered_species(
+ CatalogResource.candidates,
+ LogbookCatalog.Category.FRESH_WATER,
+ collection,
+ inventory,
+ options,
+ )
+ assert(filtered.size() == 2)
+ assert(first in filtered and second in filtered)
+
+ options["show"] = LogbookCatalog.DiscoveryFilter.MISSING
+ filtered = LogbookCatalog.filtered_species(
+ CatalogResource.candidates,
+ LogbookCatalog.Category.FRESH_WATER,
+ collection,
+ inventory,
+ options,
+ )
+ assert(filtered.size() == fresh_species.size() - 2)
+ options["sort"] = LogbookCatalog.SortMode.CATALOG_NUMBER
+ options["descending"] = true
+ filtered = LogbookCatalog.filtered_species(
+ CatalogResource.candidates,
+ LogbookCatalog.Category.FRESH_WATER,
+ collection,
+ inventory,
+ options,
+ )
+ assert(filtered.front() == fresh_species.back())
+ assert(filtered.back() == unknown)
+
+ options["show"] = LogbookCatalog.DiscoveryFilter.INCOMPLETE
+ options["descending"] = false
+ filtered = LogbookCatalog.filtered_species(
+ CatalogResource.candidates,
+ LogbookCatalog.Category.FRESH_WATER,
+ collection,
+ inventory,
+ options,
+ )
+ assert(filtered == [second])
+
+ options = _default_query_options()
+ options["show"] = LogbookCatalog.DiscoveryFilter.CAUGHT
+ options["sort"] = LogbookCatalog.SortMode.CATCH_COUNT
+ options["descending"] = true
+ filtered = LogbookCatalog.filtered_species(
+ CatalogResource.candidates,
+ LogbookCatalog.Category.FRESH_WATER,
+ collection,
+ inventory,
+ options,
+ )
+ assert(filtered == [first, second])
+
+ options = _default_query_options()
+ options["search"] = unknown.display_name
+ filtered = LogbookCatalog.filtered_species(
+ CatalogResource.candidates,
+ LogbookCatalog.Category.FRESH_WATER,
+ collection,
+ inventory,
+ options,
+ )
+ assert(filtered.is_empty())
+ options["search"] = str(unknown.catalog_number)
+ filtered = LogbookCatalog.filtered_species(
+ CatalogResource.candidates,
+ LogbookCatalog.Category.FRESH_WATER,
+ collection,
+ inventory,
+ options,
+ )
+ assert(filtered == [unknown])
+
+ options = _default_query_options()
+ options["holding"] = LogbookCatalog.HoldingFilter.FAVORITED
+ filtered = LogbookCatalog.filtered_species(
+ CatalogResource.candidates,
+ LogbookCatalog.Category.FRESH_WATER,
+ collection,
+ inventory,
+ options,
+ )
+ assert(filtered == [first])
+ options["holding"] = LogbookCatalog.HoldingFilter.IN_COOLER
+ filtered = LogbookCatalog.filtered_species(
+ CatalogResource.candidates,
+ LogbookCatalog.Category.FRESH_WATER,
+ collection,
+ inventory,
+ options,
+ )
+ assert(filtered == [first])
+
+ options = _default_query_options()
+ options["show"] = LogbookCatalog.DiscoveryFilter.CAUGHT
+ options["group"] = String(first.collection_group)
+ filtered = LogbookCatalog.filtered_species(
+ CatalogResource.candidates,
+ LogbookCatalog.Category.FRESH_WATER,
+ collection,
+ inventory,
+ options,
+ )
+ assert(first in filtered)
+ for fish: FishDataType in filtered:
+ assert(fish.collection_group == first.collection_group)
+
+ options = _default_query_options()
+ options["show"] = LogbookCatalog.DiscoveryFilter.CAUGHT
+ options["rarity"] = (
+ int(first.rarity) + LogbookCatalog.RarityFilter.COMMON
+ )
+ filtered = LogbookCatalog.filtered_species(
+ CatalogResource.candidates,
+ LogbookCatalog.Category.FRESH_WATER,
+ collection,
+ inventory,
+ options,
+ )
+ assert(first in filtered)
+ for fish: FishDataType in filtered:
+ assert(fish.rarity == first.rarity)
+
+ options = _default_query_options()
+ options["show"] = LogbookCatalog.DiscoveryFilter.CAUGHT
+ options["method"] = (
+ int(first.collection_method) + LogbookCatalog.MethodFilter.FISHING
+ )
+ filtered = LogbookCatalog.filtered_species(
+ CatalogResource.candidates,
+ LogbookCatalog.Category.FRESH_WATER,
+ collection,
+ inventory,
+ options,
+ )
+ assert(first in filtered)
+ for fish: FishDataType in filtered:
+ assert(fish.collection_method == first.collection_method)
+
+ var first_season := -1
+ for season: int in 4:
+ if first.is_available_in_season(season):
+ first_season = season
+ break
+ assert(first_season >= 0)
+ options = _default_query_options()
+ options["show"] = LogbookCatalog.DiscoveryFilter.CAUGHT
+ options["season"] = (
+ first_season + LogbookCatalog.SeasonFilter.SPRING
+ )
+ filtered = LogbookCatalog.filtered_species(
+ CatalogResource.candidates,
+ LogbookCatalog.Category.FRESH_WATER,
+ collection,
+ inventory,
+ options,
+ )
+ assert(first in filtered)
+ for fish: FishDataType in filtered:
+ assert(fish.is_available_in_season(first_season))
+
+ var first_is_available_by_day := (
+ first.availability == null or first.availability.allow_day
+ )
+ options = _default_query_options()
+ options["show"] = LogbookCatalog.DiscoveryFilter.CAUGHT
+ options["time"] = (
+ LogbookCatalog.TimeFilter.DAY
+ if first_is_available_by_day
+ else LogbookCatalog.TimeFilter.NIGHT
+ )
+ filtered = LogbookCatalog.filtered_species(
+ CatalogResource.candidates,
+ LogbookCatalog.Category.FRESH_WATER,
+ collection,
+ inventory,
+ options,
+ )
+ assert(first in filtered)
+
+ options = _default_query_options()
+ options["show"] = LogbookCatalog.DiscoveryFilter.CAUGHT
+ options["sort"] = LogbookCatalog.SortMode.QUALITY_PROGRESS
+ options["descending"] = true
+ filtered = LogbookCatalog.filtered_species(
+ CatalogResource.candidates,
+ LogbookCatalog.Category.FRESH_WATER,
+ collection,
+ inventory,
+ options,
+ )
+ assert(filtered == [first, second])
+ for sort_mode: LogbookCatalog.SortMode in [
+ LogbookCatalog.SortMode.NAME,
+ LogbookCatalog.SortMode.RARITY,
+ LogbookCatalog.SortMode.VALUE,
+ LogbookCatalog.SortMode.WEIGHT,
+ ]:
+ options = _default_query_options()
+ options["sort"] = sort_mode
+ filtered = LogbookCatalog.filtered_species(
+ CatalogResource.candidates,
+ LogbookCatalog.Category.FRESH_WATER,
+ collection,
+ inventory,
+ options,
+ )
+ assert(filtered[0] in [first, second])
+ assert(filtered[1] in [first, second])
+ assert(filtered[0] != filtered[1])
+ assert(filtered[2] == unknown)
+
+ _query_available_id = second.id
+ options = _default_query_options()
+ options["availability"] = (
+ LogbookCatalog.AvailabilityFilter.AVAILABLE_NOW
+ )
+ filtered = LogbookCatalog.filtered_species(
+ CatalogResource.candidates,
+ LogbookCatalog.Category.FRESH_WATER,
+ collection,
+ inventory,
+ options,
+ Callable(self, "_query_species_available_now"),
+ )
+ assert(filtered == [second])
+
+ _query_available_id = unknown.id
+ options["show"] = LogbookCatalog.DiscoveryFilter.MISSING
+ options["rarity"] = (
+ int(unknown.rarity) + LogbookCatalog.RarityFilter.COMMON
+ )
+ filtered = LogbookCatalog.filtered_species(
+ CatalogResource.candidates,
+ LogbookCatalog.Category.FRESH_WATER,
+ collection,
+ inventory,
+ options,
+ Callable(self, "_query_species_available_now"),
+ )
+ assert(filtered == [unknown])
+
+ options = _default_query_options()
+ options["show"] = LogbookCatalog.DiscoveryFilter.MISSING
+ options["group"] = String(unknown.collection_group)
+ filtered = LogbookCatalog.filtered_species(
+ CatalogResource.candidates,
+ LogbookCatalog.Category.FRESH_WATER,
+ collection,
+ inventory,
+ options,
+ )
+ assert(unknown in filtered)
+ assert(
+ String(unknown.collection_group)
+ in LogbookCatalog.available_groups(
+ CatalogResource.candidates,
+ LogbookCatalog.Category.FRESH_WATER,
+ collection,
+ )
+ )
+
+ var unknown_is_available_by_day := (
+ unknown.availability == null or unknown.availability.allow_day
+ )
+ options = _default_query_options()
+ options["show"] = LogbookCatalog.DiscoveryFilter.MISSING
+ options["time"] = (
+ LogbookCatalog.TimeFilter.DAY
+ if unknown_is_available_by_day
+ else LogbookCatalog.TimeFilter.NIGHT
+ )
+ filtered = LogbookCatalog.filtered_species(
+ CatalogResource.candidates,
+ LogbookCatalog.Category.FRESH_WATER,
+ collection,
+ inventory,
+ options,
+ )
+ assert(unknown in filtered)
+ inventory.free()
+ collection.free()
+
+
+func _default_query_options() -> Dictionary:
+ return {
+ "search": "",
+ "show": LogbookCatalog.DiscoveryFilter.ALL,
+ "sort": LogbookCatalog.SortMode.CATALOG_NUMBER,
+ "descending": false,
+ "availability": LogbookCatalog.AvailabilityFilter.ANY,
+ "season": LogbookCatalog.SeasonFilter.ANY,
+ "time": LogbookCatalog.TimeFilter.ANY,
+ "rarity": LogbookCatalog.RarityFilter.ANY,
+ "method": LogbookCatalog.MethodFilter.ANY,
+ "holding": LogbookCatalog.HoldingFilter.ANY,
+ "group": "",
+ }
+
+
+func _make_test_catch(fish: FishDataType, favorited: bool) -> FishCatchType:
+ var fish_catch := FishCatchType.new()
+ fish_catch.fish = fish
+ fish_catch.fish_id = fish.id
+ fish_catch.weight_lb = fish.get_minimum_weight()
+ fish_catch.display_scale = fish.get_display_scale_for_weight(
+ fish_catch.weight_lb
+ )
+ fish_catch.quality = FishQualityType.Tier.BORING
+ fish_catch.sale_value = fish.get_sale_value_for_weight(fish_catch.weight_lb)
+ fish_catch.is_favorited = favorited
+ fish_catch.ensure_identity()
+ return fish_catch
+
+
+func _query_species_available_now(fish: FishDataType) -> bool:
+ return fish != null and fish.id == _query_available_id
+
+
func _validate_page() -> void:
var collection := CollectionLogType.new()
var inventory := FishInventoryType.new()
@@ -166,14 +420,126 @@ func _validate_page() -> void:
root.add_child(inventory)
var page := LogbookPageScene.instantiate() as LogbookPage
root.add_child(page)
+ page.set_anchors_and_offsets_preset(Control.PRESET_TOP_LEFT)
+ page.size = Vector2(1280.0, 720.0)
await process_frame
page.setup(collection, inventory, CatalogResource)
page.activate()
await process_frame
- assert(page.get("_category") == WaterType.Type.FRESH_WATER)
- assert((page.get("_catalog_grid") as GridContainer).columns == 5)
+ var outer_margin := page.get("_outer_margin") as MarginContainer
+ var book := page.get("_book") as Control
+ assert(outer_margin != null)
+ assert(book != null)
+ page.set_compact_presentation(true)
+ await process_frame
+ assert(outer_margin.get_theme_constant("margin_left") == 160)
+ assert(outer_margin.get_theme_constant("margin_right") == 160)
+ assert(outer_margin.get_theme_constant("margin_bottom") == 106)
+ assert(is_equal_approx(book.offset_top, 65.0))
+ page.call("_set_index_open", true, false)
+ await process_frame
+ _validate_index_content_bounds(page)
+ page.call("_set_index_open", false, false)
+ page.set_compact_presentation(false)
+ await process_frame
+ assert(outer_margin.get_theme_constant("margin_left") == 52)
+ assert(outer_margin.get_theme_constant("margin_right") == 52)
+ assert(outer_margin.get_theme_constant("margin_bottom") == 18)
+ assert(is_equal_approx(book.offset_top, 50.0))
+ assert(
+ page.get("_category") == LogbookCatalog.Category.FRESH_WATER
+ )
+ assert((page.get("_catalog_grid") as GridContainer).columns == 4)
+ var catalog_scroll := page.get("_catalog_scroll") as ScrollContainer
+ assert(catalog_scroll != null and catalog_scroll.follow_focus)
+ catalog_scroll.scroll_vertical = 37
+ await create_timer(0.2).timeout
+ assert(catalog_scroll.scroll_vertical == 37)
+ page.call(
+ "_set_controller_zone",
+ LogbookPage.ControllerZone.ENTRIES,
+ )
+ for _frame: int in 2:
+ await process_frame
+ var initial_entries: Array = (
+ page.get("_entry_buttons") as Dictionary
+ ).values()
+ assert(initial_entries.size() > 4)
+ var final_entry := initial_entries.back() as Button
+ final_entry.grab_focus()
+ for _frame: int in 3:
+ await process_frame
+ assert(catalog_scroll.scroll_vertical > 0)
+ page.reset_controller_zone()
+ for _frame: int in 2:
+ await process_frame
+ var category_tabs: Array = page.get("_category_tabs") as Array
+ var category_tab_categories: Array = (
+ page.get("_category_tab_categories") as Array
+ )
+ assert(category_tab_categories == [
+ LogbookCatalog.Category.FRESH_WATER,
+ LogbookCatalog.Category.SALT_WATER,
+ LogbookCatalog.Category.SHELLFISH,
+ LogbookCatalog.Category.OTHER,
+ ])
+ assert(category_tabs.size() == category_tab_categories.size())
+ assert(
+ LogbookCatalog.category_label(LogbookCatalog.Category.OTHER)
+ == "Insects"
+ )
+ for tab_node: Variant in category_tabs:
+ var category_tab := tab_node as Button
+ assert(category_tab.size == LogbookPage.LOGBOOK_TAB_SIZE)
+ var text_width: float = UtilityPageStyle.TuffyFont.get_string_size(
+ category_tab.text,
+ HORIZONTAL_ALIGNMENT_LEFT,
+ -1.0,
+ OrganizerTab.FONT_SIZE,
+ ).x
+ assert(
+ text_width + LogbookPage.LOGBOOK_TAB_TEXT_SIDE_INSET * 2.0
+ <= category_tab.size.x
+ )
+ assert(
+ (category_tabs[0] as Button).get_parent().position.x
+ == LogbookPage.LOGBOOK_TAB_LEFT_INSET
+ )
var initial_detail_body := page.get("_detail_body") as VBoxContainer
assert(not initial_detail_body.get_parent() is ScrollContainer)
+ var index_tab := page.get("_index_tab") as Button
+ var index_body := page.get("_index_body") as VBoxContainer
+ assert(index_tab != null and index_body != null)
+ assert(index_tab.size == LogbookPage.INDEX_TAB_SIZE)
+ assert(
+ not index_tab.get_global_rect().intersects(
+ (category_tabs.back() as Button).get_global_rect()
+ )
+ )
+ page.call("_set_index_open", true, false)
+ await process_frame
+ assert(index_body.visible)
+ assert(not initial_detail_body.visible)
+ assert((page.get("_index_option_buttons") as Dictionary).size() == 10)
+ _validate_index_content_bounds(page)
+ page.call(
+ "_cycle_index_option",
+ &"show",
+ 1,
+ )
+ await process_frame
+ assert((page.get("_entry_buttons") as Dictionary).is_empty())
+ assert(
+ (page.get("_empty_state") as Label).text
+ == "No entries match this index."
+ )
+ page.call("_reset_index")
+ await process_frame
+ assert((page.get("_entry_buttons") as Dictionary).size() == 108)
+ page.call("_set_index_open", false, false)
+ await process_frame
+ assert(not index_body.visible)
+ assert(initial_detail_body.visible)
assert(
page.call("_entry_label_text", "flathead catfish")
== "flathead\ncatfish"
@@ -181,21 +547,29 @@ func _validate_page() -> void:
var shared_material: Material
var silhouette_count: int = 0
- for category: WaterType.Type in [
- WaterType.Type.FRESH_WATER,
- WaterType.Type.SALT_WATER,
+ var target_silhouette_area: float = (
+ LogbookPage.CATALOG_PORTRAIT_SIZE.x
+ * LogbookPage.CATALOG_PORTRAIT_SIZE.y
+ * LogbookPortraitType.UNIFORM_FOOTPRINT_AREA_RATIO
+ )
+ for category: LogbookCatalog.Category in [
+ LogbookCatalog.Category.FRESH_WATER,
+ LogbookCatalog.Category.SALT_WATER,
]:
page.call("_select_category", category)
await create_timer(0.25).timeout
var entries: Dictionary = page.get("_entry_buttons")
- assert(entries.size() == (19 if category == WaterType.Type.FRESH_WATER else 34))
+ assert(
+ entries.size()
+ == (108 if category == LogbookCatalog.Category.FRESH_WATER else 202)
+ )
for fish: FishDataType in LogbookCatalog.ordered_species(
CatalogResource.candidates
):
if LogbookCatalog.category_for(fish) != category:
continue
var unknown_key := StringName(
- "unknown_%d" % LogbookCatalog.catalog_number(fish.id)
+ "unknown_%d" % LogbookCatalog.catalog_number(fish)
)
var entry := entries.get(unknown_key) as Button
assert(entry != null)
@@ -208,8 +582,15 @@ func _validate_page() -> void:
assert(portrait != null)
assert(portrait.source_texture == fish.display_texture)
assert(
- portrait.custom_minimum_size
- == LogbookPortraitType.ENTRY_FRAME_SIZE
+ portrait.custom_minimum_size.x
+ <= LogbookPage.CATALOG_PORTRAIT_SIZE.x
+ and portrait.custom_minimum_size.y
+ <= LogbookPage.CATALOG_PORTRAIT_SIZE.y
+ )
+ assert(portrait.get_parent() is CenterContainer)
+ assert(
+ (portrait.get_parent() as CenterContainer).custom_minimum_size
+ == LogbookPage.CATALOG_PORTRAIT_SIZE
)
assert(
portrait.expand_mode
@@ -225,6 +606,23 @@ func _validate_page() -> void:
else:
assert(portrait.material == shared_material)
assert(_texture_has_transparency(portrait.texture))
+ var silhouette_area: float = (
+ portrait.custom_minimum_size.x
+ * portrait.custom_minimum_size.y
+ )
+ assert(silhouette_area > 0.0)
+ assert(silhouette_area <= target_silhouette_area * 1.05)
+ assert(
+ silhouette_area >= target_silhouette_area * 0.9
+ or is_equal_approx(
+ portrait.custom_minimum_size.x,
+ LogbookPage.CATALOG_PORTRAIT_SIZE.x,
+ )
+ or is_equal_approx(
+ portrait.custom_minimum_size.y,
+ LogbookPage.CATALOG_PORTRAIT_SIZE.y,
+ )
+ )
silhouette_count += 1
for candidate: FishDataType in CatalogResource.candidates:
assert(not entry.text.contains(candidate.display_name))
@@ -238,25 +636,43 @@ func _validate_page() -> void:
candidate.display_name
)
)
- assert(silhouette_count == 53)
+ assert(silhouette_count == 310)
- page.call("_select_category", WaterType.Type.OTHER)
+ page.call("_select_category", LogbookCatalog.Category.SHELLFISH)
await create_timer(0.25).timeout
- assert((page.get("_entry_buttons") as Dictionary).is_empty())
+ assert((page.get("_entry_buttons") as Dictionary).size() == 3)
assert(
- (page.get("_empty_state") as Label).text
- == "No entries available."
+ (page.get("_entry_buttons") as Dictionary).has(&"unknown_3902")
)
- page.call("_select_category", WaterType.Type.FRESH_WATER)
+ assert(
+ (page.get("_entry_buttons") as Dictionary).has(&"unknown_3906")
+ )
+ assert(
+ (page.get("_entry_buttons") as Dictionary).has(&"unknown_6406")
+ )
+ page.call("_select_category", LogbookCatalog.Category.OTHER)
+ await create_timer(0.25).timeout
+ assert((page.get("_entry_buttons") as Dictionary).size() == 1)
+ assert(
+ (page.get("_entry_buttons") as Dictionary).has(&"unknown_8001")
+ )
+ page.call("_select_category", LogbookCatalog.Category.FRESH_WATER)
await create_timer(0.25).timeout
var bluegill = CatalogResource.get_fish_by_id(&"bluegill")
- page.call("_select_entry", &"unknown_1", StringName())
+ var bluegill_unknown := StringName(
+ "unknown_%d" % bluegill.catalog_number
+ )
+ page.call("_select_entry", bluegill_unknown, StringName())
assert(
(page.get("_entry_buttons") as Dictionary)
- .get(&"unknown_1") != null
+ .get(bluegill_unknown) != null
)
collection.mark_discovered(&"bluegill")
+ collection.mark_quality_discovered(
+ &"bluegill",
+ FishQualityType.Tier.BORING,
+ )
await process_frame
var entries: Dictionary = page.get("_entry_buttons")
var known := entries.get(&"bluegill") as Button
@@ -270,6 +686,11 @@ func _validate_page() -> void:
assert(known_portrait != null)
assert(known_portrait.source_texture == bluegill.display_texture)
assert(known_portrait.material == null)
+ assert(known_portrait.get_parent() is CenterContainer)
+ assert(
+ (known_portrait.get_parent() as CenterContainer).custom_minimum_size
+ == LogbookPage.CATALOG_PORTRAIT_SIZE
+ )
assert(known.button_pressed)
_validate_handwritten_logbook_font(page)
@@ -285,16 +706,71 @@ func _validate_page() -> void:
)
fish_catch.ensure_identity()
inventory.add_catch(fish_catch)
+ collection.record_catch(fish_catch.fish_id, fish_catch.quality)
page.call("_select_entry", &"bluegill", &"bluegill")
await process_frame
- assert(_detail_text(page).contains("number owned\n1"))
- assert(_detail_text(page).contains("number caught\nunknown"))
+ assert(not _detail_text(page).contains("number owned"))
+ assert(_detail_text(page).contains("number caught\n1"))
assert(_detail_text(page).contains("body of water\nfresh water"))
- assert(_detail_text(page).contains("catalog number\n#001"))
+ assert(
+ _detail_text(page).contains(
+ "catalog number\n#%03d" % bluegill.catalog_number
+ )
+ )
assert(_detail_text(page).contains("fish facts\n"))
- assert(_detail_text(page).contains(LogbookCatalog.facts_for(&"bluegill")))
+ assert(_detail_text(page).contains(LogbookCatalog.facts_for(bluegill)))
_validate_detail_field_fonts(page)
_validate_handwritten_numeric_scale(page)
+ _validate_detail_content_bounds(page)
+ var detail_buttons: Array = page.get("_detail_buttons") as Array
+ assert(detail_buttons.size() == 4)
+ var portrait_detail := detail_buttons[0] as Button
+ var facts_detail := detail_buttons[1] as Button
+ var quality_detail := detail_buttons[2] as Button
+ var stats_detail := detail_buttons[3] as Button
+ assert(
+ quality_detail.custom_minimum_size
+ == LogbookPage.DETAIL_QUALITY_SECTION_SIZE
+ )
+ assert(
+ stats_detail.custom_minimum_size
+ == LogbookPage.DETAIL_STATS_SECTION_SIZE
+ )
+ assert(
+ quality_detail.size_flags_horizontal
+ == Control.SIZE_SHRINK_CENTER
+ )
+ assert(
+ stats_detail.size_flags_horizontal
+ == Control.SIZE_SHRINK_CENTER
+ )
+ assert(
+ stats_detail.size_flags_vertical & Control.SIZE_EXPAND == 0
+ )
+ assert(
+ portrait_detail.get_node(portrait_detail.focus_neighbor_right)
+ == facts_detail
+ )
+ assert(
+ portrait_detail.get_node(portrait_detail.focus_neighbor_bottom)
+ == quality_detail
+ )
+ assert(
+ facts_detail.get_node(facts_detail.focus_neighbor_left)
+ == portrait_detail
+ )
+ assert(
+ facts_detail.get_node(facts_detail.focus_neighbor_bottom)
+ == quality_detail
+ )
+ assert(
+ quality_detail.get_node(quality_detail.focus_neighbor_bottom)
+ == stats_detail
+ )
+ assert(
+ stats_detail.get_node(stats_detail.focus_neighbor_top)
+ == quality_detail
+ )
var portrait_button := page.get("_detail_portrait_button") as Button
assert(portrait_button != null)
portrait_button.pressed.emit()
@@ -306,16 +782,81 @@ func _validate_page() -> void:
)
assert(overlay_artwork.source_texture == bluegill.display_texture)
assert(
- overlay_artwork.custom_minimum_size.x <= 860.0
- and overlay_artwork.custom_minimum_size.y <= 480.0
+ overlay_artwork.custom_minimum_size.x
+ <= LogbookPage.PORTRAIT_VIEW_MAX_SIZE.x
+ and overlay_artwork.custom_minimum_size.y
+ <= LogbookPage.PORTRAIT_VIEW_MAX_SIZE.y
)
+ var overlay_card := _find_overlay_card(portrait_overlay)
+ assert(overlay_card != null)
+ assert(overlay_card.size == LogbookPage.DETAIL_OVERLAY_CARD_SIZE)
+ assert(is_equal_approx(overlay_card.size.aspect(), 4.0 / 3.0))
(page.get("_portrait_overlay_backdrop") as Button).pressed.emit()
await process_frame
assert(not portrait_overlay.visible)
+ detail_buttons = page.get("_detail_buttons") as Array
+ assert(detail_buttons.size() == 4)
+ facts_detail = detail_buttons[1] as Button
+ facts_detail.pressed.emit()
+ await process_frame
+ var overlay_text := page.get("_portrait_overlay_text") as Label
+ assert(overlay_text.visible)
+ assert(
+ overlay_text.get_theme_font_size("font_size")
+ == LogbookPage.DETAIL_OVERLAY_TEXT_FONT_SIZE
+ )
+ assert(
+ overlay_text.horizontal_alignment
+ == HORIZONTAL_ALIGNMENT_LEFT
+ )
+ assert(
+ overlay_text.get_theme_font("font")
+ == UtilityPageStyle.TuffyFont
+ )
+ (page.get("_portrait_overlay_backdrop") as Button).pressed.emit()
+ await process_frame
+ quality_detail.pressed.emit()
+ await process_frame
+ var quality_view := page.get("_portrait_overlay_quality_view") as Control
+ assert(quality_view.visible)
+ var quality_text: String = _descendant_label_text(quality_view)
+ for quality: int in FishQualityType.TIER_COUNT:
+ assert(quality_text.contains(FishQualityType.display_name(quality)))
+ assert(quality_text.contains("●"))
+ assert(quality_text.contains("○"))
+ _validate_overlay_fonts(quality_view)
+ (page.get("_portrait_overlay_backdrop") as Button).pressed.emit()
+ await process_frame
+
+ stats_detail.pressed.emit()
+ await process_frame
+ var stats_view := page.get("_portrait_overlay_stats_view") as Control
+ assert(stats_view.visible)
+ var stats_text: String = _descendant_label_text(stats_view)
+ assert(stats_text.contains("catalog number"))
+ assert(stats_text.contains("number caught"))
+ assert(stats_text.contains("1"))
+ assert(stats_text.contains("seasons"))
+ assert(not stats_text.contains("number owned"))
+ _validate_overlay_fonts(stats_view)
+ (page.get("_portrait_overlay_backdrop") as Button).pressed.emit()
+ await process_frame
+
+ var live_index_options := page.get("_index_options") as Dictionary
+ live_index_options["holding"] = LogbookCatalog.HoldingFilter.IN_COOLER
+ page.call("_refresh_catalog")
+ await process_frame
+ assert(
+ (page.get("_entry_buttons") as Dictionary).keys()
+ == [&"bluegill"]
+ )
inventory.remove_catch_by_id(fish_catch.catch_id)
await process_frame
- assert(_detail_text(page).contains("number owned\n0"))
+ assert((page.get("_entry_buttons") as Dictionary).is_empty())
+ page.call("_reset_index")
+ await process_frame
+ assert(not _detail_text(page).contains("number owned"))
page.queue_free()
collection.queue_free()
inventory.queue_free()
@@ -329,6 +870,97 @@ func _detail_text(page: LogbookPage) -> String:
return "\n".join(values)
+func _validate_index_content_bounds(page: LogbookPage) -> void:
+ var body := page.get("_index_body") as VBoxContainer
+ assert(body != null and body.visible)
+ var right_page := body.get_parent() as Control
+ assert(right_page != null)
+ var page_rect := right_page.get_global_rect()
+ var body_rect := body.get_global_rect()
+ var protected_rect := Rect2(
+ page_rect.position + Vector2(
+ LogbookPage.INDEX_CONTENT_LEFT_INSET,
+ LogbookPage.INDEX_CONTENT_TOP_INSET,
+ ),
+ page_rect.size - Vector2(
+ LogbookPage.INDEX_CONTENT_LEFT_INSET
+ + LogbookPage.INDEX_CONTENT_RIGHT_INSET,
+ LogbookPage.INDEX_CONTENT_TOP_INSET
+ + LogbookPage.INDEX_CONTENT_BOTTOM_INSET,
+ ),
+ )
+ assert(
+ body_rect.position.x
+ >= page_rect.position.x + LogbookPage.INDEX_CONTENT_LEFT_INSET - 1.0
+ )
+ assert(
+ body_rect.end.x
+ <= page_rect.end.x - LogbookPage.INDEX_CONTENT_RIGHT_INSET + 1.0
+ )
+ assert(
+ body_rect.position.y >= page_rect.position.y,
+ "Index starts above the authored page: body=%s page=%s"
+ % [body_rect, page_rect],
+ )
+ assert(
+ body_rect.end.y <= page_rect.end.y,
+ "Index extends below the authored page: body=%s page=%s"
+ % [body_rect, page_rect],
+ )
+ var controls: Array = page.get("_index_controls") as Array
+ for value: Variant in controls:
+ var control := value as Control
+ if control != null and control.visible:
+ assert(
+ protected_rect.grow(2.0).encloses(control.get_global_rect()),
+ "Index control crossed the authored right-page boundary: %s"
+ % control.name,
+ )
+
+
+func _descendant_label_text(parent: Node) -> String:
+ var values: PackedStringArray = []
+ for node: Node in parent.find_children("*", "Label", true, false):
+ values.append((node as Label).text)
+ return "\n".join(values)
+
+
+func _validate_overlay_fonts(parent: Node) -> void:
+ for node: Node in parent.find_children("*", "Label", true, false):
+ assert(
+ (node as Label).get_theme_font("font")
+ == UtilityPageStyle.TuffyFont
+ )
+
+
+func _validate_detail_content_bounds(page: LogbookPage) -> void:
+ var detail_body := page.get("_detail_body") as VBoxContainer
+ var page_rect: Rect2 = (detail_body.get_parent() as Control).get_global_rect()
+ for node: Node in detail_body.find_children("*", "Label", true, false):
+ var label := node as Label
+ var label_rect: Rect2 = label.get_global_rect()
+ assert(
+ label_rect.position.y >= page_rect.position.y - 0.5,
+ "Label starts above logbook page: %s %s vs %s"
+ % [label.text, label_rect, page_rect],
+ )
+ assert(
+ label_rect.end.y <= page_rect.end.y + 0.5,
+ "Label ends below logbook page: %s %s vs %s"
+ % [label.text, label_rect, page_rect],
+ )
+
+
+func _find_overlay_card(parent: Node) -> PanelContainer:
+ for node: Node in parent.find_children(
+ "*", "PanelContainer", true, false
+ ):
+ var panel := node as PanelContainer
+ if panel.custom_minimum_size == LogbookPage.DETAIL_OVERLAY_CARD_SIZE:
+ return panel
+ return null
+
+
func _find_portrait(entry: Button) -> LogbookPortraitType:
var pending: Array[Node] = [entry]
while not pending.is_empty():
@@ -365,10 +997,10 @@ func _validate_handwritten_logbook_font(page: LogbookPage) -> void:
"rarity",
"body of water",
"time of day",
+ "seasons",
"weight range",
"value range",
"number caught",
- "number owned",
]:
continue
assert(
@@ -385,10 +1017,10 @@ func _validate_detail_field_fonts(page: LogbookPage) -> void:
"rarity",
"body of water",
"time of day",
+ "seasons",
"weight range",
"value range",
"number caught",
- "number owned",
]
for node: Node in detail_body.find_children("*", "Label", true, false):
var label := node as Label
@@ -413,7 +1045,7 @@ func _validate_handwritten_numeric_scale(page: LogbookPage) -> void:
var label := node as Label
if label.text == "common":
text_value = label
- elif label.text == "#001":
+ elif label.text.begins_with("#"):
numeric_value = label
assert(text_value != null)
assert(numeric_value != null)
diff --git a/tests/manual/terrain_chunk_generator/terrain_chunk_compatibility_report.gd b/tests/manual/terrain_chunk_generator/terrain_chunk_compatibility_report.gd
new file mode 100644
index 0000000..0d35543
--- /dev/null
+++ b/tests/manual/terrain_chunk_generator/terrain_chunk_compatibility_report.gd
@@ -0,0 +1,122 @@
+extends SceneTree
+
+const CATALOG: TerrainChunkCatalog = preload(
+ "res://world/generation/chunks/terrain_chunk_catalog.tres"
+)
+const BIOME_CATALOG: TerrainBiomeCatalog = preload(
+ "res://world/generation/biomes/terrain_biome_catalog.tres"
+)
+
+
+func _initialize() -> void:
+ call_deferred(&"_run")
+
+
+func _run() -> void:
+ var generator := _make_generator()
+ root.add_child(generator)
+ if not generator._prepare_catalog():
+ generator.free()
+ quit(1)
+ return
+ var variants: Array[TerrainChunkVariant] = []
+ variants.assign(generator._solver_variants)
+
+ print(
+ "Terrain chunk rotations: %d authored, %d distinct constraints"
+ % [generator._variants.size(), variants.size()]
+ )
+ for variant: TerrainChunkVariant in variants:
+ print("\n", variant.stable_key())
+ var ocean_edges := variant.rotated_edge_mask(
+ variant.definition.ocean_facing_edges
+ )
+ for edge_value: int in TerrainChunkTopology.Edge.values():
+ var edge := edge_value as TerrainChunkTopology.Edge
+ var matches := PackedStringArray()
+ for other: TerrainChunkVariant in variants:
+ var opposite := TerrainChunkTopology.opposite_edge(edge)
+ if generator._edges_are_compatible(
+ variant,
+ edge,
+ other,
+ opposite,
+ ):
+ matches.append(
+ "%s.%s"
+ % [
+ other.stable_key(),
+ TerrainChunkTopology.edge_name(opposite),
+ ]
+ )
+ print(
+ " %s%s [%d points]: %s"
+ % [
+ TerrainChunkTopology.edge_name(edge),
+ (
+ " [ocean]"
+ if (ocean_edges & (1 << edge_value)) != 0
+ else ""
+ ),
+ variant.profile(edge).points.size(),
+ ", ".join(matches) if not matches.is_empty() else "NONE",
+ ]
+ )
+
+ if generator.generate():
+ var summary := generator._build_summary()
+ print(
+ (
+ "\nSeeded diagnostic layout %s "
+ + "(%d backtracks, %d repeated edges):"
+ )
+ % [
+ str(summary["layout_fingerprint"]).substr(0, 12),
+ int(summary["backtracks"]),
+ int(summary["adjacent_repeat_edges"]),
+ ]
+ )
+ var keys := generator.placement_keys()
+ for row: int in generator.grid_size.y:
+ var cells := PackedStringArray()
+ for column: int in generator.grid_size.x:
+ cells.append(keys[row * generator.grid_size.x + column])
+ print(" ", " ".join(cells))
+ var biomes := TerrainBiomeAssigner.assign(
+ BIOME_CATALOG,
+ generator.placement_records(),
+ generator.generation_seed,
+ )
+ print("\nBiome layout:")
+ for row: int in generator.grid_size.y:
+ var cells := PackedStringArray()
+ for column: int in generator.grid_size.x:
+ var biome_id: StringName = biomes.get(
+ Vector2i(column, row),
+ &"",
+ )
+ cells.append(String(biome_id).trim_prefix("biome_"))
+ print(" ", " ".join(cells))
+ generator.free()
+ quit(0)
+
+
+func _make_generator() -> TerrainChunkGenerator:
+ var generator := TerrainChunkGenerator.new()
+ generator.catalog = CATALOG
+ generator.grid_size = Vector2i(9, 9)
+ generator.generation_seed = 13001
+ generator.generate_on_ready = false
+ generator.build_collision = false
+ generator.force_center_chunk_id = &"chunk_spawn"
+ generator.required_chunk_ids = PackedStringArray(
+ [
+ "chunk_spawn",
+ "chunk_0001",
+ "chunk_0002",
+ "chunk_0003",
+ "chunk_0004",
+ ]
+ )
+ generator.grass_sand_smoothing_enabled = true
+ return generator
diff --git a/tests/manual/terrain_chunk_generator/terrain_chunk_compatibility_report.gd.uid b/tests/manual/terrain_chunk_generator/terrain_chunk_compatibility_report.gd.uid
new file mode 100644
index 0000000..1da131f
--- /dev/null
+++ b/tests/manual/terrain_chunk_generator/terrain_chunk_compatibility_report.gd.uid
@@ -0,0 +1 @@
+uid://di4yaor4f3s4e
diff --git a/tests/manual/terrain_chunk_generator/terrain_chunk_generator_test.gd b/tests/manual/terrain_chunk_generator/terrain_chunk_generator_test.gd
new file mode 100644
index 0000000..657347f
--- /dev/null
+++ b/tests/manual/terrain_chunk_generator/terrain_chunk_generator_test.gd
@@ -0,0 +1,37 @@
+extends Node3D
+
+@onready var _generator: TerrainChunkGenerator = $TerrainChunkGenerator
+@onready var _status: Label = $Instructions/Status
+
+
+func _unhandled_input(event: InputEvent) -> void:
+ if event.is_action_pressed(&"ui_cancel"):
+ get_viewport().set_input_as_handled()
+ get_tree().quit()
+ return
+ var key_event := event as InputEventKey
+ if (
+ key_event != null
+ and key_event.pressed
+ and not key_event.echo
+ and key_event.physical_keycode == KEY_R
+ ):
+ get_viewport().set_input_as_handled()
+ _generator.generation_seed += 1
+ _generator.generate()
+
+
+func _on_generation_completed(summary: Dictionary) -> void:
+ _status.text = (
+ "Terrain generator diagnostic • seed %d • %d chunks • "
+ + "%d rotations / %d constraints • %d backtracks • "
+ + "%d repeated edges\nlayout %s • R: regenerate • Esc/B: close"
+ ) % [
+ int(summary["seed"]),
+ int(summary["chunk_count"]),
+ int(summary["variant_count"]),
+ int(summary["solver_variant_count"]),
+ int(summary["backtracks"]),
+ int(summary["adjacent_repeat_edges"]),
+ str(summary["layout_fingerprint"]).substr(0, 12),
+ ]
diff --git a/tests/manual/terrain_chunk_generator/terrain_chunk_generator_test.gd.uid b/tests/manual/terrain_chunk_generator/terrain_chunk_generator_test.gd.uid
new file mode 100644
index 0000000..4713f51
--- /dev/null
+++ b/tests/manual/terrain_chunk_generator/terrain_chunk_generator_test.gd.uid
@@ -0,0 +1 @@
+uid://0dmjswsm038h
diff --git a/tests/manual/terrain_chunk_generator/terrain_chunk_generator_test.tscn b/tests/manual/terrain_chunk_generator/terrain_chunk_generator_test.tscn
new file mode 100644
index 0000000..5c1e046
--- /dev/null
+++ b/tests/manual/terrain_chunk_generator/terrain_chunk_generator_test.tscn
@@ -0,0 +1,48 @@
+[gd_scene load_steps=7 format=3]
+
+[ext_resource type="Script" path="res://tests/manual/terrain_chunk_generator/terrain_chunk_generator_test.gd" id="1_test"]
+[ext_resource type="Script" path="res://world/generation/terrain_chunk_generator.gd" id="2_generator"]
+[ext_resource type="Resource" path="res://world/generation/chunks/terrain_chunk_catalog.tres" id="3_catalog"]
+[ext_resource type="PackedScene" path="res://player/player.tscn" id="4_player"]
+[ext_resource type="Environment" path="res://world/environment/straywild_environment.tres" id="5_environment"]
+
+[node name="TerrainChunkGeneratorTest" type="Node3D"]
+script = ExtResource("1_test")
+
+[node name="WorldEnvironment" type="WorldEnvironment" parent="."]
+environment = ExtResource("5_environment")
+
+[node name="Sun" type="DirectionalLight3D" parent="."]
+rotation_degrees = Vector3(-54, -32, 0)
+light_color = Color(0.88, 0.94, 0.96, 1)
+light_energy = 1.0
+shadow_enabled = true
+
+[node name="TerrainChunkGenerator" type="Node3D" parent="."]
+script = ExtResource("2_generator")
+catalog = ExtResource("3_catalog")
+grid_size = Vector2i(15, 13)
+generation_seed = 13001
+build_collision = true
+show_chunk_labels = false
+force_center_chunk_id = &"chunk_spawn"
+required_chunk_ids = PackedStringArray("chunk_spawn", "chunk_0001", "chunk_0002", "chunk_0003", "chunk_0004")
+grass_sand_smoothing_enabled = true
+elevated_cliff_feature_enabled = true
+
+[node name="Player" parent="." instance=ExtResource("4_player")]
+position = Vector3(0, 0.3, 0)
+
+[node name="Instructions" type="CanvasLayer" parent="."]
+layer = 20
+
+[node name="Status" type="Label" parent="Instructions"]
+offset_left = 18.0
+offset_top = 18.0
+offset_right = 930.0
+offset_bottom = 74.0
+text = "Terrain generator diagnostic\nR: regenerate • Esc/B: close"
+theme_override_colors/font_color = Color(0.76, 0.88, 0.9, 1)
+theme_override_font_sizes/font_size = 18
+
+[connection signal="generation_completed" from="TerrainChunkGenerator" to="." method="_on_generation_completed"]
diff --git a/tests/movement_multiplayer_validation.gd b/tests/movement_multiplayer_validation.gd
new file mode 100644
index 0000000..05a668d
--- /dev/null
+++ b/tests/movement_multiplayer_validation.gd
@@ -0,0 +1,908 @@
+extends SceneTree
+
+const MainScene: PackedScene = preload("res://main/main.tscn")
+const PlayerScene: PackedScene = preload("res://player/player.tscn")
+const TEST_PORT: int = 18171
+
+
+func _initialize() -> void:
+ call_deferred("_run")
+
+
+func _run() -> void:
+ await _validate_latency_smoothing()
+ var arguments: PackedStringArray = OS.get_cmdline_user_args()
+ if arguments.has("unit"):
+ print("Movement latency smoothing validation: PASS")
+ quit()
+ return
+ 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 _validate_latency_smoothing() -> void:
+ var avatar := PlayerScene.instantiate() as Player
+ root.add_child(avatar)
+ await process_frame
+ avatar.set_process(false)
+ avatar.set_physics_process(false)
+ _validate_compact_input_encoding()
+ _validate_compact_snapshot_encoding()
+ _validate_compact_animation_encoding()
+ _validate_animation_action_ordering(avatar)
+ _validate_transit_estimation()
+ await _validate_remote_snapshot_smoothing(avatar)
+ _validate_remote_locomotion_playback_recovery(avatar)
+ _validate_reliable_jump_intent(avatar)
+ _validate_airborne_sitting(avatar)
+ _validate_stale_input_expiry(avatar)
+ avatar.queue_free()
+ await process_frame
+
+
+func _validate_compact_input_encoding() -> void:
+ var input: Dictionary = _movement_input(
+ 12,
+ true,
+ true,
+ true,
+ &"strike",
+ 4,
+ true,
+ )
+ var encoded: Array = NetworkSession._encode_movement_input(input)
+ assert(encoded.size() == NetworkSession.MOVEMENT_INPUT_FIELD_COUNT)
+ assert(var_to_bytes(encoded).size() < var_to_bytes(input).size())
+ assert(NetworkSession._decode_movement_input(encoded) == input)
+ assert(NetworkSession._decode_movement_input(encoded.slice(0, 3)).is_empty())
+ var unknown_flags: Array = encoded.duplicate()
+ unknown_flags[3] = 1 << 12
+ assert(NetworkSession._decode_movement_input(unknown_flags).is_empty())
+
+
+func _validate_compact_snapshot_encoding() -> void:
+ var moving_snapshot: Dictionary = _network_snapshot(
+ Vector3.ZERO,
+ Vector3(4.5, 0.0, 0.0),
+ 1,
+ )
+ assert(NetworkSession.MOVEMENT_SNAPSHOT_BATCH_SIZE == 8)
+ assert(NetworkSession.OWNER_SNAPSHOT_DIVISOR == 3)
+ var encoded_snapshots: Array = []
+ for _peer: int in NetworkSession.MOVEMENT_SNAPSHOT_BATCH_SIZE:
+ encoded_snapshots.append(
+ NetworkSession._encode_movement_snapshot(moving_snapshot)
+ )
+ assert(var_to_bytes(encoded_snapshots).size() < 1200)
+ var expected_snapshot: Dictionary = moving_snapshot.duplicate(true)
+ expected_snapshot.erase("animation_state")
+ assert(
+ NetworkSession._decode_movement_snapshot(encoded_snapshots[0])
+ == expected_snapshot
+ )
+ assert(
+ NetworkSession._decode_movement_snapshot(
+ encoded_snapshots[0].slice(0, 4)
+ ).is_empty()
+ )
+ var invalid_snapshot_flags: Array = encoded_snapshots[0].duplicate()
+ invalid_snapshot_flags[5] = 1 << 12
+ assert(
+ NetworkSession._decode_movement_snapshot(
+ invalid_snapshot_flags
+ ).is_empty()
+ )
+
+
+func _validate_compact_animation_encoding() -> void:
+ var paused_state := NetworkPlayerAnimationProtocol.make_state(
+ NetworkPlayerAnimationProtocol.LOCOMOTION_IDLE,
+ true,
+ &"strike",
+ 7,
+ 0.5,
+ true,
+ )
+ var encoded: Array = NetworkSession._encode_movement_animation(
+ 2,
+ paused_state,
+ )
+ assert(encoded.size() == NetworkSession.MOVEMENT_ANIMATION_FIELD_COUNT)
+ var decoded: Dictionary = NetworkSession._decode_movement_animation(encoded)
+ assert(int(decoded.get("peer_id", 0)) == 2)
+ assert(Dictionary(decoded.get("state", {})) == paused_state)
+ var advanced_state: Dictionary = paused_state.duplicate(true)
+ advanced_state["action"]["elapsed"] = 0.75
+ assert(
+ NetworkSession._movement_animation_signature(advanced_state)
+ == NetworkSession._movement_animation_signature(paused_state)
+ )
+ var action: Dictionary = paused_state["action"]
+ var encoded_action: Array = (
+ NetworkSession._encode_movement_animation_action(action, true)
+ )
+ assert(
+ encoded_action.size()
+ == NetworkSession.MOVEMENT_ANIMATION_ACTION_FIELD_COUNT
+ )
+ var decoded_action: Dictionary = (
+ NetworkSession._decode_movement_animation_action(encoded_action)
+ )
+ assert(Dictionary(decoded_action.get("action", {})) == action)
+ assert(bool(decoded_action.get("sitting", false)))
+ assert(
+ NetworkSession._decode_movement_animation_action(
+ encoded_action.slice(0, 2)
+ ).is_empty()
+ )
+
+
+func _validate_airborne_sitting(avatar: Player) -> void:
+ avatar.reset_network_movement_state()
+ avatar.set_local_control(true)
+ avatar.set("_sit_after_landing", true)
+ assert(avatar.get_network_sitting_intent())
+ assert(not avatar.get_network_sitting_state())
+ assert(not bool(avatar.make_network_snapshot(2)["sitting"]))
+ assert(bool(avatar.capture_network_input(1)["sitting"]))
+ avatar.call("_queue_local_network_jump_intent")
+ var jumping_input: Dictionary = avatar.capture_network_input(2)
+ assert(bool(jumping_input["jump"]))
+ assert(not bool(jumping_input["sitting"]))
+ avatar.call("_cancel_sitting_for_jump")
+ assert(not bool(avatar.get("_sit_after_landing")))
+ assert(bool(avatar.get("_sitting_intent_pending")))
+ avatar.reset_network_movement_state()
+
+ # Even a malformed or stale reconciliation that marks an airborne avatar as
+ # seated must not bypass gravity and freeze it in place.
+ avatar.set("_sit_after_landing", false)
+ avatar.call("_set_sitting", true)
+ avatar.velocity = Vector3(0.0, 4.0, 0.0)
+ avatar.call("_simulate_movement_physics", 0.1)
+ assert(not avatar.is_sitting())
+ assert(bool(avatar.get("_sit_after_landing")))
+ assert(avatar.velocity.y < 4.0)
+ avatar.reset_network_movement_state()
+
+
+func _validate_animation_action_ordering(avatar: Player) -> void:
+ var draw := NetworkPlayerAnimationProtocol.make_action_state(
+ &"draw", 5, 0.2
+ )
+ avatar.apply_authoritative_network_animation_action(draw)
+ assert(StringName(str(avatar.get("_animation_action_id"))) == &"draw")
+ var cleared := NetworkPlayerAnimationProtocol.make_action_state(
+ &"", 6, 0.0
+ )
+ avatar.apply_authoritative_network_animation_action(cleared)
+ assert(StringName(str(avatar.get("_animation_action_id"))).is_empty())
+ avatar.apply_authoritative_network_animation_action(draw)
+ assert(StringName(str(avatar.get("_animation_action_id"))).is_empty())
+ avatar.apply_authoritative_network_animation_action(
+ NetworkPlayerAnimationProtocol.make_action_state(&"strike", 6, 0.0)
+ )
+ assert(StringName(str(avatar.get("_animation_action_id"))).is_empty())
+ var strike := NetworkPlayerAnimationProtocol.make_action_state(
+ &"strike", 7, 0.2
+ )
+ avatar.apply_authoritative_network_animation_action(strike)
+ avatar.apply_authoritative_network_animation_action(
+ NetworkPlayerAnimationProtocol.make_action_state(
+ &"strike", 8, 0.4, true
+ )
+ )
+ avatar.apply_authoritative_network_animation_action(strike)
+ assert(bool(avatar.get("_animation_action_paused")))
+ avatar.apply_authoritative_network_animation_action(
+ NetworkPlayerAnimationProtocol.make_action_state(&"", 9, 0.0)
+ )
+
+ avatar.configure_network_remote(false)
+ avatar.apply_network_animation_state(
+ NetworkPlayerAnimationProtocol.make_state(
+ NetworkPlayerAnimationProtocol.LOCOMOTION_IDLE,
+ true,
+ &"strike",
+ 9,
+ 0.4,
+ true,
+ )
+ )
+ avatar.apply_network_animation_state(
+ NetworkPlayerAnimationProtocol.make_state(
+ NetworkPlayerAnimationProtocol.LOCOMOTION_WALKING,
+ true,
+ &"draw",
+ 8,
+ 0.1,
+ )
+ )
+ assert(
+ StringName(str(avatar.get("_network_target_animation_action_id")))
+ == &"strike"
+ )
+ avatar.apply_network_animation_state(
+ NetworkPlayerAnimationProtocol.make_state(
+ NetworkPlayerAnimationProtocol.LOCOMOTION_IDLE,
+ true,
+ &"",
+ 10,
+ )
+ )
+
+
+func _validate_transit_estimation() -> void:
+ assert(is_equal_approx(
+ Player.resolve_local_prediction_transit_seconds(
+ 10,
+ 16,
+ 1.0 / 30.0,
+ 0.075,
+ ),
+ 0.075,
+ ))
+ # Six outstanding 30 Hz inputs span about 200 ms round trip. The fallback
+ # must use only the approximately 100 ms one-way half of that gap.
+ assert(is_equal_approx(
+ Player.resolve_local_prediction_transit_seconds(
+ 10,
+ 16,
+ 1.0 / 30.0,
+ -1.0,
+ ),
+ 0.1,
+ ))
+ assert(is_equal_approx(
+ Player.resolve_local_prediction_transit_seconds(
+ 1,
+ 100,
+ 1.0 / 30.0,
+ -1.0,
+ ),
+ Player.LOCAL_PREDICTION_EXTRAPOLATION_LIMIT_SECONDS,
+ ))
+
+
+func _validate_remote_snapshot_smoothing(avatar: Player) -> void:
+ avatar.configure_network_remote(false)
+ var moving_snapshot: Dictionary = _network_snapshot(
+ Vector3.ZERO,
+ Vector3(4.5, 0.0, 0.0),
+ 1,
+ )
+ avatar.push_network_snapshot(moving_snapshot, 0.1)
+ assert(is_equal_approx(avatar.global_position.x, 0.45))
+ assert(is_equal_approx(
+ float(avatar.get("_network_target_position").x),
+ 0.45,
+ ))
+ avatar.call("_update_network_interpolation", 0.12)
+ var before_delayed_snapshot: Vector3 = avatar.global_position
+ var delayed_snapshot: Dictionary = _network_snapshot(
+ Vector3(0.54, 0.0, 0.0),
+ Vector3(4.5, 0.0, 0.0),
+ 2,
+ )
+ avatar.push_network_snapshot(delayed_snapshot, 0.1)
+ # Snapshot receipt updates the target without teleporting a presented
+ # remote avatar, even after a jittered packet interval.
+ assert(avatar.global_position == before_delayed_snapshot)
+ assert(float(avatar.get("_network_snapshot_jitter")) > 0.0)
+ avatar.call("_update_network_interpolation", 1.0 / 60.0)
+ assert(
+ avatar.global_position.distance_to(before_delayed_snapshot) < 0.12
+ )
+ # Simulate a burst of dropped snapshots. Extrapolation must stop at the
+ # tight limit instead of allowing the remote avatar to run indefinitely.
+ for _step: int in 20:
+ avatar.call("_update_network_interpolation", 1.0 / 30.0)
+ assert(is_equal_approx(
+ float(avatar.get("_network_snapshot_age")),
+ Player.NETWORK_EXTRAPOLATION_LIMIT_SECONDS,
+ ))
+ var maximum_extrapolated_x: float = (
+ 0.54
+ + 4.5 * (0.1 + Player.NETWORK_EXTRAPOLATION_LIMIT_SECONDS)
+ )
+ assert(avatar.global_position.x <= maximum_extrapolated_x + 0.1)
+
+ # Reconciliation only applies after both simulations report a grounded body.
+ # Give the unit avatar a real floor so this exercises the production path.
+ var floor := StaticBody3D.new()
+ floor.collision_layer = 1
+ floor.collision_mask = 0
+ floor.position = Vector3(0.0, -0.1, 0.0)
+ var floor_shape := CollisionShape3D.new()
+ var floor_box := BoxShape3D.new()
+ floor_box.size = Vector3(100.0, 0.2, 100.0)
+ floor_shape.shape = floor_box
+ floor.add_child(floor_shape)
+ root.add_child(floor)
+ await physics_frame
+ avatar.set_local_control(true)
+ avatar.global_position = Vector3(0.8, 0.0, 0.0)
+ avatar.velocity = Vector3.DOWN
+ avatar.move_and_slide()
+ assert(avatar.is_on_floor())
+ avatar.apply_local_prediction_correction(
+ moving_snapshot,
+ 2,
+ 1.0 / 30.0,
+ 0.1,
+ 0.1,
+ )
+ # Ordinary host/client disagreement is expected while a packet is in flight.
+ # It must never tug the locally controlled body or camera around.
+ assert(is_equal_approx(avatar.global_position.x, 0.8))
+ assert(
+ int(avatar.get("_local_prediction_soft_corrections")) == 0
+ )
+ assert(
+ int(avatar.get("_local_prediction_hard_corrections")) == 0
+ )
+
+ # A larger but still plausible mismatch must persist across multiple audits
+ # before a small correction is allowed. Preserve both visible character and
+ # camera positions while the collision body catches up.
+ avatar.global_position = Vector3(3.0, 0.0, 0.0)
+ var visual_position: Vector3 = avatar.get_node("Visuals").global_position
+ var camera_position: Vector3 = avatar.get_node("CameraYaw").global_position
+ var drift_snapshot: Dictionary = _network_snapshot(
+ Vector3.ZERO,
+ Vector3.ZERO,
+ 20,
+ )
+ for _audit: int in 3:
+ avatar.apply_local_prediction_correction(
+ drift_snapshot,
+ 20,
+ 1.0 / 30.0,
+ 0.0,
+ 0.1,
+ )
+ assert(is_equal_approx(avatar.global_position.x, 3.0))
+ avatar.apply_local_prediction_correction(
+ drift_snapshot,
+ 20,
+ 1.0 / 30.0,
+ 0.0,
+ 0.1,
+ )
+ assert(avatar.global_position.x < 3.0)
+ assert(avatar.global_position.x >= 2.85 - 0.001)
+ assert(avatar.get_node("Visuals").global_position == visual_position)
+ assert(avatar.get_node("CameraYaw").global_position == camera_position)
+ assert(
+ int(avatar.get("_local_prediction_soft_corrections")) == 1
+ )
+
+ # Host and client jump timing naturally differs. An airborne authoritative
+ # snapshot must not drag the local capsule sideways or down before landing.
+ avatar.global_position = Vector3(3.0, 0.0, 0.0)
+ avatar.velocity = Vector3(0.0, 1.0, 0.0)
+ var airborne_snapshot: Dictionary = drift_snapshot.duplicate(true)
+ airborne_snapshot["grounded"] = false
+ for _audit: int in 6:
+ avatar.apply_local_prediction_correction(
+ airborne_snapshot,
+ 20,
+ 1.0 / 30.0,
+ 0.0,
+ 0.1,
+ )
+ assert(avatar.global_position.is_equal_approx(Vector3(3.0, 0.0, 0.0)))
+ assert(int(avatar.get("_local_prediction_soft_corrections")) == 1)
+ avatar.velocity = Vector3.ZERO
+
+ # The local body may reach its apex after the host has already landed. The
+ # local airborne state still owns presentation and must not be pulled toward
+ # the host until it has landed too.
+ avatar.global_position = Vector3(3.0, 2.0, 0.0)
+ avatar.velocity = Vector3.ZERO
+ avatar.move_and_slide()
+ assert(not avatar.is_on_floor())
+ for _audit: int in 6:
+ avatar.apply_local_prediction_correction(
+ drift_snapshot,
+ 20,
+ 1.0 / 30.0,
+ 0.0,
+ 0.1,
+ )
+ assert(avatar.global_position.is_equal_approx(Vector3(3.0, 2.0, 0.0)))
+ assert(int(avatar.get("_local_prediction_soft_corrections")) == 1)
+
+ # Genuine divergence still recovers immediately, as do the separate reliable
+ # teleport and water-recovery paths used by gameplay transitions.
+ avatar.global_position = Vector3(10.0, 0.0, 0.0)
+ avatar.apply_local_prediction_correction(
+ drift_snapshot,
+ 20,
+ 1.0 / 30.0,
+ 0.0,
+ 0.1,
+ )
+ assert(avatar.global_position == Vector3.ZERO)
+ assert(
+ int(avatar.get("_local_prediction_hard_corrections")) == 1
+ )
+
+ # Soft reconciliation must respect the same terrain collision as ordinary
+ # player movement instead of phasing the capsule through a solid obstacle.
+ var blocker := StaticBody3D.new()
+ blocker.collision_layer = 1
+ blocker.collision_mask = 0
+ blocker.position = Vector3(1.0, 1.0, 0.0)
+ var blocker_shape := CollisionShape3D.new()
+ var blocker_box := BoxShape3D.new()
+ blocker_box.size = Vector3(0.2, 4.0, 4.0)
+ blocker_shape.shape = blocker_box
+ blocker.add_child(blocker_shape)
+ root.add_child(blocker)
+ await physics_frame
+ avatar.global_position = Vector3.ZERO
+ avatar.velocity = Vector3.ZERO
+ avatar.call("_clear_local_reconciliation_offsets")
+ var blocked_visual_position: Vector3 = (
+ avatar.get_node("Visuals") as Node3D
+ ).global_position
+ var blocked_camera_position: Vector3 = (
+ avatar.get_node("CameraYaw") as Node3D
+ ).global_position
+ assert(bool(avatar.call(
+ "_apply_camera_safe_local_correction",
+ Vector3(2.0, 0.0, 0.0),
+ )))
+ assert(avatar.global_position.x > 0.0 and avatar.global_position.x < 0.6)
+ assert(
+ (avatar.get_node("Visuals") as Node3D).global_position
+ == blocked_visual_position
+ )
+ assert(
+ (avatar.get_node("CameraYaw") as Node3D).global_position
+ == blocked_camera_position
+ )
+ blocker.queue_free()
+ floor.queue_free()
+ await physics_frame
+
+
+func _validate_remote_locomotion_playback_recovery(avatar: Player) -> void:
+ avatar.configure_network_remote(false)
+ avatar.push_network_snapshot(
+ _network_snapshot(Vector3.ZERO, Vector3(4.5, 0.0, 0.0), 21)
+ )
+ avatar.call("_update_character_animation")
+ var animation_player := avatar.get_node(
+ "Visuals/CharacterRig/AnimationPlayer"
+ ) as AnimationPlayer
+ assert(animation_player.assigned_animation == &"running")
+ assert(animation_player.is_playing())
+
+ # Reproduce issue #111: locomotion replication still says RUNNING (and the
+ # dust therefore still emits), but the rig playback has fallen idle. The
+ # local presentation pass must repair that state without another packet.
+ animation_player.stop()
+ assert(not animation_player.is_playing())
+ assert(
+ int(avatar.get("_network_target_locomotion_state"))
+ == Player.LocomotionState.RUNNING
+ )
+ avatar.call("_update_character_animation")
+ assert(animation_player.assigned_animation == &"running")
+ assert(animation_player.is_playing())
+
+
+func _validate_reliable_jump_intent(avatar: Player) -> void:
+ avatar.set_local_control(true)
+ avatar.call("_queue_local_network_jump_intent")
+ var first: Dictionary = avatar.capture_network_input(20)
+ var repeated: Dictionary = avatar.capture_network_input(21)
+ assert(bool(first["jump"]))
+ assert(bool(repeated["jump"]))
+ assert(int(avatar.get("_local_network_jump_intent_sequence")) == 20)
+ avatar.apply_local_prediction_correction(
+ _network_snapshot(avatar.global_position, Vector3.ZERO, 20),
+ 20,
+ 1.0 / 30.0,
+ 0.0,
+ 0.1,
+ )
+ assert(not bool(avatar.capture_network_input(22)["jump"]))
+
+ avatar.configure_network_remote(true)
+ var first_host_jump: Dictionary = _movement_input(30, false, false)
+ first_host_jump["jump"] = true
+ avatar.apply_authoritative_network_input(first_host_jump)
+ assert(bool(avatar.get("_network_jump_pending")))
+ avatar.set("_network_jump_pending", false)
+ var repeated_host_jump: Dictionary = _movement_input(31, false, false)
+ repeated_host_jump["jump"] = true
+ avatar.apply_authoritative_network_input(repeated_host_jump)
+ assert(not bool(avatar.get("_network_jump_pending")))
+ avatar.apply_authoritative_network_input(
+ _movement_input(32, false, false)
+ )
+ var next_host_jump: Dictionary = _movement_input(33, false, false)
+ next_host_jump["jump"] = true
+ avatar.apply_authoritative_network_input(next_host_jump)
+ assert(bool(avatar.get("_network_jump_pending")))
+ avatar.reset_network_movement_state()
+ assert(not bool(avatar.get("_network_jump_pending")))
+ assert(not bool(avatar.get("_local_network_jump_intent_pending")))
+ assert(int(avatar.get("_last_network_input_sequence")) == 0)
+
+
+func _validate_stale_input_expiry(avatar: Player) -> void:
+ avatar.configure_network_remote(true)
+ var high_latency_timeout := (
+ Player.resolve_network_input_stale_timeout_seconds(400)
+ )
+ assert(high_latency_timeout > Player.NETWORK_INPUT_STALE_TIMEOUT_SECONDS)
+ avatar.apply_authoritative_network_input(
+ _movement_input(40, true),
+ high_latency_timeout,
+ )
+ assert((avatar.get("_network_axis") as Vector2).length_squared() > 0.0)
+ avatar.call(
+ "_update_network_input_freshness",
+ Player.NETWORK_INPUT_STALE_TIMEOUT_SECONDS + 0.01,
+ )
+ assert(not bool(avatar.get("_network_input_stale")))
+ avatar.call(
+ "_update_network_input_freshness",
+ high_latency_timeout,
+ )
+ assert((avatar.get("_network_axis") as Vector2) == Vector2.ZERO)
+ assert(not bool(avatar.get("_network_sprint")))
+ assert(bool(avatar.get("_network_input_stale")))
+ avatar.apply_authoritative_network_input(_movement_input(41, false))
+ assert(not bool(avatar.get("_network_input_stale")))
+ assert((avatar.get("_network_axis") as Vector2).length_squared() > 0.0)
+
+
+func _network_snapshot(
+ position: Vector3,
+ velocity: Vector3,
+ acknowledged_input: int,
+) -> Dictionary:
+ return {
+ "peer_id": 2,
+ "acknowledged_input": acknowledged_input,
+ "position": [position.x, position.y, position.z],
+ "velocity": [velocity.x, velocity.y, velocity.z],
+ "visual_yaw": 0.0,
+ "grounded": true,
+ "animation_state": NetworkPlayerAnimationProtocol.make_state(
+ NetworkPlayerAnimationProtocol.LOCOMOTION_RUNNING,
+ true,
+ ),
+ "sitting": false,
+ "casting": false,
+ }
+
+
+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(bool(main.call(
+ "_apply_world",
+ WorldLayout.STARTER_ISLAND,
+ NetworkProtocol.DEFAULT_WORLD_SEED,
+ true,
+ )))
+ assert(bool(main.call(
+ "_prepare_host_world",
+ WorldLayout.STARTER_ISLAND,
+ NetworkProtocol.DEFAULT_WORLD_SEED,
+ )))
+ assert(save_manager.initialize_new_game(
+ NetworkProtocol.DEFAULT_WORLD_SEED,
+ WorldLayout.STARTER_ISLAND,
+ ))
+ assert(session.start_private_host(TEST_PORT))
+ 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)
+ # Authentication completes before the client has necessarily created and
+ # begun observing the host avatar. Give its gameplay scene a bounded moment
+ # to settle before emitting the one-shot locomotion/action sequence.
+ await create_timer(3.0).timeout
+ var player := main.get("_player") as Player
+ var world := main.get_node("TestWorld") as TestWorld
+ player.configure_network_remote(true)
+ await _reset_locomotion_test_position(player, session, world)
+ var input_sequence: int = 1
+ input_sequence = await _sustain_locomotion_input(
+ player, input_sequence, 1.5, true
+ )
+ await _reset_locomotion_test_position(player, session, world)
+ input_sequence = await _sustain_locomotion_input(
+ player, input_sequence, 1.5, false
+ )
+ await _reset_locomotion_test_position(player, session, world)
+ input_sequence = await _sustain_locomotion_input(
+ player, input_sequence, 1.5, false, true, true
+ )
+ input_sequence = await _sustain_locomotion_input(
+ player, input_sequence, 1.5, false, false, true
+ )
+ player.apply_authoritative_network_input(
+ _movement_input(input_sequence, false, false, true, &"draw", 1)
+ )
+ input_sequence += 1
+ await create_timer(1.0).timeout
+ player.apply_authoritative_network_input(
+ _movement_input(
+ input_sequence, false, false, true, &"strike", 2, true
+ )
+ )
+ input_sequence += 1
+ await create_timer(2.0).timeout
+ player.apply_authoritative_network_input(
+ _movement_input(input_sequence, false, false, false, &"", 3)
+ )
+ var disconnect_deadline: int = Time.get_ticks_msec() + 15000
+ 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 = await _wait_for_avatar(spawn_service, 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_sneaking: bool = false
+ var saw_idle_sneak: bool = false
+ var saw_net_draw: bool = false
+ var saw_net_strike: bool = false
+ var saw_net_strike_paused: bool = false
+ var saw_animation_advance: bool = false
+ var saw_dust: bool = false
+ var observed_animations: Dictionary = {}
+ var observed_locomotion_states: Dictionary = {}
+ var maximum_sneak_speed: float = 0.0
+ var previous_animation: StringName = &""
+ var previous_animation_position: float = -1.0
+ var observation_deadline: int = Time.get_ticks_msec() + 22000
+ while Time.get_ticks_msec() < observation_deadline:
+ await process_frame
+ var current_animation: StringName = animation_player.current_animation
+ observed_animations[current_animation] = true
+ var target_locomotion_state: int = int(host_avatar.get(
+ "_network_target_locomotion_state"
+ ))
+ observed_locomotion_states[target_locomotion_state] = true
+ if target_locomotion_state == Player.LocomotionState.SNEAKING:
+ maximum_sneak_speed = maxf(
+ maximum_sneak_speed,
+ Vector2(
+ host_avatar.velocity.x, host_avatar.velocity.z
+ ).length(),
+ )
+ saw_running = saw_running or current_animation.begins_with("running")
+ saw_walking = saw_walking or current_animation.begins_with("walking")
+ saw_sneaking = saw_sneaking or current_animation == &"sneaking"
+ saw_idle_sneak = saw_idle_sneak or current_animation == &"idle_sneak"
+ saw_net_draw = saw_net_draw or current_animation == &"draw"
+ saw_net_strike = (
+ saw_net_strike
+ or current_animation == &"strike"
+ or animation_player.assigned_animation == &"strike"
+ )
+ saw_net_strike_paused = (
+ saw_net_strike_paused
+ or bool(host_avatar.get(
+ "_network_target_animation_action_paused"
+ ))
+ )
+ 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_sneaking
+ and saw_idle_sneak
+ and saw_net_draw
+ and saw_net_strike
+ and saw_net_strike_paused
+ and saw_animation_advance
+ and saw_dust
+ ):
+ break
+ var missing_observations: Array[String] = []
+ if not saw_running:
+ missing_observations.append("running")
+ if not saw_walking:
+ missing_observations.append("walking")
+ if not saw_sneaking:
+ missing_observations.append("sneaking")
+ if not saw_idle_sneak:
+ missing_observations.append("idle_sneak")
+ if not saw_net_draw:
+ missing_observations.append("net_draw")
+ if not saw_net_strike:
+ missing_observations.append("net_strike")
+ if not saw_net_strike_paused:
+ missing_observations.append("net_strike_paused")
+ if not saw_animation_advance:
+ missing_observations.append("animation_advance")
+ if not saw_dust:
+ missing_observations.append("sprint_dust")
+ if not missing_observations.is_empty():
+ push_error(
+ (
+ "Missing movement observations %s; observed animations: %s; "
+ + "locomotion states: %s; maximum sneak speed: %.3f"
+ ) % [
+ missing_observations,
+ observed_animations.keys(),
+ observed_locomotion_states.keys(),
+ maximum_sneak_speed,
+ ]
+ )
+ session.disconnect_session("")
+ main.queue_free()
+ for _frame: int in 4:
+ await process_frame
+ await create_timer(0.1).timeout
+ quit(1)
+ return
+ 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,
+ sneaking: bool = false,
+ action_id: StringName = &"",
+ action_sequence: int = 0,
+ action_paused: bool = false,
+) -> 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": sneaking,
+ "slow_walk": false,
+ "sitting": false,
+ "casting": false,
+ "animation_action": NetworkPlayerAnimationProtocol.make_action_state(
+ action_id,
+ action_sequence,
+ 0.0,
+ action_paused,
+ ),
+ }
+
+
+func _sustain_locomotion_input(
+ player: Player,
+ starting_sequence: int,
+ duration_seconds: float,
+ sprinting: bool,
+ moving: bool = true,
+ sneaking: bool = false,
+) -> int:
+ var sequence: int = starting_sequence
+ var deadline: int = (
+ Time.get_ticks_msec() + roundi(duration_seconds * 1000.0)
+ )
+ while Time.get_ticks_msec() < deadline:
+ player.apply_authoritative_network_input(
+ _movement_input(sequence, sprinting, moving, sneaking)
+ )
+ sequence += 1
+ await create_timer(0.1).timeout
+ return sequence
+
+
+func _reset_locomotion_test_position(
+ player: Player,
+ session: NetworkSession,
+ world: TestWorld,
+) -> void:
+ player.velocity = Vector3.ZERO
+ player.global_transform = world.get_player_spawn_transform()
+ session.publish_authoritative_teleport(1)
+ for _frame: int in 4:
+ await physics_frame
+
+
+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 _wait_for_avatar(
+ spawn_service: PlayerSpawnService,
+ peer_id: int,
+) -> Player:
+ var deadline: int = Time.get_ticks_msec() + 8000
+ while Time.get_ticks_msec() < deadline:
+ await process_frame
+ var avatar := spawn_service.get_avatar(peer_id) as Player
+ if avatar != null:
+ return avatar
+ return null
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/net_attachment_validation.gd b/tests/net_attachment_validation.gd
new file mode 100644
index 0000000..6b53c08
--- /dev/null
+++ b/tests/net_attachment_validation.gd
@@ -0,0 +1,163 @@
+extends SceneTree
+
+const NetAttachmentScene: PackedScene = preload(
+ "res://player/net_attachment.tscn"
+)
+const FishingRodAttachmentScene: PackedScene = preload(
+ "res://player/fishing_rod_attachment.tscn"
+)
+
+var failures: Array[String] = []
+
+
+func _initialize() -> void:
+ call_deferred("_run")
+
+
+func _run() -> void:
+ var host_skeleton := Skeleton3D.new()
+ host_skeleton.name = "HostSkeleton"
+ host_skeleton.add_bone("rod_socket")
+ host_skeleton.add_bone("rod_socket.001")
+ host_skeleton.set_bone_parent(1, 0)
+ host_skeleton.set_bone_rest(
+ 1,
+ Transform3D(
+ Basis(Quaternion(Vector3.UP, Vector3.RIGHT)),
+ Vector3(0.0, 0.25, 0.0),
+ ),
+ )
+ root.add_child(host_skeleton)
+
+ var attachment := NetAttachmentScene.instantiate() as BoneAttachment3D
+ host_skeleton.add_child(attachment)
+ await process_frame
+ await process_frame
+
+ _check(
+ attachment.bone_name == "rod_socket",
+ "Attachment moved off rod_socket.",
+ )
+ var catching_net := attachment.get_node_or_null("CatchingNet") as Node3D
+ _check(catching_net != null, "CatchingNet presentation root is missing.")
+ if catching_net == null:
+ _finish(host_skeleton)
+ return
+ _check(not catching_net.visible, "Net must start hidden.")
+ _check(
+ catching_net.position.is_zero_approx(),
+ "Net direction alignment moved its hand anchor.",
+ )
+ _check(
+ catching_net.basis.y.is_equal_approx(Vector3.UP),
+ "Net working axis no longer follows rod_socket.",
+ )
+ _check(
+ catching_net.basis.z.is_equal_approx(Vector3.RIGHT),
+ "Net rear face did not follow the socket direction guide.",
+ )
+
+ var model := catching_net.get_node_or_null("ModelMount/NetModel") as Node3D
+ _check(model != null, "Imported net model is missing.")
+ if model == null:
+ _finish(host_skeleton)
+ return
+ var skeleton := model.find_child("Skeleton3D", true, false) as Skeleton3D
+ _check(skeleton != null, "Imported net Skeleton3D is missing.")
+ if skeleton == null:
+ _finish(host_skeleton)
+ return
+ _check(
+ skeleton.find_bone("net_root") >= 0,
+ "Imported net_root bone is missing.",
+ )
+ _check(
+ skeleton.find_bone("net_rim") >= 0,
+ "Imported net_rim bone is missing.",
+ )
+ _check(
+ skeleton.find_bone("net_mid") >= 0,
+ "Imported net_mid bone is missing.",
+ )
+ _check(
+ skeleton.find_bone("net_tip") >= 0,
+ "Imported net_tip bone is missing.",
+ )
+
+ var spring := skeleton.get_node_or_null("NetSpring") as SpringBoneSimulator3D
+ _check(spring != null, "Net spring simulator was not created.")
+ if spring != null:
+ _check(spring.get_setting_count() == 1, "Net spring settings changed.")
+ _check(
+ spring.get_root_bone_name(0) == &"net_mid",
+ "Net spring root must remain net_mid.",
+ )
+ _check(
+ spring.get_end_bone_name(0) == &"net_tip",
+ "Net spring end must remain net_tip.",
+ )
+ _check(not spring.active, "Hidden net spring must be suspended.")
+ catching_net.visible = true
+ await process_frame
+ await process_frame
+ _check(spring.active, "Visible net spring must be active.")
+ _check(spring.get_joint_count(0) == 2, "Net spring chain is incomplete.")
+ _check(
+ not attachment.get_impact_world_position().is_equal_approx(
+ catching_net.global_position
+ ),
+ "Net impact position did not follow the authored rim bone.",
+ )
+ catching_net.visible = false
+ await process_frame
+ _check(not spring.active, "Hidden net spring did not suspend.")
+
+ for child: Node in model.find_children("*", "MeshInstance3D", true, false):
+ var mesh_instance := child as MeshInstance3D
+ if mesh_instance.mesh == null:
+ continue
+ for surface_index: int in mesh_instance.mesh.get_surface_count():
+ var material := mesh_instance.mesh.surface_get_material(surface_index)
+ if material is BaseMaterial3D:
+ _check(
+ (material as BaseMaterial3D).texture_filter
+ == BaseMaterial3D.TEXTURE_FILTER_NEAREST,
+ "Net material does not use nearest texture sampling.",
+ )
+
+ var rod_attachment := (
+ FishingRodAttachmentScene.instantiate() as BoneAttachment3D
+ )
+ host_skeleton.add_child(rod_attachment)
+ await process_frame
+ var fishing_rod := rod_attachment.get_node("FishingRod") as Node3D
+ _check(
+ fishing_rod.position.is_zero_approx(),
+ "Rod direction alignment moved its hand anchor.",
+ )
+ _check(
+ fishing_rod.basis.y.is_equal_approx(Vector3.UP),
+ "Rod working axis no longer follows rod_socket.",
+ )
+ _check(
+ fishing_rod.basis.z.is_equal_approx(Vector3.RIGHT),
+ "Rod rear face did not follow the socket direction guide.",
+ )
+
+ _finish(host_skeleton)
+
+
+func _finish(host_skeleton: Skeleton3D) -> void:
+ host_skeleton.queue_free()
+ if failures.is_empty():
+ print("Net attachment validation: PASS")
+ quit(0)
+ return
+ for failure: String in failures:
+ printerr("Net attachment validation: ", failure)
+ quit(1)
+
+
+func _check(condition: bool, message: String) -> void:
+ if not condition:
+ failures.append(message)
diff --git a/tests/net_attachment_validation.gd.uid b/tests/net_attachment_validation.gd.uid
new file mode 100644
index 0000000..5f968d1
--- /dev/null
+++ b/tests/net_attachment_validation.gd.uid
@@ -0,0 +1 @@
+uid://clnr8ofu1fqw5
diff --git a/tests/network_chat_protocol_validation.gd b/tests/network_chat_protocol_validation.gd
new file mode 100644
index 0000000..9368189
--- /dev/null
+++ b/tests/network_chat_protocol_validation.gd
@@ -0,0 +1,55 @@
+extends SceneTree
+
+const EDGE_PADDING: String = " \t\n ⠀"
+
+
+func _initialize() -> void:
+ call_deferred("_validate_protocol")
+
+
+func _validate_protocol() -> void:
+ _validate_type_and_empty_handling()
+ _validate_message_cleanup()
+ _validate_protocol_limits()
+ print("Network chat protocol validation: PASS")
+ quit()
+
+
+func _validate_type_and_empty_handling() -> void:
+ assert(NetworkChatProtocol.sanitize_body(null) == "")
+ assert(NetworkChatProtocol.sanitize_body(42) == "")
+ assert(NetworkChatProtocol.sanitize_body(EDGE_PADDING) == "")
+ assert(NetworkChatProtocol.sanitize_body("\u0001") == "")
+
+
+func _validate_message_cleanup() -> void:
+ assert(NetworkChatProtocol.sanitize_body(" hello ") == "hello")
+ assert(NetworkChatProtocol.sanitize_body("a\tb\nc") == "a b c")
+ assert(NetworkChatProtocol.sanitize_body("word word") == "word word")
+ assert(NetworkChatProtocol.sanitize_body(" !!! ") == "!!!")
+ assert(
+ NetworkChatProtocol.sanitize_body(
+ EDGE_PADDING + "visible" + EDGE_PADDING
+ ) == "visible"
+ )
+
+
+func _validate_protocol_limits() -> void:
+ var maximum_body: String = "a".repeat(
+ NetworkChatProtocol.MAX_VISIBLE_CHARACTERS
+ )
+ assert(NetworkChatProtocol.sanitize_body(maximum_body) == maximum_body)
+ assert(
+ NetworkChatProtocol.sanitize_body(maximum_body + "a") == ""
+ )
+ var maximum_utf8_body: String = "🐟".repeat(
+ NetworkChatProtocol.MAX_VISIBLE_CHARACTERS
+ )
+ assert(
+ maximum_utf8_body.to_utf8_buffer().size()
+ == NetworkChatProtocol.MAX_UTF8_BYTES
+ )
+ assert(
+ NetworkChatProtocol.sanitize_body(maximum_utf8_body)
+ == maximum_utf8_body
+ )
diff --git a/tests/network_chat_protocol_validation.gd.uid b/tests/network_chat_protocol_validation.gd.uid
new file mode 100644
index 0000000..5e98b42
--- /dev/null
+++ b/tests/network_chat_protocol_validation.gd.uid
@@ -0,0 +1 @@
+uid://bj7nq2k6q5c7f
diff --git a/tests/network_player_animation_protocol_validation.gd b/tests/network_player_animation_protocol_validation.gd
new file mode 100644
index 0000000..66a3ec3
--- /dev/null
+++ b/tests/network_player_animation_protocol_validation.gd
@@ -0,0 +1,65 @@
+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 paused_action := NetworkPlayerAnimationProtocol.make_action_state(
+ &"strike",
+ 18,
+ 0.72,
+ true,
+ )
+ assert(NetworkPlayerAnimationProtocol.validate_action_state(paused_action))
+ assert(bool(paused_action["paused"]))
+ var future_locomotion := NetworkPlayerAnimationProtocol.make_state(
+ &"swimming_fast",
+ false,
+ )
+ assert(NetworkPlayerAnimationProtocol.validate_state(future_locomotion))
+ var sneaking := NetworkPlayerAnimationProtocol.make_state(
+ NetworkPlayerAnimationProtocol.LOCOMOTION_SNEAKING,
+ true,
+ )
+ assert(NetworkPlayerAnimationProtocol.validate_state(sneaking))
+ 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))
+ var invalid_paused: Dictionary = emote.duplicate(true)
+ invalid_paused["action"]["paused"] = "yes"
+ assert(not NetworkPlayerAnimationProtocol.validate_state(invalid_paused))
+ 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
index e794cda..a685c65 100644
--- a/tests/on_screen_keyboard_validation.gd
+++ b/tests/on_screen_keyboard_validation.gd
@@ -1,11 +1,44 @@
extends SceneTree
const KeyboardType = preload("res://ui/on_screen_keyboard.gd")
+const ControllerMappingManagerType = preload(
+ "res://settings/controller_mapping_manager.gd"
+)
const SettingsManagerType = preload(
"res://settings/player_settings_manager.gd"
)
+class ControllerInputProbe:
+ extends Node
+
+ var pressed_buttons: Array[int] = []
+
+
+ func _input(event: InputEvent) -> void:
+ var button := event as InputEventJoypadButton
+ if button != null and button.pressed:
+ pressed_buttons.append(button.button_index)
+
+
+class NativeKeyboardProbe:
+ extends OnScreenKeyboard
+ var shown_controls: Array[Control] = []
+ var hide_count: int = 0
+
+
+ func _uses_native_virtual_keyboard() -> bool:
+ return true
+
+
+ func _show_native_keyboard_for(control: Control) -> void:
+ shown_controls.append(control)
+
+
+ func _hide_native_keyboard() -> void:
+ hide_count += 1
+
+
func _initialize() -> void:
call_deferred("_run")
@@ -13,6 +46,8 @@ func _initialize() -> void:
func _run() -> void:
_validate_default_and_persistence()
await _validate_keyboard_entry()
+ await _validate_automapped_face_buttons()
+ await _validate_native_keyboard()
print("On-screen keyboard validation: PASS")
quit()
@@ -20,16 +55,46 @@ func _run() -> void:
func _validate_default_and_persistence() -> void:
var defaults := PlayerSettings.new()
assert(not defaults.on_screen_keyboard_enabled)
+ assert(not defaults.swap_hotbar_camera_scroll)
+ assert(
+ KeyboardType.should_enable_for_controller(false, false, "Linux")
+ )
+ assert(
+ KeyboardType.should_enable_for_controller(false, true, "Linux")
+ )
+ assert(
+ not KeyboardType.should_enable_for_controller(false, true, "Android")
+ )
+ assert(
+ KeyboardType.should_enable_for_controller(true, true, "Android")
+ )
+ assert(
+ KeyboardType.should_use_native_virtual_keyboard(
+ false, true, "Android"
+ )
+ )
+ assert(
+ not KeyboardType.should_use_native_virtual_keyboard(
+ true, true, "Android"
+ )
+ )
+ assert(
+ not KeyboardType.should_use_native_virtual_keyboard(
+ false, true, "Linux"
+ )
+ )
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
+ edited.swap_hotbar_camera_scroll = 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)
+ assert(reloaded.current_settings.swap_hotbar_camera_scroll)
manager.queue_free()
reloaded.queue_free()
@@ -38,37 +103,110 @@ func _validate_keyboard_entry() -> void:
var host := Control.new()
root.add_child(host)
var edit := LineEdit.new()
+ edit.text = "seed"
+ edit.caret_column = 0
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()
+ keyboard.set_enabled(false)
+ assert(keyboard.is_enabled())
var activate_event := InputEventJoypadButton.new()
activate_event.button_index = JOY_BUTTON_A
activate_event.pressed = true
+ if keyboard.is_enabled():
+ edit.grab_focus()
+ keyboard.call("_input", activate_event)
+ assert(keyboard.is_open())
+ keyboard.call("_close_keyboard", true)
+ assert(not keyboard.is_open())
+ keyboard.set_enabled(true)
+ edit.grab_focus()
keyboard.call("_input", activate_event)
assert(keyboard.is_open())
+ assert(edit.caret_column == edit.text.length())
+ assert(keyboard.get("_buffer_caret") == edit.text.length())
+ assert(
+ str(
+ (keyboard.get("_preview") as RichTextLabel).get_parsed_text()
+ ).begins_with("seed")
+ )
+ var left_page_event := InputEventJoypadButton.new()
+ left_page_event.button_index = JOY_BUTTON_LEFT_SHOULDER
+ left_page_event.pressed = true
+ keyboard.call("_input", left_page_event)
+ assert(keyboard.is_open())
+ assert(keyboard.get("_page") == KeyboardType.Page.SYMBOLS)
+ var right_page_event := InputEventJoypadButton.new()
+ right_page_event.button_index = JOY_BUTTON_RIGHT_SHOULDER
+ right_page_event.pressed = true
+ keyboard.call("_input", right_page_event)
+ assert(keyboard.is_open())
+ assert(keyboard.get("_page") == KeyboardType.Page.LOWER)
+ for _frame: int in 2:
+ await process_frame
+ var focused_key := root.gui_get_focus_owner() as Button
+ assert(focused_key != null and focused_key.text == "q")
+ var second_key := keyboard.get("_key_buttons")[1] as Button
+ second_key.grab_focus()
+ var outside_button := Button.new()
+ host.add_child(outside_button)
+ outside_button.grab_focus()
+ assert(root.gui_get_focus_owner() == outside_button)
+ keyboard.call("_process", 0.0)
+ assert(root.gui_get_focus_owner() == second_key)
+ var conflicting_cancel_binding := InputEventJoypadButton.new()
+ conflicting_cancel_binding.button_index = JOY_BUTTON_A
+ InputMap.action_add_event(&"ui_cancel", conflicting_cancel_binding)
+ keyboard.call("_input", activate_event)
+ InputMap.action_erase_event(&"ui_cancel", conflicting_cancel_binding)
+ assert(keyboard.is_open())
+ assert(edit.text == "seedw")
+ var left_trigger := InputEventJoypadMotion.new()
+ left_trigger.axis = JOY_AXIS_TRIGGER_LEFT
+ left_trigger.axis_value = 1.0
+ keyboard.call("_input", left_trigger)
+ assert(keyboard.get("_buffer_caret") == edit.text.length() - 1)
+ var right_trigger := InputEventJoypadMotion.new()
+ right_trigger.axis = JOY_AXIS_TRIGGER_RIGHT
+ right_trigger.axis_value = 1.0
+ keyboard.call("_input", right_trigger)
+ assert(keyboard.get("_buffer_caret") == edit.text.length())
+ var preview := keyboard.get("_preview") as RichTextLabel
+ var preview_height_before_blink: float = preview.get_content_height()
+ var preview_text_before_blink: String = preview.get_parsed_text()
+ keyboard.call("_process", KeyboardType.CARET_BLINK_INTERVAL)
+ assert(
+ not bool(keyboard.get("_caret_visible"))
+ )
+ assert(preview.get_parsed_text() == preview_text_before_blink)
+ assert(is_equal_approx(preview.get_content_height(), preview_height_before_blink))
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")
+ assert(edit.text == "seedwa B")
keyboard.call("_move_caret", -1)
keyboard.call("_type_character", "C")
- assert(edit.text == "a CB")
+ assert(edit.text == "seedwa CB")
+ var input_probe := ControllerInputProbe.new()
+ root.add_child(input_probe)
+ root.move_child(input_probe, 0)
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)
+ Input.parse_input_event(backspace_event)
+ await process_frame
+ assert(edit.text == "seedwa B")
+ assert(input_probe.pressed_buttons.is_empty())
+ var close_event := InputEventJoypadButton.new()
+ close_event.button_index = JOY_BUTTON_B
+ close_event.pressed = true
+ keyboard.call("_input", close_event)
assert(not keyboard.is_open())
- assert(root.gui_get_focus_owner() == null)
+ assert(root.gui_get_focus_owner() == edit)
+ input_probe.queue_free()
edit.grab_focus()
keyboard.call("_input", activate_event)
assert(keyboard.is_open())
@@ -78,5 +216,145 @@ func _validate_keyboard_entry() -> void:
)
keyboard.call("_submit")
assert(not keyboard.is_open())
- assert(submitted == ["a B"])
+ assert(submitted == ["seedwa B"])
+ var letter_body := TextEdit.new()
+ letter_body.text = "letter"
+ letter_body.focus_mode = Control.FOCUS_ALL
+ host.add_child(letter_body)
+ letter_body.text_changed.connect(func() -> void:
+ outside_button.call_deferred("grab_focus")
+ )
+ letter_body.grab_focus()
+ keyboard.call("_input", activate_event)
+ assert(keyboard.is_open())
+ for _frame: int in 2:
+ await process_frame
+ var letter_key := keyboard.get("_key_buttons")[2] as Button
+ letter_key.grab_focus()
+ keyboard.call("_input", activate_event)
+ for _frame: int in 2:
+ await process_frame
+ assert(letter_body.text == "lettere")
+ assert(root.gui_get_focus_owner() == letter_key)
+ keyboard.call("_input", close_event)
+ assert(not keyboard.is_open())
+ host.queue_free()
+
+
+func _validate_automapped_face_buttons() -> void:
+ var manager := ControllerMappingManagerType.new()
+ root.add_child(manager)
+ await process_frame
+ var swapped_bindings: Dictionary = (
+ ControllerMappingManagerType.default_bindings()
+ )
+ swapped_bindings[str(ControllerMappingManagerType.ROLE_A)] = {
+ "kind": "button",
+ "button": int(JOY_BUTTON_B),
+ }
+ swapped_bindings[str(ControllerMappingManagerType.ROLE_B)] = {
+ "kind": "button",
+ "button": int(JOY_BUTTON_A),
+ }
+ manager._active_profile_key = "keyboard-mapping-test"
+ manager._profiles[manager._active_profile_key] = {
+ "controller_name": "keyboard mapping test",
+ "bindings": swapped_bindings,
+ }
+ 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)
+ keyboard.setup_controller_mapping(manager)
+ keyboard.set_enabled(true)
+ await process_frame
+ var mapped_accept := InputEventJoypadButton.new()
+ mapped_accept.device = manager.get_active_device_id()
+ mapped_accept.button_index = JOY_BUTTON_B
+ mapped_accept.pressed = true
+ edit.grab_focus()
+ keyboard.call("_input", mapped_accept)
+ assert(keyboard.is_open())
+ for _frame: int in 2:
+ await process_frame
+ keyboard.call("_input", mapped_accept)
+ assert(keyboard.is_open())
+ assert(edit.text == "q")
+ var mapped_cancel := InputEventJoypadButton.new()
+ mapped_cancel.device = manager.get_active_device_id()
+ mapped_cancel.button_index = JOY_BUTTON_A
+ mapped_cancel.pressed = true
+ keyboard.call("_input", mapped_cancel)
+ assert(not keyboard.is_open())
+ host.queue_free()
+ manager.queue_free()
+
+
+func _validate_native_keyboard() -> void:
+ var host := Control.new()
+ root.add_child(host)
+ var edit := LineEdit.new()
+ edit.focus_mode = Control.FOCUS_ALL
+ edit.virtual_keyboard_enabled = true
+ host.add_child(edit)
+ var outside_button := Button.new()
+ host.add_child(outside_button)
+ var keyboard := NativeKeyboardProbe.new()
+ host.add_child(keyboard)
+ await process_frame
+ edit.grab_focus()
+ assert(keyboard.is_open())
+ var focus_exit_count: int = 0
+ edit.focus_exited.connect(func() -> void:
+ focus_exit_count += 1
+ )
+ assert(keyboard.request_for_control(edit))
+ assert(edit.has_focus())
+ assert(focus_exit_count == 0)
+ assert(not edit.virtual_keyboard_enabled)
+ assert(not edit.virtual_keyboard_show_on_focus)
+ assert(keyboard.shown_controls == [edit])
+ var character_event := InputEventKey.new()
+ character_event.keycode = KEY_A
+ character_event.unicode = 97
+ character_event.pressed = true
+ Input.parse_input_event(character_event)
+ await process_frame
+ assert(edit.has_focus())
+ assert(edit.text == "a")
+ outside_button.grab_focus()
+ await process_frame
+ assert(outside_button.has_focus())
+ assert(keyboard.is_open())
+ var backspace_event := InputEventKey.new()
+ backspace_event.keycode = KEY_BACKSPACE
+ backspace_event.pressed = true
+ Input.parse_input_event(backspace_event)
+ await process_frame
+ assert(edit.has_focus())
+ assert(edit.text.is_empty())
+ var close_event := InputEventJoypadButton.new()
+ close_event.button_index = JOY_BUTTON_B
+ close_event.pressed = true
+ keyboard.call("_input", close_event)
+ assert(not edit.has_focus())
+ assert(not keyboard.is_open())
+ assert(edit.virtual_keyboard_enabled)
+ assert(edit.virtual_keyboard_show_on_focus)
+ assert(keyboard.hide_count == 1)
+ assert(keyboard.request_for_control(edit))
+ await process_frame
+ assert(edit.has_focus())
+ assert(keyboard.is_open())
+ assert(keyboard.shown_controls == [edit, edit])
+ var escape_event := InputEventKey.new()
+ escape_event.keycode = KEY_ESCAPE
+ escape_event.pressed = true
+ keyboard.call("_input", escape_event)
+ assert(not edit.has_focus())
+ assert(not keyboard.is_open())
+ assert(keyboard.hide_count == 2)
host.queue_free()
diff --git a/tests/operator_multiplayer_validation.gd b/tests/operator_multiplayer_validation.gd
new file mode 100644
index 0000000..05f8ee9
--- /dev/null
+++ b/tests/operator_multiplayer_validation.gd
@@ -0,0 +1,266 @@
+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(bool(main.call(
+ "_apply_world", WorldLayout.STARTER_ISLAND, 1, true
+ )))
+ assert(session.set_host_world(WorldLayout.STARTER_ISLAND, 1))
+ 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"))
+ assert(_has_button_tooltip(players_page, "scrub art"))
+
+ 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(3) as Button).visible)
+ players_page.call("_select_tab", 3)
+ assert(int(players_page.get("_current_tab")) == 3)
+ 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(3) 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
+
+
+func _has_button_tooltip(root_node: Node, tooltip: String) -> bool:
+ for node: Node in root_node.find_children("*", "Button", true, false):
+ var button := node as Button
+ if button != null and button.tooltip_text == tooltip:
+ 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 c9b36cd..a76a3dd 100644
--- a/tests/player_experience_validation.gd
+++ b/tests/player_experience_validation.gd
@@ -143,14 +143,21 @@ func _validate_save_migration() -> void:
version_five,
5,
)
- assert(int(migrated.get("save_version", -1)) == 7)
+ assert(int(migrated.get("save_version", -1)) == 11)
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(
+ int(world_data.get("seed", 0))
+ == PlayerSaveManager.DEFAULT_WORLD_SEED
+ )
assert(
PlayerJobService.validate_save_data(migrated.get("jobs", {}))
)
+ assert(not PlayerHomeState.sanitize_save_data(
+ migrated.get("home", {})
+ ).is_empty())
manager.queue_free()
diff --git a/tests/player_gathering_animation_validation.gd b/tests/player_gathering_animation_validation.gd
new file mode 100644
index 0000000..42f97ca
--- /dev/null
+++ b/tests/player_gathering_animation_validation.gd
@@ -0,0 +1,140 @@
+extends SceneTree
+
+
+const PlayerScene: PackedScene = preload("res://player/player.tscn")
+const CrabBrown: FishData = preload(
+ "res://fish/species/crab_brown/crab_brown.tres"
+)
+const ClamManila: FishData = preload(
+ "res://fish/species/clam_manila/clam_manila.tres"
+)
+const BeetleStagCommon: FishData = preload(
+ "res://fish/species/beetle_stag_common/beetle_stag_common.tres"
+)
+
+
+func _initialize() -> void:
+ call_deferred("_run")
+
+
+func _run() -> void:
+ var player := PlayerScene.instantiate() as Player
+ root.add_child(player)
+ await process_frame
+ assert(is_equal_approx(
+ Player.get_catch_texture_resolution_scale(CrabBrown.display_texture),
+ 20.0,
+ ))
+ assert(is_equal_approx(
+ Player.get_catch_texture_resolution_scale(ClamManila.display_texture),
+ 20.0,
+ ))
+ assert(is_equal_approx(
+ Player.get_catch_texture_resolution_scale(BeetleStagCommon.display_texture),
+ 20.0,
+ ))
+ player.set_process(false)
+ player.set_physics_process(false)
+ var animation_player := player.get_node(
+ "Visuals/CharacterRig/AnimationPlayer"
+ ) as AnimationPlayer
+ assert(animation_player != null)
+ for required_animation: StringName in [
+ &"draw",
+ &"idle_sneak",
+ &"sneaking",
+ &"strike",
+ ]:
+ assert(animation_player.has_animation(required_animation))
+
+ Input.action_press(&"sneak")
+ player.velocity = Vector3.ZERO
+ player.call("_update_character_animation")
+ assert(animation_player.current_animation == &"idle_sneak")
+
+ player.velocity = Vector3(player.sneak_speed, 0.0, 0.0)
+ player.call("_update_character_animation")
+ assert(animation_player.current_animation == &"sneaking")
+
+ player.velocity = Vector3.ZERO
+ assert(player.begin_net_draw_visual())
+ assert(animation_player.current_animation == &"draw")
+ player.call("_on_character_animation_finished", &"draw")
+ assert(StringName(player.get("_animation_action_id")) == &"draw")
+ assert(animation_player.current_animation == &"draw")
+
+ assert(player.play_net_strike_visual())
+ assert(animation_player.current_animation == &"strike")
+ player.call("_on_character_animation_finished", &"strike")
+ assert(StringName(player.get("_animation_action_id")) == &"strike")
+ assert(bool(player.get("_animation_action_paused")))
+ assert(player.is_net_strike_held())
+ assert(player.release_net_strike_hold())
+ assert(StringName(player.get("_animation_action_id")).is_empty())
+ assert(animation_player.current_animation == &"idle_sneak")
+
+ Input.action_release(&"sneak")
+ assert(player.begin_net_draw_visual())
+ assert(animation_player.current_animation == &"casting")
+ player.cancel_net_action_visual()
+ assert(StringName(player.get("_animation_action_id")).is_empty())
+ assert(animation_player.current_animation == &"idle")
+ Input.action_press(&"sneak")
+ var visuals := player.get_node("Visuals") as Node3D
+
+ assert(player.play_net_strike_visual())
+ player.resolve_net_strike_visual(true)
+ player.net_success_contact_pause_duration = 0.05
+ player.showcase_turn_duration = 0.05
+ player.showcase_camera_transition_duration = 0.05
+ player.net_showcase_camera_yaw_offset = 0.0
+ var crab_catch := FishCatch.new()
+ crab_catch.fish = CrabBrown
+ crab_catch.fish_id = CrabBrown.id
+ crab_catch.weight_lb = CrabBrown.get_minimum_weight()
+ crab_catch.display_scale = CrabBrown.get_display_scale_for_weight(
+ crab_catch.weight_lb
+ )
+ crab_catch.sale_value = CrabBrown.get_sale_value_for_weight(
+ crab_catch.weight_lb
+ )
+ crab_catch.ensure_identity()
+ player.begin_catch_showcase(crab_catch)
+ assert(not bool(player.get("_showcase_animation_active")))
+ assert(player.get("_pending_net_showcase_catch") == crab_catch)
+ player.call("_on_character_animation_finished", &"strike")
+ assert(StringName(player.get("_animation_action_id")) == &"strike")
+ assert(bool(player.get("_animation_action_paused")))
+ assert(not bool(player.get("_showcase_animation_active")))
+ var contact_pause := player.get("_net_strike_showcase_pause_tween") as Tween
+ assert(contact_pause != null and contact_pause.is_valid())
+ await contact_pause.finished
+ assert(StringName(player.get("_animation_action_id")).is_empty())
+ assert(bool(player.get("_showcase_animation_active")))
+ var stored_showcase_rotation: Vector3 = player.get(
+ "_showcase_visual_rotation"
+ )
+ await create_timer(0.1).timeout
+ assert(
+ absf(wrapf(
+ visuals.rotation.y - stored_showcase_rotation.y,
+ -PI,
+ PI,
+ )) > 3.0
+ )
+ var catch_display := player.get("_catch_display") as Node3D
+ assert(catch_display != null and catch_display.visible)
+ player.end_catch_showcase(Callable(), true, true)
+ assert(
+ absf(wrapf(
+ visuals.rotation.y - stored_showcase_rotation.y,
+ -PI,
+ PI,
+ )) < 0.01
+ )
+
+ Input.action_release(&"sneak")
+ player.queue_free()
+ await process_frame
+ print("Player gathering animation validation: PASS")
+ quit()
diff --git a/tests/player_gathering_animation_validation.gd.uid b/tests/player_gathering_animation_validation.gd.uid
new file mode 100644
index 0000000..276cd27
--- /dev/null
+++ b/tests/player_gathering_animation_validation.gd.uid
@@ -0,0 +1 @@
+uid://cba1j1dfxk5t4
diff --git a/tests/profile_multiplayer_validation.gd b/tests/profile_multiplayer_validation.gd
new file mode 100644
index 0000000..4d2bd70
--- /dev/null
+++ b/tests/profile_multiplayer_validation.gd
@@ -0,0 +1,311 @@
+extends SceneTree
+
+const MainScene: PackedScene = preload("res://main/main.tscn")
+const AnimaleseVoiceType = preload("res://ui/animalese_voice.gd")
+const TEST_PORT: int = 18144
+const HOST_HISTORY_MESSAGE: String = "silent history replay"
+const HOST_VOICE_MESSAGE: String = "remote voice playback"
+
+
+func _initialize() -> void:
+ call_deferred("_run")
+
+
+func _run() -> void:
+ _validate_voice_distance_attenuation()
+ 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("Profile multiplayer validation needs host or client mode.")
+ quit(1)
+
+
+func _validate_voice_distance_attenuation() -> void:
+ assert(is_zero_approx(
+ ChatUI.animalese_volume_offset_db_for_distance(0.0)
+ ))
+ assert(is_zero_approx(
+ ChatUI.animalese_volume_offset_db_for_distance(4.0)
+ ))
+ var middle_distance_volume := (
+ ChatUI.animalese_volume_offset_db_for_distance(14.0)
+ )
+ assert(middle_distance_volume < 0.0)
+ assert(middle_distance_volume > -80.0)
+ assert(is_equal_approx(
+ ChatUI.animalese_volume_offset_db_for_distance(24.0),
+ -80.0,
+ ))
+
+
+func _run_host() -> void:
+ var main: Node = await _create_initialized_main()
+ var session := main.get_node("%NetworkSession") as NetworkSession
+ var profile := main.get_node(
+ "%NetworkProfilePreferences"
+ ) as NetworkProfilePreferences
+ assert(profile.set_profile_identity(
+ profile.display_name,
+ "deep",
+ profile.speech_speed_id,
+ profile.call_id,
+ "kim",
+ ))
+ var save_manager := main.get("_save_manager") as PlayerSaveManager
+ assert(bool(main.call(
+ "_apply_world", WorldLayout.STARTER_ISLAND, 1, true
+ )))
+ assert(session.set_host_world(WorldLayout.STARTER_ISLAND, 1))
+ assert(session.start_private_host(TEST_PORT))
+ var local_avatar := main.get_node("%PlayerSpawnService").get_avatar(
+ 1
+ ) as Player
+ assert(local_avatar != null)
+ assert(local_avatar.get_animalese_voice_id() == "deep")
+ assert(local_avatar.get_animalese_sample_set_id() == "kim")
+ 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 chat_service := main.get_node(
+ "%NetworkChatService"
+ ) as NetworkChatService
+ assert(chat_service.send_local_message(
+ HOST_HISTORY_MESSAGE,
+ profile.voice_id,
+ profile.sample_set_id,
+ ))
+ var voice_messages: Array[Dictionary] = []
+ chat_service.message_received.connect(func(message: Dictionary) -> void:
+ if str(message.get("body", "")) == "voice profile sync":
+ voice_messages.append(message)
+ )
+ var remote_peer_id: int = await _wait_for_remote_peer(session)
+ assert(remote_peer_id > 1)
+ var record := session.get_peer_record(remote_peer_id)
+ assert(record != null)
+ assert(
+ NetworkProtocol.APPEARANCE_PREVIEW_CAPABILITY
+ in record.capability_flags
+ )
+ var original: Dictionary = record.appearance_snapshot.duplicate(true)
+ var changed: Dictionary = _changed_appearance(original)
+ assert(changed != original)
+ await _wait_for_remote_appearance(session, remote_peer_id, changed)
+ await _wait_for_remote_appearance(session, remote_peer_id, original)
+ await _wait_for_remote_appearance(session, remote_peer_id, changed)
+ var avatar := main.get_node("%PlayerSpawnService").get_avatar(
+ remote_peer_id
+ ) as Player
+ assert(avatar != null)
+ assert(avatar.appearance_snapshot == changed)
+ assert(record.display_name != "NetworkProfileService")
+ assert(chat_service.send_local_message(
+ HOST_VOICE_MESSAGE,
+ profile.voice_id,
+ profile.sample_set_id,
+ ))
+ var voice_message_deadline: int = Time.get_ticks_msec() + 8000
+ while (
+ Time.get_ticks_msec() < voice_message_deadline
+ and voice_messages.is_empty()
+ ):
+ await process_frame
+ assert(not voice_messages.is_empty())
+ assert(str(voice_messages[0].get("voice_id", "")) == "deep")
+ assert(str(voice_messages[0].get("sample_set_id", "")) == "kim")
+ 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("Profile multiplayer host validation: PASS")
+ await _session_cleanup(main, session)
+
+
+func _run_client() -> void:
+ var main: Node = await _create_initialized_main()
+ var chat_ui := main.get_node("%GameUI").get_node("%ChatUI") as ChatUI
+ var animalese := chat_ui.get("_animalese_voice") as AnimaleseVoiceType
+ assert(animalese != null)
+ var remote_voice_samples: Array[String] = []
+ animalese.character_sample_played.connect(
+ func(sample_set_id: String, _character: String) -> void:
+ remote_voice_samples.append(sample_set_id)
+ )
+ 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())
+ assert(session.supports_server_capability(
+ NetworkProtocol.APPEARANCE_PREVIEW_CAPABILITY
+ ))
+ var chat_service := main.get_node(
+ "%NetworkChatService"
+ ) as NetworkChatService
+ # Player-hosted rooms deliberately send no prior scrollback to joining or
+ # reconnecting clients. Live messages sent after authentication still work.
+ await create_timer(0.5).timeout
+ assert(not chat_service.get_history().any(
+ func(message: Dictionary) -> bool:
+ return str(message.get("body", "")) == HOST_HISTORY_MESSAGE
+ ))
+ assert(remote_voice_samples.is_empty())
+ var service := main.get_node(
+ "%NetworkProfileService"
+ ) as NetworkProfileService
+ var original: Dictionary = service.get_persisted_appearance()
+ var changed: Dictionary = _changed_appearance(original)
+ assert(service.preview_appearance(changed))
+ await create_timer(0.75).timeout
+ service.restore_persisted_appearance()
+ await create_timer(0.75).timeout
+ assert(service.preview_appearance(changed))
+ await create_timer(0.75).timeout
+ var apply_results: Array = []
+ service.apply_finished.connect(func(accepted: bool, message: String) -> void:
+ apply_results.append([accepted, message])
+ )
+ assert(service.apply_profile(
+ service.get_persisted_name(),
+ changed,
+ true,
+ "deep",
+ service.get_persisted_speech_speed_id(),
+ service.get_persisted_call_id(),
+ "kim",
+ ))
+ var apply_deadline: int = Time.get_ticks_msec() + 8000
+ while Time.get_ticks_msec() < apply_deadline and apply_results.is_empty():
+ await process_frame
+ assert(not apply_results.is_empty())
+ assert(bool(apply_results[0][0]))
+ assert(service.get_persisted_appearance() == changed)
+ assert(service.get_persisted_voice_id() == "deep")
+ assert(service.get_persisted_sample_set_id() == "kim")
+ assert(Dictionary(session.get("_local_appearance_snapshot")) == changed)
+ var player := main.get_node("%PlayerSpawnService").get_local_player() as Player
+ assert(player.get_animalese_voice_id() == "deep")
+ assert(player.get_animalese_sample_set_id() == "kim")
+ var host_voice_messages: Array[Dictionary] = []
+ for message: Dictionary in chat_service.get_history():
+ if str(message.get("body", "")) == HOST_VOICE_MESSAGE:
+ host_voice_messages.append(message)
+ var host_voice_deadline: int = Time.get_ticks_msec() + 8000
+ while (
+ Time.get_ticks_msec() < host_voice_deadline
+ and (
+ host_voice_messages.is_empty()
+ or remote_voice_samples.is_empty()
+ )
+ ):
+ await process_frame
+ for message: Dictionary in chat_service.get_history():
+ if (
+ str(message.get("body", "")) == HOST_VOICE_MESSAGE
+ and message not in host_voice_messages
+ ):
+ host_voice_messages.append(message)
+ assert(not host_voice_messages.is_empty())
+ assert(str(host_voice_messages[0].get("voice_id", "")) == "deep")
+ assert(str(host_voice_messages[0].get("sample_set_id", "")) == "kim")
+ assert(remote_voice_samples.has("kim"))
+ var local_confirmations: Array[Dictionary] = []
+ chat_service.local_message_confirmed.connect(
+ func(message: Dictionary) -> void:
+ if str(message.get("body", "")) == "voice profile sync":
+ local_confirmations.append(message)
+ )
+ assert(chat_service.send_local_message(
+ "voice profile sync",
+ service.get_persisted_voice_id(),
+ service.get_persisted_sample_set_id(),
+ ))
+ var confirmation_deadline: int = Time.get_ticks_msec() + 8000
+ while (
+ Time.get_ticks_msec() < confirmation_deadline
+ and local_confirmations.is_empty()
+ ):
+ await process_frame
+ assert(not local_confirmations.is_empty())
+ assert(str(local_confirmations[0].get("voice_id", "")) == "deep")
+ assert(str(local_confirmations[0].get("sample_set_id", "")) == "kim")
+ print("Profile multiplayer client validation: PASS")
+ await _session_cleanup(main, session)
+
+
+func _changed_appearance(original: Dictionary) -> Dictionary:
+ var changed := original.duplicate(true)
+ for category_id: String in CharacterCustomizationCatalog.CATEGORY_IDS:
+ if category_id in CharacterCustomizationCatalog.FUR_STYLE_IDS:
+ continue
+ var options: Array = CharacterCustomizationCatalog.options_for(category_id)
+ for option: Dictionary in options:
+ var option_id: Variant = option.get("id")
+ if option_id != changed.get(category_id):
+ changed[category_id] = option_id
+ if CharacterCustomizationCatalog.validate_snapshot(changed):
+ return changed
+ changed[category_id] = original.get(category_id)
+ assert(false, "No alternate appearance option is available.")
+ return changed
+
+
+func _wait_for_remote_appearance(
+ session: NetworkSession,
+ peer_id: int,
+ expected: Dictionary,
+) -> void:
+ var deadline: int = Time.get_ticks_msec() + 8000
+ while Time.get_ticks_msec() < deadline:
+ await process_frame
+ var record := session.get_peer_record(peer_id)
+ if record != null and record.appearance_snapshot == expected:
+ return
+ assert(false, "Timed out waiting for a remote appearance update.")
+
+
+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 _session_cleanup(main: Node, session: NetworkSession) -> void:
+ session.disconnect_session("")
+ main.queue_free()
+ for _frame: int in 4:
+ await process_frame
+ await create_timer(0.1).timeout
+ quit()
diff --git a/tests/profile_multiplayer_validation.gd.uid b/tests/profile_multiplayer_validation.gd.uid
new file mode 100644
index 0000000..d561da1
--- /dev/null
+++ b/tests/profile_multiplayer_validation.gd.uid
@@ -0,0 +1 @@
+uid://qvk8y0fd7o2j
diff --git a/tests/progression_archive_validation.gd b/tests/progression_archive_validation.gd
new file mode 100644
index 0000000..30f0df6
--- /dev/null
+++ b/tests/progression_archive_validation.gd
@@ -0,0 +1,377 @@
+extends SceneTree
+
+const MainScene: PackedScene = preload("res://main/main.tscn")
+
+
+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 save_manager := main.get("_save_manager") as PlayerSaveManager
+ var player := main.get("_player") as Player
+ var data_root := main.get("_data_root") as PlayerDataRoot
+ var home_state := main.get("_home_state") as PlayerHomeState
+ assert(
+ save_manager != null
+ and player != null
+ and data_root != null
+ and home_state != null
+ )
+ assert(save_manager.initialize_new_game(864209))
+ save_manager.set_autosave_enabled(true)
+ assert(player.wallet.restore_balance(4321))
+ assert(player.bag.add_item(&"coffee"))
+ assert(home_state.add_owned_decor(&"basic_cube"))
+ assert(not home_state.place_decor(
+ &"basic_cube",
+ Vector3.ZERO,
+ 0.0,
+ PlayerHomeState.PlacementMode.GRID,
+ ).is_empty())
+ assert(save_manager.save_now())
+
+ var save_path: String = str(save_manager.get("_save_path"))
+ assert(save_path.ends_with("player/player_save.nfsave"))
+ assert(FileAccess.file_exists(save_path))
+ assert(not FileAccess.file_exists(save_path.get_base_dir().path_join(
+ "player_save.json"
+ )))
+ _assert_opaque(save_path)
+ var decoded: Dictionary = ProgressionSaveCodec.read_local_save(save_path)
+ assert(bool(decoded.get("ok", false)))
+ assert(int((decoded["data"] as Dictionary)["save_version"]) == 11)
+ assert(not PlayerHomeState.sanitize_save_data(
+ (decoded["data"] as Dictionary).get("home", {})
+ ).is_empty())
+ assert(
+ ((decoded["data"] as Dictionary)["home"] as Dictionary)
+ .get("placements", []).size() == 1
+ )
+ var legacy_container_path: String = data_root.progression_backup_directory().path_join(
+ "netfishing-container.nfsave"
+ )
+ _assert_legacy_progression_container(
+ legacy_container_path,
+ decoded["data"] as Dictionary,
+ )
+ _assert_legacy_data_manifest(data_root.root_path.get_base_dir())
+ _assert_legacy_identity_backup(main, data_root)
+
+ var archive_path: String = data_root.progression_backup_directory().path_join(
+ "progression-test.nfsave"
+ )
+ var exported: Dictionary = save_manager.export_progression_archive(
+ archive_path
+ )
+ assert(bool(exported.get("ok", false)))
+ assert(FileAccess.file_exists(archive_path))
+ _assert_opaque(archive_path)
+ var inspected: Dictionary = save_manager.inspect_progression_archive(
+ archive_path
+ )
+ assert(bool(inspected.get("ok", false)))
+ assert(int(inspected.get("wallet_balance", -1)) == 4321)
+ assert(int(inspected.get("world_seed", -1)) == 864209)
+
+ assert(player.wallet.restore_balance(7))
+ assert(save_manager.save_now())
+ var imported: Dictionary = save_manager.import_progression_archive(
+ archive_path
+ )
+ assert(bool(imported.get("ok", false)))
+ assert(save_manager.load_player_data())
+ assert(player.wallet.get_balance() == 4321)
+ assert(save_manager.get_world_seed() == 864209)
+ assert(home_state.get_owned_count(&"basic_cube") == 1)
+ assert(home_state.get_placed_count(&"basic_cube") == 1)
+
+ var legacy_data: Dictionary = (
+ ProgressionSaveCodec.read_local_save(save_path)["data"]
+ )
+ assert(save_manager.delete_progression_save())
+ var legacy_path: String = save_path.get_base_dir().path_join(
+ "player_save.json"
+ )
+ var legacy_file := FileAccess.open(legacy_path, FileAccess.WRITE)
+ assert(legacy_file != null)
+ legacy_file.store_string(JSON.stringify(legacy_data, "\t"))
+ legacy_file.close()
+ assert(save_manager.load_player_data())
+ assert(FileAccess.file_exists(save_path))
+ assert(not FileAccess.file_exists(legacy_path))
+ _assert_opaque(save_path)
+
+ save_manager.set_autosave_enabled(false)
+ var save_slots := main.get("_save_slots") as PlayerSaveSlotCatalog
+ assert(save_slots != null)
+ assert(save_slots.configure(data_root, save_manager))
+ var adopted_slots: Array[Dictionary] = save_slots.list_slots()
+ assert(adopted_slots.size() == 1)
+ var adopted_slot_id: String = str(adopted_slots.front().get("slot_id", ""))
+ assert(bool(adopted_slots.front().get("legacy", false)))
+ assert(bool(adopted_slots.front().get("has_save", false)))
+
+ var duplicated: Dictionary = save_slots.duplicate_slot(
+ adopted_slot_id,
+ "another island",
+ WorldLayout.GENERATED,
+ 97531,
+ )
+ assert(bool(duplicated.get("ok", false)))
+ var duplicated_slot_id: String = str(duplicated.get("slot_id", ""))
+ var duplicate_summary: Dictionary = save_slots.get_slot(duplicated_slot_id)
+ assert(int(duplicate_summary.get("wallet_balance", -1)) == 4321)
+ assert(int(duplicate_summary.get("world_seed", -1)) == 97531)
+ assert(save_slots.activate_slot(duplicated_slot_id))
+ assert(save_manager.load_player_data())
+ assert(player.wallet.get_balance() == 4321)
+ assert(save_manager.get_world_seed() == 97531)
+ assert(save_slots.rename_slot(duplicated_slot_id, "renamed island"))
+
+ var slot_archive: String = data_root.progression_backup_directory().path_join(
+ "save-slot-export.nfsave"
+ )
+ assert(bool(save_slots.export_slot(
+ duplicated_slot_id,
+ slot_archive,
+ ).get("ok", false)))
+ var imported_slot: Dictionary = save_slots.import_slot(
+ slot_archive,
+ "imported island",
+ )
+ assert(bool(imported_slot.get("ok", false)))
+ var imported_slot_id: String = str(imported_slot.get("slot_id", ""))
+ assert(not save_slots.get_slot(imported_slot_id).is_empty())
+ assert(save_slots.delete_slot(duplicated_slot_id))
+ assert(save_slots.get_slot(duplicated_slot_id).is_empty())
+ assert(save_slots.list_slots().size() == 2)
+
+ var settings_panels: Array[Node] = main.find_children(
+ "*", "SettingsPanel", true, false
+ )
+ assert(settings_panels.size() == 2)
+ for settings_panel: SettingsPanel in settings_panels:
+ assert(settings_panel.get_node_or_null("%ExportProgression") == null)
+ assert(settings_panel.get_node_or_null("%ImportProgression") == null)
+ var settings_panel: SettingsPanel = settings_panels.front() as SettingsPanel
+ settings_panel.set(
+ "_presentation_mode", SettingsPanel.PresentationMode.TITLE_EMBEDDED
+ )
+ var unchanged_root: String = data_root.root_path
+ settings_panel.call("_choose_data_folder")
+ var data_folder_dialog := (
+ settings_panel.get("_data_folder_dialog") as FileDialog
+ )
+ assert(data_folder_dialog != null)
+ assert(data_folder_dialog.current_dir == unchanged_root)
+ data_folder_dialog.hide()
+ settings_panel.call("_change_data_folder", unchanged_root + "/")
+ await process_frame
+ assert(data_root.root_path == unchanged_root)
+ assert(
+ (settings_panel.get_node("%SettingsFeedback") as Label).text
+ == "this data folder is already active."
+ )
+ var save_slots_page := main.find_child(
+ "SaveSlotsPage", true, false
+ ) as SaveSlotsPage
+ assert(save_slots_page != null)
+ assert(save_slots_page.get_node("%ExportSlotButton") is Button)
+ assert(save_slots_page.get_node("%ImportSlotButton") is Button)
+
+ var unchanged_migration: Dictionary = (
+ PortableDataMigration.migrate_active_to(
+ data_root, unchanged_root + "/"
+ )
+ )
+ assert(bool(unchanged_migration.get("ok", false)))
+ assert(not bool(unchanged_migration.get("changed", true)))
+ assert(data_root.root_path == unchanged_root)
+
+ var migrated_root: String = data_root.root_path.get_base_dir().path_join(
+ "progression-migrated-data"
+ )
+ settings_panel.call("_change_data_folder", migrated_root)
+ for _frame: int in 4:
+ await process_frame
+ assert(data_root.root_path == migrated_root)
+ assert(
+ (settings_panel.get_node("%SettingsFeedback") as Label).text
+ == "data folder changed."
+ )
+ var migrated_save: String = migrated_root.path_join(
+ "player/player_save.nfsave"
+ )
+ assert(FileAccess.file_exists(migrated_save))
+ assert(bool(
+ ProgressionSaveCodec.read_local_save(migrated_save).get("ok", false)
+ ))
+ assert(FileAccess.file_exists(migrated_root.path_join(
+ "player/save_slots.json"
+ )))
+ var migrated_imported_slot: String = migrated_root.path_join(
+ "player/saves/%s.nfsave" % imported_slot_id
+ )
+ assert(FileAccess.file_exists(migrated_imported_slot))
+ assert(bool(
+ ProgressionSaveCodec.read_local_save(migrated_imported_slot).get(
+ "ok", false
+ )
+ ))
+ var live_slots: Array[Dictionary] = save_slots.list_slots()
+ assert(live_slots.size() == 2)
+ var live_created: Dictionary = save_slots.create_empty_slot(
+ "live root switch"
+ )
+ assert(bool(live_created.get("ok", false)))
+ var live_slot_id: String = str(live_created.get("slot_id", ""))
+ assert(save_slots.rename_slot(live_slot_id, "live root ready"))
+ assert(
+ str(save_slots.get_slot(live_slot_id).get("display_name", ""))
+ == "live root ready"
+ )
+
+ main.queue_free()
+ for _frame: int in 4:
+ await process_frame
+ await create_timer(0.1).timeout
+ print("Progression archive validation: PASS")
+ quit()
+
+
+func _assert_legacy_progression_container(
+ path: String,
+ payload: Dictionary,
+) -> void:
+ var payload_json: String = JSON.stringify(payload)
+ var envelope: Dictionary = {
+ "magic": ProgressionSaveCodec.LEGACY_MAGIC,
+ "format_version": ProgressionSaveCodec.FORMAT_VERSION,
+ "kind": ProgressionSaveCodec.LOCAL_KIND,
+ "game_version": "0.18.1-alpha",
+ "created_at_unix": 1,
+ "payload_sha256": PortableFileGuard.hash_bytes(
+ payload_json.to_utf8_buffer()
+ ),
+ "payload_json": payload_json,
+ }
+ var file: FileAccess = FileAccess.open_encrypted_with_pass(
+ path,
+ FileAccess.WRITE,
+ ProgressionSaveCodec.CONTAINER_PASSPHRASE,
+ )
+ assert(file != null)
+ file.store_string(JSON.stringify(envelope))
+ file.close()
+ var decoded: Dictionary = ProgressionSaveCodec.read_local_save(path)
+ assert(bool(decoded.get("ok", false)))
+ assert(bool(decoded.get("legacy_container", false)))
+
+
+func _assert_legacy_data_manifest(parent: String) -> void:
+ var legacy_root: String = parent.path_join("netfishing-manifest-root")
+ assert(DirAccess.make_dir_recursive_absolute(legacy_root) == OK)
+ var legacy_id: String = "0123456789abcdef0123456789abcdef"
+ var legacy_manifest: Dictionary = {
+ "format_version": PlayerDataRoot.MANIFEST_VERSION,
+ "layout_version": PlayerDataRoot.LAYOUT_VERSION,
+ "application": PlayerDataRoot.LEGACY_APPLICATION_ID,
+ "root_id": legacy_id,
+ "created_at_unix": 1,
+ "last_opened_at_unix": 1,
+ }
+ var legacy_path: String = legacy_root.path_join(
+ PlayerDataRoot.LEGACY_MANIFEST_FILENAME
+ )
+ var file := FileAccess.open(legacy_path, FileAccess.WRITE)
+ assert(file != null)
+ file.store_string(JSON.stringify(legacy_manifest, "\t"))
+ file.close()
+ var legacy_data_root := PlayerDataRoot.new()
+ root.add_child(legacy_data_root)
+ assert(legacy_data_root.use_existing_root(legacy_root))
+ assert(legacy_data_root.root_id == legacy_id)
+ assert(FileAccess.file_exists(legacy_path))
+ var canonical_path: String = legacy_root.path_join(
+ PlayerDataRoot.MANIFEST_FILENAME
+ )
+ assert(FileAccess.file_exists(canonical_path))
+ var parser := JSON.new()
+ assert(parser.parse(FileAccess.get_file_as_string(canonical_path)) == OK)
+ assert((parser.data as Dictionary).get("application") == PlayerDataRoot.APPLICATION_ID)
+ legacy_data_root.queue_free()
+
+
+func _assert_legacy_identity_backup(main: Node, data_root: PlayerDataRoot) -> void:
+ var player_identity := main.get("_player_identity") as PlayerIdentityStore
+ var identity_backups := main.get("_identity_backups") as IdentityBackupService
+ assert(player_identity != null and player_identity.is_ready())
+ assert(identity_backups != null)
+ var material: Dictionary = player_identity.export_identity_material()
+ var fingerprint: String = str(material.get("fingerprint", ""))
+ var private_key := CryptoKey.new()
+ assert(private_key.load_from_string(str(material.get("private_pem", ""))) == OK)
+ var canonical: PackedByteArray = NetworkIdentityCrypto.canonical_bytes_for_prefix(
+ NetworkIdentityCrypto.LEGACY_DOMAIN_PREFIX,
+ "identity_backup_self_test",
+ ["player", fingerprint],
+ )
+ var context := HashingContext.new()
+ assert(context.start(HashingContext.HASH_SHA256) == OK)
+ context.update(canonical)
+ var signature: PackedByteArray = Crypto.new().sign(
+ HashingContext.HASH_SHA256,
+ context.finish(),
+ private_key,
+ )
+ var envelope: Dictionary = {
+ "magic": IdentityBackupService.LEGACY_MAGIC,
+ "format_version": IdentityBackupService.FORMAT_VERSION,
+ "identity_type": "player",
+ "algorithm": NetworkIdentityCrypto.ALGORITHM,
+ "private_pem": material.get("private_pem", ""),
+ "public_pem": material.get("public_pem", ""),
+ "fingerprint": fingerprint,
+ "created_at_unix": 1,
+ "source_device_id": data_root.device_id,
+ "self_signature": Marshalls.raw_to_base64(signature),
+ }
+ var backup_path: String = data_root.identity_backup_directory().path_join(
+ "netfishing-player.nfidentity"
+ )
+ var passphrase := "legacy-test-passphrase"
+ var file: FileAccess = FileAccess.open_encrypted_with_pass(
+ backup_path,
+ FileAccess.WRITE,
+ passphrase,
+ )
+ assert(file != null)
+ file.store_string(JSON.stringify(envelope))
+ file.close()
+ var inspected: Dictionary = identity_backups.inspect_backup(
+ backup_path,
+ passphrase,
+ "player",
+ )
+ assert(bool(inspected.get("ok", false)))
+ assert(str(inspected.get("fingerprint", "")) == fingerprint)
+
+
+func _assert_opaque(path: String) -> void:
+ var bytes: PackedByteArray = PortableFileGuard.read_bytes(path)
+ assert(not bytes.is_empty())
+ var encoded_hex: String = bytes.hex_encode()
+ assert("save_version".to_utf8_buffer().hex_encode() not in encoded_hex)
+ assert("wallet".to_utf8_buffer().hex_encode() not in encoded_hex)
diff --git a/tests/progression_archive_validation.gd.uid b/tests/progression_archive_validation.gd.uid
new file mode 100644
index 0000000..59699fd
--- /dev/null
+++ b/tests/progression_archive_validation.gd.uid
@@ -0,0 +1 @@
+uid://ccl47ftc1cu5l
diff --git a/tests/rod_catalog_validation.gd b/tests/rod_catalog_validation.gd
new file mode 100644
index 0000000..3b43b3f
--- /dev/null
+++ b/tests/rod_catalog_validation.gd
@@ -0,0 +1,109 @@
+extends SceneTree
+
+const FishingRodDataType = preload("res://items/fishing_rod_data.gd")
+const FishingShopStockType = preload("res://economy/fishing_shop_stock.gd")
+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 PlayerBagType = preload("res://inventory/player_bag.gd")
+const PlayerWalletType = preload("res://economy/player_wallet.gd")
+
+const ItemCatalogResource: ItemCatalogType = preload(
+ "res://items/catalog/item_catalog.tres"
+)
+const RodAttachmentScene = preload(
+ "res://player/fishing_rod_attachment.tscn"
+)
+
+
+func _initialize() -> void:
+ call_deferred("_run")
+
+
+func _run() -> void:
+ _validate_catalog_activation()
+ _validate_shop_stock()
+ _validate_inactive_save_compatibility()
+ _validate_fallback_attachment()
+ print("Rod catalog validation: PASS")
+ quit()
+
+
+func _validate_catalog_activation() -> void:
+ var rods: Array[FishingRodDataType] = []
+ for item: ItemDataType in ItemCatalogResource.get_valid_items():
+ var rod := item as FishingRodDataType
+ if rod == null:
+ continue
+ rods.append(rod)
+ assert(rod.is_valid())
+ if rod.icon == null:
+ assert(not rod.active)
+ assert(rods.size() == 21)
+ var available_rods: Array[FishingRodDataType] = []
+ for rod: FishingRodDataType in rods:
+ if rod.is_available():
+ available_rods.append(rod)
+ assert(available_rods.size() == 1)
+ var basic: FishingRodDataType = available_rods.front()
+ assert(basic.item_id == &"basic_fishing_rod")
+ assert(basic.icon != null)
+ assert(basic.rod_model_scene == null)
+ assert(
+ ItemCatalogResource.get_available_item_by_id(&"aurora_rod") == null
+ )
+ assert(ItemCatalogResource.get_item_by_id(&"aurora_rod") != null)
+
+
+func _validate_shop_stock() -> void:
+ var stock: Array[FishingRodDataType] = (
+ FishingShopStockType.get_rod_stock(ItemCatalogResource)
+ )
+ assert(stock.size() == 1)
+ assert(stock.front().item_id == &"basic_fishing_rod")
+ var wallet := PlayerWalletType.new()
+ wallet.current_balance = 100
+ var bag := PlayerBagType.new()
+ bag.setup(ItemCatalogResource)
+ assert(
+ FishingShopStockType.purchase_one(
+ &"basic_fishing_rod",
+ wallet,
+ bag,
+ ItemCatalogResource,
+ )
+ )
+ assert(bag.owns_item(&"basic_fishing_rod"))
+ assert(wallet.get_balance() == 100)
+ assert(
+ not FishingShopStockType.purchase_one(
+ &"aurora_rod",
+ wallet,
+ bag,
+ ItemCatalogResource,
+ )
+ )
+ bag.free()
+ wallet.free()
+
+
+func _validate_inactive_save_compatibility() -> void:
+ var bag := PlayerBagType.new()
+ bag.setup(ItemCatalogResource)
+ assert(not bag.can_add_item(&"aurora_rod", 1))
+ assert(not bag.add_item(&"aurora_rod", 1))
+ var legacy_record := OwnedItemType.new()
+ legacy_record.item_id = &"aurora_rod"
+ legacy_record.quantity = 1
+ var legacy_items: Array[OwnedItemType] = [legacy_record]
+ assert(bag.replace_all_items(legacy_items))
+ assert(bag.owns_item(&"aurora_rod"))
+ bag.free()
+
+
+func _validate_fallback_attachment() -> void:
+ var attachment := RodAttachmentScene.instantiate()
+ assert(attachment.get_node_or_null("FishingRod/FallbackRodMesh") != null)
+ assert(attachment.get_node_or_null("FishingRod/ModelMount") != null)
+ assert(attachment.get_node_or_null("FishingRod/FishingRodTip") != null)
+ attachment.free()
diff --git a/tests/rod_catalog_validation.gd.uid b/tests/rod_catalog_validation.gd.uid
new file mode 100644
index 0000000..ae0537e
--- /dev/null
+++ b/tests/rod_catalog_validation.gd.uid
@@ -0,0 +1 @@
+uid://bjbgbahx5iuyy
diff --git a/tests/session_switch_multiplayer_validation.gd b/tests/session_switch_multiplayer_validation.gd
new file mode 100644
index 0000000..86c9a84
--- /dev/null
+++ b/tests/session_switch_multiplayer_validation.gd
@@ -0,0 +1,177 @@
+extends SceneTree
+
+const MainScene: PackedScene = preload("res://main/main.tscn")
+const FIRST_HOST_PORT: int = 18142
+const SECOND_HOST_PORT: int = 18143
+const WAIT_SECONDS: int = 20
+
+
+func _initialize() -> void:
+ call_deferred("_run")
+
+
+func _run() -> void:
+ var arguments: PackedStringArray = OS.get_cmdline_user_args()
+ if arguments.has("first_host"):
+ await _run_host(FIRST_HOST_PORT, "first")
+ return
+ if arguments.has("second_host"):
+ await _run_host(SECOND_HOST_PORT, "second")
+ return
+ if arguments.has("client"):
+ await _run_client()
+ return
+ push_error(
+ "Session switch multiplayer validation needs first_host, "
+ + "second_host, or client mode."
+ )
+ quit(1)
+
+
+func _run_host(port: int, label: String) -> void:
+ var main: Node = await _create_initialized_main()
+ var session := main.get_node("%NetworkSession") as NetworkSession
+ assert(bool(main.call(
+ "_apply_world", WorldLayout.STARTER_ISLAND, 1, true
+ )))
+ assert(session.set_host_world(WorldLayout.STARTER_ISLAND, 1))
+ assert(session.start_dedicated_host(port, 8, "127.0.0.1"))
+ assert(session.set_host_open(true))
+ var remote_peer_id: int = await _wait_for_remote_peer(session)
+ assert(remote_peer_id > 1)
+ var disconnect_deadline: int = (
+ Time.get_ticks_msec() + WAIT_SECONDS * 1000
+ )
+ 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("Session switch %s host validation: PASS" % label)
+ 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()
+ var session := main.get_node("%NetworkSession") as NetworkSession
+ main.call(
+ "_on_title_join_game_requested",
+ "127.0.0.1:%d" % FIRST_HOST_PORT,
+ )
+ await _wait_for_join(main, session, FIRST_HOST_PORT)
+ var game_ui := main.get("_game_ui") as GameUI
+ var pause_menu := game_ui.get_pause_menu()
+ var join_page := pause_menu.get_node("%JoinGamePage") as JoinGamePage
+ var discovery := main.get_node("%DiscoveryClient") as DiscoveryClient
+ pause_menu.show()
+ (pause_menu.get_node("%RootPage") as Control).hide()
+ join_page.open_page()
+ join_page.set("_mode", JoinGamePage.Mode.DISCOVER)
+ var rooms: Array[Dictionary] = [{
+ "room_id": "deferred-public-room",
+ "room_name": "Deferred public room",
+ "address": "127.0.0.1",
+ "port": SECOND_HOST_PORT,
+ "current_players": 0,
+ "max_players": 8,
+ }]
+ join_page.set("_discovery_rooms", rooms)
+ join_page.set("_selected_discovery_index", 0)
+ assert(bool(join_page.get("_gameplay_context")))
+ assert(
+ str(
+ (join_page.call("_selected_discovery_room") as Dictionary).get(
+ "room_id", ""
+ )
+ ) == "deferred-public-room"
+ )
+ join_page.call("_request_join")
+ assert(not bool(discovery.get("_join_request_in_flight")))
+ assert(
+ str(
+ (join_page.get("_pending_confirmation_room") as Dictionary).get(
+ "room_id", ""
+ )
+ ) == "deferred-public-room"
+ )
+ assert(
+ int(pause_menu.get("_confirmation_action"))
+ == PauseMenu.ConfirmationAction.JOIN_ANOTHER
+ )
+ join_page.cancel_pending_join_confirmation()
+ (pause_menu.get_node("%ConfirmationPage") as Control).hide()
+ pause_menu.set("_confirmation_action", PauseMenu.ConfirmationAction.NONE)
+ pause_menu.set("_confirmation_returns_to_join_game", false)
+ join_page.open_page("127.0.0.1:%d" % SECOND_HOST_PORT)
+ join_page.set("_mode", JoinGamePage.Mode.DIRECT)
+ join_page.call("_request_join")
+ assert(
+ int(pause_menu.get("_confirmation_action"))
+ == PauseMenu.ConfirmationAction.JOIN_ANOTHER
+ )
+ assert(not join_page.visible)
+ assert((pause_menu.get_node("%ConfirmationPage") as Control).visible)
+ pause_menu.call(
+ "_finish_confirmation_accept",
+ PauseMenu.ConfirmationAction.JOIN_ANOTHER,
+ )
+ await _wait_for_join(main, session, SECOND_HOST_PORT)
+ assert(bool(main.get("_gameplay_started")))
+ print("Session switch 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 _wait_for_join(
+ main: Node,
+ session: NetworkSession,
+ expected_port: int,
+) -> void:
+ var deadline: int = Time.get_ticks_msec() + WAIT_SECONDS * 1000
+ while Time.get_ticks_msec() < deadline:
+ await process_frame
+ if session.state == NetworkSession.State.VERIFYING_SERVER_IDENTITY:
+ main.call("_confirm_server_trust")
+ var endpoint: ConnectionEndpoint = session.get_current_endpoint()
+ if (
+ session.is_joined_client()
+ and bool(main.get("_gameplay_started"))
+ and endpoint != null
+ and endpoint.port == expected_port
+ ):
+ return
+ assert(false, "Timed out joining UDP %d." % expected_port)
+
+
+func _wait_for_remote_peer(session: NetworkSession) -> int:
+ var deadline: int = Time.get_ticks_msec() + WAIT_SECONDS * 1000
+ while Time.get_ticks_msec() < deadline:
+ await process_frame
+ for peer_id: int in session.get_authenticated_peer_ids():
+ if peer_id > 1:
+ return peer_id
+ return 0
+
+
+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/session_switch_multiplayer_validation.gd.uid b/tests/session_switch_multiplayer_validation.gd.uid
new file mode 100644
index 0000000..6e6f24c
--- /dev/null
+++ b/tests/session_switch_multiplayer_validation.gd.uid
@@ -0,0 +1 @@
+uid://bkuih0ht2vny3
diff --git a/tests/shoreline_ambience_validation.gd b/tests/shoreline_ambience_validation.gd
index 4297d72..78829f1 100644
--- a/tests/shoreline_ambience_validation.gd
+++ b/tests/shoreline_ambience_validation.gd
@@ -35,6 +35,8 @@ func _run() -> void:
root.add_child(controller)
await process_frame
assert(runtime_audio.bus == &"Environment")
+ assert(runtime_audio.stream == null)
+ controller.set_audio_enabled(true)
assert(controller.near_distance < controller.far_distance)
assert(controller.near_volume_db > controller.far_volume_db)
var runtime_waves := runtime_audio.stream as AudioStreamWAV
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..f79700e 100644
--- a/tests/surface_drawing_multiplayer_validation.gd
+++ b/tests/surface_drawing_multiplayer_validation.gd
@@ -2,7 +2,7 @@ extends SceneTree
const MainScene: PackedScene = preload("res://main/main.tscn")
const TEST_PORT: int = 18133
-const WAIT_MSEC: int = 20000
+const WAIT_MSEC: int = 60000
func _initialize() -> void:
@@ -24,14 +24,22 @@ func _run() -> void:
func _run_host() -> void:
var main: Node = await _create_initialized_main()
var session := main.get_node("%NetworkSession") as NetworkSession
- assert(session.start_private_host(TEST_PORT))
+ assert(bool(main.call(
+ "_apply_world", WorldLayout.STARTER_ISLAND, 1, true
+ )))
+ assert(bool(main.call(
+ "_prepare_host_world", WorldLayout.STARTER_ISLAND, 1
+ )))
var save_manager := main.get("_save_manager") as PlayerSaveManager
- assert(save_manager.initialize_new_game())
+ assert(save_manager.initialize_new_game(1, WorldLayout.STARTER_ISLAND))
+ assert(session.start_private_host(TEST_PORT))
main.call("_enter_gameplay")
await physics_frame
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(
@@ -56,6 +64,40 @@ func _run_host() -> void:
remote_peer_id
)
assert(remote_record != null)
+ var network_home := main.get_node(
+ "%NetworkHomeService"
+ ) as NetworkHomeService
+ var home_world := main.get_node("%HomeWorldService") as HomeWorldService
+ var spawn_service := main.get_node(
+ "%PlayerSpawnService"
+ ) as PlayerSpawnService
+ assert(await _wait_for_home(
+ network_home, remote_record.identity_fingerprint
+ ))
+ var remote_avatar: Player = spawn_service.get_avatar(remote_peer_id)
+ assert(remote_avatar != null)
+ var exterior_spawn: Transform3D = home_world.get_exterior_spawn_transform(
+ remote_record.identity_fingerprint
+ )
+ var exterior_deadline: int = Time.get_ticks_msec() + 5000
+ while (
+ Time.get_ticks_msec() < exterior_deadline
+ and remote_avatar.global_position.distance_to(exterior_spawn.origin) > 0.25
+ ):
+ await physics_frame
+ assert(remote_avatar.global_position.distance_to(exterior_spawn.origin) <= 0.25)
+ var canvas_state: Dictionary = service.get_canvas_state(canvas_id)
+ var canvas_origin: Vector3 = SurfaceDrawingProtocol.array_to_vector(
+ canvas_state["origin"]
+ )
+ remote_avatar.global_position = Vector3(
+ canvas_origin.x + 1.0,
+ player.global_position.y,
+ canvas_origin.z,
+ )
+ session.publish_authoritative_teleport(remote_peer_id)
+ for _frame: int in 4:
+ await physics_frame
assert(session.peer_supports_capability(
remote_peer_id, SurfaceDrawingProtocol.CAPABILITY
))
@@ -81,6 +123,10 @@ func _run_host() -> void:
str(overwritten_cell["author_fingerprint"])
== remote_record.identity_fingerprint
)
+ assert(
+ remote_record.identity_fingerprint
+ in overwritten_state.get("participant_fingerprints", [])
+ )
assert(service.request_cell_edits(canvas_id, [{
"x": 8,
@@ -110,11 +156,70 @@ func _run_host() -> void:
assert(service.request_guide_visibility(canvas_id, false, true))
await create_timer(1.0).timeout
assert(service.clear_session_artwork())
+ var stamp_deadline: int = Time.get_ticks_msec() + WAIT_MSEC
+ while (
+ Time.get_ticks_msec() < stamp_deadline
+ and service.get_canvas_ids().is_empty()
+ ):
+ await process_frame
+ assert(service.get_canvas_ids().size() == 1)
+ var stamp_id: String = service.get_canvas_ids()[0]
+ var stamp_state: Dictionary = service.get_canvas_state(stamp_id)
+ assert(bool(stamp_state.get("stamp", false)))
+ assert(bool(stamp_state["finalized"]))
+ assert((stamp_state["cells"] as Array).size() == 1)
+ assert(str(stamp_state["cells"][0]["color_id"]) == "coral")
+ assert(
+ stamp_state.get("participant_fingerprints", [])
+ == [remote_record.identity_fingerprint]
+ )
+ # Give the client a full observation window before the authoritative clear.
await create_timer(1.0).timeout
+ assert(
+ service.clear_artwork_by_fingerprint(
+ remote_record.identity_fingerprint
+ ) == 1
+ )
+ assert(service.get_canvas_ids().is_empty())
+ var second_stamp_deadline: int = Time.get_ticks_msec() + WAIT_MSEC
+ while (
+ Time.get_ticks_msec() < second_stamp_deadline
+ and service.get_canvas_ids().is_empty()
+ ):
+ await process_frame
+ assert(service.get_canvas_ids().size() == 1)
+ # Keep the second stamp observable on the client before the ban removes all
+ # artwork carrying that participant fingerprint.
+ await create_timer(1.0).timeout
+ var player_list := main.get_node(
+ "%NetworkPlayerListService"
+ ) as NetworkPlayerListService
+ var remote_entry: PlayerListEntry = null
+ for entry: PlayerListEntry in player_list.get_entries():
+ if entry.peer_id == remote_peer_id:
+ remote_entry = entry
+ break
+ assert(remote_entry != null and remote_entry.can_ban)
+ assert(player_list.ban(
+ remote_peer_id,
+ remote_record.identity_fingerprint,
+ remote_record.display_name,
+ remote_entry.revision,
+ ))
+ assert(service.get_canvas_ids().is_empty())
+ var removed_deadline: int = Time.get_ticks_msec() + WAIT_MSEC
+ while (
+ Time.get_ticks_msec() < removed_deadline
+ and session.is_authenticated_peer(remote_peer_id)
+ ):
+ await process_frame
+ assert(not session.is_authenticated_peer(remote_peer_id))
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 +243,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(
@@ -153,6 +260,20 @@ func _run_client() -> void:
var canvas_id: String = service.get_canvas_ids()[0]
var snapshot: Dictionary = service.get_canvas_state(canvas_id)
assert(str(_find_cell(snapshot, 7, 7).get("color_id", "")) == "coral")
+ var canvas_origin: Vector3 = SurfaceDrawingProtocol.array_to_vector(
+ snapshot["origin"]
+ )
+ var placement_deadline: int = Time.get_ticks_msec() + 10000
+ while (
+ Time.get_ticks_msec() < placement_deadline
+ and player.global_position.distance_to(canvas_origin)
+ > NetworkSurfaceDrawingService.MAX_DRAW_DISTANCE + 2.0
+ ):
+ await physics_frame
+ assert(
+ player.global_position.distance_to(canvas_origin)
+ <= NetworkSurfaceDrawingService.MAX_DRAW_DISTANCE + 2.0
+ )
assert(service.request_cell_edits(canvas_id, [{
"x": 7,
"y": 7,
@@ -204,10 +325,67 @@ func _run_client() -> void:
if service.get_canvas_ids().is_empty():
break
assert(service.get_canvas_ids().is_empty())
+ var data_root := main.get("_data_root") as PlayerDataRoot
+ var stamp_directory: String = data_root.root_path.path_join("artwork")
+ assert(DirAccess.make_dir_recursive_absolute(stamp_directory) == OK)
+ var stamp_path: String = stamp_directory.path_join("multiplayer-stamp.png")
+ var stamp_image := Image.create(16, 16, false, Image.FORMAT_RGBA8)
+ stamp_image.fill(Color.TRANSPARENT)
+ stamp_image.set_pixel(3, 11, SurfaceDrawingPalette.get_color(&"coral"))
+ assert(stamp_image.save_png(stamp_path) == OK)
+ assert(service.select_saved_stamp(stamp_path))
+ var stamp_surface: Dictionary = _surface_below_local_player(main)
+ assert(not stamp_surface.is_empty())
+ assert(service.request_stamp_at_surface(
+ stamp_surface["position"], stamp_surface["normal"], Vector3.RIGHT
+ ))
+ var shared_stamp_deadline: int = Time.get_ticks_msec() + WAIT_MSEC
+ while (
+ Time.get_ticks_msec() < shared_stamp_deadline
+ and service.get_canvas_ids().is_empty()
+ ):
+ await process_frame
+ assert(service.get_canvas_ids().size() == 1)
+ var stamp_state: Dictionary = service.get_canvas_state(
+ service.get_canvas_ids()[0]
+ )
+ assert(bool(stamp_state.get("stamp", false)))
+ assert(bool(stamp_state["finalized"]))
+ assert(str(stamp_state["cells"][0]["color_id"]) == "coral")
+ assert(
+ session.get_local_identity_fingerprint()
+ in stamp_state.get("participant_fingerprints", [])
+ )
+ var clear_deadline: int = Time.get_ticks_msec() + WAIT_MSEC
+ while (
+ Time.get_ticks_msec() < clear_deadline
+ and not service.get_canvas_ids().is_empty()
+ ):
+ await process_frame
+ assert(service.get_canvas_ids().is_empty())
+ assert(service.request_stamp_at_surface(
+ stamp_surface["position"], stamp_surface["normal"], Vector3.RIGHT
+ ))
+ var second_stamp_deadline: int = Time.get_ticks_msec() + WAIT_MSEC
+ while (
+ Time.get_ticks_msec() < second_stamp_deadline
+ and service.get_canvas_ids().is_empty()
+ ):
+ await process_frame
+ assert(service.get_canvas_ids().size() == 1)
+ var ban_deadline: int = Time.get_ticks_msec() + WAIT_MSEC
+ while (
+ Time.get_ticks_msec() < ban_deadline
+ and session.state != NetworkSession.State.SERVER_LOST
+ ):
+ await process_frame
+ assert(session.state == NetworkSession.State.SERVER_LOST)
+ assert(service.get_canvas_ids().is_empty())
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()
@@ -256,3 +434,18 @@ func _create_initialized_main() -> Node:
await process_frame
assert(bool(main.get("_application_initialized")))
return main
+
+
+func _wait_for_home(
+ network_home: NetworkHomeService,
+ fingerprint: String,
+) -> bool:
+ var deadline: int = Time.get_ticks_msec() + 10000
+ while Time.get_ticks_msec() < deadline:
+ await process_frame
+ var manifests: Dictionary = network_home.get(
+ "_manifests_by_fingerprint"
+ )
+ if manifests.has(fingerprint):
+ return true
+ return false
diff --git a/tests/surface_drawing_runtime_validation.gd b/tests/surface_drawing_runtime_validation.gd
index 6e6e68d..85110cd 100644
--- a/tests/surface_drawing_runtime_validation.gd
+++ b/tests/surface_drawing_runtime_validation.gd
@@ -20,21 +20,28 @@ func _run() -> void:
await process_frame
assert(bool(main.get("_application_initialized")))
var session := main.get_node("%NetworkSession") as NetworkSession
+ assert(bool(main.call(
+ "_apply_world", WorldLayout.STARTER_ISLAND, 1, true
+ )))
+ assert(session.set_host_world(WorldLayout.STARTER_ISLAND, 1))
assert(session.start_private_host(TEST_PORT))
var save_manager := main.get("_save_manager") as PlayerSaveManager
- assert(save_manager.initialize_new_game())
+ assert(save_manager.initialize_new_game(1, WorldLayout.STARTER_ISLAND))
main.call("_enter_gameplay")
await physics_frame
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(
"%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,
@@ -64,6 +71,28 @@ func _run() -> void:
state["cells"][0]["author_fingerprint"]
== session.get_local_identity_fingerprint()
)
+ service.set("_last_hovered_canvas_id", canvas_id)
+ service.set("_hovered_canvas_id", "")
+ var game_ui := main.get_node("%GameUI") as GameUI
+ var toolbar := game_ui.get_node(
+ "%SurfaceDrawingToolbar"
+ ) as SurfaceDrawingToolbar
+ var export_button := toolbar.get_node("%ExportButton") as Button
+ export_button.pressed.emit()
+ var exported_entries: Array[Dictionary] = service.get_saved_stamp_entries()
+ assert(exported_entries.size() == 1)
+ var export_path: String = str(exported_entries[0]["path"])
+ assert(not export_path.is_empty())
+ var data_root := main.get("_data_root") as PlayerDataRoot
+ assert(export_path.begins_with(data_root.root_path.path_join("artwork")))
+ assert(FileAccess.file_exists(export_path))
+ var status_label := game_ui.get_node("%StatusLabel") as Label
+ assert(status_label.text == "artwork exported to the data folder • artwork")
+ var exported_image: Image = Image.load_from_file(export_path)
+ assert(exported_image.get_size() == Vector2i(16, 16))
+ assert(exported_image.get_pixel(8, 7).is_equal_approx(
+ SurfaceDrawingPalette.get_color(&"ocean_teal")
+ ))
assert(service.request_guide_visibility(canvas_id, false))
state = service.get_canvas_state(canvas_id)
assert(not bool(state["guide_visible"]))
@@ -75,6 +104,7 @@ func _run() -> void:
assert(service.request_guide_visibility(canvas_id, false, true))
assert(bool(service.get_canvas_state(canvas_id)["finalized"]))
assert(canvas.is_finalized())
+ assert(not canvas.has_guide_geometry())
assert(service.request_canvas_at_surface(
hit["position"], hit["normal"], Vector3.RIGHT
))
@@ -106,11 +136,45 @@ func _run() -> void:
assert(player_list.get_session_artwork_counts() == Vector2i(2, 1))
assert(player_list.reset_session_artwork())
assert(service.get_canvas_ids().is_empty())
+ var stamp_entries: Array[Dictionary] = service.get_saved_stamp_entries()
+ assert(stamp_entries.size() == 1)
+ assert(str(stamp_entries[0]["path"]) == export_path)
+ assert(int(stamp_entries[0]["grid_size"]) == 16)
+ assert(bool(stamp_entries[0]["available"]))
+ service.activate(Vector2(640.0, 360.0))
+ assert(service.select_saved_stamp(export_path))
+ assert(service.is_stamp_mode())
+ assert(service.request_stamp_at_surface(
+ hit["position"], hit["normal"], Vector3.RIGHT
+ ))
+ assert(service.get_canvas_ids().size() == 1)
+ var stamp_id: String = service.get_canvas_ids()[0]
+ var stamp_state: Dictionary = service.get_canvas_state(stamp_id)
+ assert(bool(stamp_state["stamp"]))
+ assert(bool(stamp_state["finalized"]))
+ assert(not bool(stamp_state["guide_visible"]))
+ assert((stamp_state["cells"] as Array).size() == 1)
+ assert(int(stamp_state["cells"][0]["x"]) == 8)
+ assert(int(stamp_state["cells"][0]["y"]) == 8)
+ assert(str(stamp_state["cells"][0]["color_id"]) == "ocean_teal")
+ assert(
+ stamp_state.get("participant_fingerprints", [])
+ == [session.get_local_identity_fingerprint()]
+ )
+ var stamp_canvas_nodes: Dictionary = service.get("_canvas_nodes")
+ var stamp_canvas := stamp_canvas_nodes[stamp_id] as SurfaceDrawingCanvas
+ assert(stamp_canvas.is_finalized())
+ assert(not stamp_canvas.has_guide_geometry())
+ service.set_placement_mode(false)
+ assert(not service.is_stamp_mode())
+ assert(service.clear_session_artwork())
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()
@@ -122,7 +186,7 @@ func _validate_marker_controls(
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)
@@ -131,7 +195,7 @@ 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()
@@ -169,6 +233,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_texture_renderer_validation.gd b/tests/surface_drawing_texture_renderer_validation.gd
new file mode 100644
index 0000000..ed99383
--- /dev/null
+++ b/tests/surface_drawing_texture_renderer_validation.gd
@@ -0,0 +1,101 @@
+extends SceneTree
+
+const CANVAS_COUNT: int = 5
+const GRID_SIZE: int = 32
+const FINGERPRINT: String = (
+ "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"
+)
+
+
+func _initialize() -> void:
+ call_deferred("_run")
+
+
+func _run() -> void:
+ var world := Node3D.new()
+ root.add_child(world)
+ var color_ids: Array[StringName] = SurfaceDrawingPalette.get_color_ids()
+ var cells: Array[Dictionary] = []
+ for y: int in range(GRID_SIZE):
+ for x: int in range(GRID_SIZE):
+ cells.append({
+ "x": x,
+ "y": y,
+ "color_id": str(color_ids[(x + y) % color_ids.size()]),
+ "author_fingerprint": FINGERPRINT,
+ })
+ var canvases: Array[SurfaceDrawingCanvas] = []
+ var persistent_meshes: Array[Mesh] = []
+ var persistent_textures: Array[Texture2D] = []
+ for canvas_index: int in range(CANVAS_COUNT):
+ var canvas := SurfaceDrawingCanvas.new()
+ world.add_child(canvas)
+ assert(canvas.setup({
+ "session_id": "texture-renderer-session",
+ "canvas_id": "canvas-%d" % canvas_index,
+ "origin": [float(canvas_index) * 3.0, 0.0, 0.0],
+ "normal": [0.0, 1.0, 0.0],
+ "tangent": [1.0, 0.0, 0.0],
+ "width": GRID_SIZE,
+ "height": GRID_SIZE,
+ "cell_size": SurfaceDrawingProtocol.CELL_SIZE,
+ "revision": 0,
+ "guide_visible": true,
+ "finalized": false,
+ "layer": canvas_index,
+ "creator_fingerprint": FINGERPRINT,
+ "cells": cells,
+ }, null))
+ assert(canvas.get_rendered_pixel_count() == GRID_SIZE * GRID_SIZE)
+ var pixel_instance := canvas.get("_pixel_instance") as MeshInstance3D
+ assert(pixel_instance != null)
+ assert(pixel_instance.mesh.get_surface_count() == 1)
+ var material := pixel_instance.mesh.surface_get_material(
+ 0
+ ) as StandardMaterial3D
+ assert(material != null)
+ assert(material.albedo_texture is ImageTexture)
+ canvases.append(canvas)
+ persistent_meshes.append(pixel_instance.mesh)
+ persistent_textures.append(material.albedo_texture)
+ for canvas_index: int in range(CANVAS_COUNT):
+ var canvas: SurfaceDrawingCanvas = canvases[canvas_index]
+ assert(canvas.apply_update({
+ "session_id": "texture-renderer-session",
+ "canvas_id": "canvas-%d" % canvas_index,
+ "revision": 1,
+ "edits": [{
+ "x": canvas_index,
+ "y": canvas_index,
+ "color_id": "coral",
+ "author_fingerprint": FINGERPRINT,
+ }],
+ }))
+ var pixel_instance := canvas.get("_pixel_instance") as MeshInstance3D
+ var material := pixel_instance.mesh.surface_get_material(
+ 0
+ ) as StandardMaterial3D
+ assert(pixel_instance.mesh == persistent_meshes[canvas_index])
+ assert(material.albedo_texture == persistent_textures[canvas_index])
+ assert(canvas.apply_guide_update({
+ "session_id": "texture-renderer-session",
+ "canvas_id": "canvas-%d" % canvas_index,
+ "revision": 2,
+ "guide_visible": false,
+ "finalized": true,
+ }))
+ assert(not canvas.has_guide_geometry())
+ await process_frame
+ for canvas: SurfaceDrawingCanvas in canvases:
+ var artwork_mesh_count: int = 0
+ for child: Node in canvas.get_children():
+ if child is MultiMeshInstance3D:
+ assert(false, "Art Kit pixels regressed to per-color MultiMeshes.")
+ if child is MeshInstance3D:
+ artwork_mesh_count += 1
+ assert(artwork_mesh_count == 1)
+ assert(canvas.get_export_image().get_size() == Vector2i(32, 32))
+ world.queue_free()
+ await process_frame
+ print("Surface drawing texture renderer validation: PASS")
+ quit()
diff --git a/tests/surface_drawing_texture_renderer_validation.gd.uid b/tests/surface_drawing_texture_renderer_validation.gd.uid
new file mode 100644
index 0000000..c64b594
--- /dev/null
+++ b/tests/surface_drawing_texture_renderer_validation.gd.uid
@@ -0,0 +1 @@
+uid://qj7jd2p3b4p8
diff --git a/tests/surface_drawing_validation.gd b/tests/surface_drawing_validation.gd
index aa94f0f..ac48a67 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()
@@ -88,6 +90,21 @@ func _validate_protocol_bounds() -> void:
unsupported_size["width"] = 48
unsupported_size["height"] = 48
assert(not SurfaceDrawingProtocol.validate_canvas_request(unsupported_size))
+ var stamp_pixels := PackedByteArray()
+ stamp_pixels.resize(16 * 16)
+ stamp_pixels[0] = 1
+ var stamp_request: Dictionary = canvas_request.duplicate(true)
+ stamp_request["request_id"] = "stamp-1"
+ stamp_request["pixels"] = stamp_pixels
+ assert(SurfaceDrawingProtocol.validate_stamp_request(stamp_request))
+ var invalid_stamp: Dictionary = stamp_request.duplicate(true)
+ var invalid_pixels: PackedByteArray = invalid_stamp["pixels"]
+ invalid_pixels[0] = SurfaceDrawingPalette.COLORS.size() + 1
+ invalid_stamp["pixels"] = invalid_pixels
+ assert(not SurfaceDrawingProtocol.validate_stamp_request(invalid_stamp))
+ var empty_stamp: Dictionary = stamp_request.duplicate(true)
+ empty_stamp["pixels"] = PackedByteArray()
+ assert(not SurfaceDrawingProtocol.validate_stamp_request(empty_stamp))
var guide_request: Dictionary = {
"request_id": "guide-1",
"session_id": "session",
@@ -114,10 +131,13 @@ func _validate_art_unlocks() -> void:
assert(unlocks.unlock_product(&"marker_ocean_teal"))
assert(unlocks.unlock_product(&"brush_4x"))
assert(unlocks.unlock_product(&"grid_128x"))
+ assert(unlocks.unlock_product(PlayerArtUnlocks.STAMP_PRODUCT_ID))
assert(unlocks.is_color_unlocked(&"ocean_teal"))
assert(unlocks.is_brush_size_unlocked(4))
assert(unlocks.is_grid_size_unlocked(128))
+ assert(unlocks.is_stamp_unlocked())
assert(not unlocks.restore_mask(PlayerArtUnlocks.ALL_UNLOCK_MASK + 1))
+ unlocks.free()
func _validate_grid_snapping() -> void:
@@ -174,6 +194,16 @@ func _validate_canvas_geometry_and_collaboration() -> void:
"cells": [],
}
assert(canvas.setup(state, null))
+ var pixel_instance := canvas.get("_pixel_instance") as MeshInstance3D
+ assert(pixel_instance != null)
+ assert(pixel_instance.name == "ArtworkTexture")
+ var persistent_mesh: Mesh = pixel_instance.mesh
+ var pixel_material := persistent_mesh.surface_get_material(
+ 0
+ ) as StandardMaterial3D
+ assert(pixel_material != null)
+ var persistent_texture: Texture2D = pixel_material.albedo_texture
+ assert(persistent_texture is ImageTexture)
var first_center: Vector3 = canvas.get_cell_world_position(0, 0)
assert(canvas.cell_at_world_point(first_center) == Vector2i(0, 0))
assert(canvas.contains_world_point(first_center))
@@ -189,6 +219,15 @@ func _validate_canvas_geometry_and_collaboration() -> void:
}],
}
assert(canvas.apply_update(first_update))
+ assert(pixel_instance.mesh == persistent_mesh)
+ assert(pixel_material.albedo_texture == persistent_texture)
+ assert(canvas.get_rendered_pixel_count() == 1)
+ var export_image: Image = canvas.get_export_image()
+ assert(export_image.get_size() == Vector2i(16, 16))
+ assert(export_image.get_pixel(3, 11).is_equal_approx(
+ SurfaceDrawingPalette.get_color(&"coral")
+ ))
+ assert(export_image.get_pixel(0, 0).is_equal_approx(Color.TRANSPARENT))
var finish_update: Dictionary = {
"session_id": "session",
"canvas_id": "canvas-1",
@@ -199,6 +238,9 @@ func _validate_canvas_geometry_and_collaboration() -> void:
assert(canvas.apply_guide_update(finish_update))
assert(not canvas.is_guide_visible())
assert(canvas.is_finalized())
+ assert(not canvas.has_guide_geometry())
+ assert(pixel_instance.mesh == persistent_mesh)
+ assert(pixel_material.albedo_texture == persistent_texture)
await process_frame
var finished_pixel_scale: float = canvas.get_rendered_pixel_size()
assert(
@@ -220,6 +262,8 @@ func _validate_canvas_geometry_and_collaboration() -> void:
}],
}
assert(canvas.apply_update(second_update))
+ assert(pixel_instance.mesh == persistent_mesh)
+ assert(pixel_material.albedo_texture == persistent_texture)
var cells: Array[Dictionary] = canvas.get_authoritative_cells()
assert(cells.size() == 1)
assert(cells[0]["color_id"] == "blue")
@@ -237,6 +281,10 @@ func _validate_canvas_geometry_and_collaboration() -> void:
}
assert(canvas.apply_update(erase_update))
assert(canvas.get_authoritative_cells().is_empty())
+ assert(canvas.get_rendered_pixel_count() == 0)
+ assert(canvas.get_export_image().get_pixel(3, 11).is_equal_approx(
+ Color.TRANSPARENT
+ ))
world.queue_free()
await process_frame
@@ -265,6 +313,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 = {
@@ -278,6 +327,7 @@ func _validate_blocked_author_visibility() -> void:
"cell_size": SurfaceDrawingProtocol.CELL_SIZE,
"revision": 1,
"creator_fingerprint": FINGERPRINT_A,
+ "participant_fingerprints": [FINGERPRINT_A, FINGERPRINT_B],
"cells": [
{
"x": 1,
@@ -294,20 +344,30 @@ func _validate_blocked_author_visibility() -> void:
],
}
assert(canvas.setup(state, relationships))
- assert(_rendered_pixel_count(canvas) == 1)
+ assert(canvas.is_hidden_by_relationship())
+ assert(canvas.get_rendered_pixel_count() == 0)
relationships.set("_records", {})
canvas.refresh_relationship_visibility()
await process_frame
- assert(_rendered_pixel_count(canvas) == 2)
+ assert(not canvas.is_hidden_by_relationship())
+ assert(canvas.get_rendered_pixel_count() == 2)
+ relationships.set("_records", {
+ FINGERPRINT_B: {"blocked": true, "muted": true},
+ })
+ assert(canvas.apply_update({
+ "session_id": "session",
+ "canvas_id": "blocked-author-canvas",
+ "revision": 2,
+ "participant_fingerprints": [FINGERPRINT_A, FINGERPRINT_B],
+ "edits": [{
+ "x": 2,
+ "y": 1,
+ "color_id": "",
+ "author_fingerprint": "",
+ }],
+ }))
+ canvas.refresh_relationship_visibility()
+ assert(canvas.is_hidden_by_relationship())
+ assert(canvas.get_rendered_pixel_count() == 0)
world.queue_free()
await process_frame
-
-
-func _rendered_pixel_count(canvas: SurfaceDrawingCanvas) -> int:
- var count: int = 0
- for child: Node in canvas.get_children():
- if child is MultiMeshInstance3D:
- var instance := child as MultiMeshInstance3D
- if instance.multimesh != null:
- count += instance.multimesh.instance_count
- return count
diff --git a/tests/tackle_order_validation.gd b/tests/tackle_order_validation.gd
new file mode 100644
index 0000000..5a38e06
--- /dev/null
+++ b/tests/tackle_order_validation.gd
@@ -0,0 +1,61 @@
+extends SceneTree
+
+const Catalog: ItemCatalog = preload("res://items/catalog/item_catalog.tres")
+const PlayerMenuType = preload("res://ui/player_menu.gd")
+
+const EXPECTED_BAIT_ORDER: Array[StringName] = [
+ &"worms",
+ &"snails",
+ &"shrimp",
+ &"squid_chunks",
+ &"whole_sardine",
+ &"whole_anchovy",
+ &"luminous_roe",
+]
+
+
+func _initialize() -> void:
+ call_deferred("_run")
+
+
+func _run() -> void:
+ var player_menu := PlayerMenuType.new() as PlayerMenu
+ player_menu.set("_item_catalog", Catalog)
+ var tackle_items: Array[OwnedItem] = []
+ for item_id: StringName in [
+ &"luminous_roe",
+ &"whole_anchovy",
+ &"shrimp",
+ &"snails",
+ &"whole_sardine",
+ &"squid_chunks",
+ &"worms",
+ ]:
+ var owned := OwnedItem.new()
+ owned.item_id = item_id
+ owned.quantity = 1
+ tackle_items.append(owned)
+ tackle_items.sort_custom(
+ func(first: OwnedItem, second: OwnedItem) -> bool:
+ return bool(player_menu.call(
+ "_sort_tackle_items", first, second
+ ))
+ )
+ var sorted_ids: Array[StringName] = []
+ for owned: OwnedItem in tackle_items:
+ sorted_ids.append(owned.item_id)
+ assert(sorted_ids == EXPECTED_BAIT_ORDER)
+ var bag := PlayerBag.new()
+ bag.setup(Catalog)
+ assert(bag.add_item(&"worms", 1))
+ assert(bag.add_item(&"shrimp", 1))
+ var worms_slot: int = bag.get_storage_slot(&"worms")
+ var shrimp_slot: int = bag.get_storage_slot(&"shrimp")
+ assert(worms_slot >= 0 and shrimp_slot >= 0 and worms_slot != shrimp_slot)
+ assert(bag.move_item_to_storage_slot(&"worms", shrimp_slot))
+ assert(bag.get_storage_slot(&"worms") == shrimp_slot)
+ assert(bag.get_storage_slot(&"shrimp") == worms_slot)
+ bag.free()
+ player_menu.free()
+ print("Tackle order validation: PASS")
+ quit()
diff --git a/tests/tackle_order_validation.gd.uid b/tests/tackle_order_validation.gd.uid
new file mode 100644
index 0000000..353ba7e
--- /dev/null
+++ b/tests/tackle_order_validation.gd.uid
@@ -0,0 +1 @@
+uid://27tkdvxgkv3y
diff --git a/tests/terrain_biome_validation.gd b/tests/terrain_biome_validation.gd
new file mode 100644
index 0000000..968ff5e
--- /dev/null
+++ b/tests/terrain_biome_validation.gd
@@ -0,0 +1,135 @@
+extends SceneTree
+
+const BIOME_CATALOG: TerrainBiomeCatalog = preload(
+ "res://world/generation/biomes/terrain_biome_catalog.tres"
+)
+const PROP_CATALOG: TerrainPropCatalog = preload(
+ "res://world/generation/props/terrain_prop_catalog.tres"
+)
+const EXPECTED_BIOMES: Array[StringName] = [
+ &"biome_plains",
+ &"biome_forest",
+ &"biome_pine_forest",
+ &"biome_coast",
+]
+const TEST_SEED := 13001
+
+var _failures: Array[String] = []
+
+
+func _initialize() -> void:
+ call_deferred(&"_run")
+
+
+func _run() -> void:
+ _validate_catalog()
+ _validate_assignment()
+ _finish()
+
+
+func _validate_catalog() -> void:
+ for error: String in BIOME_CATALOG.validation_errors():
+ _check(false, error)
+ _check(
+ BIOME_CATALOG.definitions.size() == EXPECTED_BIOMES.size(),
+ "The biome catalog must contain all initial authored profiles.",
+ )
+ for biome_id: StringName in EXPECTED_BIOMES:
+ var biome := BIOME_CATALOG.definition_for_id(biome_id)
+ _check(biome != null, "The biome catalog is missing %s." % biome_id)
+ if biome == null:
+ continue
+ for rule: TerrainBiomePropRule in biome.prop_rules:
+ for prop_id_value: String in rule.allowed_prop_ids:
+ var prop := PROP_CATALOG.definition_for_id(
+ StringName(prop_id_value)
+ )
+ _check(
+ prop != null and rule.allows_prop(prop),
+ "%s has an invalid %s prop rule."
+ % [biome_id, prop_id_value],
+ )
+
+
+func _validate_assignment() -> void:
+ var records := _synthetic_records()
+ var first := TerrainBiomeAssigner.assign(
+ BIOME_CATALOG,
+ records,
+ TEST_SEED,
+ )
+ var second := TerrainBiomeAssigner.assign(
+ BIOME_CATALOG,
+ records,
+ TEST_SEED,
+ )
+ _check(first == second, "Biome assignment must be deterministic by seed.")
+ _check(
+ TerrainBiomeAssigner.fingerprint(first).length() == 64,
+ "Biome assignments must expose a SHA-256 fingerprint.",
+ )
+ var counts := TerrainBiomeAssigner.counts(first)
+ for biome_id: StringName in EXPECTED_BIOMES:
+ _check(
+ counts.get(biome_id, 0) > 0,
+ "The synthetic map must contain %s." % biome_id,
+ )
+ _check(
+ first.get(Vector2i(3, 3), &"") == &"biome_plains",
+ "The spawn-tagged center must remain plains.",
+ )
+ for record: Dictionary in records:
+ var coordinate: Vector2i = record["coordinate"]
+ var tags: PackedStringArray = record["tags"]
+ var biome_id: StringName = first.get(coordinate, &"")
+ if "fresh_water" in tags:
+ _check(
+ biome_id == &"",
+ "Freshwater-only chunks must not inherit a land-prop biome.",
+ )
+ continue
+ var biome := BIOME_CATALOG.definition_for_id(biome_id)
+ _check(
+ biome != null and biome.supports_chunk_tags(tags),
+ "Biome %s does not support chunk %s."
+ % [biome_id, coordinate],
+ )
+
+
+func _synthetic_records() -> Array[Dictionary]:
+ var result: Array[Dictionary] = []
+ for row: int in 7:
+ for column: int in 7:
+ var coordinate := Vector2i(column, row)
+ var tags := PackedStringArray(["land", "walkable", "grass"])
+ if row == 0:
+ tags = PackedStringArray(["land", "walkable", "sand"])
+ elif coordinate == Vector2i(0, 6):
+ tags = PackedStringArray([
+ "land",
+ "walkable",
+ "fresh_water",
+ "pond",
+ ])
+ elif coordinate == Vector2i(3, 3):
+ tags.append("spawn")
+ result.append({
+ "coordinate": coordinate,
+ "tags": tags,
+ })
+ return result
+
+
+func _check(condition: bool, message: String) -> void:
+ if not condition:
+ _failures.append(message)
+
+
+func _finish() -> void:
+ if _failures.is_empty():
+ print("Terrain biome validation: PASS")
+ quit(0)
+ return
+ for failure: String in _failures:
+ printerr("Terrain biome validation: ", failure)
+ quit(1)
diff --git a/tests/terrain_biome_validation.gd.uid b/tests/terrain_biome_validation.gd.uid
new file mode 100644
index 0000000..40de9e4
--- /dev/null
+++ b/tests/terrain_biome_validation.gd.uid
@@ -0,0 +1 @@
+uid://cxioi62f6oap0
diff --git a/tests/terrain_blender_material_validation.gd b/tests/terrain_blender_material_validation.gd
index 8a17122..fe3de22 100644
--- a/tests/terrain_blender_material_validation.gd
+++ b/tests/terrain_blender_material_validation.gd
@@ -49,6 +49,7 @@ func _run() -> void:
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()
@@ -118,6 +119,62 @@ func _validate_terrain_materials(
)
+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)
@@ -125,9 +182,9 @@ func _check(condition: bool, message: String) -> void:
func _finish() -> void:
if failures.is_empty():
- print("Blender terrain material validation: PASS")
+ print("Blender terrain and collision validation: PASS")
quit(0)
return
for failure: String in failures:
- printerr("Blender terrain material validation: ", failure)
+ printerr("Blender terrain and collision validation: ", failure)
quit(1)
diff --git a/tests/terrain_chunk_generator_validation.gd b/tests/terrain_chunk_generator_validation.gd
new file mode 100644
index 0000000..ec736d9
--- /dev/null
+++ b/tests/terrain_chunk_generator_validation.gd
@@ -0,0 +1,2082 @@
+extends SceneTree
+
+const CATALOG: TerrainChunkCatalog = preload(
+ "res://world/generation/chunks/terrain_chunk_catalog.tres"
+)
+const EXPECTED_IDS: Array[String] = [
+ "chunk_0000",
+ "chunk_0001",
+ "chunk_0002",
+ "chunk_0003",
+ "chunk_0004",
+ "chunk_0005",
+ "chunk_0006",
+ "chunk_0007",
+ "chunk_0008",
+ "chunk_0009",
+ "chunk_0010",
+ "chunk_0011",
+ "chunk_0012",
+ "chunk_0013",
+ "chunk_0014",
+ "chunk_0015",
+ "chunk_0016",
+ "chunk_0017",
+ "chunk_0018",
+ "chunk_0019",
+ "chunk_0020",
+ "chunk_0021",
+ "chunk_0022",
+ "chunk_0023",
+ "chunk_0024",
+ "chunk_0025",
+ "chunk_0026",
+ "chunk_0027",
+ "chunk_0028",
+ "chunk_0029",
+ "chunk_0030",
+ "chunk_0031",
+ "chunk_0032",
+ "chunk_0033",
+ "chunk_0034",
+ "chunk_0035",
+ "chunk_0036",
+ "chunk_0037",
+ "chunk_0038",
+ "chunk_9999",
+ "chunk_9998",
+ "chunk_9997",
+ "chunk_9996",
+ "chunk_9995",
+ "chunk_spawn",
+]
+const REQUIRED_IDS: Array[String] = [
+ "chunk_spawn",
+ "chunk_0001",
+ "chunk_0002",
+ "chunk_0003",
+ "chunk_0004",
+]
+const TEST_SEED := 13001
+const CONNECTOR_STRESS_SEED := 287
+const EXPECTED_SOLVER_VARIANT_COUNT := 35
+const EXPECTED_VARIANT_COUNTS: Dictionary[StringName, int] = {
+ &"chunk_0000": 4,
+ &"chunk_0001": 4,
+ &"chunk_0002": 4,
+ &"chunk_0003": 4,
+ &"chunk_0004": 4,
+ &"chunk_0005": 4,
+ &"chunk_0006": 4,
+ &"chunk_0007": 4,
+ &"chunk_0008": 4,
+ &"chunk_0009": 4,
+ &"chunk_0010": 4,
+ &"chunk_0011": 4,
+ &"chunk_0012": 4,
+ &"chunk_0013": 4,
+ &"chunk_0014": 4,
+ &"chunk_0015": 4,
+ &"chunk_0016": 4,
+ &"chunk_0017": 4,
+ &"chunk_0018": 4,
+ &"chunk_0019": 1,
+ &"chunk_0020": 4,
+ &"chunk_0021": 4,
+ &"chunk_0022": 4,
+ &"chunk_0023": 4,
+ &"chunk_0024": 4,
+ &"chunk_0025": 4,
+ &"chunk_0026": 4,
+ &"chunk_0027": 4,
+ &"chunk_0028": 4,
+ &"chunk_0029": 4,
+ &"chunk_0030": 4,
+ &"chunk_0031": 4,
+ &"chunk_0032": 4,
+ &"chunk_0033": 4,
+ &"chunk_0034": 4,
+ &"chunk_0035": 4,
+ &"chunk_0036": 4,
+ &"chunk_0037": 4,
+ &"chunk_0038": 4,
+ &"chunk_9999": 4,
+ &"chunk_9998": 4,
+ &"chunk_9997": 4,
+ &"chunk_9996": 4,
+ &"chunk_9995": 4,
+ &"chunk_spawn": 1,
+}
+
+var _failures: Array[String] = []
+
+
+func _initialize() -> void:
+ call_deferred(&"_run")
+
+
+func _run() -> void:
+ _validate_catalog()
+ _validate_profiles()
+ _validate_second_tier_coastal_layout()
+ _validate_generation()
+ _finish()
+
+
+func _validate_catalog() -> void:
+ for error: String in CATALOG.validation_errors():
+ _check(false, error)
+ _check(
+ CATALOG.definitions.size() == EXPECTED_IDS.size(),
+ "Catalog must contain the authored chunks and spawn definition.",
+ )
+ for stable_id: String in EXPECTED_IDS:
+ var definition := CATALOG.definition_for_id(StringName(stable_id))
+ _check(definition != null, "Catalog is missing %s." % stable_id)
+ if definition == null:
+ continue
+ _check(
+ definition.packed_scene != null,
+ "%s must reference an imported GLB." % stable_id,
+ )
+ var grass := CATALOG.definition_for_id(&"chunk_0000")
+ var sand := CATALOG.definition_for_id(&"chunk_0001")
+ var beach := CATALOG.definition_for_id(&"chunk_0002")
+ var grass_ocean_edge := CATALOG.definition_for_id(&"chunk_0005")
+ var grass_beach_transition := CATALOG.definition_for_id(&"chunk_0006")
+ var grass_ocean_corner := CATALOG.definition_for_id(&"chunk_0007")
+ var beach_ocean_corner := CATALOG.definition_for_id(&"chunk_0008")
+ var grass_sand_diagonal := CATALOG.definition_for_id(&"chunk_0013")
+ var pond := CATALOG.definition_for_id(&"chunk_0004")
+ var cliff_sea_edge := CATALOG.definition_for_id(&"chunk_0014")
+ var cliff_sea_corner := CATALOG.definition_for_id(&"chunk_0015")
+ var cliff_sea_transition_right := CATALOG.definition_for_id(&"chunk_9999")
+ var cliff_sea_transition_left := CATALOG.definition_for_id(&"chunk_9998")
+ var cliff_beach_transition_right := CATALOG.definition_for_id(&"chunk_9997")
+ var cliff_beach_transition_left := CATALOG.definition_for_id(&"chunk_9996")
+ var stream_variant := CATALOG.definition_for_id(&"chunk_0016")
+ var lake_edge := CATALOG.definition_for_id(&"chunk_0017")
+ var lake_edge_variant := CATALOG.definition_for_id(&"chunk_0037")
+ var lake_corner := CATALOG.definition_for_id(&"chunk_0018")
+ var lake_fill := CATALOG.definition_for_id(&"chunk_0019")
+ var lake_diagonal := CATALOG.definition_for_id(&"chunk_0020")
+ var lake_narrow := CATALOG.definition_for_id(&"chunk_0021")
+ var lake_regular_to_narrow := CATALOG.definition_for_id(&"chunk_0022")
+ var lake_narrow_to_regular := CATALOG.definition_for_id(&"chunk_0023")
+ var river_edge := CATALOG.definition_for_id(&"chunk_0024")
+ var river_edge_variant := CATALOG.definition_for_id(&"chunk_0025")
+ var river_stone_bridge := CATALOG.definition_for_id(&"chunk_0038")
+ var river_source_right := CATALOG.definition_for_id(&"chunk_0029")
+ var river_source_left := CATALOG.definition_for_id(&"chunk_0030")
+ var river_outlet_right := CATALOG.definition_for_id(&"chunk_0031")
+ var river_outlet_left := CATALOG.definition_for_id(&"chunk_0032")
+ var river_bend_c := CATALOG.definition_for_id(&"chunk_0033")
+ var river_bend_b := CATALOG.definition_for_id(&"chunk_0034")
+ var river_bend_a := CATALOG.definition_for_id(&"chunk_0035")
+ var river_bend_d := CATALOG.definition_for_id(&"chunk_0036")
+ var river_cap := CATALOG.definition_for_id(&"chunk_9995")
+ var cliff_top := CATALOG.definition_for_id(&"chunk_0009")
+ var cliff_corner := CATALOG.definition_for_id(&"chunk_0010")
+ var cliff_edge := CATALOG.definition_for_id(&"chunk_0011")
+ var cliff_ramp := CATALOG.definition_for_id(&"chunk_0012")
+ var cliff_third_tier_edge := CATALOG.definition_for_id(&"chunk_0026")
+ var cliff_third_tier_corner := CATALOG.definition_for_id(&"chunk_0027")
+ var cliff_third_tier_top := CATALOG.definition_for_id(&"chunk_0028")
+ var spawn := CATALOG.definition_for_id(&"chunk_spawn")
+ _check(
+ (
+ grass != null
+ and sand != null
+ and beach != null
+ and grass_ocean_edge != null
+ and grass_beach_transition != null
+ and grass_ocean_corner != null
+ and beach_ocean_corner != null
+ and grass_sand_diagonal != null
+ and pond != null
+ and cliff_sea_edge != null
+ and cliff_sea_corner != null
+ and cliff_sea_transition_right != null
+ and cliff_sea_transition_left != null
+ and cliff_beach_transition_right != null
+ and cliff_beach_transition_left != null
+ and stream_variant != null
+ and lake_edge != null
+ and lake_edge_variant != null
+ and lake_corner != null
+ and lake_fill != null
+ and lake_diagonal != null
+ and lake_narrow != null
+ and lake_regular_to_narrow != null
+ and lake_narrow_to_regular != null
+ and river_edge != null
+ and river_edge_variant != null
+ and river_stone_bridge != null
+ and river_source_right != null
+ and river_source_left != null
+ and river_outlet_right != null
+ and river_outlet_left != null
+ and river_bend_a != null
+ and river_bend_b != null
+ and river_bend_c != null
+ and river_bend_d != null
+ and river_cap != null
+ and cliff_top != null
+ and cliff_corner != null
+ and cliff_edge != null
+ and cliff_ramp != null
+ and cliff_third_tier_edge != null
+ and cliff_third_tier_corner != null
+ and cliff_third_tier_top != null
+ and spawn != null
+ ),
+ "The authored biome-rule definitions must be available.",
+ )
+ if (
+ grass != null
+ and sand != null
+ and beach != null
+ and grass_ocean_edge != null
+ and grass_beach_transition != null
+ and grass_ocean_corner != null
+ and beach_ocean_corner != null
+ and grass_sand_diagonal != null
+ and pond != null
+ and cliff_sea_edge != null
+ and cliff_sea_corner != null
+ and cliff_sea_transition_right != null
+ and cliff_sea_transition_left != null
+ and cliff_beach_transition_right != null
+ and cliff_beach_transition_left != null
+ and stream_variant != null
+ and lake_edge != null
+ and lake_edge_variant != null
+ and lake_corner != null
+ and lake_fill != null
+ and lake_diagonal != null
+ and lake_narrow != null
+ and lake_regular_to_narrow != null
+ and lake_narrow_to_regular != null
+ and river_edge != null
+ and river_edge_variant != null
+ and river_stone_bridge != null
+ and river_source_right != null
+ and river_source_left != null
+ and river_outlet_right != null
+ and river_outlet_left != null
+ and river_bend_a != null
+ and river_bend_b != null
+ and river_bend_c != null
+ and river_bend_d != null
+ and river_cap != null
+ and cliff_top != null
+ and cliff_corner != null
+ and cliff_edge != null
+ and cliff_ramp != null
+ and cliff_third_tier_edge != null
+ and cliff_third_tier_corner != null
+ and cliff_third_tier_top != null
+ and spawn != null
+ ):
+ _check(
+ sand.allows_non_water_neighbor(beach),
+ "Flat sand must permit an adjacent authored beach.",
+ )
+ _check(
+ sand.allows_non_water_neighbor(grass),
+ "Flat sand must permit the inland transition to grass.",
+ )
+ _check(
+ grass.allows_non_water_neighbor(sand),
+ "Grass must permit the inland side of a flat-sand transition.",
+ )
+ _check(
+ (
+ "sand" in sand.required_neighbor_tags
+ and "coast" in sand.required_neighbor_tags
+ and sand.maximum_boundary_distance == 1
+ ),
+ (
+ "Every flat-sand chunk must remain attached to sand or authored "
+ + "coast within the first inland terrain ring."
+ ),
+ )
+ _check(
+ spawn.minimum_required_neighbors == 3,
+ "Spawn must retain its three-sided safe-grass requirement.",
+ )
+ _check(
+ beach.ocean_facing_edges
+ == (1 << int(TerrainChunkTopology.Edge.EAST)),
+ "The authored beach must descend eastward into ocean before rotation.",
+ )
+ _check(
+ grass_ocean_edge.ocean_facing_edges
+ == (1 << int(TerrainChunkTopology.Edge.EAST)),
+ "The authored grass cliff must face eastward ocean before rotation.",
+ )
+ _check(
+ grass_beach_transition.ocean_facing_edges
+ == (1 << int(TerrainChunkTopology.Edge.EAST)),
+ "The authored grass/beach join must face eastward ocean before rotation.",
+ )
+ _check(
+ grass_beach_transition.minimum_required_neighbors == 2
+ and "coast" in grass_beach_transition.required_neighbor_tags,
+ "The grass/beach join must remain between two coastline chunks.",
+ )
+ _check(
+ grass_beach_transition.allows_non_water_neighbor_on_edge(
+ grass,
+ TerrainChunkTopology.Edge.WEST,
+ )
+ and not grass_beach_transition.allows_non_water_neighbor_on_edge(
+ sand,
+ TerrainChunkTopology.Edge.WEST,
+ ),
+ "The grass-backed transition edge must accept grass and reject sand.",
+ )
+ _check(
+ grass_beach_transition.allows_non_water_neighbor_on_edge(
+ beach,
+ TerrainChunkTopology.Edge.NORTH,
+ )
+ and grass_beach_transition.allows_non_water_neighbor_on_edge(
+ grass_ocean_edge,
+ TerrainChunkTopology.Edge.SOUTH,
+ ),
+ "The transition's coastline seams must retain their authored roles.",
+ )
+ _check(
+ not grass_ocean_edge.allows_non_water_neighbor_on_edge(
+ sand,
+ TerrainChunkTopology.Edge.WEST,
+ ),
+ "The straight grass coast's inland edge must reject sand.",
+ )
+ _check(
+ grass_ocean_corner.ocean_facing_edges
+ == (
+ (1 << int(TerrainChunkTopology.Edge.EAST))
+ | (1 << int(TerrainChunkTopology.Edge.SOUTH))
+ ),
+ "The authored grass corner must expose east and south to ocean.",
+ )
+ _check(
+ grass_ocean_corner.minimum_required_neighbors == 2
+ and (
+ "grass_ocean_edge"
+ in grass_ocean_corner.required_neighbor_tags
+ ),
+ "The grass corner must join two straight grass ocean edges.",
+ )
+ _check(
+ grass_ocean_corner.allows_non_water_neighbor_on_edge(
+ grass_ocean_edge,
+ TerrainChunkTopology.Edge.NORTH,
+ )
+ and grass_ocean_corner.allows_non_water_neighbor_on_edge(
+ grass_ocean_edge,
+ TerrainChunkTopology.Edge.WEST,
+ ),
+ "Both corner land seams must accept straight grass ocean edges.",
+ )
+ _check(
+ beach_ocean_corner.ocean_facing_edges
+ == (
+ (1 << int(TerrainChunkTopology.Edge.EAST))
+ | (1 << int(TerrainChunkTopology.Edge.SOUTH))
+ ),
+ "The authored beach corner must expose east and south to ocean.",
+ )
+ _check(
+ beach_ocean_corner.minimum_required_neighbors == 2
+ and (
+ "beach_ocean_edge"
+ in beach_ocean_corner.required_neighbor_tags
+ ),
+ "The beach corner must join two straight beach ocean edges.",
+ )
+ _check(
+ grass_sand_diagonal.must_be_interior
+ and not grass_sand_diagonal.participates_in_base_solver
+ and grass_sand_diagonal.maximum_boundary_distance == 2
+ and "mixed_surface" in grass_sand_diagonal.tags,
+ (
+ "The grass-sand diagonal must remain an inland post-process "
+ + "mixed-surface chunk."
+ ),
+ )
+ _check(
+ grass_sand_diagonal.surface_tags_for_edge(
+ TerrainChunkTopology.Edge.NORTH
+ ) == PackedStringArray(["sand"])
+ and grass_sand_diagonal.surface_tags_for_edge(
+ TerrainChunkTopology.Edge.WEST
+ ) == PackedStringArray(["sand"])
+ and grass_sand_diagonal.surface_tags_for_edge(
+ TerrainChunkTopology.Edge.EAST
+ ) == PackedStringArray(["grass"])
+ and grass_sand_diagonal.surface_tags_for_edge(
+ TerrainChunkTopology.Edge.SOUTH
+ ) == PackedStringArray(["grass"]),
+ "The diagonal's authored edges must retain two sand and two grass sides.",
+ )
+ var stream := CATALOG.definition_for_id(&"chunk_0003")
+ _check(
+ pond.water_inlet_edges == pond.water_outlet_edges
+ and pond.water_inlet_edges
+ == (1 << int(TerrainChunkTopology.Edge.NORTH)),
+ "A pond must terminate either end of an inland freshwater chain.",
+ )
+ _check(
+ pond.maximum_placements == 2,
+ "Generated worlds must contain only one pond/stream chain.",
+ )
+ _check(
+ stream != null
+ and is_equal_approx(stream.selection_weight, 0.3)
+ and is_equal_approx(stream_variant.selection_weight, 0.3)
+ and stream.maximum_placements == 2
+ and stream_variant.maximum_placements == 1
+ and "pond" in stream.tags
+ and "river" not in stream.tags
+ and stream_variant.tags == stream.tags
+ and stream_variant.allowed_neighbor_tags
+ == stream.allowed_neighbor_tags
+ and stream_variant.water_inlet_edges == stream.water_inlet_edges
+ and stream_variant.water_outlet_edges == stream.water_outlet_edges
+ and stream_variant.water_surface_size == stream.water_surface_size
+ and stream.water_surface_offset.x < 0.0
+ and stream_variant.water_surface_offset.x > 0.0
+ and is_equal_approx(
+ stream_variant.water_surface_offset.y,
+ stream.water_surface_offset.y,
+ ),
+ (
+ "Both pond-stream visuals must share one combined selection weight "
+ + "and mirrored water footprints."
+ ),
+ )
+ _check(
+ (
+ not lake_edge.participates_in_base_solver
+ and not lake_edge_variant.participates_in_base_solver
+ and not lake_corner.participates_in_base_solver
+ and not lake_fill.participates_in_base_solver
+ and not lake_diagonal.participates_in_base_solver
+ and not lake_narrow.participates_in_base_solver
+ and not lake_regular_to_narrow.participates_in_base_solver
+ and not lake_narrow_to_regular.participates_in_base_solver
+ and lake_edge.water_inlet_edges == lake_edge.water_outlet_edges
+ and lake_edge.water_inlet_edges == 7
+ and lake_edge_variant.water_inlet_edges
+ == lake_edge.water_inlet_edges
+ and lake_edge_variant.water_outlet_edges
+ == lake_edge.water_outlet_edges
+ and lake_edge_variant.water_surface_size
+ == lake_edge.water_surface_size
+ and lake_edge_variant.water_surface_offset
+ == lake_edge.water_surface_offset
+ and lake_corner.water_inlet_edges
+ == lake_corner.water_outlet_edges
+ and lake_corner.water_inlet_edges == 6
+ and lake_fill.water_inlet_edges
+ == lake_fill.water_outlet_edges
+ and lake_fill.water_inlet_edges == 15
+ and lake_fill.water_surface_size == Vector2(10.0, 10.0)
+ and lake_diagonal.water_inlet_edges
+ == lake_diagonal.water_outlet_edges
+ and lake_diagonal.water_inlet_edges == 6
+ and lake_diagonal.maximum_placements == 4
+ and lake_diagonal.water_surface_polygon.size() == 6
+ and lake_narrow.water_inlet_edges == 14
+ and lake_narrow.water_outlet_edges == 14
+ and lake_narrow.water_surface_polygon.size() == 4
+ and lake_regular_to_narrow.water_inlet_edges == 14
+ and lake_regular_to_narrow.water_outlet_edges == 14
+ and lake_regular_to_narrow.water_surface_polygon.size() == 5
+ and lake_narrow_to_regular.water_inlet_edges == 14
+ and lake_narrow_to_regular.water_outlet_edges == 14
+ and lake_narrow_to_regular.water_surface_polygon.size() == 5
+ and "lake" in lake_edge.tags
+ and "lake" in lake_edge_variant.tags
+ and "lake" in lake_corner.tags
+ and "lake" in lake_fill.tags
+ and "lake" in lake_diagonal.tags
+ and "lake" in lake_narrow.tags
+ and "lake" in lake_regular_to_narrow.tags
+ and "lake" in lake_narrow_to_regular.tags
+ ),
+ "The deterministic lake ring and bottom must remain connected freshwater chunks.",
+ )
+ _check(
+ (
+ not river_edge.participates_in_base_solver
+ and not river_edge_variant.participates_in_base_solver
+ and not river_stone_bridge.participates_in_base_solver
+ and river_stone_bridge.maximum_placements == 2
+ and not river_source_right.participates_in_base_solver
+ and not river_source_left.participates_in_base_solver
+ and not river_outlet_right.participates_in_base_solver
+ and not river_outlet_left.participates_in_base_solver
+ and not river_bend_a.participates_in_base_solver
+ and not river_bend_b.participates_in_base_solver
+ and not river_bend_c.participates_in_base_solver
+ and not river_bend_d.participates_in_base_solver
+ and not river_cap.participates_in_base_solver
+ and river_edge.water_inlet_edges == 14
+ and river_edge.water_outlet_edges == 14
+ and river_edge_variant.water_inlet_edges == 14
+ and river_edge_variant.water_outlet_edges == 14
+ and river_stone_bridge.water_inlet_edges == 14
+ and river_stone_bridge.water_outlet_edges == 14
+ and river_source_right.water_inlet_edges == 6
+ and river_source_right.water_outlet_edges == 6
+ and river_source_left.water_inlet_edges == 3
+ and river_source_left.water_outlet_edges == 3
+ and river_outlet_right.water_inlet_edges == 14
+ and river_outlet_right.water_outlet_edges == 14
+ and river_outlet_left.water_inlet_edges == 11
+ and river_outlet_left.water_outlet_edges == 11
+ and river_bend_a.water_inlet_edges == 14
+ and river_bend_a.water_outlet_edges == 14
+ and river_bend_b.water_inlet_edges == 12
+ and river_bend_b.water_outlet_edges == 12
+ and river_bend_c.water_inlet_edges == 15
+ and river_bend_c.water_outlet_edges == 15
+ and river_bend_c.continuous_water_seam_edges == 15
+ and river_bend_d.water_inlet_edges == 13
+ and river_bend_d.water_outlet_edges == 13
+ and river_bend_d.continuous_water_seam_edges == 1
+ and river_cap.water_inlet_edges == 6
+ and river_cap.water_outlet_edges == 6
+ and river_edge.water_surface_polygon.size() == 13
+ and river_edge_variant.water_surface_polygon.size() == 13
+ and river_stone_bridge.water_surface_polygon.size() == 13
+ and river_source_right.water_surface_polygon.size() == 13
+ and river_source_left.water_surface_polygon.size() == 13
+ and river_outlet_right.water_surface_polygon.size() == 19
+ and river_outlet_left.water_surface_polygon.size() == 19
+ and river_outlet_right.ocean_facing_edges == 2
+ and river_outlet_left.ocean_facing_edges == 2
+ and river_cap.water_surface_polygon.size() == 6
+ and river_cap.primary_mesh_name == &"chunk_0020"
+ and "river" in river_edge.tags
+ and "river" in river_edge_variant.tags
+ and "river_bridge" in river_stone_bridge.tags
+ and "stepping_stones" in river_stone_bridge.tags
+ and "river_source_right" in river_source_right.tags
+ and "river_source_left" in river_source_left.tags
+ and "river_outlet_right" in river_outlet_right.tags
+ and "river_outlet_left" in river_outlet_left.tags
+ and "river_bend_a" in river_bend_a.tags
+ and "river_bend_b" in river_bend_b.tags
+ and "river_bend_c" in river_bend_c.tags
+ and "river_bend_d" in river_bend_d.tags
+ and "river" in river_cap.tags
+ ),
+ (
+ "The river banks, bend, cliff source, ocean outlet, and legacy "
+ + "caps must remain connected freshwater chunks."
+ ),
+ )
+ for cliff_definition: TerrainChunkDefinition in [
+ cliff_top,
+ cliff_corner,
+ cliff_edge,
+ cliff_ramp,
+ ]:
+ _check(
+ cliff_definition.overlay_only
+ and cliff_definition.must_be_interior
+ and cliff_definition.base_layer_scene != null
+ and cliff_definition.base_layer_mesh_name == &"chunk_0000",
+ "Every second-tier piece must overlay level-one grass inland.",
+ )
+ for cliff_definition: TerrainChunkDefinition in [
+ cliff_third_tier_top,
+ cliff_third_tier_corner,
+ cliff_third_tier_edge,
+ ]:
+ _check(
+ cliff_definition.overlay_only
+ and cliff_definition.must_be_interior
+ and cliff_definition.base_layer_scene != null
+ and cliff_definition.base_layer_mesh_name == &"chunk_0000"
+ and "elevation_3" in cliff_definition.tags,
+ "Every third-tier piece must overlay level-one grass inland.",
+ )
+ for coastal_cliff: TerrainChunkDefinition in [
+ cliff_sea_edge,
+ cliff_sea_corner,
+ ]:
+ _check(
+ coastal_cliff.overlay_only
+ and coastal_cliff.base_layer_scene == null
+ and not coastal_cliff.must_be_interior
+ and coastal_cliff.prefers_map_boundary
+ and "coast" in coastal_cliff.tags
+ and "elevation_2_sea_edge" in coastal_cliff.tags,
+ (
+ "Each second-tier sea piece must be a complete coastal "
+ + "overlay without an inland grass base."
+ ),
+ )
+ for transition: TerrainChunkDefinition in [
+ cliff_sea_transition_right,
+ cliff_sea_transition_left,
+ ]:
+ _check(
+ transition.overlay_only
+ and transition.primary_mesh_name == &"chunk_0010"
+ and transition.base_layer_scene != null
+ and transition.base_layer_mesh_name == &"chunk_0007"
+ and not transition.must_be_interior
+ and transition.prefers_map_boundary
+ and "transition" in transition.tags,
+ (
+ "Each raised-coast endpoint must layer the inland corner "
+ + "over an authored level-one coastline."
+ ),
+ )
+ _check(
+ cliff_sea_transition_right.base_layer_quarter_turns == 3
+ and cliff_sea_transition_right.ocean_facing_edges
+ == (1 << int(TerrainChunkTopology.Edge.SOUTH)),
+ "The right coastal transition must align its south-facing base coast.",
+ )
+ _check(
+ cliff_sea_transition_left.base_layer_quarter_turns == 1
+ and cliff_sea_transition_left.ocean_facing_edges
+ == (1 << int(TerrainChunkTopology.Edge.EAST)),
+ "The left coastal transition must align its east-facing base coast.",
+ )
+ for transition: TerrainChunkDefinition in [
+ cliff_beach_transition_right,
+ cliff_beach_transition_left,
+ ]:
+ _check(
+ transition.overlay_only
+ and transition.primary_mesh_name == &"chunk_0010"
+ and transition.base_layer_scene != null
+ and transition.base_layer_mesh_name == &"chunk_0008"
+ and not transition.must_be_interior
+ and transition.prefers_map_boundary
+ and "sand" in transition.tags
+ and "transition" in transition.tags,
+ (
+ "Each raised beach endpoint must close over an authored "
+ + "level-one beach corner."
+ ),
+ )
+ _check(
+ cliff_beach_transition_right.base_layer_quarter_turns == 3
+ and cliff_beach_transition_right.ocean_facing_edges
+ == (1 << int(TerrainChunkTopology.Edge.SOUTH))
+ and cliff_beach_transition_left.base_layer_quarter_turns == 1
+ and cliff_beach_transition_left.ocean_facing_edges
+ == (1 << int(TerrainChunkTopology.Edge.EAST)),
+ "The raised beach endpoint corners must face their open coastline.",
+ )
+ _check(
+ cliff_sea_edge.ocean_facing_edges
+ == (1 << int(TerrainChunkTopology.Edge.EAST)),
+ "The second-tier sea edge must face east before rotation.",
+ )
+ _check(
+ cliff_sea_corner.ocean_facing_edges
+ == (
+ (1 << int(TerrainChunkTopology.Edge.EAST))
+ | (1 << int(TerrainChunkTopology.Edge.SOUTH))
+ ),
+ "The second-tier sea corner must face east and south before rotation.",
+ )
+ _check(
+ cliff_sea_corner.minimum_required_neighbors == 2
+ and (
+ "elevation_2_sea_edge"
+ in cliff_sea_corner.required_neighbor_tags
+ ),
+ "The second-tier sea corner must join two straight sea edges.",
+ )
+ _check(
+ cliff_top.minimum_required_neighbors == 4
+ and "elevation_2" in cliff_top.required_neighbor_tags,
+ "The second-tier top must be enclosed on all four sides.",
+ )
+ _check(
+ "ramp" in cliff_ramp.tags
+ and cliff_ramp.maximum_placements > 0,
+ "The second-tier ramp must remain available without being required.",
+ )
+ _check(
+ cliff_third_tier_top.minimum_required_neighbors == 4
+ and "elevation_3" in cliff_third_tier_top.required_neighbor_tags
+ and "ramp" not in cliff_third_tier_edge.tags
+ and "ramp" not in cliff_third_tier_corner.tags,
+ "The third-tier set must form a closed, intentionally unramped plateau.",
+ )
+
+
+func _validate_profiles() -> void:
+ for definition: TerrainChunkDefinition in CATALOG.definitions:
+ var variants := TerrainChunkAnalyzer.create_variants(
+ definition,
+ CATALOG.chunk_size,
+ )
+ _check(
+ not variants.is_empty(),
+ "%s must produce at least one rotation variant."
+ % definition.stable_id,
+ )
+ for variant: TerrainChunkVariant in variants:
+ _check(
+ variant.edge_profiles.size() == 4,
+ "%s must expose four edge profiles." % variant.stable_key(),
+ )
+ var ocean_edges := variant.rotated_edge_mask(
+ definition.ocean_facing_edges
+ )
+ for edge_index: int in variant.edge_profiles.size():
+ var profile := variant.edge_profiles[edge_index]
+ _check(
+ (
+ not profile.points.is_empty()
+ or (ocean_edges & (1 << edge_index)) != 0
+ ),
+ (
+ "%s has an empty non-ocean edge profile."
+ % variant.stable_key()
+ ),
+ )
+
+
+func _validate_second_tier_coastal_layout() -> void:
+ var generator := TerrainChunkGenerator.new()
+ generator.catalog = CATALOG
+ generator.grid_size = Vector2i(4, 4)
+ generator.generate_on_ready = false
+ generator.build_collision = false
+ generator.force_center_chunk_id = &""
+ root.add_child(generator)
+ var keys := PackedStringArray()
+ for row: int in generator.grid_size.y:
+ for column: int in generator.grid_size.x:
+ var at_north := row == 0
+ var at_east := column == generator.grid_size.x - 1
+ var at_south := row == generator.grid_size.y - 1
+ var at_west := column == 0
+ if at_north and at_west:
+ keys.append("chunk_0015@2")
+ elif at_north and at_east:
+ keys.append("chunk_0015@1")
+ elif at_south and at_east:
+ keys.append("chunk_0015@0")
+ elif at_south and at_west:
+ keys.append("chunk_0015@3")
+ elif at_north:
+ keys.append("chunk_0014@1")
+ elif at_east:
+ keys.append("chunk_0014@0")
+ elif at_south:
+ keys.append("chunk_0014@3")
+ elif at_west:
+ keys.append("chunk_0014@2")
+ else:
+ keys.append("chunk_0009@0")
+ _check(
+ generator.generate_from_placement_keys(keys),
+ (
+ "The second-tier sea edge and corner must form a complete "
+ + "raised coastal perimeter."
+ ),
+ )
+ if generator.placement_keys().size() == keys.size():
+ _check(
+ _all_neighbor_edges_match(generator),
+ "Every raised coastal-perimeter seam must match.",
+ )
+ _check(
+ _count_placements(generator.placement_keys(), "chunk_0014") == 8
+ and _count_placements(
+ generator.placement_keys(), "chunk_0015"
+ ) == 4,
+ "The raised coastal perimeter must use its straight and corner pieces.",
+ )
+ for record: Dictionary in generator.placement_records():
+ var stable_id: StringName = record.get("stable_id", &"")
+ if stable_id not in [&"chunk_0014", &"chunk_0015"]:
+ continue
+ var coordinate: Vector2i = record.get(
+ "coordinate", Vector2i.ZERO
+ )
+ var variant := _find_variant(
+ generator,
+ stable_id,
+ int(record.get("rotation_quarters", 0)),
+ )
+ _check(
+ variant != null
+ and generator._variant_respects_ocean_boundary(
+ variant, coordinate
+ ),
+ "Every second-tier sea opening must face outside the terrain grid.",
+ )
+ var generated := generator.get_generated_chunks_root()
+ if generated != null:
+ for child: Node in generated.get_children():
+ var stable_id := StringName(
+ child.get_meta(&"terrain_chunk_id", &"")
+ )
+ if stable_id in [&"chunk_0014", &"chunk_0015"]:
+ _check(
+ child.get_node_or_null("TerrainBaseLayer") == null,
+ "Second-tier sea pieces must not render inland grass over ocean.",
+ )
+ generator.free()
+
+
+func _validate_generation() -> void:
+ var first := _make_generator()
+ root.add_child(first)
+ var first_solved := first.generate()
+ _check(first_solved, "Generator must solve the prototype 9x9 grid.")
+ if not first_solved:
+ first.free()
+ return
+ var first_keys := first.placement_keys()
+ _check(first_keys.size() == 81, "Generator must place exactly 81 chunks.")
+ for stable_id: String in REQUIRED_IDS:
+ _check(
+ _placements_contain(first_keys, stable_id),
+ "Generated diagnostic must contain %s." % stable_id,
+ )
+ _check(
+ _all_neighbor_edges_match(first),
+ "Every generated neighboring edge must match.",
+ )
+ _check(
+ _count_placements(first_keys, "chunk_spawn") == 1,
+ "Generated layouts must contain exactly one spawn chunk.",
+ )
+ _check(
+ first_keys[40].begins_with("chunk_spawn@"),
+ "The spawn chunk must occupy the center cell.",
+ )
+ _validate_layout_rules(first)
+ _validate_authored_rotations(first)
+ _validate_generation_summary(first)
+ var first_generated_root := first.get_generated_chunks_root()
+ _check(
+ first.generate(),
+ "Repeated generation on one generator must solve.",
+ )
+ _check(
+ first.placement_keys() == first_keys,
+ "Repeated generation on one generator must remain deterministic.",
+ )
+ _check(
+ first.get_generated_chunks_root() != first_generated_root,
+ "Successful regeneration must replace the generated scene atomically.",
+ )
+
+ var second := _make_generator()
+ root.add_child(second)
+ var second_solved := second.generate()
+ _check(second_solved, "Repeated deterministic generation must solve.")
+ if not second_solved:
+ first.free()
+ second.free()
+ return
+ _check(
+ second.placement_keys() == first_keys,
+ "The same seed and catalog must produce the same layout.",
+ )
+ var restored := _make_generator()
+ restored.generation_seed = TEST_SEED + 999
+ root.add_child(restored)
+ _check(
+ restored.generate_from_placement_keys(first_keys),
+ "A resolved placement manifest must rebuild successfully.",
+ )
+ _check(
+ restored.placement_keys() == first_keys,
+ "A resolved placement manifest must preserve exact rotations.",
+ )
+ first.free()
+ second.free()
+ restored.free()
+
+ for seed_offset: int in range(1, 6):
+ var generator := _make_generator()
+ generator.generation_seed = TEST_SEED + seed_offset
+ root.add_child(generator)
+ var solved := generator.generate()
+ _check(
+ solved,
+ "Generator must solve seed %d." % generator.generation_seed,
+ )
+ if solved:
+ var keys := generator.placement_keys()
+ for stable_id: String in REQUIRED_IDS:
+ _check(
+ _placements_contain(keys, stable_id),
+ "Seed %d must contain %s."
+ % [generator.generation_seed, stable_id],
+ )
+ _check(
+ _all_neighbor_edges_match(generator),
+ "Seed %d has a mismatched neighboring edge."
+ % generator.generation_seed,
+ )
+ _validate_layout_rules(generator)
+ generator.free()
+
+ var connector_stress := _make_generator()
+ connector_stress.generation_seed = CONNECTOR_STRESS_SEED
+ connector_stress.maximum_backtracks = 100
+ root.add_child(connector_stress)
+ _check(
+ connector_stress.generate(),
+ "Connector propagation must solve the known stress seed.",
+ )
+ _check(
+ connector_stress._backtrack_count < 100,
+ "Connector propagation must reject unsupported branches early.",
+ )
+ connector_stress.free()
+
+
+func _validate_authored_rotations(generator: TerrainChunkGenerator) -> void:
+ var counts: Dictionary[StringName, int] = {}
+ for variant: TerrainChunkVariant in generator._variants:
+ var stable_id := variant.definition.stable_id
+ counts[stable_id] = counts.get(stable_id, 0) + 1
+ for stable_id: StringName in EXPECTED_VARIANT_COUNTS:
+ _check(
+ counts.get(stable_id, 0) == EXPECTED_VARIANT_COUNTS[stable_id],
+ "%s must preserve every explicitly allowed rotation." % stable_id,
+ )
+
+ var stream := CATALOG.definition_for_id(&"chunk_0003")
+ var alternate_stream := CATALOG.definition_for_id(&"chunk_0016")
+ _check(
+ stream != null and alternate_stream != null,
+ "Both stream visual definitions must be available.",
+ )
+ if stream == null or alternate_stream == null:
+ return
+ for quarter_turns: int in 4:
+ var stream_variant := _find_variant(
+ generator,
+ &"chunk_0003",
+ quarter_turns,
+ )
+ _check(
+ stream_variant != null,
+ "The stream must support rotation %d." % quarter_turns,
+ )
+ var alternate_variant := _find_variant(
+ generator,
+ &"chunk_0016",
+ quarter_turns,
+ )
+ _check(
+ alternate_variant != null,
+ "The alternate stream must support rotation %d." % quarter_turns,
+ )
+ if stream_variant == null or alternate_variant == null:
+ continue
+ var expected_inlet := 1 << int(TerrainChunkTopology.rotated_edge(
+ TerrainChunkTopology.Edge.NORTH,
+ quarter_turns,
+ ))
+ var expected_outlet := 1 << int(TerrainChunkTopology.rotated_edge(
+ TerrainChunkTopology.Edge.SOUTH,
+ quarter_turns,
+ ))
+ _check(
+ stream_variant.rotated_edge_mask(stream.water_inlet_edges)
+ == expected_inlet,
+ "The rotated stream inlet must follow its authored orientation.",
+ )
+ _check(
+ stream_variant.rotated_edge_mask(stream.water_outlet_edges)
+ == expected_outlet,
+ "The rotated stream outlet must follow its authored orientation.",
+ )
+ _check(
+ alternate_variant.rotated_edge_mask(
+ alternate_stream.water_inlet_edges
+ ) == expected_inlet
+ and alternate_variant.rotated_edge_mask(
+ alternate_stream.water_outlet_edges
+ ) == expected_outlet,
+ "The alternate stream connectors must rotate exactly like the original.",
+ )
+ for edge_value: int in TerrainChunkTopology.Edge.values():
+ var edge := edge_value as TerrainChunkTopology.Edge
+ _check(
+ stream_variant.profile(edge).signature(
+ TerrainChunkGenerator.CONSTRAINT_PROFILE_QUANTIZATION
+ ) == alternate_variant.profile(edge).signature(
+ TerrainChunkGenerator.CONSTRAINT_PROFILE_QUANTIZATION
+ ),
+ "Both stream visuals must expose identical terrain edge profiles.",
+ )
+
+ var beach := CATALOG.definition_for_id(&"chunk_0002")
+ _check(beach != null, "The beach definition must be available.")
+ if beach == null:
+ return
+ for quarter_turns: int in 4:
+ var beach_variant := _find_variant(
+ generator,
+ &"chunk_0002",
+ quarter_turns,
+ )
+ _check(
+ beach_variant != null,
+ "The beach must support rotation %d." % quarter_turns,
+ )
+ if beach_variant == null:
+ continue
+ var expected_ocean_edge := 1 << int(
+ TerrainChunkTopology.rotated_edge(
+ TerrainChunkTopology.Edge.EAST,
+ quarter_turns,
+ )
+ )
+ _check(
+ beach_variant.rotated_edge_mask(beach.ocean_facing_edges)
+ == expected_ocean_edge,
+ "The rotated beach must keep its low edge facing the ocean.",
+ )
+ _check(
+ not generator._variant_respects_ocean_boundary(
+ beach_variant,
+ Vector2i(3, 3),
+ ),
+ "A beach slope must never direct its low edge into the map.",
+ )
+ var boundary_coordinate := Vector2i(3, 3)
+ match TerrainChunkTopology.rotated_edge(
+ TerrainChunkTopology.Edge.EAST,
+ quarter_turns,
+ ):
+ TerrainChunkTopology.Edge.NORTH:
+ boundary_coordinate.y = 0
+ TerrainChunkTopology.Edge.EAST:
+ boundary_coordinate.x = generator.grid_size.x - 1
+ TerrainChunkTopology.Edge.SOUTH:
+ boundary_coordinate.y = generator.grid_size.y - 1
+ TerrainChunkTopology.Edge.WEST:
+ boundary_coordinate.x = 0
+ _check(
+ generator._variant_respects_ocean_boundary(
+ beach_variant,
+ boundary_coordinate,
+ ),
+ "A beach slope must be legal when its low edge faces open ocean.",
+ )
+
+ var grass_ocean_edge := CATALOG.definition_for_id(&"chunk_0005")
+ var grass_beach_transition := CATALOG.definition_for_id(&"chunk_0006")
+ var grass_ocean_corner := CATALOG.definition_for_id(&"chunk_0007")
+ var beach_ocean_corner := CATALOG.definition_for_id(&"chunk_0008")
+ _check(
+ (
+ grass_ocean_edge != null
+ and grass_beach_transition != null
+ and grass_ocean_corner != null
+ and beach_ocean_corner != null
+ ),
+ "The authored coastline additions must be available.",
+ )
+ if (
+ grass_ocean_edge == null
+ or grass_beach_transition == null
+ or grass_ocean_corner == null
+ or beach_ocean_corner == null
+ ):
+ return
+ var beach_zero := _find_variant(generator, &"chunk_0002", 0)
+ var grass_edge_zero := _find_variant(generator, &"chunk_0005", 0)
+ var grass_edge_south := _find_variant(generator, &"chunk_0005", 3)
+ var transition_zero := _find_variant(generator, &"chunk_0006", 0)
+ var corner_zero := _find_variant(generator, &"chunk_0007", 0)
+ var beach_corner_zero := _find_variant(generator, &"chunk_0008", 0)
+ _check(
+ beach_zero != null
+ and grass_edge_zero != null
+ and grass_edge_south != null
+ and transition_zero != null
+ and corner_zero != null
+ and beach_corner_zero != null,
+ "The unrotated coastline variants must be available.",
+ )
+ if (
+ beach_zero != null
+ and grass_edge_zero != null
+ and grass_edge_south != null
+ and transition_zero != null
+ and corner_zero != null
+ and beach_corner_zero != null
+ ):
+ _check(
+ grass_edge_zero.profile(
+ TerrainChunkTopology.Edge.EAST
+ ).points.is_empty(),
+ "The grass cliff's authored east edge must remain open to ocean.",
+ )
+ _check(
+ transition_zero.profile(
+ TerrainChunkTopology.Edge.NORTH
+ ).matches(
+ beach_zero.profile(TerrainChunkTopology.Edge.SOUTH),
+ generator.edge_match_tolerance,
+ ),
+ "The transition's north side must match the authored beach.",
+ )
+ _check(
+ transition_zero.profile(
+ TerrainChunkTopology.Edge.SOUTH
+ ).matches(
+ grass_edge_zero.profile(TerrainChunkTopology.Edge.NORTH),
+ generator.edge_match_tolerance,
+ ),
+ "The transition's south side must match the grass cliff.",
+ )
+ _check(
+ corner_zero.profile(TerrainChunkTopology.Edge.EAST).points.is_empty()
+ and corner_zero.profile(
+ TerrainChunkTopology.Edge.SOUTH
+ ).points.is_empty(),
+ "The corner's authored east and south edges must remain open to ocean.",
+ )
+ _check(
+ corner_zero.profile(TerrainChunkTopology.Edge.NORTH).matches(
+ grass_edge_zero.profile(TerrainChunkTopology.Edge.SOUTH),
+ generator.edge_match_tolerance,
+ ),
+ "The corner's north side must join a straight grass ocean edge.",
+ )
+ _check(
+ corner_zero.profile(TerrainChunkTopology.Edge.WEST).matches(
+ grass_edge_south.profile(TerrainChunkTopology.Edge.EAST),
+ generator.edge_match_tolerance,
+ ),
+ "The corner's west side must join a straight grass ocean edge.",
+ )
+ _check(
+ beach_corner_zero.profile(TerrainChunkTopology.Edge.NORTH).matches(
+ beach_zero.profile(TerrainChunkTopology.Edge.SOUTH),
+ generator.edge_match_tolerance,
+ ),
+ "The beach corner's north side must join a straight beach.",
+ )
+ var beach_south := _find_variant(generator, &"chunk_0002", 3)
+ _check(
+ beach_south != null
+ and beach_corner_zero.profile(
+ TerrainChunkTopology.Edge.WEST
+ ).matches(
+ beach_south.profile(TerrainChunkTopology.Edge.EAST),
+ generator.edge_match_tolerance,
+ ),
+ "The beach corner's west side must join a rotated straight beach.",
+ )
+ var cliff_top_zero := _find_variant(generator, &"chunk_0009", 0)
+ var cliff_edge_zero := _find_variant(generator, &"chunk_0011", 0)
+ var cliff_edge_south := _find_variant(generator, &"chunk_0011", 3)
+ var cliff_sea_edge_zero := _find_variant(generator, &"chunk_0014", 0)
+ var cliff_sea_edge_south := _find_variant(generator, &"chunk_0014", 3)
+ var cliff_sea_corner_zero := _find_variant(generator, &"chunk_0015", 0)
+ _check(
+ cliff_top_zero != null
+ and cliff_edge_zero != null
+ and cliff_edge_south != null
+ and cliff_sea_edge_zero != null
+ and cliff_sea_edge_south != null
+ and cliff_sea_corner_zero != null,
+ "The inland and coastal second-tier cliff variants must be available.",
+ )
+ if (
+ cliff_top_zero != null
+ and cliff_edge_zero != null
+ and cliff_edge_south != null
+ and cliff_sea_edge_zero != null
+ and cliff_sea_edge_south != null
+ and cliff_sea_corner_zero != null
+ ):
+ _check(
+ cliff_sea_edge_zero.profile(
+ TerrainChunkTopology.Edge.EAST
+ ).points.is_empty(),
+ "The second-tier sea edge must remain open on its ocean side.",
+ )
+ _check(
+ cliff_sea_corner_zero.profile(
+ TerrainChunkTopology.Edge.EAST
+ ).points.is_empty()
+ and cliff_sea_corner_zero.profile(
+ TerrainChunkTopology.Edge.SOUTH
+ ).points.is_empty(),
+ "The second-tier sea corner must retain both ocean openings.",
+ )
+ _check(
+ generator._edges_are_compatible(
+ cliff_sea_edge_zero,
+ TerrainChunkTopology.Edge.WEST,
+ cliff_top_zero,
+ TerrainChunkTopology.Edge.EAST,
+ ),
+ "The second-tier sea edge must join the raised grass top inland.",
+ )
+ _check(
+ generator._edges_are_compatible(
+ cliff_sea_corner_zero,
+ TerrainChunkTopology.Edge.NORTH,
+ cliff_sea_edge_zero,
+ TerrainChunkTopology.Edge.SOUTH,
+ )
+ and generator._edges_are_compatible(
+ cliff_sea_corner_zero,
+ TerrainChunkTopology.Edge.WEST,
+ cliff_sea_edge_south,
+ TerrainChunkTopology.Edge.EAST,
+ ),
+ "The second-tier sea corner must join two straight sea edges.",
+ )
+ _check(
+ generator._edges_are_compatible(
+ cliff_sea_edge_zero,
+ TerrainChunkTopology.Edge.NORTH,
+ cliff_edge_zero,
+ TerrainChunkTopology.Edge.SOUTH,
+ )
+ and generator._edges_are_compatible(
+ cliff_sea_edge_zero,
+ TerrainChunkTopology.Edge.SOUTH,
+ cliff_edge_zero,
+ TerrainChunkTopology.Edge.NORTH,
+ ),
+ (
+ "A coastal cliff edge must transition directly into its "
+ + "inland edge counterpart."
+ ),
+ )
+ _check(
+ generator._edges_are_compatible(
+ cliff_sea_corner_zero,
+ TerrainChunkTopology.Edge.NORTH,
+ cliff_edge_zero,
+ TerrainChunkTopology.Edge.SOUTH,
+ )
+ and generator._edges_are_compatible(
+ cliff_sea_corner_zero,
+ TerrainChunkTopology.Edge.WEST,
+ cliff_edge_south,
+ TerrainChunkTopology.Edge.EAST,
+ ),
+ (
+ "A coastal cliff corner must transition directly into "
+ + "ordinary inland cliff edges."
+ ),
+ )
+ var flat_grass := _find_variant(generator, &"chunk_0000", 0)
+ var flat_sand := _find_variant(generator, &"chunk_0001", 0)
+ _check(
+ flat_grass != null and flat_sand != null,
+ "Flat grass and sand variants must be available for seam validation.",
+ )
+ if flat_grass != null and flat_sand != null:
+ for quarter_turns: int in 4:
+ var transition := _find_variant(
+ generator,
+ &"chunk_0006",
+ quarter_turns,
+ )
+ if transition == null:
+ continue
+ var inland_edge := TerrainChunkTopology.rotated_edge(
+ TerrainChunkTopology.Edge.WEST,
+ quarter_turns,
+ )
+ var neighbor_edge := TerrainChunkTopology.opposite_edge(inland_edge)
+ _check(
+ generator._edges_are_compatible(
+ transition,
+ inland_edge,
+ flat_grass,
+ neighbor_edge,
+ ),
+ "Every transition rotation must accept grass behind it.",
+ )
+ _check(
+ not generator._edges_are_compatible(
+ transition,
+ inland_edge,
+ flat_sand,
+ neighbor_edge,
+ ),
+ "No transition rotation may accept sand behind it.",
+ )
+ for quarter_turns: int in 4:
+ var diagonal := _find_variant(
+ generator,
+ &"chunk_0013",
+ quarter_turns,
+ )
+ _check(
+ diagonal != null,
+ "The grass-sand diagonal must support rotation %d."
+ % quarter_turns,
+ )
+ if diagonal == null:
+ continue
+ for source_edge_value: int in TerrainChunkTopology.Edge.values():
+ var source_edge := source_edge_value as TerrainChunkTopology.Edge
+ var edge := TerrainChunkTopology.rotated_edge(
+ source_edge,
+ quarter_turns,
+ )
+ var neighbor_edge := TerrainChunkTopology.opposite_edge(edge)
+ var expects_sand := source_edge in [
+ TerrainChunkTopology.Edge.NORTH,
+ TerrainChunkTopology.Edge.WEST,
+ ]
+ var expected := flat_sand if expects_sand else flat_grass
+ var rejected := flat_grass if expects_sand else flat_sand
+ _check(
+ generator._edges_are_compatible(
+ diagonal,
+ edge,
+ expected,
+ neighbor_edge,
+ ),
+ "Each rotated diagonal edge must accept its authored surface.",
+ )
+ _check(
+ not generator._edges_are_compatible(
+ diagonal,
+ edge,
+ rejected,
+ neighbor_edge,
+ ),
+ "No rotated diagonal edge may accept the opposite surface.",
+ )
+ var lake_diagonal_northwest := _find_variant(generator, &"chunk_0020", 0)
+ var lake_diagonal_northeast := _find_variant(generator, &"chunk_0020", 3)
+ var lake_narrow_top := _find_variant(generator, &"chunk_0021", 0)
+ var lake_narrow_left := _find_variant(generator, &"chunk_0021", 1)
+ var lake_regular_to_narrow := _find_variant(generator, &"chunk_0022", 0)
+ var lake_narrow_to_regular := _find_variant(generator, &"chunk_0023", 0)
+ var lake_regular_top := _find_variant(generator, &"chunk_0017", 3)
+ var lake_regular_variant_top := _find_variant(generator, &"chunk_0037", 3)
+ _check(
+ (
+ lake_diagonal_northwest != null
+ and lake_diagonal_northeast != null
+ and lake_narrow_top != null
+ and lake_narrow_left != null
+ and lake_regular_to_narrow != null
+ and lake_narrow_to_regular != null
+ and lake_regular_top != null
+ and lake_regular_variant_top != null
+ ),
+ "Every authored diagonal lake perimeter variant must be available.",
+ )
+ if (
+ lake_diagonal_northwest != null
+ and lake_diagonal_northeast != null
+ and lake_narrow_top != null
+ and lake_narrow_left != null
+ and lake_regular_to_narrow != null
+ and lake_narrow_to_regular != null
+ and lake_regular_top != null
+ and lake_regular_variant_top != null
+ ):
+ _check(
+ lake_regular_variant_top.topology_signature(0.001)
+ == lake_regular_top.topology_signature(0.001),
+ "Both regular lake edge visuals must expose identical seams.",
+ )
+ _check(
+ generator._edges_are_compatible(
+ lake_diagonal_northwest,
+ TerrainChunkTopology.Edge.EAST,
+ lake_narrow_top,
+ TerrainChunkTopology.Edge.WEST,
+ )
+ and generator._edges_are_compatible(
+ lake_diagonal_northwest,
+ TerrainChunkTopology.Edge.SOUTH,
+ lake_narrow_left,
+ TerrainChunkTopology.Edge.NORTH,
+ ),
+ "Both diagonal banks must join the exact narrow lake profile.",
+ )
+ _check(
+ generator._edges_are_compatible(
+ lake_narrow_top,
+ TerrainChunkTopology.Edge.EAST,
+ lake_narrow_to_regular,
+ TerrainChunkTopology.Edge.WEST,
+ )
+ and generator._edges_are_compatible(
+ lake_narrow_to_regular,
+ TerrainChunkTopology.Edge.EAST,
+ lake_regular_top,
+ TerrainChunkTopology.Edge.WEST,
+ )
+ and generator._edges_are_compatible(
+ lake_regular_top,
+ TerrainChunkTopology.Edge.EAST,
+ lake_regular_to_narrow,
+ TerrainChunkTopology.Edge.WEST,
+ )
+ and generator._edges_are_compatible(
+ lake_regular_to_narrow,
+ TerrainChunkTopology.Edge.EAST,
+ lake_narrow_top,
+ TerrainChunkTopology.Edge.WEST,
+ ),
+ "The narrow lake run must transition exactly into the regular edge.",
+ )
+ _check(
+ generator._edges_are_compatible(
+ lake_diagonal_northwest,
+ TerrainChunkTopology.Edge.EAST,
+ lake_narrow_to_regular,
+ TerrainChunkTopology.Edge.WEST,
+ )
+ and generator._edges_are_compatible(
+ lake_narrow_to_regular,
+ TerrainChunkTopology.Edge.EAST,
+ lake_regular_to_narrow,
+ TerrainChunkTopology.Edge.WEST,
+ )
+ and generator._edges_are_compatible(
+ lake_regular_to_narrow,
+ TerrainChunkTopology.Edge.EAST,
+ lake_diagonal_northeast,
+ TerrainChunkTopology.Edge.WEST,
+ ),
+ "The five-chunk lake side must remain valid without a narrow run.",
+ )
+ var river_cap_northwest := _find_variant(generator, &"chunk_9995", 0)
+ var river_cap_northeast := _find_variant(generator, &"chunk_9995", 3)
+ var river_cap_southwest := _find_variant(generator, &"chunk_9995", 1)
+ var river_cap_southeast := _find_variant(generator, &"chunk_9995", 2)
+ var river_edge_top := _find_variant(generator, &"chunk_0024", 0)
+ var river_edge_variant_top := _find_variant(generator, &"chunk_0025", 0)
+ var river_edge_variant_bottom := _find_variant(generator, &"chunk_0025", 2)
+ var river_edge_vertical_left := _find_variant(generator, &"chunk_0024", 1)
+ var river_edge_vertical_right := _find_variant(generator, &"chunk_0025", 3)
+ var river_stones_top := _find_variant(generator, &"chunk_0038", 0)
+ var river_stones_bottom := _find_variant(generator, &"chunk_0038", 2)
+ var river_stones_left := _find_variant(generator, &"chunk_0038", 1)
+ var river_stones_right := _find_variant(generator, &"chunk_0038", 3)
+ var river_source_right := _find_variant(generator, &"chunk_0029", 0)
+ var river_source_left := _find_variant(generator, &"chunk_0030", 0)
+ var river_outlet_right := _find_variant(generator, &"chunk_0031", 0)
+ var river_outlet_left := _find_variant(generator, &"chunk_0032", 0)
+ var river_bend_a := _find_variant(generator, &"chunk_0035", 0)
+ var river_bend_b := _find_variant(generator, &"chunk_0034", 0)
+ var river_bend_c := _find_variant(generator, &"chunk_0033", 2)
+ var river_bend_d := _find_variant(generator, &"chunk_0036", 0)
+ var beach_east := _find_variant(generator, &"chunk_0002", 0)
+ var canonical_bend_specs := generator._river_bend_placement_specs(
+ Vector2i(5, 5),
+ 0,
+ )
+ var rotated_bend_specs := generator._river_bend_placement_specs(
+ Vector2i(5, 5),
+ 1,
+ )
+ _check(
+ (
+ canonical_bend_specs.size() == 4
+ and canonical_bend_specs[2]["id"] == &"chunk_0033"
+ and int(canonical_bend_specs[2]["turns"]) == 2
+ and rotated_bend_specs.size() == 4
+ and rotated_bend_specs[2]["id"] == &"chunk_0033"
+ and int(rotated_bend_specs[2]["turns"]) == 3
+ ),
+ "River bend C must retain its authored half-turn at every bend rotation.",
+ )
+ _check(
+ (
+ river_cap_northwest != null
+ and river_cap_northeast != null
+ and river_cap_southwest != null
+ and river_cap_southeast != null
+ and river_edge_top != null
+ and river_edge_variant_top != null
+ and river_edge_variant_bottom != null
+ and river_edge_vertical_left != null
+ and river_edge_vertical_right != null
+ and river_stones_top != null
+ and river_stones_bottom != null
+ and river_stones_left != null
+ and river_stones_right != null
+ and river_source_right != null
+ and river_source_left != null
+ and river_outlet_right != null
+ and river_outlet_left != null
+ and river_bend_a != null
+ and river_bend_b != null
+ and river_bend_c != null
+ and river_bend_d != null
+ and beach_east != null
+ ),
+ (
+ "Every authored river-bank, bend, source, outlet, and legacy-cap "
+ + "variant must be available."
+ ),
+ )
+ if (
+ river_cap_northwest != null
+ and river_cap_northeast != null
+ and river_cap_southwest != null
+ and river_cap_southeast != null
+ and river_edge_top != null
+ and river_edge_variant_top != null
+ and river_edge_variant_bottom != null
+ and river_edge_vertical_left != null
+ and river_edge_vertical_right != null
+ and river_stones_top != null
+ and river_stones_bottom != null
+ and river_stones_left != null
+ and river_stones_right != null
+ and river_source_right != null
+ and river_source_left != null
+ and river_outlet_right != null
+ and river_outlet_left != null
+ and river_bend_a != null
+ and river_bend_b != null
+ and river_bend_c != null
+ and river_bend_d != null
+ and beach_east != null
+ ):
+ _check(
+ generator._edges_are_compatible(
+ river_edge_top,
+ TerrainChunkTopology.Edge.EAST,
+ river_stones_top,
+ TerrainChunkTopology.Edge.WEST,
+ )
+ and generator._edges_are_compatible(
+ river_stones_top,
+ TerrainChunkTopology.Edge.SOUTH,
+ river_stones_bottom,
+ TerrainChunkTopology.Edge.NORTH,
+ )
+ and generator._edges_are_compatible(
+ river_stones_left,
+ TerrainChunkTopology.Edge.EAST,
+ river_stones_right,
+ TerrainChunkTopology.Edge.WEST,
+ ),
+ (
+ "The stepping-stone banks must replace one paired straight river "
+ + "station in either orientation."
+ ),
+ )
+ _check(
+ generator._edges_are_compatible(
+ river_source_right,
+ TerrainChunkTopology.Edge.SOUTH,
+ river_source_left,
+ TerrainChunkTopology.Edge.NORTH,
+ )
+ and generator._edges_are_compatible(
+ river_source_right,
+ TerrainChunkTopology.Edge.EAST,
+ river_edge_top,
+ TerrainChunkTopology.Edge.WEST,
+ )
+ and generator._edges_are_compatible(
+ river_source_left,
+ TerrainChunkTopology.Edge.EAST,
+ river_edge_variant_bottom,
+ TerrainChunkTopology.Edge.WEST,
+ ),
+ "The paired cliff source must open directly into both river banks.",
+ )
+ _check(
+ generator._edges_are_compatible(
+ river_bend_a,
+ TerrainChunkTopology.Edge.EAST,
+ river_bend_b,
+ TerrainChunkTopology.Edge.WEST,
+ )
+ and generator._edges_are_compatible(
+ river_bend_a,
+ TerrainChunkTopology.Edge.SOUTH,
+ river_bend_c,
+ TerrainChunkTopology.Edge.NORTH,
+ )
+ and generator._edges_are_compatible(
+ river_bend_b,
+ TerrainChunkTopology.Edge.SOUTH,
+ river_bend_d,
+ TerrainChunkTopology.Edge.NORTH,
+ )
+ and generator._edges_are_compatible(
+ river_bend_c,
+ TerrainChunkTopology.Edge.EAST,
+ river_bend_d,
+ TerrainChunkTopology.Edge.WEST,
+ ),
+ "The four authored river-bend quadrants must form one continuous turn.",
+ )
+ _check(
+ generator._edges_are_compatible(
+ river_edge_top,
+ TerrainChunkTopology.Edge.EAST,
+ river_bend_a,
+ TerrainChunkTopology.Edge.WEST,
+ )
+ and generator._edges_are_compatible(
+ river_edge_variant_bottom,
+ TerrainChunkTopology.Edge.EAST,
+ river_bend_c,
+ TerrainChunkTopology.Edge.WEST,
+ )
+ and generator._edges_are_compatible(
+ river_bend_c,
+ TerrainChunkTopology.Edge.SOUTH,
+ river_edge_vertical_left,
+ TerrainChunkTopology.Edge.NORTH,
+ )
+ and generator._edges_are_compatible(
+ river_bend_d,
+ TerrainChunkTopology.Edge.SOUTH,
+ river_edge_vertical_right,
+ TerrainChunkTopology.Edge.NORTH,
+ ),
+ "The bend must connect both incoming and outgoing authored river banks.",
+ )
+ _check(
+ generator._edges_are_compatible(
+ river_edge_top,
+ TerrainChunkTopology.Edge.EAST,
+ river_outlet_right,
+ TerrainChunkTopology.Edge.WEST,
+ )
+ and generator._edges_are_compatible(
+ river_edge_variant_bottom,
+ TerrainChunkTopology.Edge.EAST,
+ river_outlet_left,
+ TerrainChunkTopology.Edge.WEST,
+ )
+ and generator._edges_are_compatible(
+ river_outlet_right,
+ TerrainChunkTopology.Edge.SOUTH,
+ river_outlet_left,
+ TerrainChunkTopology.Edge.NORTH,
+ ),
+ "Both river banks must terminate in the paired beach outlet.",
+ )
+ _check(
+ generator._edges_are_compatible(
+ beach_east,
+ TerrainChunkTopology.Edge.SOUTH,
+ river_outlet_right,
+ TerrainChunkTopology.Edge.NORTH,
+ )
+ and generator._edges_are_compatible(
+ river_outlet_left,
+ TerrainChunkTopology.Edge.SOUTH,
+ beach_east,
+ TerrainChunkTopology.Edge.NORTH,
+ ),
+ "The ocean outlet must remain straddled by ordinary beach tiles.",
+ )
+ _check(
+ generator._edges_are_compatible(
+ river_cap_northwest,
+ TerrainChunkTopology.Edge.EAST,
+ river_edge_top,
+ TerrainChunkTopology.Edge.WEST,
+ )
+ and generator._edges_are_compatible(
+ river_edge_top,
+ TerrainChunkTopology.Edge.EAST,
+ river_edge_variant_top,
+ TerrainChunkTopology.Edge.WEST,
+ )
+ and generator._edges_are_compatible(
+ river_edge_variant_top,
+ TerrainChunkTopology.Edge.EAST,
+ river_cap_northeast,
+ TerrainChunkTopology.Edge.WEST,
+ ),
+ "Both river-bank visuals must chain exactly between composite caps.",
+ )
+ _check(
+ generator._edges_are_compatible(
+ river_cap_northwest,
+ TerrainChunkTopology.Edge.SOUTH,
+ river_cap_southwest,
+ TerrainChunkTopology.Edge.NORTH,
+ )
+ and generator._edges_are_compatible(
+ river_edge_top,
+ TerrainChunkTopology.Edge.SOUTH,
+ river_edge_variant_bottom,
+ TerrainChunkTopology.Edge.NORTH,
+ )
+ and generator._edges_are_compatible(
+ river_cap_northeast,
+ TerrainChunkTopology.Edge.SOUTH,
+ river_cap_southeast,
+ TerrainChunkTopology.Edge.NORTH,
+ ),
+ "Opposing river banks and end caps must close the water channel.",
+ )
+ for stable_id: StringName in [
+ &"chunk_0005",
+ &"chunk_0006",
+ &"chunk_0007",
+ &"chunk_0008",
+ &"chunk_0014",
+ &"chunk_0015",
+ &"chunk_9999",
+ &"chunk_9998",
+ &"chunk_9997",
+ &"chunk_9996",
+ ]:
+ for quarter_turns: int in 4:
+ var coast_variant := _find_variant(
+ generator,
+ stable_id,
+ quarter_turns,
+ )
+ _check(
+ coast_variant != null,
+ "%s must support rotation %d." % [stable_id, quarter_turns],
+ )
+ if coast_variant == null:
+ continue
+ _check(
+ not generator._variant_respects_ocean_boundary(
+ coast_variant,
+ Vector2i(3, 3),
+ ),
+ "%s must never place its ocean edge inside the map." % stable_id,
+ )
+ var coast_definition := CATALOG.definition_for_id(stable_id)
+ if coast_definition == null:
+ continue
+ var boundary_coordinate := Vector2i(3, 3)
+ var ocean_edges := coast_variant.rotated_edge_mask(
+ coast_definition.ocean_facing_edges
+ )
+ for edge_value: int in TerrainChunkTopology.Edge.values():
+ if (ocean_edges & (1 << edge_value)) == 0:
+ continue
+ match edge_value as TerrainChunkTopology.Edge:
+ TerrainChunkTopology.Edge.NORTH:
+ boundary_coordinate.y = 0
+ TerrainChunkTopology.Edge.EAST:
+ boundary_coordinate.x = generator.grid_size.x - 1
+ TerrainChunkTopology.Edge.SOUTH:
+ boundary_coordinate.y = generator.grid_size.y - 1
+ TerrainChunkTopology.Edge.WEST:
+ boundary_coordinate.x = 0
+ _check(
+ generator._variant_respects_ocean_boundary(
+ coast_variant,
+ boundary_coordinate,
+ ),
+ "Each coast rotation must fit its matching open map boundary.",
+ )
+
+
+func _validate_generation_summary(generator: TerrainChunkGenerator) -> void:
+ var summary := generator._build_summary()
+ _check(
+ int(summary.get("variant_count", 0)) == 174,
+ "The current catalog must expose all 174 authored rotations.",
+ )
+ _check(
+ int(summary.get("solver_variant_count", 0))
+ == EXPECTED_SOLVER_VARIANT_COUNT,
+ "Equivalent visual rotations must share one solver constraint.",
+ )
+ _check(
+ int(summary.get("adjacent_repeat_edges", -1))
+ == generator._count_adjacent_repeat_edges(),
+ "The summary must report adjacent repeated chunk definitions.",
+ )
+ _check(
+ str(summary.get("layout_fingerprint", ""))
+ == generator.placement_fingerprint(),
+ "The summary must identify the exact resolved layout.",
+ )
+ _check(
+ generator.placement_fingerprint().length() == 64,
+ "The resolved layout fingerprint must be SHA-256.",
+ )
+ var variant_counts: Dictionary = summary.get("variant_counts", {})
+ for stable_id: StringName in EXPECTED_VARIANT_COUNTS:
+ _check(
+ int(variant_counts.get(stable_id, 0))
+ == EXPECTED_VARIANT_COUNTS[stable_id],
+ "The summary must report %s rotation candidates." % stable_id,
+ )
+
+
+func _find_variant(
+ generator: TerrainChunkGenerator,
+ stable_id: StringName,
+ quarter_turns: int,
+) -> TerrainChunkVariant:
+ for variant: TerrainChunkVariant in generator._variants:
+ if (
+ variant.definition.stable_id == stable_id
+ and variant.quarter_turns == quarter_turns
+ ):
+ return variant
+ return null
+
+
+func _make_generator() -> TerrainChunkGenerator:
+ var generator := TerrainChunkGenerator.new()
+ generator.catalog = CATALOG
+ generator.grid_size = Vector2i(9, 9)
+ generator.generation_seed = TEST_SEED
+ generator.generate_on_ready = false
+ generator.build_collision = false
+ generator.force_center_chunk_id = &"chunk_spawn"
+ generator.required_chunk_ids = PackedStringArray(REQUIRED_IDS)
+ generator.grass_sand_smoothing_enabled = true
+ generator.maximum_backtracks = 100000
+ return generator
+
+
+func _placements_contain(keys: PackedStringArray, stable_id: String) -> bool:
+ for key: String in keys:
+ if key.begins_with(stable_id + "@"):
+ return true
+ return false
+
+
+func _count_placements(keys: PackedStringArray, stable_id: String) -> int:
+ var count := 0
+ for key: String in keys:
+ if key.begins_with(stable_id + "@"):
+ count += 1
+ return count
+
+
+func _all_neighbor_edges_match(generator: TerrainChunkGenerator) -> bool:
+ for index: int in generator._placements.size():
+ var coordinate := Vector2i(
+ index % generator.grid_size.x,
+ index / generator.grid_size.x,
+ )
+ var current: TerrainChunkVariant = generator._placements[index]
+ if coordinate.x > 0:
+ var west: TerrainChunkVariant = generator._placements[index - 1]
+ if not generator._edges_are_compatible(
+ current,
+ TerrainChunkTopology.Edge.WEST,
+ west,
+ TerrainChunkTopology.Edge.EAST,
+ ):
+ return false
+ if coordinate.y > 0:
+ var north: TerrainChunkVariant = (
+ generator._placements[index - generator.grid_size.x]
+ )
+ if not generator._edges_are_compatible(
+ current,
+ TerrainChunkTopology.Edge.NORTH,
+ north,
+ TerrainChunkTopology.Edge.SOUTH,
+ ):
+ return false
+ return true
+
+
+func _validate_layout_rules(generator: TerrainChunkGenerator) -> void:
+ _check(
+ generator._neighbor_requirement_validation_error(
+ generator._placements
+ ).is_empty(),
+ "Every generated chunk must satisfy its authored neighbor count.",
+ )
+ _check(
+ generator._walkable_connectivity_validation_error(
+ generator._placements
+ ).is_empty(),
+ "All walkable generated terrain must remain connected to spawn.",
+ )
+ var center := Vector2i(
+ generator.grid_size.x / 2,
+ generator.grid_size.y / 2,
+ )
+ var safe_spawn_neighbors := 0
+ for edge_value: int in TerrainChunkTopology.Edge.values():
+ var neighbor_coordinate := (
+ center
+ + TerrainChunkTopology.grid_offset(
+ edge_value as TerrainChunkTopology.Edge
+ )
+ )
+ var neighbor := generator._placements[
+ neighbor_coordinate.y * generator.grid_size.x
+ + neighbor_coordinate.x
+ ]
+ if "spawn_safe" in neighbor.definition.tags:
+ safe_spawn_neighbors += 1
+ _check(
+ safe_spawn_neighbors >= 3,
+ "At least three cardinal spawn neighbors must be safe flat grass.",
+ )
+ for index: int in generator._placements.size():
+ var placement := generator._placements[index]
+ var coordinate := Vector2i(
+ index % generator.grid_size.x,
+ index / generator.grid_size.x,
+ )
+ if "coast" in placement.definition.tags:
+ _check(
+ generator._variant_respects_ocean_boundary(
+ placement,
+ coordinate,
+ ),
+ "Every coastline chunk must direct its ocean edge outside the grid.",
+ )
+ var ocean_edges := placement.rotated_edge_mask(
+ placement.definition.ocean_facing_edges
+ )
+ for edge_value: int in TerrainChunkTopology.Edge.values():
+ var neighbor_coordinate := (
+ coordinate
+ + TerrainChunkTopology.grid_offset(
+ edge_value as TerrainChunkTopology.Edge
+ )
+ )
+ var outside := not generator._coordinate_is_inside_grid(
+ neighbor_coordinate
+ )
+ _check(
+ ((ocean_edges & (1 << edge_value)) != 0) == outside,
+ "Every map-boundary edge must use authored coastline geometry.",
+ )
+ if placement.definition.stable_id == &"chunk_0006":
+ var touches_beach := false
+ var touches_grass_edge := false
+ for edge_value: int in TerrainChunkTopology.Edge.values():
+ var neighbor_coordinate := (
+ coordinate
+ + TerrainChunkTopology.grid_offset(
+ edge_value as TerrainChunkTopology.Edge
+ )
+ )
+ if not generator._coordinate_is_inside_grid(neighbor_coordinate):
+ continue
+ var neighbor := generator._placements[
+ neighbor_coordinate.y * generator.grid_size.x
+ + neighbor_coordinate.x
+ ]
+ touches_beach = (
+ touches_beach
+ or neighbor.definition.stable_id == &"chunk_0002"
+ )
+ touches_grass_edge = (
+ touches_grass_edge
+ or neighbor.definition.stable_id == &"chunk_0005"
+ )
+ _check(
+ touches_beach and touches_grass_edge,
+ "Every grass/beach transition must join both authored edge types.",
+ )
+ if placement.definition.stable_id == &"chunk_0007":
+ var straight_edge_neighbors := 0
+ for edge_value: int in TerrainChunkTopology.Edge.values():
+ var neighbor_coordinate := (
+ coordinate
+ + TerrainChunkTopology.grid_offset(
+ edge_value as TerrainChunkTopology.Edge
+ )
+ )
+ if not generator._coordinate_is_inside_grid(neighbor_coordinate):
+ continue
+ var neighbor := generator._placements[
+ neighbor_coordinate.y * generator.grid_size.x
+ + neighbor_coordinate.x
+ ]
+ if neighbor.definition.stable_id == &"chunk_0005":
+ straight_edge_neighbors += 1
+ _check(
+ straight_edge_neighbors == 2,
+ "Every grass corner must join two straight grass ocean edges.",
+ )
+ if placement.definition.stable_id == &"chunk_0008":
+ var straight_beach_neighbors := 0
+ for edge_value: int in TerrainChunkTopology.Edge.values():
+ var neighbor_coordinate := (
+ coordinate
+ + TerrainChunkTopology.grid_offset(
+ edge_value as TerrainChunkTopology.Edge
+ )
+ )
+ if not generator._coordinate_is_inside_grid(neighbor_coordinate):
+ continue
+ var neighbor := generator._placements[
+ neighbor_coordinate.y * generator.grid_size.x
+ + neighbor_coordinate.x
+ ]
+ if neighbor.definition.stable_id == &"chunk_0002":
+ straight_beach_neighbors += 1
+ _check(
+ straight_beach_neighbors == 2,
+ "Every beach corner must join two straight beach ocean edges.",
+ )
+ if placement.definition.stable_id != &"chunk_0001":
+ continue
+ var touches_sand_or_coast := false
+ for edge_value: int in TerrainChunkTopology.Edge.values():
+ var neighbor_coordinate := (
+ coordinate
+ + TerrainChunkTopology.grid_offset(
+ edge_value as TerrainChunkTopology.Edge
+ )
+ )
+ if not generator._coordinate_is_inside_grid(neighbor_coordinate):
+ continue
+ var neighbor := generator._placements[
+ neighbor_coordinate.y * generator.grid_size.x
+ + neighbor_coordinate.x
+ ]
+ if (
+ "sand" in neighbor.definition.tags
+ or "coast" in neighbor.definition.tags
+ ):
+ touches_sand_or_coast = true
+ _check(
+ touches_sand_or_coast,
+ "Every flat-sand chunk must remain attached to sand or coast.",
+ )
+
+
+func _check(condition: bool, message: String) -> void:
+ if not condition:
+ _failures.append(message)
+
+
+func _finish() -> void:
+ if _failures.is_empty():
+ print("Terrain chunk generator validation: PASS")
+ quit(0)
+ return
+ for failure: String in _failures:
+ printerr("Terrain chunk generator validation: ", failure)
+ quit(1)
diff --git a/tests/terrain_chunk_generator_validation.gd.uid b/tests/terrain_chunk_generator_validation.gd.uid
new file mode 100644
index 0000000..d13a22b
--- /dev/null
+++ b/tests/terrain_chunk_generator_validation.gd.uid
@@ -0,0 +1 @@
+uid://bt6pjgkomx76s
diff --git a/tests/texture_sampling_validation.gd b/tests/texture_sampling_validation.gd
new file mode 100644
index 0000000..dcccc38
--- /dev/null
+++ b/tests/texture_sampling_validation.gd
@@ -0,0 +1,158 @@
+extends SceneTree
+
+const POLICY := preload("res://main/texture_sampling_policy.gd")
+const PROJECTED_TERRAIN_TEXTURE_IMPORTS: Array[String] = [
+ "res://world/generation/chunks/assets/chunk_0000_grass_lite.png.import",
+ "res://world/generation/chunks/assets/chunk_0001_sand.png.import",
+ "res://world/generation/chunks/assets/chunk_0031_dirt.png.import",
+]
+
+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)
+ var expects_mipmaps := path in PROJECTED_TERRAIN_TEXTURE_IMPORTS
+ _check(
+ ("mipmaps/generate=true" in source) == expects_mipmaps,
+ (
+ "Projected terrain mipmap policy mismatch: %s"
+ if expects_mipmaps
+ else "Texture import generates unapproved 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..e5ac4cc
--- /dev/null
+++ b/tests/title_credits_validation.gd
@@ -0,0 +1,267 @@
+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 play_button := title_screen.get_node("%PlayButton") as BubbleButton
+ var continue_stats_drawer := title_screen.get_node(
+ "%ContinueStatsDrawer"
+ ) as RichTextLabel
+ 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(play_button != null, "title menu exposes the play button")
+ _expect(
+ continue_stats_drawer != null,
+ "play button owns a dedicated save-preview drawer",
+ )
+ if play_button != null and continue_stats_drawer != null:
+ _expect(
+ continue_stats_drawer.get_parent() == play_button,
+ "save preview extends directly from the play button",
+ )
+ _expect(
+ continue_stats_drawer.position.y < play_button.size.y
+ and (
+ continue_stats_drawer.position.y
+ + continue_stats_drawer.size.y
+ ) > play_button.size.y,
+ "save preview overlaps the play edge and extends below it",
+ )
+ _expect(
+ continue_stats_drawer.mouse_filter
+ == Control.MOUSE_FILTER_IGNORE,
+ "save preview drawer cannot intercept menu input",
+ )
+ _expect(
+ credits_button.icon != null and credits_button.text.is_empty(),
+ "credits action uses its icon in the square-button presentation",
+ )
+ _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 button stays inside the authored title field",
+ )
+ _expect(
+ credits_button.mouse_filter == Control.MOUSE_FILTER_STOP,
+ "credits button accepts pointer input",
+ )
+ if OS.has_environment("straywild_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("straywild_TITLE_MENU_CAPTURE")
+ ) == OK,
+ "title menu validation capture saves successfully",
+ )
+ title_screen.set_process(false)
+
+ title_screen.set("_navigation_focus_active", false)
+ credits_button.call("_set_hovered", true)
+ credits_button.advance_emphasis(1.0)
+ _expect(
+ (credits_button.get_node("HoverDrawer") as Control).visible,
+ "pointer hover reveals the credits drawer before activation",
+ )
+ 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(
+ not (credits_button.get_node("HoverDrawer") as Control).visible,
+ "opening a title page collapses its hover drawer",
+ )
+ _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("straywild_CREDITS_CAPTURE"):
+ await process_frame
+ _expect(
+ _save_capture(
+ OS.get_environment("straywild_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",
+ )
+
+ var online_button := title_screen.get_node(
+ "%JoinGameButton"
+ ) as BubbleButton
+ var join_game_page := title_screen.get_node("%JoinGamePage") as Control
+ online_button.call("_set_hovered", true)
+ online_button.advance_emphasis(1.0)
+ _expect(
+ (online_button.get_node("HoverDrawer") as Control).visible,
+ "pointer hover reveals the online drawer before activation",
+ )
+ online_button.pressed.emit()
+ await process_frame
+ _expect(join_game_page.visible, "online activation opens the join page")
+ _expect(
+ not (online_button.get_node("HoverDrawer") as Control).visible,
+ "opening the join page collapses the online drawer",
+ )
+ title_screen.call("_close_join_game")
+ await process_frame
+ _expect(
+ not (online_button.get_node("HoverDrawer") as Control).visible,
+ "returning from the join page does not restore stale pointer hover",
+ )
+
+ 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 button",
+ )
+
+ 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 credits_paper := credits_page.get_node("Paper") as PanelContainer
+ var audio_credits := credits_page.get_node(
+ "Paper/Margin/Layout/Columns/AdditionalCredits/AudioCredits"
+ ) as Label
+ _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(
+ "kat • animalese source recording" in audio_credits.text,
+ "in-game credits name kat's animalese source contribution",
+ )
+ _expect(
+ "kim • animalese source recording" in audio_credits.text,
+ "in-game credits name kim's animalese source contribution",
+ )
+ _expect(
+ credits_paper.get_global_rect().encloses(
+ audio_credits.get_global_rect()
+ ),
+ "audio credits stay inside the authored credits panel",
+ )
+
+ 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/tree_gathering_prototype_validation.gd b/tests/tree_gathering_prototype_validation.gd
new file mode 100644
index 0000000..2814e19
--- /dev/null
+++ b/tests/tree_gathering_prototype_validation.gd
@@ -0,0 +1,166 @@
+extends SceneTree
+
+const StarterIslandScene = preload(
+ "res://world/regions/starter_island_region.tscn"
+)
+const Gatherables: GatherableCatalog = preload(
+ "res://gathering/catalog/gatherable_catalog.tres"
+)
+const FishCatalog: FishPool = preload("res://fish/pools/fish_catalog.tres")
+const MeshSurfaceAnchorSamplerType = preload(
+ "res://world/generation/mesh_surface_anchor_sampler.gd"
+)
+
+
+func _initialize() -> void:
+ call_deferred("_run")
+
+
+func _run() -> void:
+ _validate_beetle_data()
+ await _validate_mesh_surface_sampler()
+ await _validate_tree_anchors()
+ _validate_anchored_presentation()
+ _validate_three_dimensional_targeting()
+ print("Tree gathering prototype validation: PASS")
+ quit()
+
+
+func _validate_beetle_data() -> void:
+ var beetle: GatherableData = Gatherables.get_entry(&"beetle_stag_common")
+ assert(beetle != null and beetle.is_available())
+ assert(beetle.catch_data == FishCatalog.get_fish_by_id(&"beetle_stag_common"))
+ assert(beetle.catch_data.display_texture != null)
+ assert(beetle.catch_data.collection_method == FishData.CollectionMethod.NET)
+ assert(beetle.catch_data.collection_group == &"Beetles")
+ assert(beetle.required_tool_id == &"crab_net")
+ assert(beetle.spawn_anchor_set_id == &"starter_reachable_tree_trunks")
+ assert(beetle.population == 3)
+ assert(is_equal_approx(beetle.spawn_anchor_occupancy_ratio, 0.35))
+ assert(beetle.maximum_anchor_population == 64)
+ assert(beetle.target_population_for_anchor_count(8) == 3)
+ assert(beetle.target_population_for_anchor_count(40) == 14)
+ assert(beetle.target_population_for_anchor_count(400) == 64)
+ assert(is_equal_approx(beetle.sprite_pixel_size, 0.0075))
+ assert(beetle.is_stationary_spawn())
+ assert(not beetle.can_be_scared())
+
+
+func _validate_mesh_surface_sampler() -> void:
+ var prop := Node3D.new()
+ # The accessibility band is local to the planted prop, so the same tree on
+ # a raised cliff remains reachable from that cliff's walkable surface.
+ prop.position = Vector3(3.0, 12.0, -4.0)
+ root.add_child(prop)
+ var visual := MeshInstance3D.new()
+ var box := BoxMesh.new()
+ box.size = Vector3(2.0, 4.0, 2.0)
+ var wood := StandardMaterial3D.new()
+ wood.resource_name = "wood"
+ box.material = wood
+ visual.mesh = box
+ visual.position.y = 2.0
+ prop.add_child(visual)
+ await process_frame
+ var random := RandomNumberGenerator.new()
+ random.seed = 115
+ var sample: Dictionary = MeshSurfaceAnchorSamplerType.sample_vertical_surface(
+ prop,
+ prop,
+ PackedStringArray(["wood"]),
+ 0.7,
+ 1.5,
+ 0.35,
+ 0.025,
+ random,
+ )
+ assert(not sample.is_empty())
+ var surface_position: Vector3 = sample["surface_position"]
+ var anchor_position: Vector3 = sample["position"]
+ assert(surface_position.y >= 0.7 and surface_position.y <= 1.5)
+ var world_anchor_position := prop.to_global(anchor_position)
+ assert(
+ world_anchor_position.y >= 12.7
+ and world_anchor_position.y <= 13.5
+ )
+ assert(is_equal_approx(
+ maxf(absf(surface_position.x), absf(surface_position.z)),
+ 1.0,
+ ))
+ assert(is_equal_approx(
+ anchor_position.distance_to(surface_position),
+ 0.025,
+ ))
+ assert(MeshSurfaceAnchorSamplerType.sample_vertical_surface(
+ prop,
+ prop,
+ PackedStringArray(["leaf"]),
+ 0.7,
+ 1.5,
+ 0.35,
+ 0.025,
+ random,
+ ).is_empty())
+ prop.queue_free()
+
+
+func _validate_tree_anchors() -> void:
+ var region := StarterIslandScene.instantiate() as WorldRegion
+ root.add_child(region)
+ await process_frame
+ var anchor_set: GatherableAnchorSet3D = region.get_gatherable_anchor_set(
+ &"starter_reachable_tree_trunks"
+ )
+ assert(anchor_set != null)
+ var positions: PackedVector3Array = anchor_set.get_spawn_positions()
+ assert(positions.size() == 8)
+ for position: Vector3 in positions:
+ assert(position.is_finite())
+ assert(position.y >= 4.3 and position.y <= 4.5)
+ region.queue_free()
+
+
+func _validate_anchored_presentation() -> void:
+ var beetle: GatherableData = Gatherables.get_entry(&"beetle_stag_common")
+ var presentation := WorldGatherable.new()
+ root.add_child(presentation)
+ presentation.configure("beetle-visual", beetle, Vector3.ZERO, 0.0)
+ var sprite := presentation.get_node("GatherableSprite") as Sprite3D
+ assert(sprite != null)
+ assert(is_zero_approx(sprite.position.y))
+ assert(is_equal_approx(sprite.pixel_size, 0.0075))
+ assert(not sprite.shaded)
+ assert(sprite.billboard == BaseMaterial3D.BILLBOARD_ENABLED)
+ assert(sprite.texture_filter == BaseMaterial3D.TEXTURE_FILTER_NEAREST)
+ presentation.queue_free()
+
+
+func _validate_three_dimensional_targeting() -> void:
+ var service := NetworkWorldSpawnService.new()
+ var beetle: GatherableData = Gatherables.get_entry(&"beetle_stag_common")
+ service.set(
+ "_entities",
+ {
+ "beetle-test": {
+ "entity_id": "beetle-test",
+ "type_id": &"beetle_stag_common",
+ "data": beetle,
+ "position": Vector3(2.0, 4.4, 3.0),
+ "locked": false,
+ },
+ },
+ )
+ assert(
+ service.find_capture_target(
+ Vector3(2.0, 4.4, 3.0),
+ &"crab_net",
+ )
+ == "beetle-test"
+ )
+ assert(
+ service.find_capture_target(
+ Vector3(2.0, 3.0, 3.0),
+ &"crab_net",
+ ).is_empty()
+ )
+ service.free()
diff --git a/tests/tree_gathering_prototype_validation.gd.uid b/tests/tree_gathering_prototype_validation.gd.uid
new file mode 100644
index 0000000..defb119
--- /dev/null
+++ b/tests/tree_gathering_prototype_validation.gd.uid
@@ -0,0 +1 @@
+uid://d0g1xubbi48n
diff --git a/tests/ui_scaling_runtime_validation.gd b/tests/ui_scaling_runtime_validation.gd
index d329755..6e6182a 100644
--- a/tests/ui_scaling_runtime_validation.gd
+++ b/tests/ui_scaling_runtime_validation.gd
@@ -6,6 +6,9 @@ const UIPixelationPresenterType = preload(
const UIReferencePresentationType = preload(
"res://ui/ui_reference_presentation.gd"
)
+const FullscreenMenuPresentationType = preload(
+ "res://ui/fullscreen_menu_presentation.gd"
+)
const GameUIScene := preload("res://ui/game_ui.tscn")
const TARGET_SIZES: Array[Vector2i] = [
Vector2i(1280, 720),
@@ -40,25 +43,49 @@ func _run() -> void:
var player_menu := game_ui.get_node(
"UIRoot/CanonicalStage/PlayerMenu"
) as PlayerMenu
+ var player_menu_profile_stage := player_menu.get_node(
+ "ResponsivePlayerMenuStage"
+ ) as Control
var hotbar := game_ui.get_node(
"UIRoot/CanonicalStage/Hotbar"
) as HotbarUI
+ assert(game_ui.get_node_or_null(
+ "UIRoot/CanonicalStage/GameplayTransientHUD/EffectStatus"
+ ) == null)
var screen_fade := game_ui.get_node("UIRoot/ScreenFade") as Control
var title_screen := game_ui.get_node("UIRoot/TitleScreen") as TitleScreen
var title_content_stage := title_screen.get_node(
"ResponsiveTitleStage"
) as Control
- var decorative_fish_layer := title_screen.get_node(
- "DecorativeFishLayer"
+ var title_profile_root := title_screen.get_node(
+ "ResponsiveTitleStage/TitlePresentationScaleRoot"
) as Control
- var decorative_bubble_layer := title_screen.get_node(
- "DecorativeBubbleLayer"
+ var pause_menu := game_ui.get_node(
+ "UIRoot/CanonicalStage/PauseMenu"
+ ) as PauseMenu
+ var pause_profile_stage := pause_menu.get_node(
+ "ResponsivePauseStage"
) as Control
ui_viewport.add_child(game_ui)
presenter.add_child(ui_viewport)
root.add_child(presenter)
await process_frame
chat_ui.set_available(true)
+ var speech_layer := chat_ui.get("_speech_layer") as Control
+ assert(speech_layer != null and speech_layer.visible)
+ game_ui.call("_on_player_menu_visibility_changed", true)
+ assert(not chat_ui.is_world_speech_visible())
+ assert(not speech_layer.visible)
+ game_ui.call("_on_player_menu_visibility_changed", false)
+ assert(chat_ui.is_world_speech_visible())
+ assert(speech_layer.visible)
+ chat_ui.set_available(true)
+ chat_ui.call(
+ "_set_presentation_state",
+ ChatUI.PresentationState.EXPANDED,
+ false,
+ false,
+ )
print(
"UI viewport embedding: root=%s subviewport=%s"
% [root.gui_embed_subwindows, ui_viewport.gui_embed_subwindows]
@@ -76,6 +103,12 @@ func _run() -> void:
var expected_stage_offset: Vector2 = UIReferencePresentationType.get_offset(
display_size
)
+ var expected_profile_scale: float = (
+ FullscreenMenuPresentationType.get_profile_scale(display_size)
+ )
+ var expected_profile_position: Vector2 = (
+ FullscreenMenuPresentationType.get_profile_position(display_size)
+ )
assert(presenter.position.is_equal_approx(Vector2.ZERO))
assert(ui_root.position.is_equal_approx(Vector2.ZERO))
var expected_visible_size: Vector2 = (
@@ -94,6 +127,12 @@ func _run() -> void:
assert(player_menu.size.is_equal_approx(
UIReferencePresentationType.REFERENCE_SIZE
))
+ assert(player_menu_profile_stage.scale.is_equal_approx(
+ Vector2.ONE * expected_profile_scale
+ ))
+ assert(player_menu_profile_stage.position.is_equal_approx(
+ expected_profile_position
+ ))
assert(hotbar.position.is_equal_approx(Vector2(
0.0,
canonical_stage.position.y,
@@ -108,26 +147,18 @@ func _run() -> void:
assert(title_content_stage.position.is_equal_approx(
canonical_stage.position
))
- assert(decorative_fish_layer.size.is_equal_approx(
- expected_visible_size
+ assert(title_profile_root.scale.is_equal_approx(
+ Vector2.ONE * expected_profile_scale
))
- assert(decorative_bubble_layer.size.is_equal_approx(
- expected_visible_size
+ assert(title_profile_root.position.is_equal_approx(
+ expected_profile_position
+ ))
+ assert(pause_profile_stage.scale.is_equal_approx(
+ Vector2.ONE * expected_profile_scale
+ ))
+ assert(pause_profile_stage.position.is_equal_approx(
+ expected_profile_position
))
- var left_to_right_bounds: Vector2 = (
- TitleScreen.get_decorative_fish_crossing_bounds(
- expected_visible_size.x, 160.0, 1.0
- )
- )
- var right_to_left_bounds: Vector2 = (
- TitleScreen.get_decorative_fish_crossing_bounds(
- expected_visible_size.x, 160.0, -1.0
- )
- )
- assert(left_to_right_bounds.x < -160.0)
- assert(left_to_right_bounds.y > expected_visible_size.x)
- assert(right_to_left_bounds.x > expected_visible_size.x)
- assert(right_to_left_bounds.y < -160.0)
assert(is_equal_approx(presenter.scale.x, presenter.scale.y))
assert(is_equal_approx(ui_root.scale.x, ui_root.scale.y))
var effective_scale: float = presenter.scale.x * ui_root.scale.x
@@ -135,6 +166,34 @@ func _run() -> void:
assert((
canonical_stage.position * effective_scale
).distance_to(expected_stage_offset) <= 0.01)
+ var profile_source_rect: Rect2 = (
+ FullscreenMenuPresentationType.get_source_rect(display_size)
+ )
+ var profile_screen_start: Vector2 = expected_stage_offset + (
+ expected_profile_position
+ + profile_source_rect.position * expected_profile_scale
+ ) * expected_scale
+ var profile_screen_size: Vector2 = (
+ profile_source_rect.size
+ * expected_profile_scale
+ * expected_scale
+ )
+ var compact_target_scale: float = minf(
+ display_size.x / profile_source_rect.size.x,
+ display_size.y / profile_source_rect.size.y,
+ )
+ var expected_profile_screen_size: Vector2 = (
+ profile_source_rect.size * compact_target_scale
+ )
+ var expected_profile_screen_start: Vector2 = (
+ display_size - expected_profile_screen_size
+ ) * 0.5
+ assert(profile_screen_start.distance_to(
+ expected_profile_screen_start
+ ) <= 0.01)
+ assert(profile_screen_size.distance_to(
+ expected_profile_screen_size
+ ) <= 0.01)
var expected_render_height: int = mini(
roundi(UIReferencePresentationType.REFERENCE_SIZE.y * expected_scale),
PlayerSettings.get_ui_render_height(
@@ -154,9 +213,17 @@ func _run() -> void:
assert(presenter.size.is_equal_approx(Vector2(expected_viewport_size)))
var chat_panel := chat_ui.get_node("ChatPanel") as Control
var clock_panel := chat_ui.get_node("WorldClockPanel") as Control
+ var calendar_panel := chat_ui.get_node(
+ "WorldCalendarPanel"
+ ) as Control
+ var status_effect_column := chat_ui.get_node(
+ "StatusEffectColumn"
+ ) as Control
var weather_icon := chat_ui.get_node("WorldWeatherIcon") as Control
assert(chat_panel != null)
assert(clock_panel != null)
+ assert(calendar_panel != null)
+ assert(status_effect_column != null)
assert(weather_icon != null)
assert(chat_panel.visible)
assert(is_equal_approx(chat_panel.position.x, 0.0))
@@ -168,6 +235,34 @@ func _run() -> void:
ChatUI.CLOCK_EDGE_MARGIN,
ChatUI.CLOCK_EDGE_MARGIN,
)))
+ assert(calendar_panel.position.is_equal_approx(Vector2(
+ ChatUI.CLOCK_EDGE_MARGIN,
+ ChatUI.CLOCK_EDGE_MARGIN
+ + ChatUI.CLOCK_SIZE.y
+ + ChatUI.CALENDAR_TOP_GAP,
+ )))
+ assert(calendar_panel.size.is_equal_approx(ChatUI.CALENDAR_SIZE))
+ assert(
+ chat_panel.position.y
+ >= calendar_panel.position.y
+ + calendar_panel.size.y
+ + ChatUI.EXPANDED_CHAT_TOP_GAP
+ - 0.01
+ )
+ assert(is_equal_approx(
+ status_effect_column.position.x,
+ calendar_panel.position.x
+ + (
+ ChatUI.CALENDAR_SIZE.x
+ - ChatUI.STATUS_EFFECT_ICON_SIZE.x
+ ) * 0.5,
+ ))
+ assert(is_equal_approx(
+ status_effect_column.position.y,
+ calendar_panel.position.y
+ + ChatUI.CALENDAR_SIZE.y
+ + ChatUI.STATUS_EFFECT_TOP_GAP,
+ ))
assert(is_equal_approx(
weather_icon.position.y, ChatUI.CLOCK_EDGE_MARGIN
))
@@ -189,13 +284,13 @@ func _run() -> void:
chat_panel.position.x * effective_scale,
]
)
- if OS.has_environment("NETFISHING_UI_SCALE_CAPTURE"):
+ if OS.has_environment("straywild_UI_SCALE_CAPTURE"):
await process_frame
var capture: Image = root.get_texture().get_image()
var capture_path: String = (
"%s-requested-%dx%d-actual-%dx%d.png"
) % [
- OS.get_environment("NETFISHING_UI_SCALE_CAPTURE"),
+ OS.get_environment("straywild_UI_SCALE_CAPTURE"),
target_size.x,
target_size.y,
actual_size.x,
@@ -205,6 +300,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/unified_inventory_validation.gd b/tests/unified_inventory_validation.gd
new file mode 100644
index 0000000..6928969
--- /dev/null
+++ b/tests/unified_inventory_validation.gd
@@ -0,0 +1,288 @@
+extends SceneTree
+
+const ItemCatalogResource: ItemCatalog = preload(
+ "res://items/catalog/item_catalog.tres"
+)
+const Bluegill: FishData = preload(
+ "res://fish/species/bluegill/bluegill.tres"
+)
+const MainShopBuyer: FishBuyerProfile = preload(
+ "res://economy/buyers/main_fishing_shop.tres"
+)
+const FishCatalogResource: FishPool = preload(
+ "res://fish/pools/fish_catalog.tres"
+)
+
+
+func _initialize() -> void:
+ call_deferred("_run")
+
+
+func _run() -> void:
+ var root := Node.new()
+ get_root().add_child(root)
+ var bag := PlayerBag.new()
+ var catches := FishInventory.new()
+ var capacity := PlayerCoolerCapacity.new()
+ var layout := PlayerInventoryLayout.new()
+ var hotbar := PlayerHotbar.new()
+ for node: Node in [bag, catches, capacity, layout, hotbar]:
+ root.add_child(node)
+ bag.setup(ItemCatalogResource)
+ layout.setup(bag, catches, ItemCatalogResource, capacity)
+ bag.set_inventory_layout(layout)
+ catches.set_inventory_layout(layout)
+ hotbar.setup(bag, ItemCatalogResource, catches, layout)
+ assert(PlayerInventoryLayout.INVENTORY_CAPACITIES == [9, 18, 27, 36])
+ assert(PlayerCoolerCapacity.CAPACITIES == [9, 18, 27, 36, 45, 54, 63, 72])
+ var basic_cube: ItemData = ItemCatalogResource.get_item_by_id(&"basic_cube")
+ assert(basic_cube != null)
+ assert(basic_cube.icon != null)
+ assert(basic_cube.hotbar_allowed)
+ assert(bag.add_item(&"basic_cube"))
+ assert(layout.is_item_in_inventory(&"basic_cube"))
+ assert(hotbar.assign_item(0, &"basic_cube"))
+ assert(hotbar.get_item_id(0) == &"basic_cube")
+ assert(
+ layout.get_container(
+ PlayerInventoryLayout.EntryKind.ITEM,
+ &"basic_cube",
+ ) == PlayerInventoryLayout.InventoryContainer.HOTBAR
+ )
+ assert(hotbar.clear_slot(0))
+ assert(layout.is_item_in_inventory(&"basic_cube"))
+ assert(bag.remove_item(&"basic_cube"))
+ var packed_decor_slot: int = hotbar.find_first_empty_slot()
+ assert(packed_decor_slot == 0)
+ assert(layout.prepare_new_item_placement(
+ &"basic_cube",
+ PlayerInventoryLayout.InventoryContainer.HOTBAR,
+ packed_decor_slot,
+ ))
+ assert(bag.add_item(&"basic_cube"))
+ assert(
+ layout.get_container(
+ PlayerInventoryLayout.EntryKind.ITEM,
+ &"basic_cube",
+ ) == PlayerInventoryLayout.InventoryContainer.HOTBAR
+ )
+ assert(hotbar.assign_item(packed_decor_slot, &"basic_cube"))
+ assert(hotbar.select_slot(packed_decor_slot))
+ assert(hotbar.get_selected_item_id() == &"basic_cube")
+ assert(bag.remove_item(&"basic_cube"))
+
+ assert(bag.add_item(&"coffee", 3))
+ assert(layout.get_inventory_count() == 1)
+ assert(layout.get_inventory_capacity() == 9)
+ assert(layout.get_hotbar_count() == 0)
+ assert(hotbar.assign_item(2, &"coffee"))
+ assert(layout.get_inventory_count() == 0)
+ assert(layout.get_hotbar_count() == 1)
+ assert(
+ layout.get_container(PlayerInventoryLayout.EntryKind.ITEM, &"coffee")
+ == PlayerInventoryLayout.InventoryContainer.HOTBAR
+ )
+ assert(hotbar.clear_slot(2))
+ assert(layout.get_inventory_count() == 1)
+ assert(layout.get_hotbar_count() == 0)
+
+ var fish_catch := FishCatch.new()
+ fish_catch.fish = Bluegill
+ fish_catch.fish_id = Bluegill.id
+ fish_catch.catch_id = &"bluegill:unified_inventory_test"
+ fish_catch.weight_lb = Bluegill.get_minimum_weight()
+ fish_catch.display_scale = Bluegill.get_display_scale_for_weight(
+ fish_catch.weight_lb
+ )
+ fish_catch.sale_value = Bluegill.get_sale_value_for_weight(
+ fish_catch.weight_lb
+ )
+ fish_catch.quality = FishQuality.Tier.IMPRESSIVE
+ assert(catches.add_catch(fish_catch))
+ assert(layout.get_inventory_count() == 2)
+ assert(layout.move_entry_to_first_free(
+ PlayerInventoryLayout.EntryKind.CATCH,
+ fish_catch.catch_id,
+ PlayerInventoryLayout.InventoryContainer.STORAGE,
+ ))
+ assert(layout.get_inventory_count() == 1)
+ assert(layout.get_storage_count() == 1)
+ assert(layout.get_storage_capacity() == 9)
+ var inventory_grid := GeneralInventoryGrid.new()
+ root.add_child(inventory_grid)
+ inventory_grid.set_slot_presentation(Vector2(78.0, 78.0), 10)
+ inventory_grid.setup(
+ layout,
+ bag,
+ catches,
+ hotbar,
+ ItemCatalogResource,
+ PlayerInventoryLayout.InventoryContainer.INVENTORY,
+ )
+ var all_inventory_slots: Array = inventory_grid.get("_slots")
+ assert(all_inventory_slots.size() == 36)
+ assert(inventory_grid.get_slots().size() == 9)
+ for slot_index: int in all_inventory_slots.size():
+ var slot := all_inventory_slots[slot_index] as GeneralInventorySlot
+ assert(slot.custom_minimum_size == Vector2(78.0, 78.0))
+ assert(slot.disabled == (slot_index >= 9))
+ assert(slot.tooltip_text.is_empty())
+ var normal := slot.get_theme_stylebox("normal") as StyleBoxFlat
+ assert(normal != null and normal.corner_radius_top_left == 39)
+ var locked_slot := all_inventory_slots[9] as GeneralInventorySlot
+ var locked_icon := locked_slot.get("_icon") as TextureRect
+ assert(locked_icon != null)
+ assert(is_equal_approx(locked_icon.size.x, 24.0))
+ assert(is_equal_approx(locked_icon.size.y, 24.0))
+ assert(is_equal_approx(locked_icon.modulate.a, 0.18))
+ var staged_slot := all_inventory_slots[0] as GeneralInventorySlot
+ staged_slot.set_staged(true)
+ assert(
+ (staged_slot.get_theme_stylebox("normal") as StyleBoxFlat).bg_color
+ == Color(UtilityPageStyle.OCEAN_SELECTED, 0.92)
+ )
+ staged_slot.set_staged(false)
+ var quality_slot := GeneralInventorySlot.new()
+ root.add_child(quality_slot)
+ quality_slot.set_presentation_size(Vector2(78.0, 78.0))
+ quality_slot.configure(
+ 0,
+ PlayerInventoryLayout.InventoryContainer.STORAGE,
+ false,
+ layout,
+ bag,
+ catches,
+ hotbar,
+ ItemCatalogResource,
+ )
+ assert(quality_slot.entry_identity == fish_catch.catch_id)
+ assert(
+ (quality_slot.get_theme_stylebox("normal") as StyleBoxFlat).bg_color
+ == GeneralInventorySlot.quality_background_color(
+ FishQuality.Tier.IMPRESSIVE,
+ GeneralInventorySlot.QualityEmphasis.NORMAL,
+ )
+ )
+ quality_slot.set_staged(true)
+ assert(
+ (quality_slot.get_theme_stylebox("normal") as StyleBoxFlat).bg_color
+ == GeneralInventorySlot.quality_background_color(
+ FishQuality.Tier.IMPRESSIVE,
+ GeneralInventorySlot.QualityEmphasis.SELECTED,
+ )
+ )
+ var distinct_quality_colors: Dictionary[Color, bool] = {}
+ for quality: int in FishQuality.TIER_COUNT:
+ var quality_color := GeneralInventorySlot.quality_background_color(
+ quality,
+ GeneralInventorySlot.QualityEmphasis.NORMAL,
+ )
+ assert(not distinct_quality_colors.has(quality_color))
+ distinct_quality_colors[quality_color] = true
+ assert(distinct_quality_colors.size() == FishQuality.TIER_COUNT)
+ var sale_tray_slot := ShopSaleTraySlot.new()
+ root.add_child(sale_tray_slot)
+ sale_tray_slot.configure(
+ "catch:%s" % fish_catch.catch_id,
+ fish_catch.fish.display_texture,
+ fish_catch.fish.display_name,
+ 1,
+ fish_catch.quality,
+ )
+ assert(
+ sale_tray_slot.custom_minimum_size
+ == GeneralInventoryGrid.DEFAULT_SLOT_SIZE
+ )
+ var sale_tray_style := sale_tray_slot.get_theme_stylebox(
+ "normal"
+ ) as StyleBoxFlat
+ assert(sale_tray_style != null)
+ assert(sale_tray_style.corner_radius_top_left == 26)
+ var expected_tray_quality_color := (
+ GeneralInventorySlot.quality_background_color(
+ FishQuality.Tier.IMPRESSIVE,
+ GeneralInventorySlot.QualityEmphasis.NORMAL,
+ )
+ )
+ expected_tray_quality_color.a = 0.96
+ assert(sale_tray_style.bg_color == expected_tray_quality_color)
+ var wallet := PlayerWallet.new()
+ root.add_child(wallet)
+ assert(wallet.restore_balance(15000))
+ assert(layout.get_next_backpack_cost() == 1500)
+ assert(layout.purchase_backpack(wallet))
+ assert(layout.get_inventory_capacity() == 18)
+ assert(inventory_grid.get_slots().size() == 18)
+ assert(layout.get_next_backpack_cost() == 4500)
+ assert(layout.purchase_backpack(wallet))
+ assert(layout.get_inventory_capacity() == 27)
+ assert(inventory_grid.get_slots().size() == 27)
+ assert(layout.get_next_backpack_cost() == 9000)
+ assert(layout.purchase_backpack(wallet))
+ assert(layout.get_inventory_capacity() == 36)
+ assert(inventory_grid.get_slots().size() == 36)
+ assert(layout.get_next_backpack_cost() == -1)
+ var storage_grid := GeneralInventoryGrid.new()
+ root.add_child(storage_grid)
+ storage_grid.setup(
+ layout,
+ bag,
+ catches,
+ hotbar,
+ ItemCatalogResource,
+ PlayerInventoryLayout.InventoryContainer.STORAGE,
+ )
+ var all_storage_slots: Array = storage_grid.get("_slots")
+ assert(all_storage_slots.size() == 72)
+ assert(storage_grid.get_slots().size() == 9)
+ assert(
+ (all_storage_slots[9] as GeneralInventorySlot).accessibility_name
+ == "locked storage slot 10"
+ )
+ var storage_wallet := PlayerWallet.new()
+ root.add_child(storage_wallet)
+ assert(storage_wallet.restore_balance(9500))
+ for expected_capacity: int in [18, 27, 36, 45, 54, 63, 72]:
+ assert(capacity.purchase(storage_wallet))
+ assert(layout.get_storage_capacity() == expected_capacity)
+ assert(storage_grid.get_slots().size() == expected_capacity)
+ assert(capacity.get_next_capacity() == -1)
+ assert(capacity.get_next_cost() == -1)
+
+ var saved := layout.to_save_data()
+ assert(layout.move_entry_to_first_free(
+ PlayerInventoryLayout.EntryKind.ITEM,
+ &"coffee",
+ PlayerInventoryLayout.InventoryContainer.STORAGE,
+ ))
+ assert(layout.restore_from_save_data(saved))
+ assert(layout.is_item_in_inventory(&"coffee"))
+ assert(
+ layout.get_container(
+ PlayerInventoryLayout.EntryKind.CATCH,
+ fish_catch.catch_id,
+ ) == PlayerInventoryLayout.InventoryContainer.STORAGE
+ )
+
+ var network_sale := NetworkSaleService.new()
+ var session := NetworkSession.new()
+ root.add_child(session)
+ root.add_child(network_sale)
+ network_sale.set("_session", session)
+ network_sale.set("_item_catalog", ItemCatalogResource)
+ network_sale.set("_fish_catalog", FishCatalogResource)
+ var result: Dictionary = network_sale.call(
+ "_build_authoritative_result",
+ 1,
+ "mixed_sale_test",
+ [],
+ [{"item_id": "coffee", "quantity": 2}],
+ MainShopBuyer,
+ )
+ assert(bool(result.get("accepted", false)))
+ assert(int(result.get("payout", -1)) == 20)
+ assert((result.get("items", []) as Array).size() == 1)
+
+ print("Unified inventory validation: PASS")
+ root.free()
+ quit()
diff --git a/tests/unified_inventory_validation.gd.uid b/tests/unified_inventory_validation.gd.uid
new file mode 100644
index 0000000..baaf9c3
--- /dev/null
+++ b/tests/unified_inventory_validation.gd.uid
@@ -0,0 +1 @@
+uid://osuf2xa2bxht
diff --git a/tests/world_layout_multiplayer_validation.gd b/tests/world_layout_multiplayer_validation.gd
new file mode 100644
index 0000000..51e7be6
--- /dev/null
+++ b/tests/world_layout_multiplayer_validation.gd
@@ -0,0 +1,126 @@
+extends SceneTree
+
+const MainScene: PackedScene = preload("res://main/main.tscn")
+const TEST_PORT: int = 18155
+const TEST_SEED: int = 86421357
+
+
+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("World layout 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(bool(main.call(
+ "_apply_world",
+ WorldLayout.STARTER_ISLAND,
+ TEST_SEED,
+ true,
+ )))
+ assert(bool(main.call(
+ "_prepare_host_world",
+ WorldLayout.STARTER_ISLAND,
+ TEST_SEED,
+ )))
+ assert(save_manager.initialize_new_game(
+ TEST_SEED,
+ WorldLayout.STARTER_ISLAND,
+ ))
+ assert(session.start_private_host(TEST_PORT))
+ main.call("_enter_gameplay")
+ assert(session.set_host_open(true))
+ var remote_peer_id: int = await _wait_for_remote_peer(session)
+ assert(remote_peer_id > 1)
+ assert(session.peer_supports_capability(
+ remote_peer_id,
+ NetworkProtocol.WORLD_LAYOUT_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("World layout multiplayer host validation: PASS")
+ await _cleanup(main, session)
+
+
+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 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")):
+ break
+ assert(session.is_joined_client())
+ assert(session.supports_server_capability(
+ NetworkProtocol.WORLD_LAYOUT_CAPABILITY
+ ))
+ var metadata: Dictionary = session.get_last_server_metadata()
+ assert(str(metadata.get("world_layout", "")) == String(
+ WorldLayout.STARTER_ISLAND
+ ))
+ assert(int(metadata.get("world_seed", 0)) == TEST_SEED)
+ var world := main.get("_test_world") as TestWorld
+ assert(world.get_world_layout() == WorldLayout.STARTER_ISLAND)
+ assert(world.get_generation_seed() == TEST_SEED)
+ assert(world.get_node_or_null("Regions/StarterIslandRegion") != null)
+ assert(world.get_fishing_shop() != null)
+ assert(world.get_player_storage() != null)
+ print("World layout multiplayer client validation: PASS")
+ await _cleanup(main, session)
+
+
+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 _cleanup(main: Node, session: NetworkSession) -> void:
+ session.disconnect_session("")
+ main.queue_free()
+ for _frame: int in 4:
+ await process_frame
+ await create_timer(0.1).timeout
+ quit()
diff --git a/tests/world_layout_multiplayer_validation.gd.uid b/tests/world_layout_multiplayer_validation.gd.uid
new file mode 100644
index 0000000..e666cb2
--- /dev/null
+++ b/tests/world_layout_multiplayer_validation.gd.uid
@@ -0,0 +1 @@
+uid://bg52pbgv5ykjc
diff --git a/tests/world_layout_validation.gd b/tests/world_layout_validation.gd
new file mode 100644
index 0000000..e6eec39
--- /dev/null
+++ b/tests/world_layout_validation.gd
@@ -0,0 +1,159 @@
+extends SceneTree
+
+const TestWorldScene: PackedScene = preload("res://world/test_world.tscn")
+const SetupPageScene: PackedScene = preload(
+ "res://ui/new_game_setup_page.tscn"
+)
+
+
+func _initialize() -> void:
+ call_deferred("_run")
+
+
+func _run() -> void:
+ root.size = Vector2i(1280, 720)
+ _validate_layout_values()
+ await _validate_setup_page()
+ await _validate_world_switching()
+ _validate_network_metadata()
+ print("World layout validation: PASS")
+ quit()
+
+
+func _validate_layout_values() -> void:
+ assert(WorldLayout.is_valid(WorldLayout.GENERATED))
+ assert(WorldLayout.is_valid(String(WorldLayout.STARTER_ISLAND)))
+ assert(not WorldLayout.is_valid(&"unknown"))
+ assert(WorldLayout.normalized("unknown") == WorldLayout.GENERATED)
+ assert(WorldLayout.display_name(WorldLayout.STARTER_ISLAND) == "starter island")
+
+
+func _validate_setup_page() -> void:
+ assert(NewGameSetupPage.parse_seed_text(" 24680 ") == 24680)
+ assert(NewGameSetupPage.parse_seed_text("0") == 0)
+ assert(NewGameSetupPage.parse_seed_text("2147483647") == 0)
+ assert(NewGameSetupPage.parse_seed_text("") == 0)
+ var text_seed: int = NewGameSetupPage.parse_seed_text("voyager island")
+ assert(text_seed > 0 and text_seed <= PlayerSaveManager.MAX_WORLD_SEED)
+ assert(NewGameSetupPage.parse_seed_text(" voyager island ") == text_seed)
+ assert(NewGameSetupPage.parse_seed_text("voyager island") == text_seed)
+ assert(NewGameSetupPage.parse_seed_text("Voyager Island") != text_seed)
+ var page := SetupPageScene.instantiate() as NewGameSetupPage
+ root.add_child(page)
+ await process_frame
+ page.open_page(true)
+ await process_frame
+ assert(page.visible)
+ var paper := page.get_node("%Paper") as PanelContainer
+ assert(paper.get_combined_minimum_size().x <= paper.size.x)
+ assert(paper.get_combined_minimum_size().y <= paper.size.y)
+ assert(page.get_selected_world_layout() == WorldLayout.GENERATED)
+ assert(page.get_selected_world_seed() > 0)
+ assert((page.get_node("%OverwriteWarning") as Label).visible)
+ var generated_button := page.get_node("%GeneratedButton") as Button
+ var starter_button := page.get_node("%StarterButton") as Button
+ var random_button := page.get_node("%RandomSeedButton") as Button
+ var custom_button := page.get_node("%CustomSeedButton") as Button
+ var seed_edit := page.get_node("%SeedEdit") as LineEdit
+ var start_button := page.get_node("%StartButton") as Button
+ var back_button := page.get_node("%BackButton") as Button
+ assert(generated_button.get_node(generated_button.focus_neighbor_right) == starter_button)
+ assert(generated_button.get_node(generated_button.focus_neighbor_bottom) == random_button)
+ assert(random_button.get_node(random_button.focus_neighbor_right) == custom_button)
+ assert(start_button.get_node(start_button.focus_neighbor_right) == back_button)
+ page.call("_choose_custom_seed")
+ assert(seed_edit.editable)
+ assert(custom_button.get_node(custom_button.focus_neighbor_bottom) == seed_edit)
+ assert(seed_edit.get_node(seed_edit.focus_neighbor_bottom) == start_button)
+ page.call("_set_world_layout", WorldLayout.STARTER_ISLAND)
+ assert(not (page.get_node("%SeedSection") as Control).visible)
+ var request: Dictionary = {}
+ page.start_requested.connect(func(layout: StringName, seed: int) -> void:
+ request["layout"] = layout
+ request["seed"] = seed
+ )
+ page.call("_request_start")
+ assert(request.get("layout") == WorldLayout.STARTER_ISLAND)
+ assert(int(request.get("seed", 0)) > 0)
+ page.queue_free()
+ await process_frame
+
+
+func _validate_world_switching() -> void:
+ var world := TestWorldScene.instantiate() as TestWorld
+ root.add_child(world)
+ await process_frame
+ assert(world.get_world_layout() == WorldLayout.GENERATED)
+ var initial_generator := world.get_node(
+ "Regions/GeneratedWorldRegion/Terrain/TerrainChunkGenerator"
+ ) as TerrainChunkGenerator
+ assert(initial_generator != null)
+ assert(initial_generator.get_generated_chunks_root() == null)
+ assert(world.get_fishing_shop() != null)
+ assert(world.get_player_storage() != null)
+ _validate_world_boundary_clearance(world)
+ assert(world.activate_world(WorldLayout.STARTER_ISLAND, 13579))
+ assert(world.get_world_layout() == WorldLayout.STARTER_ISLAND)
+ assert(world.get_generation_seed() == 13579)
+ assert(world.get_node_or_null("Regions/StarterIslandRegion") != null)
+ assert(world.get_node_or_null("Regions/GeneratedWorldRegion") == null)
+ assert(world.get_fishing_shop() != null)
+ assert(world.get_player_storage() != null)
+ assert(not world.get_fishable_water_regions().is_empty())
+ var starter_safe_points := world.get_safe_respawn_points()
+ assert(not starter_safe_points.is_empty())
+ var authored_safe_point: SafeRespawnPoint = starter_safe_points.back()
+ assert(
+ world.get_water_recovery_position(
+ authored_safe_point.global_position
+ ).is_equal_approx(authored_safe_point.global_position)
+ )
+ assert(world.activate_world(WorldLayout.GENERATED, 24680))
+ assert(world.get_world_layout() == WorldLayout.GENERATED)
+ assert(world.get_generation_seed() == 24680)
+ assert(world.get_node_or_null("Regions/GeneratedWorldRegion") != null)
+ assert(world.get_node_or_null("Regions/StarterIslandRegion") == null)
+ world.queue_free()
+ await process_frame
+
+
+func _validate_world_boundary_clearance(world: TestWorld) -> void:
+ var active_region := world.get("_active_region") as WorldRegion
+ assert(active_region != null)
+ var half := active_region.get_playable_half_extents()
+ var north := world.get_node("WorldBounds/North") as StaticBody3D
+ var south := world.get_node("WorldBounds/South") as StaticBody3D
+ var west := world.get_node("WorldBounds/West") as StaticBody3D
+ var east := world.get_node("WorldBounds/East") as StaticBody3D
+ var fishing_defaults := FishingSpot.new()
+ assert(
+ TestWorld.WORLD_BOUNDARY_SHORELINE_CLEARANCE
+ > fishing_defaults.maximum_cast_distance + 1.0
+ )
+ fishing_defaults.free()
+ assert(north.position.z - half.y >= 18.0)
+ assert(-south.position.z - half.y >= 18.0)
+ assert(-west.position.x - half.x >= 18.0)
+ assert(east.position.x - half.x >= 18.0)
+
+
+func _validate_network_metadata() -> void:
+ var hello: Dictionary = NetworkProtocol.make_server_hello(
+ true,
+ NetworkProtocol.RejectionCode.NONE,
+ "session",
+ 2,
+ 1,
+ 16,
+ "room",
+ 97531,
+ WorldLayout.STARTER_ISLAND,
+ )
+ assert(str(hello.get("world_layout", "")) == String(
+ WorldLayout.STARTER_ISLAND
+ ))
+ assert(int(hello.get("world_seed", 0)) == 97531)
+ assert(
+ NetworkProtocol.WORLD_LAYOUT_CAPABILITY
+ in PackedStringArray(hello.get("capability_flags", []))
+ )
diff --git a/tests/world_layout_validation.gd.uid b/tests/world_layout_validation.gd.uid
new file mode 100644
index 0000000..9002c58
--- /dev/null
+++ b/tests/world_layout_validation.gd.uid
@@ -0,0 +1 @@
+uid://bgxgyqq73sdpi
diff --git a/tests/world_spawn_multiplayer_validation.gd b/tests/world_spawn_multiplayer_validation.gd
new file mode 100644
index 0000000..f5ffd2e
--- /dev/null
+++ b/tests/world_spawn_multiplayer_validation.gd
@@ -0,0 +1,256 @@
+extends SceneTree
+
+const MainScene: PackedScene = preload("res://main/main.tscn")
+const TEST_PORT: int = 18170
+const EXPECTED_POPULATION: int = 12
+const EXPECTED_BY_TYPE: Dictionary = {
+ &"crab_brown": 2,
+ &"crab_blue": 4,
+ &"clam_manila": 3,
+ &"beetle_stag_common": 3,
+}
+const WINTER_POPULATION: int = 5
+const WINTER_BY_TYPE: Dictionary = {
+ &"crab_brown": 2,
+ &"clam_manila": 3,
+}
+const SUMMER_DATE_ID: String = "2026-08-18"
+const WINTER_DATE_ID: String = "2026-12-18"
+
+
+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("World spawn 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: Node = main.get_node("%NetworkWorldSpawnService")
+ var world_time := main.get_node("%WorldTimeService") as WorldTimeService
+ assert(bool(main.call(
+ "_apply_world", WorldLayout.STARTER_ISLAND, 1, true
+ )))
+ assert(session.set_host_world(WorldLayout.STARTER_ISLAND, 1))
+ assert(session.start_dedicated_host(TEST_PORT, 8, "127.0.0.1"))
+ assert(world_time.synchronize_calendar_time(12.0, SUMMER_DATE_ID))
+ assert(world_time.set_authoritative_time(12.0))
+ assert(session.set_host_open(true))
+ await _wait_for_population(service, EXPECTED_POPULATION)
+ _freeze_timed_entries(service)
+ _validate_population(service, true)
+
+ 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.WORLD_SPAWN_CAPABILITY,
+ ))
+ await create_timer(0.75).timeout
+ assert(world_time.synchronize_calendar_time(12.0, WINTER_DATE_ID))
+ await _wait_for_population(service, WINTER_POPULATION)
+ _validate_population(service, true, WINTER_BY_TYPE)
+ await create_timer(0.75).timeout
+ assert(world_time.synchronize_calendar_time(12.0, SUMMER_DATE_ID))
+ await _wait_for_population(service, EXPECTED_POPULATION)
+ _validate_population(service, true)
+
+ 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))
+ _validate_population(service, true)
+ _validate_respawn_budget(service)
+ print("World spawn 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: Node = main.get_node("%NetworkWorldSpawnService")
+ var world_time := main.get_node("%WorldTimeService") as WorldTimeService
+ 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())
+ assert(session.supports_server_capability(
+ NetworkProtocol.WORLD_SPAWN_CAPABILITY
+ ))
+ await _wait_for_population(service, EXPECTED_POPULATION)
+ _validate_population(service, false)
+ await _wait_for_population(service, WINTER_POPULATION)
+ _validate_population(service, false, WINTER_BY_TYPE)
+ assert(world_time.get_calendar_date_id() == WINTER_DATE_ID)
+ await _wait_for_population(service, EXPECTED_POPULATION)
+ _validate_population(service, false)
+ assert(world_time.get_calendar_date_id() == SUMMER_DATE_ID)
+ print("World spawn 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 _wait_for_population(service: Node, expected_population: int) -> void:
+ var deadline: int = Time.get_ticks_msec() + 12000
+ while Time.get_ticks_msec() < deadline:
+ await process_frame
+ if (service.get("_entities") as Dictionary).size() == expected_population:
+ return
+ assert(false, "Timed out waiting for the world spawn population.")
+
+
+func _validate_population(
+ service: Node,
+ expect_authoritative_state: bool,
+ expected_by_type: Dictionary = EXPECTED_BY_TYPE,
+) -> void:
+ var entities: Dictionary = service.get("_entities")
+ var presentations: Dictionary = service.get("_presentations")
+ var expected_population: int = 0
+ for amount: int in expected_by_type.values():
+ expected_population += amount
+ assert(entities.size() == expected_population)
+ assert(presentations.size() == expected_population)
+ var counts: Dictionary = {}
+ for state: Dictionary in entities.values():
+ var type_id := state.get("type_id") as StringName
+ counts[type_id] = int(counts.get(type_id, 0)) + 1
+ var position: Variant = state.get("position")
+ assert(typeof(position) == TYPE_VECTOR3)
+ assert((position as Vector3).is_finite())
+ var entry := state.get("data") as GatherableData
+ assert(entry != null)
+ assert(
+ (position as Vector3).y
+ >= entry.minimum_surface_y - 0.001
+ )
+ if entry.type_id == &"crab_brown":
+ assert((position as Vector3).y > -0.45)
+ elif entry.type_id == &"crab_blue":
+ assert((position as Vector3).y < -0.45)
+ if expect_authoritative_state:
+ var quality: int = int(state.get("quality", -1))
+ assert(FishQuality.is_valid(quality))
+ if entry.is_stationary_spawn():
+ assert(entry.get_movement_speed_for_quality(quality) <= 0.0)
+ assert(not entry.can_be_scared())
+ else:
+ assert(entry.get_movement_speed_for_quality(quality) > 0.0)
+ assert(entry.get_scare_radius_for_quality(quality) > 0.0)
+ assert(counts == expected_by_type)
+
+
+func _freeze_timed_entries(service: Node) -> void:
+ for state: Dictionary in (service.get("_entities") as Dictionary).values():
+ var entry := state.get("data") as GatherableData
+ if entry != null and entry.active_lifetime_seconds > 0.0:
+ state["expires_at"] = INF
+
+
+func _validate_respawn_budget(service: Node) -> void:
+ var entities: Dictionary = service.get("_entities")
+ var crab_ids: Array[String] = []
+ for entity_id: String in entities:
+ var state: Dictionary = entities[entity_id]
+ if state.get("type_id") == &"crab_brown":
+ crab_ids.append(entity_id)
+ assert(crab_ids.size() == 2)
+ for entity_id: String in crab_ids:
+ service.call(
+ "_despawn_entity",
+ entity_id,
+ &"captured",
+ false,
+ true,
+ )
+ assert(
+ (service.get("_entities") as Dictionary).size()
+ == EXPECTED_POPULATION - crab_ids.size()
+ )
+
+ var now: float = Time.get_ticks_msec() / 1000.0
+ var respawns: Array = service.get("_respawns")
+ assert(respawns.size() == crab_ids.size())
+ for index: int in respawns.size():
+ var respawn: Dictionary = respawns[index]
+ var delay: float = float(respawn.get("due", 0.0)) - now
+ assert(delay >= 479.0 and delay <= 721.0)
+ respawn["due"] = now - 1.0
+ respawns[index] = respawn
+
+ service.call("_update_respawns")
+ assert(
+ (service.get("_entities") as Dictionary).size()
+ == EXPECTED_POPULATION - 1
+ )
+ assert((service.get("_respawns") as Array).size() == 1)
+ var next_by_type: Dictionary = service.get("_next_respawn_by_type")
+ var next_allowed: float = float(next_by_type.get(&"crab_brown", 0.0))
+ assert(next_allowed - now >= 179.0)
+
+ service.call("_update_respawns")
+ assert(
+ (service.get("_entities") as Dictionary).size()
+ == EXPECTED_POPULATION - 1
+ )
+ assert((service.get("_respawns") as Array).size() == 1)
+ next_by_type[&"crab_brown"] = now - 1.0
+ service.call("_update_respawns")
+ assert(
+ (service.get("_entities") as Dictionary).size()
+ == EXPECTED_POPULATION
+ )
+ assert((service.get("_respawns") as Array).is_empty())
+ _validate_population(service, true)
+
+
+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/world_spawn_multiplayer_validation.gd.uid b/tests/world_spawn_multiplayer_validation.gd.uid
new file mode 100644
index 0000000..ea4f1de
--- /dev/null
+++ b/tests/world_spawn_multiplayer_validation.gd.uid
@@ -0,0 +1 @@
+uid://wumg5ev7jr6e
diff --git a/tests/world_spawn_protocol_validation.gd b/tests/world_spawn_protocol_validation.gd
new file mode 100644
index 0000000..e300808
--- /dev/null
+++ b/tests/world_spawn_protocol_validation.gd
@@ -0,0 +1,315 @@
+extends SceneTree
+
+const Gatherables: GatherableCatalog = preload(
+ "res://gathering/catalog/gatherable_catalog.tres"
+)
+const FishCatalog: FishPool = preload("res://fish/pools/fish_catalog.tres")
+const GatheringControllerType = preload(
+ "res://gathering/gathering_controller.gd"
+)
+const WorldGatherableType = preload("res://gathering/world_gatherable.gd")
+
+const CRAB_PIXEL_SIZE: float = 0.0005
+const REDUCED_CATCH_RING_RADIUS: float = 0.175
+const NET_STRIKE_MARKER_DISTANCE: float = 0.85
+const CalendarSeasonType = preload("res://world/calendar_season.gd")
+
+
+func _initialize() -> void:
+ call_deferred(&"_run")
+
+
+func _run() -> void:
+ assert(NetworkProtocol.PROTOCOL_VERSION == 12)
+ assert(
+ NetworkWorldSpawnProtocol.CAPABILITY
+ == NetworkProtocol.WORLD_SPAWN_CAPABILITY
+ )
+ assert(NetworkWorldSpawnProtocol.SNAPSHOT_ENTITIES_PER_ENVELOPE <= 4)
+ _validate_catalog_statuses()
+ await _validate_billboard_presentation()
+ _validate_envelopes()
+ print("World spawn protocol validation: PASS")
+ quit()
+
+
+func _validate_catalog_statuses() -> void:
+ var brown: GatherableData = Gatherables.get_entry(&"crab_brown")
+ assert(brown != null and brown.is_available())
+ assert(brown.catch_data.active)
+ assert(brown.catch_data.collection_method == FishData.CollectionMethod.NET)
+ assert(brown.catch_data.logbook_section == FishData.LogbookSection.SHELLFISH)
+ assert(brown.population == 2)
+ assert(is_equal_approx(brown.charge_duration, 2.0))
+ assert(
+ is_equal_approx(
+ brown.sprite_pixel_size,
+ CRAB_PIXEL_SIZE,
+ )
+ )
+ _validate_quality_behavior(brown)
+ assert(is_equal_approx(brown.capture_respawn_min_seconds, 480.0))
+ assert(is_equal_approx(brown.capture_respawn_max_seconds, 720.0))
+ assert(is_equal_approx(brown.scare_respawn_min_seconds, 45.0))
+ assert(is_equal_approx(brown.scare_respawn_max_seconds, 90.0))
+ assert(is_equal_approx(brown.minimum_respawn_spacing_seconds, 180.0))
+ assert(brown.required_tool_id == &"crab_net")
+ assert(is_equal_approx(brown.minimum_surface_y, -0.44))
+ assert(
+ brown.surface_water_relation
+ == GatherableData.SurfaceWaterRelation.ABOVE_SALT_WATER
+ )
+ assert(is_equal_approx(
+ brown.surface_population_per_100_square_meters,
+ 0.15,
+ ))
+ assert(brown.maximum_surface_population == 24)
+ assert(brown.target_population_for_surface_area(100.0) == 2)
+ assert(brown.target_population_for_surface_area(7500.0) == 12)
+ assert(FishCatalog.get_fish_by_id(&"crab_brown") == brown.catch_data)
+ assert(not brown.catch_data.is_fishable())
+ var clam: GatherableData = Gatherables.get_entry(&"clam_manila")
+ assert(clam != null and clam.is_available())
+ assert(clam.catch_data.collection_method == FishData.CollectionMethod.DIGGING)
+ assert(clam.catch_data.logbook_section == FishData.LogbookSection.SHELLFISH)
+ assert(clam.required_tool_id == &"standard_shovel")
+ assert(clam.diggable_area_id == &"starter_beach")
+ assert(clam.presentation_mode == GatherableData.PresentationMode.WATER_SPURT)
+ assert(clam.is_stationary_hotspot())
+ assert(not clam.requires_sneaking)
+ assert(not clam.can_be_scared())
+ assert(is_equal_approx(clam.active_lifetime_seconds, 10.0))
+ assert(FishCatalog.get_fish_by_id(&"clam_manila") == clam.catch_data)
+ assert(not clam.catch_data.is_fishable())
+ var beetle: GatherableData = Gatherables.get_entry(&"beetle_stag_common")
+ assert(beetle != null and beetle.is_available())
+ assert(beetle.is_available(CalendarSeasonType.Season.SUMMER))
+ assert(not beetle.is_available(CalendarSeasonType.Season.WINTER))
+ assert(beetle.catch_data.collection_method == FishData.CollectionMethod.NET)
+ assert(beetle.required_tool_id == &"crab_net")
+ assert(beetle.spawn_anchor_set_id == &"starter_reachable_tree_trunks")
+ assert(is_equal_approx(beetle.spawn_anchor_occupancy_ratio, 0.35))
+ assert(beetle.maximum_anchor_population == 64)
+ assert(beetle.is_stationary_spawn())
+ assert(not beetle.is_stationary_hotspot())
+ assert(not beetle.requires_sneaking)
+ assert(not beetle.can_be_scared())
+ assert(FishCatalog.get_fish_by_id(&"beetle_stag_common") == beetle.catch_data)
+ assert(not beetle.catch_data.is_fishable())
+
+ var blue: GatherableData = Gatherables.get_entry(&"crab_blue")
+ assert(blue != null and blue.is_available())
+ assert(blue.catch_data.active)
+ assert(blue.is_available(CalendarSeasonType.Season.SUMMER))
+ assert(blue.is_available(CalendarSeasonType.Season.FALL))
+ assert(not blue.is_available(CalendarSeasonType.Season.WINTER))
+ assert(
+ blue.surface_water_relation
+ == GatherableData.SurfaceWaterRelation.BELOW_SALT_WATER
+ )
+ assert(is_equal_approx(
+ blue.surface_population_per_100_square_meters,
+ 0.4,
+ ))
+ assert(blue.maximum_surface_population == 16)
+ assert(blue.target_population_for_surface_area(100.0) == 4)
+ assert(blue.target_population_for_surface_area(2250.0) == 9)
+ assert(FishCatalog.get_fish_by_id(&"crab_blue") == blue.catch_data)
+ assert(not blue.catch_data.is_fishable())
+
+ for type_id: StringName in [&"crab_ghost", &"crab_dungeness"]:
+ var entry: GatherableData = Gatherables.get_entry(type_id)
+ assert(entry != null)
+ assert(entry.is_valid())
+ assert(not entry.catch_data.active)
+ assert(not entry.is_available())
+ assert(
+ is_equal_approx(
+ entry.sprite_pixel_size,
+ CRAB_PIXEL_SIZE,
+ )
+ )
+ assert(entry.catch_data.collection_method == FishData.CollectionMethod.NET)
+ assert(
+ entry.catch_data.logbook_section
+ == FishData.LogbookSection.SHELLFISH
+ )
+
+ var available: Array[GatherableData] = Gatherables.get_available_entries()
+ assert(available.size() == 4)
+ assert(available.has(brown))
+ assert(available.has(blue))
+ assert(available.has(clam))
+ assert(available.has(beetle))
+ var winter_available: Array[GatherableData] = (
+ Gatherables.get_available_entries(CalendarSeasonType.Season.WINTER)
+ )
+ assert(winter_available.has(brown))
+ assert(winter_available.has(clam))
+ assert(not winter_available.has(blue))
+ assert(not winter_available.has(beetle))
+ var rng := RandomNumberGenerator.new()
+ rng.seed = 24680
+ var captured_delay: float = brown.get_respawn_delay(&"captured", rng)
+ var scared_delay: float = brown.get_respawn_delay(&"scared", rng)
+ assert(captured_delay >= 480.0 and captured_delay <= 720.0)
+ assert(scared_delay >= 45.0 and scared_delay <= 90.0)
+
+ var gathering_controller := GatheringControllerType.new()
+ assert(
+ is_equal_approx(
+ gathering_controller.marker_radius,
+ REDUCED_CATCH_RING_RADIUS,
+ )
+ )
+ assert(
+ is_equal_approx(
+ gathering_controller.marker_distance,
+ NET_STRIKE_MARKER_DISTANCE,
+ )
+ )
+ gathering_controller.free()
+
+
+func _validate_billboard_presentation() -> void:
+ var gatherable := WorldGatherableType.new()
+ gatherable.call("_ensure_visual")
+ var sprite := gatherable.get_node("GatherableSprite") as Sprite3D
+ assert(sprite != null)
+ assert(sprite.billboard == BaseMaterial3D.BILLBOARD_ENABLED)
+ assert(sprite.texture_filter == BaseMaterial3D.TEXTURE_FILTER_NEAREST)
+ assert(not sprite.shaded)
+ gatherable.free()
+
+ var unconfigured_hotspot := WorldGatherableType.new()
+ unconfigured_hotspot.call("_ensure_water_spurt_visual")
+ assert(
+ unconfigured_hotspot.get_node_or_null(
+ "WaterSpurt/WaterDroplets"
+ ) == null
+ )
+ unconfigured_hotspot.free()
+
+ var clam: GatherableData = Gatherables.get_entry(&"clam_manila")
+ assert(clam != null)
+ var hotspot := WorldGatherableType.new()
+ root.add_child(hotspot)
+ var configured_position := Vector3(24.0, 0.3, -11.0)
+ hotspot.configure(
+ "world:clam-presentation-regression",
+ clam,
+ configured_position,
+ 0.0,
+ )
+ assert(hotspot.global_position.is_equal_approx(configured_position))
+ var hole := hotspot.get_node("WaterSpurt/BurrowMark") as MeshInstance3D
+ assert(hole != null)
+ assert(hole.cast_shadow == GeometryInstance3D.SHADOW_CASTING_SETTING_OFF)
+ var hole_material := hole.mesh.surface_get_material(0) as StandardMaterial3D
+ assert(hole_material != null)
+ assert(hole_material.shading_mode == BaseMaterial3D.SHADING_MODE_UNSHADED)
+ assert(hole_material.transparency == BaseMaterial3D.TRANSPARENCY_DISABLED)
+ assert(is_equal_approx(hole_material.albedo_color.a, 1.0))
+ var particles := hotspot.get_node(
+ "WaterSpurt/WaterDroplets"
+ ) as CPUParticles3D
+ assert(particles != null and particles.emitting)
+ assert(particles.local_coords)
+ assert(particles.global_position.is_equal_approx(
+ configured_position + Vector3.UP * 0.035
+ ))
+ hotspot.set_process(false)
+ await create_timer(0.6).timeout
+ await process_frame
+ assert(hotspot.get_node_or_null("WaterSpurt/WaterDroplets") == null)
+ hotspot.queue_free()
+
+
+func _validate_quality_behavior(entry: GatherableData) -> void:
+ var prior_speed: float = -1.0
+ var prior_scare_radius: float = -1.0
+ for quality: int in FishQuality.TIER_COUNT:
+ var movement_speed: float = entry.get_movement_speed_for_quality(
+ quality
+ )
+ var scare_radius: float = entry.get_scare_radius_for_quality(quality)
+ assert(movement_speed > prior_speed)
+ assert(scare_radius > prior_scare_radius)
+ prior_speed = movement_speed
+ prior_scare_radius = scare_radius
+ assert(
+ is_equal_approx(
+ entry.get_movement_speed_for_quality(FishQuality.Tier.BORING),
+ entry.movement_speed,
+ )
+ )
+ assert(
+ is_equal_approx(
+ entry.get_movement_speed_for_quality(FishQuality.Tier.SHINY),
+ entry.movement_speed * 2.0,
+ )
+ )
+ assert(
+ is_equal_approx(
+ entry.get_scare_radius_for_quality(FishQuality.Tier.SHINY),
+ entry.scare_radius * 1.7,
+ )
+ )
+ var service := NetworkWorldSpawnService.new()
+ var spawned_catch := service.call(
+ "_create_catch_for_state",
+ entry,
+ {"quality": FishQuality.Tier.EXCEPTIONAL},
+ ) as FishCatch
+ assert(spawned_catch != null and spawned_catch.is_valid())
+ assert(spawned_catch.quality == FishQuality.Tier.EXCEPTIONAL)
+ assert(
+ spawned_catch.sale_value
+ == FishQuality.apply_sale_value(
+ entry.catch_data.get_sale_value_for_weight(
+ spawned_catch.weight_lb
+ ),
+ FishQuality.Tier.EXCEPTIONAL,
+ )
+ )
+ spawned_catch = null
+ service.free()
+
+
+func _validate_envelopes() -> void:
+ var entity: Dictionary = {
+ "entity_id": "world:sample",
+ "type_id": "future_shellfish",
+ "position": [1.0, 2.0, 3.0],
+ "yaw": 0.5,
+ "revision": 7,
+ }
+ assert(NetworkWorldSpawnProtocol.validate_entity_state(entity))
+ var future_entity: Dictionary = entity.duplicate(true)
+ future_entity["future_state"] = {"buried": false}
+ assert(NetworkWorldSpawnProtocol.validate_entity_state(future_entity))
+
+ var envelope: Dictionary = NetworkWorldSpawnProtocol.make_envelope(
+ "session-test",
+ 12,
+ &"future_spawn_type",
+ {"entity": entity, "future_payload": [1, 2, 3]},
+ )
+ assert(NetworkWorldSpawnProtocol.validate_envelope(envelope))
+
+ var malformed_position: Dictionary = entity.duplicate(true)
+ malformed_position["position"] = [1.0, "not-a-number", 3.0]
+ assert(
+ not NetworkWorldSpawnProtocol.validate_entity_state(
+ malformed_position
+ )
+ )
+ assert(
+ not NetworkWorldSpawnProtocol.array_to_vector3(
+ malformed_position["position"]
+ ).is_finite()
+ )
+
+ var invalid_envelope: Dictionary = envelope.duplicate(true)
+ invalid_envelope["envelope_version"] = 0
+ assert(not NetworkWorldSpawnProtocol.validate_envelope(invalid_envelope))
diff --git a/tests/world_spawn_protocol_validation.gd.uid b/tests/world_spawn_protocol_validation.gd.uid
new file mode 100644
index 0000000..e03c707
--- /dev/null
+++ b/tests/world_spawn_protocol_validation.gd.uid
@@ -0,0 +1 @@
+uid://clu8j64mh4x54
diff --git a/tests/world_time_multiplayer_validation.gd b/tests/world_time_multiplayer_validation.gd
index 2d82aea..635ad4f 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
@@ -30,18 +30,15 @@ func _run_host() -> void:
var world_weather := (
main.get_node("%WorldWeatherService") as WorldWeatherService
)
+ assert(bool(main.call(
+ "_apply_world", WorldLayout.STARTER_ISLAND, 1, true
+ )))
+ assert(session.set_host_world(WorldLayout.STARTER_ISLAND, 1))
assert(session.start_private_host(TEST_PORT))
- world_time.synchronize_time(INITIAL_HOST_TIME)
- assert(is_equal_approx(
- world_time.get_persistent_time_hours(), INITIAL_HOST_TIME
+ assert(world_time.set_authoritative_time(INITIAL_HOST_TIME))
+ assert(world_weather.set_authoritative_weather(
+ WorldWeatherService.Weather.RAINY
))
- world_weather.apply_authoritative_snapshot(
- WorldWeatherService.Weather.RAINY, 300.0
- )
- assert(
- world_weather.get_persistent_weather()
- == WorldWeatherService.Weather.RAINY
- )
assert(session.set_host_open(true))
var remote_peer_id: int = 0
@@ -60,19 +57,14 @@ func _run_host() -> void:
remote_peer_id, NetworkProtocol.WORLD_WEATHER_CAPABILITY
))
assert(world_time.get_phase() == WorldTimeService.Phase.DUSK)
-
- await create_timer(1.0).timeout
- world_time.synchronize_time(UPDATED_HOST_TIME)
- assert(is_equal_approx(
- world_time.get_persistent_time_hours(), UPDATED_HOST_TIME
+ assert(WorldTimeService.is_valid_calendar_date_id(
+ world_time.get_calendar_date_id()
+ ))
+ await create_timer(1.0).timeout
+ assert(world_time.set_authoritative_time(UPDATED_HOST_TIME))
+ assert(world_weather.set_authoritative_weather(
+ WorldWeatherService.Weather.FOGGY
))
- world_weather.apply_authoritative_snapshot(
- WorldWeatherService.Weather.FOGGY, 300.0
- )
- assert(
- world_weather.get_persistent_weather()
- == WorldWeatherService.Weather.FOGGY
- )
var disconnect_deadline: int = Time.get_ticks_msec() + 12000
while (
Time.get_ticks_msec() < disconnect_deadline
@@ -83,7 +75,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()
@@ -112,12 +106,6 @@ func _run_client() -> void:
assert(session.supports_server_capability(
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 (
Time.get_ticks_msec() < initial_deadline
@@ -130,6 +118,10 @@ func _run_client() -> void:
world_time.get_time_hours(), INITIAL_HOST_TIME
) <= TIME_TOLERANCE_HOURS)
assert(world_time.get_phase() == WorldTimeService.Phase.DUSK)
+ var expected_date_id: String = WorldTimeService.date_id_from_datetime(
+ Time.get_datetime_dict_from_system(false)
+ )
+ assert(world_time.get_calendar_date_id() == expected_date_id)
var weather_deadline: int = Time.get_ticks_msec() + 8000
while (
Time.get_ticks_msec() < weather_deadline
@@ -141,16 +133,48 @@ func _run_client() -> void:
var chat_ui := game_ui.get_node("%ChatUI") as Control
var clock_panel := chat_ui.get_node("WorldClockPanel") as PanelContainer
var clock_label := clock_panel.get_node("WorldClockLabel") as Label
+ var calendar_panel := (
+ chat_ui.get_node("WorldCalendarPanel") as PanelContainer
+ )
+ var calendar_row := (
+ calendar_panel.get_node("WorldCalendarRow") as HBoxContainer
+ )
+ var calendar_label := (
+ calendar_row.get_node("WorldCalendarLabel") as Label
+ )
+ var calendar_balance := (
+ calendar_row.get_node("WorldCalendarBalance") as CurrencyAmount
+ )
var weather_icon := chat_ui.get_node("WorldWeatherIcon") as WeatherIcon
var chat_panel := chat_ui.get_node("ChatPanel") as PanelContainer
assert(clock_panel.visible)
assert(clock_label.text == world_time.get_clock_text())
assert(clock_label.text.ends_with(" pm"))
+ assert(calendar_panel.visible)
+ assert(calendar_label.text == world_time.get_calendar_text())
+ assert(not calendar_label.text.is_empty())
+ assert(
+ calendar_label.horizontal_alignment
+ == HORIZONTAL_ALIGNMENT_LEFT
+ )
+ assert(calendar_label.size_flags_horizontal == Control.SIZE_EXPAND_FILL)
+ var player := chat_ui.get("_player") as Player
+ assert(calendar_balance.amount == player.wallet.get_balance())
+ assert(player.wallet.credit(1))
+ assert(calendar_balance.amount == player.wallet.get_balance())
assert(weather_icon.visible)
assert(weather_icon.get_weather() == WorldWeatherService.Weather.RAINY)
assert(is_equal_approx(clock_panel.position.y, 10.0))
assert(is_equal_approx(weather_icon.position.y, 10.0))
+ assert(is_equal_approx(
+ calendar_panel.position.y,
+ clock_panel.position.y
+ + clock_panel.size.y
+ + ChatUI.CALENDAR_TOP_GAP,
+ ))
+ assert(calendar_panel.size.is_equal_approx(ChatUI.CALENDAR_SIZE))
assert(clock_panel.position.y + clock_panel.size.y < chat_panel.position.y)
+ assert(not chat_ui.has_method("_handle_chat_command"))
var update_deadline: int = Time.get_ticks_msec() + 10000
while (
@@ -164,7 +188,6 @@ func _run_client() -> void:
world_time.get_time_hours(), UPDATED_HOST_TIME
) <= TIME_TOLERANCE_HOURS)
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())
var fog_deadline: int = Time.get_ticks_msec() + 8000
while (
@@ -174,28 +197,28 @@ 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)
assert(weather_icon.position.x < clock_panel.position.x)
+ assert(is_equal_approx(
+ calendar_panel.position.x, weather_icon.position.x
+ ))
assert(is_equal_approx(clock_panel.position.y, 10.0))
chat_ui.call("set_dock_right", false)
await process_frame
assert(clock_panel.position.x < 20.0)
assert(weather_icon.position.x > clock_panel.position.x)
+ assert(is_equal_approx(
+ calendar_panel.position.x, clock_panel.position.x
+ ))
assert(is_equal_approx(clock_panel.position.y, 10.0))
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 1fed6e7..3caea2b 100644
--- a/tests/world_time_validation.gd
+++ b/tests/world_time_validation.gd
@@ -15,6 +15,7 @@ const FishableWaterRegionType = preload(
"res://world/fishable_water_region.gd"
)
const WeatherIconType = preload("res://ui/weather_icon.gd")
+const CalendarSeasonType = preload("res://world/calendar_season.gd")
const Catalog: FishPoolType = preload("res://fish/pools/fish_catalog.tres")
const SaltWaterMaterial: ShaderMaterial = preload(
@@ -23,14 +24,13 @@ const SaltWaterMaterial: ShaderMaterial = preload(
const FreshWaterMaterial: ShaderMaterial = preload(
"res://world/materials/stylized_water_fresh.tres"
)
-
-
func _initialize() -> void:
call_deferred("_run")
func _run() -> void:
_validate_clock_boundaries_and_duration()
+ _validate_system_clock_authority()
_validate_persistent_host_clock()
_validate_fishing_availability()
_validate_fishing_spot_context()
@@ -42,7 +42,63 @@ func _run() -> void:
func _validate_clock_boundaries_and_duration() -> void:
- assert(WorldTimeServiceType.REAL_SECONDS_PER_CYCLE == 3600.0)
+ assert(WorldTimeServiceType.REAL_SECONDS_PER_CYCLE == 86400.0)
+ assert(is_equal_approx(
+ WorldTimeServiceType.HOURS_PER_REAL_SECOND,
+ 1.0 / 3600.0,
+ ))
+ assert(is_equal_approx(
+ WorldTimeServiceType.time_hours_from_datetime({
+ "hour": 17, "minute": 30, "second": 18,
+ }),
+ 17.505,
+ ))
+ assert(
+ WorldTimeServiceType.date_id_from_datetime({
+ "year": 2026, "month": 8, "day": 18,
+ }) == "2026-08-18"
+ )
+ assert(WorldTimeServiceType.is_valid_calendar_date_id("2026-08-18"))
+ assert(WorldTimeServiceType.is_valid_calendar_date_id("2028-02-29"))
+ assert(not WorldTimeServiceType.is_valid_calendar_date_id("2026-02-29"))
+ assert(not WorldTimeServiceType.is_valid_calendar_date_id("2026-13-01"))
+ assert(not WorldTimeServiceType.is_valid_calendar_date_id("2026-8-18"))
+ assert(
+ WorldTimeServiceType.format_calendar_date("2026-08-18")
+ == "tuesday, august 18"
+ )
+ assert(
+ CalendarSeasonType.from_date_id("2026-03-01")
+ == CalendarSeasonType.Season.SPRING
+ )
+ assert(
+ CalendarSeasonType.from_date_id("2026-06-01")
+ == CalendarSeasonType.Season.SUMMER
+ )
+ assert(
+ CalendarSeasonType.from_date_id("2026-09-01")
+ == CalendarSeasonType.Season.FALL
+ )
+ assert(
+ CalendarSeasonType.from_date_id("2026-12-01")
+ == CalendarSeasonType.Season.WINTER
+ )
+ assert(
+ CalendarSeasonType.from_date_id("invalid")
+ == CalendarSeasonType.UNKNOWN
+ )
+ assert(
+ WorldTimeServiceType.calendar_cycle_id_from_datetime({
+ "year": 2026, "month": 8, "day": 18,
+ "hour": 7, "minute": 59, "second": 0,
+ }, WorldTimeServiceType.DAY_START_HOUR) == "2026-08-17"
+ )
+ assert(
+ WorldTimeServiceType.calendar_cycle_id_from_datetime({
+ "year": 2026, "month": 8, "day": 18,
+ "hour": 8, "minute": 0, "second": 0,
+ }, WorldTimeServiceType.DAY_START_HOUR) == "2026-08-18"
+ )
assert(
WorldTimeServiceType.phase_for_hour(7.49)
== WorldTimeServiceType.Phase.NIGHT
@@ -73,12 +129,16 @@ func _validate_clock_boundaries_and_duration() -> void:
var clock := WorldTimeServiceType.new()
root.add_child(clock)
- clock.begin_session(8.0)
- clock.advance_time(1800.0)
+ clock.begin_test_session(8.0, "2026-08-18")
+ assert(clock.get_calendar_date_id() == "2026-08-18")
+ assert(clock.get_calendar_text() == "tuesday, august 18")
+ assert(clock.get_season() == CalendarSeasonType.Season.SUMMER)
+ assert(clock.get_season_text() == "summer")
+ clock.advance_time(12.0 * 60.0 * 60.0)
assert(is_equal_approx(clock.get_time_hours(), 20.0))
assert(clock.is_night_period())
assert(clock.is_transition())
- clock.advance_time(1800.0)
+ clock.advance_time(12.0 * 60.0 * 60.0)
assert(is_equal_approx(clock.get_time_hours(), 8.0))
assert(not clock.is_night_period())
assert(clock.is_transition())
@@ -90,10 +150,10 @@ func _validate_clock_boundaries_and_duration() -> void:
func(time_hours: float, _phase: WorldTimeService.Phase) -> void:
emitted_times.append(time_hours)
)
- clock.begin_session(12.0 + 31.0 / 60.0)
+ clock.begin_test_session(12.0 + 31.0 / 60.0)
emitted_times.clear()
- for _frame: int in 1201:
- clock.advance_time(1.0 / 240.0)
+ for _frame: int in 7201:
+ clock.advance_time(1.0 / 60.0)
assert(clock.get_clock_text() == "12:33 pm")
assert(emitted_times.size() >= 2)
clock.queue_free()
@@ -103,29 +163,52 @@ func _validate_persistent_host_clock() -> void:
var clock := WorldTimeServiceType.new()
root.add_child(clock)
assert(clock.restore_persistent_time_hours(18.75))
- clock.set_persistence_tracking_enabled(true)
- clock.begin_session(clock.get_persistent_time_hours())
+ clock.begin_test_session(18.75)
assert(is_equal_approx(clock.get_time_hours(), 18.75))
clock.synchronize_time(19.25)
assert(is_equal_approx(clock.get_persistent_time_hours(), 19.25))
- clock.set_persistence_tracking_enabled(false)
clock.synchronize_time(6.5)
assert(is_equal_approx(clock.get_time_hours(), 6.5))
- assert(is_equal_approx(clock.get_persistent_time_hours(), 19.25))
+ assert(is_equal_approx(clock.get_persistent_time_hours(), 6.5))
assert(not clock.restore_persistent_time_hours(-1.0))
assert(not clock.restore_persistent_time_hours(24.0))
clock.queue_free()
+func _validate_system_clock_authority() -> void:
+ var clock := WorldTimeServiceType.new()
+ root.add_child(clock)
+ clock.begin_authoritative_session()
+ var system_datetime: Dictionary = Time.get_datetime_dict_from_system(false)
+ var system_hour: float = WorldTimeServiceType.time_hours_from_datetime(
+ system_datetime
+ )
+ assert(clock.is_using_system_clock())
+ assert(_wrapped_time_difference(
+ clock.get_time_hours(), system_hour
+ ) < 2.0 / 3600.0)
+ assert(
+ clock.get_calendar_date_id()
+ == WorldTimeServiceType.date_id_from_datetime(system_datetime)
+ )
+ assert(clock.set_authoritative_time(12.0))
+ assert(not clock.is_using_system_clock())
+ clock.clear_editor_time_override()
+ assert(clock.is_using_system_clock())
+ clock.queue_free()
+
+
func _validate_fishing_availability() -> void:
var day_context := FishingContextType.new()
- day_context.location_tags = [&"starter_pond"]
+ # Cover both freshwater habitat families so this test isolates time-of-day
+ # gating from the separate pond-versus-river availability rules.
+ day_context.location_tags = [&"starter_pond", &"river"]
day_context.is_night = false
var night_context := FishingContextType.new()
- night_context.location_tags = [&"starter_pond"]
+ night_context.location_tags = [&"starter_pond", &"river"]
night_context.is_night = true
var transition_context := FishingContextType.new()
- transition_context.location_tags = [&"starter_pond"]
+ transition_context.location_tags = [&"starter_pond", &"river"]
transition_context.is_night = true
transition_context.is_day_night_transition = true
@@ -167,7 +250,7 @@ func _validate_fishing_availability() -> void:
func _validate_fishing_spot_context() -> void:
var clock := WorldTimeServiceType.new()
- clock.begin_session(20.25)
+ clock.begin_test_session(20.25, "2026-12-18")
var fishing_spot := FishingSpotType.new()
fishing_spot.set("_world_time", clock)
var region := FishableWaterRegionType.new()
@@ -176,6 +259,7 @@ func _validate_fishing_spot_context() -> void:
var dusk_context: FishingContext = fishing_spot.build_network_context(region)
assert(dusk_context.is_night)
assert(dusk_context.is_day_night_transition)
+ assert(dusk_context.season == CalendarSeasonType.Season.WINTER)
clock.synchronize_time(14.0)
var day_context: FishingContext = fishing_spot.build_network_context(region)
assert(not day_context.is_night)
@@ -189,16 +273,25 @@ func _validate_network_snapshot_bounds() -> void:
assert(NetworkWorldTimeServiceType.validate_snapshot({
"session_id": "session",
"time_hours": 8.25,
+ "calendar_date_id": "2026-08-18",
"sequence": 1,
}))
assert(not NetworkWorldTimeServiceType.validate_snapshot({
"session_id": "session",
"time_hours": 24.0,
+ "calendar_date_id": "2026-08-18",
"sequence": 1,
}))
assert(not NetworkWorldTimeServiceType.validate_snapshot({
"session_id": "",
"time_hours": 8.0,
+ "calendar_date_id": "2026-08-18",
+ "sequence": 1,
+ }))
+ assert(not NetworkWorldTimeServiceType.validate_snapshot({
+ "session_id": "session",
+ "time_hours": 8.0,
+ "calendar_date_id": "2026-02-29",
"sequence": 1,
}))
@@ -211,7 +304,7 @@ func _validate_environment_presentation() -> void:
var sky := Sky.new()
var sky_material := ShaderMaterial.new()
sky_material.shader = preload(
- "res://world/environment/netfishing_sky.gdshader"
+ "res://world/environment/straywild_sky.gdshader"
)
sky.sky_material = sky_material
environment.sky = sky
@@ -248,7 +341,45 @@ func _validate_environment_presentation() -> void:
float(runtime_sky_material.get_shader_parameter("moon_visibility"))
< 0.01
)
+ assert(
+ float(runtime_sky_material.get_shader_parameter("star_visibility"))
+ < 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/straywild_sky.gdshader"
+ )
+ assert("uniform float fog_horizon_occlusion" in sky_shader.code)
+ assert("fog_horizon_color.rgb" in sky_shader.code)
+ assert("uniform float star_visibility" in sky_shader.code)
+ assert("float procedural_star_field" in sky_shader.code)
+ assert("upper_sky_fade" 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(
+ "water_alpha *= mix(0.82, 1.0, contact_stability);"
+ 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")
)
@@ -276,9 +407,23 @@ func _validate_environment_presentation() -> void:
runtime_sky_material.get_shader_parameter("sun_direction") as Vector3
)
assert(night_moon_visibility > 0.99)
+ assert(
+ float(runtime_sky_material.get_shader_parameter("star_visibility"))
+ > 0.99
+ )
+ assert((
+ runtime_sky_material.get_shader_parameter("star_color") as Color
+ ).is_equal_approx(WorldTimeVisualController.STAR_COLOR))
+ assert(is_equal_approx(
+ float(runtime_sky_material.get_shader_parameter("star_strength")),
+ WorldTimeVisualController.STAR_STRENGTH,
+ ))
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")
)
@@ -292,17 +437,61 @@ func _validate_environment_presentation() -> void:
)
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(is_zero_approx(float(
+ runtime_sky_material.get_shader_parameter("star_visibility")
+ )))
+ 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_zero_approx(runtime_environment.fog_sky_affect))
+ 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, 28.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,
@@ -311,6 +500,14 @@ func _validate_environment_presentation() -> void:
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,
@@ -319,8 +516,39 @@ func _validate_environment_presentation() -> void:
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_star_visibility := float(
+ runtime_sky_material.get_shader_parameter("star_visibility")
+ )
+ assert(dusk_star_visibility > 0.45 and dusk_star_visibility < 0.55)
var dusk_horizon: Color = runtime_sky_material.get_shader_parameter(
"sky_horizon_color"
) as Color
@@ -338,3 +566,8 @@ func _validate_weather_clock_icon() -> void:
weather_icon.set_weather(WorldWeatherService.Weather.CLOUDY)
assert(weather_icon.tooltip_text == "cloudy")
weather_icon.queue_free()
+
+
+static func _wrapped_time_difference(left: float, right: float) -> float:
+ var difference: float = absf(left - right)
+ return minf(difference, WorldTimeServiceType.HOURS_PER_DAY - difference)
diff --git a/tests/world_weather_validation.gd b/tests/world_weather_validation.gd
index 1035a09..7bc5114 100644
--- a/tests/world_weather_validation.gd
+++ b/tests/world_weather_validation.gd
@@ -10,6 +10,9 @@ const WorldTimeServiceType = preload("res://world/world_time_service.gd")
const WorldTimeVisualControllerType = preload(
"res://world/world_time_visual_controller.gd"
)
+const PrecipitationOcclusionType = preload(
+ "res://world/environment/precipitation_occlusion.gd"
+)
const FishingContextType = preload("res://fishing/fishing_context.gd")
const FishingSpotType = preload("res://fishing/fishing_spot.gd")
const FishAvailabilityType = preload("res://fish/fish_availability.gd")
@@ -24,7 +27,9 @@ func _initialize() -> void:
func _run() -> void:
_validate_weather_scheduler()
+ _validate_authoritative_weather_override()
_validate_weather_persistence()
+ _validate_legacy_forecast_save_migration()
_validate_snapshot_bounds()
_validate_fishing_weather_seams()
_validate_fishing_spot_context()
@@ -64,14 +69,57 @@ func _validate_weather_scheduler() -> void:
func _duration_is_valid(weather: WorldWeatherServiceType) -> bool:
var seconds: float = weather.get_seconds_remaining()
- match weather.get_weather():
- WorldWeatherServiceType.Weather.SUNNY:
- return seconds >= 480.0 and seconds <= 900.0
- WorldWeatherServiceType.Weather.CLOUDY:
- return seconds >= 300.0 and seconds <= 720.0
- WorldWeatherServiceType.Weather.RAINY, WorldWeatherServiceType.Weather.FOGGY:
- return seconds >= 300.0 and seconds <= 600.0
- return false
+ return is_equal_approx(
+ seconds, WorldWeatherServiceType.WEATHER_PERIOD_SECONDS
+ )
+
+
+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_test_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:
@@ -110,8 +158,11 @@ func _validate_weather_persistence() -> void:
))
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(weather.get_weather() == WorldWeatherServiceType.DEFAULT_WEATHER)
+ assert(is_equal_approx(
+ weather.get_seconds_remaining(),
+ WorldWeatherServiceType.WEATHER_PERIOD_SECONDS,
+ ))
assert(not weather.restore_persistent_state(
WorldWeatherServiceType.Weather.RAINY,
WorldWeatherServiceType.MAX_PERSISTED_SECONDS + 1.0,
@@ -135,11 +186,49 @@ func _validate_snapshot_bounds() -> void:
assert(not NetworkWorldWeatherServiceType.validate_snapshot({
"session_id": "session",
"weather": int(WorldWeatherServiceType.Weather.RAINY),
- "seconds_remaining": 1801.0,
+ "seconds_remaining": 3601.0,
"sequence": 2,
}))
+func _validate_legacy_forecast_save_migration() -> void:
+ var legacy_schedule: Array[Dictionary] = []
+ for index: int in JobCatalog.LEGACY_WEATHER_SEGMENT_COUNT:
+ legacy_schedule.append({
+ "start_hour": fposmod(
+ WorldTimeService.DAY_START_HOUR
+ + float(index) * JobCatalog.LEGACY_WEATHER_SEGMENT_HOURS,
+ WorldTimeService.HOURS_PER_DAY,
+ ),
+ "weather": int(WorldWeatherService.Weather.SUNNY),
+ })
+ var legacy_job: Dictionary = {
+ "id": "legacy-catch",
+ "title": "legacy catch",
+ "description": "catch one fish",
+ "kind": int(JobCatalog.Kind.CATCH_TOTAL),
+ "target": 1,
+ "fish_coin": 1,
+ "experience": 1,
+ }
+ var save_data: Dictionary = PlayerJobService.default_save_data()
+ save_data["host_board"] = {
+ "plan_id": "legacy-plan",
+ "cycle": 4,
+ "schedule_anchor_index": 0,
+ "jobs": [legacy_job],
+ "weather_schedule": legacy_schedule,
+ }
+ save_data["active_plan_id"] = "legacy-plan"
+ assert(PlayerJobService.validate_save_data(save_data))
+ var jobs := PlayerJobService.new()
+ assert(jobs.restore_from_save_data(save_data))
+ var migrated: Dictionary = jobs.to_save_data()
+ assert((migrated.get("host_board", {}) as Dictionary).is_empty())
+ assert(str(migrated.get("active_plan_id", "")).is_empty())
+ jobs.free()
+
+
func _validate_fishing_weather_seams() -> void:
var clear_context := FishingContextType.new()
var rain_context := FishingContextType.new()
@@ -194,7 +283,7 @@ func _validate_weather_presentation() -> void:
var sky := Sky.new()
var sky_material := ShaderMaterial.new()
sky_material.shader = preload(
- "res://world/environment/netfishing_sky.gdshader"
+ "res://world/environment/straywild_sky.gdshader"
)
sky.sky_material = sky_material
environment.sky = sky
@@ -206,26 +295,170 @@ func _validate_weather_presentation() -> void:
world_root.add_child(sun)
var clock := WorldTimeServiceType.new()
world_root.add_child(clock)
- clock.begin_session(14.0)
+ clock.begin_test_session(14.0)
var weather := WorldWeatherServiceType.new()
world_root.add_child(weather)
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 clear_saturation: float = runtime_environment.adjustment_saturation
+ var runtime_sky_material := (
+ runtime_environment.sky.sky_material as ShaderMaterial
+ )
+ clock.set_authoritative_time(0.0)
+ visuals.apply_time_immediately(0.0)
+ assert(float(
+ runtime_sky_material.get_shader_parameter("star_visibility")
+ ) > 0.99)
+ visuals.apply_weather_immediately(
+ WorldWeatherServiceType.Weather.CLOUDY
+ )
+ assert(is_equal_approx(float(
+ runtime_sky_material.get_shader_parameter("star_visibility")
+ ), 0.18))
+ visuals.apply_weather_immediately(
+ WorldWeatherServiceType.Weather.RAINY
+ )
+ assert(is_equal_approx(float(
+ runtime_sky_material.get_shader_parameter("star_visibility")
+ ), 0.03))
visuals.apply_weather_immediately(
WorldWeatherServiceType.Weather.FOGGY
)
+ assert(is_zero_approx(float(
+ runtime_sky_material.get_shader_parameter("star_visibility")
+ )))
+ visuals.apply_weather_immediately(
+ WorldWeatherServiceType.Weather.SUNNY
+ )
+ clock.set_authoritative_time(14.0)
+ visuals.apply_time_immediately(14.0)
+ 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
+ )
+ 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(is_zero_approx(runtime_environment.fog_sky_affect))
- assert(runtime_environment.fog_aerial_perspective >= 0.91)
+ 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,
@@ -233,13 +466,89 @@ func _validate_weather_presentation() -> void:
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(
+ WorldTimeVisualControllerType.RAIN_EMITTER_OFFSET.y
+ >= WorldTimeVisualControllerType.RAIN_SUPPORTED_CANOPY_HEIGHT
+ + WorldTimeVisualControllerType.RAIN_CANOPY_CLEARANCE
+ )
+ assert(is_equal_approx(
+ rain.lifetime,
+ WorldTimeVisualControllerType.RAIN_LIFETIME_SECONDS,
+ ))
+ assert(rain.visibility_aabb == (
+ WorldTimeVisualControllerType.RAIN_VISIBILITY_AABB
+ ))
var rain_material := rain.process_material as ParticleProcessMaterial
assert(rain_material != null)
+ assert(
+ rain_material.collision_mode
+ == ParticleProcessMaterial.COLLISION_HIDE_ON_CONTACT
+ )
+ assert(is_equal_approx(
+ rain.collision_base_size,
+ WorldTimeVisualControllerType.RAIN_COLLISION_BASE_SIZE,
+ ))
+ 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,
@@ -248,13 +557,85 @@ func _validate_weather_presentation() -> void:
rain_material.initial_velocity_max,
WorldTimeVisualControllerType.RAIN_VELOCITY_MAX,
))
+ assert(rain_material.gravity.is_equal_approx(
+ WorldTimeVisualControllerType.RAIN_GRAVITY
+ ))
var rain_mesh := rain.draw_pass_1 as BoxMesh
assert(rain_mesh != null)
assert(rain_mesh.size.is_equal_approx(
WorldTimeVisualControllerType.RAIN_DROP_SIZE
))
+ var rain_collision := visuals.get_node(
+ "LocalRainCanopyCollision"
+ ) as GPUParticlesCollisionHeightField3D
+ assert(rain_collision != null)
+ assert(rain_collision.follow_camera_enabled)
+ assert(
+ rain_collision.resolution
+ == GPUParticlesCollisionHeightField3D.RESOLUTION_256
+ )
+ assert(
+ rain_collision.update_mode
+ == GPUParticlesCollisionHeightField3D.UPDATE_MODE_WHEN_MOVED
+ )
+ assert(rain_collision.size.is_equal_approx(
+ WorldTimeVisualControllerType.RAIN_COLLISION_SIZE
+ ))
+ assert(
+ rain_collision.size.y * 0.5
+ > WorldTimeVisualControllerType.RAIN_EMITTER_OFFSET.y
+ + WorldTimeVisualControllerType.RAIN_EMISSION_EXTENTS.y
+ )
+ assert(
+ rain_collision.heightfield_mask
+ == PrecipitationOcclusionType.RENDER_LAYER_MASK
+ )
+ 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/tools/art/export_creature_art.py b/tools/art/export_creature_art.py
new file mode 100644
index 0000000..758be87
--- /dev/null
+++ b/tools/art/export_creature_art.py
@@ -0,0 +1,256 @@
+#!/usr/bin/env python3
+"""Export cataloged creature artwork with consistent transparent margins."""
+
+from __future__ import annotations
+
+import argparse
+import hashlib
+import re
+import sys
+import zipfile
+import xml.etree.ElementTree as ET
+from pathlib import Path
+
+from PIL import Image
+
+
+TABLE_NAMESPACE = "urn:oasis:names:tc:opendocument:xmlns:table:1.0"
+TABLE_ATTRIBUTE = "{%s}" % TABLE_NAMESPACE
+NAMESPACES = {"table": TABLE_NAMESPACE}
+VALID_EXPORT_SIZES = {64, 128, 256, 512}
+SAFE_AREA_RATIO = 0.875
+KNOWN_SOURCE_ALIASES = {
+ # The delivered filename predates the catalog's authoritative species ID.
+ "bowfish": "bowfin",
+}
+
+
+def parse_arguments() -> argparse.Namespace:
+ parser = argparse.ArgumentParser(
+ description=(
+ "Export PNG creature art according to the catalog's recommended "
+ "canvas size, using alpha-bound trimming and nearest-neighbor scaling."
+ )
+ )
+ parser.add_argument("--tracker", required=True, type=Path)
+ parser.add_argument("--source", required=True, type=Path)
+ parser.add_argument("--output", required=True, type=Path)
+ parser.add_argument(
+ "--skip",
+ action="append",
+ default=[],
+ help="Catalog creature ID or source filename stem to skip; repeat as needed.",
+ )
+ parser.add_argument(
+ "--dry-run",
+ action="store_true",
+ help="Validate and report the batch without writing output files.",
+ )
+ return parser.parse_args()
+
+
+def normalize_name(value: str) -> str:
+ normalized = value.strip().lower().replace("&", " and ")
+ normalized = re.sub(r"[^a-z0-9]+", "_", normalized)
+ return normalized.strip("_")
+
+
+def expanded_row_values(row: ET.Element) -> list[str]:
+ values: list[str] = []
+ for cell in row.findall("table:table-cell", NAMESPACES):
+ repeat = int(
+ cell.attrib.get(
+ TABLE_ATTRIBUTE + "number-columns-repeated",
+ "1",
+ )
+ )
+ values.extend(["".join(cell.itertext()).strip()] * repeat)
+ return values
+
+
+def load_catalog(tracker_path: Path) -> list[dict[str, str]]:
+ with zipfile.ZipFile(tracker_path) as archive:
+ root = ET.fromstring(archive.read("content.xml"))
+ table = root.find(".//table:table", NAMESPACES)
+ if table is None:
+ raise ValueError("tracker does not contain a table")
+ rows = table.findall("table:table-row", NAMESPACES)
+ if not rows:
+ raise ValueError("tracker table is empty")
+ headers = expanded_row_values(rows[0])
+ required_headers = {
+ "catalog_number",
+ "display_name",
+ "id",
+ "recommended_export_canvas_px",
+ }
+ missing_headers = required_headers.difference(headers)
+ if missing_headers:
+ raise ValueError(
+ "tracker is missing required columns: "
+ + ", ".join(sorted(missing_headers))
+ )
+ records: list[dict[str, str]] = []
+ for row in rows[1:]:
+ values = expanded_row_values(row)
+ if not any(values):
+ continue
+ record = dict(zip(headers, values))
+ if record.get("id", "").strip():
+ records.append(record)
+ return records
+
+
+def catalog_lookup(records: list[dict[str, str]]) -> dict[str, dict[str, str]]:
+ lookup: dict[str, dict[str, str]] = {}
+ for record in records:
+ keys = {
+ normalize_name(record["catalog_number"]),
+ normalize_name(record["id"]),
+ normalize_name(record["display_name"]),
+ }
+ for key in keys:
+ previous = lookup.get(key)
+ if previous is not None and previous["id"] != record["id"]:
+ raise ValueError(
+ f"ambiguous normalized catalog name {key!r}: "
+ f"{previous['id']} and {record['id']}"
+ )
+ lookup[key] = record
+ return lookup
+
+
+def resolve_record(
+ source_path: Path,
+ lookup: dict[str, dict[str, str]],
+) -> dict[str, str]:
+ source_key = normalize_name(source_path.stem)
+ catalog_key = KNOWN_SOURCE_ALIASES.get(source_key, source_key)
+ record = lookup.get(catalog_key)
+ if record is None:
+ raise ValueError(
+ f"cannot map source {source_path.name!r} to one catalog creature"
+ )
+ return record
+
+
+def normalize_artwork(source_path: Path, canvas_size: int) -> tuple[Image.Image, tuple[int, int, int, int], tuple[int, int]]:
+ with Image.open(source_path) as source_image:
+ artwork = source_image.convert("RGBA")
+ alpha_bounds = artwork.getchannel("A").getbbox()
+ if alpha_bounds is None:
+ raise ValueError(f"source {source_path.name!r} is fully transparent")
+ visible = artwork.crop(alpha_bounds)
+ safe_long_side = round(canvas_size * SAFE_AREA_RATIO)
+ scale = safe_long_side / max(visible.width, visible.height)
+ scaled_size = (
+ max(1, round(visible.width * scale)),
+ max(1, round(visible.height * scale)),
+ )
+ visible = visible.resize(scaled_size, Image.Resampling.NEAREST)
+ canvas = Image.new("RGBA", (canvas_size, canvas_size), (0, 0, 0, 0))
+ offset = (
+ (canvas_size - scaled_size[0]) // 2,
+ (canvas_size - scaled_size[1]) // 2,
+ )
+ canvas.alpha_composite(visible, offset)
+ return canvas, alpha_bounds, scaled_size
+
+
+def sha256(path: Path) -> str:
+ digest = hashlib.sha256()
+ with path.open("rb") as source:
+ for block in iter(lambda: source.read(1024 * 1024), b""):
+ digest.update(block)
+ return digest.hexdigest()
+
+
+def main() -> int:
+ arguments = parse_arguments()
+ records = load_catalog(arguments.tracker)
+ lookup = catalog_lookup(records)
+ skip_keys = {normalize_name(value) for value in arguments.skip}
+ source_paths = sorted(arguments.source.glob("*.png"))
+ if not source_paths:
+ raise ValueError(f"no PNG files found under {arguments.source}")
+
+ resolved_by_output: dict[str, tuple[Path, dict[str, str]]] = {}
+ for source_path in source_paths:
+ record = resolve_record(source_path, lookup)
+ source_key = normalize_name(source_path.stem)
+ creature_key = normalize_name(record["id"])
+ if source_key in skip_keys or creature_key in skip_keys:
+ print(f"SKIP {source_path.name} -> {record['id']}")
+ continue
+ output_name = f"{record['catalog_number']}.png"
+ previous = resolved_by_output.get(output_name)
+ if previous is not None:
+ previous_path, _previous_record = previous
+ catalog_key = normalize_name(record["catalog_number"])
+ previous_is_numbered = (
+ normalize_name(previous_path.stem) == catalog_key
+ )
+ current_is_numbered = source_key == catalog_key
+ if current_is_numbered and not previous_is_numbered:
+ print(
+ "SUPERSEDE "
+ f"{previous_path.name} with numbered source "
+ f"{source_path.name} -> {record['id']}"
+ )
+ resolved_by_output[output_name] = (source_path, record)
+ continue
+ if previous_is_numbered and not current_is_numbered:
+ print(
+ "SKIP "
+ f"{source_path.name}; numbered source "
+ f"{previous_path.name} takes precedence -> {record['id']}"
+ )
+ continue
+ raise ValueError(
+ f"duplicate sources for output filename {output_name}: "
+ f"{previous_path.name} and {source_path.name}"
+ )
+ resolved_by_output[output_name] = (source_path, record)
+
+ resolved = list(resolved_by_output.values())
+
+ if not arguments.dry_run:
+ arguments.output.mkdir(parents=True, exist_ok=True)
+
+ for source_path, record in resolved:
+ canvas_size = int(record["recommended_export_canvas_px"])
+ if canvas_size not in VALID_EXPORT_SIZES:
+ raise ValueError(
+ f"{record['id']} has invalid export size {canvas_size}"
+ )
+ image, alpha_bounds, scaled_size = normalize_artwork(
+ source_path,
+ canvas_size,
+ )
+ output_path = arguments.output / f"{record['catalog_number']}.png"
+ if not arguments.dry_run:
+ image.save(output_path, format="PNG", optimize=False, compress_level=6)
+ output_hash = sha256(output_path)
+ else:
+ output_hash = "dry-run"
+ print(
+ "EXPORT "
+ f"{source_path.name} -> {output_path.name} "
+ f"id={record['id']} canvas={canvas_size}x{canvas_size} "
+ f"alpha_bounds={alpha_bounds} visible={scaled_size[0]}x{scaled_size[1]} "
+ f"source_sha256={sha256(source_path)} output_sha256={output_hash}"
+ )
+
+ print(
+ f"Complete: {len(resolved)} export(s), "
+ f"{len(source_paths) - len(resolved)} skipped."
+ )
+ return 0
+
+
+if __name__ == "__main__":
+ try:
+ raise SystemExit(main())
+ except (OSError, ValueError, zipfile.BadZipFile) as error:
+ print(f"ERROR: {error}", file=sys.stderr)
+ raise SystemExit(1)
diff --git a/tools/blender/export_terrain_chunks.py b/tools/blender/export_terrain_chunks.py
new file mode 100644
index 0000000..d4a278e
--- /dev/null
+++ b/tools/blender/export_terrain_chunks.py
@@ -0,0 +1,873 @@
+#!/usr/bin/env python3
+"""Export convention-named terrain chunk collections to individual GLBs.
+
+Run through Blender rather than a standalone Python interpreter:
+
+ blender --background terrain_chunks.blend \
+ --python tools/blender/export_terrain_chunks.py -- \
+ --output /path/to/terrain_chunks
+
+Collections use ``chunk_####_description`` and contain one primary terrain
+mesh named ``chunk_####``. Additional production objects may live in the same
+collection. Reusable procedural props use individual ``prop_description``
+mesh objects. Their child empties are exported with them so authored sockets
+remain attached to the prop. Props may be arranged anywhere in the source file
+because each root object's authored origin becomes the exported runtime anchor.
+A same-named ``prop_description`` collection remains supported for multi-object
+props. Unrelated objects and collections are ignored.
+"""
+
+from __future__ import annotations
+
+import argparse
+import hashlib
+import json
+import math
+import re
+import struct
+import sys
+from dataclasses import dataclass
+from pathlib import Path
+from typing import Iterable
+
+import bpy
+from mathutils import Vector
+
+
+CHUNK_SIZE_METERS = 10.0
+# Match the runtime boundary analyzer's five-millimeter authoring tolerance.
+DEFAULT_TOLERANCE = 0.005
+COLLECTION_PATTERN = re.compile(
+ r"^chunk_(?P\d{4})(?:_(?P