Compare commits

..

No commits in common. "c85f70985163392b37fbb85b9e274cdeebd03694" and "2f30691808c76a4e5b5a5e050819e0af15c5c1fa" have entirely different histories.

210 changed files with 1329 additions and 4834 deletions

View file

@ -37,8 +37,6 @@ written with or without assistance.
- “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
- “Quick Water Droplet” by qubodup, used for the bobber water-impact sound.
Freesound sound 792931, Creative Commons Zero (CC0):
https://freesound.org/s/792931/

View file

@ -38,6 +38,32 @@ scripts/run_validations.sh quick
Additional suites and individual commands are documented in
[`docs/TESTING.md`](docs/TESTING.md).
## Texture import normalization
Texture import settings are cached in `.import` sidecars. To keep new textures
consistent by default, normalize imported textures before committing:
```sh
./scripts/normalize_texture_profiles.sh
```
To normalize only a specific directory (for example environment textures):
```sh
./scripts/normalize_texture_profiles.sh res://art/exported/environment/textures
```
The script enforces a single baseline texture profile by default and sets
`import_profile = "managed_default"` in `[params]`. If any texture needs
custom settings, set:
```ini
[params]
import_profile="custom"
```
in its `.import` file to opt it out.
## Building
Export presets are maintained for Linux, Windows, and Android. Repository

View file

Before

Width:  |  Height:  |  Size: 8.8 KiB

After

Width:  |  Height:  |  Size: 8.8 KiB

Before After
Before After

View file

@ -0,0 +1,41 @@
[remap]
importer="texture"
type="CompressedTexture2D"
uid="uid://cd7qwsbeg1jxx"
path="res://.godot/imported/cliff_wall.png-7ce756d49c24c7b8fdda307d68086d87.ctex"
metadata={
"vram_texture": false
}
[deps]
source_file="res://art/exported/environment/textures/cliff_wall.png"
dest_files=["res://.godot/imported/cliff_wall.png-7ce756d49c24c7b8fdda307d68086d87.ctex"]
[params]
compress/mode=2
compress/high_quality=false
compress/lossy_quality=0.7
compress/uastc_level=0
compress/rdo_quality_loss=0.0
compress/hdr_compression=1
compress/normal_map=0
compress/channel_pack=0
mipmaps/generate=true
mipmaps/limit=-1
roughness/mode=0
roughness/src_normal=""
process/channel_remap/red=0
process/channel_remap/green=1
process/channel_remap/blue=2
process/channel_remap/alpha=3
process/fix_alpha_border=true
process/premult_alpha=false
process/normal_map_invert_y=false
process/hdr_as_srgb=false
process/hdr_clamp_exposure=false
process/size_limit=0
detect_3d/compress_to=0
import_profile="managed_default"

View file

Before

Width:  |  Height:  |  Size: 5.5 KiB

After

Width:  |  Height:  |  Size: 5.5 KiB

Before After
Before After

View file

@ -2,20 +2,20 @@
importer="texture"
type="CompressedTexture2D"
uid="uid://bn6815x0rrspw"
path="res://.godot/imported/64_bait_worms.png-c9f31d77527e73888fc56963d4e78e30.ctex"
uid="uid://b2ce1ikmnbktx"
path="res://.godot/imported/dirt.png-ca5bf3f3fbf583edaa719ac8c686549d.ctex"
metadata={
"vram_texture": false
}
[deps]
source_file="res://items/icons/bait/64_bait_worms.png"
dest_files=["res://.godot/imported/64_bait_worms.png-c9f31d77527e73888fc56963d4e78e30.ctex"]
source_file="res://art/exported/environment/textures/dirt.png"
dest_files=["res://.godot/imported/dirt.png-ca5bf3f3fbf583edaa719ac8c686549d.ctex"]
[params]
compress/mode=0
compress/mode=2
compress/high_quality=false
compress/lossy_quality=0.7
compress/uastc_level=0
@ -23,7 +23,7 @@ compress/rdo_quality_loss=0.0
compress/hdr_compression=1
compress/normal_map=0
compress/channel_pack=0
mipmaps/generate=false
mipmaps/generate=true
mipmaps/limit=-1
roughness/mode=0
roughness/src_normal=""
@ -37,4 +37,5 @@ process/normal_map_invert_y=false
process/hdr_as_srgb=false
process/hdr_clamp_exposure=false
process/size_limit=0
detect_3d/compress_to=1
detect_3d/compress_to=0
import_profile="managed_default"

View file

Before

Width:  |  Height:  |  Size: 5.2 KiB

After

Width:  |  Height:  |  Size: 5.2 KiB

Before After
Before After

View file

@ -0,0 +1,41 @@
[remap]
importer="texture"
type="CompressedTexture2D"
uid="uid://dtv2k327higrb"
path="res://.godot/imported/dirt_light_wall.png-40336902aac8516f68e99f8e8988d86f.ctex"
metadata={
"vram_texture": false
}
[deps]
source_file="res://art/exported/environment/textures/dirt_light_wall.png"
dest_files=["res://.godot/imported/dirt_light_wall.png-40336902aac8516f68e99f8e8988d86f.ctex"]
[params]
compress/mode=2
compress/high_quality=false
compress/lossy_quality=0.7
compress/uastc_level=0
compress/rdo_quality_loss=0.0
compress/hdr_compression=1
compress/normal_map=0
compress/channel_pack=0
mipmaps/generate=true
mipmaps/limit=-1
roughness/mode=0
roughness/src_normal=""
process/channel_remap/red=0
process/channel_remap/green=1
process/channel_remap/blue=2
process/channel_remap/alpha=3
process/fix_alpha_border=true
process/premult_alpha=false
process/normal_map_invert_y=false
process/hdr_as_srgb=false
process/hdr_clamp_exposure=false
process/size_limit=0
detect_3d/compress_to=0
import_profile="managed_default"

View file

Before

Width:  |  Height:  |  Size: 3.6 KiB

After

Width:  |  Height:  |  Size: 3.6 KiB

Before After
Before After

View file

@ -2,20 +2,20 @@
importer="texture"
type="CompressedTexture2D"
uid="uid://d4aah3j5brkij"
path="res://.godot/imported/arrow_dark_down_full.png-0e7bb59c41a81c0bd02261d6a7b319ef.ctex"
uid="uid://c3j0gailr6yfy"
path="res://.godot/imported/dirt_wall.png-8ba7bb24831d83f929bfa44b53af9288.ctex"
metadata={
"vram_texture": false
}
[deps]
source_file="res://ui/icons/pictograms/arrow_dark_down_full.png"
dest_files=["res://.godot/imported/arrow_dark_down_full.png-0e7bb59c41a81c0bd02261d6a7b319ef.ctex"]
source_file="res://art/exported/environment/textures/dirt_wall.png"
dest_files=["res://.godot/imported/dirt_wall.png-8ba7bb24831d83f929bfa44b53af9288.ctex"]
[params]
compress/mode=0
compress/mode=2
compress/high_quality=false
compress/lossy_quality=0.7
compress/uastc_level=0
@ -23,7 +23,7 @@ compress/rdo_quality_loss=0.0
compress/hdr_compression=1
compress/normal_map=0
compress/channel_pack=0
mipmaps/generate=false
mipmaps/generate=true
mipmaps/limit=-1
roughness/mode=0
roughness/src_normal=""
@ -37,4 +37,5 @@ process/normal_map_invert_y=false
process/hdr_as_srgb=false
process/hdr_clamp_exposure=false
process/size_limit=0
detect_3d/compress_to=1
detect_3d/compress_to=0
import_profile="managed_default"

View file

Before

Width:  |  Height:  |  Size: 6.9 KiB

After

Width:  |  Height:  |  Size: 6.9 KiB

Before After
Before After

View file

@ -2,20 +2,20 @@
importer="texture"
type="CompressedTexture2D"
uid="uid://baht2hredfky8"
path="res://.godot/imported/64_cooler_plus.png-d0daa39246f92fd9774ec1721970efad.ctex"
uid="uid://b4n8bf4cgua4s"
path="res://.godot/imported/grass.png-4d1108b11a421d9c16bd72fb0bc993d3.ctex"
metadata={
"vram_texture": false
}
[deps]
source_file="res://items/icons/equipment/64_cooler_plus.png"
dest_files=["res://.godot/imported/64_cooler_plus.png-d0daa39246f92fd9774ec1721970efad.ctex"]
source_file="res://art/exported/environment/textures/grass.png"
dest_files=["res://.godot/imported/grass.png-4d1108b11a421d9c16bd72fb0bc993d3.ctex"]
[params]
compress/mode=0
compress/mode=2
compress/high_quality=false
compress/lossy_quality=0.7
compress/uastc_level=0
@ -23,7 +23,7 @@ compress/rdo_quality_loss=0.0
compress/hdr_compression=1
compress/normal_map=0
compress/channel_pack=0
mipmaps/generate=false
mipmaps/generate=true
mipmaps/limit=-1
roughness/mode=0
roughness/src_normal=""
@ -37,4 +37,5 @@ process/normal_map_invert_y=false
process/hdr_as_srgb=false
process/hdr_clamp_exposure=false
process/size_limit=0
detect_3d/compress_to=1
detect_3d/compress_to=0
import_profile="managed_default"

View file

Before

Width:  |  Height:  |  Size: 9.4 KiB

After

Width:  |  Height:  |  Size: 9.4 KiB

Before After
Before After

View file

@ -0,0 +1,41 @@
[remap]
importer="texture"
type="CompressedTexture2D"
uid="uid://dusa52a3eghr7"
path="res://.godot/imported/grass_heavy.png-ba7f679d1bc5402f8b0a3465ac6a44b8.ctex"
metadata={
"vram_texture": false
}
[deps]
source_file="res://art/exported/environment/textures/grass_heavy.png"
dest_files=["res://.godot/imported/grass_heavy.png-ba7f679d1bc5402f8b0a3465ac6a44b8.ctex"]
[params]
compress/mode=2
compress/high_quality=false
compress/lossy_quality=0.7
compress/uastc_level=0
compress/rdo_quality_loss=0.0
compress/hdr_compression=1
compress/normal_map=0
compress/channel_pack=0
mipmaps/generate=true
mipmaps/limit=-1
roughness/mode=0
roughness/src_normal=""
process/channel_remap/red=0
process/channel_remap/green=1
process/channel_remap/blue=2
process/channel_remap/alpha=3
process/fix_alpha_border=true
process/premult_alpha=false
process/normal_map_invert_y=false
process/hdr_as_srgb=false
process/hdr_clamp_exposure=false
process/size_limit=0
detect_3d/compress_to=0
import_profile="managed_default"

View file

Before

Width:  |  Height:  |  Size: 6.2 KiB

After

Width:  |  Height:  |  Size: 6.2 KiB

Before After
Before After

View file

@ -0,0 +1,43 @@
[remap]
importer="texture"
type="CompressedTexture2D"
uid="uid://crci2on30ctoc"
path.s3tc="res://.godot/imported/grass_lite.png-f89cf6413257cd31582bd4f9f875ae85.s3tc.ctex"
path.etc2="res://.godot/imported/grass_lite.png-f89cf6413257cd31582bd4f9f875ae85.etc2.ctex"
metadata={
"imported_formats": ["s3tc_bptc", "etc2_astc"],
"vram_texture": true
}
[deps]
source_file="res://art/exported/environment/textures/grass_lite.png"
dest_files=["res://.godot/imported/grass_lite.png-f89cf6413257cd31582bd4f9f875ae85.s3tc.ctex", "res://.godot/imported/grass_lite.png-f89cf6413257cd31582bd4f9f875ae85.etc2.ctex"]
[params]
compress/mode=2
compress/high_quality=false
compress/lossy_quality=0.7
compress/uastc_level=0
compress/rdo_quality_loss=0.0
compress/hdr_compression=1
compress/normal_map=0
compress/channel_pack=0
mipmaps/generate=true
mipmaps/limit=-1
roughness/mode=0
roughness/src_normal=""
process/channel_remap/red=0
process/channel_remap/green=1
process/channel_remap/blue=2
process/channel_remap/alpha=3
process/fix_alpha_border=true
process/premult_alpha=false
process/normal_map_invert_y=false
process/hdr_as_srgb=false
process/hdr_clamp_exposure=false
process/size_limit=0
detect_3d/compress_to=0
import_profile="managed_default"

View file

Before

Width:  |  Height:  |  Size: 6.1 KiB

After

Width:  |  Height:  |  Size: 6.1 KiB

Before After
Before After

View file

@ -0,0 +1,43 @@
[remap]
importer="texture"
type="CompressedTexture2D"
uid="uid://btxhq3rdlum2"
path.s3tc="res://.godot/imported/grass_olive_tile.png-72072a382d0948818df97773afa5493f.s3tc.ctex"
path.etc2="res://.godot/imported/grass_olive_tile.png-72072a382d0948818df97773afa5493f.etc2.ctex"
metadata={
"imported_formats": ["s3tc_bptc", "etc2_astc"],
"vram_texture": true
}
[deps]
source_file="res://art/exported/environment/textures/grass_olive_tile.png"
dest_files=["res://.godot/imported/grass_olive_tile.png-72072a382d0948818df97773afa5493f.s3tc.ctex", "res://.godot/imported/grass_olive_tile.png-72072a382d0948818df97773afa5493f.etc2.ctex"]
[params]
compress/mode=2
compress/high_quality=false
compress/lossy_quality=0.7
compress/uastc_level=0
compress/rdo_quality_loss=0.0
compress/hdr_compression=1
compress/normal_map=0
compress/channel_pack=0
mipmaps/generate=true
mipmaps/limit=-1
roughness/mode=0
roughness/src_normal=""
process/channel_remap/red=0
process/channel_remap/green=1
process/channel_remap/blue=2
process/channel_remap/alpha=3
process/fix_alpha_border=true
process/premult_alpha=false
process/normal_map_invert_y=false
process/hdr_as_srgb=false
process/hdr_clamp_exposure=false
process/size_limit=0
detect_3d/compress_to=0
import_profile="managed_default"

View file

Before

Width:  |  Height:  |  Size: 6.1 KiB

After

Width:  |  Height:  |  Size: 6.1 KiB

Before After
Before After

View file

@ -0,0 +1,43 @@
[remap]
importer="texture"
type="CompressedTexture2D"
uid="uid://bnje415gq85nn"
path.s3tc="res://.godot/imported/sand.png-41fc96c392119deeba43c2d4e49d88a5.s3tc.ctex"
path.etc2="res://.godot/imported/sand.png-41fc96c392119deeba43c2d4e49d88a5.etc2.ctex"
metadata={
"imported_formats": ["s3tc_bptc", "etc2_astc"],
"vram_texture": true
}
[deps]
source_file="res://art/exported/environment/textures/sand.png"
dest_files=["res://.godot/imported/sand.png-41fc96c392119deeba43c2d4e49d88a5.s3tc.ctex", "res://.godot/imported/sand.png-41fc96c392119deeba43c2d4e49d88a5.etc2.ctex"]
[params]
compress/mode=2
compress/high_quality=false
compress/lossy_quality=0.7
compress/uastc_level=0
compress/rdo_quality_loss=0.0
compress/hdr_compression=1
compress/normal_map=0
compress/channel_pack=0
mipmaps/generate=true
mipmaps/limit=-1
roughness/mode=0
roughness/src_normal=""
process/channel_remap/red=0
process/channel_remap/green=1
process/channel_remap/blue=2
process/channel_remap/alpha=3
process/fix_alpha_border=true
process/premult_alpha=false
process/normal_map_invert_y=false
process/hdr_as_srgb=false
process/hdr_clamp_exposure=false
process/size_limit=0
detect_3d/compress_to=0
import_profile="managed_default"

View file

Before

Width:  |  Height:  |  Size: 6.7 KiB

After

Width:  |  Height:  |  Size: 6.7 KiB

Before After
Before After

View file

@ -0,0 +1,41 @@
[remap]
importer="texture"
type="CompressedTexture2D"
uid="uid://cyukfgkghrigk"
path="res://.godot/imported/sand_dark.png-ab4ec28097312f566fe7b882978a5858.ctex"
metadata={
"vram_texture": false
}
[deps]
source_file="res://art/exported/environment/textures/sand_dark.png"
dest_files=["res://.godot/imported/sand_dark.png-ab4ec28097312f566fe7b882978a5858.ctex"]
[params]
compress/mode=2
compress/high_quality=false
compress/lossy_quality=0.7
compress/uastc_level=0
compress/rdo_quality_loss=0.0
compress/hdr_compression=1
compress/normal_map=0
compress/channel_pack=0
mipmaps/generate=true
mipmaps/limit=-1
roughness/mode=0
roughness/src_normal=""
process/channel_remap/red=0
process/channel_remap/green=1
process/channel_remap/blue=2
process/channel_remap/alpha=3
process/fix_alpha_border=true
process/premult_alpha=false
process/normal_map_invert_y=false
process/hdr_as_srgb=false
process/hdr_clamp_exposure=false
process/size_limit=0
detect_3d/compress_to=0
import_profile="managed_default"

View file

Before

Width:  |  Height:  |  Size: 6.1 KiB

After

Width:  |  Height:  |  Size: 6.1 KiB

Before After
Before After

View file

@ -2,20 +2,20 @@
importer="texture"
type="CompressedTexture2D"
uid="uid://bnwbcopsjmi3s"
path="res://.godot/imported/ui_logbook.png-e45558834671114c04857bb4a6091575.ctex"
uid="uid://whknttjshvv1"
path="res://.godot/imported/sand_lite.png-6962f2117f97a265a7074c81ef2370fa.ctex"
metadata={
"vram_texture": false
}
[deps]
source_file="res://ui/assets/logbook/ui_logbook.png"
dest_files=["res://.godot/imported/ui_logbook.png-e45558834671114c04857bb4a6091575.ctex"]
source_file="res://art/exported/environment/textures/sand_lite.png"
dest_files=["res://.godot/imported/sand_lite.png-6962f2117f97a265a7074c81ef2370fa.ctex"]
[params]
compress/mode=0
compress/mode=2
compress/high_quality=false
compress/lossy_quality=0.7
compress/uastc_level=0
@ -23,7 +23,7 @@ compress/rdo_quality_loss=0.0
compress/hdr_compression=1
compress/normal_map=0
compress/channel_pack=0
mipmaps/generate=false
mipmaps/generate=true
mipmaps/limit=-1
roughness/mode=0
roughness/src_normal=""
@ -37,4 +37,5 @@ process/normal_map_invert_y=false
process/hdr_as_srgb=false
process/hdr_clamp_exposure=false
process/size_limit=0
detect_3d/compress_to=1
detect_3d/compress_to=0
import_profile="managed_default"

View file

Before

Width:  |  Height:  |  Size: 5.1 KiB

After

Width:  |  Height:  |  Size: 5.1 KiB

Before After
Before After

View file

@ -0,0 +1,43 @@
[remap]
importer="texture"
type="CompressedTexture2D"
uid="uid://ctll4fbskvat8"
path.s3tc="res://.godot/imported/sandstone_heavy.png-c324c68e8c28417ecd3506dbd5f24ef5.s3tc.ctex"
path.etc2="res://.godot/imported/sandstone_heavy.png-c324c68e8c28417ecd3506dbd5f24ef5.etc2.ctex"
metadata={
"imported_formats": ["s3tc_bptc", "etc2_astc"],
"vram_texture": true
}
[deps]
source_file="res://art/exported/environment/textures/sandstone_heavy.png"
dest_files=["res://.godot/imported/sandstone_heavy.png-c324c68e8c28417ecd3506dbd5f24ef5.s3tc.ctex", "res://.godot/imported/sandstone_heavy.png-c324c68e8c28417ecd3506dbd5f24ef5.etc2.ctex"]
[params]
compress/mode=2
compress/high_quality=false
compress/lossy_quality=0.7
compress/uastc_level=0
compress/rdo_quality_loss=0.0
compress/hdr_compression=1
compress/normal_map=0
compress/channel_pack=0
mipmaps/generate=true
mipmaps/limit=-1
roughness/mode=0
roughness/src_normal=""
process/channel_remap/red=0
process/channel_remap/green=1
process/channel_remap/blue=2
process/channel_remap/alpha=3
process/fix_alpha_border=true
process/premult_alpha=false
process/normal_map_invert_y=false
process/hdr_as_srgb=false
process/hdr_clamp_exposure=false
process/size_limit=0
detect_3d/compress_to=0
import_profile="managed_default"

View file

Before

Width:  |  Height:  |  Size: 7.5 KiB

After

Width:  |  Height:  |  Size: 7.5 KiB

Before After
Before After

View file

@ -0,0 +1,41 @@
[remap]
importer="texture"
type="CompressedTexture2D"
uid="uid://bgbaiuts1c4sb"
path="res://.godot/imported/sandstone_lite.png-8d5c0c5c4e3a227e0e487db42a418aba.ctex"
metadata={
"vram_texture": false
}
[deps]
source_file="res://art/exported/environment/textures/sandstone_lite.png"
dest_files=["res://.godot/imported/sandstone_lite.png-8d5c0c5c4e3a227e0e487db42a418aba.ctex"]
[params]
compress/mode=2
compress/high_quality=false
compress/lossy_quality=0.7
compress/uastc_level=0
compress/rdo_quality_loss=0.0
compress/hdr_compression=1
compress/normal_map=0
compress/channel_pack=0
mipmaps/generate=true
mipmaps/limit=-1
roughness/mode=0
roughness/src_normal=""
process/channel_remap/red=0
process/channel_remap/green=1
process/channel_remap/blue=2
process/channel_remap/alpha=3
process/fix_alpha_border=true
process/premult_alpha=false
process/normal_map_invert_y=false
process/hdr_as_srgb=false
process/hdr_clamp_exposure=false
process/size_limit=0
detect_3d/compress_to=0
import_profile="managed_default"

Binary file not shown.

View file

@ -1,19 +0,0 @@
[remap]
importer="oggvorbisstr"
type="AudioStreamOggVorbis"
uid="uid://cfqskba5j2iig"
path="res://.godot/imported/rain_loop_ontario.ogg-c206e8a7ee5398c9fdc6d59462050964.oggvorbisstr"
[deps]
source_file="res://audio/ambience/rain_loop_ontario.ogg"
dest_files=["res://.godot/imported/rain_loop_ontario.ogg-c206e8a7ee5398c9fdc6d59462050964.oggvorbisstr"]
[params]
loop=false
loop_offset=0
bpm=0
beat_count=0
bar_beats=4

View file

@ -9,33 +9,13 @@ const WORM_MAX_STACK: int = 10
const ITEM_PRICES: Dictionary[StringName, int] = {
&"worms": 1,
&"snails": 3,
&"shrimp": 7,
&"squid_chunks": 15,
&"whole_anchovy": 30,
&"whole_sardine": 60,
&"luminous_roe": 125,
&"coffee": 20,
&"energy_drink": 35,
&"snack": 30,
&"fish_finder": 60,
}
const BAIT_UNLOCK_PRICES: Dictionary[StringName, int] = {
&"snails": 400,
&"shrimp": 1200,
&"squid_chunks": 3500,
&"whole_anchovy": 9000,
&"whole_sardine": 22000,
&"luminous_roe": 50000,
}
const ITEM_ORDER: Array[StringName] = [
&"worms",
&"snails",
&"shrimp",
&"squid_chunks",
&"whole_anchovy",
&"whole_sardine",
&"luminous_roe",
&"coffee",
&"energy_drink",
&"snack",
@ -51,35 +31,14 @@ static func get_stock_item_ids() -> Array[StringName]:
return ITEM_ORDER.duplicate()
static func get_unlock_price(item_id: StringName) -> int:
return BAIT_UNLOCK_PRICES.get(item_id, -1)
static func get_purchase_quantity(
item_id: StringName,
owned: int,
maximum_stack: int = WORM_MAX_STACK,
) -> int:
if is_bait_topoff(item_id):
return maxi(maximum_stack - owned, 0)
static func get_purchase_quantity(item_id: StringName, owned: int) -> int:
if item_id == &"worms":
return maxi(WORM_MAX_STACK - owned, 0)
return 1
static func is_bait_topoff(item_id: StringName) -> bool:
return item_id == &"worms" or BAIT_UNLOCK_PRICES.has(item_id)
static func get_purchase_cost(
item_id: StringName,
quantity: int,
bait_unlocked: bool,
) -> int:
if quantity <= 0:
return -1
if is_bait_topoff(item_id) and item_id != &"worms" and not bait_unlocked:
return get_unlock_price(item_id)
var unit_price: int = get_price(item_id)
return unit_price * quantity if unit_price >= 0 else -1
return item_id == &"worms"
static func purchase_one(
@ -90,32 +49,22 @@ static func purchase_one(
) -> bool:
if wallet == null or bag == null or catalog == null:
return false
var price: int = get_price(item_id)
var item: ItemDataType = catalog.get_item_by_id(item_id)
var bait_topoff: bool = is_bait_topoff(item_id)
var quantity: int = get_purchase_quantity(
item_id,
bag.get_quantity(item_id),
item.max_stack if item != null else WORM_MAX_STACK,
)
var price: int = get_purchase_cost(
item_id,
quantity,
bag.is_bait_unlocked(item_id),
)
if (
price < 0
or item == null
or not item.is_valid()
or (item.category != ItemDataType.Category.CONSUMABLE and not bait_topoff)
or (item.category != ItemDataType.Category.CONSUMABLE and not is_bait_topoff(item_id))
or not item.stackable
or (not item.usable and not bait_topoff)
or not bag.can_add_item(item_id, quantity)
or (not item.usable and not is_bait_topoff(item_id))
or not bag.can_add_item(item_id, 1)
or not wallet.can_afford(price)
):
return false
if not wallet.debit(price):
return false
if bag.add_item(item_id, quantity):
if bag.add_item(item_id, 1):
return true
if not wallet.credit(price):
push_error("Fishing Shop failed to roll back an item purchase.")

View file

@ -16,12 +16,6 @@ const ALL_TIERS_MASK: int = (1 << TIER_COUNT) - 1
# multipliers later without changing catch serialization or tier identity.
const BASE_ROLL_WEIGHTS: Array[float] = [40.0, 32.0, 18.0, 8.0, 2.0]
const WORM_ROLL_WEIGHTS: Array[float] = [80.0, 10.0, 5.0, 4.0, 1.0]
const SNAIL_ROLL_WEIGHTS: Array[float] = [68.0, 16.0, 8.0, 6.0, 2.0]
const SHRIMP_ROLL_WEIGHTS: Array[float] = [56.0, 21.0, 12.0, 8.0, 3.0]
const SQUID_ROLL_WEIGHTS: Array[float] = [44.0, 25.0, 16.0, 11.0, 4.0]
const ANCHOVY_ROLL_WEIGHTS: Array[float] = [34.0, 27.0, 20.0, 13.0, 6.0]
const SARDINE_ROLL_WEIGHTS: Array[float] = [25.0, 28.0, 23.0, 16.0, 8.0]
const LUMINOUS_ROE_ROLL_WEIGHTS: Array[float] = [16.0, 26.0, 26.0, 20.0, 12.0]
const SALE_MULTIPLIERS: Array[float] = [1.0, 1.1, 1.25, 1.5, 2.0]
# Legacy profile multiplier retained for serialized/profile compatibility.
# New encounters use the weighted quality/rarity/weight bands below.
@ -142,34 +136,17 @@ static func roll(
static func roll_weights_for_bait(active_bait_tags: Array[StringName]) -> Array[float]:
var bait_weights: Array[float] = rarity_weights_for_bait(active_bait_tags)
var weights: Array[float] = []
for quality: int in TIER_COUNT:
weights.append(bait_weights[quality] / BASE_ROLL_WEIGHTS[quality])
return weights
static func rarity_weights_for_bait(
active_bait_tags: Array[StringName],
) -> Array[float]:
var weights: Array[float] = []
if active_bait_tags.is_empty():
weights.assign([100.0, 0.0, 0.0, 0.0, 0.0])
elif active_bait_tags.has(&"bait_rarity_6"):
weights.assign(LUMINOUS_ROE_ROLL_WEIGHTS)
elif active_bait_tags.has(&"bait_rarity_5"):
weights.assign(SARDINE_ROLL_WEIGHTS)
elif active_bait_tags.has(&"bait_rarity_4"):
weights.assign(ANCHOVY_ROLL_WEIGHTS)
elif active_bait_tags.has(&"bait_rarity_3"):
weights.assign(SQUID_ROLL_WEIGHTS)
elif active_bait_tags.has(&"bait_rarity_2"):
weights.assign(SHRIMP_ROLL_WEIGHTS)
elif active_bait_tags.has(&"bait_rarity_1"):
weights.assign(SNAIL_ROLL_WEIGHTS)
weights.assign([2.5, 0.0, 0.0, 0.0, 0.0])
else:
# Unknown and legacy bait tags retain the original worms behavior.
weights.assign(WORM_ROLL_WEIGHTS)
weights.assign([
WORM_ROLL_WEIGHTS[0] / BASE_ROLL_WEIGHTS[0],
WORM_ROLL_WEIGHTS[1] / BASE_ROLL_WEIGHTS[1],
WORM_ROLL_WEIGHTS[2] / BASE_ROLL_WEIGHTS[2],
WORM_ROLL_WEIGHTS[3] / BASE_ROLL_WEIGHTS[3],
WORM_ROLL_WEIGHTS[4] / BASE_ROLL_WEIGHTS[4],
])
return weights

View file

@ -81,9 +81,9 @@ func select_fish(
func _roll_rarity(context: FishingContextType) -> int:
var weights: Array[float] = FishQualityType.rarity_weights_for_bait(
context.active_bait_tags
)
var weights: Array[float] = [100.0, 0.0, 0.0, 0.0, 0.0]
if not context.active_bait_tags.is_empty():
weights = [80.0, 10.0, 5.0, 4.0, 1.0]
for index: int in range(weights.size()):
if index < rarity_weight_multipliers.size():
weights[index] *= maxf(rarity_weight_multipliers[index], 0.0)

View file

@ -1,10 +1,6 @@
class_name FishingPresentation
extends Node3D
const WaterSurfaceMotionType = preload(
"res://world/water_surface_motion.gd"
)
signal cast_completed
signal outcome_completed(outcome: StringName)
signal presentation_interrupted
@ -56,7 +52,6 @@ var _rod: Node3D
var _rod_tip: Marker3D
var _rod_neutral_rotation: Vector3
var _cast_arrival_position: Vector3
var _cast_tracks_water_surface: bool = false
var _bobber_surface_position: Vector3
var _bobber_idle_elapsed: float = 0.0
var _active_tween: Tween
@ -73,9 +68,6 @@ func _process(delta: float) -> void:
if _mode == VisualMode.FISHING and _bobber.visible:
_bobber_idle_elapsed += delta
_apply_bobber_idle_motion()
var bobber_position := _bobber.global_position
bobber_position.y += WaterSurfaceMotionType.get_default_height_offset()
_bobber.global_position = bobber_position
if _line_mode != LineMode.HIDDEN:
_update_line(delta)
@ -162,7 +154,6 @@ func begin_cast(
_cast_arrival_position = (
blocked_landing_position if cast_is_blocked else target
)
_cast_tracks_water_surface = not cast_is_blocked
var cast_start: Vector3 = _rod_tip.global_position
_bobber.global_position = cast_start
@ -298,7 +289,6 @@ func cleanup() -> void:
_bobber.scale = Vector3.ONE
_bobber.rotation = Vector3.ZERO
_cast_arrival_position = Vector3.ZERO
_cast_tracks_water_surface = false
_bobber_surface_position = Vector3.ZERO
_bobber_idle_elapsed = 0.0
set_line_mode(LineMode.HIDDEN)
@ -316,11 +306,6 @@ func _set_cast_sample(
) -> void:
var sample: Vector3 = start.lerp(target, progress)
sample.y += sin(progress * PI) * cast_arc_height
if _cast_tracks_water_surface:
sample.y += (
WaterSurfaceMotionType.get_default_height_offset()
* smoothstep(0.72, 1.0, progress)
)
_bobber.global_position = sample

View file

@ -100,14 +100,14 @@ enum FishingState {
@export_range(0.0, 1.0, 0.01) var withdrawal_surface_clearance: float = 0.4
@export_category("Timing")
const BITE_QUICK_MIN_SECONDS: float = 6.0
const BITE_QUICK_MAX_SECONDS: float = 18.0
const BITE_TYPICAL_MAX_SECONDS: float = 48.0
const BITE_LONG_MAX_SECONDS: float = 72.0
const BITE_MAX_SECONDS: float = 90.0
const BITE_QUICK_PROBABILITY: float = 0.30
const BITE_QUICK_MIN_SECONDS: float = 8.0
const BITE_QUICK_MAX_SECONDS: float = 24.0
const BITE_TYPICAL_MAX_SECONDS: float = 72.0
const BITE_LONG_MAX_SECONDS: float = 144.0
const BITE_MAX_SECONDS: float = 192.0
const BITE_QUICK_PROBABILITY: float = 0.25
const BITE_TYPICAL_PROBABILITY: float = 0.65
const BITE_LONG_PROBABILITY: float = 0.04
const BITE_LONG_PROBABILITY: float = 0.08
const NETWORK_INPUT_RESEND_INTERVAL_SECONDS: float = 0.1
@export_range(0.1, 10.0, 0.1) var cooldown_duration: float = 1.0

View file

@ -4,9 +4,6 @@ extends RefCounted
const FishableWaterRegionType = preload(
"res://world/fishable_water_region.gd"
)
const WaterSurfaceMotionType = preload(
"res://world/water_surface_motion.gd"
)
var has_surface: bool = false
var position: Vector3 = Vector3.ZERO
@ -22,12 +19,7 @@ func is_fishable() -> bool:
func get_marker_position(vertical_offset: float) -> Vector3:
if not has_surface:
return position
var presentation_offset := vertical_offset
if is_water_surface:
presentation_offset += (
WaterSurfaceMotionType.get_default_height_offset()
)
return position + normal.normalized() * presentation_offset
return position + normal.normalized() * vertical_offset
func get_bobber_position(solid_clearance: float) -> Vector3:

View file

@ -1,10 +1,6 @@
class_name RemoteFishingPresentation
extends Node3D
const WaterSurfaceMotionType = preload(
"res://world/water_surface_motion.gd"
)
signal return_completed
var _owner: Player
@ -144,10 +140,6 @@ func _set_cast_sample(
) -> void:
_target = start.lerp(target, progress)
_target.y += sin(progress * PI) * 2.0
_target.y += (
WaterSurfaceMotionType.get_default_height_offset()
* smoothstep(0.72, 1.0, progress)
)
_bobber.global_position = _target
@ -160,10 +152,7 @@ func _on_cast_finished() -> void:
func _apply_bobber_idle_motion() -> void:
var phase: float = _bobber_idle_elapsed * 0.7 * TAU
_target = _pending_target + Vector3.UP * (
WaterSurfaceMotionType.get_default_height_offset()
+ sin(phase) * 0.035
)
_target = _pending_target + Vector3.UP * sin(phase) * 0.035
_bobber.global_position = _target
_bobber.rotation.z = deg_to_rad(4.0) * sin(phase * 0.73)

View file

@ -5,15 +5,10 @@ const ItemCatalogType = preload("res://items/item_catalog.gd")
const ItemDataType = preload("res://items/item_data.gd")
const OwnedItemType = preload("res://items/owned_item.gd")
const DEFAULT_UNLOCKED_BAIT_IDS: Array[StringName] = [&"worms"]
signal contents_changed
var _catalog: ItemCatalogType
var _items: Array[OwnedItemType] = []
var _unlocked_bait_ids: Array[StringName] = (
DEFAULT_UNLOCKED_BAIT_IDS.duplicate()
)
func setup(catalog: ItemCatalogType) -> void:
@ -32,8 +27,6 @@ func add_item(item_id: StringName, quantity: int = 1) -> bool:
owned.item_id = item_id
owned.quantity = quantity
_items.append(owned)
if item.is_bait() and not _unlocked_bait_ids.has(item_id):
_unlocked_bait_ids.append(item_id)
contents_changed.emit()
return true
@ -92,46 +85,6 @@ func get_all_items() -> Array[OwnedItemType]:
return result
func get_unlocked_bait_ids() -> Array[StringName]:
return _unlocked_bait_ids.duplicate()
func is_bait_unlocked(item_id: StringName) -> bool:
return _unlocked_bait_ids.has(item_id)
func get_unlocked_bait_items() -> Array[OwnedItemType]:
var result: Array[OwnedItemType] = []
for item_id: StringName in _unlocked_bait_ids:
var owned: OwnedItemType = get_owned_item(item_id)
if owned != null:
result.append(owned.duplicate_record())
continue
var empty_record := OwnedItemType.new()
empty_record.item_id = item_id
empty_record.quantity = 0
result.append(empty_record)
return result
func replace_unlocked_bait_ids(item_ids: Array[StringName]) -> bool:
var validated: Array[StringName] = DEFAULT_UNLOCKED_BAIT_IDS.duplicate()
var seen: Dictionary[StringName, bool] = {}
for item_id: StringName in item_ids:
if item_id.is_empty() or seen.has(item_id):
return false
seen[item_id] = true
if validated.has(item_id):
continue
var item: ItemDataType = _resolve_valid_item(item_id)
if item == null or not item.is_bait():
return false
validated.append(item_id)
_unlocked_bait_ids = validated
contents_changed.emit()
return true
func replace_all_items(items: Array[OwnedItemType]) -> bool:
var validated: Array[OwnedItemType] = []
var seen: Dictionary[StringName, bool] = {}

View file

@ -1,4 +1,4 @@
[gd_resource type="Resource" script_class="ItemCatalog" load_steps=16 format=3]
[gd_resource type="Resource" script_class="ItemCatalog" load_steps=10 format=3]
[ext_resource type="Script" path="res://items/item_catalog.gd" id="1_script"]
[ext_resource type="Resource" path="res://items/catalog/basic_fishing_rod.tres" id="2_rod"]
@ -9,13 +9,7 @@
[ext_resource type="Resource" path="res://items/catalog/cooler_expansion.tres" id="7_cooler"]
[ext_resource type="Resource" path="res://items/catalog/art_kit.tres" id="8_art_kit"]
[ext_resource type="Resource" path="res://items/catalog/worms.tres" id="9_worms"]
[ext_resource type="Resource" path="res://items/catalog/snails.tres" id="10_snails"]
[ext_resource type="Resource" path="res://items/catalog/shrimp.tres" id="11_shrimp"]
[ext_resource type="Resource" path="res://items/catalog/squid_chunks.tres" id="12_squid"]
[ext_resource type="Resource" path="res://items/catalog/whole_anchovy.tres" id="13_anchovy"]
[ext_resource type="Resource" path="res://items/catalog/whole_sardine.tres" id="14_sardine"]
[ext_resource type="Resource" path="res://items/catalog/luminous_roe.tres" id="15_roe"]
[resource]
script = ExtResource("1_script")
items = [ExtResource("2_rod"), ExtResource("3_coffee"), ExtResource("4_energy"), ExtResource("5_snack"), ExtResource("6_finder"), ExtResource("7_cooler"), ExtResource("8_art_kit"), ExtResource("9_worms"), ExtResource("10_snails"), ExtResource("11_shrimp"), ExtResource("12_squid"), ExtResource("13_anchovy"), ExtResource("14_sardine"), ExtResource("15_roe")]
items = [ExtResource("2_rod"), ExtResource("3_coffee"), ExtResource("4_energy"), ExtResource("5_snack"), ExtResource("6_finder"), ExtResource("7_cooler"), ExtResource("8_art_kit"), ExtResource("9_worms")]

View file

@ -1,14 +0,0 @@
[gd_resource type="Resource" script_class="ItemData" load_steps=2 format=3]
[ext_resource type="Script" path="res://items/item_data.gd" id="1"]
[resource]
script = ExtResource("1")
item_id = &"luminous_roe"
display_name = "luminous roe"
description = "rare glowing roe with the strongest rare catch odds."
category = 2
bait_tags = Array[StringName]([&"bait_rarity_6"])
stackable = true
max_stack = 10
hotbar_allowed = false

View file

@ -1,14 +0,0 @@
[gd_resource type="Resource" script_class="ItemData" load_steps=2 format=3]
[ext_resource type="Script" path="res://items/item_data.gd" id="1"]
[resource]
script = ExtResource("1")
item_id = &"shrimp"
display_name = "shrimp"
description = "lively bait that noticeably improves rare catch chances."
category = 2
bait_tags = Array[StringName]([&"bait_rarity_2"])
stackable = true
max_stack = 10
hotbar_allowed = false

View file

@ -1,14 +0,0 @@
[gd_resource type="Resource" script_class="ItemData" load_steps=2 format=3]
[ext_resource type="Script" path="res://items/item_data.gd" id="1"]
[resource]
script = ExtResource("1")
item_id = &"snails"
display_name = "snails"
description = "slow-moving bait with a better chance for rarer catches."
category = 2
bait_tags = Array[StringName]([&"bait_rarity_1"])
stackable = true
max_stack = 10
hotbar_allowed = false

View file

@ -1,14 +0,0 @@
[gd_resource type="Resource" script_class="ItemData" load_steps=2 format=3]
[ext_resource type="Script" path="res://items/item_data.gd" id="1"]
[resource]
script = ExtResource("1")
item_id = &"squid_chunks"
display_name = "squid chunks"
description = "rich cut bait that strongly favors rarer catches."
category = 2
bait_tags = Array[StringName]([&"bait_rarity_3"])
stackable = true
max_stack = 10
hotbar_allowed = false

View file

@ -1,14 +0,0 @@
[gd_resource type="Resource" script_class="ItemData" load_steps=2 format=3]
[ext_resource type="Script" path="res://items/item_data.gd" id="1"]
[resource]
script = ExtResource("1")
item_id = &"whole_anchovy"
display_name = "whole anchovy"
description = "premium whole bait with high rare catch potential."
category = 2
bait_tags = Array[StringName]([&"bait_rarity_4"])
stackable = true
max_stack = 10
hotbar_allowed = false

View file

@ -1,14 +0,0 @@
[gd_resource type="Resource" script_class="ItemData" load_steps=2 format=3]
[ext_resource type="Script" path="res://items/item_data.gd" id="1"]
[resource]
script = ExtResource("1")
item_id = &"whole_sardine"
display_name = "whole sardine"
description = "valuable whole bait tuned for exceptional catches."
category = 2
bait_tags = Array[StringName]([&"bait_rarity_5"])
stackable = true
max_stack = 10
hotbar_allowed = false

View file

@ -1,16 +1,14 @@
[gd_resource type="Resource" script_class="ItemData" load_steps=3 format=3]
[gd_resource type="Resource" script_class="ItemData" load_steps=2 format=3]
[ext_resource type="Script" path="res://items/item_data.gd" id="1"]
[ext_resource type="Texture2D" path="res://items/icons/bait/64_bait_worms.png" id="2_icon"]
[resource]
script = ExtResource("1")
item_id = &"worms"
display_name = "worms"
description = "basic bait that opens a small chance for rarer catches."
icon = ExtResource("2_icon")
description = "common bait. fills your tacklebox at the fishing shop."
category = 2
bait_tags = Array[StringName]([&"worm", &"bait_rarity_0"])
bait_tags = Array[StringName]([&"worm"])
stackable = true
max_stack = 10
hotbar_allowed = false

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.6 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.6 KiB

View file

@ -36,7 +36,7 @@ var _pending_rewards: Array[Dictionary] = []
var _lifetime_claimed: Array[String] = []
var _total_catches: int = 0
var _total_sold: int = 0
var _daily_clock_hour: float = WorldTimeService.DEFAULT_START_HOUR
var _previous_hour: float = WorldTimeService.DEFAULT_START_HOUR
func setup(
@ -55,8 +55,8 @@ func setup(
_world_time = world_time
_world_weather = world_weather
_session = session
_daily_clock_hour = _world_time.get_time_hours()
_world_time.natural_time_advanced.connect(_on_natural_time_advanced)
_previous_hour = _world_time.get_time_hours()
_world_time.time_changed.connect(_on_time_changed)
_session.state_changed.connect(_on_session_state_changed)
_collection.collection_changed.connect(_on_current_state_changed)
_experience.experience_changed.connect(_on_experience_changed)
@ -86,6 +86,7 @@ func set_save_manager(save_manager: PlayerSaveManager) -> void:
func begin_progression_session() -> void:
_progression_ready = true
_previous_hour = _world_time.get_time_hours()
if _session.is_host():
_activate_host_board()
changed.emit()
@ -154,7 +155,7 @@ func get_time_until_refresh_text() -> String:
if _active_plan_id.is_empty() or _world_time == null:
return "daily jobs unavailable"
var elapsed: float = fposmod(
_daily_clock_hour - DAILY_REFRESH_HOUR,
_world_time.get_time_hours() - DAILY_REFRESH_HOUR,
WorldTimeService.HOURS_PER_DAY,
)
var hours_remaining: float = WorldTimeService.HOURS_PER_DAY - elapsed
@ -184,10 +185,7 @@ func claim(claim_id: String) -> bool:
func get_host_board_network_data() -> Dictionary:
var board := _host_board.duplicate(true)
if not board.is_empty():
board["daily_clock_hour"] = _daily_clock_hour
return board
return _host_board.duplicate(true)
func apply_remote_board(board: Dictionary) -> bool:
@ -200,8 +198,6 @@ func apply_remote_board(board: Dictionary) -> bool:
_daily_progress.clear()
_daily_completions.clear()
_active_board = board.duplicate(true)
if board.has("daily_clock_hour"):
_daily_clock_hour = float(board["daily_clock_hour"])
changed.emit()
return true
@ -227,7 +223,6 @@ func to_save_data() -> Dictionary:
"active_plan_id": _active_plan_id,
"daily_progress": progress,
"daily_completions": completions,
"daily_clock_hour": _daily_clock_hour,
"pending_rewards": _pending_rewards.duplicate(true),
"lifetime_claimed": _lifetime_claimed.duplicate(),
"statistics": {
@ -251,12 +246,6 @@ func restore_from_save_data(data: Dictionary) -> bool:
var completions: Dictionary = data.get("daily_completions", {})
for key: Variant in completions:
_daily_completions[str(key)] = mini(int(completions[key]), 1)
_daily_clock_hour = float(data.get(
"daily_clock_hour",
_world_time.get_time_hours()
if _world_time != null
else WorldTimeService.DEFAULT_START_HOUR,
))
_pending_rewards.clear()
var rewards: Array = data.get("pending_rewards", [])
var restored_daily_rewards: Dictionary[String, bool] = {}
@ -301,7 +290,6 @@ func reset_to_defaults() -> void:
_lifetime_claimed.clear()
_total_catches = 0
_total_sold = 0
_daily_clock_hour = WorldTimeService.DEFAULT_START_HOUR
changed.emit()
@ -312,7 +300,6 @@ static func default_save_data() -> Dictionary:
"active_plan_id": "",
"daily_progress": {},
"daily_completions": {},
"daily_clock_hour": WorldTimeService.DEFAULT_START_HOUR,
"pending_rewards": [],
"lifetime_claimed": [],
"statistics": {"fish_caught": 0, "fish_sold": 0},
@ -376,16 +363,6 @@ static func validate_save_data(value: Variant) -> bool:
var host_board: Dictionary = data.get("host_board", {})
if not host_board.is_empty() and not validate_board(host_board):
return false
if (
data.has("daily_clock_hour")
and (
typeof(data["daily_clock_hour"]) not in [TYPE_FLOAT, TYPE_INT]
or not is_finite(float(data["daily_clock_hour"]))
or float(data["daily_clock_hour"]) < 0.0
or float(data["daily_clock_hour"]) >= WorldTimeService.HOURS_PER_DAY
)
):
return false
var progress: Dictionary = data.get("daily_progress", {})
if progress.size() > 8:
return false
@ -498,22 +475,19 @@ func _apply_host_weather_plan() -> void:
)
func _on_natural_time_advanced(hours: float) -> void:
if not is_finite(hours) or hours <= 0.0:
return
var previous_hour := _daily_clock_hour
_daily_clock_hour = fposmod(
_daily_clock_hour + hours,
WorldTimeService.HOURS_PER_DAY,
)
func _on_time_changed(
time_hours: float,
_phase: WorldTimeService.Phase,
) -> void:
if (
_progression_ready
and _session != null
and _session.is_host()
and _crossed_daily_refresh(previous_hour, _daily_clock_hour)
and _crossed_daily_refresh(_previous_hour, time_hours)
):
var cycle: int = int(_host_board.get("cycle", -1)) + 1
_generate_host_board(maxi(cycle, 0))
_previous_hour = time_hours
func _on_session_state_changed(state: NetworkSession.State) -> void:

View file

@ -91,7 +91,6 @@ const WorldWeatherServiceType = preload("res://world/world_weather_service.gd")
const NetworkWorldWeatherServiceType = preload(
"res://network/network_world_weather_service.gd"
)
const RainAmbienceType = preload("res://world/rain_ambience.gd")
const PlayerJobServiceType = preload("res://jobs/player_job_service.gd")
const NetworkJobServiceType = preload("res://network/network_job_service.gd")
@ -211,15 +210,10 @@ var _pending_existing_root_path := ""
var _local_recovery_attempt_id: String = ""
@onready var _shoreline_ambience: ShorelineAmbience = %ShorelineAmbience
var _rain_ambience: RainAmbienceType
func _ready() -> void:
DisplayServer.window_set_title("NETfishing")
_rain_ambience = RainAmbienceType.new()
_rain_ambience.name = "RainAmbience"
add_child(_rain_ambience)
_rain_ambience.configure(_world_weather)
_shoreline_ambience.configure(
_player,
_test_world.get_saltwater_shoreline_mesh(),
@ -1102,16 +1096,7 @@ func _unhandled_input(event: InputEvent) -> void:
func _process(_delta: float) -> void:
var show_shop_prompt := _can_show_shop_prompt()
var shop_prompt_anchor := (
_shop_interaction.get_prompt_anchor_position()
if _shop_interaction != null
else Vector3.ZERO
)
_game_ui.set_shop_prompt_visible(
show_shop_prompt,
shop_prompt_anchor,
)
_game_ui.set_shop_prompt_visible(_can_show_shop_prompt())
func _apply_runtime_settings(settings: PlayerSettingsType) -> void:
@ -1166,7 +1151,6 @@ func _reset_pixelation() -> void:
func _set_gameplay_active(active: bool) -> void:
_gameplay_started = active
_shoreline_ambience.set_active(active)
_rain_ambience.set_active(active)
_title_background.visible = not active
_world_pixelation.set_gameplay_active(active)
_ui_pixelation.set_gameplay_active(active)

File diff suppressed because one or more lines are too long

View file

@ -79,21 +79,6 @@ func send_local_message(body: String) -> bool:
return true
func broadcast_system_message(body: String) -> bool:
if _session == null or not _session.is_host():
return false
var clean := NetworkChatProtocol.sanitize_body(body)
if clean.is_empty():
return false
_broadcast(_make_message(
NetworkChatProtocol.Kind.SYSTEM,
0,
"",
clean,
))
return true
func get_history() -> Array[Dictionary]:
var result: Array[Dictionary] = []
for message: Dictionary in _history:

View file

@ -992,7 +992,6 @@ func _capture_assets() -> Dictionary:
return {
"wallet": _wallet.get_balance(),
"bag": _bag.get_all_items(),
"unlocked_bait_ids": _bag.get_unlocked_bait_ids(),
"catches": _inventory.get_all_catches(),
"next_sequence": _inventory.get_next_catch_sequence(),
"discovered": _collection_log.get_discovered_ids(),
@ -1003,7 +1002,6 @@ func _capture_assets() -> Dictionary:
func _restore_assets(snapshot: Dictionary) -> void:
_wallet.restore_balance(int(snapshot["wallet"]))
_bag.replace_all_items(snapshot["bag"])
_bag.replace_unlocked_bait_ids(snapshot["unlocked_bait_ids"])
_inventory.replace_all_catches(
snapshot["catches"], int(snapshot["next_sequence"])
)

View file

@ -2,13 +2,8 @@ class_name NetworkProfilePreferences
extends Node
const FORMAT_VERSION: int = 1
const VoiceProfilesType = preload(
"res://player/animalese_voice_profiles.gd"
)
var profile_id: String = ""
var display_name: String = "Player"
var voice_id: String = VoiceProfilesType.DEFAULT_ID
var speech_speed_id: String = VoiceProfilesType.DEFAULT_SPEED_ID
var created_at_unix: int = 0
var _profile_path := ""
var _expected_hash := ""
@ -53,39 +48,19 @@ func load_or_create() -> bool:
Time.get_ticks_usec(),
]
display_name = "Player"
voice_id = VoiceProfilesType.DEFAULT_ID
speech_speed_id = VoiceProfilesType.DEFAULT_SPEED_ID
created_at_unix = int(Time.get_unix_time_from_system())
return _save_atomic()
func set_display_name(value: String) -> bool:
return set_profile_identity(value, voice_id, speech_speed_id)
func set_profile_identity(
value: String,
selected_voice_id: String,
selected_speech_speed_id: String,
) -> bool:
var clean_name: String = value.strip_edges()
if (
not is_valid_display_name(clean_name)
or not VoiceProfilesType.is_valid(selected_voice_id)
or not VoiceProfilesType.is_valid_speed(selected_speech_speed_id)
):
if not is_valid_display_name(clean_name):
return false
var previous_name: String = display_name
var previous_voice_id: String = voice_id
var previous_speech_speed_id: String = speech_speed_id
var previous: String = display_name
display_name = clean_name
voice_id = selected_voice_id
speech_speed_id = selected_speech_speed_id
if _save_atomic():
return true
display_name = previous_name
voice_id = previous_voice_id
speech_speed_id = previous_speech_speed_id
display_name = previous
return false
@ -134,12 +109,6 @@ func _load_existing() -> bool:
return false
profile_id = loaded_id
display_name = loaded_name
voice_id = VoiceProfilesType.sanitized_id(
str(data.get("voice_id", VoiceProfilesType.DEFAULT_ID))
)
speech_speed_id = VoiceProfilesType.sanitized_speed_id(
str(data.get("speech_speed_id", VoiceProfilesType.DEFAULT_SPEED_ID))
)
created_at_unix = int(data["created_at_unix"])
return true
@ -149,8 +118,6 @@ func _save_atomic() -> bool:
"format_version": FORMAT_VERSION,
"profile_id": profile_id,
"display_name": display_name,
"voice_id": voice_id,
"speech_speed_id": speech_speed_id,
"created_at_unix": created_at_unix,
}
var result := PortableFileGuard.write_guarded(

View file

@ -1,10 +1,6 @@
class_name NetworkProfileService
extends Node
const VoiceProfilesType = preload(
"res://player/animalese_voice_profiles.gd"
)
signal conflict_result(
request_id: String,
has_conflict: bool,
@ -22,8 +18,6 @@ var _preferences: NetworkProfilePreferences
var _appearance_store: PlayerAppearanceStore
var _spawn_service: PlayerSpawnService
var _pending_apply: Dictionary[String, Dictionary] = {}
var _pending_voice_ids: Dictionary[String, String] = {}
var _pending_speech_speed_ids: Dictionary[String, String] = {}
var _host_pending_apply: Dictionary[String, Dictionary] = {}
var _latest_check_id: String = ""
var _latest_check_name: String = ""
@ -46,7 +40,6 @@ func setup(
_session.join_authenticated.connect(_on_join_authenticated)
_session.state_changed.connect(_on_session_state_changed)
_apply_to_avatar(1, _appearance_store.get_snapshot())
_apply_local_voice_to_avatar()
func get_persisted_name() -> String:
@ -57,14 +50,6 @@ func get_persisted_appearance() -> Dictionary:
return _appearance_store.get_snapshot()
func get_persisted_voice_id() -> String:
return _preferences.voice_id
func get_persisted_speech_speed_id() -> String:
return _preferences.speech_speed_id
func get_identity_fingerprint() -> String:
return (
_session.get_local_identity_fingerprint()
@ -102,15 +87,11 @@ func apply_profile(
display_name: String,
appearance: Dictionary,
use_anyway: bool,
voice_id: String = VoiceProfilesType.DEFAULT_ID,
speech_speed_id: String = VoiceProfilesType.DEFAULT_SPEED_ID,
) -> bool:
var clean_name := display_name.strip_edges()
if (
not NetworkProfilePreferences.is_valid_display_name(clean_name)
or not CharacterCustomizationCatalog.validate_snapshot(appearance)
or not VoiceProfilesType.is_valid(voice_id)
or not VoiceProfilesType.is_valid_speed(speech_speed_id)
):
apply_finished.emit(false, "Check the player name and appearance choices.")
return false
@ -127,8 +108,6 @@ func apply_profile(
"profile_update", NetworkProfileProtocol.signature_fields(request)
)
_pending_apply[request_id] = request
_pending_voice_ids[request_id] = voice_id
_pending_speech_speed_ids[request_id] = speech_speed_id
if _session == null or not _session.is_gameplay_session_active():
_apply_local_result(request_id, true, "", false, PackedStringArray())
elif _session.is_host():
@ -285,46 +264,20 @@ func _apply_local_result(
return
if not accepted:
_pending_apply.erase(request_id)
_pending_voice_ids.erase(request_id)
_pending_speech_speed_ids.erase(request_id)
conflict_result.emit(request_id, conflict, suggestions)
apply_finished.emit(false, message)
return
var previous_name := _preferences.display_name
var previous_voice_id := _preferences.voice_id
var previous_speech_speed_id := _preferences.speech_speed_id
var requested_voice_id: String = _pending_voice_ids.get(
request_id,
VoiceProfilesType.DEFAULT_ID,
)
var requested_speech_speed_id: String = _pending_speech_speed_ids.get(
request_id,
VoiceProfilesType.DEFAULT_SPEED_ID,
)
if not _preferences.set_profile_identity(
str(request["display_name"]),
requested_voice_id,
requested_speech_speed_id,
):
if not _preferences.set_display_name(str(request["display_name"])):
_pending_apply.erase(request_id)
_pending_voice_ids.erase(request_id)
_pending_speech_speed_ids.erase(request_id)
apply_finished.emit(false, "Profile could not be saved.")
return
if not _appearance_store.save_snapshot(request["appearance"]):
_preferences.set_profile_identity(
previous_name,
previous_voice_id,
previous_speech_speed_id,
)
_preferences.set_display_name(previous_name)
_pending_apply.erase(request_id)
_pending_voice_ids.erase(request_id)
_pending_speech_speed_ids.erase(request_id)
apply_finished.emit(false, "Profile could not be saved.")
return
_pending_apply.erase(request_id)
_pending_voice_ids.erase(request_id)
_pending_speech_speed_ids.erase(request_id)
if _session != null and _session.is_gameplay_session_active():
if _session.is_host():
_session.apply_canonical_profile(
@ -351,7 +304,6 @@ func _apply_local_result(
_session.get_local_peer_id() if _session != null else 1,
_appearance_store.get_snapshot(),
)
_apply_local_voice_to_avatar()
apply_finished.emit(true, "Profile saved.")
@ -409,7 +361,6 @@ func _on_join_authenticated() -> void:
_apply_to_avatar(
_session.get_local_peer_id(), _appearance_store.get_snapshot()
)
_apply_local_voice_to_avatar()
func _on_peer_removed(_peer_id: int) -> void:
@ -449,8 +400,6 @@ func _refresh_latest_check() -> void:
func _on_session_state_changed(state: NetworkSession.State) -> void:
if state == NetworkSession.State.INACTIVE:
_pending_apply.clear()
_pending_voice_ids.clear()
_pending_speech_speed_ids.clear()
_host_pending_apply.clear()
_latest_check_id = ""
_latest_check_name = ""
@ -516,16 +465,5 @@ func _apply_to_avatar(peer_id: int, appearance: Dictionary) -> void:
avatar.apply_appearance_snapshot(appearance)
func _apply_local_voice_to_avatar() -> void:
if _spawn_service == null:
return
var local_peer_id := (
_session.get_local_peer_id() if _session != null else 1
)
var avatar := _spawn_service.get_avatar(local_peer_id)
if avatar != null:
avatar.apply_animalese_voice_id(_preferences.voice_id)
func _new_id() -> String:
return Crypto.new().generate_random_bytes(16).hex_encode()

View file

@ -56,11 +56,6 @@ static func validate_request(data: Variant) -> String:
var quantity: int = payload["quantity"]
var wallet_balance: int = payload["wallet_balance"]
var current_state: int = payload["current_state"]
if (
payload.has("bait_unlocked")
and typeof(payload["bait_unlocked"]) != TYPE_BOOL
):
return "Purchase could not be completed."
if (
request_id.is_empty()
or request_id.length() > MAX_ID_LENGTH

View file

@ -109,15 +109,7 @@ func is_local_purchase_pending() -> bool:
func request_supply(item_id: StringName) -> String:
var owned: int = _bag.get_quantity(item_id) if _bag != null else 0
var item: ItemDataType = (
_item_catalog.get_item_by_id(item_id)
if _item_catalog != null else null
)
var quantity: int = FishingShopStockType.get_purchase_quantity(
item_id,
owned,
item.max_stack if item != null else FishingShopStockType.WORM_MAX_STACK,
)
var quantity: int = FishingShopStockType.get_purchase_quantity(item_id, owned)
return _request_purchase(
item_id,
NetworkShopProtocol.ProductCategory.SUPPLY,
@ -231,13 +223,6 @@ func _request_purchase(
"wallet_balance": _wallet.get_balance() if _wallet != null else 0,
"current_state": current_state,
}
if (
category == NetworkShopProtocol.ProductCategory.SUPPLY
and FishingShopStockType.is_bait_topoff(product_id)
):
request["bait_unlocked"] = (
_bag != null and _bag.is_bait_unlocked(product_id)
)
_pending_local_request = request.duplicate(true)
local_purchase_pending.emit(request_id)
if _session.is_host():
@ -332,18 +317,16 @@ func _build_authoritative_result(
var item: ItemDataType = _item_catalog.get_item_by_id(
product_id
) if _item_catalog != null else null
var bait_topoff: bool = (
FishingShopStockType.is_bait_topoff(product_id)
)
cost = FishingShopStockType.get_price(product_id)
if (
item == null
or not item.is_valid()
or product_id not in (
FishingShopStockType.get_stock_item_ids()
)
or (item.category != ItemDataType.Category.CONSUMABLE and not bait_topoff)
or (item.category != ItemDataType.Category.CONSUMABLE and not FishingShopStockType.is_bait_topoff(product_id))
or not item.stackable
or (not item.usable and not bait_topoff)
or (not item.usable and not FishingShopStockType.is_bait_topoff(product_id))
or current_state >= item.max_stack
or current_state + quantity > item.max_stack
):
@ -353,21 +336,10 @@ func _build_authoritative_result(
else "Purchase could not be completed."
)
else:
var expected_quantity: int = (
FishingShopStockType.get_purchase_quantity(
product_id, current_state, item.max_stack
)
)
if quantity != expected_quantity:
if not FishingShopStockType.is_bait_topoff(product_id) and quantity != 1:
rejection = "Purchase could not be completed."
else:
cost = FishingShopStockType.get_purchase_cost(
product_id,
quantity,
bool(request.get("bait_unlocked", true)),
)
if cost < 0:
rejection = "Purchase could not be completed."
cost *= quantity
resulting_state = current_state + quantity
NetworkShopProtocol.ProductCategory.ROD:
rejection = "This item is not sold here."
@ -560,9 +532,6 @@ func _apply_purchase_result(data: Dictionary) -> void:
return
var wallet_snapshot: int = _wallet.get_balance()
var bag_snapshot: Array[OwnedItemType] = _bag.get_all_items()
var bait_unlock_snapshot: Array[StringName] = (
_bag.get_unlocked_bait_ids()
)
var reel_snapshot: int = _upgrades.get_reel_speed_level()
var barrier_snapshot: int = _upgrades.get_barrier_power_level()
var cooler_snapshot: int = _cooler_capacity.get_level()
@ -570,7 +539,6 @@ func _apply_purchase_result(data: Dictionary) -> void:
var applied: bool = _apply_local_product(data)
if not applied or not _save_manager.save_if_dirty():
_bag.replace_all_items(bag_snapshot)
_bag.replace_unlocked_bait_ids(bait_unlock_snapshot)
_upgrades.restore_levels(reel_snapshot, barrier_snapshot)
_cooler_capacity.restore_level(cooler_snapshot)
_art_unlocks.restore_mask(art_snapshot)
@ -622,22 +590,9 @@ func _validate_local_result(data: Dictionary) -> String:
return "Not enough fish coin."
match category:
NetworkShopProtocol.ProductCategory.SUPPLY:
var item: ItemDataType = (
_item_catalog.get_item_by_id(product_id)
if _item_catalog != null else null
)
var bait_unlocked: bool = _bag.is_bait_unlocked(product_id)
if (
item == null
or _bag.get_quantity(product_id) != expected_state
or int(data["quantity"]) != FishingShopStockType.get_purchase_quantity(
product_id, expected_state, item.max_stack
)
):
if _bag.get_quantity(product_id) != expected_state:
return "Purchase could not be completed."
if cost != FishingShopStockType.get_purchase_cost(
product_id, int(data["quantity"]), bait_unlocked
):
if cost != FishingShopStockType.get_price(product_id) * int(data["quantity"]):
return "Purchase could not be completed."
if not _bag.can_add_item(product_id, data["quantity"]):
return "Your Bag is full."

View file

@ -17,12 +17,6 @@ func setup(session: NetworkSession, world_time: WorldTimeService) -> void:
_world_time = world_time
_session.state_changed.connect(_on_session_state_changed)
_session.peer_authenticated.connect(_on_peer_authenticated)
if not _world_time.authoritative_time_set.is_connected(
_on_authoritative_time_set
):
_world_time.authoritative_time_set.connect(
_on_authoritative_time_set
)
set_process(true)
@ -88,13 +82,6 @@ func _on_peer_authenticated(peer_id: int, _display_name: String) -> void:
_send_snapshot(peer_id)
func _on_authoritative_time_set(_time_hours: float) -> void:
if _session == null or not _session.is_host():
return
_sync_elapsed = 0.0
_broadcast_snapshot()
func _broadcast_snapshot() -> void:
if _session == null or not _session.is_host():
return

View file

@ -1,57 +0,0 @@
class_name AnimaleseVoiceProfiles
extends RefCounted
const DEFAULT_ID: String = "natural"
const DEFAULT_SPEED_ID: String = "normal"
const OPTIONS: Array[Dictionary] = [
{"id": "tiny", "label": "tiny", "pitch": 1.24},
{"id": "bright", "label": "bright", "pitch": 1.10},
{"id": "natural", "label": "natural", "pitch": 1.0},
{"id": "mellow", "label": "mellow", "pitch": 0.89},
{"id": "deep", "label": "deep", "pitch": 0.77},
]
const SPEED_OPTIONS: Array[Dictionary] = [
{"id": "slow", "label": "slow", "characters_per_second": 20.0},
{"id": "relaxed", "label": "relaxed", "characters_per_second": 24.0},
{"id": "normal", "label": "normal", "characters_per_second": 28.0},
{"id": "quick", "label": "quick", "characters_per_second": 34.0},
{"id": "rapid", "label": "rapid", "characters_per_second": 40.0},
]
static func is_valid(voice_id: String) -> bool:
for option: Dictionary in OPTIONS:
if str(option.get("id", "")) == voice_id:
return true
return false
static func sanitized_id(voice_id: String) -> String:
return voice_id if is_valid(voice_id) else DEFAULT_ID
static func pitch_for(voice_id: String) -> float:
var resolved_id := sanitized_id(voice_id)
for option: Dictionary in OPTIONS:
if str(option.get("id", "")) == resolved_id:
return float(option.get("pitch", 1.0))
return 1.0
static func is_valid_speed(speed_id: String) -> bool:
for option: Dictionary in SPEED_OPTIONS:
if str(option.get("id", "")) == speed_id:
return true
return false
static func sanitized_speed_id(speed_id: String) -> String:
return speed_id if is_valid_speed(speed_id) else DEFAULT_SPEED_ID
static func speed_for(speed_id: String) -> float:
var resolved_id := sanitized_speed_id(speed_id)
for option: Dictionary in SPEED_OPTIONS:
if str(option.get("id", "")) == resolved_id:
return float(option.get("characters_per_second", 28.0))
return 28.0

View file

@ -1 +0,0 @@
uid://dlut32tq2v66j

View file

@ -50,7 +50,6 @@ const CONTROLLER_ZOOM_TRIGGER_AXIS: JoyAxis = JOY_AXIS_TRIGGER_LEFT
var appearance_snapshot: Dictionary = (
CharacterCustomizationCatalog.default_snapshot()
)
var animalese_voice_id: String = "natural"
var active_bait_id: StringName = StringName()
signal active_bait_changed(item_id: StringName)
@ -77,14 +76,6 @@ func apply_appearance_snapshot(snapshot: Dictionary) -> void:
appearance_snapshot = snapshot.duplicate(true)
PlayerVisualPresenter.apply_appearance(_visuals, appearance_snapshot)
func apply_animalese_voice_id(voice_id: String) -> void:
animalese_voice_id = voice_id
func get_animalese_voice_id() -> String:
return animalese_voice_id
class ShowcaseCameraSnapshot:
extends RefCounted
@ -138,8 +129,6 @@ class ShowcaseCameraSnapshot:
@export var zoom_smoothing: float = 12.0
@export_range(0.1, 12.0, 0.1) var controller_zoom_speed: float = 4.0
@export_range(0.0, 1.0, 0.01) var controller_trigger_threshold: float = 0.55
@export_range(1.0, 40.0, 0.5) var free_camera_speed: float = 10.0
@export_range(1.0, 10.0, 0.5) var free_camera_sprint_multiplier: float = 3.0
@onready var _visuals: Node3D = %Visuals
@onready var _character_rig: Node3D = get_node_or_null(
@ -152,7 +141,6 @@ class ShowcaseCameraSnapshot:
@onready var _camera_pitch: Node3D = %CameraPitch
@onready var _spring_arm: SpringArm3D = %SpringArm3D
@onready var _camera: Camera3D = %Camera3D
@onready var _player_collision_shape: CollisionShape3D = $CollisionShape3D
@onready var inventory: FishInventoryType = %Inventory
@onready var collection_log: CollectionLogType = %CollectionLog
@onready var wallet: PlayerWalletType = %Wallet
@ -177,11 +165,6 @@ var _catch_attachment_offset: Vector3 = Vector3(0.0, 0.08, 0.04)
var _gravity: float = float(ProjectSettings.get_setting("physics/3d/default_gravity"))
var _camera_dragging: bool = false
var _camera_input_enabled: bool = true
var _free_camera_active: bool = false
var _free_camera_body: CharacterBody3D
var _free_camera: Camera3D
var _free_camera_yaw: float = 0.0
var _free_camera_pitch: float = 0.0
var _movement_enabled: bool = true
var _water_recovery_active: bool = false
var _remote_recovery_presentation_active: bool = false
@ -214,8 +197,6 @@ var _network_target_visual_yaw: float = 0.0
var _network_snapshot_ready: bool = false
var _character_animation_name: StringName = &""
var _sitting: bool = false
var _sitting_intent_pending: bool = false
var _sitting_intent_sequence: int = -1
var _held_fish_visible: bool = false
var _showcase_animation_active: bool = false
var _fishing_rod: Node3D
@ -224,8 +205,6 @@ var _controller_mapping_manager: ControllerMappingManagerType
func _ready() -> void:
if not bag.contents_changed.is_connected(_on_bag_contents_changed):
bag.contents_changed.connect(_on_bag_contents_changed)
PlayerVisualPresenter.apply_appearance(_visuals, appearance_snapshot)
_initialize_fishing_rod()
_target_zoom = clampf(_spring_arm.spring_length, minimum_zoom, maximum_zoom)
@ -234,14 +213,6 @@ func _ready() -> void:
_initialize_held_item_attachment()
func _on_bag_contents_changed() -> void:
if (
not active_bait_id.is_empty()
and bag.get_quantity(active_bait_id) <= 0
):
unequip_bait()
func set_controller_mapping_manager(
mapping_manager: ControllerMappingManagerType,
) -> void:
@ -296,18 +267,8 @@ func _physics_process(delta: float) -> void:
if _network_interpolation_enabled:
_update_network_interpolation(delta)
return
if local_control_enabled and _free_camera_active:
_update_free_camera_physics()
velocity.x = 0.0
velocity.z = 0.0
if not is_on_floor():
velocity.y -= _gravity * fall_gravity_multiplier * delta
move_and_slide()
_network_jump_pending = false
return
var jump_requested: bool = (
_movement_enabled
and not _free_camera_active
and (
(local_control_enabled and Input.is_action_just_pressed("jump"))
or (_network_authoritative_simulation and _network_jump_pending)
@ -315,7 +276,7 @@ func _physics_process(delta: float) -> void:
)
if _sitting:
if jump_requested:
_set_sitting(false, local_control_enabled)
_set_sitting(false)
else:
velocity = Vector3.ZERO
_network_jump_pending = false
@ -409,8 +370,6 @@ func _process(delta: float) -> void:
)
if not local_control_enabled:
return
if _free_camera_active:
return
if (
_camera_input_enabled
@ -560,7 +519,7 @@ func toggle_sitting() -> void:
])
):
return
_set_sitting(should_sit, local_control_enabled)
_set_sitting(should_sit)
func _has_any_character_animation(candidates: Array[StringName]) -> bool:
@ -572,16 +531,10 @@ func _has_any_character_animation(candidates: Array[StringName]) -> bool:
return false
func _set_sitting(
should_sit: bool,
is_local_intent: bool = false,
) -> void:
func _set_sitting(should_sit: bool) -> void:
if _sitting == should_sit:
return
_sitting = should_sit
if is_local_intent:
_sitting_intent_pending = true
_sitting_intent_sequence = -1
if _sitting:
velocity = Vector3.ZERO
_character_animation_name = &""
@ -595,10 +548,6 @@ func is_sitting() -> bool:
func _unhandled_input(event: InputEvent) -> void:
if not local_control_enabled or not _camera_input_enabled:
return
if event.is_action_pressed("toggle_free_camera"):
_set_free_camera_active(not _free_camera_active)
get_viewport().set_input_as_handled()
return
if event.is_action("camera_drag"):
_camera_dragging = event.is_pressed()
@ -606,14 +555,9 @@ func _unhandled_input(event: InputEvent) -> void:
return
if event is InputEventMouseMotion and _camera_dragging:
if _free_camera_active:
_rotate_free_camera(event.relative * mouse_sensitivity)
else:
_rotate_camera(event.relative * mouse_sensitivity)
_rotate_camera(event.relative * mouse_sensitivity)
get_viewport().set_input_as_handled()
return
if _free_camera_active:
return
var mouse_zoom_in: bool = (
event is InputEventMouseButton
@ -673,98 +617,6 @@ func _rotate_camera(delta_rotation: Vector2) -> void:
)
func _set_free_camera_active(active: bool) -> void:
if _free_camera_active == active:
return
if active:
var camera_transform: Transform3D = _camera.global_transform
var duplicated_camera := _camera.duplicate() as Camera3D
if duplicated_camera == null:
return
var camera_body := CharacterBody3D.new()
camera_body.name = "FreeCameraBody"
camera_body.collision_layer = 0
camera_body.collision_mask = collision_mask
camera_body.motion_mode = CharacterBody3D.MOTION_MODE_FLOATING
add_child(camera_body)
camera_body.top_level = true
camera_body.global_position = camera_transform.origin
var camera_collision := CollisionShape3D.new()
camera_collision.name = "FreeCameraCollision"
camera_collision.shape = _player_collision_shape.shape
camera_body.add_child(camera_collision)
duplicated_camera.name = "FreeCamera"
duplicated_camera.unique_name_in_owner = false
camera_body.add_child(duplicated_camera)
duplicated_camera.transform = Transform3D(
camera_transform.basis,
Vector3.ZERO,
)
_free_camera_body = camera_body
_free_camera = duplicated_camera
var camera_rotation: Vector3 = _free_camera.global_rotation
_free_camera_yaw = camera_rotation.y
_free_camera_pitch = camera_rotation.x
_camera.current = false
_free_camera.current = true
_free_camera_active = true
return
_free_camera_active = false
_camera_dragging = false
if _free_camera != null:
_free_camera.current = false
_free_camera = null
if _free_camera_body != null:
_free_camera_body.queue_free()
_free_camera_body = null
_camera.current = local_control_enabled
func _update_free_camera_physics() -> void:
if _free_camera == null or _free_camera_body == null:
_set_free_camera_active(false)
return
var input_vector: Vector2 = Input.get_vector(
"move_left",
"move_right",
"move_forward",
"move_backward",
)
var vertical_input: float = (
Input.get_action_strength("jump")
- Input.get_action_strength("sneak")
)
var movement: Vector3 = (
_free_camera.global_basis.x * input_vector.x
+ _free_camera.global_basis.z * input_vector.y
+ Vector3.UP * vertical_input
)
if movement.length_squared() > 1.0:
movement = movement.normalized()
var movement_speed: float = free_camera_speed
if Input.is_action_pressed("sprint"):
movement_speed *= free_camera_sprint_multiplier
_free_camera_body.velocity = movement * movement_speed
_free_camera_body.move_and_slide()
func _rotate_free_camera(delta_rotation: Vector2) -> void:
if _free_camera == null:
return
_free_camera_yaw -= delta_rotation.x
var vertical_direction: float = -1.0 if invert_camera_y else 1.0
_free_camera_pitch = clampf(
_free_camera_pitch - delta_rotation.y * vertical_direction,
deg_to_rad(minimum_pitch_degrees),
deg_to_rad(maximum_pitch_degrees),
)
_free_camera.global_rotation = Vector3(
_free_camera_pitch,
_free_camera_yaw,
0.0,
)
func _set_target_zoom(value: float) -> void:
_target_zoom = clampf(value, minimum_zoom, maximum_zoom)
@ -781,8 +633,6 @@ func _apply_axis_deadzone(value: float) -> float:
func set_local_control(enabled: bool) -> void:
if not enabled and _free_camera_active:
_set_free_camera_active(false)
local_control_enabled = enabled
if is_node_ready():
_camera.current = enabled
@ -807,9 +657,7 @@ func configure_network_remote(authoritative_simulation: bool) -> void:
func capture_network_input(sequence: int) -> Dictionary:
if _sitting_intent_pending and _sitting_intent_sequence < 0:
_sitting_intent_sequence = sequence
if not _movement_enabled or _water_recovery_active or _free_camera_active:
if not _movement_enabled or _water_recovery_active:
return {
"sequence": sequence,
"axis": [0.0, 0.0],
@ -886,17 +734,7 @@ func apply_local_prediction_correction(snapshot: Dictionary) -> void:
var parsed: Dictionary = _parse_network_snapshot(snapshot)
if parsed.is_empty():
return
var acknowledged_input: int = parsed["acknowledged_input"]
var sitting_intent_acknowledged: bool = (
_sitting_intent_pending
and _sitting_intent_sequence >= 0
and acknowledged_input >= _sitting_intent_sequence
)
if sitting_intent_acknowledged:
_sitting_intent_pending = false
_sitting_intent_sequence = -1
if not _sitting_intent_pending:
_set_sitting(bool(parsed["sitting"]))
_set_sitting(bool(parsed["sitting"]))
var authoritative_position: Vector3 = parsed["position"]
var error_distance: float = global_position.distance_to(
authoritative_position
@ -942,17 +780,6 @@ func _parse_network_snapshot(snapshot: Dictionary) -> Dictionary:
float(network_velocity[2])
)
var visual_yaw: float = float(snapshot.get("visual_yaw", 0.0))
if (
snapshot.has("acknowledged_input")
and (
typeof(snapshot.get("acknowledged_input")) != TYPE_INT
or int(snapshot.get("acknowledged_input")) < 0
)
):
return {}
var acknowledged_input: int = int(
snapshot.get("acknowledged_input", 0)
)
if snapshot.has("sitting") and typeof(snapshot.get("sitting")) != TYPE_BOOL:
return {}
var sitting: bool = bool(snapshot.get("sitting", false))
@ -966,7 +793,6 @@ func _parse_network_snapshot(snapshot: Dictionary) -> Dictionary:
"position": parsed_position,
"velocity": parsed_velocity,
"visual_yaw": visual_yaw,
"acknowledged_input": acknowledged_input,
"sitting": sitting,
}
@ -1009,10 +835,7 @@ func set_camera_input_enabled(enabled: bool) -> void:
func set_camera_active(active: bool) -> void:
if _free_camera_active and _free_camera != null:
_free_camera.current = active
else:
_camera.current = active
_camera.current = active
func apply_camera_settings(

View file

@ -43,7 +43,6 @@ class LoadSnapshot:
var wallet_balance: int = 0
var next_catch_sequence: int = 1
var bag_items: Array[OwnedItemType] = []
var unlocked_bait_ids: Array[StringName] = []
var hotbar_slots: Array[StringName] = []
var fish_hotbar_slots: Array[StringName] = []
var selected_hotbar_slot: int = 0
@ -259,10 +258,7 @@ func load_player_data() -> bool:
var wallet_restored: bool = _wallet.restore_balance(
snapshot.wallet_balance
)
var bag_restored: bool = (
_bag.replace_all_items(snapshot.bag_items)
and _bag.replace_unlocked_bait_ids(snapshot.unlocked_bait_ids)
)
var bag_restored: bool = _bag.replace_all_items(snapshot.bag_items)
var hotbar_restored: bool = _hotbar.replace_state(
snapshot.hotbar_slots,
snapshot.selected_hotbar_slot,
@ -496,12 +492,6 @@ func _build_save_dictionary() -> Dictionary:
if owned == null or not owned.is_valid():
return {}
serialized_items.append(owned.to_save_dict())
var serialized_unlocked_baits: Array[String] = []
for item_id: StringName in _bag.get_unlocked_bait_ids():
var item_data = _item_catalog.get_item_by_id(item_id)
if item_data == null or not item_data.is_valid() or not item_data.is_bait():
return {}
serialized_unlocked_baits.append(String(item_id))
var serialized_slots: Array[String] = []
for item_id: StringName in _hotbar.get_slots():
serialized_slots.append(String(item_id))
@ -534,7 +524,6 @@ func _build_save_dictionary() -> Dictionary:
},
"bag": {
"items": serialized_items,
"unlocked_bait_ids": serialized_unlocked_baits,
},
"hotbar": {
"selected_slot": _hotbar.get_selected_slot(),
@ -600,10 +589,6 @@ func _build_load_snapshot(save_data: Dictionary) -> LoadSnapshot:
or typeof(inventory_data.get("catches")) != TYPE_ARRAY
or not inventory_data.has("next_catch_sequence")
or typeof(bag_data.get("items")) != TYPE_ARRAY
or (
bag_data.has("unlocked_bait_ids")
and typeof(bag_data.get("unlocked_bait_ids")) != TYPE_ARRAY
)
or typeof(hotbar_data.get("slots")) != TYPE_ARRAY
or not hotbar_data.has("selected_slot")
or not experience_data.has("total_experience")
@ -796,34 +781,6 @@ func _build_load_snapshot(save_data: Dictionary) -> LoadSnapshot:
seen_items[item_id] = true
snapshot.bag_items.append(owned)
var seen_unlocked_baits: Dictionary[StringName, bool] = {}
if bag_data.has("unlocked_bait_ids"):
var unlocked_bait_values: Array = bag_data["unlocked_bait_ids"]
for value: Variant in unlocked_bait_values:
if typeof(value) not in [TYPE_STRING, TYPE_STRING_NAME]:
continue
var bait_id: StringName = StringName(str(value))
var bait_data = _item_catalog.get_item_by_id(bait_id)
if (
bait_id.is_empty()
or seen_unlocked_baits.has(bait_id)
or bait_data == null
or not bait_data.is_valid()
or not bait_data.is_bait()
):
continue
seen_unlocked_baits[bait_id] = true
snapshot.unlocked_bait_ids.append(bait_id)
for owned: OwnedItemType in snapshot.bag_items:
var owned_item_data = _item_catalog.get_item_by_id(owned.item_id)
if (
owned_item_data != null
and owned_item_data.is_bait()
and not seen_unlocked_baits.has(owned.item_id)
):
seen_unlocked_baits[owned.item_id] = true
snapshot.unlocked_bait_ids.append(owned.item_id)
var slot_values: Array = hotbar_data["slots"]
snapshot.hotbar_slots.resize(PlayerHotbarType.SLOT_COUNT)
snapshot.hotbar_slots.fill(StringName())
@ -1141,8 +1098,6 @@ func _restore_defaults() -> void:
)
_wallet.restore_balance(0)
_bag.replace_all_items(default_items)
var default_unlocked_baits: Array[StringName] = []
_bag.replace_unlocked_bait_ids(default_unlocked_baits)
_hotbar.replace_state(default_slots, 0)
_fishing_upgrades.reset_to_defaults()
_cooler_capacity.reset_to_defaults()

View file

@ -1,71 +0,0 @@
extends SceneTree
const OUTPUT_DIRECTORY := "res://sound/dialogue/animalese/placeholder"
const SAMPLE_RATE: int = 22050
const SAMPLE_SECONDS: float = 0.055
const CHARACTERS := "abcdefghijklmnopqrstuvwxyz"
const VOWELS := "aeiou"
func _initialize() -> void:
var absolute_directory := ProjectSettings.globalize_path(OUTPUT_DIRECTORY)
var directory_error: Error = DirAccess.make_dir_recursive_absolute(
absolute_directory
)
if directory_error != OK:
push_error("Could not create animalese sample directory.")
quit(1)
return
for character_index: int in range(CHARACTERS.length()):
var character := CHARACTERS.substr(character_index, 1)
_save_sample(character, character_index)
_save_sample("fallback", CHARACTERS.length())
quit()
func _save_sample(sample_name: String, sample_index: int) -> void:
var stream := _build_sample(sample_name, sample_index)
var output_path := "%s/%s" % [OUTPUT_DIRECTORY, sample_name]
var save_error: Error = stream.save_to_wav(output_path)
if save_error != OK:
push_error("Could not save animalese sample: %s" % sample_name)
func _build_sample(sample_name: String, sample_index: int) -> AudioStreamWAV:
var frame_count := roundi(SAMPLE_RATE * SAMPLE_SECONDS)
var pcm := PackedByteArray()
pcm.resize(frame_count * 2)
var base_frequency := 245.0 + float(sample_index % 7) * 18.0
var formant_one := 620.0 + float(sample_index % 5) * 85.0
var formant_two := 1320.0 + float(sample_index % 6) * 115.0
var is_vowel := VOWELS.contains(sample_name)
var noise_seed := sample_index * 7919 + 104729
for frame_index: int in range(frame_count):
var time := float(frame_index) / SAMPLE_RATE
var attack := minf(time / 0.004, 1.0)
var release := minf((SAMPLE_SECONDS - time) / 0.014, 1.0)
var envelope := maxf(minf(attack, release), 0.0)
noise_seed = int(
(noise_seed * 1103515245 + 12345) & 0x7fffffff
)
var noise := float(noise_seed % 65536) / 32767.5 - 1.0
var voiced := (
sin(TAU * base_frequency * time) * 0.42
+ sin(TAU * base_frequency * 2.0 * time) * 0.16
)
var formants := (
sin(TAU * formant_one * time) * 0.18
+ sin(TAU * formant_two * time) * 0.10
)
var consonant_noise := noise * (0.08 if is_vowel else 0.24)
var value := envelope * (voiced + formants + consonant_noise) * 0.72
var signed_sample := clampi(roundi(value * 32767.0), -32768, 32767)
var encoded_sample := signed_sample if signed_sample >= 0 else signed_sample + 65536
pcm[frame_index * 2] = encoded_sample & 0xff
pcm[frame_index * 2 + 1] = (encoded_sample >> 8) & 0xff
var stream := AudioStreamWAV.new()
stream.set("format", AudioStreamWAV.FORMAT_16_BITS)
stream.mix_rate = SAMPLE_RATE
stream.stereo = false
stream.data = pcm
return stream

View file

@ -1 +0,0 @@
uid://cwqm75icgcr4u

View file

@ -25,27 +25,30 @@ const DEFAULT_PROFILE: Dictionary = {
"process/hdr_clamp_exposure": false,
"process/size_limit": 0,
"detect_3d/compress_to": 0,
"import_profile": "managed_default",
},
}
var apply_changes: bool = false
var verbose: bool = false
var roots: PackedStringArray = PackedStringArray()
var should_exit: bool = false
const CUSTOM_PROFILE_KEY := "custom"
var errors: int = 0
var skipped_non_texture: int = 0
var updated: int = 0
var unchanged: int = 0
var apply_changes := false
var verbose := false
var roots: PackedStringArray = PackedStringArray()
var user_root_overrides := false
var should_exit := false
var skipped_custom := 0
var skipped_non_texture := 0
var skipped_unreadable := 0
var updated := 0
var unchanged := 0
func _initialize() -> void:
_parse_args()
if should_exit:
return
if roots.is_empty():
printerr("At least one --root path is required.")
quit(2)
return
if roots.is_empty() and not user_root_overrides:
roots.append("res://")
for root: String in roots:
var normalized_root := root
@ -60,7 +63,7 @@ func _initialize() -> void:
_scan_directory(normalized_root)
print_summary()
quit(1 if errors > 0 else 0)
quit(0)
func _parse_args() -> void:
@ -86,23 +89,25 @@ func _parse_args() -> void:
quit(1)
return
i += 1
roots.append(args[i])
i += 1
elif arg.begins_with("--root="):
roots.append(arg.trim_prefix("--root="))
i += 1
roots.append(args[i])
i += 1
user_root_overrides = true
elif arg.begins_with("--root="):
roots.append(arg.trim_prefix("--root="))
i += 1
user_root_overrides = true
else:
printerr("Unknown argument: ", arg)
should_exit = true
quit(2)
return
if arg.begins_with("-"):
_print("Ignoring unknown option: %s" % arg)
i += 1
continue
func _scan_directory(path: String) -> void:
var dir := DirAccess.open(path)
if dir == null:
printerr("Failed to open root: ", path)
errors += 1
skipped_non_texture += 1
return
dir.list_dir_begin()
@ -131,7 +136,7 @@ func _handle_import_file(import_path: String) -> void:
var err := cfg.load(import_path)
if err != OK:
printerr("Could not load import settings: ", import_path, " (", err, ")")
errors += 1
skipped_unreadable += 1
return
if not cfg.has_section("remap") or not cfg.has_section_key("remap", "importer"):
@ -142,6 +147,15 @@ func _handle_import_file(import_path: String) -> void:
skipped_non_texture += 1
return
var current_profile := ""
if cfg.has_section("params") and cfg.has_section_key("params", "import_profile"):
current_profile = str(cfg.get_value("params", "import_profile"))
if current_profile == CUSTOM_PROFILE_KEY:
skipped_custom += 1
if verbose:
_print("Skipping custom profile: %s" % import_path)
return
var changed := false
for section_name: String in DEFAULT_PROFILE.keys():
var section_data: Dictionary = DEFAULT_PROFILE[section_name]
@ -164,7 +178,7 @@ func _handle_import_file(import_path: String) -> void:
err = cfg.save(import_path)
if err != OK:
printerr("Could not save import settings: ", import_path, " (", err, ")")
errors += 1
skipped_unreadable += 1
return
if verbose:
_print("Updated: %s" % import_path)
@ -176,18 +190,11 @@ func _handle_import_file(import_path: String) -> void:
func print_summary() -> void:
if apply_changes:
_print(
"Texture import normalization complete: "
+ "updated=%d unchanged=%d skipped_non_texture=%d errors=%d"
% [updated, unchanged, skipped_non_texture, errors]
)
_print("Texture import normalization complete: updated=%d unchanged=%d skipped_custom=%d skipped_non_texture=%d skipped_unreadable=%d" % [updated, unchanged, skipped_custom, skipped_non_texture, skipped_unreadable])
else:
_print(
"Dry-run complete: "
+ "would-update=%d unchanged=%d skipped_non_texture=%d errors=%d"
% [updated, unchanged, skipped_non_texture, errors]
)
_print("Dry-run complete: would-update=%d unchanged=%d skipped_custom=%d skipped_non_texture=%d skipped_unreadable=%d" % [updated, unchanged, skipped_custom, skipped_non_texture, skipped_unreadable])
_print("Pass --apply to write normalized settings into .import files.")
_print("Set [params] import_profile = \"custom\" to opt a texture out of default normalization.")
func _print(msg: String) -> void:
@ -200,5 +207,6 @@ func _print_usage() -> void:
print()
print("Defaults:")
print("- Dry-run only unless --apply is provided")
print("- Requires at least one explicit --root path")
print("- Scans all .import files under each root")
print("- Skips .godot")
print("- Skips textures marked with [params] import_profile = \"custom\"")

View file

@ -5,26 +5,15 @@ set -euo pipefail
readonly SCRIPT_DIR="$(cd -- "$(dirname -- "${BASH_SOURCE[0]}")" && pwd)"
readonly PROJECT_ROOT="$(cd -- "${SCRIPT_DIR}/.." && pwd)"
readonly GODOT_BIN="${GODOT_BIN:-godot}"
if [[ $# -ne 1 ]]; then
echo "usage: scripts/normalize_texture_profiles.sh res://path/to/textures" >&2
exit 2
fi
readonly ROOT_PATH="$1"
readonly ROOT_PATH="${1:-res://}"
readonly WORK_ROOT="$(mktemp -d -t netfishing-godot-data.XXXXXX)"
trap 'rm -rf -- "${WORK_ROOT}"' EXIT
cd "${PROJECT_ROOT}"
export XDG_DATA_HOME="${WORK_ROOT}/data"
export XDG_CONFIG_HOME="${WORK_ROOT}/config"
export XDG_CACHE_HOME="${WORK_ROOT}/cache"
"${GODOT_BIN}" --headless --path . \
--script scripts/normalize_texture_imports.gd -- --apply --root "${ROOT_PATH}"
# Synchronize Godot-generated remap and destination metadata with the updated
# import parameters before the caller reviews or commits the sidecars.
"${GODOT_BIN}" --headless --path . --import
XDG_DATA_HOME="${WORK_ROOT}/data" \
XDG_CONFIG_HOME="${WORK_ROOT}/config" \
XDG_CACHE_HOME="${WORK_ROOT}/cache" \
${GODOT_BIN} --headless --path . \
--script scripts/normalize_texture_imports.gd -- --apply --root "${ROOT_PATH}"

View file

@ -18,7 +18,7 @@ readonly -a QUICK_TESTS=(
"tests/player_experience_validation.gd"
"tests/shoreline_ambience_validation.gd"
"tests/surface_drawing_validation.gd"
"tests/terrain_blender_material_validation.gd"
"tests/terrain_projection_validation.gd"
"tests/world_time_validation.gd"
"tests/world_weather_validation.gd"
)

View file

@ -1,7 +1,7 @@
#!/usr/bin/env bash
set -euo pipefail
ANDROID_HOME="${ANDROID_HOME:-${HOME}/Android/Sdk}"
ANDROID_HOME="${ANDROID_HOME:-/home/sol6_vi/Android/Sdk}"
JAVA_HOME="${JAVA_HOME:-}"
INSTALL=0
AUTO=0
@ -55,9 +55,6 @@ done
PROJECT_ROOT="$(cd -- "$(dirname -- "${BASH_SOURCE[0]}")/.." && pwd)"
GODOT="${GODOT_BIN:-godot}"
PROJECT_VERSION="$(
sed -n 's/^config\/version="\([^"]*\)"/\1/p' "${PROJECT_ROOT}/project.godot"
)"
MISSING=0
print_status() { printf '%s %s\n' "[$1]" "$2"; }
@ -74,7 +71,7 @@ check_dir() {
check_file() {
local path="$1" label="$2"
if [[ -x "$path" ]]; then
if [[ -x "$path" || -f "$path" ]]; then
print_status OK "${label}: ${path}"
else
print_status ERR "Missing ${label}: ${path}"
@ -116,8 +113,6 @@ resolve_java_home() {
check_java() {
if resolve_java_home && [[ -x "$JAVA_HOME/bin/java" ]]; then
print_status OK "Java binary: $JAVA_HOME/bin/java"
check_file "$JAVA_HOME/bin/javac" "javac binary"
check_file "$JAVA_HOME/bin/keytool" "keytool binary"
"$JAVA_HOME/bin/java" -version 2>&1 | sed -n '1,2p'
local java_ver
java_ver=$("$JAVA_HOME/bin/java" -version 2>&1 | sed -n '1p')
@ -166,13 +161,18 @@ check_sdk_paths() {
check_dir "$ANDROID_HOME/platform-tools" "platform-tools"
check_file "$ANDROID_HOME/platform-tools/adb" "adb binary"
if [[ -d "$ANDROID_HOME/build-tools/$BUILDTOOLS_VERSION" ]]; then
print_status OK "build-tools ${BUILDTOOLS_VERSION}: present"
check_file \
"$ANDROID_HOME/build-tools/$BUILDTOOLS_VERSION/apksigner" \
"apksigner"
if [[ -d "$ANDROID_HOME/build-tools" ]]; then
local bt
bt="$(ls -1v "$ANDROID_HOME/build-tools" 2>/dev/null | tail -n 1 || true)"
if [[ -n "$bt" ]]; then
print_status OK "build-tools versions: $(ls -1 "$ANDROID_HOME/build-tools" | tr '\n' ' ')"
check_file "$ANDROID_HOME/build-tools/$bt/apksigner" "apksigner"
else
print_status ERR "build-tools directory exists but is empty"
MISSING=$((MISSING+1))
fi
else
print_status ERR "Missing build-tools version: ${BUILDTOOLS_VERSION}"
print_status ERR "Missing build-tools root: $ANDROID_HOME/build-tools"
MISSING=$((MISSING+1))
fi
@ -198,7 +198,6 @@ check_sdk_paths() {
install_jdk_hint() {
local pm=0
local -a elevate=()
echo "Attempting automatic JDK install..."
if command -v apt-get >/dev/null 2>&1; then
pm="apt-get"
@ -213,18 +212,19 @@ install_jdk_hint() {
fi
if [[ $EUID -ne 0 ]]; then
local sudo_cmd=(sudo)
if ! command -v sudo >/dev/null 2>&1; then
echo "sudo not found; please run with root privileges for package install."
return 1
fi
elevate=(sudo)
fi
if [[ "$pm" == "apt-get" ]]; then
echo "apt-get install -y openjdk-17-jdk"
if [[ $AUTO -eq 1 ]]; then
"${elevate[@]}" apt-get update
"${elevate[@]}" apt-get install -y openjdk-17-jdk
if command -v sudo >/dev/null 2>&1; then
sudo apt-get update && sudo apt-get install -y openjdk-17-jdk
fi
else
echo "Run as manual step first: sudo apt-get update && sudo apt-get install -y openjdk-17-jdk"
return 1
@ -232,14 +232,14 @@ install_jdk_hint() {
elif [[ "$pm" == "dnf" ]]; then
echo "dnf install -y java-17-openjdk-devel"
if [[ $AUTO -eq 1 ]]; then
"${elevate[@]}" dnf install -y java-17-openjdk-devel
sudo dnf install -y java-17-openjdk-devel
else
return 1
fi
else
echo "pacman -S --noconfirm jdk17-openjdk"
if [[ $AUTO -eq 1 ]]; then
"${elevate[@]}" pacman -S --noconfirm jdk17-openjdk
sudo pacman -S --noconfirm jdk17-openjdk
else
return 1
fi
@ -305,7 +305,7 @@ install_sdk_components() {
echo "Cannot install SDK packages without sdkmanager."
return 1
fi
"$sdkmanager_path" --sdk_root="${ANDROID_HOME}" --list | sed -n '1,40p'
"$sdkmanager_path" --sdk_root="${ANDROID_HOME}" --list | head -n 40 | sed -n '1,40p'
echo "Installing required SDK components: platform-tools, platform-android-${SDK_VERSION}, build-tools-${BUILDTOOLS_VERSION}"
if [[ $AUTO -eq 1 ]]; then
yes | "$sdkmanager_path" --sdk_root="${ANDROID_HOME}" --install "platform-tools" "platforms;android-${SDK_VERSION}" "build-tools;${BUILDTOOLS_VERSION}"
@ -336,7 +336,7 @@ check_editor_settings() {
run_export_hint() {
echo
echo "Example export commands:"
echo " ${GODOT} --headless --path \"$PROJECT_ROOT\" --export-release \"Android\" builds/${PROJECT_VERSION}/android/NETfishing.apk"
echo " ${GODOT} --headless --path \"$PROJECT_ROOT\" --export-release \"Android\" builds/v0.5.1-prealpha/android/NETFISHING.apk"
echo " ${GODOT} --headless --path \"$PROJECT_ROOT\" --export-debug \"Android\" builds/android/NETfishing-debug.apk"
}
@ -354,12 +354,7 @@ fi
if [[ $INSTALL -eq 1 ]]; then
echo
print_status INFO "Attempting dependency install path..."
if [[ \
-z "$JAVA_HOME" \
|| ! -x "$JAVA_HOME/bin/java" \
|| ! -x "$JAVA_HOME/bin/javac" \
|| ! -x "$JAVA_HOME/bin/keytool" \
]]; then
if [[ -z "$JAVA_HOME" || ! -x "$JAVA_HOME/bin/java" ]]; then
install_jdk_hint || true
fi
@ -382,11 +377,11 @@ if [[ $INSTALL -eq 1 ]]; then
fi
print_status ERR "Dependency check failed with ${MISSING} issue(s)."
cat <<TIPS
cat <<'TIPS'
Suggested manual recovery:
1) Install OpenJDK 17 JDK.
2) Install Android SDK command line tools and run:
sdkmanager --sdk_root="${ANDROID_HOME}" --install "platform-tools" "platforms;android-${SDK_VERSION}" "build-tools;${BUILDTOOLS_VERSION}"
sdkmanager --sdk_root="${ANDROID_HOME}" --install "platform-tools" "platforms;android-33" "build-tools;34.0.0"
3) Set JAVA_HOME and ANDROID_HOME in your shell and Godot Exporter settings.
4) Re-run this script without --install (verification), then re-run Godot Android export.
TIPS

View file

@ -1,24 +0,0 @@
[remap]
importer="wav"
type="AudioStreamWAV"
uid="uid://dvajjy4bwadio"
path="res://.godot/imported/a.wav-9ab4b1a9a555e6fcf07951f399769b30.sample"
[deps]
source_file="res://sound/dialogue/animalese/placeholder/a.wav"
dest_files=["res://.godot/imported/a.wav-9ab4b1a9a555e6fcf07951f399769b30.sample"]
[params]
force/8_bit=false
force/mono=false
force/max_rate=false
force/max_rate_hz=44100
edit/trim=false
edit/normalize=false
edit/loop_mode=0
edit/loop_begin=0
edit/loop_end=-1
compress/mode=2

View file

@ -1,24 +0,0 @@
[remap]
importer="wav"
type="AudioStreamWAV"
uid="uid://cs30tdyuo6wtc"
path="res://.godot/imported/b.wav-eed860af2033bda258bd172899061b45.sample"
[deps]
source_file="res://sound/dialogue/animalese/placeholder/b.wav"
dest_files=["res://.godot/imported/b.wav-eed860af2033bda258bd172899061b45.sample"]
[params]
force/8_bit=false
force/mono=false
force/max_rate=false
force/max_rate_hz=44100
edit/trim=false
edit/normalize=false
edit/loop_mode=0
edit/loop_begin=0
edit/loop_end=-1
compress/mode=2

View file

@ -1,24 +0,0 @@
[remap]
importer="wav"
type="AudioStreamWAV"
uid="uid://2inox7x31xam"
path="res://.godot/imported/c.wav-61dc49533634805d70c71d509b63e37b.sample"
[deps]
source_file="res://sound/dialogue/animalese/placeholder/c.wav"
dest_files=["res://.godot/imported/c.wav-61dc49533634805d70c71d509b63e37b.sample"]
[params]
force/8_bit=false
force/mono=false
force/max_rate=false
force/max_rate_hz=44100
edit/trim=false
edit/normalize=false
edit/loop_mode=0
edit/loop_begin=0
edit/loop_end=-1
compress/mode=2

View file

@ -1,24 +0,0 @@
[remap]
importer="wav"
type="AudioStreamWAV"
uid="uid://yvha8eukw6l7"
path="res://.godot/imported/d.wav-910bc02bc42bdb1b37561959a317c28a.sample"
[deps]
source_file="res://sound/dialogue/animalese/placeholder/d.wav"
dest_files=["res://.godot/imported/d.wav-910bc02bc42bdb1b37561959a317c28a.sample"]
[params]
force/8_bit=false
force/mono=false
force/max_rate=false
force/max_rate_hz=44100
edit/trim=false
edit/normalize=false
edit/loop_mode=0
edit/loop_begin=0
edit/loop_end=-1
compress/mode=2

View file

@ -1,24 +0,0 @@
[remap]
importer="wav"
type="AudioStreamWAV"
uid="uid://8ujopf8aqs83"
path="res://.godot/imported/e.wav-7da5f8fcd017bf931de2bd49dc00f652.sample"
[deps]
source_file="res://sound/dialogue/animalese/placeholder/e.wav"
dest_files=["res://.godot/imported/e.wav-7da5f8fcd017bf931de2bd49dc00f652.sample"]
[params]
force/8_bit=false
force/mono=false
force/max_rate=false
force/max_rate_hz=44100
edit/trim=false
edit/normalize=false
edit/loop_mode=0
edit/loop_begin=0
edit/loop_end=-1
compress/mode=2

View file

@ -1,24 +0,0 @@
[remap]
importer="wav"
type="AudioStreamWAV"
uid="uid://c0gv4cphnfcxf"
path="res://.godot/imported/f.wav-8eb31f2de6240647a0d6d2520aadcd6a.sample"
[deps]
source_file="res://sound/dialogue/animalese/placeholder/f.wav"
dest_files=["res://.godot/imported/f.wav-8eb31f2de6240647a0d6d2520aadcd6a.sample"]
[params]
force/8_bit=false
force/mono=false
force/max_rate=false
force/max_rate_hz=44100
edit/trim=false
edit/normalize=false
edit/loop_mode=0
edit/loop_begin=0
edit/loop_end=-1
compress/mode=2

View file

@ -1,24 +0,0 @@
[remap]
importer="wav"
type="AudioStreamWAV"
uid="uid://bw0pod4tmexm0"
path="res://.godot/imported/fallback.wav-7a40516b188f10028ac39bdc24fb52e5.sample"
[deps]
source_file="res://sound/dialogue/animalese/placeholder/fallback.wav"
dest_files=["res://.godot/imported/fallback.wav-7a40516b188f10028ac39bdc24fb52e5.sample"]
[params]
force/8_bit=false
force/mono=false
force/max_rate=false
force/max_rate_hz=44100
edit/trim=false
edit/normalize=false
edit/loop_mode=0
edit/loop_begin=0
edit/loop_end=-1
compress/mode=2

View file

@ -1,24 +0,0 @@
[remap]
importer="wav"
type="AudioStreamWAV"
uid="uid://c7706p0fc8y7c"
path="res://.godot/imported/g.wav-45b2cbd9653a31477e3f00c9432d560c.sample"
[deps]
source_file="res://sound/dialogue/animalese/placeholder/g.wav"
dest_files=["res://.godot/imported/g.wav-45b2cbd9653a31477e3f00c9432d560c.sample"]
[params]
force/8_bit=false
force/mono=false
force/max_rate=false
force/max_rate_hz=44100
edit/trim=false
edit/normalize=false
edit/loop_mode=0
edit/loop_begin=0
edit/loop_end=-1
compress/mode=2

View file

@ -1,24 +0,0 @@
[remap]
importer="wav"
type="AudioStreamWAV"
uid="uid://cl66wka105xmo"
path="res://.godot/imported/h.wav-2da53a6638eebb75f0867eaf75dc24ba.sample"
[deps]
source_file="res://sound/dialogue/animalese/placeholder/h.wav"
dest_files=["res://.godot/imported/h.wav-2da53a6638eebb75f0867eaf75dc24ba.sample"]
[params]
force/8_bit=false
force/mono=false
force/max_rate=false
force/max_rate_hz=44100
edit/trim=false
edit/normalize=false
edit/loop_mode=0
edit/loop_begin=0
edit/loop_end=-1
compress/mode=2

View file

@ -1,24 +0,0 @@
[remap]
importer="wav"
type="AudioStreamWAV"
uid="uid://jicgdbnyt6bm"
path="res://.godot/imported/i.wav-1e8d7f13980a6f2f61d73f65a2efc91c.sample"
[deps]
source_file="res://sound/dialogue/animalese/placeholder/i.wav"
dest_files=["res://.godot/imported/i.wav-1e8d7f13980a6f2f61d73f65a2efc91c.sample"]
[params]
force/8_bit=false
force/mono=false
force/max_rate=false
force/max_rate_hz=44100
edit/trim=false
edit/normalize=false
edit/loop_mode=0
edit/loop_begin=0
edit/loop_end=-1
compress/mode=2

View file

@ -1,24 +0,0 @@
[remap]
importer="wav"
type="AudioStreamWAV"
uid="uid://cji2evr0u3m6c"
path="res://.godot/imported/j.wav-2f355374ffcea77f8dc630ee45879444.sample"
[deps]
source_file="res://sound/dialogue/animalese/placeholder/j.wav"
dest_files=["res://.godot/imported/j.wav-2f355374ffcea77f8dc630ee45879444.sample"]
[params]
force/8_bit=false
force/mono=false
force/max_rate=false
force/max_rate_hz=44100
edit/trim=false
edit/normalize=false
edit/loop_mode=0
edit/loop_begin=0
edit/loop_end=-1
compress/mode=2

View file

@ -1,24 +0,0 @@
[remap]
importer="wav"
type="AudioStreamWAV"
uid="uid://eypaqf1iaq5"
path="res://.godot/imported/k.wav-b85d045dc022b9055c25a650b2e3af2a.sample"
[deps]
source_file="res://sound/dialogue/animalese/placeholder/k.wav"
dest_files=["res://.godot/imported/k.wav-b85d045dc022b9055c25a650b2e3af2a.sample"]
[params]
force/8_bit=false
force/mono=false
force/max_rate=false
force/max_rate_hz=44100
edit/trim=false
edit/normalize=false
edit/loop_mode=0
edit/loop_begin=0
edit/loop_end=-1
compress/mode=2

View file

@ -1,24 +0,0 @@
[remap]
importer="wav"
type="AudioStreamWAV"
uid="uid://bixgnaq4idkhj"
path="res://.godot/imported/l.wav-b21006c3b048e0c797d6baf19ab0f663.sample"
[deps]
source_file="res://sound/dialogue/animalese/placeholder/l.wav"
dest_files=["res://.godot/imported/l.wav-b21006c3b048e0c797d6baf19ab0f663.sample"]
[params]
force/8_bit=false
force/mono=false
force/max_rate=false
force/max_rate_hz=44100
edit/trim=false
edit/normalize=false
edit/loop_mode=0
edit/loop_begin=0
edit/loop_end=-1
compress/mode=2

View file

@ -1,24 +0,0 @@
[remap]
importer="wav"
type="AudioStreamWAV"
uid="uid://dan4o17oreugv"
path="res://.godot/imported/m.wav-aa394c4b1d5309d859ce73a368754264.sample"
[deps]
source_file="res://sound/dialogue/animalese/placeholder/m.wav"
dest_files=["res://.godot/imported/m.wav-aa394c4b1d5309d859ce73a368754264.sample"]
[params]
force/8_bit=false
force/mono=false
force/max_rate=false
force/max_rate_hz=44100
edit/trim=false
edit/normalize=false
edit/loop_mode=0
edit/loop_begin=0
edit/loop_end=-1
compress/mode=2

Some files were not shown because too many files have changed in this diff Show more