Consolidate project documentation and attribution
Add public-alias credits and a tested in-game credits page, centralize project guidance and asset provenance, import the complete authored fur channel maps, and keep starter-island collision synchronized with imported props.
|
|
@ -1,19 +1,20 @@
|
||||||
# NETfishing Asset License 1.0
|
# NETfishing Asset License 1.0
|
||||||
|
|
||||||
Copyright © 2026 Alexander Sellite and Rheannon Eisworth. All rights
|
Copyright © 2026 Woofmeow. All rights reserved except for the permissions
|
||||||
reserved except for the permissions expressly granted below.
|
expressly granted below.
|
||||||
|
|
||||||
## 1. Scope
|
## 1. Scope
|
||||||
|
|
||||||
This license applies to original NETfishing artwork, graphics, textures,
|
This license applies to original NETfishing artwork, graphics, textures,
|
||||||
models, animations, music, sound recordings, narrative content, visual
|
models, animations, music, sound recordings, narrative content, visual
|
||||||
designs, characters, and other creative assets owned by Alexander Sellite and
|
designs, characters, and other creative assets owned by the NETfishing owners,
|
||||||
Rheannon Eisworth (the **“NETfishing Assets”**).
|
publishing as Woofmeow (the **“NETfishing Assets”**).
|
||||||
|
|
||||||
It does not apply to:
|
It does not apply to:
|
||||||
|
|
||||||
- software source code identified as GPL-3.0-or-later;
|
- 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
|
- material accompanied by a different license; or
|
||||||
- the NETfishing and Woofmeow names and logos, which are addressed in
|
- the NETfishing and Woofmeow names and logos, which are addressed in
|
||||||
`TRADEMARKS.md`.
|
`TRADEMARKS.md`.
|
||||||
|
|
@ -62,9 +63,9 @@ A distributed Mod must:
|
||||||
2. be presented as unofficial community content;
|
2. be presented as unofficial community content;
|
||||||
3. include the following notice in its documentation or distribution page:
|
3. include the following notice in its documentation or distribution page:
|
||||||
|
|
||||||
> Contains or adapts NETfishing assets © 2026 Alexander Sellite and
|
> Contains or adapts NETfishing assets © 2026 Woofmeow, used under the
|
||||||
> Rheannon Eisworth, used under the NETfishing Asset License 1.0. This Mod
|
> NETfishing Asset License 1.0. This Mod is unofficial and is not endorsed
|
||||||
> is unofficial and is not endorsed by Woofmeow.
|
> by Woofmeow.
|
||||||
|
|
||||||
4. identify material created by the Mod author separately from NETfishing
|
4. identify material created by the Mod author separately from NETfishing
|
||||||
Assets; and
|
Assets; and
|
||||||
|
|
@ -98,9 +99,9 @@ owners grant separate written permission.
|
||||||
|
|
||||||
## 6. Ownership of Mods
|
## 6. Ownership of Mods
|
||||||
|
|
||||||
You retain ownership of your original Mod material. Alexander Sellite and
|
You retain ownership of your original Mod material. The NETfishing owners
|
||||||
Rheannon Eisworth retain ownership of the NETfishing Assets and adaptations of
|
retain ownership of the NETfishing Assets and adaptations of those assets to
|
||||||
those assets to the extent provided by applicable law.
|
the extent provided by applicable law.
|
||||||
|
|
||||||
This license does not require you to assign your original Mod material to the
|
This license does not require you to assign your original Mod material to the
|
||||||
NETfishing owners. It also does not grant you ownership of the underlying
|
NETfishing 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
|
## 9. Additional permission
|
||||||
|
|
||||||
Uses not granted here require written permission from both Alexander Sellite
|
Uses not granted here require written permission from both NETfishing owners.
|
||||||
and Rheannon Eisworth. A permission granted to one person or project does not
|
A permission granted to one person or project does not amend this license for
|
||||||
amend this license for others.
|
others.
|
||||||
|
|
||||||
This is a custom asset license, not an open-source software license or a
|
This is a custom asset license, not an open-source software license or a
|
||||||
Creative Commons license.
|
Creative Commons license.
|
||||||
|
|
|
||||||
|
|
@ -21,7 +21,8 @@ separate written terms are needed.
|
||||||
- Keep presentation-only systems out of save files and network messages.
|
- Keep presentation-only systems out of save files and network messages.
|
||||||
- Store runtime assets under repository-owned paths; never reference a
|
- Store runtime assets under repository-owned paths; never reference a
|
||||||
workstation sync or temporary directory.
|
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
|
- Do not assume the GPL code license applies to project assets; observe
|
||||||
[`ASSET-LICENSE.md`](ASSET-LICENSE.md) and third-party terms.
|
[`ASSET-LICENSE.md`](ASSET-LICENSE.md) and third-party terms.
|
||||||
- Do not commit `.godot/`, test data, logs, captures, or build outputs.
|
- Do not commit `.godot/`, test data, logs, captures, or build outputs.
|
||||||
|
|
@ -34,7 +35,8 @@ scripts/run_validations.sh quick
|
||||||
git status --short
|
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
|
Changes involving networking should run the network suite and a real
|
||||||
two-process check. Visual changes still require graphical review; headless
|
two-process check. Visual changes still require graphical review; headless
|
||||||
tests are supporting evidence, not a replacement.
|
tests are supporting evidence, not a replacement.
|
||||||
|
|
|
||||||
|
|
@ -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
|
NETfishing after these terms are published. They do not retroactively change
|
||||||
ownership of earlier work or replace a separately signed agreement.
|
ownership of earlier work or replace a separately signed agreement.
|
||||||
|
|
||||||
“Owners” means Alexander Sellite and Rheannon Eisworth. “Project” means the
|
“Owners” means the two NETfishing co-owners who publish the Project as
|
||||||
official NETfishing game, its servers, tools, documentation, websites, ports,
|
Woofmeow and are publicly credited as Voyager and Endeavour. “Project” means
|
||||||
updates, and promotional materials.
|
the official NETfishing game, its servers, tools, documentation, websites,
|
||||||
|
ports, updates, and promotional materials.
|
||||||
|
|
||||||
## 1. Your ownership and authority
|
## 1. Your ownership and authority
|
||||||
|
|
||||||
|
|
|
||||||
65
CREDITS.md
|
|
@ -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).
|
|
||||||
23
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.
|
save migrations may continue to change before a stable release.
|
||||||
|
|
||||||
NETfishing is developed and published by **Woofmeow**, the independent team of
|
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
|
## Requirements
|
||||||
|
|
||||||
|
|
@ -38,8 +38,8 @@ Run the fast, deterministic suite before submitting a change:
|
||||||
scripts/run_validations.sh quick
|
scripts/run_validations.sh quick
|
||||||
```
|
```
|
||||||
|
|
||||||
Additional suites and individual commands are documented in
|
Additional suites and individual commands are documented in the
|
||||||
[`docs/TESTING.md`](docs/TESTING.md).
|
[`docs/DEVELOPMENT.md`](docs/DEVELOPMENT.md#validation) validation section.
|
||||||
|
|
||||||
## Building
|
## Building
|
||||||
|
|
||||||
|
|
@ -68,9 +68,10 @@ scripts do not create or publish tags.
|
||||||
- `tests/`: focused Godot validation scripts
|
- `tests/`: focused Godot validation scripts
|
||||||
- `scripts/`: build, bake, and validation entry points
|
- `scripts/`: build, bake, and validation entry points
|
||||||
|
|
||||||
See [`docs/ARCHITECTURE.md`](docs/ARCHITECTURE.md) and
|
All maintained project guidance starts at the
|
||||||
[`world/README-WORLD-AUTHORING.md`](world/README-WORLD-AUTHORING.md) for more
|
[`docs/` documentation index](docs/README.md). Architecture, content policy,
|
||||||
detail.
|
world and UI authoring, and testing are consolidated in
|
||||||
|
[`docs/DEVELOPMENT.md`](docs/DEVELOPMENT.md).
|
||||||
|
|
||||||
## Licensing
|
## Licensing
|
||||||
|
|
||||||
|
|
@ -82,11 +83,11 @@ detail.
|
||||||
unrelated products.
|
unrelated products.
|
||||||
- The NETfishing and Woofmeow names and branding are reserved as described in
|
- The NETfishing and Woofmeow names and branding are reserved as described in
|
||||||
[TRADEMARKS.md](TRADEMARKS.md).
|
[TRADEMARKS.md](TRADEMARKS.md).
|
||||||
- Third-party materials retain their own terms; see
|
- Third-party materials retain their own terms; see the consolidated
|
||||||
[THIRD-PARTY-NOTICES.md](THIRD-PARTY-NOTICES.md).
|
[credits, notices, and provenance record](docs/ATTRIBUTION.md).
|
||||||
- Contributions are accepted under [CONTRIBUTOR-TERMS.md](CONTRIBUTOR-TERMS.md).
|
- Contributions are accepted under [CONTRIBUTOR-TERMS.md](CONTRIBUTOR-TERMS.md).
|
||||||
|
|
||||||
The material boundaries and binary-release requirements are summarized in
|
Material boundaries and binary-release requirements are recorded in that same
|
||||||
[`docs/LICENSING.md`](docs/LICENSING.md).
|
[`docs/ATTRIBUTION.md`](docs/ATTRIBUTION.md) reference.
|
||||||
|
|
||||||
Copyright © 2026 Alexander Sellite and Rheannon Eisworth.
|
Copyright © 2026 Woofmeow.
|
||||||
|
|
|
||||||
|
|
@ -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.
|
|
||||||
|
|
@ -1,9 +1,9 @@
|
||||||
# NETfishing and Woofmeow branding
|
# NETfishing and Woofmeow branding
|
||||||
|
|
||||||
The NETfishing and Woofmeow names, logos, icons, trade dress, and other source
|
The NETfishing and Woofmeow names, logos, icons, trade dress, and other source
|
||||||
identifiers are reserved by Alexander Sellite and Rheannon Eisworth. Their
|
identifiers are reserved by the NETfishing owners, publishing as Woofmeow.
|
||||||
presence in a GPL-licensed repository does not grant trademark rights or imply
|
Their presence in a GPL-licensed repository does not grant trademark rights or
|
||||||
permission to represent another product as official.
|
imply permission to represent another product as official.
|
||||||
|
|
||||||
No trademark registration is asserted by this notice. Applicable rights may
|
No trademark registration is asserted by this notice. Applicable rights may
|
||||||
exist through use and other law.
|
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.
|
code or lawful nominative, descriptive, comparative, or fair use.
|
||||||
|
|
||||||
Permission for broader branding use must be obtained in writing from both
|
Permission for broader branding use must be obtained in writing from both
|
||||||
Alexander Sellite and Rheannon Eisworth.
|
NETfishing owners.
|
||||||
|
|
|
||||||
|
|
@ -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/<id>.png
|
|
||||||
noses/<id>.png
|
|
||||||
mouth/<id>.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.
|
|
||||||
|
Before Width: | Height: | Size: 87 KiB After Width: | Height: | Size: 87 KiB |
|
|
@ -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
|
||||||
BIN
art/exported/characters/patterns/bengal/body_main_bengal.png
Normal file
|
After Width: | Height: | Size: 151 KiB |
|
|
@ -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
|
||||||
BIN
art/exported/characters/patterns/bengal/head_round_bengal.png
Normal file
|
After Width: | Height: | Size: 117 KiB |
|
|
@ -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
|
||||||
|
Before Width: | Height: | Size: 58 KiB After Width: | Height: | Size: 58 KiB |
|
|
@ -2,16 +2,16 @@
|
||||||
|
|
||||||
importer="texture"
|
importer="texture"
|
||||||
type="CompressedTexture2D"
|
type="CompressedTexture2D"
|
||||||
uid="uid://bdwqv502so784"
|
uid="uid://p5cg2bwrxf7v"
|
||||||
path="res://.godot/imported/body_arms.png-a4119ca84faf9d8bfa9bc0ab6f8ca133.ctex"
|
path="res://.godot/imported/body_arms_fox.png-91ab21ce544fdc29b8a0ebf3e54ef7cd.ctex"
|
||||||
metadata={
|
metadata={
|
||||||
"vram_texture": false
|
"vram_texture": false
|
||||||
}
|
}
|
||||||
|
|
||||||
[deps]
|
[deps]
|
||||||
|
|
||||||
source_file="res://art/exported/characters/patterns/fox/body_arms.png"
|
source_file="res://art/exported/characters/patterns/fox/body_arms_fox.png"
|
||||||
dest_files=["res://.godot/imported/body_arms.png-a4119ca84faf9d8bfa9bc0ab6f8ca133.ctex"]
|
dest_files=["res://.godot/imported/body_arms_fox.png-91ab21ce544fdc29b8a0ebf3e54ef7cd.ctex"]
|
||||||
|
|
||||||
[params]
|
[params]
|
||||||
|
|
||||||
|
Before Width: | Height: | Size: 64 KiB After Width: | Height: | Size: 64 KiB |
|
|
@ -2,16 +2,16 @@
|
||||||
|
|
||||||
importer="texture"
|
importer="texture"
|
||||||
type="CompressedTexture2D"
|
type="CompressedTexture2D"
|
||||||
uid="uid://ccbaq4d4nt3bm"
|
uid="uid://wcsadkjjpaei"
|
||||||
path="res://.godot/imported/body_main.png-c83ac58a68a1485819fba3e07de784cd.ctex"
|
path="res://.godot/imported/body_main_fox.png-25e4fa03a48053f6b37b3bbadb1f01ba.ctex"
|
||||||
metadata={
|
metadata={
|
||||||
"vram_texture": false
|
"vram_texture": false
|
||||||
}
|
}
|
||||||
|
|
||||||
[deps]
|
[deps]
|
||||||
|
|
||||||
source_file="res://art/exported/characters/patterns/fox/body_main.png"
|
source_file="res://art/exported/characters/patterns/fox/body_main_fox.png"
|
||||||
dest_files=["res://.godot/imported/body_main.png-c83ac58a68a1485819fba3e07de784cd.ctex"]
|
dest_files=["res://.godot/imported/body_main_fox.png-25e4fa03a48053f6b37b3bbadb1f01ba.ctex"]
|
||||||
|
|
||||||
[params]
|
[params]
|
||||||
|
|
||||||
BIN
art/exported/characters/patterns/fox/head_pointy_fox.png
Normal file
|
After Width: | Height: | Size: 51 KiB |
|
|
@ -2,16 +2,16 @@
|
||||||
|
|
||||||
importer="texture"
|
importer="texture"
|
||||||
type="CompressedTexture2D"
|
type="CompressedTexture2D"
|
||||||
uid="uid://cybpx6ua1vaw"
|
uid="uid://c4vlbrp7qevvo"
|
||||||
path="res://.godot/imported/body_main.png-eff221076f4e25415158ecf38fdf03da.ctex"
|
path="res://.godot/imported/head_pointy_fox.png-ef3535a6c05a49aae36fe5514844a3df.ctex"
|
||||||
metadata={
|
metadata={
|
||||||
"vram_texture": false
|
"vram_texture": false
|
||||||
}
|
}
|
||||||
|
|
||||||
[deps]
|
[deps]
|
||||||
|
|
||||||
source_file="res://art/exported/characters/patterns/spots_bengal/body_main.png"
|
source_file="res://art/exported/characters/patterns/fox/head_pointy_fox.png"
|
||||||
dest_files=["res://.godot/imported/body_main.png-eff221076f4e25415158ecf38fdf03da.ctex"]
|
dest_files=["res://.godot/imported/head_pointy_fox.png-ef3535a6c05a49aae36fe5514844a3df.ctex"]
|
||||||
|
|
||||||
[params]
|
[params]
|
||||||
|
|
||||||
BIN
art/exported/characters/patterns/fox/tails_fox_fox.png
Normal file
|
After Width: | Height: | Size: 34 KiB |
|
|
@ -2,16 +2,16 @@
|
||||||
|
|
||||||
importer="texture"
|
importer="texture"
|
||||||
type="CompressedTexture2D"
|
type="CompressedTexture2D"
|
||||||
uid="uid://m2un0oahhss3"
|
uid="uid://cyr0wcavb1cmi"
|
||||||
path="res://.godot/imported/body_arms.png-a55eeb0787397c58508a7c65cc2d04f8.ctex"
|
path="res://.godot/imported/tails_fox_fox.png-061a224c36e90f10b33d37e616ff7e94.ctex"
|
||||||
metadata={
|
metadata={
|
||||||
"vram_texture": false
|
"vram_texture": false
|
||||||
}
|
}
|
||||||
|
|
||||||
[deps]
|
[deps]
|
||||||
|
|
||||||
source_file="res://art/exported/characters/patterns/spots_bengal/body_arms.png"
|
source_file="res://art/exported/characters/patterns/fox/tails_fox_fox.png"
|
||||||
dest_files=["res://.godot/imported/body_arms.png-a55eeb0787397c58508a7c65cc2d04f8.ctex"]
|
dest_files=["res://.godot/imported/tails_fox_fox.png-061a224c36e90f10b33d37e616ff7e94.ctex"]
|
||||||
|
|
||||||
[params]
|
[params]
|
||||||
|
|
||||||
|
Before Width: | Height: | Size: 174 KiB |
|
|
@ -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).
|
|
||||||
|
|
@ -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.
|
|
||||||
321
docs/ATTRIBUTION.md
Normal file
|
|
@ -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.
|
||||||
|
|
@ -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.
|
|
||||||
331
docs/DEVELOPMENT.md
Normal file
|
|
@ -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/<id>.png
|
||||||
|
noses/<id>.png
|
||||||
|
mouth/<id>.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.
|
||||||
|
|
@ -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.
|
|
||||||
|
|
@ -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.
|
|
||||||
|
|
@ -1,4 +1,4 @@
|
||||||
# PortMaster release builds
|
# PortMaster reference
|
||||||
|
|
||||||
NETfishing's PortMaster release is a canonical PortMaster package, not a
|
NETfishing's PortMaster release is a canonical PortMaster package, not a
|
||||||
version-named generic ZIP. The installer-facing archive must be named
|
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
|
e2a9d132744684c67865c01eca027a8c9946b4c5a19da57c82f1af0373dc83b7 gameinfo.xml
|
||||||
```
|
```
|
||||||
|
|
||||||
The package’s `licenses/` directory is now assembled from the authoritative
|
The package’s `licenses/` directory is assembled from the authoritative root
|
||||||
root license, asset-license, credits, trademark, font, and third-party-notice
|
licenses and trademarks, the consolidated `docs/ATTRIBUTION.md`, and the font
|
||||||
files at build time. It also records the exact release tag and corresponding
|
notices at build time. It also records the exact release tag and corresponding
|
||||||
GPL source commit. Do not restore stale duplicated license templates beneath
|
GPL source commit. Do not restore stale duplicated notice templates beneath
|
||||||
`scripts/portmaster/`.
|
`scripts/portmaster/`.
|
||||||
|
|
||||||
The original catalog porter credit, `Voyager`, remains in `port.json`.
|
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.
|
- Both sticks, both bumpers, both triggers, Select, Start, L3, and R3 register.
|
||||||
- A single button never cancels auto-map.
|
- A single button never cancels auto-map.
|
||||||
- Holding both bumpers together for 1.25 seconds 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.
|
||||||
60
docs/README.md
Normal file
|
|
@ -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.
|
||||||
|
|
@ -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.
|
|
||||||
|
|
@ -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.
|
|
||||||
|
|
@ -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.
|
|
||||||
|
|
@ -7,8 +7,8 @@ advanced_options=false
|
||||||
dedicated_server=false
|
dedicated_server=false
|
||||||
custom_features=""
|
custom_features=""
|
||||||
export_filter="all_resources"
|
export_filter="all_resources"
|
||||||
include_filter="LICENSE,ASSET-LICENSE.md,TRADEMARKS.md,THIRD-PARTY-NOTICES.md,CREDITS.md,ui/fonts/*LICENSE.txt"
|
include_filter="LICENSE,ASSET-LICENSE.md,TRADEMARKS.md,docs/ATTRIBUTION.md,ui/fonts/*LICENSE.txt"
|
||||||
exclude_filter="builds/*,playtest/*,scripts/*"
|
exclude_filter="builds/*,scripts/*"
|
||||||
export_path="builds/v0.6.7-alpha/windows-x86_64/NETfishing.exe"
|
export_path="builds/v0.6.7-alpha/windows-x86_64/NETfishing.exe"
|
||||||
patches=PackedStringArray()
|
patches=PackedStringArray()
|
||||||
encryption_include_filters=""
|
encryption_include_filters=""
|
||||||
|
|
@ -40,7 +40,7 @@ application/product_version="0.6.7.0"
|
||||||
application/company_name="Woofmeow"
|
application/company_name="Woofmeow"
|
||||||
application/product_name="NETfishing"
|
application/product_name="NETfishing"
|
||||||
application/file_description="NETfishing v0.6.7-alpha"
|
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/trademarks="NETfishing and Woofmeow branding is reserved; see TRADEMARKS.md"
|
||||||
application/export_angle=0
|
application/export_angle=0
|
||||||
ssh_remote_deploy/enabled=false
|
ssh_remote_deploy/enabled=false
|
||||||
|
|
@ -60,8 +60,8 @@ advanced_options=false
|
||||||
dedicated_server=false
|
dedicated_server=false
|
||||||
custom_features=""
|
custom_features=""
|
||||||
export_filter="all_resources"
|
export_filter="all_resources"
|
||||||
include_filter="LICENSE,ASSET-LICENSE.md,TRADEMARKS.md,THIRD-PARTY-NOTICES.md,CREDITS.md,ui/fonts/*LICENSE.txt"
|
include_filter="LICENSE,ASSET-LICENSE.md,TRADEMARKS.md,docs/ATTRIBUTION.md,ui/fonts/*LICENSE.txt"
|
||||||
exclude_filter="builds/*,playtest/*,scripts/*"
|
exclude_filter="builds/*,scripts/*"
|
||||||
export_path="builds/v0.6.7-alpha/linux-arm64/NETfishing.arm64"
|
export_path="builds/v0.6.7-alpha/linux-arm64/NETfishing.arm64"
|
||||||
patches=PackedStringArray()
|
patches=PackedStringArray()
|
||||||
encryption_include_filters=""
|
encryption_include_filters=""
|
||||||
|
|
@ -90,8 +90,8 @@ advanced_options=false
|
||||||
dedicated_server=false
|
dedicated_server=false
|
||||||
custom_features=""
|
custom_features=""
|
||||||
export_filter="all_resources"
|
export_filter="all_resources"
|
||||||
include_filter="LICENSE,ASSET-LICENSE.md,TRADEMARKS.md,THIRD-PARTY-NOTICES.md,CREDITS.md,ui/fonts/*LICENSE.txt"
|
include_filter="LICENSE,ASSET-LICENSE.md,TRADEMARKS.md,docs/ATTRIBUTION.md,ui/fonts/*LICENSE.txt"
|
||||||
exclude_filter="builds/*,playtest/*,scripts/*"
|
exclude_filter="builds/*,scripts/*"
|
||||||
export_path="builds/v0.6.7-alpha/macos/NETfishing.zip"
|
export_path="builds/v0.6.7-alpha/macos/NETfishing.zip"
|
||||||
patches=PackedStringArray()
|
patches=PackedStringArray()
|
||||||
encryption_include_filters=""
|
encryption_include_filters=""
|
||||||
|
|
@ -123,8 +123,8 @@ advanced_options=false
|
||||||
dedicated_server=true
|
dedicated_server=true
|
||||||
custom_features=""
|
custom_features=""
|
||||||
export_filter="all_resources"
|
export_filter="all_resources"
|
||||||
include_filter="LICENSE,ASSET-LICENSE.md,TRADEMARKS.md,THIRD-PARTY-NOTICES.md,CREDITS.md,ui/fonts/*LICENSE.txt"
|
include_filter="LICENSE,ASSET-LICENSE.md,TRADEMARKS.md,docs/ATTRIBUTION.md,ui/fonts/*LICENSE.txt"
|
||||||
exclude_filter="builds/*,playtest/*,scripts/*,tests/*"
|
exclude_filter="builds/*,scripts/*,tests/*"
|
||||||
export_path="builds/v0.6.7-alpha/server-linux-x86_64/NETfishingServer.x86_64"
|
export_path="builds/v0.6.7-alpha/server-linux-x86_64/NETfishingServer.x86_64"
|
||||||
patches=PackedStringArray()
|
patches=PackedStringArray()
|
||||||
encryption_include_filters=""
|
encryption_include_filters=""
|
||||||
|
|
@ -153,8 +153,8 @@ advanced_options=false
|
||||||
dedicated_server=false
|
dedicated_server=false
|
||||||
custom_features=""
|
custom_features=""
|
||||||
export_filter="all_resources"
|
export_filter="all_resources"
|
||||||
include_filter="LICENSE,ASSET-LICENSE.md,TRADEMARKS.md,THIRD-PARTY-NOTICES.md,CREDITS.md,ui/fonts/*LICENSE.txt"
|
include_filter="LICENSE,ASSET-LICENSE.md,TRADEMARKS.md,docs/ATTRIBUTION.md,ui/fonts/*LICENSE.txt"
|
||||||
exclude_filter="builds/*,playtest/*,scripts/*"
|
exclude_filter="builds/*,scripts/*"
|
||||||
export_path="builds/v0.6.7-alpha/android/NETfishing.apk"
|
export_path="builds/v0.6.7-alpha/android/NETfishing.apk"
|
||||||
patches=PackedStringArray()
|
patches=PackedStringArray()
|
||||||
encryption_include_filters=""
|
encryption_include_filters=""
|
||||||
|
|
@ -212,8 +212,8 @@ advanced_options=false
|
||||||
dedicated_server=false
|
dedicated_server=false
|
||||||
custom_features=""
|
custom_features=""
|
||||||
export_filter="all_resources"
|
export_filter="all_resources"
|
||||||
include_filter="LICENSE,ASSET-LICENSE.md,TRADEMARKS.md,THIRD-PARTY-NOTICES.md,CREDITS.md,ui/fonts/*LICENSE.txt"
|
include_filter="LICENSE,ASSET-LICENSE.md,TRADEMARKS.md,docs/ATTRIBUTION.md,ui/fonts/*LICENSE.txt"
|
||||||
exclude_filter="builds/*,playtest/*,scripts/*"
|
exclude_filter="builds/*,scripts/*"
|
||||||
export_path="builds/v0.6.7-alpha/linux-x86_64/NETfishing.x86_64"
|
export_path="builds/v0.6.7-alpha/linux-x86_64/NETfishing.x86_64"
|
||||||
patches=PackedStringArray()
|
patches=PackedStringArray()
|
||||||
encryption_include_filters=""
|
encryption_include_filters=""
|
||||||
|
|
|
||||||
1
icon.svg
|
|
@ -1 +0,0 @@
|
||||||
<svg xmlns="http://www.w3.org/2000/svg" width="128" height="128"><rect width="124" height="124" x="2" y="2" fill="#363d52" stroke="#212532" stroke-width="4" rx="14"/><g fill="#fff" transform="translate(12.322 12.322)scale(.101)"><path d="M105 673v33q407 354 814 0v-33z"/><path fill="#478cbf" d="m105 673 152 14q12 1 15 14l4 67 132 10 8-61q2-11 15-15h162q13 4 15 15l8 61 132-10 4-67q3-13 15-14l152-14V427q30-39 56-81-35-59-83-108-43 20-82 47-40-37-88-64 7-51 8-102-59-28-123-42-26 43-46 89-49-7-98 0-20-46-46-89-64 14-123 42 1 51 8 102-48 27-88 64-39-27-82-47-48 49-83 108 26 42 56 81zm0 33v39c0 276 813 276 814 0v-39l-134 12-5 69q-2 10-14 13l-162 11q-12 0-16-11l-10-65H446l-10 65q-4 11-16 11l-162-11q-12-3-14-13l-5-69z"/><path d="M483 600c0 34 58 34 58 0v-86c0-34-58-34-58 0z"/><circle cx="725" cy="526" r="90"/><circle cx="299" cy="526" r="90"/></g><g fill="#414042" transform="translate(12.322 12.322)scale(.101)"><circle cx="307" cy="532" r="60"/><circle cx="717" cy="532" r="60"/></g></svg>
|
|
||||||
|
Before Width: | Height: | Size: 995 B |
|
|
@ -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
|
|
||||||
|
|
@ -124,11 +124,15 @@ const OPTIONS: Dictionary = {
|
||||||
"textures": {
|
"textures": {
|
||||||
"body_main": (
|
"body_main": (
|
||||||
"res://art/exported/characters/patterns/"
|
"res://art/exported/characters/patterns/"
|
||||||
+ "spots_bengal/body_main.png"
|
+ "bengal/body_main_bengal.png"
|
||||||
),
|
),
|
||||||
"body_arms": (
|
"body_arms": (
|
||||||
"res://art/exported/characters/patterns/"
|
"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": {
|
"textures": {
|
||||||
"body_main": (
|
"body_main": (
|
||||||
"res://art/exported/characters/patterns/"
|
"res://art/exported/characters/patterns/"
|
||||||
+ "fox/body_main.png"
|
+ "fox/body_main_fox.png"
|
||||||
),
|
),
|
||||||
"body_arms": (
|
"body_arms": (
|
||||||
"res://art/exported/characters/patterns/"
|
"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"
|
||||||
),
|
),
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
|
|
||||||
|
|
@ -7,7 +7,7 @@ readonly PROJECT_ROOT="$(cd -- "${SCRIPT_DIR}/.." && pwd)"
|
||||||
readonly BUILD_ROOT="${PROJECT_ROOT}/builds/v0.6.7-alpha"
|
readonly BUILD_ROOT="${PROJECT_ROOT}/builds/v0.6.7-alpha"
|
||||||
readonly WINDOWS_DIR="${BUILD_ROOT}/windows-x86_64"
|
readonly WINDOWS_DIR="${BUILD_ROOT}/windows-x86_64"
|
||||||
readonly LINUX_DIR="${BUILD_ROOT}/linux-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_COMMIT="$(git -C "${PROJECT_ROOT}" rev-parse HEAD)"
|
||||||
readonly SOURCE_URL="https://forge.makearmy.io/woofmeow/netfishing"
|
readonly SOURCE_URL="https://forge.makearmy.io/woofmeow/netfishing"
|
||||||
readonly WINDOWS_ZIP="${BUILD_ROOT}/NETfishing-v0.6.7-alpha-windows-x86_64.zip"
|
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"
|
"${destination}/GPL-3.0-or-later.txt"
|
||||||
install -m 0644 "${PROJECT_ROOT}/ASSET-LICENSE.md" \
|
install -m 0644 "${PROJECT_ROOT}/ASSET-LICENSE.md" \
|
||||||
"${destination}/NETFISHING-ASSET-LICENSE.md"
|
"${destination}/NETFISHING-ASSET-LICENSE.md"
|
||||||
install -m 0644 "${PROJECT_ROOT}/THIRD-PARTY-NOTICES.md" \
|
install -m 0644 "${PROJECT_ROOT}/docs/ATTRIBUTION.md" \
|
||||||
"${destination}/THIRD-PARTY-NOTICES.md"
|
"${destination}/ATTRIBUTION.md"
|
||||||
install -m 0644 "${PROJECT_ROOT}/TRADEMARKS.md" \
|
install -m 0644 "${PROJECT_ROOT}/TRADEMARKS.md" \
|
||||||
"${destination}/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" \
|
install -m 0644 "${PROJECT_ROOT}/ui/fonts/Tuffy-LICENSE.txt" \
|
||||||
"${destination}/Tuffy-LICENSE.txt"
|
"${destination}/Tuffy-LICENSE.txt"
|
||||||
install -m 0644 "${PROJECT_ROOT}/ui/fonts/Seattle-Avenue-LICENSE.txt" \
|
install -m 0644 "${PROJECT_ROOT}/ui/fonts/Seattle-Avenue-LICENSE.txt" \
|
||||||
|
|
|
||||||
|
|
@ -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}/gameinfo.xml" "${GAME_ROOT}/gameinfo.xml"
|
||||||
install -m 0644 "${TEMPLATE_ROOT}/screenshot.png" "${GAME_ROOT}/screenshot.png"
|
install -m 0644 "${TEMPLATE_ROOT}/screenshot.png" "${GAME_ROOT}/screenshot.png"
|
||||||
install -m 0644 \
|
install -m 0644 \
|
||||||
"${PROJECT_ROOT}/CREDITS.md" \
|
"${PROJECT_ROOT}/docs/ATTRIBUTION.md" \
|
||||||
"${GAME_ROOT}/licenses/CREDITS.md"
|
"${GAME_ROOT}/licenses/ATTRIBUTION.md"
|
||||||
install -m 0644 \
|
install -m 0644 \
|
||||||
"${PROJECT_ROOT}/LICENSE" \
|
"${PROJECT_ROOT}/LICENSE" \
|
||||||
"${GAME_ROOT}/licenses/GPL-3.0-or-later.txt"
|
"${GAME_ROOT}/licenses/GPL-3.0-or-later.txt"
|
||||||
install -m 0644 \
|
install -m 0644 \
|
||||||
"${PROJECT_ROOT}/ASSET-LICENSE.md" \
|
"${PROJECT_ROOT}/ASSET-LICENSE.md" \
|
||||||
"${GAME_ROOT}/licenses/NETFISHING-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 \
|
install -m 0644 \
|
||||||
"${PROJECT_ROOT}/TRADEMARKS.md" \
|
"${PROJECT_ROOT}/TRADEMARKS.md" \
|
||||||
"${GAME_ROOT}/licenses/TRADEMARKS.md"
|
"${GAME_ROOT}/licenses/TRADEMARKS.md"
|
||||||
|
|
|
||||||
|
|
@ -9,8 +9,7 @@
|
||||||
"attr": {
|
"attr": {
|
||||||
"title": "NETfishing",
|
"title": "NETfishing",
|
||||||
"porter": [
|
"porter": [
|
||||||
"Voyager",
|
"Voyager"
|
||||||
"asellite"
|
|
||||||
],
|
],
|
||||||
"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": "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,
|
"desc_md": null,
|
||||||
|
|
|
||||||
|
|
@ -29,6 +29,7 @@ readonly -a RUNTIME_TESTS=(
|
||||||
"tests/inventory_notepad_art_validation.gd"
|
"tests/inventory_notepad_art_validation.gd"
|
||||||
"tests/logbook_runtime_validation.gd"
|
"tests/logbook_runtime_validation.gd"
|
||||||
"tests/player_experience_ui_validation.gd"
|
"tests/player_experience_ui_validation.gd"
|
||||||
|
"tests/title_credits_validation.gd"
|
||||||
"tests/ui_scaling_runtime_validation.gd"
|
"tests/ui_scaling_runtime_validation.gd"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -67,12 +67,26 @@ func _run() -> void:
|
||||||
assert(fox_arm_pattern_texture != null)
|
assert(fox_arm_pattern_texture != null)
|
||||||
assert(fox_arm_pattern_texture.get_width() == 1024)
|
assert(fox_arm_pattern_texture.get_width() == 1024)
|
||||||
assert(fox_arm_pattern_texture.get_height() == 1024)
|
assert(fox_arm_pattern_texture.get_height() == 1024)
|
||||||
assert(
|
var bengal_head_texture := (
|
||||||
CharacterCustomizationCatalog.fur_pattern_texture(
|
CharacterCustomizationCatalog.fur_pattern_texture(
|
||||||
"spots_bengal",
|
"spots_bengal", "head_round"
|
||||||
"head_round",
|
)
|
||||||
) == null
|
|
||||||
)
|
)
|
||||||
|
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)
|
var patterned := migrated.duplicate(true)
|
||||||
patterned["fur_pattern"] = "orange"
|
patterned["fur_pattern"] = "orange"
|
||||||
|
|
@ -117,7 +131,36 @@ func _run() -> void:
|
||||||
)
|
)
|
||||||
var round_head := skeleton.get_node_or_null("head_round") as MeshInstance3D
|
var round_head := skeleton.get_node_or_null("head_round") as MeshInstance3D
|
||||||
assert(round_head != null)
|
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)
|
var recolored := patterned.duplicate(true)
|
||||||
recolored["fur_color_3"] = "teal"
|
recolored["fur_color_3"] = "teal"
|
||||||
|
|
|
||||||
|
|
@ -49,6 +49,7 @@ func _run() -> void:
|
||||||
runtime_terrain.get_surface_override_material(surface_index) == null,
|
runtime_terrain.get_surface_override_material(surface_index) == null,
|
||||||
"Region surface %d must use its Blender material." % surface_index,
|
"Region surface %d must use its Blender material." % surface_index,
|
||||||
)
|
)
|
||||||
|
_validate_region_collision(region)
|
||||||
region.free()
|
region.free()
|
||||||
_finish()
|
_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:
|
func _check(condition: bool, message: String) -> void:
|
||||||
if not condition:
|
if not condition:
|
||||||
failures.append(message)
|
failures.append(message)
|
||||||
|
|
@ -125,9 +182,9 @@ func _check(condition: bool, message: String) -> void:
|
||||||
|
|
||||||
func _finish() -> void:
|
func _finish() -> void:
|
||||||
if failures.is_empty():
|
if failures.is_empty():
|
||||||
print("Blender terrain material validation: PASS")
|
print("Blender terrain and collision validation: PASS")
|
||||||
quit(0)
|
quit(0)
|
||||||
return
|
return
|
||||||
for failure: String in failures:
|
for failure: String in failures:
|
||||||
printerr("Blender terrain material validation: ", failure)
|
printerr("Blender terrain and collision validation: ", failure)
|
||||||
quit(1)
|
quit(1)
|
||||||
|
|
|
||||||
207
tests/title_credits_validation.gd
Normal file
|
|
@ -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)
|
||||||
1
tests/title_credits_validation.gd.uid
Normal file
|
|
@ -0,0 +1 @@
|
||||||
|
uid://dimq4ehtlw64n
|
||||||
|
|
@ -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.
|
|
||||||
87
ui/title_credits_page.gd
Normal file
|
|
@ -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()
|
||||||
1
ui/title_credits_page.gd.uid
Normal file
|
|
@ -0,0 +1 @@
|
||||||
|
uid://c07nehcd5yw6m
|
||||||
180
ui/title_credits_page.tscn
Normal file
|
|
@ -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"
|
||||||
|
|
@ -16,6 +16,7 @@ const BubbleClusterType = preload(
|
||||||
const TitleConfirmationBubblePageType = preload(
|
const TitleConfirmationBubblePageType = preload(
|
||||||
"res://ui/title_confirmation_bubble_page.gd"
|
"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 NetworkSessionType = preload("res://network/network_session.gd")
|
||||||
const SavedServerStoreType = preload("res://network/saved_server_store.gd")
|
const SavedServerStoreType = preload("res://network/saved_server_store.gd")
|
||||||
const JoinGamePageType = preload("res://ui/network/join_game_page.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_HORIZONTAL_MARGIN: float = 48.0
|
||||||
const TITLE_DESKTOP_REFERENCE_SIZE: Vector2 = Vector2(1280.0, 720.0)
|
const TITLE_DESKTOP_REFERENCE_SIZE: Vector2 = Vector2(1280.0, 720.0)
|
||||||
const TITLE_COMPACT_REFERENCE_SIZE: Vector2 = Vector2(640.0, 480.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_DESKTOP_HEIGHT: float = 318.0
|
||||||
const BUBBLE_FIELD_COMPACT_WIDTH: float = 294.0
|
const BUBBLE_FIELD_COMPACT_WIDTH: float = 294.0
|
||||||
const BUBBLE_FIELD_COMPACT_HEIGHT: float = 200.0
|
const BUBBLE_FIELD_COMPACT_HEIGHT: float = 200.0
|
||||||
|
|
@ -99,6 +100,7 @@ enum ConfirmationAction {
|
||||||
@onready var _continue_button: BubbleButtonType = %ContinueButton
|
@onready var _continue_button: BubbleButtonType = %ContinueButton
|
||||||
@onready var _new_game_button: BubbleButtonType = %NewGameButton
|
@onready var _new_game_button: BubbleButtonType = %NewGameButton
|
||||||
@onready var _settings_button: BubbleButtonType = %SettingsButton
|
@onready var _settings_button: BubbleButtonType = %SettingsButton
|
||||||
|
@onready var _credits_button: BubbleButtonType = %CreditsButton
|
||||||
@onready var _delete_button: BubbleButtonType = %DeleteSaveButton
|
@onready var _delete_button: BubbleButtonType = %DeleteSaveButton
|
||||||
@onready var _quit_button: BubbleButtonType = %QuitButton
|
@onready var _quit_button: BubbleButtonType = %QuitButton
|
||||||
@onready var _join_game_button: BubbleButtonType = %JoinGameButton
|
@onready var _join_game_button: BubbleButtonType = %JoinGameButton
|
||||||
|
|
@ -134,6 +136,7 @@ enum ConfirmationAction {
|
||||||
@onready var _start_prompt_center: CenterContainer = %StartPromptCenter
|
@onready var _start_prompt_center: CenterContainer = %StartPromptCenter
|
||||||
@onready var _start_prompt_label: Label = %StartPromptLabel
|
@onready var _start_prompt_label: Label = %StartPromptLabel
|
||||||
@onready var _join_game_page: JoinGamePageType = %JoinGamePage
|
@onready var _join_game_page: JoinGamePageType = %JoinGamePage
|
||||||
|
@onready var _credits_page: TitleCreditsPageType = %CreditsPage
|
||||||
|
|
||||||
var _save_manager: SaveManagerType
|
var _save_manager: SaveManagerType
|
||||||
var _settings_manager: SettingsManagerType
|
var _settings_manager: SettingsManagerType
|
||||||
|
|
@ -200,6 +203,7 @@ func _ready() -> void:
|
||||||
_new_game_button.pressed.connect(_on_new_game_pressed)
|
_new_game_button.pressed.connect(_on_new_game_pressed)
|
||||||
_join_game_button.pressed.connect(_open_join_game)
|
_join_game_button.pressed.connect(_open_join_game)
|
||||||
_settings_button.pressed.connect(_open_settings)
|
_settings_button.pressed.connect(_open_settings)
|
||||||
|
_credits_button.pressed.connect(_open_credits)
|
||||||
_delete_button.pressed.connect(_on_delete_pressed)
|
_delete_button.pressed.connect(_on_delete_pressed)
|
||||||
%QuitButton.pressed.connect(_on_quit_pressed)
|
%QuitButton.pressed.connect(_on_quit_pressed)
|
||||||
_confirmation_page.confirmed.connect(_on_confirmation_accepted)
|
_confirmation_page.confirmed.connect(_on_confirmation_accepted)
|
||||||
|
|
@ -211,6 +215,7 @@ func _ready() -> void:
|
||||||
_settings_panel.navigation_transition_started.connect(
|
_settings_panel.navigation_transition_started.connect(
|
||||||
_emit_navigation_bubble_flurry
|
_emit_navigation_bubble_flurry
|
||||||
)
|
)
|
||||||
|
_credits_page.back_requested.connect(_close_credits)
|
||||||
_bubble_field.configure(_get_title_buttons())
|
_bubble_field.configure(_get_title_buttons())
|
||||||
_decorative_fish_timer.timeout.connect(_on_decorative_fish_timer_timeout)
|
_decorative_fish_timer.timeout.connect(_on_decorative_fish_timer_timeout)
|
||||||
_decorative_bubble_event_timer.timeout.connect(
|
_decorative_bubble_event_timer.timeout.connect(
|
||||||
|
|
@ -261,6 +266,7 @@ func reopen() -> void:
|
||||||
_reset_confirmation()
|
_reset_confirmation()
|
||||||
_settings_panel.hide()
|
_settings_panel.hide()
|
||||||
_join_game_page.close_page()
|
_join_game_page.close_page()
|
||||||
|
_credits_page.close_page()
|
||||||
_refresh_save_inspection()
|
_refresh_save_inspection()
|
||||||
show()
|
show()
|
||||||
_start_decorative_presentation()
|
_start_decorative_presentation()
|
||||||
|
|
@ -274,6 +280,7 @@ func open_join_game_page(endpoint: String = "") -> void:
|
||||||
_presentation_center.hide()
|
_presentation_center.hide()
|
||||||
_settings_panel.hide()
|
_settings_panel.hide()
|
||||||
_confirmation_page.hide_page()
|
_confirmation_page.hide_page()
|
||||||
|
_credits_page.close_page()
|
||||||
_join_game_page.open_page(endpoint)
|
_join_game_page.open_page(endpoint)
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -286,7 +293,11 @@ func report_network_error(message: String) -> void:
|
||||||
|
|
||||||
|
|
||||||
func _open_join_game() -> 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
|
return
|
||||||
open_join_game_page()
|
open_join_game_page()
|
||||||
|
|
||||||
|
|
@ -299,6 +310,39 @@ func _close_join_game() -> void:
|
||||||
_focus_initial_button()
|
_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:
|
func is_awaiting_start_input() -> bool:
|
||||||
return _awaiting_start_input
|
return _awaiting_start_input
|
||||||
|
|
||||||
|
|
@ -403,6 +447,7 @@ func _get_title_buttons() -> Array[BubbleButton]:
|
||||||
_new_game_button,
|
_new_game_button,
|
||||||
_join_game_button,
|
_join_game_button,
|
||||||
_settings_button,
|
_settings_button,
|
||||||
|
_credits_button,
|
||||||
_delete_button,
|
_delete_button,
|
||||||
_quit_button,
|
_quit_button,
|
||||||
]
|
]
|
||||||
|
|
@ -498,6 +543,10 @@ func _input(event: InputEvent) -> void:
|
||||||
and _settings_panel.is_controller_mapping_capturing()
|
and _settings_panel.is_controller_mapping_capturing()
|
||||||
):
|
):
|
||||||
return
|
return
|
||||||
|
if _credits_page.visible:
|
||||||
|
if _credits_page.handle_input(event):
|
||||||
|
get_viewport().set_input_as_handled()
|
||||||
|
return
|
||||||
if _join_game_page.visible:
|
if _join_game_page.visible:
|
||||||
if event.is_action_pressed("ui_cancel"):
|
if event.is_action_pressed("ui_cancel"):
|
||||||
_close_join_game()
|
_close_join_game()
|
||||||
|
|
@ -549,6 +598,7 @@ func _handle_primary_menu_focus_input(event: InputEvent) -> bool:
|
||||||
not _button_center.visible
|
not _button_center.visible
|
||||||
or _is_confirmation_active()
|
or _is_confirmation_active()
|
||||||
or _settings_panel.visible
|
or _settings_panel.visible
|
||||||
|
or _credits_page.visible
|
||||||
):
|
):
|
||||||
return false
|
return false
|
||||||
if event is InputEventMouseMotion:
|
if event is InputEventMouseMotion:
|
||||||
|
|
@ -1186,6 +1236,7 @@ func _open_settings() -> void:
|
||||||
_is_confirmation_active()
|
_is_confirmation_active()
|
||||||
or _action_in_progress
|
or _action_in_progress
|
||||||
or _settings_panel.visible
|
or _settings_panel.visible
|
||||||
|
or _credits_page.visible
|
||||||
or _title_settings_transition_active
|
or _title_settings_transition_active
|
||||||
):
|
):
|
||||||
return
|
return
|
||||||
|
|
@ -1398,6 +1449,7 @@ func _focus_initial_button() -> void:
|
||||||
or not visible
|
or not visible
|
||||||
or _awaiting_start_input
|
or _awaiting_start_input
|
||||||
or not _button_center.visible
|
or not _button_center.visible
|
||||||
|
or _credits_page.visible
|
||||||
):
|
):
|
||||||
return
|
return
|
||||||
if not _continue_button.disabled:
|
if not _continue_button.disabled:
|
||||||
|
|
@ -1413,6 +1465,7 @@ func _on_quit_pressed() -> void:
|
||||||
not _action_in_progress
|
not _action_in_progress
|
||||||
and not _is_confirmation_active()
|
and not _is_confirmation_active()
|
||||||
and not _settings_panel.visible
|
and not _settings_panel.visible
|
||||||
|
and not _credits_page.visible
|
||||||
):
|
):
|
||||||
_hide_continue_stats_context()
|
_hide_continue_stats_context()
|
||||||
quit_requested.emit()
|
quit_requested.emit()
|
||||||
|
|
@ -1430,6 +1483,7 @@ func _on_title_visibility_changed() -> void:
|
||||||
_navigation_focus_active = false
|
_navigation_focus_active = false
|
||||||
_modal_restore_navigation_focus = false
|
_modal_restore_navigation_focus = false
|
||||||
_reset_confirmation()
|
_reset_confirmation()
|
||||||
|
_credits_page.close_page()
|
||||||
_stop_decorative_presentation()
|
_stop_decorative_presentation()
|
||||||
|
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -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="Script" path="res://ui/title_screen.gd" id="1_script"]
|
||||||
[ext_resource type="Theme" path="res://ui/game_theme.tres" id="2_theme"]
|
[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/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/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="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"]
|
[sub_resource type="ShaderMaterial" id="ShaderMaterial_title_water"]
|
||||||
shader = ExtResource("4_water_shader")
|
shader = ExtResource("4_water_shader")
|
||||||
|
|
@ -224,21 +225,21 @@ layout_mode = 2
|
||||||
|
|
||||||
[node name="BubbleLayoutSlot" type="Control" parent="ResponsiveTitleStage/TitlePresentationScaleRoot/Center/MainContent/ButtonCenter"]
|
[node name="BubbleLayoutSlot" type="Control" parent="ResponsiveTitleStage/TitlePresentationScaleRoot/Center/MainContent/ButtonCenter"]
|
||||||
unique_name_in_owner = true
|
unique_name_in_owner = true
|
||||||
custom_minimum_size = Vector2(396, 318)
|
custom_minimum_size = Vector2(440, 318)
|
||||||
layout_mode = 2
|
layout_mode = 2
|
||||||
|
|
||||||
[node name="BubbleMotionRoot" type="Control" parent="ResponsiveTitleStage/TitlePresentationScaleRoot/Center/MainContent/ButtonCenter/BubbleLayoutSlot"]
|
[node name="BubbleMotionRoot" type="Control" parent="ResponsiveTitleStage/TitlePresentationScaleRoot/Center/MainContent/ButtonCenter/BubbleLayoutSlot"]
|
||||||
unique_name_in_owner = true
|
unique_name_in_owner = true
|
||||||
layout_mode = 0
|
layout_mode = 0
|
||||||
offset_right = 396.0
|
offset_right = 440.0
|
||||||
offset_bottom = 318.0
|
offset_bottom = 318.0
|
||||||
mouse_filter = 2
|
mouse_filter = 2
|
||||||
|
|
||||||
[node name="BubbleField" type="Control" parent="ResponsiveTitleStage/TitlePresentationScaleRoot/Center/MainContent/ButtonCenter/BubbleLayoutSlot/BubbleMotionRoot"]
|
[node name="BubbleField" type="Control" parent="ResponsiveTitleStage/TitlePresentationScaleRoot/Center/MainContent/ButtonCenter/BubbleLayoutSlot/BubbleMotionRoot"]
|
||||||
unique_name_in_owner = true
|
unique_name_in_owner = true
|
||||||
custom_minimum_size = Vector2(396, 318)
|
custom_minimum_size = Vector2(440, 318)
|
||||||
layout_mode = 0
|
layout_mode = 0
|
||||||
offset_right = 396.0
|
offset_right = 440.0
|
||||||
offset_bottom = 318.0
|
offset_bottom = 318.0
|
||||||
script = ExtResource("8_bubble_cluster")
|
script = ExtResource("8_bubble_cluster")
|
||||||
profile = ExtResource("9_bubble_profile")
|
profile = ExtResource("9_bubble_profile")
|
||||||
|
|
@ -322,6 +323,30 @@ motion_period = 4.9
|
||||||
motion_phase = 2.5
|
motion_phase = 2.5
|
||||||
deformation_period = 6.3
|
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"]
|
[node name="JoinGameButton" type="Button" parent="ResponsiveTitleStage/TitlePresentationScaleRoot/Center/MainContent/ButtonCenter/BubbleLayoutSlot/BubbleMotionRoot/BubbleField"]
|
||||||
unique_name_in_owner = true
|
unique_name_in_owner = true
|
||||||
offset_left = 126.0
|
offset_left = 126.0
|
||||||
|
|
@ -444,6 +469,16 @@ offset_bottom = 300.0
|
||||||
grow_horizontal = 2
|
grow_horizontal = 2
|
||||||
grow_vertical = 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")]
|
[node name="JoinGamePage" parent="ResponsiveTitleStage/TitlePresentationScaleRoot" instance=ExtResource("11_join_page")]
|
||||||
unique_name_in_owner = true
|
unique_name_in_owner = true
|
||||||
z_index = 220
|
z_index = 220
|
||||||
|
|
|
||||||
|
|
@ -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.
|
|
||||||
|
|
@ -32,7 +32,9 @@ var fishing_shop_path: NodePath = (
|
||||||
^"Interactables/FishingShopWorld/InteractionArea"
|
^"Interactables/FishingShopWorld/InteractionArea"
|
||||||
)
|
)
|
||||||
@export_group("Terrain Collision")
|
@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_group("Foliage Wind")
|
||||||
@export_range(0.0, 0.4, 0.005) var foliage_wind_strength: float = 0.36
|
@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
|
@export_range(0.0, 4.0, 0.05) var foliage_wind_speed: float = 0.9
|
||||||
|
|
|
||||||