diff --git a/ASSET-LICENSE.md b/ASSET-LICENSE.md index 56a95ab..50933b5 100644 --- a/ASSET-LICENSE.md +++ b/ASSET-LICENSE.md @@ -1,19 +1,20 @@ # NETfishing Asset License 1.0 -Copyright © 2026 Alexander Sellite and Rheannon Eisworth. All rights -reserved except for the permissions expressly granted below. +Copyright © 2026 Woofmeow. All rights reserved except for the permissions +expressly granted below. ## 1. Scope This license applies to original NETfishing artwork, graphics, textures, models, animations, music, sound recordings, narrative content, visual -designs, characters, and other creative assets owned by Alexander Sellite and -Rheannon Eisworth (the **“NETfishing Assets”**). +designs, characters, and other creative assets owned by the NETfishing owners, +publishing as Woofmeow (the **“NETfishing Assets”**). It does not apply to: - software source code identified as GPL-3.0-or-later; -- third-party material identified in `THIRD-PARTY-NOTICES.md`; +- third-party material identified in the source repository's + `docs/ATTRIBUTION.md` or the packaged `ATTRIBUTION.md` notice; - material accompanied by a different license; or - the NETfishing and Woofmeow names and logos, which are addressed in `TRADEMARKS.md`. @@ -62,9 +63,9 @@ A distributed Mod must: 2. be presented as unofficial community content; 3. include the following notice in its documentation or distribution page: - > Contains or adapts NETfishing assets © 2026 Alexander Sellite and - > Rheannon Eisworth, used under the NETfishing Asset License 1.0. This Mod - > is unofficial and is not endorsed by Woofmeow. + > Contains or adapts NETfishing assets © 2026 Woofmeow, used under the + > NETfishing Asset License 1.0. This Mod is unofficial and is not endorsed + > by Woofmeow. 4. identify material created by the Mod author separately from NETfishing Assets; and @@ -98,9 +99,9 @@ owners grant separate written permission. ## 6. Ownership of Mods -You retain ownership of your original Mod material. Alexander Sellite and -Rheannon Eisworth retain ownership of the NETfishing Assets and adaptations of -those assets to the extent provided by applicable law. +You retain ownership of your original Mod material. The NETfishing owners +retain ownership of the NETfishing Assets and adaptations of those assets to +the extent provided by applicable law. This license does not require you to assign your original Mod material to the NETfishing owners. It also does not grant you ownership of the underlying @@ -126,9 +127,9 @@ OTHER LIABILITY ARISING FROM THIS LICENSE OR USE OF THE NETFISHING ASSETS. ## 9. Additional permission -Uses not granted here require written permission from both Alexander Sellite -and Rheannon Eisworth. A permission granted to one person or project does not -amend this license for others. +Uses not granted here require written permission from both NETfishing owners. +A permission granted to one person or project does not amend this license for +others. This is a custom asset license, not an open-source software license or a Creative Commons license. diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index a6dba79..62c7813 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -21,7 +21,8 @@ separate written terms are needed. - 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. +- Record the source and license of newly imported assets in + [`docs/ATTRIBUTION.md`](docs/ATTRIBUTION.md). - Do not assume the GPL code license applies to project assets; observe [`ASSET-LICENSE.md`](ASSET-LICENSE.md) and third-party terms. - Do not commit `.godot/`, test data, logs, captures, or build outputs. @@ -34,7 +35,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. diff --git a/CONTRIBUTOR-TERMS.md b/CONTRIBUTOR-TERMS.md index 9663550..8ef207e 100644 --- a/CONTRIBUTOR-TERMS.md +++ b/CONTRIBUTOR-TERMS.md @@ -4,9 +4,10 @@ These terms apply to contributions intentionally submitted for inclusion in NETfishing after these terms are published. They do not retroactively change ownership of earlier work or replace a separately signed agreement. -“Owners” means Alexander Sellite and Rheannon Eisworth. “Project” means the -official NETfishing game, its servers, tools, documentation, websites, ports, -updates, and promotional materials. +“Owners” means the two NETfishing co-owners who publish the Project as +Woofmeow and are publicly credited as Voyager and Endeavour. “Project” means +the official NETfishing game, its servers, tools, documentation, websites, +ports, updates, and promotional materials. ## 1. Your ownership and authority diff --git a/CREDITS.md b/CREDITS.md deleted file mode 100644 index 95d6d49..0000000 --- a/CREDITS.md +++ /dev/null @@ -1,65 +0,0 @@ -# Credits - -## Project ownership and development - -NETfishing is jointly owned, developed, and published by Alexander Sellite and -Rheannon Eisworth, operating collectively as the independent team Woofmeow. - -- Alexander Sellite: co-owner, developer, project director, and 3D artist. -- Rheannon Eisworth: co-owner, developer, and lead 2D artist. - -## 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: public-domain font by JLH Fonts, bundled as - `ui/fonts/seattle_avenue.otf`; see `ui/fonts/Seattle-Avenue-LICENSE.txt`. - -## Music - -- `audio/music/title/as_in_four_wolves.ogg`: original title/ambient music by - Alexander Sellite. -- `audio/music/world/craft.mp3`: original world music composed by Alexander - Sellite. - -## 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/ -- `sound/dialogue/animalese/placeholder/*.wav`: original animalese tones - composed by Alexander Sellite without third-party samples. - -## Artwork - -- Original 2D artwork: Rheannon Eisworth. -- Original 3D models: Alexander Sellite. - -Asset categories and release-record requirements are listed in -[`docs/ASSET-PROVENANCE.md`](docs/ASSET-PROVENANCE.md). - -Complete license wording, modification notices, and Godot Engine attribution -are recorded in [`THIRD-PARTY-NOTICES.md`](THIRD-PARTY-NOTICES.md). diff --git a/README.md b/README.md index f11f2c7..b3d3e3f 100644 --- a/README.md +++ b/README.md @@ -9,7 +9,7 @@ The project is under active development. Interfaces, balance, content, and save migrations may continue to change before a stable release. NETfishing is developed and published by **Woofmeow**, the independent team of -co-owners Alexander Sellite and Rheannon Eisworth. +co-owners publicly credited as Voyager and Endeavour. ## Requirements @@ -38,8 +38,8 @@ Run the fast, deterministic suite before submitting a change: 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 @@ -68,9 +68,10 @@ 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 @@ -82,11 +83,11 @@ detail. unrelated products. - The NETfishing and Woofmeow names and branding are reserved as described in [TRADEMARKS.md](TRADEMARKS.md). -- Third-party materials retain their own terms; see - [THIRD-PARTY-NOTICES.md](THIRD-PARTY-NOTICES.md). +- Third-party materials retain their own terms; see the consolidated + [credits, notices, and provenance record](docs/ATTRIBUTION.md). - Contributions are accepted under [CONTRIBUTOR-TERMS.md](CONTRIBUTOR-TERMS.md). -The material boundaries and binary-release requirements are summarized in -[`docs/LICENSING.md`](docs/LICENSING.md). +Material boundaries and binary-release requirements are recorded in that same +[`docs/ATTRIBUTION.md`](docs/ATTRIBUTION.md) reference. -Copyright © 2026 Alexander Sellite and Rheannon Eisworth. +Copyright © 2026 Woofmeow. diff --git a/THIRD-PARTY-NOTICES.md b/THIRD-PARTY-NOTICES.md deleted file mode 100644 index 7ea0b6b..0000000 --- a/THIRD-PARTY-NOTICES.md +++ /dev/null @@ -1,135 +0,0 @@ -# NETfishing third-party notices - -NETfishing includes or is distributed with the materials identified below. -They are not relicensed by the NETfishing GPL or Asset License; their own -terms continue to apply. - -## Godot Engine - -NETfishing uses Godot Engine. Godot Engine is licensed 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 Engine and its bundled third-party-component notices are documented at: - -- https://godotengine.org/license/ -- https://github.com/godotengine/godot/blob/master/COPYRIGHT.txt - -Godot uses FreeType for font rendering. Portions of the engine are copyright -The FreeType Project (https://www.freetype.org). All rights reserved under the -FreeType Project’s applicable license. - -## Fonts - -### Tuffy Bold - -- Creators: Thatcher Ulrich, Karoly Barta, and Michael Evans -- Repository file: `ui/fonts/Tuffy_Bold.otf` -- Status: dedicated to the public domain -- Bundled notice: `ui/fonts/Tuffy-LICENSE.txt` - -### Seattle Avenue - -- Creator: JLH Fonts -- Repository file: `ui/fonts/seattle_avenue.otf` -- Status: dedicated to the public domain by the creator -- Source record: - https://www.publicdomainfiles.com/show_file.php?id=13486266913972 -- Additional creator attribution and public-domain listing: - https://www.dafont.com/seattle-avenue.font -- Bundled notice: `ui/fonts/Seattle-Avenue-LICENSE.txt` - -Public-domain status means these fonts are not subject to the NETfishing Asset -License. - -## Sound recordings - -### Spinning Reel - -- Original title: “Spinning reel.wav” -- Creator: Freesound user `tosha73` -- Source: https://freesound.org/s/509902/ -- License: Creative Commons Zero (CC0) -- Used for: `audio/sfx/fishing/fighting.wav` and - `audio/sfx/fishing/reeling.wav` -- Changes: edited into separate looping fight and manual-reeling sounds; - converted/exported as runtime WAV files - -CC0 information: https://creativecommons.org/publicdomain/zero/1.0/ - -### Gentle Ocean Waves Loop - -- Original title: “Gentle Ocean Waves Loop” -- Creator: Freesound user `kkenny101` -- Source: https://freesound.org/s/852826/ -- License: Creative Commons Zero (CC0) -- Used for: `audio/ambience/waves.wav` -- Changes: configured as a quiet looping saltwater ambience - -### Quick Water Droplet - -- Original title: “Quick Water Droplet” -- Creator: Freesound user `qubodup` -- Source: https://freesound.org/s/792931/ -- License: Creative Commons Zero (CC0) -- Used for: `audio/sfx/fishing/bobber.wav` -- Changes: used as the bobber water-impact sound - -### Dog Bark - -- Original title: “Dog_Bark.wav” -- Creator: Freesound user `ivolipa` -- Source: https://freesound.org/s/328729/ -- License: Creative Commons Zero (CC0) -- Used for: `sound/dialogue/calls/bark.wav` -- Changes: edited and exported as a short in-game character-call sound - -### Rain Loop Ontario - -- Original title: “Rain Loop Ontario” -- Creator: Freesound user `Ayton` -- Source: https://freesound.org/s/212799/ -- License: Creative Commons Attribution 3.0 -- License text: https://creativecommons.org/licenses/by/3.0/legalcode -- Used for: `audio/ambience/rain_loop_ontario.ogg` -- Changes: converted from the supplied AIFF source to Ogg Vorbis and configured - as looping weather ambience - -### Cat Meow - -- Original title: “Cat meow.m4a” -- Creator: Freesound user `Christyboy100` -- Source: https://freesound.org/s/495694/ -- License: Creative Commons Attribution 3.0 -- License text: https://creativecommons.org/licenses/by/3.0/legalcode -- Used for: `sound/dialogue/calls/meow.wav` -- Changes: converted to WAV and edited into a short in-game character-call - sound - -The attribution above does not imply that any third-party creator endorses -NETfishing, Woofmeow, Alexander Sellite, or Rheannon Eisworth. - -## Complete provenance records - -Source filenames, transformations, and available SHA-256 records are kept in -`docs/ASSET-PROVENANCE.md`. Absence from that development record does not -remove an applicable third-party license or attribution requirement. diff --git a/TRADEMARKS.md b/TRADEMARKS.md index fe6df35..6848f47 100644 --- a/TRADEMARKS.md +++ b/TRADEMARKS.md @@ -1,9 +1,9 @@ # NETfishing and Woofmeow branding The NETfishing and Woofmeow names, logos, icons, trade dress, and other source -identifiers are reserved by Alexander Sellite and Rheannon Eisworth. Their -presence in a GPL-licensed repository does not grant trademark rights or imply -permission to represent another product as official. +identifiers are reserved by the NETfishing owners, publishing as Woofmeow. +Their presence in a GPL-licensed repository does not grant trademark rights or +imply permission to represent another product as official. No trademark registration is asserted by this notice. Applicable rights may exist through use and other law. @@ -39,4 +39,4 @@ 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 -Alexander Sellite and Rheannon Eisworth. +NETfishing owners. 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/patterns/spots_bengal/body_arms.png b/art/exported/characters/patterns/bengal/body_arms_bengal.png similarity index 100% rename from art/exported/characters/patterns/spots_bengal/body_arms.png rename to art/exported/characters/patterns/bengal/body_arms_bengal.png 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..6b5183d --- /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=true +mipmaps/limit=-1 +roughness/mode=0 +roughness/src_normal="" +process/channel_remap/red=0 +process/channel_remap/green=1 +process/channel_remap/blue=2 +process/channel_remap/alpha=3 +process/fix_alpha_border=true +process/premult_alpha=false +process/normal_map_invert_y=false +process/hdr_as_srgb=false +process/hdr_clamp_exposure=false +process/size_limit=0 +detect_3d/compress_to=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..c2be11a --- /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=true +mipmaps/limit=-1 +roughness/mode=0 +roughness/src_normal="" +process/channel_remap/red=0 +process/channel_remap/green=1 +process/channel_remap/blue=2 +process/channel_remap/alpha=3 +process/fix_alpha_border=true +process/premult_alpha=false +process/normal_map_invert_y=false +process/hdr_as_srgb=false +process/hdr_clamp_exposure=false +process/size_limit=0 +detect_3d/compress_to=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..2a16376 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..0bb47a8 --- /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=true +mipmaps/limit=-1 +roughness/mode=0 +roughness/src_normal="" +process/channel_remap/red=0 +process/channel_remap/green=1 +process/channel_remap/blue=2 +process/channel_remap/alpha=3 +process/fix_alpha_border=true +process/premult_alpha=false +process/normal_map_invert_y=false +process/hdr_as_srgb=false +process/hdr_clamp_exposure=false +process/size_limit=0 +detect_3d/compress_to=1 diff --git a/art/exported/characters/patterns/fox/body_arms.png b/art/exported/characters/patterns/fox/body_arms_fox.png similarity index 100% rename from art/exported/characters/patterns/fox/body_arms.png rename to art/exported/characters/patterns/fox/body_arms_fox.png diff --git a/art/exported/characters/patterns/fox/body_arms.png.import b/art/exported/characters/patterns/fox/body_arms_fox.png.import similarity index 78% rename from art/exported/characters/patterns/fox/body_arms.png.import rename to art/exported/characters/patterns/fox/body_arms_fox.png.import index 1518ce5..51dacfb 100644 --- a/art/exported/characters/patterns/fox/body_arms.png.import +++ b/art/exported/characters/patterns/fox/body_arms_fox.png.import @@ -2,16 +2,16 @@ importer="texture" type="CompressedTexture2D" -uid="uid://bdwqv502so784" -path="res://.godot/imported/body_arms.png-a4119ca84faf9d8bfa9bc0ab6f8ca133.ctex" +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.png" -dest_files=["res://.godot/imported/body_arms.png-a4119ca84faf9d8bfa9bc0ab6f8ca133.ctex"] +source_file="res://art/exported/characters/patterns/fox/body_arms_fox.png" +dest_files=["res://.godot/imported/body_arms_fox.png-91ab21ce544fdc29b8a0ebf3e54ef7cd.ctex"] [params] diff --git a/art/exported/characters/patterns/fox/body_main.png b/art/exported/characters/patterns/fox/body_main_fox.png similarity index 100% rename from art/exported/characters/patterns/fox/body_main.png rename to art/exported/characters/patterns/fox/body_main_fox.png diff --git a/art/exported/characters/patterns/fox/body_main.png.import b/art/exported/characters/patterns/fox/body_main_fox.png.import similarity index 78% rename from art/exported/characters/patterns/fox/body_main.png.import rename to art/exported/characters/patterns/fox/body_main_fox.png.import index ddfb990..939abd0 100644 --- a/art/exported/characters/patterns/fox/body_main.png.import +++ b/art/exported/characters/patterns/fox/body_main_fox.png.import @@ -2,16 +2,16 @@ importer="texture" type="CompressedTexture2D" -uid="uid://ccbaq4d4nt3bm" -path="res://.godot/imported/body_main.png-c83ac58a68a1485819fba3e07de784cd.ctex" +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.png" -dest_files=["res://.godot/imported/body_main.png-c83ac58a68a1485819fba3e07de784cd.ctex"] +source_file="res://art/exported/characters/patterns/fox/body_main_fox.png" +dest_files=["res://.godot/imported/body_main_fox.png-25e4fa03a48053f6b37b3bbadb1f01ba.ctex"] [params] 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..63e8bc0 Binary files /dev/null and b/art/exported/characters/patterns/fox/head_pointy_fox.png differ diff --git a/art/exported/characters/patterns/spots_bengal/body_main.png.import b/art/exported/characters/patterns/fox/head_pointy_fox.png.import similarity index 71% rename from art/exported/characters/patterns/spots_bengal/body_main.png.import rename to art/exported/characters/patterns/fox/head_pointy_fox.png.import index bf26452..c508b05 100644 --- a/art/exported/characters/patterns/spots_bengal/body_main.png.import +++ b/art/exported/characters/patterns/fox/head_pointy_fox.png.import @@ -2,16 +2,16 @@ importer="texture" type="CompressedTexture2D" -uid="uid://cybpx6ua1vaw" -path="res://.godot/imported/body_main.png-eff221076f4e25415158ecf38fdf03da.ctex" +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/spots_bengal/body_main.png" -dest_files=["res://.godot/imported/body_main.png-eff221076f4e25415158ecf38fdf03da.ctex"] +source_file="res://art/exported/characters/patterns/fox/head_pointy_fox.png" +dest_files=["res://.godot/imported/head_pointy_fox.png-ef3535a6c05a49aae36fe5514844a3df.ctex"] [params] 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/spots_bengal/body_arms.png.import b/art/exported/characters/patterns/fox/tails_fox_fox.png.import similarity index 71% rename from art/exported/characters/patterns/spots_bengal/body_arms.png.import rename to art/exported/characters/patterns/fox/tails_fox_fox.png.import index 74164ab..06c67e7 100644 --- a/art/exported/characters/patterns/spots_bengal/body_arms.png.import +++ b/art/exported/characters/patterns/fox/tails_fox_fox.png.import @@ -2,16 +2,16 @@ importer="texture" type="CompressedTexture2D" -uid="uid://m2un0oahhss3" -path="res://.godot/imported/body_arms.png-a55eeb0787397c58508a7c65cc2d04f8.ctex" +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/spots_bengal/body_arms.png" -dest_files=["res://.godot/imported/body_arms.png-a55eeb0787397c58508a7c65cc2d04f8.ctex"] +source_file="res://art/exported/characters/patterns/fox/tails_fox_fox.png" +dest_files=["res://.godot/imported/tails_fox_fox.png-061a224c36e90f10b33d37e616ff7e94.ctex"] [params] diff --git a/art/exported/characters/patterns/spots_bengal/body_main.png b/art/exported/characters/patterns/spots_bengal/body_main.png deleted file mode 100644 index 23eed8e..0000000 Binary files a/art/exported/characters/patterns/spots_bengal/body_main.png and /dev/null differ diff --git a/art/exported/environment/terrain/starter_island.glb b/art/exported/environment/terrain/starter_island.glb index 09210a7..d69e5b5 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/source/characters/base/netfishing_character_source.blend b/art/source/characters/base/netfishing_character_source.blend index 805da6d..01845d6 100644 Binary files a/art/source/characters/base/netfishing_character_source.blend and b/art/source/characters/base/netfishing_character_source.blend differ diff --git a/art/source/environment/terrain/starter_island.blend b/art/source/environment/terrain/starter_island.blend index 14f9394..9b8445c 100644 Binary files a/art/source/environment/terrain/starter_island.blend and b/art/source/environment/terrain/starter_island.blend differ diff --git a/docs/ARCHITECTURE.md b/docs/ARCHITECTURE.md deleted file mode 100644 index 4e18113..0000000 --- a/docs/ARCHITECTURE.md +++ /dev/null @@ -1,88 +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). - -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 - -`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 56d1cf4..0000000 --- a/docs/ASSET-PROVENANCE.md +++ /dev/null @@ -1,161 +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/` | Original 2D artwork by co-owner Rheannon Eisworth. | -| Item icons | `items/icons/` | Original 2D artwork by co-owner Rheannon Eisworth. | -| Inventory notepad | `art/ui/ui_notepad.png` | Original 2D artwork by co-owner Rheannon Eisworth; integrated without a runtime external path. | -| Environment textures | `art/exported/environment/textures/` | Original 2D artwork by co-owner Rheannon Eisworth. | -| UI patterns | `art/patterns/` | Original 2D artwork by co-owner Rheannon Eisworth. | -| Character and world models | `art/exported/` | Original 3D models by co-owner Alexander Sellite. Any embedded original 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` | Public-domain font by JLH Fonts; source record and hash in `ui/fonts/Seattle-Avenue-LICENSE.txt`. | -| Title music | `audio/music/title/as_in_four_wolves.ogg` | Original music composed and owned by co-owner Alexander Sellite. | -| Dusk music | `audio/music/world/craft.mp3` | Original music composed and owned by co-owner Alexander Sellite. | -| Fishing fight loop | `audio/sfx/fishing/fighting.wav` | Edited from “Spinning reel.wav” by Freesound user tosha73, sound 509902, CC0. | -| Manual-reeling loop | `audio/sfx/fishing/reeling.wav` | Edited from the same CC0 “Spinning reel.wav” source. | -| Saltwater wave ambience | `audio/ambience/waves.wav` | “Gentle Ocean Waves Loop” by Freesound user kkenny101, sound 852826, CC0. | -| Bobber water impact | `audio/sfx/fishing/bobber.wav` | “Quick Water Droplet” by Freesound user qubodup, sound 792931, CC0. | -| Rain ambience | `audio/ambience/rain_loop_ontario.ogg` | Converted from “Rain Loop Ontario” by Freesound user Ayton, sound 212799, CC BY 3.0. | -| Character bark call | `sound/dialogue/calls/bark.wav` | Edited from `Dog_Bark.wav` by Freesound user ivolipa, sound 328729, CC0. | -| Character meow call | `sound/dialogue/calls/meow.wav` | Edited from `Cat meow.m4a` by Freesound user Christyboy100, sound 495694, Attribution 3.0. | -| Animalese placeholder tones | `sound/dialogue/animalese/placeholder/` | Original tones composed and owned by co-owner Alexander Sellite without third-party samples. | - -### 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. Preserve `LICENSE`, `ASSET-LICENSE.md`, `TRADEMARKS.md`, credits, and all - required third-party notices with the package. -3. Publish or offer the exact corresponding GPL source for every distributed - binary release. diff --git a/docs/ATTRIBUTION.md b/docs/ATTRIBUTION.md new file mode 100644 index 0000000..c00197c --- /dev/null +++ b/docs/ATTRIBUTION.md @@ -0,0 +1,321 @@ +# Attribution, licensing, and asset provenance + +This is the authoritative project record for ownership, credits, license +boundaries, third-party notices, asset provenance, and redistribution checks. +A Git commit proves when bytes entered the repository; it does not by itself +prove authorship or licensing. + +## Ownership and credits + +NETfishing is jointly owned, developed, and published by its creators, +publicly credited as Voyager and Endeavour and operating collectively as the +independent team Woofmeow. + +- Voyager: co-owner, developer, project director, creator of the game's 3D + art, and composer of the original title/ambient track + `audio/music/title/as_in_four_wolves.ogg`, world track + `audio/music/world/craft.mp3`, and animalese placeholder tones. +- Endeavour: co-owner, developer, and creator of the game's 2D art, including + the original fish, environment, UI, and other artwork. +- chillnfill: contributor of original 3D models for the character bodies and + arms, ears and tails, and multiple world props and decorative assets, + including trees and bridges. The contributor requested to be credited as + `chillnfill`. +- Tekgator: creator of the cooler-capacity and barrier-power shop icons. + +Voyager is also the original catalog porter credit in PortMaster metadata. +Third-party creators are credited with their individual records below. Those +credits do not imply endorsement of NETfishing, Woofmeow, or its owners. + +## License map + +Do not describe the complete repository as covered only by the GPL. + +| Material | Governing terms | +| --- | --- | +| Project-owned software code, shaders, code-oriented scene/resource configuration, tests, build tooling, and server software | GPL-3.0-or-later; source-root `LICENSE` and packaged `GPL-3.0-or-later.txt` | +| Project-owned artwork, textures, models, animation, music, audio, narrative content, characters, and other creative assets | Source-root or packaged `ASSET-LICENSE.md` | +| NETfishing and Woofmeow names, logos, icons, and official identity | Source-root or packaged `TRADEMARKS.md` | +| Third-party engines, fonts, sounds, and other external material | Their original terms, recorded below and in adjacent license texts where applicable | +| New outside contributions | Source-root `CONTRIBUTOR-TERMS.md` plus any separately signed agreement | + +Generated Godot import metadata and deterministic generated resources follow +the terms of their source material or generating code. Repository location +alone does not relicense third-party material. + +The GPL permits study, modification, redistribution, and forks of covered +code. It does not grant rights to NETfishing creative assets or branding. The +NETfishing Asset License separately permits qualifying noncommercial add-on +Mods for official NETfishing software; it does not permit those assets to +accompany code forks, clones, standalone games, unrelated products, or +general-purpose asset packs. A GPL fork must replace NETfishing assets and +reserved branding with independently licensed material and a distinct +identity. + +Project-wide code, asset, branding, or contributor-license changes require +written approval from both NETfishing owners under their private joint +ownership agreement. + +## Godot Engine notice + +NETfishing uses Godot Engine under the MIT License: + +> Copyright (c) 2014-present Godot Engine contributors. +> Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. +> +> Permission is hereby granted, free of charge, to any person obtaining a copy +> of this software and associated documentation files (the “Software”), to deal +> in the Software without restriction, including without limitation the rights +> to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +> copies of the Software, and to permit persons to whom the Software is +> furnished to do so, subject to the following conditions: +> +> The above copyright notice and this permission notice shall be included in +> all copies or substantial portions of the Software. +> +> THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +> IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +> FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +> AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +> LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +> OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +> SOFTWARE. + +Godot licensing and bundled third-party-component notices are available at +https://godotengine.org/license/ and +https://github.com/godotengine/godot/blob/master/COPYRIGHT.txt. Godot uses +FreeType for font rendering; portions are copyright The FreeType Project +(https://www.freetype.org) under its applicable license. + +## Font notices + +- **Tuffy Bold** — Thatcher Ulrich, Karoly Barta, and Michael Evans; dedicated + to the public domain. Runtime file: `ui/fonts/Tuffy_Bold.otf`; original notice: + `ui/fonts/Tuffy-LICENSE.txt`. +- **Seattle Avenue** — JLH Fonts; dedicated to the public domain by the creator. + Runtime file: `ui/fonts/seattle_avenue.otf`; source, hash, and original notice: + `ui/fonts/Seattle-Avenue-LICENSE.txt`. + +Public-domain fonts are not governed by the NETfishing Asset License. + +## Intake policy + +For every new external or supplied asset, record: + +- creator or supplying party; +- original source location or delivery channel; +- source filename and repository destination; +- source and destination SHA-256 when copied without modification; +- license or explicit permission; +- any permitted transformations; +- the importing commit or release. + +Maintainers must also obtain affirmative agreement to the source-root +`CONTRIBUTOR-TERMS.md` before accepting an outside contribution and retain +that agreement in a durable pull request, issue, email, or signed document. + +Runtime resources must use `res://` paths. Workstation Sync folders are intake +locations only and must never appear in scenes or resources. + +## Asset inventory and provenance + +| Asset family | Repository location | Recorded provenance | +| --- | --- | --- | +| Fish art and portraits | `fish/species/`, `art/exported/fish/` | Original 2D artwork by co-owner Endeavour. | +| Item icons | `items/icons/` | Original 2D artwork by co-owner Endeavour except where separately credited below. | +| Cooler-capacity shop icon | `items/icons/equipment/64_cooler_plus.png` | Original artwork by Tekgator. Repository SHA-256: `b63705be5b78915753c068170323781a90e22b555b57a5b2ca9466e2726539f7`. | +| Barrier-power shop icon | `items/icons/shop/64_power_plus.png` | Original artwork by Tekgator. Repository SHA-256: `164d34ce152aa6d75de79b69f0be2d9074f7891057de9636678889028ff98ae7`. | +| Inventory notepad | `art/ui/ui_notepad.png` | Original 2D artwork by co-owner Endeavour; integrated without a runtime external path. | +| Environment textures | `art/exported/environment/textures/` | Original 2D artwork by co-owner Endeavour. | +| UI patterns | `art/patterns/` | Original 2D artwork by co-owner Endeavour. | +| Fur customization channel maps | `art/exported/characters/patterns/` | Original 2D channel-map artwork by co-owner Endeavour. Exact file hashes are recorded below. | +| Character base meshes and appendages | `art/source/characters/base/`, `art/exported/characters/base/`, and derived character resources | Original body and arm models and original ear and tail models by chillnfill. Any embedded original 2D artwork is by Endeavour. | +| World and decorative prop models | `art/source/environment/`, `art/exported/environment/`, and derived world resources | Original prop and decorative models by chillnfill, including trees and bridges. Any embedded original 2D artwork is by Endeavour. | +| Tuffy font | `ui/fonts/Tuffy_Bold.otf` | Public-domain dedication in `ui/fonts/Tuffy-LICENSE.txt`. | +| Seattle Avenue font | `ui/fonts/seattle_avenue.otf` | Public-domain font by JLH Fonts; source record and hash in `ui/fonts/Seattle-Avenue-LICENSE.txt`. | +| Title music | `audio/music/title/as_in_four_wolves.ogg` | Original music composed and owned by co-owner Voyager. | +| Dusk music | `audio/music/world/craft.mp3` | Original music composed and owned by co-owner Voyager. | +| Fishing fight loop | `audio/sfx/fishing/fighting.wav` | Edited from “Spinning reel.wav” by Freesound user tosha73, sound 509902, CC0. | +| Manual-reeling loop | `audio/sfx/fishing/reeling.wav` | Edited from the same CC0 “Spinning reel.wav” source. | +| Saltwater wave ambience | `audio/ambience/waves.wav` | “Gentle Ocean Waves Loop” by Freesound user kkenny101, sound 852826, CC0. | +| Bobber water impact | `audio/sfx/fishing/bobber.wav` | “Quick Water Droplet” by Freesound user qubodup, sound 792931, CC0. | +| Rain ambience | `audio/ambience/rain_loop_ontario.ogg` | Converted from “Rain Loop Ontario” by Freesound user Ayton, sound 212799, CC BY 3.0. | +| Character bark call | `sound/dialogue/calls/bark.wav` | Edited from `Dog_Bark.wav` by Freesound user ivolipa, sound 328729, CC0. | +| Character meow call | `sound/dialogue/calls/meow.wav` | Edited from `Cat meow.m4a` by Freesound user Christyboy100, sound 495694, Attribution 3.0. | +| Animalese placeholder tones | `sound/dialogue/animalese/placeholder/` | Original tones composed and owned by co-owner Voyager without third-party samples. | + +Creative Commons Zero information: +https://creativecommons.org/publicdomain/zero/1.0/. Creative Commons +Attribution 3.0 legal text: +https://creativecommons.org/licenses/by/3.0/legalcode. + +### chillnfill 3D model contribution record + +- Requested credit: `chillnfill`. +- Original contribution families: character bodies and arms; character ears + and tails; and multiple world props and decorative assets, including trees + and bridges. +- Repository mapping: character contributions are incorporated into the + character source/export families identified above. Prop contributions may + be incorporated into combined environment source and export files rather + than retained as one file per originally supplied model. +- Permission record: because these historical contributions predate the + current `CONTRIBUTOR-TERMS.md`, retain the original permission record or a + later written confirmation covering Project use, modification, + distribution, commercial release, and applicable sublicensing. + +### Fur customization channel-map record + +Original channel-map artwork by Endeavour. Repository paths preserve the +authored `mesh_style.png` filenames. + +| Style and mesh | Source filename | Repository file | SHA-256 | +| --- | --- | --- | --- | +| Bengal spots, body and arms | `body_arms_bengal.png` | `art/exported/characters/patterns/bengal/body_arms_bengal.png` | `fa90bd4e74b0b770ce97bf69d069676f5d4bd1f5d649f89e09638a66b61a8470` | +| Bengal spots, main body | `body_main_bengal.png` | `art/exported/characters/patterns/bengal/body_main_bengal.png` | `8662fa198220cfdbe0aa83bdeeef7b855e20124d74f8481467f750d32b67ee8b` | +| Bengal spots, round head | `head_round_bengal.png` | `art/exported/characters/patterns/bengal/head_round_bengal.png` | `7a48c5ac47cfd35ddec1783e6c4defa7807b5ef3f1c6b428157fbc0651e71ef3` | +| Fox, body and arms | `body_arms_fox.png` | `art/exported/characters/patterns/fox/body_arms_fox.png` | `fd2ab31ede371129ca4806480783d57153318a909d6b87c593b6ff82d03d65eb` | +| Fox, main body | `body_main_fox.png` | `art/exported/characters/patterns/fox/body_main_fox.png` | `d2a975cf042728299de4819a023a95e9c1467671a6886f6c6eaf8b44a5a9ca3a` | +| Fox, pointy head | `head_pointy_fox.png` | `art/exported/characters/patterns/fox/head_pointy_fox.png` | `8e74685ef7a9f61b5ae75c16e86898ea80f3996471e2999862479798ce28aa52` | +| Fox tail | `tails_fox_fox.png` | `art/exported/characters/patterns/fox/tails_fox_fox.png` | `72da9673f038e2c3bb8aca19ed99932085fa3b162cab95366901c4e97153dd3e` | + +### Tekgator shop icon contribution record + +- Requested credit: `Tekgator`. +- Original contributions: cooler-capacity and barrier-power shop icons. +- Repository files and SHA-256: + - `items/icons/equipment/64_cooler_plus.png`: + `b63705be5b78915753c068170323781a90e22b555b57a5b2ca9466e2726539f7` + - `items/icons/shop/64_power_plus.png`: + `164d34ce152aa6d75de79b69f0be2d9074f7891057de9636678889028ff98ae7` +- Permission record: retain the original permission record or a later written + confirmation covering Project use, modification, distribution, commercial + release, and applicable sublicensing. + +### Fishing fight loop source record + +- Source page: https://freesound.org/s/509902/ +- Creator: tosha73 +- Source title: `Spinning reel.wav` +- License: Creative Commons Zero (CC0) +- Downloaded source filename: `509902__tosha73__spinning-reel.wav` +- Downloaded source SHA-256: + `9741afdb1ec9c73f5e039f0e5ac174998cb0a1f737b626531e63919d4d232fad` +- Audacity working project: `fighting.aup3` +- Audacity project SHA-256: + `9928f077ecf48d01416f8d05700707e1b07d0f731846db80e27116f5b82dbbf5` +- Runtime edit source/destination SHA-256: + `b6f3f2f94bfaf9254ac04dd96b1fd28c15e1011a1db44bb3e70eeaa95de3c212` +- Runtime format: 48 kHz, 16-bit, stereo PCM WAV; 2.593479 seconds; + configured as a forward loop by the fishing presentation. +- Manual-reeling Audacity project: `reeling.aup3` +- Manual-reeling Audacity project SHA-256: + `20ad52f6bdd16ae6561f06f8355fe309e95023e815053fd5dfbd5a5ce3c1f9f9` +- Manual-reeling runtime source/destination SHA-256: + `e403d5b7c8af1da4b28599cc0f51de3eda70d9e9296db27852fe7dc95617a29d` +- Manual-reeling runtime format: 48 kHz, 16-bit, stereo PCM WAV; + 2.388083 seconds; configured as a forward loop. +- The downloaded source and Audacity projects remain in the project owner's + source-work archive; only the finished runtime edits ship in the game. + +### Saltwater wave ambience source record + +- Source page: https://freesound.org/s/852826/ +- Creator: kkenny101 +- Source title: `Gentle Ocean Waves Loop` +- License: Creative Commons Zero (CC0) +- Downloaded source filename: + `852826__kkenny101__gentle-ocean-waves-loop.wav` +- Downloaded source and runtime source/destination SHA-256: + `f93e9890583f072ac52cf197b8b5942397d3d5e889fbdb9112a0776142f50229` +- Runtime format: 48 kHz, 24-bit, mono PCM WAV; 21.769917 seconds; + configured as a forward loop. +- The downloaded source remains in the project owner's source-work archive; + only the finished runtime loop ships in the game. + +### Bobber water-impact source record + +- Source page: https://freesound.org/s/792931/ +- Creator: qubodup +- Source title: `Quick Water Droplet` +- License: Creative Commons Zero (CC0) +- Downloaded source filename: `792931__qubodup__quick-water-droplet.wav` +- Downloaded source and runtime source/destination SHA-256: + `aef0d16384efed4a7b071c4d80c21597617f882f4e0c1d5295ac194d315eafb7` +- Runtime format: 48 kHz, 16-bit, mono PCM WAV; 0.197146 seconds; one-shot. +- The downloaded source remains in the project owner's source-work archive; + only the finished runtime sound ships in the game. + +### Character-call source records + +#### Bark + +- Source page: https://freesound.org/s/328729/ +- Creator: ivolipa +- Source title: `Dog_Bark.wav` +- License: Creative Commons Zero (CC0) +- Downloaded source filename: `328729__ivolipa__dog_bark.wav` +- Downloaded source SHA-256: + `c3d542dee7bfcc1901428e0c1408c0bd522d778d8e5e1dbca156cab008f3d712` +- Runtime edit source/destination SHA-256: + `5ac2f32f33724a2fc232d725c5c37d4cfe3e93b3c1f19aa4177d2c8e3009ec78` +- Runtime format: 44.1 kHz, 16-bit, stereo PCM WAV; 0.446780 seconds; + one-shot. + +#### Meow + +- Source page: https://freesound.org/s/495694/ +- Creator: Christyboy100 +- Source title: `Cat meow.m4a` +- License: Attribution 3.0 +- Downloaded source filename: `495694__christyboy100__cat-meow.m4a` +- Downloaded source SHA-256: + `afa3f6ef7afa2f7fee1e634d5505d2101cbba4421f8d8ff9adf562939c7a18f8` +- Converted working WAV SHA-256: + `a505e6e230e7d9d76b65c46270d97a35a1acc510b43aa5e7343d7b04f424cade` +- Runtime edit source/destination SHA-256: + `cce22ce1fbbc9325126a153aedc4526fde1aacf78780574fb432197dd44eb6b3` +- Runtime format: 44.1 kHz, 16-bit, stereo PCM WAV; 0.605420 seconds; + one-shot. +- The downloaded sources and working edits remain in the project owner's + source-work archive; only the finished runtime sounds ship in the game. + +### Rain ambience source record + +- Source page: https://freesound.org/s/212799/ +- Creator: Ayton +- Source title: `Rain Loop Ontario` +- License: Creative Commons Attribution 3.0 +- Downloaded source filename: `212799__ayton__rain-loop-ontario.aiff` +- Downloaded source SHA-256: + `b491fc036b78a274d9ac7f6acbd014af1b5d24f5cba9bd7e3fdc05c5432ee66f` +- Runtime edit SHA-256: + `b1979609b20fe34ed6f8902f3de65ae787a09abd71429e8aab850e87b74a4fa2` +- Changes: converted from AIFF to Ogg Vorbis and configured as looping + weather ambience. + +## Generated resources + +Godot `.import` sidecars and deterministic shoreline `.tres` meshes are derived +repository resources, not original artwork. `.godot/imported/`, editor caches, +captures, exports, and temporary test data are not tracked. + +## Release gate + +Before a public binary or source release: + +1. Search exported resources for external filesystem paths. +2. Include or make readily accessible the complete GPL text and identify the + exact corresponding source tag or commit. +3. Include `ASSET-LICENSE.md`, `TRADEMARKS.md`, this attribution/provenance + record, both font notices, and every applicable third-party license. +4. Retain the complete Godot Engine MIT notice above. +5. Preserve third-party terms without adding incompatible restrictions. +6. Avoid referring to a dirty or unavailable source revision. +7. Publish or offer equivalent access to exact corresponding GPL source for + every distributed binary release. + +Desktop and PortMaster build scripts stage these authoritative files as +sidecars. Godot export presets include them in exported resource packs for +platforms where sidecar documents are not normally visible. Store and release +pages should link to the exact source revision and public notices. diff --git a/docs/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..e0635cf --- /dev/null +++ b/docs/DEVELOPMENT.md @@ -0,0 +1,331 @@ +# Development reference + +This is the authoritative reference for NETfishing architecture, engineering +decisions, content policy, authoring, and validation. + +## Architecture + +### Composition + +`main/main.tscn` is the application composition root. It owns long-lived +services, the active world, player container, save/settings managers, and the +pixelated UI viewport. Services are scene-owned rather than global autoloads, +which makes their dependencies visible in the main scene and allows focused +tests to instantiate the same composition. + +The main script coordinates title, session, world, and UI lifecycle. Domain +logic remains in typed resources and services rather than being stored solely +in controls. + +### Domain boundaries + +- `fish/` defines species, availability, catches, pools, quality, and selection. +- `fishing/` owns cast/chase state and resolves authoritative fishing surfaces. +- `inventory/`, `items/`, `economy/`, `progression/`, and `jobs/` own persistent + player-facing game state. +- `world/` owns authored map composition and presentation. Water type is a + shared typed definition used by water bodies, fish habitat, fishing + validation, Logbook classification, and saltwater shoreline presentation. +- `ui/` observes and invokes domain services. It does not define stable item or + fish identity. + +Resource files (`.tres`) are authored data. Stable IDs, not display names or +node names, connect persistent and networked state to that data. + +### Networking and authority + +`NetworkSession` owns ENet session lifecycle and peer registration. Dedicated +network services validate and replicate bounded domains such as fishing, +sales, shops, item use, profiles, jobs, mail, chat, drawings, time, and weather. + +`DiscoveryClient` is an optional directory layer beside `NetworkSession`. An +open host may publish a short-lived room lease, and the shared Join Game page +may browse compatible leases before handing the selected address back to the +existing direct ENet connection flow. The directory does not carry gameplay +traffic or become a gameplay authority. Its base URL comes from +`network/discovery/base_url`, with `NETFISHING_DISCOVERY_URL` available as a +development/deployment override. + +The host is authoritative. Clients submit requests or evidence; the host +derives trusted context from registered peers, authoritative regions, and +server-owned state before mutating inventory, wallet, progression, or shared +world state. Private single-player uses the same host path. Client evidence is +bounded and reconstructed where possible; UI activation is never proof that a +server-side mutation succeeded. + +Moderation follows the same boundary. Player hosts may grant session-scoped +operator status to an authenticated identity. Dedicated servers derive +operators from their configured fingerprint allowlist. Operator status is +replicated for presentation, but kick, ban, unban, and artwork-reset requests +are always reauthorized against the authenticated sender by the host. Only a +player host can grant or revoke operators; operators cannot moderate the host +or another operator. + +Protocol compatibility is defined in `network/network_protocol.gd`. A visible +release version is not a reason to change the protocol number. + +### Persistence and stable identifiers + +`PlayerDataRoot` selects and validates a portable data root. Stores receive +paths from that owner rather than inventing unrelated locations. Progression is +written by `PlayerSaveManager`; device settings and social/identity stores have +separate formats and lifecycles. + +Save migrations are sequential and explicit. Existing catches and ownership +are keyed by stable IDs so authored metadata can evolve without rewriting +historical records. Current source constants—not documentation—are +authoritative for format versions. + +Repository-owned resources are authoritative authored data. Persistent and +networked records refer to stable IDs; display names, filenames, node names, +coordinates, and pool names are not compatibility identifiers. Cross-domain +concepts such as water type use one typed definition. Removing or changing a +stable ID requires an explicit compatibility plan. + +### Presentation + +Gameplay is rendered in 3D with the GL Compatibility renderer. The main UI is +presented through a uniformly scaled SubViewport. Shared UI components and +palette resources prevent page-specific geometry and style drift. + +World presentation systems (time/weather visuals, procedural sky and water, +shoreline ribbons, player blob shadows) do not own gameplay collision or +network authority. Generated shoreline meshes are deterministic presentation +resources baked from explicitly configured static terrain. + +### Engineering consequences + +- Shared mutations must behave consistently for private hosts, open hosts, + dedicated servers, and joined clients. +- Networking changes require local-host and paired host/client validation. +- Protocol payloads and rejection cleanup are explicit and bounded. +- Renaming visible UI does not require a save migration. +- Content validation covers stable-ID uniqueness, catalog completeness, pool + membership, and typed habitat compatibility. +- New authored assets require provenance records as well as resource + references. + +## Content and balance + +Balance is authored data and should change deliberately. Exact current values +remain authoritative in `.tres` resources and typed scripts. + +### Fish + +- Stable fish IDs and catalog numbers must not be changed for display cleanup. +- Species availability may constrain water type, time, weather, bait, and + other authored context. +- Location pools control selection weight; the global catalog remains + comprehensive. +- Freshwater and saltwater habitat is validated by the host in addition to + pool membership. +- Value, rarity, quality, weight ranges, and authored barrier-health bands + should be reviewed together because they affect economy and catch + difficulty. + +### Economy and progression + +- Sales and purchases are host-authoritative and must mutate inventory and + wallet exactly once. +- Reserved assets are rejected atomically; mixed valid and reserved batches + must not partially succeed. +- Item effects, shop prices, cooler capacity, fishing upgrades, jobs, and + experience are separate balance axes. Avoid changing several in an + unrelated presentation pass. + +Record affected stable IDs and resource paths, old and new values, intended +player-facing outcome, interactions with quality/availability/economy, +deterministic validation, and save-compatibility impact with each balance +change. Do not bump a save schema or network protocol merely because an +authored number changed. + +## Authoring + +### World composition + +The active test world uses the canonical starter island through +`world/regions/starter_island_region.tscn`. + +`world/test_world.tscn` owns the gameplay-wide environment, sun, world bounds, +and below-world failsafe. `StarterIslandRegion` owns the placed island content: + +```text +StarterIslandRegion +├── Terrain +│ ├── Visual +│ └── Collision +├── WaterBodies +│ ├── Pond +│ │ ├── VisualWater +│ │ ├── FishingRegion +│ │ └── RecoveryRegion +│ └── Ocean +│ ├── VisualWater +│ ├── FishingRegions +│ └── RecoveryRegions +├── PlayerSpawn +├── SafeRespawns +└── Interactables + ├── FishingShopWorld + └── PelicanCoolerPerch +``` + +Move a meaningful feature root rather than one of its implementation children. +Child transforms are local offsets owned by that feature. + +Every fishable region authors its `water_type` explicitly. The starter pond is +`FRESH_WATER`; the starter coast is `SALT_WATER`. Fish species use the same +central type through an allowed-water-type bitmask. Do not infer habitat from +node names, pool filenames, coordinates, or water height. + +Select `WaterBodies/Pond` to move or resize the pond. Its transform is the +authoritative surface position; surface size and fishing/recovery coverage are +owned together. Select `WaterBodies/Ocean` to move the surrounding water as one +feature. Its explicit visual, fishing, and recovery lobes remain local to that +root. + +Placed-feature ownership: + +- `PlayerSpawn` defines the initial player transform and facing. +- `SafeRespawns` contains authoritative recovery destinations. Recovery + preserves current facing. +- `Interactables/FishingShopWorld` owns the shop visual, collision, interaction + area, prompt, and entrance marker. +- `Interactables/PelicanCoolerPerch` owns the Pelican landmark visual; selling + remains a Cooler action. +- `Terrain` owns imported visuals and generated collision. +- `BelowWorldFailsafe/Coverage` owns below-world recovery coverage. + +The starter-island GLB hierarchy is the terrain-collision authority. The +region rebuilds one concave collision shape from every imported +`MeshInstance3D` when it loads, so newly exported terrain and props participate +without a separate stale collision bake. Keep purely decorative meshes out of +that hierarchy if they should not block players. + +The active `Environment`, `Sun`, and starter-island grass material remain +Inspector-authored and are not recreated by runtime scripts. + +### Reusable world props + +Use one placed `WorldProp` root for a complete landmark: + +```text +WorldProp +├── Visual +└── Collision + └── CollisionShape3D +``` + +Put optional interaction areas, labels, and markers beneath the same root. +Instance imported GLB content under `Visual` and keep gameplay collision under +`Collision`. Author reusable assets at scale `(1, 1, 1)`. Uniform provisional +root scaling is acceptable when visual and collision scale together; avoid +non-uniform root scaling. Make mutable per-instance shapes, meshes, and +materials local to the scene. + +### Shoreline tide ribbons + +Maps using generated tide ribbons own a `ShorelineRibbonBaker` and one +`ShorelineRibbonConfig` per water body. Each configuration selects only its +static terrain source, water height, generation bounds, water-facing reference, +smoothing overrides, and generated `.tres` path. Props, players, bobbers, and +gameplay areas are not scanned unless selected explicitly. Only `SALT_WATER` +bodies generate ribbons. + +In the editor, select the baker and press **Rebuild Shoreline Ribbons**. The +equivalent validation command is: + +```sh +godot --headless --path . --script scripts/bake_shoreline_ribbons.gd +``` + +Use `debug_path_stage` only while comparing extraction stages and leave it Off +in committed scenes. Rebuild when saltwater terrain at the waterline, water +height, or generation bounds change. Normal gameplay loads committed generated +meshes and never runs extraction. + +### Facial-feature textures + +Put new PNGs under the matching directory in +`art/exported/characters/faces/`: + +```text +eyes/.png +noses/.png +mouth/.png +``` + +The category directory is authoritative. A category prefix is optional, so +both `sleepy.png` and `eyes_sleepy.png` produce the stable option ID `sleepy`. +Filenames normalize to lowercase snake_case and are stored in appearance +snapshots, so do not casually rename them. Preserve the established RGBA +transparent canvas. Restart a development build after adding an image; rebuild +exports to package new `res://` files. + +### Bubble menus + +Instance `ui/components/bubble_menu/bubble_button.tscn` for each action, or +attach `bubble_button.gd` to an existing button. Author neutral size, desktop +and compact anchors, font-size limits, and deterministic motion in the +Inspector. A label child may be assigned with `label_control_path`. + +Place buttons under a Control using `bubble_cluster.gd`, pass ordered button +references to `configure()`, and call `apply_layout()` when available size or +responsive layout changes. Order defines keyboard/controller focus neighbors. +The shared profile owns palette, styles, proportional-font ratio, hover +response, and contact tuning. Parent menus own labels, actions, anchors, +availability, and confirmation behavior. + +`motion_scale = 0.0` disables idle drift/deformation while preserving hover and +focus feedback. Contact correction is deterministic and bounded; do not replace +it with physics that destabilizes layout, focus order, or hit targets. + +## Validation + +Tests are executable Godot `SceneTree` scripts. Some are pure content/domain +checks, some instantiate the main scene, and multiplayer checks run paired host +and client processes on loopback. The repository runner provides isolation and +consistent entry points. + +### Consolidated runner + +```sh +scripts/run_validations.sh quick +scripts/run_validations.sh full +scripts/run_validations.sh host +scripts/run_validations.sh network +scripts/run_validations.sh all +scripts/run_validations.sh --list +``` + +- `quick` runs deterministic content and domain checks. +- `full` adds socket-free scene/runtime checks. +- `host` runs single-process authority checks that bind local UDP. +- `network` runs loopback host/client pairs. +- `all` runs `full`, `host`, and `network`. + +Set `GODOT_BIN` to select an executable and `TEST_TIMEOUT_SECONDS` to change +the per-process timeout. The runner isolates and removes XDG roots. + +For one focused test: + +```sh +test_root="$(mktemp -d)" +XDG_DATA_HOME="$test_root/data" \ +XDG_CONFIG_HOME="$test_root/config" \ +godot --headless --path . --script tests/fish_catalog_content_validation.gd +rm -rf -- "$test_root" +``` + +Do not point `NETFISHING_DATA_DIR` at an arbitrary empty directory; it is an +explicit portable-data override and must identify a valid data root. + +Headless tests cannot prove visual alignment, mouse routing, shader appearance, +controller feel, or resize behavior. Presentation changes require graphical +review at the canonical 1280×720 layout and relevant low/high and ultrawide +resolutions. + +Network tests require free loopback UDP ports and local-socket permission. A +release audit additionally requires clean Git/tag verification, editor import, +platform exports, executable smoke tests, archive inspection, and SHA-256 +verification; those checks are intentionally outside the development runner. diff --git a/docs/LICENSING.md b/docs/LICENSING.md deleted file mode 100644 index e10c3df..0000000 --- a/docs/LICENSING.md +++ /dev/null @@ -1,77 +0,0 @@ -# NETfishing licensing and release notices - -NETfishing uses separate licenses for code, creative assets, third-party -material, and branding. Do not describe the complete repository as being -covered only by the GPL. - -## Ownership - -Copyright © 2026 Alexander Sellite and Rheannon Eisworth, operating -collectively as the independent development and publishing team Woofmeow. - -## License map - -| Material | Terms | -| --- | --- | -| Project-owned software code, shaders, scripts, code-oriented scene/resource configuration, tests, build tooling, and server software | GPL-3.0-or-later; full text in `LICENSE` | -| Project-owned artwork, textures, models, animation, music, audio, narrative content, characters, and other creative assets | `ASSET-LICENSE.md` | -| NETfishing and Woofmeow names, logos, icons, and official identity | `TRADEMARKS.md` | -| Third-party engines, fonts, sounds, and other external material | Their original terms, recorded in `THIRD-PARTY-NOTICES.md` and `docs/ASSET-PROVENANCE.md` | -| New outside contributions | `CONTRIBUTOR-TERMS.md` plus any separately signed agreement | - -Generated Godot import metadata and deterministic generated resources follow -the terms of the source material or code from which they are derived. Merely -being stored in this repository does not relicense third-party material. - -## Modding boundary - -The GPL permits community members to study, modify, redistribute, and fork the -covered code under the GPL. It does not grant rights to NETfishing creative -assets or branding. - -The NETfishing Asset License separately permits noncommercial add-on Mods that -require and operate with official NETfishing software. It does not permit the -assets to accompany code forks, clones, standalone games, unrelated products, -or general-purpose asset packs. - -A fork may exercise its GPL code rights by replacing all NETfishing assets and -reserved branding with independently licensed material and a distinct -identity. - -## Contributor intake - -Maintainers must obtain affirmative agreement to `CONTRIBUTOR-TERMS.md` before -accepting a new outside contribution. Record the agreement in a durable pull -request, issue, email, or signed document. - -For each external asset, preserve the creator, source, original filename, -license, transformations, and available hashes in `docs/ASSET-PROVENANCE.md`. - -## Binary-release checklist - -Every public binary package must: - -1. include or make readily accessible the complete GPL text; -2. identify the exact corresponding source tag or commit and provide - equivalent access to that source; -3. include `ASSET-LICENSE.md`, `TRADEMARKS.md`, `CREDITS.md`, and - `THIRD-PARTY-NOTICES.md`; -4. include the public-domain font records and every required third-party - attribution; -5. retain the Godot Engine MIT notice; -6. avoid referring to a dirty or unavailable source revision; and -7. preserve third-party license terms without adding incompatible - restrictions. - -Desktop and PortMaster build scripts stage these notices as sidecar files. -Godot export presets also include the authoritative notice files in exported -resource packs for platforms such as Android and macOS. Store and release -pages should link to the exact source revision and the public notices. A future -in-game legal-notices screen should expose the packed notices directly on -platforms where sidecar documents are not normally visible. - -## Changing terms - -Project-wide code, asset, branding, or contributor-license changes require -written approval from both Alexander Sellite and Rheannon Eisworth under their -joint ownership agreement. 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.md similarity index 78% rename from docs/PORTMASTER-RELEASE.md rename to docs/PORTMASTER.md index 0f84258..2cd6038 100644 --- a/docs/PORTMASTER-RELEASE.md +++ b/docs/PORTMASTER.md @@ -1,4 +1,4 @@ -# PortMaster release builds +# PortMaster reference NETfishing's PortMaster release is a canonical PortMaster package, not a version-named generic ZIP. The installer-facing archive must be named @@ -95,10 +95,10 @@ The remaining catalog metadata assets were copied from the installed official e2a9d132744684c67865c01eca027a8c9946b4c5a19da57c82f1af0373dc83b7 gameinfo.xml ``` -The package’s `licenses/` directory is now assembled from the authoritative -root license, asset-license, credits, trademark, font, and third-party-notice -files at build time. It also records the exact release tag and corresponding -GPL source commit. Do not restore stale duplicated license templates beneath +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`. @@ -139,3 +139,31 @@ following in the installed game: - Both sticks, both bumpers, both triggers, Select, Start, L3, and R3 register. - A single button never cancels auto-map. - Holding both bumpers together for 1.25 seconds cancels auto-map. + +## Low-end performance profile + +The launcher keeps the normal game profile on unknown and stronger hardware. +It enables the low-end profile only when the Linux device tree reports one of +the identifiers used by Allwinner H616/H700 XX handhelds: + +- `allwinner,h616` +- `sun50iw9p1` +- `allwinner,sun50i-h700` + +The low-end profile passes these Godot options: + +- `--single-window` +- `--disable-vsync` +- `--max-fps 30` +- `--audio-output-latency 40` + +It also passes `NETFISHING_LOW_END=1`. The game renders the 3D world at 75% +linear resolution with nearest-neighbor scaling, reducing 3D pixel work by +about 44% while the separately rendered UI retains its canonical resolution. + +Do not use Godot's low processor mode for this profile. It reduces idle CPU +usage by sleeping between updates and is not a game-performance optimization. + +Before adding a device family, record its exact NUL-separated device-tree +`compatible` value from hardware. Do not infer detection from a retail product +name alone. diff --git a/playtest/README-PLAYTEST.txt b/docs/README-PLAYTEST.txt similarity index 100% rename from playtest/README-PLAYTEST.txt rename to docs/README-PLAYTEST.txt diff --git a/docs/README.md b/docs/README.md new file mode 100644 index 0000000..49645b3 --- /dev/null +++ b/docs/README.md @@ -0,0 +1,60 @@ +# NETfishing documentation + +This directory is the single home for maintained project documentation. +Start here instead of searching feature, asset, world, or script directories +for local README files. + +## Authoritative references + +- [`DEVELOPMENT.md`](DEVELOPMENT.md): architecture, engineering decisions, + content and balance policy, authoring guidance, and validation. +- [`ATTRIBUTION.md`](ATTRIBUTION.md): ownership, credits, licensing map, + third-party notices, asset provenance, and redistribution requirements. +- [`PORTMASTER.md`](PORTMASTER.md): PortMaster packaging, installation, + controller mapping, performance behavior, and hardware release checks. +- [`README-PLAYTEST.txt`](README-PLAYTEST.txt): the player-facing README staged + into desktop playtest packages. + +## Related repositories + +- [Discovery service API](https://forge.makearmy.io/woofmeow/netfishing-discovery-server/src/branch/main/docs/API.md) + stays with the service implementation and defines its versioned HTTP + contract. +- [Dedicated-server packaging](https://forge.makearmy.io/woofmeow/netfishing-dedicated-server/src/branch/main/README.md) + stays with its installer and update scripts. It consumes this repository's + attribution and legal notices from the exact pinned game commit instead of + maintaining copies. + +Those code-coupled references remain in their own repositories. Game-wide +architecture, authoring, licensing, credits, and provenance are not duplicated +there. + +Exact runtime values and behavior remain authoritative in source code, scenes, +and resources. These documents describe boundaries, policy, and maintenance +procedures; they should not duplicate changing constants or inventories that +can be derived reliably from the project. + +## Files intentionally kept at the repository root + +The following are entry points or governing legal documents rather than +general project documentation, so they remain at conventional root paths: + +- `README.md` +- `CONTRIBUTING.md` +- `LICENSE` +- `ASSET-LICENSE.md` +- `TRADEMARKS.md` +- `CONTRIBUTOR-TERMS.md` + +Third-party font license texts stay beside their font files under `ui/fonts/` +so the licensed files and their original notices cannot drift apart. Build +scripts collect those legal files and the authoritative attribution record +when assembling releases. + +## Maintenance rule + +Do not add feature-specific README files elsewhere in the repository. Add or +update the appropriate reference above and link to its heading when source +comments need more context. Player-facing package instructions belong only in +`README-PLAYTEST.txt`; source and scripts remain authoritative for command-line +usage. diff --git a/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/export_presets.cfg b/export_presets.cfg index 98b32c3..ff8f39e 100644 --- a/export_presets.cfg +++ b/export_presets.cfg @@ -7,8 +7,8 @@ advanced_options=false dedicated_server=false custom_features="" export_filter="all_resources" -include_filter="LICENSE,ASSET-LICENSE.md,TRADEMARKS.md,THIRD-PARTY-NOTICES.md,CREDITS.md,ui/fonts/*LICENSE.txt" -exclude_filter="builds/*,playtest/*,scripts/*" +include_filter="LICENSE,ASSET-LICENSE.md,TRADEMARKS.md,docs/ATTRIBUTION.md,ui/fonts/*LICENSE.txt" +exclude_filter="builds/*,scripts/*" export_path="builds/v0.6.7-alpha/windows-x86_64/NETfishing.exe" patches=PackedStringArray() encryption_include_filters="" @@ -40,7 +40,7 @@ application/product_version="0.6.7.0" application/company_name="Woofmeow" application/product_name="NETfishing" application/file_description="NETfishing v0.6.7-alpha" -application/copyright="Copyright © 2026 Alexander Sellite and Rheannon Eisworth" +application/copyright="Copyright © 2026 Woofmeow" application/trademarks="NETfishing and Woofmeow branding is reserved; see TRADEMARKS.md" application/export_angle=0 ssh_remote_deploy/enabled=false @@ -60,8 +60,8 @@ advanced_options=false dedicated_server=false custom_features="" export_filter="all_resources" -include_filter="LICENSE,ASSET-LICENSE.md,TRADEMARKS.md,THIRD-PARTY-NOTICES.md,CREDITS.md,ui/fonts/*LICENSE.txt" -exclude_filter="builds/*,playtest/*,scripts/*" +include_filter="LICENSE,ASSET-LICENSE.md,TRADEMARKS.md,docs/ATTRIBUTION.md,ui/fonts/*LICENSE.txt" +exclude_filter="builds/*,scripts/*" export_path="builds/v0.6.7-alpha/linux-arm64/NETfishing.arm64" patches=PackedStringArray() encryption_include_filters="" @@ -90,8 +90,8 @@ advanced_options=false dedicated_server=false custom_features="" export_filter="all_resources" -include_filter="LICENSE,ASSET-LICENSE.md,TRADEMARKS.md,THIRD-PARTY-NOTICES.md,CREDITS.md,ui/fonts/*LICENSE.txt" -exclude_filter="builds/*,playtest/*,scripts/*" +include_filter="LICENSE,ASSET-LICENSE.md,TRADEMARKS.md,docs/ATTRIBUTION.md,ui/fonts/*LICENSE.txt" +exclude_filter="builds/*,scripts/*" export_path="builds/v0.6.7-alpha/macos/NETfishing.zip" patches=PackedStringArray() encryption_include_filters="" @@ -123,8 +123,8 @@ advanced_options=false dedicated_server=true custom_features="" export_filter="all_resources" -include_filter="LICENSE,ASSET-LICENSE.md,TRADEMARKS.md,THIRD-PARTY-NOTICES.md,CREDITS.md,ui/fonts/*LICENSE.txt" -exclude_filter="builds/*,playtest/*,scripts/*,tests/*" +include_filter="LICENSE,ASSET-LICENSE.md,TRADEMARKS.md,docs/ATTRIBUTION.md,ui/fonts/*LICENSE.txt" +exclude_filter="builds/*,scripts/*,tests/*" export_path="builds/v0.6.7-alpha/server-linux-x86_64/NETfishingServer.x86_64" patches=PackedStringArray() encryption_include_filters="" @@ -153,8 +153,8 @@ advanced_options=false dedicated_server=false custom_features="" export_filter="all_resources" -include_filter="LICENSE,ASSET-LICENSE.md,TRADEMARKS.md,THIRD-PARTY-NOTICES.md,CREDITS.md,ui/fonts/*LICENSE.txt" -exclude_filter="builds/*,playtest/*,scripts/*" +include_filter="LICENSE,ASSET-LICENSE.md,TRADEMARKS.md,docs/ATTRIBUTION.md,ui/fonts/*LICENSE.txt" +exclude_filter="builds/*,scripts/*" export_path="builds/v0.6.7-alpha/android/NETfishing.apk" patches=PackedStringArray() encryption_include_filters="" @@ -212,8 +212,8 @@ advanced_options=false dedicated_server=false custom_features="" export_filter="all_resources" -include_filter="LICENSE,ASSET-LICENSE.md,TRADEMARKS.md,THIRD-PARTY-NOTICES.md,CREDITS.md,ui/fonts/*LICENSE.txt" -exclude_filter="builds/*,playtest/*,scripts/*" +include_filter="LICENSE,ASSET-LICENSE.md,TRADEMARKS.md,docs/ATTRIBUTION.md,ui/fonts/*LICENSE.txt" +exclude_filter="builds/*,scripts/*" export_path="builds/v0.6.7-alpha/linux-x86_64/NETfishing.x86_64" patches=PackedStringArray() encryption_include_filters="" diff --git a/icon.svg b/icon.svg deleted file mode 100644 index c6bbb7d..0000000 --- a/icon.svg +++ /dev/null @@ -1 +0,0 @@ - diff --git a/icon.svg.import b/icon.svg.import deleted file mode 100644 index 1b9da13..0000000 --- a/icon.svg.import +++ /dev/null @@ -1,43 +0,0 @@ -[remap] - -importer="texture" -type="CompressedTexture2D" -uid="uid://cxpiqjwgff0xr" -path="res://.godot/imported/icon.svg-218a8f2b3041327d8a5756f3a245f83b.ctex" -metadata={ -"vram_texture": false -} - -[deps] - -source_file="res://icon.svg" -dest_files=["res://.godot/imported/icon.svg-218a8f2b3041327d8a5756f3a245f83b.ctex"] - -[params] - -compress/mode=0 -compress/high_quality=false -compress/lossy_quality=0.7 -compress/uastc_level=0 -compress/rdo_quality_loss=0.0 -compress/hdr_compression=1 -compress/normal_map=0 -compress/channel_pack=0 -mipmaps/generate=false -mipmaps/limit=-1 -roughness/mode=0 -roughness/src_normal="" -process/channel_remap/red=0 -process/channel_remap/green=1 -process/channel_remap/blue=2 -process/channel_remap/alpha=3 -process/fix_alpha_border=true -process/premult_alpha=false -process/normal_map_invert_y=false -process/hdr_as_srgb=false -process/hdr_clamp_exposure=false -process/size_limit=0 -detect_3d/compress_to=1 -svg/scale=1.0 -editor/scale_with_editor_scale=false -editor/convert_colors_with_editor_theme=false diff --git a/progression/character_customization_catalog.gd b/progression/character_customization_catalog.gd index f3ca297..09303cc 100644 --- a/progression/character_customization_catalog.gd +++ b/progression/character_customization_catalog.gd @@ -124,11 +124,15 @@ const OPTIONS: Dictionary = { "textures": { "body_main": ( "res://art/exported/characters/patterns/" - + "spots_bengal/body_main.png" + + "bengal/body_main_bengal.png" ), "body_arms": ( "res://art/exported/characters/patterns/" - + "spots_bengal/body_arms.png" + + "bengal/body_arms_bengal.png" + ), + "head_round": ( + "res://art/exported/characters/patterns/" + + "bengal/head_round_bengal.png" ), }, }, @@ -138,11 +142,19 @@ const OPTIONS: Dictionary = { "textures": { "body_main": ( "res://art/exported/characters/patterns/" - + "fox/body_main.png" + + "fox/body_main_fox.png" ), "body_arms": ( "res://art/exported/characters/patterns/" - + "fox/body_arms.png" + + "fox/body_arms_fox.png" + ), + "head_pointy": ( + "res://art/exported/characters/patterns/" + + "fox/head_pointy_fox.png" + ), + "tails_fox": ( + "res://art/exported/characters/patterns/" + + "fox/tails_fox_fox.png" ), }, }, diff --git a/scripts/build_playtest.sh b/scripts/build_playtest.sh index c7e4193..eaf1106 100755 --- a/scripts/build_playtest.sh +++ b/scripts/build_playtest.sh @@ -7,7 +7,7 @@ readonly PROJECT_ROOT="$(cd -- "${SCRIPT_DIR}/.." && pwd)" readonly BUILD_ROOT="${PROJECT_ROOT}/builds/v0.6.7-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 README_SOURCE="${PROJECT_ROOT}/docs/README-PLAYTEST.txt" readonly SOURCE_COMMIT="$(git -C "${PROJECT_ROOT}" rev-parse HEAD)" readonly SOURCE_URL="https://forge.makearmy.io/woofmeow/netfishing" readonly WINDOWS_ZIP="${BUILD_ROOT}/NETfishing-v0.6.7-alpha-windows-x86_64.zip" @@ -61,12 +61,10 @@ stage_licenses() { "${destination}/GPL-3.0-or-later.txt" install -m 0644 "${PROJECT_ROOT}/ASSET-LICENSE.md" \ "${destination}/NETFISHING-ASSET-LICENSE.md" - install -m 0644 "${PROJECT_ROOT}/THIRD-PARTY-NOTICES.md" \ - "${destination}/THIRD-PARTY-NOTICES.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}/CREDITS.md" \ - "${destination}/CREDITS.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" \ diff --git a/scripts/build_portmaster.sh b/scripts/build_portmaster.sh index be0d74c..198cba5 100755 --- a/scripts/build_portmaster.sh +++ b/scripts/build_portmaster.sh @@ -85,17 +85,14 @@ install -m 0644 "${ARM64_PCK}" "${GAME_ROOT}/NETfishing.pck" install -m 0644 "${TEMPLATE_ROOT}/gameinfo.xml" "${GAME_ROOT}/gameinfo.xml" install -m 0644 "${TEMPLATE_ROOT}/screenshot.png" "${GAME_ROOT}/screenshot.png" install -m 0644 \ - "${PROJECT_ROOT}/CREDITS.md" \ - "${GAME_ROOT}/licenses/CREDITS.md" + "${PROJECT_ROOT}/docs/ATTRIBUTION.md" \ + "${GAME_ROOT}/licenses/ATTRIBUTION.md" install -m 0644 \ "${PROJECT_ROOT}/LICENSE" \ "${GAME_ROOT}/licenses/GPL-3.0-or-later.txt" install -m 0644 \ "${PROJECT_ROOT}/ASSET-LICENSE.md" \ "${GAME_ROOT}/licenses/NETFISHING-ASSET-LICENSE.md" -install -m 0644 \ - "${PROJECT_ROOT}/THIRD-PARTY-NOTICES.md" \ - "${GAME_ROOT}/licenses/THIRD-PARTY-NOTICES.md" install -m 0644 \ "${PROJECT_ROOT}/TRADEMARKS.md" \ "${GAME_ROOT}/licenses/TRADEMARKS.md" diff --git a/scripts/portmaster/port.json b/scripts/portmaster/port.json index d14fa8f..98df160 100644 --- a/scripts/portmaster/port.json +++ b/scripts/portmaster/port.json @@ -9,8 +9,7 @@ "attr": { "title": "NETfishing", "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, diff --git a/scripts/run_validations.sh b/scripts/run_validations.sh index c3b311e..fc34960 100755 --- a/scripts/run_validations.sh +++ b/scripts/run_validations.sh @@ -29,6 +29,7 @@ readonly -a RUNTIME_TESTS=( "tests/inventory_notepad_art_validation.gd" "tests/logbook_runtime_validation.gd" "tests/player_experience_ui_validation.gd" + "tests/title_credits_validation.gd" "tests/ui_scaling_runtime_validation.gd" ) diff --git a/tests/fur_pattern_validation.gd b/tests/fur_pattern_validation.gd index 088dc63..36a4d93 100644 --- a/tests/fur_pattern_validation.gd +++ b/tests/fur_pattern_validation.gd @@ -67,12 +67,26 @@ func _run() -> void: assert(fox_arm_pattern_texture != null) assert(fox_arm_pattern_texture.get_width() == 1024) assert(fox_arm_pattern_texture.get_height() == 1024) - assert( + var bengal_head_texture := ( CharacterCustomizationCatalog.fur_pattern_texture( - "spots_bengal", - "head_round", - ) == null + "spots_bengal", "head_round" + ) ) + assert(bengal_head_texture != null) + assert(bengal_head_texture.get_width() == 1024) + assert(bengal_head_texture.get_height() == 1024) + var fox_head_texture := CharacterCustomizationCatalog.fur_pattern_texture( + "fox", "head_pointy" + ) + assert(fox_head_texture != null) + assert(fox_head_texture.get_width() == 1024) + assert(fox_head_texture.get_height() == 1024) + var fox_tail_texture := CharacterCustomizationCatalog.fur_pattern_texture( + "fox", "tails_fox" + ) + assert(fox_tail_texture != null) + assert(fox_tail_texture.get_width() == 1024) + assert(fox_tail_texture.get_height() == 1024) var patterned := migrated.duplicate(true) patterned["fur_pattern"] = "orange" @@ -117,7 +131,36 @@ func _run() -> void: ) var round_head := skeleton.get_node_or_null("head_round") as MeshInstance3D assert(round_head != null) - assert(round_head.material_override is StandardMaterial3D) + var round_head_material := round_head.material_override as ShaderMaterial + assert(round_head_material != null) + assert( + round_head_material.get_shader_parameter("pattern_texture") + == bengal_head_texture + ) + + var fox_snapshot := patterned.duplicate(true) + fox_snapshot["species"] = "pointy" + fox_snapshot["fur_style"] = "fox" + fox_snapshot["tail"] = "fox" + PlayerVisualPresenter.apply_appearance(visuals, fox_snapshot) + var pointy_head := ( + skeleton.get_node_or_null("head_pointy") as MeshInstance3D + ) + assert(pointy_head != null) + var pointy_head_material := pointy_head.material_override as ShaderMaterial + assert(pointy_head_material != null) + assert( + pointy_head_material.get_shader_parameter("pattern_texture") + == fox_head_texture + ) + var fox_tail := skeleton.get_node_or_null("tails_fox") as MeshInstance3D + assert(fox_tail != null) + var fox_tail_material := fox_tail.material_override as ShaderMaterial + assert(fox_tail_material != null) + assert( + fox_tail_material.get_shader_parameter("pattern_texture") + == fox_tail_texture + ) var recolored := patterned.duplicate(true) recolored["fur_color_3"] = "teal" 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/title_credits_validation.gd b/tests/title_credits_validation.gd new file mode 100644 index 0000000..7e50f89 --- /dev/null +++ b/tests/title_credits_validation.gd @@ -0,0 +1,207 @@ +extends SceneTree + +const TitleScreenScene := preload("res://ui/title_screen.tscn") +const TitleCreditsPageType = preload("res://ui/title_credits_page.gd") + +var _failures: Array[String] = [] + + +func _initialize() -> void: + _run.call_deferred() + + +func _run() -> void: + var original_size: Vector2i = root.size + root.size = Vector2i(1280, 720) + var title_screen := TitleScreenScene.instantiate() as TitleScreen + root.add_child(title_screen) + await process_frame + await process_frame + + var credits_button := title_screen.get_node( + "%CreditsButton" + ) as BubbleButton + var credits_page := title_screen.get_node( + "%CreditsPage" + ) as TitleCreditsPageType + var back_button: Button = credits_page.get_back_button() + var presentation := title_screen.get_node("%Center") as Control + var bubble_field := title_screen.get_node("%BubbleField") as Control + title_screen.call("_update_title_layout") + (title_screen.get_node("%ButtonCenter") as Control).show() + bubble_field.show() + title_screen.call("_set_title_bubbles_interactive", true) + await process_frame + + _expect(credits_button != null, "title menu exposes a credits button") + _expect(credits_page != null, "title screen contains the credits page") + _expect(not credits_page.visible, "credits page starts closed") + _expect( + Rect2(Vector2.ZERO, bubble_field.size).encloses( + Rect2(credits_button.position, credits_button.size) + ), + "credits bubble stays inside the authored title field", + ) + _expect( + credits_button.mouse_filter == Control.MOUSE_FILTER_STOP, + "credits bubble accepts pointer input", + ) + if OS.has_environment("NETFISHING_TITLE_MENU_CAPTURE"): + (title_screen.get_node("%StartPromptCenter") as Control).hide() + title_screen.set_process(true) + await create_timer(0.5).timeout + _expect( + _save_capture( + OS.get_environment("NETFISHING_TITLE_MENU_CAPTURE") + ) == OK, + "title menu validation capture saves successfully", + ) + title_screen.set_process(false) + + title_screen.set("_navigation_focus_active", false) + credits_button.pressed.emit() + await process_frame + _expect(credits_page.visible, "pointer activation opens credits") + _expect(not presentation.visible, "credits replace the primary title menu") + _expect( + root.gui_get_focus_owner() != back_button, + "pointer activation does not pseudo-select back", + ) + await create_timer(0.25).timeout + _expect( + credits_page.mouse_filter == Control.MOUSE_FILTER_PASS, + "credits page becomes pointer-interactive after entry motion", + ) + _expect( + back_button.mouse_filter == Control.MOUSE_FILTER_STOP, + "credits back button accepts pointer clicks", + ) + if OS.has_environment("NETFISHING_CREDITS_CAPTURE"): + await process_frame + _expect( + _save_capture( + OS.get_environment("NETFISHING_CREDITS_CAPTURE") + ) == OK, + "credits validation capture saves successfully", + ) + back_button.pressed.emit() + await process_frame + _expect(not credits_page.visible, "pointer back closes credits") + _expect(presentation.visible, "closing credits restores the title menu") + _expect( + root.gui_get_focus_owner() == null, + "pointer return leaves the title menu unfocused", + ) + + title_screen.set("_navigation_focus_active", true) + credits_button.grab_focus() + credits_button.pressed.emit() + await process_frame + await process_frame + _expect( + root.gui_get_focus_owner() == back_button, + "keyboard or controller activation focuses the credits back button", + ) + + var pointer_motion := InputEventMouseMotion.new() + pointer_motion.position = Vector2(640.0, 360.0) + title_screen._input(pointer_motion) + await process_frame + _expect( + root.gui_get_focus_owner() == null, + "pointer motion clears credits navigation focus", + ) + + var navigate := InputEventAction.new() + navigate.action = &"ui_down" + navigate.pressed = true + title_screen._input(navigate) + await process_frame + _expect( + root.gui_get_focus_owner() == back_button, + "navigation input restores credits focus", + ) + + var cancel := InputEventAction.new() + cancel.action = &"ui_cancel" + cancel.pressed = true + title_screen._input(cancel) + await process_frame + _expect(not credits_page.visible, "ui cancel closes credits") + _expect( + root.gui_get_focus_owner() == credits_button, + "navigation return restores focus to the credits bubble", + ) + + var chillnfill_name := credits_page.get_node( + "Paper/Margin/Layout/Columns/CreativeCredits/ChillnfillName" + ) as Label + var voyager_name := credits_page.get_node( + "Paper/Margin/Layout/Columns/CreativeCredits/VoyagerName" + ) as Label + var endeavour_name := credits_page.get_node( + "Paper/Margin/Layout/Columns/CreativeCredits/EndeavourName" + ) as Label + var chillnfill_credit := credits_page.get_node( + "Paper/Margin/Layout/Columns/CreativeCredits/ChillnfillCredit" + ) as Label + var tekgator_name := credits_page.get_node( + "Paper/Margin/Layout/Columns/CreativeCredits/TekgatorName" + ) as Label + var tekgator_credit := credits_page.get_node( + "Paper/Margin/Layout/Columns/CreativeCredits/TekgatorCredit" + ) as Label + var creative_credits := credits_page.get_node( + "Paper/Margin/Layout/Columns/CreativeCredits" + ) as VBoxContainer + _expect(voyager_name.text == "Voyager", "in-game credits use Voyager") + _expect( + endeavour_name.text == "Endeavour", + "in-game credits use Endeavour", + ) + _expect( + chillnfill_name.text == "chillnfill", + "in-game credits name chillnfill", + ) + _expect( + "trees and bridges" in chillnfill_credit.text.to_lower(), + "in-game credits describe the supplied model families", + ) + _expect( + tekgator_name.text == "Tekgator", + "in-game credits name Tekgator", + ) + _expect( + "shop icon" in tekgator_credit.text.to_lower(), + "in-game credits describe Tekgator's shop icon artwork", + ) + _expect( + Rect2(Vector2.ZERO, creative_credits.size).encloses( + Rect2(tekgator_credit.position, tekgator_credit.size) + ), + "Tekgator's credit stays inside the authored credits column", + ) + + title_screen.queue_free() + root.size = original_size + await process_frame + await process_frame + if _failures.is_empty(): + print("Title credits validation: PASS") + quit(0) + return + for failure: String in _failures: + push_error(failure) + quit(1) + + +func _expect(condition: bool, message: String) -> void: + if not condition: + _failures.append(message) + + +func _save_capture(path: String) -> Error: + var viewport_texture: Texture2D = root.get_texture() + if viewport_texture == null: + return ERR_UNAVAILABLE + return viewport_texture.get_image().save_png(path) diff --git a/tests/title_credits_validation.gd.uid b/tests/title_credits_validation.gd.uid new file mode 100644 index 0000000..747e70c --- /dev/null +++ b/tests/title_credits_validation.gd.uid @@ -0,0 +1 @@ +uid://dimq4ehtlw64n diff --git a/ui/components/bubble_menu/README.md b/ui/components/bubble_menu/README.md deleted file mode 100644 index ca549bb..0000000 --- a/ui/components/bubble_menu/README.md +++ /dev/null @@ -1,26 +0,0 @@ -# Bubble menu authoring - -Instance `bubble_button.tscn` for each action, or attach `bubble_button.gd` -to an existing Button. Set its neutral size, desktop and compact anchors, -font-size limits, and deterministic motion values in the Inspector. A label -child can be assigned through `label_control_path` when wrapped text is needed. -Text size is calculated once per layout from the neutral smaller dimension and -the profile ratio. - -Place the buttons under a Control using `bubble_cluster.gd`. Give the cluster -the ordered BubbleButton references with `configure()`, then call -`apply_layout()` when its available size or responsive layout changes. The -button order defines explicit keyboard and controller focus neighbors. Compact -anchors and minimum sizes remain authored per button. - -The shared profile owns the palette, rounded styles, proportional-font ratio, -hover response, and contact tuning. Labels, actions, sizes, anchors, per-button -motion, responsive wrapping, availability, and confirmation behavior remain -owned by the menu. Connect each Button's `pressed` signal in that parent menu. - -`motion_scale` defaults to `1.0`. Setting it to `0.0` removes idle drift and -deformation while retaining hover and focus feedback. - -Contact is a deterministic, bounded visual correction around authored anchors. -Real physics is intentionally avoided so layouts, focus order, and hit targets -remain stable. diff --git a/ui/title_credits_page.gd b/ui/title_credits_page.gd new file mode 100644 index 0000000..f8841ab --- /dev/null +++ b/ui/title_credits_page.gd @@ -0,0 +1,87 @@ +class_name TitleCreditsPage +extends Control + +signal back_requested(restore_navigation_focus: bool) + +@onready var _paper: PanelContainer = %Paper +@onready var _back_button: Button = %CreditsBackButton + +var _navigation_focus_active: bool = false + + +func _ready() -> void: + UtilityPageStyle.apply_page(self) + _paper.add_theme_stylebox_override( + "panel", UtilityPageStyle.panel_style() + ) + UtilityPageStyle.apply_ocean_button(_back_button) + _back_button.pressed.connect(_request_back) + hide() + + +func open_page(use_navigation_focus: bool) -> void: + _navigation_focus_active = use_navigation_focus + show() + UtilityPageStyle.animate_in(self) + if use_navigation_focus: + call_deferred("_focus_back") + else: + _release_page_focus() + + +func close_page() -> void: + _navigation_focus_active = false + _release_page_focus() + hide() + scale = Vector2.ONE + modulate.a = 1.0 + + +func handle_input(event: InputEvent) -> bool: + if not visible: + return false + if event is InputEventMouseMotion: + _navigation_focus_active = false + _release_page_focus() + return false + if event is InputEventKey and (event as InputEventKey).echo: + return false + var moves_focus: bool = ( + event.is_action_pressed("ui_up") + or event.is_action_pressed("ui_down") + or event.is_action_pressed("ui_left") + or event.is_action_pressed("ui_right") + ) + if moves_focus: + _navigation_focus_active = true + if not _page_has_focus(): + _back_button.grab_focus() + return true + if event.is_action_pressed("ui_cancel"): + _request_back() + return true + return false + + +func get_back_button() -> Button: + return _back_button + + +func _focus_back() -> void: + if visible and _navigation_focus_active: + _back_button.grab_focus() + + +func _request_back() -> void: + back_requested.emit(_navigation_focus_active) + + +func _page_has_focus() -> bool: + var focus_owner: Control = get_viewport().gui_get_focus_owner() + return focus_owner != null and is_ancestor_of(focus_owner) + + +func _release_page_focus() -> void: + var focus_owner: Control = get_viewport().gui_get_focus_owner() + if focus_owner != null and is_ancestor_of(focus_owner): + focus_owner.release_focus() diff --git a/ui/title_credits_page.gd.uid b/ui/title_credits_page.gd.uid new file mode 100644 index 0000000..1cbdf93 --- /dev/null +++ b/ui/title_credits_page.gd.uid @@ -0,0 +1 @@ +uid://c07nehcd5yw6m diff --git a/ui/title_credits_page.tscn b/ui/title_credits_page.tscn new file mode 100644 index 0000000..8c210f3 --- /dev/null +++ b/ui/title_credits_page.tscn @@ -0,0 +1,180 @@ +[gd_scene load_steps=3 format=3] + +[ext_resource type="Script" path="res://ui/title_credits_page.gd" id="1_script"] +[ext_resource type="Theme" path="res://ui/game_theme.tres" id="2_theme"] + +[node name="TitleCreditsPage" type="Control"] +visible = false +layout_mode = 3 +anchors_preset = 15 +anchor_right = 1.0 +anchor_bottom = 1.0 +grow_horizontal = 2 +grow_vertical = 2 +mouse_filter = 1 +theme = ExtResource("2_theme") +script = ExtResource("1_script") + +[node name="Paper" type="PanelContainer" parent="."] +unique_name_in_owner = true +layout_mode = 0 +offset_left = 160.0 +offset_top = 38.0 +offset_right = 1120.0 +offset_bottom = 682.0 + +[node name="Margin" type="MarginContainer" parent="Paper"] +layout_mode = 2 +theme_override_constants/margin_left = 52 +theme_override_constants/margin_top = 26 +theme_override_constants/margin_right = 52 +theme_override_constants/margin_bottom = 24 + +[node name="Layout" type="VBoxContainer" parent="Paper/Margin"] +layout_mode = 2 +theme_override_constants/separation = 10 + +[node name="Title" type="Label" parent="Paper/Margin/Layout"] +layout_mode = 2 +theme_override_colors/font_color = Color(0.902, 0.969, 0.969, 1) +theme_override_font_sizes/font_size = 36 +text = "credits" +horizontal_alignment = 1 + +[node name="Subtitle" type="Label" parent="Paper/Margin/Layout"] +layout_mode = 2 +theme_override_colors/font_color = Color(0.624, 0.812, 0.824, 1) +theme_override_font_sizes/font_size = 18 +text = "NETfishing is created by Woofmeow" +horizontal_alignment = 1 + +[node name="Columns" type="HBoxContainer" parent="Paper/Margin/Layout"] +layout_mode = 2 +size_flags_vertical = 3 +theme_override_constants/separation = 52 + +[node name="CreativeCredits" type="VBoxContainer" parent="Paper/Margin/Layout/Columns"] +custom_minimum_size = Vector2(404, 0) +layout_mode = 2 +size_flags_horizontal = 3 +theme_override_constants/separation = 5 + +[node name="CreativeHeading" type="Label" parent="Paper/Margin/Layout/Columns/CreativeCredits"] +layout_mode = 2 +theme_override_colors/font_color = Color(0.902, 0.969, 0.969, 1) +theme_override_font_sizes/font_size = 23 +text = "creative team" + +[node name="VoyagerName" type="Label" parent="Paper/Margin/Layout/Columns/CreativeCredits"] +layout_mode = 2 +theme_override_colors/font_color = Color(0.902, 0.969, 0.969, 1) +theme_override_font_sizes/font_size = 18 +text = "Voyager" + +[node name="VoyagerRoles" type="Label" parent="Paper/Margin/Layout/Columns/CreativeCredits"] +layout_mode = 2 +theme_override_colors/font_color = Color(0.624, 0.812, 0.824, 1) +theme_override_font_sizes/font_size = 15 +text = "co-owner • development • project direction • 3d art • music" +autowrap_mode = 2 + +[node name="EndeavourName" type="Label" parent="Paper/Margin/Layout/Columns/CreativeCredits"] +layout_mode = 2 +theme_override_colors/font_color = Color(0.902, 0.969, 0.969, 1) +theme_override_font_sizes/font_size = 18 +text = "Endeavour" + +[node name="EndeavourRoles" type="Label" parent="Paper/Margin/Layout/Columns/CreativeCredits"] +layout_mode = 2 +theme_override_colors/font_color = Color(0.624, 0.812, 0.824, 1) +theme_override_font_sizes/font_size = 15 +text = "co-owner • development • 2d art" +autowrap_mode = 2 + +[node name="ContributorSpacer" type="Control" parent="Paper/Margin/Layout/Columns/CreativeCredits"] +custom_minimum_size = Vector2(0, 9) +layout_mode = 2 + +[node name="ContributorHeading" type="Label" parent="Paper/Margin/Layout/Columns/CreativeCredits"] +layout_mode = 2 +theme_override_colors/font_color = Color(0.902, 0.969, 0.969, 1) +theme_override_font_sizes/font_size = 23 +text = "additional contributors" + +[node name="ChillnfillName" type="Label" parent="Paper/Margin/Layout/Columns/CreativeCredits"] +layout_mode = 2 +theme_override_colors/font_color = Color(0.902, 0.969, 0.969, 1) +theme_override_font_sizes/font_size = 18 +text = "chillnfill" + +[node name="ChillnfillCredit" type="Label" parent="Paper/Margin/Layout/Columns/CreativeCredits"] +layout_mode = 2 +theme_override_colors/font_color = Color(0.624, 0.812, 0.824, 1) +theme_override_font_sizes/font_size = 15 +text = "original 3d models for character bodies and arms, ears and tails, and multiple world props and decorative assets including trees and bridges" +autowrap_mode = 2 + +[node name="TekgatorName" type="Label" parent="Paper/Margin/Layout/Columns/CreativeCredits"] +layout_mode = 2 +theme_override_colors/font_color = Color(0.902, 0.969, 0.969, 1) +theme_override_font_sizes/font_size = 18 +text = "Tekgator" + +[node name="TekgatorCredit" type="Label" parent="Paper/Margin/Layout/Columns/CreativeCredits"] +layout_mode = 2 +theme_override_colors/font_color = Color(0.624, 0.812, 0.824, 1) +theme_override_font_sizes/font_size = 15 +text = "cooler-capacity and barrier-power shop icon artwork" +autowrap_mode = 2 + +[node name="AdditionalCredits" type="VBoxContainer" parent="Paper/Margin/Layout/Columns"] +custom_minimum_size = Vector2(404, 0) +layout_mode = 2 +size_flags_horizontal = 3 +theme_override_constants/separation = 5 + +[node name="AudioHeading" type="Label" parent="Paper/Margin/Layout/Columns/AdditionalCredits"] +layout_mode = 2 +theme_override_colors/font_color = Color(0.902, 0.969, 0.969, 1) +theme_override_font_sizes/font_size = 23 +text = "additional audio" + +[node name="AudioCredits" type="Label" parent="Paper/Margin/Layout/Columns/AdditionalCredits"] +layout_mode = 2 +theme_override_colors/font_color = Color(0.624, 0.812, 0.824, 1) +theme_override_font_sizes/font_size = 15 +text = "tosha73 • fishing reel sounds\nkkenny101 • ocean ambience\nqubodup • water impact\nAyton • rain ambience\nivolipa • dog bark\nChristyboy100 • cat meow" + +[node name="TechnologySpacer" type="Control" parent="Paper/Margin/Layout/Columns/AdditionalCredits"] +custom_minimum_size = Vector2(0, 9) +layout_mode = 2 + +[node name="TechnologyHeading" type="Label" parent="Paper/Margin/Layout/Columns/AdditionalCredits"] +layout_mode = 2 +theme_override_colors/font_color = Color(0.902, 0.969, 0.969, 1) +theme_override_font_sizes/font_size = 23 +text = "fonts & technology" + +[node name="TechnologyCredits" type="Label" parent="Paper/Margin/Layout/Columns/AdditionalCredits"] +layout_mode = 2 +theme_override_colors/font_color = Color(0.624, 0.812, 0.824, 1) +theme_override_font_sizes/font_size = 15 +text = "Tuffy • Thatcher Ulrich, Karoly Barta & Michael Evans\nSeattle Avenue • JLH Fonts\nbuilt with Godot Engine" +autowrap_mode = 2 + +[node name="Notice" type="Label" parent="Paper/Margin/Layout"] +layout_mode = 2 +theme_override_colors/font_color = Color(0.624, 0.812, 0.824, 1) +theme_override_font_sizes/font_size = 13 +text = "complete attribution, licensing, and asset provenance are included with every release" +horizontal_alignment = 1 +autowrap_mode = 2 + +[node name="BackCenter" type="CenterContainer" parent="Paper/Margin/Layout"] +layout_mode = 2 + +[node name="CreditsBackButton" type="Button" parent="Paper/Margin/Layout/BackCenter"] +unique_name_in_owner = true +custom_minimum_size = Vector2(112, 50) +layout_mode = 2 +text = "back" diff --git a/ui/title_screen.gd b/ui/title_screen.gd index c95ebb3..739890c 100644 --- a/ui/title_screen.gd +++ b/ui/title_screen.gd @@ -16,6 +16,7 @@ const BubbleClusterType = preload( const TitleConfirmationBubblePageType = preload( "res://ui/title_confirmation_bubble_page.gd" ) +const TitleCreditsPageType = preload("res://ui/title_credits_page.gd") const NetworkSessionType = preload("res://network/network_session.gd") const SavedServerStoreType = preload("res://network/saved_server_store.gd") const JoinGamePageType = preload("res://ui/network/join_game_page.gd") @@ -70,7 +71,7 @@ const LOGO_MAX_WIDTH: float = 662.0 const TITLE_HORIZONTAL_MARGIN: float = 48.0 const TITLE_DESKTOP_REFERENCE_SIZE: Vector2 = Vector2(1280.0, 720.0) const TITLE_COMPACT_REFERENCE_SIZE: Vector2 = Vector2(640.0, 480.0) -const BUBBLE_FIELD_MAX_WIDTH: float = 396.0 +const BUBBLE_FIELD_MAX_WIDTH: float = 440.0 const BUBBLE_FIELD_DESKTOP_HEIGHT: float = 318.0 const BUBBLE_FIELD_COMPACT_WIDTH: float = 294.0 const BUBBLE_FIELD_COMPACT_HEIGHT: float = 200.0 @@ -99,6 +100,7 @@ enum ConfirmationAction { @onready var _continue_button: BubbleButtonType = %ContinueButton @onready var _new_game_button: BubbleButtonType = %NewGameButton @onready var _settings_button: BubbleButtonType = %SettingsButton +@onready var _credits_button: BubbleButtonType = %CreditsButton @onready var _delete_button: BubbleButtonType = %DeleteSaveButton @onready var _quit_button: BubbleButtonType = %QuitButton @onready var _join_game_button: BubbleButtonType = %JoinGameButton @@ -134,6 +136,7 @@ enum ConfirmationAction { @onready var _start_prompt_center: CenterContainer = %StartPromptCenter @onready var _start_prompt_label: Label = %StartPromptLabel @onready var _join_game_page: JoinGamePageType = %JoinGamePage +@onready var _credits_page: TitleCreditsPageType = %CreditsPage var _save_manager: SaveManagerType var _settings_manager: SettingsManagerType @@ -200,6 +203,7 @@ func _ready() -> void: _new_game_button.pressed.connect(_on_new_game_pressed) _join_game_button.pressed.connect(_open_join_game) _settings_button.pressed.connect(_open_settings) + _credits_button.pressed.connect(_open_credits) _delete_button.pressed.connect(_on_delete_pressed) %QuitButton.pressed.connect(_on_quit_pressed) _confirmation_page.confirmed.connect(_on_confirmation_accepted) @@ -211,6 +215,7 @@ func _ready() -> void: _settings_panel.navigation_transition_started.connect( _emit_navigation_bubble_flurry ) + _credits_page.back_requested.connect(_close_credits) _bubble_field.configure(_get_title_buttons()) _decorative_fish_timer.timeout.connect(_on_decorative_fish_timer_timeout) _decorative_bubble_event_timer.timeout.connect( @@ -261,6 +266,7 @@ func reopen() -> void: _reset_confirmation() _settings_panel.hide() _join_game_page.close_page() + _credits_page.close_page() _refresh_save_inspection() show() _start_decorative_presentation() @@ -274,6 +280,7 @@ func open_join_game_page(endpoint: String = "") -> void: _presentation_center.hide() _settings_panel.hide() _confirmation_page.hide_page() + _credits_page.close_page() _join_game_page.open_page(endpoint) @@ -286,7 +293,11 @@ func report_network_error(message: String) -> void: func _open_join_game() -> void: - if _action_in_progress or _is_confirmation_active(): + if ( + _action_in_progress + or _is_confirmation_active() + or _credits_page.visible + ): return open_join_game_page() @@ -299,6 +310,39 @@ func _close_join_game() -> void: _focus_initial_button() +func _open_credits() -> void: + if ( + _action_in_progress + or _is_confirmation_active() + or _settings_panel.visible + or _join_game_page.visible + or _credits_page.visible + ): + return + _hide_continue_stats_context() + var restore_navigation_focus: bool = _navigation_focus_active + _set_title_bubbles_interactive(false) + _release_primary_menu_focus() + _presentation_center.hide() + _start_prompt_center.hide() + _credits_page.open_page(restore_navigation_focus) + + +func _close_credits(restore_navigation_focus: bool) -> void: + _credits_page.close_page() + _presentation_center.show() + _button_center.show() + _start_prompt_center.hide() + _set_title_bubbles_interactive(true) + if restore_navigation_focus: + _navigation_focus_active = true + _credits_button.grab_focus() + else: + _navigation_focus_active = false + _release_title_focus() + _update_continue_stats_visibility() + + func is_awaiting_start_input() -> bool: return _awaiting_start_input @@ -403,6 +447,7 @@ func _get_title_buttons() -> Array[BubbleButton]: _new_game_button, _join_game_button, _settings_button, + _credits_button, _delete_button, _quit_button, ] @@ -498,6 +543,10 @@ func _input(event: InputEvent) -> void: and _settings_panel.is_controller_mapping_capturing() ): return + if _credits_page.visible: + if _credits_page.handle_input(event): + get_viewport().set_input_as_handled() + return if _join_game_page.visible: if event.is_action_pressed("ui_cancel"): _close_join_game() @@ -549,6 +598,7 @@ func _handle_primary_menu_focus_input(event: InputEvent) -> bool: not _button_center.visible or _is_confirmation_active() or _settings_panel.visible + or _credits_page.visible ): return false if event is InputEventMouseMotion: @@ -1186,6 +1236,7 @@ func _open_settings() -> void: _is_confirmation_active() or _action_in_progress or _settings_panel.visible + or _credits_page.visible or _title_settings_transition_active ): return @@ -1398,6 +1449,7 @@ func _focus_initial_button() -> void: or not visible or _awaiting_start_input or not _button_center.visible + or _credits_page.visible ): return if not _continue_button.disabled: @@ -1413,6 +1465,7 @@ func _on_quit_pressed() -> void: not _action_in_progress and not _is_confirmation_active() and not _settings_panel.visible + and not _credits_page.visible ): _hide_continue_stats_context() quit_requested.emit() @@ -1430,6 +1483,7 @@ func _on_title_visibility_changed() -> void: _navigation_focus_active = false _modal_restore_navigation_focus = false _reset_confirmation() + _credits_page.close_page() _stop_decorative_presentation() diff --git a/ui/title_screen.tscn b/ui/title_screen.tscn index 285da68..6fa1760 100644 --- a/ui/title_screen.tscn +++ b/ui/title_screen.tscn @@ -1,4 +1,4 @@ -[gd_scene load_steps=17 format=3] +[gd_scene load_steps=18 format=3] [ext_resource type="Script" path="res://ui/title_screen.gd" id="1_script"] [ext_resource type="Theme" path="res://ui/game_theme.tres" id="2_theme"] @@ -14,6 +14,7 @@ [ext_resource type="Texture2D" path="res://ui/icons/pictograms/online_dark.png" id="12_online_dark"] [ext_resource type="Texture2D" path="res://ui/icons/pictograms/settings_dark.png" id="13_settings_dark"] [ext_resource type="Texture2D" path="res://ui/icons/pictograms/x_dark.png" id="14_x_dark"] +[ext_resource type="PackedScene" path="res://ui/title_credits_page.tscn" id="15_credits_page"] [sub_resource type="ShaderMaterial" id="ShaderMaterial_title_water"] shader = ExtResource("4_water_shader") @@ -224,21 +225,21 @@ layout_mode = 2 [node name="BubbleLayoutSlot" type="Control" parent="ResponsiveTitleStage/TitlePresentationScaleRoot/Center/MainContent/ButtonCenter"] unique_name_in_owner = true -custom_minimum_size = Vector2(396, 318) +custom_minimum_size = Vector2(440, 318) layout_mode = 2 [node name="BubbleMotionRoot" type="Control" parent="ResponsiveTitleStage/TitlePresentationScaleRoot/Center/MainContent/ButtonCenter/BubbleLayoutSlot"] unique_name_in_owner = true layout_mode = 0 -offset_right = 396.0 +offset_right = 440.0 offset_bottom = 318.0 mouse_filter = 2 [node name="BubbleField" type="Control" parent="ResponsiveTitleStage/TitlePresentationScaleRoot/Center/MainContent/ButtonCenter/BubbleLayoutSlot/BubbleMotionRoot"] unique_name_in_owner = true -custom_minimum_size = Vector2(396, 318) +custom_minimum_size = Vector2(440, 318) layout_mode = 0 -offset_right = 396.0 +offset_right = 440.0 offset_bottom = 318.0 script = ExtResource("8_bubble_cluster") profile = ExtResource("9_bubble_profile") @@ -322,6 +323,30 @@ motion_period = 4.9 motion_phase = 2.5 deformation_period = 6.3 +[node name="CreditsButton" type="Button" parent="ResponsiveTitleStage/TitlePresentationScaleRoot/Center/MainContent/ButtonCenter/BubbleLayoutSlot/BubbleMotionRoot/BubbleField"] +unique_name_in_owner = true +offset_left = 330.0 +offset_top = 125.0 +offset_right = 414.0 +offset_bottom = 207.0 +text = "credits" +tooltip_text = "credits" +accessibility_name = "credits" +script = ExtResource("7_bubble_button") +profile = ExtResource("9_bubble_profile") +neutral_size = Vector2(84, 82) +desktop_anchor = Vector2(372, 166) +compact_anchor = Vector2(372, 166) +compact_minimum_size = Vector2(62, 62) +minimum_font_size = 12 +maximum_font_size = 17 +horizontal_amplitude = 1.4 +vertical_amplitude = 3.0 +motion_period = 5.0 +motion_phase = 1.8 +deformation_amplitude = 0.015 +deformation_period = 5.6 + [node name="JoinGameButton" type="Button" parent="ResponsiveTitleStage/TitlePresentationScaleRoot/Center/MainContent/ButtonCenter/BubbleLayoutSlot/BubbleMotionRoot/BubbleField"] unique_name_in_owner = true offset_left = 126.0 @@ -444,6 +469,16 @@ offset_bottom = 300.0 grow_horizontal = 2 grow_vertical = 2 +[node name="CreditsPage" parent="ResponsiveTitleStage/TitlePresentationScaleRoot" instance=ExtResource("15_credits_page")] +unique_name_in_owner = true +z_index = 220 +layout_mode = 1 +anchors_preset = 15 +anchor_right = 1.0 +anchor_bottom = 1.0 +grow_horizontal = 2 +grow_vertical = 2 + [node name="JoinGamePage" parent="ResponsiveTitleStage/TitlePresentationScaleRoot" instance=ExtResource("11_join_page")] unique_name_in_owner = true z_index = 220 diff --git a/world/README-WORLD-AUTHORING.md b/world/README-WORLD-AUTHORING.md deleted file mode 100644 index 60cfdc8..0000000 --- a/world/README-WORLD-AUTHORING.md +++ /dev/null @@ -1,127 +0,0 @@ -# World authoring - -The active test world uses the canonical starter island through -`world/regions/starter_island_region.tscn`. - -## Active composition - -`world/test_world.tscn` owns the gameplay-wide environment, sun, world bounds, -and below-world failsafe. `StarterIslandRegion` owns the placed island content: - -```text -StarterIslandRegion -├── Terrain -│ ├── Visual -│ └── Collision -├── WaterBodies -│ ├── Pond -│ │ ├── VisualWater -│ │ ├── FishingRegion -│ │ └── RecoveryRegion -│ └── Ocean -│ ├── VisualWater -│ ├── FishingRegions -│ └── RecoveryRegions -├── PlayerSpawn -├── SafeRespawns -└── Interactables - ├── FishingShopWorld - └── PelicanCoolerPerch -``` - -Move the meaningful feature root rather than one of its implementation -children. Child transforms are local offsets owned by that feature. - -## Water bodies - -Every fishable region must author its `water_type` explicitly. The starter pond -is `FRESH_WATER`; the starter coast is `SALT_WATER`. Fish species use the same -central type definition through an allowed-water-type bitmask, so pool contents -and authoritative selection are both validated against the region. Do not infer -habitat from node names, pool filenames, coordinates, or water height. - -Select `WaterBodies/Pond` to move or resize the pond. Its transform is the -authoritative surface position. The `surface_size`, fishing padding, recovery -padding, and recovery depth properties update the visible water and gameplay -coverage together. Fishing and recovery derive the surface height from the -owned surface transform. - -Select `WaterBodies/Ocean` to move the surrounding water as one feature. Its -visual, fishing lobes, and recovery lobes use local transforms beneath the -ocean root. The existing multi-lobe footprint remains intentionally explicit; -changing that footprint still requires editing its owned lobe children. - -## Placed features - -- `PlayerSpawn` is the authoritative initial player transform. Its rotation - controls initial facing. -- Each marker under `SafeRespawns` is an authoritative recovery destination. - Recovery preserves the player's current facing; marker rotation is - intentionally not applied. -- `Interactables/FishingShopWorld` owns the shop visual, collision, - interaction area, prompt, and entrance marker. -- `Interactables/PelicanCoolerPerch` owns the complete Pelican landmark visual. - Pelican selling remains a Cooler action and has no world interaction area. -- `Terrain` owns both the imported visual and generated terrain collision. -- `BelowWorldFailsafe/Coverage` exposes the below-world recovery coverage as a - saved collision shape in `world/test_world.tscn`. - -The active `Environment`, `Sun`, and external starter-island grass material -remain directly editable through the Inspector. Their tuning is not recreated -by runtime scripts. - -## Reusable world props - -Use a single placed `WorldProp` root for trees, rocks, buildings, benches, -signs, vegetation, and similar landmarks: - -```text -WorldProp -├── Visual -└── Collision - └── CollisionShape3D -``` - -Add optional interaction areas, labels, and markers beneath the same root. -Instance imported GLB visuals beneath `Visual`; keep gameplay collision -Godot-owned beneath `Collision`. Place and duplicate the complete prop scene, -not its visual child. - -Author reusable assets at scale `(1, 1, 1)`. Uniform root scaling is acceptable -for provisional props when visual and collision scale together. Avoid -non-uniform root scaling because it can deform collision and imported geometry -unpredictably. Make mutable per-instance shapes, meshes, and materials local to -the scene so editing one prop does not change unrelated instances. - -## Shoreline tide ribbons - -Each map that uses generated tide ribbons owns a `ShorelineRibbonBaker` node and -one `ShorelineRibbonConfig` resource per water body. Each fishable water body -declares the shared `WaterType` value used by fishing, Logbook classification, -and shoreline presentation. A configuration designates -only the static terrain `CollisionShape3D` or `MeshInstance3D` to intersect, the -water height, explicit generation bounds, water-facing reference, optional -smoothing overrides, and the generated `.tres` output path. Props, docks, -players, bobbers, and gameplay areas are not scanned unless a map author -explicitly selects one as the terrain source. The baker reports every configured -body but intentionally generates ribbons only for `SALT_WATER`; freshwater uses -the shared depth-tinted shader without tide marks or an advancing wash. - -To rebuild in the editor, select the map's `ShorelineRibbonBaker` node and press -**Rebuild Shoreline Ribbons** in the Inspector. The equivalent validation/CI -command for the starter map is: - -```sh -godot --headless --path . --script scripts/bake_shoreline_ribbons.gd -``` - -Set the baker's `debug_path_stage` to Raw, Simplified, or Smoothed before an -editor rebuild to compare the extraction stages. Keep it Off in the finished -scene; the temporary editor-only line preview is never saved as gameplay data. - -Rebuild a saltwater ribbon only when its terrain at the waterline, configured -water height, or generation bounds change. Freshwater terrain changes do not -require a ribbon rebuild because freshwater bodies have no ribbon. Adding or -moving unrelated props, fishing areas, recovery volumes, or other gameplay -nodes does not require a rebuild. Normal gameplay loads the committed generated -saltwater meshes and never runs the extraction step. diff --git a/world/regions/starter_island_region.gd b/world/regions/starter_island_region.gd index 58a504b..4c8880c 100644 --- a/world/regions/starter_island_region.gd +++ b/world/regions/starter_island_region.gd @@ -32,7 +32,9 @@ var fishing_shop_path: NodePath = ( ^"Interactables/FishingShopWorld/InteractionArea" ) @export_group("Terrain Collision") -@export var rebuild_terrain_collision_on_ready: bool = false +# The imported GLB is the collision authority. Rebuild once per region load so +# newly authored terrain and props cannot retain a stale saved fallback shape. +@export var rebuild_terrain_collision_on_ready: bool = true @export_group("Foliage Wind") @export_range(0.0, 0.4, 0.005) var foliage_wind_strength: float = 0.36 @export_range(0.0, 4.0, 0.05) var foliage_wind_speed: float = 0.9