Compare commits

..

No commits in common. "633bbd9a881b5182dab87aff675624125e1c98bf" and "a91f2b98257c367fb7af0cb1a9d0c52a2a211708" have entirely different histories.

79 changed files with 446 additions and 2432 deletions

View file

@ -1,19 +0,0 @@
[remap]
importer="oggvorbisstr"
type="AudioStreamOggVorbis"
uid="uid://bgkfluumuen4m"
path="res://.godot/imported/as_in_four_wolves.ogg-3d5f167a45d25a52b56a9dd816196d44.oggvorbisstr"
[deps]
source_file="res://audio/music/title/as_in_four_wolves.ogg"
dest_files=["res://.godot/imported/as_in_four_wolves.ogg-3d5f167a45d25a52b56a9dd816196d44.oggvorbisstr"]
[params]
loop=true
loop_offset=0
bpm=0
beat_count=0
bar_beats=4

View file

@ -1,15 +0,0 @@
[gd_resource type="AudioBusLayout" format=3]
[resource]
bus/0/name = &"Master"
bus/0/solo = false
bus/0/mute = false
bus/0/bypass_fx = false
bus/0/volume_db = 0.0
bus/0/send = &""
bus/1/name = &"Music"
bus/1/solo = false
bus/1/mute = false
bus/1/bypass_fx = false
bus/1/volume_db = 0.0
bus/1/send = &"Master"

View file

@ -5,7 +5,7 @@
[resource]
script = ExtResource("1_profile")
id = &"main_fishing_shop"
display_name = "main fishing shop"
display_name = "Main Fishing Shop"
animal_name_singular = "shopkeeper"
animal_name_plural = "shopkeepers"
payout_multiplier = 1.0

View file

@ -5,7 +5,7 @@
[resource]
script = ExtResource("1_profile")
id = &"pelicans"
display_name = "pelicans"
display_name = "Pelicans"
animal_name_singular = "pelican"
animal_name_plural = "pelicans"
payout_multiplier = 0.25

View file

@ -5,7 +5,7 @@
[resource]
script = ExtResource("1_profile")
id = &"trading_post"
display_name = "trading post"
display_name = "Trading Post"
animal_name_singular = "otter"
animal_name_plural = "otters"
payout_multiplier = 0.85

View file

@ -39,7 +39,7 @@ func get_sale_message(
var buyer_name: String = animal_name_plural
if buyer_name.is_empty():
buyer_name = display_name
return "you sold your %s to the %s for $%d." % [
return "You sold your %s to the %s for $%d." % [
fish_name,
buyer_name,
payout,

View file

@ -8,15 +8,12 @@ enum Status {
INVALID_VALUE,
INVALID_BUYER,
INVALID_OFFER,
INVALID_SELECTION,
TRANSACTION_FAILED,
}
var success: bool = false
var status: Status = Status.TRANSACTION_FAILED
var catch_id: StringName
var catch_ids: Array[StringName] = []
var fish_count: int = 0
var fish_name: String = ""
var buyer_id: StringName
var buyer_display_name: String = ""
@ -35,16 +32,14 @@ func get_message() -> String:
Status.SUCCESS:
return sale_message
Status.NOT_FOUND:
return "fish no longer exists."
return "Fish no longer exists."
Status.FAVORITED:
return "favorited fish cannot be sold."
return "Favorited fish cannot be sold."
Status.INVALID_VALUE:
return "invalid sale value."
return "Invalid sale value."
Status.INVALID_BUYER:
return "buyer is unavailable."
return "Buyer is unavailable."
Status.INVALID_OFFER:
return "invalid buyer offer."
Status.INVALID_SELECTION:
return "the fish selection is invalid."
return "Invalid buyer offer."
_:
return "transaction failed."
return "Transaction failed."

View file

@ -25,53 +25,24 @@ func can_sell(
catch_id: StringName,
buyer: FishBuyerProfileType,
) -> bool:
return preview_batch([catch_id], buyer).is_success()
func can_sell_batch(
catch_ids: Array[StringName],
buyer: FishBuyerProfileType,
) -> bool:
return preview_batch(catch_ids, buyer).is_success()
func preview_batch(
catch_ids: Array[StringName],
buyer: FishBuyerProfileType,
) -> FishSaleResultType:
return _validate_batch(catch_ids, buyer)
return _validate_sale(catch_id, buyer).is_success()
func sell(
catch_id: StringName,
buyer: FishBuyerProfileType,
) -> FishSaleResultType:
return sell_batch([catch_id], buyer)
func sell_batch(
catch_ids: Array[StringName],
buyer: FishBuyerProfileType,
) -> FishSaleResultType:
var result: FishSaleResultType = _validate_batch(catch_ids, buyer)
var result: FishSaleResultType = _validate_sale(catch_id, buyer)
if not result.is_success():
return result
var inventory_snapshot: Array[FishCatchType] = _inventory.get_all_catches()
var next_sequence_snapshot: int = _inventory.get_next_catch_sequence()
var removed_catches: Array[FishCatchType] = (
_inventory.remove_catches_by_ids(result.catch_ids)
)
if removed_catches.size() != result.catch_ids.size():
var removed_catch: FishCatchType = _inventory.remove_catch_by_id(catch_id)
if removed_catch == null:
result.success = false
result.status = FishSaleResultType.Status.TRANSACTION_FAILED
result.status = FishSaleResultType.Status.NOT_FOUND
return result
if not _wallet.credit(result.payout):
if not _inventory.replace_all_catches(
inventory_snapshot,
next_sequence_snapshot
):
push_error("Unable to roll back a failed fish batch sale.")
_inventory.add_catch(removed_catch)
result.success = false
result.status = FishSaleResultType.Status.TRANSACTION_FAILED
return result
@ -79,22 +50,27 @@ func sell_batch(
return result
func _validate_batch(
catch_ids: Array[StringName],
func _validate_sale(
catch_id: StringName,
buyer: FishBuyerProfileType,
) -> FishSaleResultType:
var result := FishSaleResultType.new()
result.catch_ids = catch_ids.duplicate()
result.fish_count = catch_ids.size()
if _inventory == null or _wallet == null:
result.status = FishSaleResultType.Status.TRANSACTION_FAILED
return result
if catch_ids.is_empty():
result.status = FishSaleResultType.Status.INVALID_SELECTION
result.catch_id = catch_id
if (
catch_id.is_empty()
or _inventory == null
or _wallet == null
):
result.status = FishSaleResultType.Status.NOT_FOUND
return result
if buyer == null or not buyer.is_valid():
result.status = FishSaleResultType.Status.INVALID_BUYER
return result
var fish_catch: FishCatchType = _inventory.get_catch_by_id(catch_id)
if fish_catch == null:
result.status = FishSaleResultType.Status.NOT_FOUND
return result
result.fish_name = fish_catch.fish.display_name
result.buyer_id = buyer.id
result.buyer_display_name = buyer.display_name
result.buyer_animal_name = (
@ -102,55 +78,21 @@ func _validate_batch(
if not buyer.animal_name_plural.is_empty()
else buyer.display_name
)
var seen_ids: Dictionary[StringName, bool] = {}
var contains_favorite: bool = false
const MAX_SAFE_TOTAL: int = 9223372036854775807
for catch_id: StringName in catch_ids:
if catch_id.is_empty() or seen_ids.has(catch_id):
result.status = FishSaleResultType.Status.INVALID_SELECTION
return result
seen_ids[catch_id] = true
var fish_catch: FishCatchType = _inventory.get_catch_by_id(catch_id)
if fish_catch == null:
result.status = FishSaleResultType.Status.NOT_FOUND
return result
result.base_value = fish_catch.sale_value
result.payout = buyer.get_offer(result.base_value)
if fish_catch.is_favorited:
contains_favorite = true
if fish_catch.sale_value < 0:
result.status = FishSaleResultType.Status.INVALID_VALUE
return result
var offer: int = buyer.get_offer(fish_catch.sale_value)
if offer < 0:
result.status = FishSaleResultType.Status.INVALID_OFFER
return result
if (
result.base_value > MAX_SAFE_TOTAL - fish_catch.sale_value
or result.payout > MAX_SAFE_TOTAL - offer
):
result.status = FishSaleResultType.Status.TRANSACTION_FAILED
return result
result.base_value += fish_catch.sale_value
result.payout += offer
if result.fish_count == 1:
result.catch_id = fish_catch.catch_id
result.fish_name = fish_catch.fish.display_name
if contains_favorite:
result.status = FishSaleResultType.Status.FAVORITED
return result
if not _wallet.can_credit(result.payout):
elif fish_catch.sale_value < 0:
result.status = FishSaleResultType.Status.INVALID_VALUE
elif result.payout < 0:
result.status = FishSaleResultType.Status.INVALID_OFFER
elif not _wallet.can_credit(result.payout):
result.status = FishSaleResultType.Status.TRANSACTION_FAILED
return result
else:
result.status = FishSaleResultType.Status.SUCCESS
result.success = true
if result.fish_count == 1:
result.sale_message = buyer.get_sale_message(
result.fish_name,
result.payout
)
else:
result.sale_message = "you sold %d fish to the %s for $%d." % [
result.fish_count,
result.buyer_animal_name,
result.payout,
]
return result

View file

@ -98,4 +98,4 @@ func get_sale_value_for_weight(weight_lb: float) -> int:
func get_rarity_name() -> String:
return Rarity.keys()[rarity].to_lower()
return Rarity.keys()[rarity].capitalize()

View file

@ -1,11 +1,10 @@
[gd_resource type="Resource" load_steps=6 format=3]
[gd_resource type="Resource" load_steps=5 format=3]
[ext_resource type="Script" path="res://fish/fish_pool.gd" id="1_pool"]
[ext_resource type="Resource" path="res://fish/species/bluegill/bluegill.tres" id="2_bluegill"]
[ext_resource type="Resource" path="res://fish/species/bass/bass.tres" id="3_bass"]
[ext_resource type="Resource" path="res://fish/species/carp/carp.tres" id="4_carp"]
[ext_resource type="Resource" path="res://fish/species/sunfish/sunfish.tres" id="5_sunfish"]
[resource]
script = ExtResource("1_pool")
candidates = [ExtResource("2_bluegill"), ExtResource("3_bass"), ExtResource("4_carp"), ExtResource("5_sunfish")]
candidates = [ExtResource("2_bluegill"), ExtResource("3_bass"), ExtResource("4_carp")]

View file

@ -3,7 +3,7 @@
[ext_resource type="Script" path="res://fish/fish_data.gd" id="1_fish_data"]
[ext_resource type="Resource" path="res://fishing/bass_catch_profile.tres" id="2_profile"]
[ext_resource type="Script" path="res://fish/fish_availability.gd" id="3_availability_script"]
[ext_resource type="Texture2D" path="res://fish/species/bass/fish_bass_striped.png" id="4_texture"]
[ext_resource type="Texture2D" path="res://fish/species/bass/bass.svg" id="4_texture"]
[sub_resource type="Resource" id="BassAvailability"]
script = ExtResource("3_availability_script")
@ -15,7 +15,7 @@ preferred_bait_weight_multiplier = 1.4
[resource]
script = ExtResource("1_fish_data")
id = &"bass"
display_name = "striped bass"
display_name = "Bass"
rarity = 1
base_catch_weight = 4.0
catch_profile = ExtResource("2_profile")

Binary file not shown.

Before

Width:  |  Height:  |  Size: 14 KiB

View file

@ -1,40 +0,0 @@
[remap]
importer="texture"
type="CompressedTexture2D"
uid="uid://dvh6s86asl4ix"
path="res://.godot/imported/fish_bass_striped.png-b8c0def688ff4dd2d32a6dd21fa2339c.ctex"
metadata={
"vram_texture": false
}
[deps]
source_file="res://fish/species/bass/fish_bass_striped.png"
dest_files=["res://.godot/imported/fish_bass_striped.png-b8c0def688ff4dd2d32a6dd21fa2339c.ctex"]
[params]
compress/mode=0
compress/high_quality=false
compress/lossy_quality=0.7
compress/uastc_level=0
compress/rdo_quality_loss=0.0
compress/hdr_compression=1
compress/normal_map=0
compress/channel_pack=0
mipmaps/generate=false
mipmaps/limit=-1
roughness/mode=0
roughness/src_normal=""
process/channel_remap/red=0
process/channel_remap/green=1
process/channel_remap/blue=2
process/channel_remap/alpha=3
process/fix_alpha_border=true
process/premult_alpha=false
process/normal_map_invert_y=false
process/hdr_as_srgb=false
process/hdr_clamp_exposure=false
process/size_limit=0
detect_3d/compress_to=1

View file

@ -3,12 +3,12 @@
[ext_resource type="Script" path="res://fish/fish_data.gd" id="1_fish_data"]
[ext_resource type="Resource" path="res://fishing/common_catch_profile.tres" id="2_profile"]
[ext_resource type="Resource" path="res://fish/default_availability.tres" id="3_availability"]
[ext_resource type="Texture2D" path="res://fish/species/bluegill/fish_bluegill.png" id="4_texture"]
[ext_resource type="Texture2D" path="res://fish/species/bluegill/bluegill.svg" id="4_texture"]
[resource]
script = ExtResource("1_fish_data")
id = &"bluegill"
display_name = "bluegill"
display_name = "Bluegill"
rarity = 0
base_catch_weight = 10.0
catch_profile = ExtResource("2_profile")

Binary file not shown.

Before

Width:  |  Height:  |  Size: 21 KiB

View file

@ -1,40 +0,0 @@
[remap]
importer="texture"
type="CompressedTexture2D"
uid="uid://bwsl8862rf2vs"
path="res://.godot/imported/fish_bluegill.png-2307d25b88382e057d2259e28f2f89aa.ctex"
metadata={
"vram_texture": false
}
[deps]
source_file="res://fish/species/bluegill/fish_bluegill.png"
dest_files=["res://.godot/imported/fish_bluegill.png-2307d25b88382e057d2259e28f2f89aa.ctex"]
[params]
compress/mode=0
compress/high_quality=false
compress/lossy_quality=0.7
compress/uastc_level=0
compress/rdo_quality_loss=0.0
compress/hdr_compression=1
compress/normal_map=0
compress/channel_pack=0
mipmaps/generate=false
mipmaps/limit=-1
roughness/mode=0
roughness/src_normal=""
process/channel_remap/red=0
process/channel_remap/green=1
process/channel_remap/blue=2
process/channel_remap/alpha=3
process/fix_alpha_border=true
process/premult_alpha=false
process/normal_map_invert_y=false
process/hdr_as_srgb=false
process/hdr_clamp_exposure=false
process/size_limit=0
detect_3d/compress_to=1

View file

@ -3,7 +3,7 @@
[ext_resource type="Script" path="res://fish/fish_data.gd" id="1_fish_data"]
[ext_resource type="Resource" path="res://fishing/carp_catch_profile.tres" id="2_profile"]
[ext_resource type="Script" path="res://fish/fish_availability.gd" id="3_availability_script"]
[ext_resource type="Texture2D" path="res://fish/species/carp/fish_carp_common.png" id="4_texture"]
[ext_resource type="Texture2D" path="res://fish/species/carp/carp.svg" id="4_texture"]
[sub_resource type="Resource" id="CarpAvailability"]
script = ExtResource("3_availability_script")
@ -15,7 +15,7 @@ preferred_bait_weight_multiplier = 1.5
[resource]
script = ExtResource("1_fish_data")
id = &"carp"
display_name = "common carp"
display_name = "Carp"
rarity = 2
base_catch_weight = 1.5
catch_profile = ExtResource("2_profile")

Binary file not shown.

Before

Width:  |  Height:  |  Size: 15 KiB

View file

@ -1,40 +0,0 @@
[remap]
importer="texture"
type="CompressedTexture2D"
uid="uid://c6pa1gqwv1a5s"
path="res://.godot/imported/fish_carp_common.png-4111bce53aeff44874d8e561aa1600a2.ctex"
metadata={
"vram_texture": false
}
[deps]
source_file="res://fish/species/carp/fish_carp_common.png"
dest_files=["res://.godot/imported/fish_carp_common.png-4111bce53aeff44874d8e561aa1600a2.ctex"]
[params]
compress/mode=0
compress/high_quality=false
compress/lossy_quality=0.7
compress/uastc_level=0
compress/rdo_quality_loss=0.0
compress/hdr_compression=1
compress/normal_map=0
compress/channel_pack=0
mipmaps/generate=false
mipmaps/limit=-1
roughness/mode=0
roughness/src_normal=""
process/channel_remap/red=0
process/channel_remap/green=1
process/channel_remap/blue=2
process/channel_remap/alpha=3
process/fix_alpha_border=true
process/premult_alpha=false
process/normal_map_invert_y=false
process/hdr_as_srgb=false
process/hdr_clamp_exposure=false
process/size_limit=0
detect_3d/compress_to=1

Binary file not shown.

Before

Width:  |  Height:  |  Size: 22 KiB

View file

@ -1,40 +0,0 @@
[remap]
importer="texture"
type="CompressedTexture2D"
uid="uid://x0exmgq6g7yy"
path="res://.godot/imported/fish_sunfish.png-a59a80b2543272e8c6e8ab17d8d3670e.ctex"
metadata={
"vram_texture": false
}
[deps]
source_file="res://fish/species/sunfish/fish_sunfish.png"
dest_files=["res://.godot/imported/fish_sunfish.png-a59a80b2543272e8c6e8ab17d8d3670e.ctex"]
[params]
compress/mode=0
compress/high_quality=false
compress/lossy_quality=0.7
compress/uastc_level=0
compress/rdo_quality_loss=0.0
compress/hdr_compression=1
compress/normal_map=0
compress/channel_pack=0
mipmaps/generate=false
mipmaps/limit=-1
roughness/mode=0
roughness/src_normal=""
process/channel_remap/red=0
process/channel_remap/green=1
process/channel_remap/blue=2
process/channel_remap/alpha=3
process/fix_alpha_border=true
process/premult_alpha=false
process/normal_map_invert_y=false
process/hdr_as_srgb=false
process/hdr_clamp_exposure=false
process/size_limit=0
detect_3d/compress_to=1

View file

@ -1,24 +0,0 @@
[gd_resource type="Resource" load_steps=5 format=3]
[ext_resource type="Script" path="res://fish/fish_data.gd" id="1_fish_data"]
[ext_resource type="Resource" path="res://fishing/common_catch_profile.tres" id="2_profile"]
[ext_resource type="Resource" path="res://fish/default_availability.tres" id="3_availability"]
[ext_resource type="Texture2D" path="res://fish/species/sunfish/fish_sunfish.png" id="4_texture"]
[resource]
script = ExtResource("1_fish_data")
id = &"sunfish"
display_name = "sunfish"
rarity = 0
base_catch_weight = 7.0
catch_profile = ExtResource("2_profile")
availability = ExtResource("3_availability")
weight_min_lb = 0.4
weight_max_lb = 2.5
display_scale_min = 0.7
display_scale_max = 1.1
display_scale_curve = 1.0
sell_value_min = 3
sell_value_max = 3
sell_value_curve = 1.0
display_texture = ExtResource("4_texture")

View file

@ -385,14 +385,14 @@ func _unhandled_input(event: InputEvent) -> void:
get_viewport().set_input_as_handled()
return
if not has_active_fishing_rod():
status_changed.emit("select a fishing rod to cast.")
status_changed.emit("Select a fishing rod to cast.")
get_viewport().set_input_as_handled()
return
if (
_is_cooler_full()
):
status_changed.emit(
"cooler full. sell fish before casting again."
"Cooler full. Sell fish before casting again."
)
get_viewport().set_input_as_handled()
return
@ -438,7 +438,7 @@ func _begin_aiming(player: PlayerType) -> void:
_cast_charge = 0.0
_cast_target = _calculate_cast_target(minimum_cast_distance)
state = FishingState.AIMING_CAST
status_changed.emit("hold left click to aim • release to cast")
status_changed.emit("Hold left click to aim • Release to cast")
var target_is_fishable: bool = is_target_fishable(_cast_target)
var preview_position: Vector3 = _resolve_preview_surface_position(_cast_target)
_presentation.begin_aim(
@ -530,7 +530,7 @@ func _confirm_cast() -> void:
return
state = FishingState.CASTING
status_changed.emit("casting...")
status_changed.emit("Casting...")
_presentation.begin_cast(_cast_target)
_presentation.set_line_mode(FishingPresentationType.LineMode.TAUT)
@ -542,7 +542,7 @@ func _on_cast_completed() -> void:
_selected_water_region = get_fishable_water_region(_cast_target)
_cast_landing_is_fishable = _selected_water_region != null
if not _cast_landing_is_fishable:
_cleanup_attempt("can't fish there.", &"invalid")
_cleanup_attempt("Can't fish there.", &"invalid")
return
_selection_context = _build_fishing_context(_selected_water_region)
_fish_selector.undiscovered_weight_multiplier = undiscovered_weight_multiplier
@ -562,7 +562,7 @@ func _on_cast_completed() -> void:
_local_collection_log
)
if _selected_fish == null:
_cleanup_attempt("nothing is biting here.", &"invalid")
_cleanup_attempt("Nothing is biting here.", &"invalid")
return
state = FishingState.WAITING_FOR_BITE
@ -580,7 +580,7 @@ func _on_cast_completed() -> void:
_cast_origin_position.z + _cast_direction.z * withdrawal_cancel_distance
)
_presentation.set_line_mode(FishingPresentationType.LineMode.SLACK)
status_changed.emit("waiting for a bite...")
status_changed.emit("Waiting for a bite...")
func _update_waiting_for_bite(delta: float) -> void:
@ -698,7 +698,7 @@ func _activate_bite() -> void:
_withdrawal_input_held = false
_fight_start_position = _bobber_water_position
bite_activated.emit()
status_changed.emit("fish on!")
status_changed.emit("Fish on!")
_presentation.set_line_mode(FishingPresentationType.LineMode.TAUT)
_presentation.begin_reeling()
_catch_controller.start_encounter(
@ -800,9 +800,9 @@ func _on_catch_escaped() -> void:
if state != FishingState.FIGHTING:
return
var fish_name: String = "the fish"
var fish_name: String = "The fish"
if _selected_fish != null and not _selected_fish.display_name.is_empty():
fish_name = "the %s" % _selected_fish.display_name
fish_name = "The %s" % _selected_fish.display_name
_cleanup_attempt("%s got away!" % fish_name, &"escape")
@ -822,7 +822,7 @@ func _on_outcome_completed(outcome: StringName) -> void:
_pending_catch.weight_lb,
true
)
status_changed.emit("left click or escape to put away")
status_changed.emit("Left click or Escape to put away")
func _put_away_catch() -> void:
@ -846,7 +846,7 @@ func _put_away_catch() -> void:
_active_player.end_catch_showcase(
_finish_showcase_put_away.bind(
restore_generation,
"caught %s!" % caught_name
"Caught %s!" % caught_name
)
)
@ -926,7 +926,7 @@ func _return_to_ready() -> void:
func _cancel_attempt() -> void:
_cleanup_attempt("fishing cancelled.", &"cancel")
_cleanup_attempt("Fishing cancelled.", &"cancel")
func _cancel_from_withdrawal() -> void:

View file

@ -95,40 +95,18 @@ func contains_catch_id(catch_id: StringName) -> bool:
func remove_catch_by_id(catch_id: StringName) -> FishCatchType:
if catch_id.is_empty():
return null
var removed: Array[FishCatchType] = remove_catches_by_ids([catch_id])
return removed.front() if removed.size() == 1 else null
func remove_catches_by_ids(
catch_ids: Array[StringName],
) -> Array[FishCatchType]:
var removed: Array[FishCatchType] = []
if catch_ids.is_empty():
return removed
var requested_ids: Dictionary[StringName, bool] = {}
for catch_id: StringName in catch_ids:
if catch_id.is_empty() or requested_ids.has(catch_id):
return removed
requested_ids[catch_id] = true
for catch_id: StringName in catch_ids:
var fish_catch: FishCatchType = get_catch_by_id(catch_id)
if fish_catch == null:
removed.clear()
return removed
removed.append(fish_catch)
var remaining: Array[FishCatchType] = []
var affected_species: Dictionary[StringName, bool] = {}
for fish_catch: FishCatchType in _catches:
if requested_ids.has(fish_catch.catch_id):
affected_species[fish_catch.fish_id] = true
else:
remaining.append(fish_catch)
_catches = remaining
for fish_id: StringName in affected_species:
contents_changed.emit(fish_id, get_count(fish_id))
for index: int in range(_catches.size()):
var fish_catch: FishCatchType = _catches[index]
if fish_catch == null or fish_catch.catch_id != catch_id:
continue
_catches.remove_at(index)
contents_changed.emit(
fish_catch.fish_id,
get_count(fish_catch.fish_id)
)
catches_changed.emit()
return removed
return fish_catch
return null
func set_catch_favorited(

View file

@ -6,8 +6,8 @@
[resource]
script = ExtResource("1_script")
item_id = &"basic_fishing_rod"
display_name = "basic fishing rod"
description = "a dependable starter rod. select it to cast."
display_name = "Basic Fishing Rod"
description = "A dependable starter rod. Select it to cast."
category = 0
icon = ExtResource("2_icon")
stackable = false

View file

@ -6,8 +6,8 @@
[resource]
script = ExtResource("1")
item_id = &"coffee"
display_name = "coffee"
description = "temporarily increases ground movement speed."
display_name = "Coffee"
description = "Temporarily increases ground movement speed."
category = 3
icon = ExtResource("2")
stackable = true

View file

@ -6,8 +6,8 @@
[resource]
script = ExtResource("1")
item_id = &"cooler_expansion"
display_name = "cooler expansion"
description = "permanently increases the number of fish the cooler can hold."
display_name = "Cooler Expansion"
description = "Permanently increases the number of fish the Cooler can hold."
category = 4
icon = ExtResource("2")
stackable = false

View file

@ -6,8 +6,8 @@
[resource]
script = ExtResource("1")
item_id = &"energy_drink"
display_name = "energy drink"
description = "temporarily increases reeling progress."
display_name = "Energy Drink"
description = "Temporarily increases reeling progress."
category = 3
icon = ExtResource("2")
stackable = true

View file

@ -6,8 +6,8 @@
[resource]
script = ExtResource("1")
item_id = &"fish_finder"
display_name = "fish finder"
description = "temporarily shortens bite waits and improves rarity weights."
display_name = "Fish Finder"
description = "Temporarily shortens bite waits and improves rarity weights."
category = 3
icon = ExtResource("2")
stackable = true

View file

@ -6,8 +6,8 @@
[resource]
script = ExtResource("1")
item_id = &"snack"
display_name = "snack"
description = "temporarily adds one barrier damage."
display_name = "Snack"
description = "Temporarily adds one barrier damage."
category = 3
icon = ExtResource("2")
stackable = true

View file

@ -33,4 +33,4 @@ func is_valid() -> bool:
func get_category_name() -> String:
return Category.keys()[category].to_lower()
return Category.keys()[category].capitalize()

View file

@ -25,16 +25,10 @@ const FishingShopInteractionType = preload(
"res://world/fishing_shop_interaction.gd"
)
const TITLE_MUSIC_SILENCE_DB: float = -80.0
@export var fish_catalog: FishPoolType
@export var pelican_buyer_profile: FishBuyerProfileType
@export var main_shop_buyer_profile: FishBuyerProfileType
@export var item_catalog: ItemCatalogType
@export_category("Title Music")
@export_range(-40.0, 0.0, 0.5) var title_music_volume_db: float = -14.0
@export_range(0.0, 5.0, 0.05) var title_music_fade_in_seconds: float = 1.0
@export_range(0.0, 5.0, 0.05) var title_music_fade_out_seconds: float = 0.75
@onready var _test_world: TestWorldType = $TestWorld
@onready var _player: PlayerType = %Player
@ -43,14 +37,9 @@ const TITLE_MUSIC_SILENCE_DB: float = -80.0
@onready var _water_recovery: WaterRecoveryControllerType = %WaterRecovery
@onready var _save_manager: PlayerSaveManagerType = %PlayerSaveManager
@onready var _settings_manager: PlayerSettingsManagerType = %PlayerSettingsManager
@onready var _title_music: AudioStreamPlayer = %TitleMusic
var _gameplay_started: bool = false
var _shop_interaction: FishingShopInteractionType
var _title_music_tween: Tween
var _title_music_transition_generation: int = 0
var _title_music_requested: bool = false
var _quit_in_progress: bool = false
func _ready() -> void:
@ -155,7 +144,6 @@ func _ready() -> void:
_player.hotbar.get_selected_slot(),
_player.hotbar.get_selected_item_id()
)
_show_title_music()
func _input(event: InputEvent) -> void:
@ -238,26 +226,20 @@ func _set_gameplay_active(active: bool) -> void:
func _on_gameplay_requested() -> void:
if _gameplay_started or _quit_in_progress:
if _gameplay_started:
return
_fade_out_title_music()
_game_ui.get_title_screen().hide()
_set_gameplay_active(true)
func _on_return_to_title_requested() -> void:
if _quit_in_progress:
return
var pause_menu: PauseMenuType = _game_ui.get_pause_menu()
pause_menu.close_for_title_transition()
_set_gameplay_active(false)
_game_ui.get_title_screen().reopen()
_show_title_music()
func _on_reset_progress_requested() -> void:
if _quit_in_progress:
return
var pause_menu: PauseMenuType = _game_ui.get_pause_menu()
if not _save_manager.delete_progression_save():
pause_menu.report_reset_failure()
@ -268,7 +250,6 @@ func _on_reset_progress_requested() -> void:
pause_menu.close_for_title_transition()
_set_gameplay_active(false)
_game_ui.get_title_screen().reopen()
_show_title_music()
func _on_water_recovery_starting() -> void:
@ -301,116 +282,12 @@ func _refresh_active_hotbar_item() -> void:
func _on_quit_requested() -> void:
if _quit_in_progress:
return
_quit_in_progress = true
_settings_manager.save_if_dirty()
if _gameplay_started:
_save_manager.save_if_dirty()
_fade_out_title_music(_finish_quit)
func _show_title_music() -> void:
_title_music_requested = true
var generation: int = _replace_title_music_transition()
if not _title_music.playing:
_title_music.volume_db = TITLE_MUSIC_SILENCE_DB
_title_music.play()
if is_equal_approx(_title_music.volume_db, title_music_volume_db):
return
if title_music_fade_in_seconds <= 0.0:
_title_music.volume_db = title_music_volume_db
return
_title_music_tween = create_tween()
_title_music_tween.set_trans(Tween.TRANS_CUBIC)
_title_music_tween.set_ease(Tween.EASE_OUT)
_title_music_tween.tween_property(
_title_music,
"volume_db",
title_music_volume_db,
title_music_fade_in_seconds
)
_title_music_tween.finished.connect(
_on_title_music_faded_in.bind(generation),
CONNECT_ONE_SHOT
)
func _fade_out_title_music(on_complete: Callable = Callable()) -> void:
_title_music_requested = false
var generation: int = _replace_title_music_transition()
if not _title_music.playing:
_title_music.volume_db = title_music_volume_db
if on_complete.is_valid():
on_complete.call()
return
if title_music_fade_out_seconds <= 0.0:
_complete_title_music_fade_out(generation, on_complete)
return
_title_music_tween = create_tween()
_title_music_tween.set_trans(Tween.TRANS_CUBIC)
_title_music_tween.set_ease(Tween.EASE_IN)
_title_music_tween.tween_property(
_title_music,
"volume_db",
TITLE_MUSIC_SILENCE_DB,
title_music_fade_out_seconds
)
_title_music_tween.finished.connect(
_complete_title_music_fade_out.bind(generation, on_complete),
CONNECT_ONE_SHOT
)
func _replace_title_music_transition() -> int:
_title_music_transition_generation += 1
if _title_music_tween != null:
_title_music_tween.kill()
_title_music_tween = null
return _title_music_transition_generation
func _on_title_music_faded_in(generation: int) -> void:
if (
generation != _title_music_transition_generation
or not _title_music_requested
):
return
_title_music_tween = null
_title_music.volume_db = title_music_volume_db
func _complete_title_music_fade_out(
generation: int,
on_complete: Callable,
) -> void:
if (
generation != _title_music_transition_generation
or _title_music_requested
):
return
_title_music_tween = null
_title_music.stop()
_title_music.volume_db = title_music_volume_db
if on_complete.is_valid():
on_complete.call()
func _finish_quit() -> void:
_title_music.queue_free()
await get_tree().process_frame
get_tree().quit()
func _exit_tree() -> void:
if _title_music_tween != null:
_title_music_tween.kill()
_title_music_tween = null
if is_instance_valid(_title_music):
_title_music.stop()
_title_music.stream = null
func _on_shop_range_changed(in_range: bool) -> void:
if not in_range:
_game_ui.get_fishing_shop().close_for_range_exit()

View file

@ -1,4 +1,4 @@
[gd_scene load_steps=14 format=3]
[gd_scene load_steps=13 format=3]
[ext_resource type="PackedScene" path="res://world/test_world.tscn" id="1_world"]
[ext_resource type="PackedScene" path="res://player/player.tscn" id="2_player"]
@ -12,7 +12,6 @@
[ext_resource type="Script" path="res://settings/player_settings_manager.gd" id="10_settings"]
[ext_resource type="Resource" path="res://items/catalog/item_catalog.tres" id="11_items"]
[ext_resource type="Resource" path="res://economy/buyers/main_fishing_shop.tres" id="12_main_shop"]
[ext_resource type="AudioStream" path="res://audio/music/title/as_in_four_wolves.ogg" id="13_title_music"]
[node name="Main" type="Node3D"]
script = ExtResource("3_main")
@ -45,11 +44,5 @@ script = ExtResource("9_save")
unique_name_in_owner = true
script = ExtResource("10_settings")
[node name="TitleMusic" type="AudioStreamPlayer" parent="."]
unique_name_in_owner = true
stream = ExtResource("13_title_music")
volume_db = -80.0
bus = &"Music"
[node name="GameUI" parent="." instance=ExtResource("5_ui")]
unique_name_in_owner = true

View file

@ -56,7 +56,6 @@ class ShowcaseCameraSnapshot:
@export_range(-80.0, 80.0, 1.0) var showcase_camera_pitch: float = -8.0
@export_range(1.0, 10.0, 0.1) var showcase_camera_zoom_distance: float = 3.6
@export_range(0.5, 3.0, 0.05) var showcase_camera_target_height: float = 1.45
@export_range(0.01, 1.0, 0.01) var catch_presentation_base_scale: float = 0.10
@export_category("Camera")
@export var mouse_sensitivity: float = 0.005
@ -370,11 +369,7 @@ func begin_catch_showcase(fish_catch: FishCatchType) -> void:
_showcase_rod_state_stored = true
_fishing_rod.visible = false
_catch_sprite.texture = fish_catch.fish.display_texture
_catch_display.scale = (
Vector3.ONE
* fish_catch.display_scale
* catch_presentation_base_scale
)
_catch_display.scale = Vector3.ONE * fish_catch.display_scale
_catch_display.visible = _catch_sprite.texture != null

View file

@ -117,13 +117,13 @@ func get_rarity_weight_multiplier(rarity: int) -> float:
func get_feedback(item_id: StringName) -> String:
match item_id:
COFFEE_ID:
return "coffee active: movement speed increased."
return "Coffee active: movement speed increased."
ENERGY_DRINK_ID:
return "energy drink active: reeling speed increased."
return "Energy Drink active: reeling speed increased."
SNACK_ID:
return "snack active: barrier damage increased."
return "Snack active: barrier damage increased."
FISH_FINDER_ID:
return "fish finder active: bites and uncommon catches improved."
return "Fish Finder active: bites and uncommon catches improved."
_:
return ""

View file

@ -21,10 +21,6 @@ config/icon="res://icon.svg"
window/stretch/mode="canvas_items"
window/stretch/aspect="expand"
[gui]
theme/custom="res://ui/game_theme.tres"
[input]
move_forward={

View file

@ -227,46 +227,46 @@ func inspect_save() -> SaveInspectionType:
result.has_primary_file = FileAccess.file_exists(SAVE_PATH)
if not result.has_primary_file:
result.status = SaveInspectionType.Status.MISSING
result.message = "no save found."
result.message = "No save found."
return result
var save_file := FileAccess.open(SAVE_PATH, FileAccess.READ)
if save_file == null:
result.status = SaveInspectionType.Status.IO_ERROR
result.message = "the save could not be read."
result.message = "The save could not be read."
return result
var json := JSON.new()
var parse_error: Error = json.parse(save_file.get_as_text())
save_file.close()
if parse_error != OK or typeof(json.data) != TYPE_DICTIONARY:
result.status = SaveInspectionType.Status.MALFORMED
result.message = "the save is corrupt and was preserved."
result.message = "The save is corrupt and was preserved."
return result
var save_data: Dictionary = json.data
result.detected_version = _read_integer(save_data.get("save_version"), -1)
if result.detected_version > SAVE_VERSION:
result.status = SaveInspectionType.Status.UNSUPPORTED_VERSION
result.message = "this save belongs to a newer game version."
result.message = "This save belongs to a newer game version."
return result
if result.detected_version < 1:
result.status = SaveInspectionType.Status.MALFORMED
result.message = "the save version is unsupported."
result.message = "The save version is unsupported."
return result
if result.detected_version != SAVE_VERSION:
save_data = _migrate_save(save_data, result.detected_version)
if save_data.is_empty():
result.status = SaveInspectionType.Status.MALFORMED
result.message = "the save version is unsupported."
result.message = "The save version is unsupported."
return result
var snapshot: LoadSnapshot = _build_load_snapshot(save_data)
if snapshot == null:
result.status = SaveInspectionType.Status.MALFORMED
result.message = "the save is structurally invalid and was preserved."
result.message = "The save is structurally invalid and was preserved."
return result
result.status = SaveInspectionType.Status.VALID_SUPPORTED
result.catch_count = snapshot.catches.size()
result.wallet_balance = snapshot.wallet_balance
result.discovered_species_count = snapshot.discovered_ids.size()
result.message = "save ready."
result.message = "Save ready."
return result

Binary file not shown.

Before

Width:  |  Height:  |  Size: 372 B

View file

@ -1,40 +0,0 @@
[remap]
importer="texture"
type="CompressedTexture2D"
uid="uid://dkvilbotou8bp"
path="res://.godot/imported/bubble1.png-c853cfc8f5bc5b89bb69a71c23de669d.ctex"
metadata={
"vram_texture": false
}
[deps]
source_file="res://ui/assets/title/bubbles/bubble1.png"
dest_files=["res://.godot/imported/bubble1.png-c853cfc8f5bc5b89bb69a71c23de669d.ctex"]
[params]
compress/mode=0
compress/high_quality=false
compress/lossy_quality=0.7
compress/uastc_level=0
compress/rdo_quality_loss=0.0
compress/hdr_compression=1
compress/normal_map=0
compress/channel_pack=0
mipmaps/generate=false
mipmaps/limit=-1
roughness/mode=0
roughness/src_normal=""
process/channel_remap/red=0
process/channel_remap/green=1
process/channel_remap/blue=2
process/channel_remap/alpha=3
process/fix_alpha_border=true
process/premult_alpha=false
process/normal_map_invert_y=false
process/hdr_as_srgb=false
process/hdr_clamp_exposure=false
process/size_limit=0
detect_3d/compress_to=1

Binary file not shown.

Before

Width:  |  Height:  |  Size: 357 B

View file

@ -1,40 +0,0 @@
[remap]
importer="texture"
type="CompressedTexture2D"
uid="uid://dh11c1271egs7"
path="res://.godot/imported/bubble2.png-3fe11ab91543c1b1505cfc1100bfd0c7.ctex"
metadata={
"vram_texture": false
}
[deps]
source_file="res://ui/assets/title/bubbles/bubble2.png"
dest_files=["res://.godot/imported/bubble2.png-3fe11ab91543c1b1505cfc1100bfd0c7.ctex"]
[params]
compress/mode=0
compress/high_quality=false
compress/lossy_quality=0.7
compress/uastc_level=0
compress/rdo_quality_loss=0.0
compress/hdr_compression=1
compress/normal_map=0
compress/channel_pack=0
mipmaps/generate=false
mipmaps/limit=-1
roughness/mode=0
roughness/src_normal=""
process/channel_remap/red=0
process/channel_remap/green=1
process/channel_remap/blue=2
process/channel_remap/alpha=3
process/fix_alpha_border=true
process/premult_alpha=false
process/normal_map_invert_y=false
process/hdr_as_srgb=false
process/hdr_clamp_exposure=false
process/size_limit=0
detect_3d/compress_to=1

Binary file not shown.

Before

Width:  |  Height:  |  Size: 360 B

View file

@ -1,40 +0,0 @@
[remap]
importer="texture"
type="CompressedTexture2D"
uid="uid://b5praw1x4vukl"
path="res://.godot/imported/bubble3.png-7e3e34f881bccf21cec803168829b93e.ctex"
metadata={
"vram_texture": false
}
[deps]
source_file="res://ui/assets/title/bubbles/bubble3.png"
dest_files=["res://.godot/imported/bubble3.png-7e3e34f881bccf21cec803168829b93e.ctex"]
[params]
compress/mode=0
compress/high_quality=false
compress/lossy_quality=0.7
compress/uastc_level=0
compress/rdo_quality_loss=0.0
compress/hdr_compression=1
compress/normal_map=0
compress/channel_pack=0
mipmaps/generate=false
mipmaps/limit=-1
roughness/mode=0
roughness/src_normal=""
process/channel_remap/red=0
process/channel_remap/green=1
process/channel_remap/blue=2
process/channel_remap/alpha=3
process/fix_alpha_border=true
process/premult_alpha=false
process/normal_map_invert_y=false
process/hdr_as_srgb=false
process/hdr_clamp_exposure=false
process/size_limit=0
detect_3d/compress_to=1

Binary file not shown.

Before

Width:  |  Height:  |  Size: 22 KiB

View file

@ -1,40 +0,0 @@
[remap]
importer="texture"
type="CompressedTexture2D"
uid="uid://ckqayis1ckb2v"
path="res://.godot/imported/netfishing_logo.png-82ab9fce5851dcc5239eca178e8cde99.ctex"
metadata={
"vram_texture": false
}
[deps]
source_file="res://ui/assets/title/netfishing_logo.png"
dest_files=["res://.godot/imported/netfishing_logo.png-82ab9fce5851dcc5239eca178e8cde99.ctex"]
[params]
compress/mode=0
compress/high_quality=false
compress/lossy_quality=0.7
compress/uastc_level=0
compress/rdo_quality_loss=0.0
compress/hdr_compression=1
compress/normal_map=0
compress/channel_pack=0
mipmaps/generate=false
mipmaps/limit=-1
roughness/mode=0
roughness/src_normal=""
process/channel_remap/red=0
process/channel_remap/green=1
process/channel_remap/blue=2
process/channel_remap/alpha=3
process/fix_alpha_border=true
process/premult_alpha=false
process/normal_map_invert_y=false
process/hdr_as_srgb=false
process/hdr_clamp_exposure=false
process/size_limit=0
detect_3d/compress_to=1

View file

@ -1,106 +0,0 @@
class_name FishBatchSelection
extends RefCounted
var _selected_ids: Dictionary[StringName, bool] = {}
var _visible_ids: Array[StringName] = []
var _focused_id: StringName
var _anchor_id: StringName
func set_visible_order(visible_ids: Array[StringName]) -> void:
_visible_ids = visible_ids.duplicate()
var visible_set: Dictionary[StringName, bool] = {}
for catch_id: StringName in _visible_ids:
if not catch_id.is_empty():
visible_set[catch_id] = true
for catch_id: StringName in _selected_ids.keys():
if not visible_set.has(catch_id):
_selected_ids.erase(catch_id)
if not visible_set.has(_focused_id):
_focused_id = StringName()
if not visible_set.has(_anchor_id):
_anchor_id = StringName()
func apply_click(
catch_id: StringName,
ctrl_pressed: bool,
shift_pressed: bool,
) -> void:
if catch_id.is_empty() or not _visible_ids.has(catch_id):
return
if not shift_pressed or _anchor_id.is_empty():
if ctrl_pressed:
if _selected_ids.has(catch_id):
_selected_ids.erase(catch_id)
else:
_selected_ids[catch_id] = true
else:
_selected_ids.clear()
_selected_ids[catch_id] = true
_focused_id = catch_id
_anchor_id = catch_id
return
var anchor_index: int = _visible_ids.find(_anchor_id)
var target_index: int = _visible_ids.find(catch_id)
if anchor_index < 0 or target_index < 0:
select_only(catch_id)
return
if not ctrl_pressed:
_selected_ids.clear()
var first_index: int = mini(anchor_index, target_index)
var last_index: int = maxi(anchor_index, target_index)
for index: int in range(first_index, last_index + 1):
_selected_ids[_visible_ids[index]] = true
_focused_id = catch_id
func select_only(catch_id: StringName) -> void:
_selected_ids.clear()
if catch_id.is_empty() or not _visible_ids.has(catch_id):
_focused_id = StringName()
_anchor_id = StringName()
return
_selected_ids[catch_id] = true
_focused_id = catch_id
_anchor_id = catch_id
func clear() -> void:
_selected_ids.clear()
_focused_id = StringName()
_anchor_id = StringName()
func remove_ids(catch_ids: Array[StringName]) -> void:
for catch_id: StringName in catch_ids:
_selected_ids.erase(catch_id)
if _focused_id == catch_id:
_focused_id = StringName()
if _anchor_id == catch_id:
_anchor_id = StringName()
func get_selected_ids() -> Array[StringName]:
var selected_in_order: Array[StringName] = []
for catch_id: StringName in _visible_ids:
if _selected_ids.has(catch_id):
selected_in_order.append(catch_id)
return selected_in_order
func is_selected(catch_id: StringName) -> bool:
return _selected_ids.has(catch_id)
func get_selected_count() -> int:
return _selected_ids.size()
func get_focused_id() -> StringName:
return _focused_id
func get_anchor_id() -> StringName:
return _anchor_id

View file

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

View file

@ -24,9 +24,6 @@ const PlayerCoolerCapacityType = preload(
const ShopInteractionType = preload(
"res://world/fishing_shop_interaction.gd"
)
const FishBatchSelectionType = preload(
"res://ui/fish_batch_selection.gd"
)
signal menu_visibility_changed(is_open: bool)
@ -45,7 +42,6 @@ enum CloseReason {
@onready var _fish_texture: TextureRect = %FishTexture
@onready var _fish_name: Label = %FishName
@onready var _fish_details: Label = %FishDetails
@onready var _selection_summary: Label = %SelectionSummary
@onready var _sell_button: Button = %SellButton
@onready var _feedback: Label = %Feedback
@onready var _reel_level: Label = %ReelLevel
@ -77,16 +73,14 @@ var _interaction: ShopInteractionType
var _bag: PlayerBagType
var _item_catalog: ItemCatalogType
var _cooler_capacity: PlayerCoolerCapacityType
var _fish_selection := FishBatchSelectionType.new()
var _confirmation_catch_ids: Array[StringName] = []
var _confirmation_generation: int = -1
var _selected_catch_id: StringName
var _confirmation_catch_id: StringName
var _prior_movement_enabled: bool = true
var _prior_camera_enabled: bool = true
var _prior_mouse_mode: Input.MouseMode = Input.MOUSE_MODE_VISIBLE
var _snapshot_stored: bool = false
var _mouse_snapshot_stored: bool = false
var _generation: int = 0
var _selection_input_generation: int = 0
var _transaction_in_progress: bool = false
var _closing: bool = false
@ -94,7 +88,6 @@ var _closing: bool = false
func _ready() -> void:
%CloseButton.pressed.connect(close_shop)
_fish_list.item_selected.connect(_on_fish_selected)
_fish_list.item_clicked.connect(_on_fish_clicked)
_sell_button.pressed.connect(_open_sale_confirmation)
_confirm_sale.pressed.connect(_on_confirm_sale)
_cancel_sale.pressed.connect(_close_sale_confirmation)
@ -127,7 +120,6 @@ func setup(
_bag = bag
_item_catalog = item_catalog
_cooler_capacity = cooler_capacity
_fish_selection.clear()
if not _inventory.catches_changed.is_connected(_on_inventory_changed):
_inventory.catches_changed.connect(_on_inventory_changed)
if not _wallet.balance_changed.is_connected(_on_wallet_changed):
@ -197,7 +189,7 @@ func close_shop(
_generation += 1
_transaction_in_progress = false
_close_sale_confirmation()
_fish_selection.clear()
_selected_catch_id = StringName()
hide()
get_viewport().gui_release_focus()
if _fishing_spot != null and is_instance_valid(_fishing_spot):
@ -250,9 +242,9 @@ func _refresh_all() -> void:
func _refresh_wallet() -> void:
_wallet_label.text = (
"wallet: $%d" % _wallet.get_balance()
"Wallet: $%d" % _wallet.get_balance()
if _wallet != null
else "wallet: $0"
else "Wallet: $0"
)
@ -261,7 +253,7 @@ func _refresh_fish_list() -> void:
var catches: Array[FishCatchType] = (
_inventory.get_all_catches() if _inventory != null else []
)
_sales_title.text = "cooler %d / %d • full base value" % [
_sales_title.text = "Cooler %d / %d • Full base value" % [
catches.size(),
_cooler_capacity.get_capacity() if _cooler_capacity != null else 0,
]
@ -269,21 +261,11 @@ func _refresh_fish_list() -> void:
func(a: FishCatchType, b: FishCatchType) -> bool:
return a.catch_sequence > b.catch_sequence
)
var visible_ids: Array[StringName] = []
for fish_catch: FishCatchType in catches:
if fish_catch != null and fish_catch.is_valid():
visible_ids.append(fish_catch.catch_id)
_fish_selection.set_visible_order(visible_ids)
var selected_index: int = -1
for fish_catch: FishCatchType in catches:
if fish_catch == null or not fish_catch.is_valid():
continue
var marker: String = ""
if _fish_selection.is_selected(fish_catch.catch_id):
marker += ""
if fish_catch.catch_id == _fish_selection.get_focused_id():
marker += ""
if fish_catch.is_favorited:
marker += ""
var marker: String = "" if fish_catch.is_favorited else ""
var index: int = _fish_list.add_item(
"%s%s%.2f lb"
% [marker, fish_catch.fish.display_name, fish_catch.weight_lb],
@ -295,15 +277,16 @@ func _refresh_fish_list() -> void:
"%s%s"
% [
fish_catch.fish.get_rarity_name(),
"favorited" if fish_catch.is_favorited else "available",
"Favorited" if fish_catch.is_favorited else "Available",
]
)
if fish_catch.catch_id == _selected_catch_id:
selected_index = index
_fish_empty.visible = _fish_list.item_count == 0
_fish_list.deselect_all()
for index: int in range(_fish_list.item_count):
var catch_id := StringName(str(_fish_list.get_item_metadata(index)))
if _fish_selection.is_selected(catch_id):
_fish_list.select(index, false)
if selected_index >= 0:
_fish_list.select(selected_index)
elif not _selected_catch_id.is_empty():
_selected_catch_id = StringName()
func _refresh_selected_fish() -> void:
@ -312,72 +295,27 @@ func _refresh_selected_fish() -> void:
fish_catch.fish.display_texture if fish_catch != null else null
)
_fish_name.text = (
fish_catch.fish.display_name if fish_catch != null else "select a fish"
fish_catch.fish.display_name if fish_catch != null else "Select a fish"
)
if fish_catch == null:
_fish_details.text = "choose one individual fish from your cooler."
_update_sale_summary()
_fish_details.text = "Choose one individual fish from your Cooler."
_sell_button.disabled = true
return
var individual_preview: FishSaleResultType = (
_sale_service.preview_batch([fish_catch.catch_id], _buyer)
if _sale_service != null
else null
)
var offer: int = (
individual_preview.payout if individual_preview != null else -1
)
var offer: int = _buyer.get_offer(fish_catch.sale_value)
_fish_details.text = (
"%.2f lb • %s\nbase value: $%d\nmain-shop offer: $%d%s"
"%.2f lb • %s\nBase value: $%d\nMain-shop offer: $%d%s"
% [
fish_catch.weight_lb,
fish_catch.fish.get_rarity_name(),
fish_catch.sale_value,
offer,
"\nfavorited fish cannot be sold."
"\nFavorited fish cannot be sold."
if fish_catch.is_favorited
else "",
]
)
_update_sale_summary()
func _update_sale_summary() -> void:
var selected_ids: Array[StringName] = _fish_selection.get_selected_ids()
var selected_count: int = selected_ids.size()
_sell_button.text = (
"sell fish"
if selected_count <= 1
else "sell %d fish" % selected_count
)
if selected_count == 0:
_selection_summary.text = "no fish selected"
_sell_button.disabled = true
return
var preview: FishSaleResultType = (
_sale_service.preview_batch(selected_ids, _buyer)
if _sale_service != null
else null
)
_selection_summary.text = (
"1 fish selected"
if selected_count == 1
else "%d fish selected" % selected_count
)
if (
preview != null
and (
preview.is_success()
or preview.status == FishSaleResultType.Status.FAVORITED
)
):
_selection_summary.text += "\ntotal offer: $%d" % preview.payout
if preview != null and preview.status == FishSaleResultType.Status.FAVORITED:
_selection_summary.text += (
"\nfavorited fish must be removed from the selection."
)
_sell_button.disabled = (
preview == null
or not preview.is_success()
fish_catch.is_favorited
or _transaction_in_progress
or _closing
)
@ -388,7 +326,7 @@ func _refresh_upgrades() -> void:
return
var reel_level: int = _upgrades.get_reel_speed_level()
var reel_cost: int = _upgrades.get_next_reel_speed_cost()
_reel_level.text = "level %d" % reel_level
_reel_level.text = "Level %d" % reel_level
_reel_purchase.disabled = (
reel_cost < 0
or _transaction_in_progress
@ -397,7 +335,7 @@ func _refresh_upgrades() -> void:
)
if reel_cost < 0:
_reel_effect.text = "%.2f×" % _upgrades.get_reel_speed_multiplier()
_reel_cost.text = "max"
_reel_cost.text = "MAX"
else:
_reel_effect.text = (
"%.2f×%.2f×"
@ -407,10 +345,10 @@ func _refresh_upgrades() -> void:
]
)
_reel_cost.text = "$%d" % reel_cost
_reel_purchase.text = "max" if reel_cost < 0 else "purchase"
_reel_purchase.text = "MAX" if reel_cost < 0 else "Purchase"
var barrier_level: int = _upgrades.get_barrier_power_level()
var barrier_cost: int = _upgrades.get_next_barrier_power_cost()
_barrier_level.text = "level %d" % barrier_level
_barrier_level.text = "Level %d" % barrier_level
_barrier_purchase.disabled = (
barrier_cost < 0
or _transaction_in_progress
@ -419,7 +357,7 @@ func _refresh_upgrades() -> void:
)
if barrier_cost < 0:
_barrier_effect.text = "%d damage" % _upgrades.get_barrier_damage()
_barrier_cost.text = "max"
_barrier_cost.text = "MAX"
else:
_barrier_effect.text = (
"%d damage → %d damage"
@ -429,7 +367,7 @@ func _refresh_upgrades() -> void:
]
)
_barrier_cost.text = "$%d" % barrier_cost
_barrier_purchase.text = "max" if barrier_cost < 0 else "purchase"
_barrier_purchase.text = "MAX" if barrier_cost < 0 else "Purchase"
func _refresh_supplies() -> void:
@ -447,7 +385,7 @@ func _refresh_supplies() -> void:
button.icon = item.icon
button.expand_icon = true
button.alignment = HORIZONTAL_ALIGNMENT_LEFT
button.text = "%s\n$%downed %d" % [
button.text = "%s\n$%dOwned %d" % [
item.display_name,
FishingShopStockType.get_price(item_id),
_bag.get_quantity(item_id),
@ -471,7 +409,7 @@ func _refresh_cooler_capacity() -> void:
var level: int = _cooler_capacity.get_level()
var cost: int = _cooler_capacity.get_next_cost()
var next_capacity: int = _cooler_capacity.get_next_capacity()
_cooler_level.text = "level %d" % level
_cooler_level.text = "Level %d" % level
_cooler_purchase.disabled = (
cost < 0
or _transaction_in_progress
@ -480,99 +418,46 @@ func _refresh_cooler_capacity() -> void:
)
if cost < 0:
_cooler_effect.text = "%d fish" % _cooler_capacity.get_capacity()
_cooler_cost.text = "max"
_cooler_purchase.text = "max"
_cooler_cost.text = "MAX"
_cooler_purchase.text = "MAX"
else:
_cooler_effect.text = "%d%d fish" % [
_cooler_capacity.get_capacity(),
next_capacity,
]
_cooler_cost.text = "$%d" % cost
_cooler_purchase.text = "purchase"
_cooler_purchase.text = "Purchase"
func _on_fish_selected(index: int) -> void:
if index < 0 or index >= _fish_list.item_count:
return
var input_generation: int = _selection_input_generation
call_deferred(
"_apply_keyboard_fish_selection",
index,
input_generation
)
func _apply_keyboard_fish_selection(
index: int,
input_generation: int,
) -> void:
if (
input_generation != _selection_input_generation
or index < 0
or index >= _fish_list.item_count
):
return
var catch_id := StringName(str(_fish_list.get_item_metadata(index)))
_fish_selection.select_only(catch_id)
_selected_catch_id = StringName(str(_fish_list.get_item_metadata(index)))
_feedback.text = ""
_refresh_fish_list()
_refresh_selected_fish()
func _on_fish_clicked(
index: int,
_position: Vector2,
mouse_button_index: int,
) -> void:
if (
mouse_button_index != MOUSE_BUTTON_LEFT
or index < 0
or index >= _fish_list.item_count
):
return
_selection_input_generation += 1
var catch_id := StringName(str(_fish_list.get_item_metadata(index)))
_fish_selection.apply_click(
catch_id,
Input.is_key_pressed(KEY_CTRL),
Input.is_key_pressed(KEY_SHIFT)
)
_feedback.text = ""
_refresh_fish_list()
_refresh_selected_fish()
func _open_sale_confirmation() -> void:
var selected_ids: Array[StringName] = _fish_selection.get_selected_ids()
if not _is_transaction_context_valid() or selected_ids.is_empty():
_feedback.text = "the fish selection is no longer available."
var fish_catch: FishCatchType = _get_selected_catch()
if not _is_transaction_context_valid() or fish_catch == null:
_feedback.text = "This fish is no longer available."
_refresh_all()
return
var preview: FishSaleResultType = _sale_service.preview_batch(
selected_ids,
_buyer
)
if not preview.is_success():
_feedback.text = (
"favorited fish cannot be sold. "
+ "remove them from the selection first."
if preview.status == FishSaleResultType.Status.FAVORITED
else preview.get_message()
)
if fish_catch.is_favorited:
_feedback.text = "Favorited fish cannot be sold."
return
_confirmation_catch_ids = selected_ids.duplicate()
_confirmation_generation = _generation
_confirmation_catch_id = fish_catch.catch_id
var offer: int = _buyer.get_offer(fish_catch.sale_value)
_confirmation_text.text = (
"sell %d fish to the fishing shop for $%d?"
% [preview.fish_count, preview.payout]
"Sell this %.2f lb %s to the Fishing Shop for $%d?"
% [fish_catch.weight_lb, fish_catch.fish.display_name, offer]
)
_confirmation.show()
_cancel_sale.grab_focus()
func _close_sale_confirmation() -> void:
_confirmation_catch_ids.clear()
_confirmation_generation = -1
_confirmation_catch_id = StringName()
_confirmation.hide()
@ -580,23 +465,18 @@ func _on_confirm_sale() -> void:
if _transaction_in_progress:
return
var transaction_generation: int = _generation
var catch_ids: Array[StringName] = _confirmation_catch_ids.duplicate()
var confirmation_generation: int = _confirmation_generation
var catch_id: StringName = _confirmation_catch_id
_close_sale_confirmation()
if (
catch_ids.is_empty()
catch_id.is_empty()
or not _is_transaction_context_valid()
or transaction_generation != _generation
or confirmation_generation != transaction_generation
or _buyer.id != MAIN_SHOP_BUYER_ID
):
_feedback.text = "unable to complete sale."
_feedback.text = "Unable to complete sale."
return
_transaction_in_progress = true
var result: FishSaleResultType = _sale_service.sell_batch(
catch_ids,
_buyer
)
var result: FishSaleResultType = _sale_service.sell(catch_id, _buyer)
_transaction_in_progress = false
if (
transaction_generation != _generation
@ -604,15 +484,8 @@ func _on_confirm_sale() -> void:
):
return
if result.is_success():
_feedback.text = (
"fish sold for $%d."
if result.fish_count == 1
else "%d fish sold for $%d." % [
result.fish_count,
result.payout,
]
)
_fish_selection.remove_ids(catch_ids)
_feedback.text = "Fish sold for $%d." % result.payout
_selected_catch_id = StringName()
else:
_feedback.text = result.get_message()
_refresh_all()
@ -628,14 +501,14 @@ func _purchase_barrier_power() -> void:
func _purchase_supply(item_id: StringName) -> void:
if _transaction_in_progress or not _is_transaction_context_valid():
_feedback.text = "unable to complete purchase."
_feedback.text = "Unable to complete purchase."
return
var price: int = FishingShopStockType.get_price(item_id)
if not _bag.can_add_item(item_id, 1):
_feedback.text = "bag cannot accept this item."
_feedback.text = "Bag cannot accept this item."
return
if not _wallet.can_afford(price):
_feedback.text = "not enough money."
_feedback.text = "Not enough money."
return
var transaction_generation: int = _generation
_transaction_in_progress = true
@ -649,21 +522,21 @@ func _purchase_supply(item_id: StringName) -> void:
if transaction_generation != _generation or not visible:
return
_feedback.text = (
"item purchased." if purchased else "unable to complete purchase."
"Item purchased." if purchased else "Unable to complete purchase."
)
_refresh_all()
func _purchase_cooler_capacity() -> void:
if _transaction_in_progress or not _is_transaction_context_valid():
_feedback.text = "unable to complete purchase."
_feedback.text = "Unable to complete purchase."
return
var cost: int = _cooler_capacity.get_next_cost()
if cost < 0:
_feedback.text = "maximum level reached."
_feedback.text = "Maximum level reached."
return
if not _wallet.can_afford(cost):
_feedback.text = "not enough money."
_feedback.text = "Not enough money."
return
var transaction_generation: int = _generation
_transaction_in_progress = true
@ -672,16 +545,16 @@ func _purchase_cooler_capacity() -> void:
if transaction_generation != _generation or not visible:
return
_feedback.text = (
"upgrade purchased."
"Upgrade purchased."
if purchased
else "unable to complete purchase."
else "Unable to complete purchase."
)
_refresh_all()
func _purchase_upgrade(is_reel_speed: bool) -> void:
if _transaction_in_progress or not _is_transaction_context_valid():
_feedback.text = "unable to complete purchase."
_feedback.text = "Unable to complete purchase."
return
var cost: int = (
_upgrades.get_next_reel_speed_cost()
@ -689,10 +562,10 @@ func _purchase_upgrade(is_reel_speed: bool) -> void:
else _upgrades.get_next_barrier_power_cost()
)
if cost < 0:
_feedback.text = "maximum level reached."
_feedback.text = "Maximum level reached."
return
if not _wallet.can_afford(cost):
_feedback.text = "not enough money."
_feedback.text = "Not enough money."
return
var transaction_generation: int = _generation
_transaction_in_progress = true
@ -705,18 +578,17 @@ func _purchase_upgrade(is_reel_speed: bool) -> void:
if transaction_generation != _generation or not visible:
return
_feedback.text = (
"upgrade purchased."
"Upgrade purchased."
if purchased
else "unable to complete purchase."
else "Unable to complete purchase."
)
_refresh_all()
func _get_selected_catch() -> FishCatchType:
var focused_id: StringName = _fish_selection.get_focused_id()
if _inventory == null or focused_id.is_empty():
if _inventory == null or _selected_catch_id.is_empty():
return null
return _inventory.get_catch_by_id(focused_id)
return _inventory.get_catch_by_id(_selected_catch_id)
func _is_transaction_context_valid() -> bool:
@ -735,22 +607,12 @@ func _is_transaction_context_valid() -> bool:
func _on_inventory_changed() -> void:
_refresh_fish_list()
if not _confirmation_catch_ids.is_empty():
var preview: FishSaleResultType = _sale_service.preview_batch(
_confirmation_catch_ids,
_buyer
)
if (
_confirmation_generation != _generation
or not preview.is_success()
not _confirmation_catch_id.is_empty()
and _inventory.get_catch_by_id(_confirmation_catch_id) == null
):
_close_sale_confirmation()
_feedback.text = (
"favorited fish cannot be sold. "
+ "remove them from the selection first."
if preview.status == FishSaleResultType.Status.FAVORITED
else preview.get_message()
)
_feedback.text = "This fish is no longer available."
_refresh_selected_fish()

View file

@ -63,7 +63,7 @@ theme_override_constants/separation = 8
[node name="Title" type="Label" parent="ShopPanel/Margin/Layout/Header"]
layout_mode = 2
size_flags_horizontal = 3
text = "fishing shop"
text = "Fishing Shop"
theme_override_font_sizes/font_size = 24
theme_override_colors/font_color = Color(0.208, 0.725, 0.78, 1)
@ -78,12 +78,12 @@ texture_filter = 1
[node name="WalletLabel" type="Label" parent="ShopPanel/Margin/Layout/Header"]
unique_name_in_owner = true
layout_mode = 2
text = "wallet: $0"
text = "Wallet: $0"
[node name="CloseButton" type="Button" parent="ShopPanel/Margin/Layout/Header"]
unique_name_in_owner = true
layout_mode = 2
text = "close"
text = "Close"
custom_minimum_size = Vector2(72, 30)
[node name="Feedback" type="Label" parent="ShopPanel/Margin/Layout"]
@ -107,7 +107,7 @@ theme_override_constants/separation = 6
[node name="SalesTitle" type="Label" parent="ShopPanel/Margin/Layout/Body/FishSales"]
unique_name_in_owner = true
layout_mode = 2
text = "sell individual cooler fish • full base value"
text = "Sell individual Cooler fish • Full base value"
theme_override_font_sizes/font_size = 17
[node name="SalesBody" type="HSplitContainer" parent="ShopPanel/Margin/Layout/Body/FishSales"]
@ -133,7 +133,7 @@ layout_mode = 2
[node name="FishEmpty" type="Label" parent="ShopPanel/Margin/Layout/Body/FishSales/SalesBody/ListPanel/ListMargin/ListStack"]
unique_name_in_owner = true
layout_mode = 2
text = "your cooler is empty."
text = "Your Cooler is empty."
horizontal_alignment = 1
[node name="FishList" type="ItemList" parent="ShopPanel/Margin/Layout/Body/FishSales/SalesBody/ListPanel/ListMargin/ListStack"]
@ -142,7 +142,6 @@ layout_mode = 2
size_flags_vertical = 3
fixed_icon_size = Vector2i(48, 36)
icon_mode = 1
select_mode = 1
same_column_width = true
[node name="FishDetail" type="PanelContainer" parent="ShopPanel/Margin/Layout/Body/FishSales/SalesBody"]
@ -171,30 +170,22 @@ texture_filter = 1
[node name="FishName" type="Label" parent="ShopPanel/Margin/Layout/Body/FishSales/SalesBody/FishDetail/DetailMargin/DetailStack"]
unique_name_in_owner = true
layout_mode = 2
text = "select a fish"
text = "Select a fish"
horizontal_alignment = 1
theme_override_font_sizes/font_size = 19
[node name="FishDetails" type="Label" parent="ShopPanel/Margin/Layout/Body/FishSales/SalesBody/FishDetail/DetailMargin/DetailStack"]
unique_name_in_owner = true
layout_mode = 2
text = "choose one individual fish from your cooler."
text = "Choose one individual fish from your Cooler."
horizontal_alignment = 1
autowrap_mode = 2
[node name="SelectionSummary" type="Label" parent="ShopPanel/Margin/Layout/Body/FishSales/SalesBody/FishDetail/DetailMargin/DetailStack"]
unique_name_in_owner = true
layout_mode = 2
text = "no fish selected"
horizontal_alignment = 1
autowrap_mode = 2
theme_override_colors/font_color = Color(0.208, 0.725, 0.78, 1)
[node name="SellButton" type="Button" parent="ShopPanel/Margin/Layout/Body/FishSales/SalesBody/FishDetail/DetailMargin/DetailStack"]
unique_name_in_owner = true
layout_mode = 2
disabled = true
text = "sell selected"
text = "Sell Selected"
[node name="Upgrades" type="VBoxContainer" parent="ShopPanel/Margin/Layout/Body"]
custom_minimum_size = Vector2(270, 0)
@ -203,7 +194,7 @@ theme_override_constants/separation = 8
[node name="UpgradeTitle" type="Label" parent="ShopPanel/Margin/Layout/Body/Upgrades"]
layout_mode = 2
text = "fishing upgrades"
text = "Fishing Upgrades"
theme_override_font_sizes/font_size = 17
[node name="Supplies" type="VBoxContainer" parent="ShopPanel/Margin/Layout/Body"]
@ -213,7 +204,7 @@ theme_override_constants/separation = 6
[node name="Title" type="Label" parent="ShopPanel/Margin/Layout/Body/Supplies"]
layout_mode = 2
text = "supplies"
text = "Supplies"
theme_override_font_sizes/font_size = 17
[node name="Scroll" type="ScrollContainer" parent="ShopPanel/Margin/Layout/Body/Supplies"]
@ -255,13 +246,13 @@ size_flags_horizontal = 3
[node name="Name" type="Label" parent="ShopPanel/Margin/Layout/Body/Upgrades/ReelCard/Margin/Row/Data"]
layout_mode = 2
text = "reel speed"
text = "Reel Speed"
theme_override_font_sizes/font_size = 17
[node name="ReelLevel" type="Label" parent="ShopPanel/Margin/Layout/Body/Upgrades/ReelCard/Margin/Row/Data"]
unique_name_in_owner = true
layout_mode = 2
text = "level 0"
text = "Level 0"
[node name="ReelEffect" type="Label" parent="ShopPanel/Margin/Layout/Body/Upgrades/ReelCard/Margin/Row/Data"]
unique_name_in_owner = true
@ -276,7 +267,7 @@ text = "$50"
[node name="ReelPurchase" type="Button" parent="ShopPanel/Margin/Layout/Body/Upgrades/ReelCard/Margin/Row"]
unique_name_in_owner = true
layout_mode = 2
text = "purchase"
text = "Purchase"
[node name="BarrierCard" type="PanelContainer" parent="ShopPanel/Margin/Layout/Body/Upgrades"]
layout_mode = 2
@ -306,13 +297,13 @@ size_flags_horizontal = 3
[node name="Name" type="Label" parent="ShopPanel/Margin/Layout/Body/Upgrades/BarrierCard/Margin/Row/Data"]
layout_mode = 2
text = "barrier power"
text = "Barrier Power"
theme_override_font_sizes/font_size = 17
[node name="BarrierLevel" type="Label" parent="ShopPanel/Margin/Layout/Body/Upgrades/BarrierCard/Margin/Row/Data"]
unique_name_in_owner = true
layout_mode = 2
text = "level 0"
text = "Level 0"
[node name="BarrierEffect" type="Label" parent="ShopPanel/Margin/Layout/Body/Upgrades/BarrierCard/Margin/Row/Data"]
unique_name_in_owner = true
@ -327,7 +318,7 @@ text = "$100"
[node name="BarrierPurchase" type="Button" parent="ShopPanel/Margin/Layout/Body/Upgrades/BarrierCard/Margin/Row"]
unique_name_in_owner = true
layout_mode = 2
text = "purchase"
text = "Purchase"
[node name="CoolerCard" type="PanelContainer" parent="ShopPanel/Margin/Layout/Body/Upgrades"]
layout_mode = 2
@ -357,13 +348,13 @@ size_flags_horizontal = 3
[node name="Name" type="Label" parent="ShopPanel/Margin/Layout/Body/Upgrades/CoolerCard/Margin/Row/Data"]
layout_mode = 2
text = "cooler capacity"
text = "Cooler Capacity"
theme_override_font_sizes/font_size = 17
[node name="CoolerLevel" type="Label" parent="ShopPanel/Margin/Layout/Body/Upgrades/CoolerCard/Margin/Row/Data"]
unique_name_in_owner = true
layout_mode = 2
text = "level 0"
text = "Level 0"
[node name="CoolerEffect" type="Label" parent="ShopPanel/Margin/Layout/Body/Upgrades/CoolerCard/Margin/Row/Data"]
unique_name_in_owner = true
@ -378,7 +369,7 @@ text = "$75"
[node name="CoolerPurchase" type="Button" parent="ShopPanel/Margin/Layout/Body/Upgrades/CoolerCard/Margin/Row"]
unique_name_in_owner = true
layout_mode = 2
text = "purchase"
text = "Purchase"
[node name="SaleConfirmation" type="PanelContainer" parent="."]
unique_name_in_owner = true
@ -422,10 +413,10 @@ theme_override_constants/separation = 8
[node name="ConfirmSale" type="Button" parent="SaleConfirmation/Margin/Stack/Buttons"]
unique_name_in_owner = true
layout_mode = 2
text = "sell fish"
text = "Sell Fish"
theme_type_variation = &"DangerButton"
[node name="CancelSale" type="Button" parent="SaleConfirmation/Margin/Stack/Buttons"]
unique_name_in_owner = true
layout_mode = 2
text = "cancel"
text = "Cancel"

Binary file not shown.

View file

@ -1,36 +0,0 @@
[remap]
importer="font_data_dynamic"
type="FontFile"
uid="uid://ddbejmxpa3kw6"
path="res://.godot/imported/seattle_avenue.otf-6438e7427b67d392fd88ff9a0c47fc59.fontdata"
[deps]
source_file="res://ui/fonts/seattle_avenue.otf"
dest_files=["res://.godot/imported/seattle_avenue.otf-6438e7427b67d392fd88ff9a0c47fc59.fontdata"]
[params]
Rendering=null
antialiasing=1
generate_mipmaps=false
disable_embedded_bitmaps=true
multichannel_signed_distance_field=false
msdf_pixel_range=8
msdf_size=48
allow_system_fallback=true
force_autohinter=false
modulate_color_glyphs=false
hinting=3
subpixel_positioning=4
keep_rounding_remainders=true
oversampling=0.0
Fallbacks=null
fallbacks=[]
Compress=null
compress=true
preload=[]
language_support={}
script_support={}
opentype_features={}

View file

@ -1,6 +1,4 @@
[gd_resource type="Theme" load_steps=13 format=3]
[ext_resource type="FontFile" uid="uid://ddbejmxpa3kw6" path="res://ui/fonts/seattle_avenue.otf" id="1_font"]
[gd_resource type="Theme" load_steps=12 format=3]
[sub_resource type="StyleBoxFlat" id="Panel"]
bg_color = Color(0.075, 0.145, 0.17, 0.96)
@ -145,7 +143,6 @@ corner_radius_bottom_right = 6
corner_radius_bottom_left = 6
[resource]
default_font = ExtResource("1_font")
default_font_size = 15
Label/colors/font_color = Color(0.956, 0.972, 0.976, 1)
Label/colors/font_shadow_color = Color(0, 0, 0, 0)

View file

@ -129,10 +129,10 @@ func _process(_delta: float) -> void:
if remaining <= 0.0:
continue
var label: String = {
PlayerItemEffectsType.COFFEE_ID: "coffee",
PlayerItemEffectsType.ENERGY_DRINK_ID: "energy",
PlayerItemEffectsType.SNACK_ID: "snack",
PlayerItemEffectsType.FISH_FINDER_ID: "finder",
PlayerItemEffectsType.COFFEE_ID: "Coffee",
PlayerItemEffectsType.ENERGY_DRINK_ID: "Energy",
PlayerItemEffectsType.SNACK_ID: "Snack",
PlayerItemEffectsType.FISH_FINDER_ID: "Finder",
}.get(item_id, "")
parts.append(
"%s %d:%02d"
@ -296,7 +296,7 @@ func _on_catch_display_changed(
"%d%% %s"
% [roundi(barrier_positions[barrier_index] * 100.0), marker]
)
_barrier_summary.text = "barriers: %s" % " ".join(barrier_labels)
_barrier_summary.text = "Barriers: %s" % " ".join(barrier_labels)
if (
active_barrier_index >= 0
@ -304,14 +304,14 @@ func _on_catch_display_changed(
and active_barrier_index < barrier_max_health.size()
):
_barrier_health.text = (
"barrier: %d / %d"
"Barrier: %d / %d"
% [
barrier_health[active_barrier_index],
barrier_max_health[active_barrier_index],
]
)
else:
_barrier_health.text = "hold left click to reel"
_barrier_health.text = "Hold left click to reel"
var chase_gap: float = progress - chase_progress
if chase_gap <= 0.05:
@ -319,7 +319,7 @@ func _on_catch_display_changed(
"%s%s"
% [
_barrier_health.text,
"\nred is closing in!",
"\nRed is closing in!",
]
).strip_edges()
_refresh_fishing_panel_visibility()
@ -377,11 +377,11 @@ func _on_showcase_changed(
_barrier_health.visible = false
_clear_barrier_markers()
_showcase_details.text = (
"%.1f lb • %s\nleft click or escape to put away"
"%.1f lb • %s\nLeft click or Escape to put away"
% [weight_lb, rarity_name]
)
_showcase_details.visible = true
_set_fishing_status("you caught a %s!" % fish_name)
_set_fishing_status("You caught a %s!" % fish_name)
func _on_player_menu_visibility_changed(is_open: bool) -> void:

View file

@ -161,7 +161,7 @@ theme_override_constants/margin_bottom = 7
[node name="Label" type="Label" parent="ShopPrompt/Margin"]
layout_mode = 2
text = "press e to open fishing shop"
text = "Press E to open Fishing Shop"
horizontal_alignment = 1
[node name="FishingShop" parent="." instance=ExtResource("8_shop")]

View file

@ -75,7 +75,7 @@ func refresh() -> void:
_quantity_label.text = quantity_text
_quantity_label.visible = not quantity_text.is_empty()
tooltip_text = (
item.display_name if item != null else "empty hotbar slot"
item.display_name if item != null else "Empty hotbar slot"
)
button_pressed = slot_index == _hotbar.get_selected_slot()

View file

@ -161,9 +161,9 @@ func _save_now() -> void:
_action_in_progress = true
_save_button.disabled = true
if _save_manager.save_now():
_feedback.text = "game saved."
_feedback.text = "Game saved."
else:
_feedback.text = "save failed. previous save was preserved."
_feedback.text = "Save failed. Previous save was preserved."
_action_in_progress = false
call_deferred("_reenable_save_button")
@ -181,7 +181,7 @@ func _open_settings() -> void:
func _on_settings_applied() -> void:
_root_panel.show()
_feedback.text = "settings saved."
_feedback.text = "Settings saved."
%SettingsButton.grab_focus()
@ -193,9 +193,9 @@ func _on_settings_closed() -> void:
func _confirm_return_to_title() -> void:
_open_confirmation(
ConfirmationAction.RETURN_TO_TITLE,
"return to title",
"unsaved progress will be saved first.",
"save and return",
"Return to Title",
"Unsaved progress will be saved first.",
"Save and Return",
false
)
@ -203,13 +203,13 @@ func _confirm_return_to_title() -> void:
func _confirm_reset_progress() -> void:
_open_confirmation(
ConfirmationAction.RESET_PROGRESS,
"reset all progression?",
"Reset all progression?",
(
"this permanently deletes your fish, discoveries, "
"This permanently deletes your fish, discoveries, "
+ "favorites, and wallet balance.\n\n"
+ "your settings will be preserved."
+ "Your settings will be preserved."
),
"delete all progress",
"DELETE ALL PROGRESS",
true
)
@ -226,9 +226,9 @@ func _request_quit() -> void:
return
_open_confirmation(
ConfirmationAction.QUIT_ANYWAY,
"save failed",
"some progress or settings could not be saved. quit anyway?",
"quit anyway",
"Save failed",
"Some progress or settings could not be saved. Quit anyway?",
"Quit Anyway",
true
)
@ -276,7 +276,7 @@ func _accept_confirmation() -> void:
if _save_manager.save_now():
return_to_title_requested.emit()
else:
_feedback.text = "save failed. previous save was preserved."
_feedback.text = "Save failed. Previous save was preserved."
_root_panel.show()
ConfirmationAction.RESET_PROGRESS:
reset_progress_requested.emit()
@ -291,7 +291,7 @@ func report_reset_failure() -> void:
_action_in_progress = false
_root_panel.show()
_confirmation_panel.hide()
_feedback.text = "reset failed. your progression was preserved."
_feedback.text = "Reset failed. Your progression was preserved."
%ResumeButton.grab_focus()

View file

@ -55,45 +55,45 @@ theme_override_constants/separation = 11
[node name="Title" type="Label" parent="RootCenter/RootPanel/Margin/Content"]
layout_mode = 2
theme_override_font_sizes/font_size = 30
text = "game menu"
text = "Game Menu"
horizontal_alignment = 1
[node name="ResumeButton" type="Button" parent="RootCenter/RootPanel/Margin/Content"]
unique_name_in_owner = true
custom_minimum_size = Vector2(0, 43)
layout_mode = 2
text = "return to game"
text = "Return to Game"
[node name="SaveButton" type="Button" parent="RootCenter/RootPanel/Margin/Content"]
unique_name_in_owner = true
custom_minimum_size = Vector2(0, 43)
layout_mode = 2
text = "save now"
text = "Save Now"
[node name="SettingsButton" type="Button" parent="RootCenter/RootPanel/Margin/Content"]
unique_name_in_owner = true
custom_minimum_size = Vector2(0, 43)
layout_mode = 2
text = "settings"
text = "Settings"
[node name="ReturnToTitleButton" type="Button" parent="RootCenter/RootPanel/Margin/Content"]
unique_name_in_owner = true
custom_minimum_size = Vector2(0, 43)
layout_mode = 2
text = "return to title"
text = "Return to Title"
[node name="ResetProgressButton" type="Button" parent="RootCenter/RootPanel/Margin/Content"]
unique_name_in_owner = true
custom_minimum_size = Vector2(0, 43)
layout_mode = 2
theme_type_variation = &"DangerButton"
text = "reset progress"
text = "Reset Progress"
[node name="QuitButton" type="Button" parent="RootCenter/RootPanel/Margin/Content"]
unique_name_in_owner = true
custom_minimum_size = Vector2(0, 43)
layout_mode = 2
text = "quit"
text = "Quit"
[node name="FeedbackLabel" type="Label" parent="RootCenter/RootPanel/Margin/Content"]
unique_name_in_owner = true
@ -147,13 +147,13 @@ unique_name_in_owner = true
layout_mode = 2
theme_override_colors/font_color = Color(0.937, 0.357, 0.384, 1)
theme_override_font_sizes/font_size = 24
text = "confirm"
text = "Confirm"
horizontal_alignment = 1
[node name="ConfirmationText" type="Label" parent="ConfirmationCenter/ConfirmationPanel/Margin/Content"]
unique_name_in_owner = true
layout_mode = 2
text = "are you sure?"
text = "Are you sure?"
horizontal_alignment = 1
autowrap_mode = 2
@ -166,10 +166,10 @@ alignment = 1
unique_name_in_owner = true
custom_minimum_size = Vector2(190, 43)
layout_mode = 2
text = "confirm"
text = "Confirm"
[node name="CancelConfirmButton" type="Button" parent="ConfirmationCenter/ConfirmationPanel/Margin/Content/Buttons"]
unique_name_in_owner = true
custom_minimum_size = Vector2(130, 43)
layout_mode = 2
text = "cancel"
text = "Cancel"

View file

@ -22,9 +22,6 @@ const ItemDragSourceType = preload("res://ui/item_drag_source.gd")
const PlayerCoolerCapacityType = preload(
"res://progression/player_cooler_capacity.gd"
)
const FishBatchSelectionType = preload(
"res://ui/fish_batch_selection.gd"
)
signal menu_visibility_changed(is_open: bool)
@ -71,7 +68,6 @@ enum CloseReason {
@onready var _detail_texture: TextureRect = %DetailTexture
@onready var _detail_name: Label = %DetailName
@onready var _detail_data: Label = %DetailData
@onready var _selection_summary: Label = %SelectionSummary
@onready var _favorite_button: Button = %FavoriteButton
@onready var _sell_button: Button = %SellButton
@onready var _sale_unavailable: Label = %SaleUnavailable
@ -98,7 +94,7 @@ var _cooler_capacity: PlayerCoolerCapacityType
var _current_section: Section = Section.COOLER
var _sort_mode: SortMode = SortMode.CATCH_ORDER
var _sort_descending: bool = true
var _fish_selection := FishBatchSelectionType.new()
var _selected_catch_id: StringName
var _selected_bag_item_id: StringName
var _prior_movement_enabled: bool = true
var _prior_camera_input_enabled: bool = true
@ -106,10 +102,9 @@ var _prior_mouse_mode: Input.MouseMode = Input.MOUSE_MODE_VISIBLE
var _control_snapshot_stored: bool = false
var _mouse_snapshot_stored: bool = false
var _menu_generation: int = 0
var _confirmation_catch_ids: Array[StringName] = []
var _confirmation_catch_id: StringName
var _confirmation_buyer: FishBuyerProfileType
var _confirmation_buyer_id: StringName
var _confirmation_generation: int = -1
var _sale_in_progress: bool = false
@ -128,9 +123,9 @@ func _ready() -> void:
_sell_button.pressed.connect(_on_sell_pressed)
_confirm_sale_button.pressed.connect(_on_confirm_sale_pressed)
_cancel_sale_button.pressed.connect(_close_sale_confirmation)
_sort_option.add_item("catch order", SortMode.CATCH_ORDER)
_sort_option.add_item("name", SortMode.NAME)
_sort_option.add_item("rarity", SortMode.RARITY)
_sort_option.add_item("Catch order", SortMode.CATCH_ORDER)
_sort_option.add_item("Name", SortMode.NAME)
_sort_option.add_item("Rarity", SortMode.RARITY)
_sort_option.select(SortMode.CATCH_ORDER)
_update_sort_direction_text()
_show_section(_current_section)
@ -162,7 +157,6 @@ func setup(
_hotbar = hotbar
_item_catalog = item_catalog
_cooler_capacity = cooler_capacity
_fish_selection.clear()
if not _inventory.catches_changed.is_connected(_on_inventory_changed):
_inventory.catches_changed.connect(_on_inventory_changed)
if not _collection_log.fish_discovered.is_connected(_on_fish_discovered):
@ -240,13 +234,6 @@ func close_menu(
return
get_viewport().gui_cancel_drag()
_close_sale_confirmation()
if reason in [
CloseReason.BITE_STARTED,
CloseReason.WATER_RECOVERY,
CloseReason.SESSION_END,
CloseReason.TEARDOWN,
]:
_fish_selection.clear()
var closing_generation: int = _menu_generation
visible = false
get_viewport().gui_release_focus()
@ -262,7 +249,6 @@ func close_menu(
func close_for_water_recovery() -> void:
_fish_selection.clear()
close_menu(CloseReason.WATER_RECOVERY, false)
@ -271,14 +257,13 @@ func close_for_game_menu() -> void:
func close_for_session_end() -> void:
_fish_selection.clear()
close_menu(CloseReason.SESSION_END, false)
func _exit_tree() -> void:
if visible:
close_menu(CloseReason.TEARDOWN, false)
_fish_selection.clear()
_selected_catch_id = StringName()
_menu_generation += 1
@ -355,11 +340,11 @@ func _on_sort_direction_pressed() -> void:
func _update_sort_direction_text() -> void:
match _sort_mode:
SortMode.CATCH_ORDER:
_sort_direction.text = "newest first" if _sort_descending else "oldest first"
_sort_direction.text = "Newest first" if _sort_descending else "Oldest first"
SortMode.NAME:
_sort_direction.text = "za" if _sort_descending else "az"
_sort_direction.text = "ZA" if _sort_descending else "AZ"
SortMode.RARITY:
_sort_direction.text = "high to low" if _sort_descending else "low to high"
_sort_direction.text = "High to low" if _sort_descending else "Low to high"
func _refresh_all() -> void:
@ -401,9 +386,9 @@ func _refresh_bag() -> void:
item.get_category_name(),
]
card.tooltip_text = (
"drag to a hotbar slot."
"Drag to a hotbar slot."
if item.hotbar_allowed
else "this item cannot be assigned to the hotbar."
else "This item cannot be assigned to the hotbar."
)
card.setup(item.item_id, item.display_name, item.icon)
card.pressed.connect(_select_bag_item.bind(item.item_id))
@ -442,19 +427,19 @@ func _update_bag_detail() -> void:
_bag_detail_texture.texture = item.icon if item != null else null
_bag_detail_name.text = item.display_name if item != null else ""
_bag_detail_data.text = (
"%s\nquantity: %d\n%s\n%s"
"%s\nQuantity: %d\n%s\n%s"
% [
item.get_category_name(),
quantity,
item.description,
(
"can be assigned to the hotbar."
"Can be assigned to the hotbar."
if item.hotbar_allowed
else "cannot be assigned to the hotbar."
else "Cannot be assigned to the hotbar."
),
]
if item != null
else "select a bag item for details."
else "Select a Bag item for details."
)
@ -485,8 +470,8 @@ func _refresh_economy_summary() -> void:
if _inventory != null
else 0
)
_wallet_balance.text = "wallet: $%d" % balance
_held_value.text = "held fish base value: $%d" % held_total
_wallet_balance.text = "Wallet: $%d" % balance
_held_value.text = "Held fish base value: $%d" % held_total
_cooler_count.text = "%d / %d" % [
_inventory.get_all_catches().size() if _inventory != null else 0,
_cooler_capacity.get_capacity() if _cooler_capacity != null else 0,
@ -508,18 +493,16 @@ func _refresh_inventory() -> void:
catches.append(fish_catch)
catches.sort_custom(_compare_catches)
_inventory_empty.visible = catches.is_empty()
var visible_ids: Array[StringName] = []
for fish_catch: FishCatchType in catches:
visible_ids.append(fish_catch.catch_id)
_fish_selection.set_visible_order(visible_ids)
var selected: FishCatchType
if _inventory != null:
selected = _inventory.get_catch(_fish_selection.get_focused_id())
selected = _inventory.get_catch(_selected_catch_id)
if selected == null and not catches.is_empty():
selected = catches.front()
_selected_catch_id = selected.catch_id
for fish_catch: FishCatchType in catches:
_inventory_grid.add_child(_create_inventory_card(fish_catch))
_update_inventory_detail(selected)
_update_sale_summary()
_update_sort_direction_text()
@ -544,17 +527,11 @@ func _create_inventory_card(fish_catch: FishCatchType) -> Button:
card.theme_type_variation = &"CardButton"
card.custom_minimum_size = Vector2(132.0, 118.0)
card.toggle_mode = true
var is_selected: bool = _fish_selection.is_selected(fish_catch.catch_id)
var is_focused: bool = (
fish_catch.catch_id == _fish_selection.get_focused_id()
)
card.button_pressed = is_selected
card.pressed.connect(_on_catch_card_pressed.bind(fish_catch.catch_id))
card.button_pressed = fish_catch.catch_id == _selected_catch_id
card.pressed.connect(_select_catch.bind(fish_catch.catch_id))
_apply_inventory_card_styles(
card,
UIPalette.get_rarity_color(fish_catch.fish.rarity),
is_selected,
is_focused
UIPalette.get_rarity_color(fish_catch.fish.rarity)
)
var content := VBoxContainer.new()
@ -596,83 +573,34 @@ func _create_inventory_card(fish_catch: FishCatchType) -> Button:
favorite_marker.horizontal_alignment = HORIZONTAL_ALIGNMENT_CENTER
favorite_marker.mouse_filter = Control.MOUSE_FILTER_IGNORE
card.add_child(favorite_marker)
if is_selected:
var selected_marker := Label.new()
selected_marker.text = ""
selected_marker.add_theme_color_override(
"font_color",
UIPalette.SUCCESS
)
selected_marker.add_theme_font_size_override("font_size", 18)
selected_marker.set_anchors_preset(Control.PRESET_TOP_LEFT)
selected_marker.offset_left = 8.0
selected_marker.offset_top = 5.0
selected_marker.offset_right = 30.0
selected_marker.offset_bottom = 29.0
selected_marker.mouse_filter = Control.MOUSE_FILTER_IGNORE
card.add_child(selected_marker)
if is_focused:
var focus_marker := Label.new()
focus_marker.text = ""
focus_marker.add_theme_color_override(
"font_color",
UIPalette.PRIMARY
)
focus_marker.set_anchors_preset(Control.PRESET_BOTTOM_RIGHT)
focus_marker.offset_left = -26.0
focus_marker.offset_top = -25.0
focus_marker.offset_right = -8.0
focus_marker.offset_bottom = -7.0
focus_marker.horizontal_alignment = HORIZONTAL_ALIGNMENT_CENTER
focus_marker.mouse_filter = Control.MOUSE_FILTER_IGNORE
card.add_child(focus_marker)
return card
func _apply_inventory_card_styles(
card: Button,
rarity_color: Color,
is_selected: bool,
is_focused: bool,
) -> void:
card.add_theme_stylebox_override(
"normal",
_create_inventory_card_style(
rarity_color,
0,
is_selected,
is_focused
)
_create_inventory_card_style(rarity_color, 0)
)
card.add_theme_stylebox_override(
"hover",
_create_inventory_card_style(
rarity_color,
1,
is_selected,
is_focused
)
_create_inventory_card_style(rarity_color, 1)
)
card.add_theme_stylebox_override(
"focus",
_create_inventory_card_style(
rarity_color,
1,
is_selected,
true
)
_create_inventory_card_style(rarity_color, 1)
)
card.add_theme_stylebox_override(
"pressed",
_create_inventory_card_style(rarity_color, 2, true, is_focused)
_create_inventory_card_style(rarity_color, 2)
)
func _create_inventory_card_style(
rarity_color: Color,
state_index: int,
is_selected: bool,
is_focused: bool,
) -> StyleBoxFlat:
var style := StyleBoxFlat.new()
match state_index:
@ -681,13 +609,9 @@ func _create_inventory_card_style(
2:
style.bg_color = UIPalette.PRIMARY.darkened(0.48)
_:
style.bg_color = (
UIPalette.PRIMARY.darkened(0.55)
if is_selected
else UIPalette.ELEVATED_PANEL
)
style.bg_color = UIPalette.ELEVATED_PANEL
style.border_color = rarity_color
var border_width: int = 4 if state_index == 2 or is_focused else 2
var border_width: int = 4 if state_index == 2 else 2
style.set_border_width_all(border_width)
style.set_corner_radius_all(8)
style.content_margin_left = 7.0
@ -697,18 +621,13 @@ func _create_inventory_card_style(
return style
func _on_catch_card_pressed(catch_id: StringName) -> void:
func _select_catch(catch_id: StringName) -> void:
if _inventory == null:
return
var selected: FishCatchType = _inventory.get_catch(catch_id)
if selected == null:
return
_fish_selection.apply_click(
catch_id,
Input.is_key_pressed(KEY_CTRL),
Input.is_key_pressed(KEY_SHIFT)
)
_transaction_feedback.text = ""
_selected_catch_id = catch_id
_refresh_inventory()
@ -719,28 +638,26 @@ func _update_inventory_detail(fish_catch: FishCatchType) -> void:
_detail_name.text = ""
_detail_data.text = ""
_favorite_button.disabled = true
_favorite_button.text = "favorite"
_favorite_button.text = "Favorite"
_sell_button.disabled = true
_sale_unavailable.text = ""
_sale_unavailable.visible = false
return
_detail_texture.texture = fish_catch.fish.display_texture
_detail_texture.visible = fish_catch.fish.display_texture != null
_detail_name.text = fish_catch.fish.display_name
var individual_preview: FishSaleResultType = (
_sale_service.preview_batch([fish_catch.catch_id], _default_buyer)
if _sale_service != null
else null
)
var buyer_offer: int = (
individual_preview.payout
if individual_preview != null
_default_buyer.get_offer(fish_catch.sale_value)
if _default_buyer != null
else -1
)
var buyer_offer_text: String = "buyer unavailable"
if buyer_offer >= 0 and _default_buyer != null:
var buyer_offer_text: String = "Buyer unavailable"
if buyer_offer >= 0:
buyer_offer_text = "%s offer: $%d" % [
_default_buyer.display_name,
buyer_offer,
]
_detail_data.text = "%.2f lb\n%s\nbase value: $%d\n%s" % [
_detail_data.text = "%.2f lb\n%s\nBase value: $%d\n%s" % [
fish_catch.weight_lb,
fish_catch.fish.get_rarity_name(),
fish_catch.sale_value,
@ -748,68 +665,35 @@ func _update_inventory_detail(fish_catch: FishCatchType) -> void:
]
_favorite_button.disabled = false
_favorite_button.text = (
"unfavorite" if fish_catch.is_favorited else "favorite"
"Unfavorite" if fish_catch.is_favorited else "Favorite"
)
func _update_sale_summary() -> void:
var selected_ids: Array[StringName] = _fish_selection.get_selected_ids()
var selected_count: int = selected_ids.size()
_sell_button.text = (
"sell fish"
if selected_count == 1
else "sell %d fish" % selected_count
var valid_sale_value: bool = fish_catch.sale_value >= 0
var valid_buyer: bool = (
_default_buyer != null
and _default_buyer.is_valid()
and buyer_offer >= 0
)
if selected_count == 0:
_selection_summary.text = "no fish selected"
_sell_button.text = "sell fish"
_sell_button.disabled = true
_sale_unavailable.text = ""
_sale_unavailable.visible = false
return
var preview: FishSaleResultType = (
_sale_service.preview_batch(selected_ids, _default_buyer)
if _sale_service != null
else null
_sell_button.disabled = (
fish_catch.is_favorited
or not valid_sale_value
or not valid_buyer
)
var count_text: String = (
"1 fish selected"
if selected_count == 1
else "%d fish selected" % selected_count
)
_selection_summary.text = count_text
if (
preview != null
and preview.payout >= 0
and _default_buyer != null
and (
preview.is_success()
or preview.status == FishSaleResultType.Status.FAVORITED
)
):
_selection_summary.text += "\n%s total offer: $%d" % [
_default_buyer.display_name,
preview.payout,
]
_sell_button.disabled = preview == null or not preview.is_success()
if preview != null and preview.status == FishSaleResultType.Status.FAVORITED:
_sale_unavailable.text = (
"favorited fish cannot be sold. "
+ "remove them from the selection first."
)
elif preview != null and not preview.is_success():
_sale_unavailable.text = preview.get_message()
if fish_catch.is_favorited:
_sale_unavailable.text = "Favorited fish cannot be sold."
elif not valid_sale_value:
_sale_unavailable.text = "Invalid sale value."
elif not valid_buyer:
_sale_unavailable.text = "Buyer is unavailable."
else:
_sale_unavailable.text = ""
_sale_unavailable.visible = not _sale_unavailable.text.is_empty()
func _on_favorite_pressed() -> void:
var focused_id: StringName = _fish_selection.get_focused_id()
if _inventory == null or focused_id.is_empty():
if _inventory == null or _selected_catch_id.is_empty():
return
var fish_catch: FishCatchType = _inventory.get_catch_by_id(
focused_id
_selected_catch_id
)
if fish_catch == null:
_refresh_inventory()
@ -828,40 +712,46 @@ func _on_favorite_pressed() -> void:
func _on_sell_pressed() -> void:
var selected_ids: Array[StringName] = _fish_selection.get_selected_ids()
if (
_inventory == null
or _sale_service == null
or _default_buyer == null
or selected_ids.is_empty()
or _selected_catch_id.is_empty()
):
return
var preview: FishSaleResultType = _sale_service.preview_batch(
selected_ids,
_default_buyer
)
if not preview.is_success():
_transaction_feedback.text = (
"favorited fish cannot be sold. "
+ "remove them from the selection first."
if preview.status == FishSaleResultType.Status.FAVORITED
else preview.get_message()
var fish_catch: FishCatchType = _inventory.get_catch_by_id(
_selected_catch_id
)
if fish_catch == null:
_transaction_feedback.text = "Fish no longer exists."
_refresh_inventory()
return
_confirmation_catch_ids = selected_ids.duplicate()
if fish_catch.is_favorited:
_transaction_feedback.text = "Favorited fish cannot be sold."
return
if fish_catch.sale_value < 0:
_transaction_feedback.text = "Invalid sale value."
return
if not _default_buyer.is_valid():
_transaction_feedback.text = "Buyer is unavailable."
return
var buyer_offer: int = _default_buyer.get_offer(
fish_catch.sale_value
)
if buyer_offer < 0:
_transaction_feedback.text = "Invalid buyer offer."
return
_confirmation_catch_id = fish_catch.catch_id
_confirmation_buyer = _default_buyer
_confirmation_buyer_id = _default_buyer.id
_confirmation_generation = _menu_generation
var fish_label: String = "fish"
_confirmation_message.text = (
"sell %d %s to the %s for $%d?\ncombined base value: $%d"
"Sell this %.2f lb %s to the %s for $%d?\nBase value: $%d"
% [
preview.fish_count,
fish_label,
fish_catch.weight_lb,
fish_catch.fish.display_name,
_get_buyer_display_group(_default_buyer),
preview.payout,
preview.base_value,
buyer_offer,
fish_catch.sale_value,
]
)
_sale_confirmation.visible = true
@ -873,39 +763,31 @@ func _on_confirm_sale_pressed() -> void:
if (
_sale_in_progress
or _sale_service == null
or _confirmation_catch_ids.is_empty()
or _confirmation_catch_id.is_empty()
or _confirmation_buyer == null
or _confirmation_buyer.id != _confirmation_buyer_id
or _confirmation_generation != _menu_generation
):
return
_sale_in_progress = true
var requested_catch_ids: Array[StringName] = (
_confirmation_catch_ids.duplicate()
)
var transaction_generation: int = _menu_generation
var transaction_buyer: FishBuyerProfileType = _confirmation_buyer
var requested_catch_id: StringName = _confirmation_catch_id
_confirm_sale_button.disabled = true
var result: FishSaleResultType = _sale_service.sell_batch(
requested_catch_ids,
transaction_buyer
var result: FishSaleResultType = _sale_service.sell(
requested_catch_id,
_confirmation_buyer
)
_close_sale_confirmation()
_sale_in_progress = false
if transaction_generation != _menu_generation or not visible:
return
_transaction_feedback.text = result.get_message()
if result.is_success():
_fish_selection.remove_ids(requested_catch_ids)
_sale_in_progress = false
if result.is_success() and _selected_catch_id == requested_catch_id:
_selected_catch_id = StringName()
_refresh_all()
_inventory_tab.grab_focus()
func _close_sale_confirmation() -> void:
_confirmation_catch_ids.clear()
_confirmation_catch_id = StringName()
_confirmation_buyer = null
_confirmation_buyer_id = StringName()
_confirmation_generation = -1
_sale_confirmation.visible = false
_confirmation_message.text = ""
_confirm_sale_button.disabled = false
@ -914,35 +796,25 @@ func _close_sale_confirmation() -> void:
func _revalidate_confirmation() -> void:
if not _sale_confirmation.visible:
return
if (
_confirmation_generation != _menu_generation
or _confirmation_catch_ids.is_empty()
or _sale_service == null
or _confirmation_buyer == null
or _confirmation_buyer.id != _confirmation_buyer_id
):
var fish_catch: FishCatchType
if _inventory != null:
fish_catch = _inventory.get_catch_by_id(_confirmation_catch_id)
if fish_catch == null:
_close_sale_confirmation()
_transaction_feedback.text = "sale selection is no longer available."
return
var preview: FishSaleResultType = _sale_service.preview_batch(
_confirmation_catch_ids,
_confirmation_buyer
)
if (
not preview.is_success()
or (
_transaction_feedback.text = "Fish no longer exists."
elif fish_catch.is_favorited:
_close_sale_confirmation()
_transaction_feedback.text = "Favorited fish cannot be sold."
elif fish_catch.sale_value < 0:
_close_sale_confirmation()
_transaction_feedback.text = "Invalid sale value."
elif (
_confirmation_buyer == null
or _confirmation_buyer.id != _confirmation_buyer_id
or not _confirmation_buyer.is_valid()
)
):
_close_sale_confirmation()
_transaction_feedback.text = (
"favorited fish cannot be sold. "
+ "remove them from the selection first."
if preview.status == FishSaleResultType.Status.FAVORITED
else preview.get_message()
)
_transaction_feedback.text = "Buyer is unavailable."
func _get_buyer_display_group(
@ -968,9 +840,9 @@ func _refresh_logbook() -> void:
valid_species.append(fish)
_logbook_empty.visible = valid_species.is_empty()
_logbook_empty.text = (
"no fish catalog configured."
"No fish catalog configured."
if valid_species.is_empty()
else "no species discovered yet."
else "No species discovered yet."
)
if not valid_species.is_empty() and _collection_log != null:
var any_discovered: bool = false
@ -1009,12 +881,12 @@ func _create_logbook_card(fish: FishDataType) -> Control:
var owned_count: int = (
_inventory.get_count(fish.id) if _inventory != null else 0
)
details.text = "%s\nowned: %d" % [
details.text = "%s\nOwned: %d" % [
fish.get_rarity_name(),
owned_count,
]
else:
details.text = "undiscovered"
details.text = "Undiscovered"
details.horizontal_alignment = HORIZONTAL_ALIGNMENT_CENTER
content.add_child(details)
return card
@ -1032,7 +904,7 @@ func _create_texture_frame(
texture_frame.texture_filter = CanvasItem.TEXTURE_FILTER_NEAREST
texture_frame.mouse_filter = Control.MOUSE_FILTER_IGNORE
if texture == null:
texture_frame.tooltip_text = "display texture unavailable"
texture_frame.tooltip_text = "Display texture unavailable"
return texture_frame

View file

@ -56,39 +56,39 @@ layout_mode = 2
size_flags_horizontal = 3
theme_override_colors/font_color = Color(0.208, 0.725, 0.78, 1)
theme_override_font_sizes/font_size = 22
text = "player menu"
text = "Player Menu"
[node name="WalletBalance" type="Label" parent="MenuPanel/Margin/Layout/Header"]
unique_name_in_owner = true
layout_mode = 2
text = "wallet: $0"
text = "Wallet: $0"
theme_override_colors/font_color = Color(1, 0.82, 0.4, 1)
[node name="InventoryTab" type="Button" parent="MenuPanel/Margin/Layout/Header"]
unique_name_in_owner = true
layout_mode = 2
toggle_mode = true
text = "cooler"
text = "Cooler"
custom_minimum_size = Vector2(88, 30)
[node name="BagTab" type="Button" parent="MenuPanel/Margin/Layout/Header"]
unique_name_in_owner = true
layout_mode = 2
toggle_mode = true
text = "bag"
text = "Bag"
custom_minimum_size = Vector2(76, 30)
[node name="LogbookTab" type="Button" parent="MenuPanel/Margin/Layout/Header"]
unique_name_in_owner = true
layout_mode = 2
toggle_mode = true
text = "logbook"
text = "Logbook"
custom_minimum_size = Vector2(88, 30)
[node name="CloseButton" type="Button" parent="MenuPanel/Margin/Layout/Header"]
unique_name_in_owner = true
layout_mode = 2
text = "close"
text = "Close"
custom_minimum_size = Vector2(68, 30)
[node name="Separator" type="HSeparator" parent="MenuPanel/Margin/Layout"]
@ -123,7 +123,7 @@ theme_override_constants/separation = 6
[node name="SortLabel" type="Label" parent="MenuPanel/Margin/Layout/Content/InventorySection/SortBar"]
layout_mode = 2
text = "sort:"
text = "Sort:"
[node name="SortOption" type="OptionButton" parent="MenuPanel/Margin/Layout/Content/InventorySection/SortBar"]
unique_name_in_owner = true
@ -134,7 +134,7 @@ custom_minimum_size = Vector2(130, 30)
unique_name_in_owner = true
layout_mode = 2
custom_minimum_size = Vector2(112, 30)
text = "newest first"
text = "Newest first"
[node name="SortSpacer" type="Control" parent="MenuPanel/Margin/Layout/Content/InventorySection/SortBar"]
layout_mode = 2
@ -149,7 +149,7 @@ theme_override_colors/font_color = Color(0.208, 0.725, 0.78, 1)
[node name="HeldValue" type="Label" parent="MenuPanel/Margin/Layout/Content/InventorySection/SortBar"]
unique_name_in_owner = true
layout_mode = 2
text = "held fish base value: $0"
text = "Held fish base value: $0"
theme_override_colors/font_color = Color(0.682, 0.733, 0.761, 1)
[node name="InventoryBody" type="HSplitContainer" parent="MenuPanel/Margin/Layout/Content/InventorySection"]
@ -176,7 +176,7 @@ theme_override_constants/separation = 6
[node name="InventoryEmpty" type="Label" parent="MenuPanel/Margin/Layout/Content/InventorySection/InventoryBody/InventoryList/InventoryListMargin/InventoryStack"]
unique_name_in_owner = true
layout_mode = 2
text = "your cooler is empty."
text = "Your Cooler is empty."
horizontal_alignment = 1
[node name="InventoryScroll" type="ScrollContainer" parent="MenuPanel/Margin/Layout/Content/InventorySection/InventoryBody/InventoryList/InventoryListMargin/InventoryStack"]
@ -228,25 +228,17 @@ layout_mode = 2
autowrap_mode = 2
horizontal_alignment = 1
[node name="SelectionSummary" type="Label" parent="MenuPanel/Margin/Layout/Content/InventorySection/InventoryBody/DetailPanel/DetailMargin/DetailStack"]
unique_name_in_owner = true
layout_mode = 2
text = "no fish selected"
autowrap_mode = 2
horizontal_alignment = 1
theme_override_colors/font_color = Color(0.208, 0.725, 0.78, 1)
[node name="FavoriteButton" type="Button" parent="MenuPanel/Margin/Layout/Content/InventorySection/InventoryBody/DetailPanel/DetailMargin/DetailStack"]
unique_name_in_owner = true
layout_mode = 2
disabled = true
text = "favorite"
text = "Favorite"
[node name="SellButton" type="Button" parent="MenuPanel/Margin/Layout/Content/InventorySection/InventoryBody/DetailPanel/DetailMargin/DetailStack"]
unique_name_in_owner = true
layout_mode = 2
disabled = true
text = "sell"
text = "Sell"
theme_type_variation = &"DangerButton"
[node name="SaleUnavailable" type="Label" parent="MenuPanel/Margin/Layout/Content/InventorySection/InventoryBody/DetailPanel/DetailMargin/DetailStack"]
@ -270,7 +262,7 @@ theme_override_constants/separation = 7
[node name="BagHint" type="Label" parent="MenuPanel/Margin/Layout/Content/BagSection"]
layout_mode = 2
text = "drag hotbar-compatible items onto a slot below. right-click a slot to clear it."
text = "Drag hotbar-compatible items onto a slot below. Right-click a slot to clear it."
horizontal_alignment = 1
[node name="BagBody" type="HSplitContainer" parent="MenuPanel/Margin/Layout/Content/BagSection"]
@ -297,7 +289,7 @@ theme_override_constants/separation = 6
[node name="BagEmpty" type="Label" parent="MenuPanel/Margin/Layout/Content/BagSection/BagBody/BagList/Margin/Stack"]
unique_name_in_owner = true
layout_mode = 2
text = "your bag is empty."
text = "Your Bag is empty."
horizontal_alignment = 1
[node name="BagScroll" type="ScrollContainer" parent="MenuPanel/Margin/Layout/Content/BagSection/BagBody/BagList/Margin/Stack"]
@ -345,7 +337,7 @@ horizontal_alignment = 1
[node name="BagDetailData" type="Label" parent="MenuPanel/Margin/Layout/Content/BagSection/BagBody/BagDetail/Margin/Stack"]
unique_name_in_owner = true
layout_mode = 2
text = "select a bag item for details."
text = "Select a Bag item for details."
horizontal_alignment = 1
autowrap_mode = 2
@ -363,7 +355,7 @@ theme_override_constants/separation = 7
[node name="LogbookEmpty" type="Label" parent="MenuPanel/Margin/Layout/Content/LogbookSection"]
unique_name_in_owner = true
layout_mode = 2
text = "no species discovered yet."
text = "No species discovered yet."
horizontal_alignment = 1
[node name="LogbookScroll" type="ScrollContainer" parent="MenuPanel/Margin/Layout/Content/LogbookSection"]
@ -420,12 +412,12 @@ alignment = 1
[node name="ConfirmSaleButton" type="Button" parent="MenuPanel/Margin/Layout/Content/SaleConfirmation/ConfirmationMargin/ConfirmationLayout/ConfirmationButtons"]
unique_name_in_owner = true
layout_mode = 2
text = "confirm"
text = "Confirm"
theme_type_variation = &"DangerButton"
custom_minimum_size = Vector2(120, 38)
[node name="CancelSaleButton" type="Button" parent="MenuPanel/Margin/Layout/Content/SaleConfirmation/ConfirmationMargin/ConfirmationLayout/ConfirmationButtons"]
unique_name_in_owner = true
layout_mode = 2
text = "cancel"
text = "Cancel"
custom_minimum_size = Vector2(120, 38)

View file

@ -47,7 +47,7 @@ func close_panel() -> void:
func _apply_settings() -> void:
if _settings_manager == null:
_feedback.text = "settings are unavailable."
_feedback.text = "Settings are unavailable."
return
var edited := PlayerSettings.new()
edited.auto_click_enabled = _auto_click_toggle.button_pressed
@ -59,7 +59,7 @@ func _apply_settings() -> void:
hide()
applied.emit()
else:
_feedback.text = "failed to save settings."
_feedback.text = "Failed to save settings."
func _load_controls() -> void:

View file

@ -23,17 +23,17 @@ theme_override_constants/separation = 12
[node name="Heading" type="Label" parent="Margin/Content"]
layout_mode = 2
theme_override_font_sizes/font_size = 26
text = "settings"
text = "Settings"
horizontal_alignment = 1
[node name="AutoClickToggle" type="CheckButton" parent="Margin/Content"]
unique_name_in_owner = true
layout_mode = 2
text = "accessibility auto-click"
text = "Accessibility auto-click"
[node name="AutoClickHelp" type="Label" parent="Margin/Content"]
layout_mode = 2
text = "lower intervals click barriers faster while held."
text = "Lower intervals click barriers faster while held."
[node name="AutoClickRow" type="HBoxContainer" parent="Margin/Content"]
layout_mode = 2
@ -42,7 +42,7 @@ theme_override_constants/separation = 12
[node name="Label" type="Label" parent="Margin/Content/AutoClickRow"]
custom_minimum_size = Vector2(190, 0)
layout_mode = 2
text = "auto-click interval"
text = "Auto-click interval"
[node name="AutoClickInterval" type="HSlider" parent="Margin/Content/AutoClickRow"]
unique_name_in_owner = true
@ -66,7 +66,7 @@ theme_override_constants/separation = 12
[node name="Label" type="Label" parent="Margin/Content/MouseRow"]
custom_minimum_size = Vector2(190, 0)
layout_mode = 2
text = "mouse camera sensitivity"
text = "Mouse camera sensitivity"
[node name="MouseSensitivity" type="HSlider" parent="Margin/Content/MouseRow"]
unique_name_in_owner = true
@ -90,7 +90,7 @@ theme_override_constants/separation = 12
[node name="Label" type="Label" parent="Margin/Content/ControllerRow"]
custom_minimum_size = Vector2(190, 0)
layout_mode = 2
text = "controller camera sensitivity"
text = "Controller camera sensitivity"
[node name="ControllerSensitivity" type="HSlider" parent="Margin/Content/ControllerRow"]
unique_name_in_owner = true
@ -110,7 +110,7 @@ text = "2.5"
[node name="InvertYToggle" type="CheckButton" parent="Margin/Content"]
unique_name_in_owner = true
layout_mode = 2
text = "invert vertical camera"
text = "Invert vertical camera"
[node name="SettingsFeedback" type="Label" parent="Margin/Content"]
unique_name_in_owner = true
@ -127,10 +127,10 @@ alignment = 2
unique_name_in_owner = true
custom_minimum_size = Vector2(120, 42)
layout_mode = 2
text = "apply"
text = "Apply"
[node name="CancelSettingsButton" type="Button" parent="Margin/Content/Buttons"]
unique_name_in_owner = true
custom_minimum_size = Vector2(120, 42)
layout_mode = 2
text = "back"
text = "Back"

View file

@ -1,48 +0,0 @@
shader_type canvas_item;
uniform float horizontal_displacement_pixels : hint_range(0.0, 2.0, 0.25) = 1.5;
uniform float wave_speed : hint_range(0.0, 2.0, 0.05) = 0.55;
uniform float wave_scale : hint_range(0.0, 0.20, 0.005) = 0.055;
uniform vec3 underwater_tint : source_color = vec3(0.78, 0.96, 1.08);
uniform float tint_strength : hint_range(0.0, 0.35, 0.01) = 0.18;
uniform float highlight_strength : hint_range(0.0, 0.15, 0.01) = 0.06;
uniform float bob_amount_pixels : hint_range(0.0, 4.0, 1.0) = 4.0;
uniform float bob_speed : hint_range(0.0, 2.0, 0.05) = 0.65;
void vertex() {
VERTEX.y += round(
sin(TIME * bob_speed) * bob_amount_pixels
);
}
void fragment() {
float stepped_time = floor(TIME * 12.0) / 12.0;
float source_row = floor(UV.y / max(TEXTURE_PIXEL_SIZE.y, 0.000001));
float wave = sin(
source_row * wave_scale + stepped_time * wave_speed
);
float pixel_offset = clamp(
round(wave * horizontal_displacement_pixels),
-2.0,
2.0
);
vec2 sample_uv = UV + vec2(pixel_offset * TEXTURE_PIXEL_SIZE.x, 0.0);
vec4 source = texture(TEXTURE, sample_uv);
vec3 treated_color = mix(
source.rgb,
source.rgb * underwater_tint,
tint_strength
);
float highlight_wave = sin(
UV.y * 26.0 - stepped_time * wave_speed * 0.8
) * 0.5 + 0.5;
float highlight_level = floor(highlight_wave * 3.0) / 3.0;
treated_color += (
vec3(0.12, 0.30, 0.34)
* highlight_level
* highlight_strength
* source.a
);
COLOR = vec4(treated_color, source.a);
}

View file

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

View file

@ -7,52 +7,6 @@ const SettingsManagerType = preload(
"res://settings/player_settings_manager.gd"
)
const SettingsPanelType = preload("res://ui/settings_panel.gd")
const DECORATIVE_FISH_TEXTURES: Array[Texture2D] = [
preload("res://fish/species/bass/fish_bass_striped.png"),
preload("res://fish/species/bluegill/fish_bluegill.png"),
preload("res://fish/species/carp/fish_carp_common.png"),
preload("res://fish/species/sunfish/fish_sunfish.png"),
]
const DECORATIVE_BUBBLE_TEXTURES: Array[Texture2D] = [
preload("res://ui/assets/title/bubbles/bubble1.png"),
preload("res://ui/assets/title/bubbles/bubble2.png"),
preload("res://ui/assets/title/bubbles/bubble3.png"),
]
const FIRST_FISH_DELAY_MIN: float = 2.0
const FIRST_FISH_DELAY_MAX: float = 5.0
const NEXT_FISH_DELAY_MIN: float = 5.0
const NEXT_FISH_DELAY_MAX: float = 12.0
const CROSSING_DURATION_MIN: float = 8.0
const CROSSING_DURATION_MAX: float = 16.0
const FISH_LONGEST_SIDE_MIN: float = 120.0
const FISH_LONGEST_SIDE_MAX: float = 210.0
const FISH_EDGE_MARGIN: float = 24.0
const BUBBLE_EVENT_DELAY_MIN: float = 4.0
const BUBBLE_EVENT_DELAY_MAX: float = 10.0
const BUBBLE_CLUSTER_DELAY_MIN: float = 0.25
const BUBBLE_CLUSTER_DELAY_MAX: float = 0.65
const BUBBLE_TRAVEL_DURATION_MIN: float = 7.0
const BUBBLE_TRAVEL_DURATION_MAX: float = 14.0
const BUBBLE_SCALE_MIN: float = 0.65
const BUBBLE_SCALE_MAX: float = 1.15
const BUBBLE_OPACITY_MIN: float = 0.55
const BUBBLE_OPACITY_MAX: float = 0.90
const BUBBLE_DRIFT_MIN: float = 10.0
const BUBBLE_DRIFT_MAX: float = 45.0
const BUBBLE_WOBBLE_MIN: float = 3.0
const BUBBLE_WOBBLE_MAX: float = 9.0
const BUBBLE_EDGE_MARGIN: float = 16.0
const MAX_DECORATIVE_BUBBLES: int = 6
const LOGO_ASPECT_RATIO: float = 2560.0 / 760.0
const LOGO_WIDTH_FACTOR: float = 0.52
const LOGO_MIN_WIDTH: float = 320.0
const LOGO_MAX_WIDTH: float = 720.0
const BUTTON_COLUMN_WIDTH: float = 360.0
const TITLE_HORIZONTAL_MARGIN: float = 48.0
const START_PROMPT_MIN_SCALE: float = 0.985
const START_PROMPT_MAX_SCALE: float = 1.015
const START_PROMPT_CYCLE_SECONDS: float = 3.0
signal gameplay_requested
signal quit_requested
@ -67,41 +21,17 @@ enum ConfirmationAction {
@onready var _new_game_button: Button = %NewGameButton
@onready var _settings_button: Button = %SettingsButton
@onready var _delete_button: Button = %DeleteSaveButton
@onready var _quit_button: Button = %QuitButton
@onready var _feedback_label: Label = %FeedbackLabel
@onready var _confirmation_panel: PanelContainer = %ConfirmationPanel
@onready var _confirmation_text: Label = %ConfirmationText
@onready var _confirm_button: Button = %ConfirmButton
@onready var _settings_panel: SettingsPanelType = %SettingsPanel
@onready var _decorative_fish_layer: Control = %DecorativeFishLayer
@onready var _decorative_fish_timer: Timer = %DecorativeFishTimer
@onready var _decorative_bubble_layer: Control = %DecorativeBubbleLayer
@onready var _decorative_bubble_event_timer: Timer = %DecorativeBubbleEventTimer
@onready var _decorative_bubble_cluster_timer: Timer = %DecorativeBubbleClusterTimer
@onready var _title_logo: TextureRect = %TitleLogo
@onready var _button_center: CenterContainer = %ButtonCenter
@onready var _button_stack: VBoxContainer = %ButtonStack
@onready var _start_prompt_center: CenterContainer = %StartPromptCenter
@onready var _start_prompt_label: Label = %StartPromptLabel
var _save_manager: SaveManagerType
var _settings_manager: SettingsManagerType
var _inspection: SaveInspectionType
var _confirmation_action: ConfirmationAction = ConfirmationAction.NONE
var _action_in_progress: bool = false
var _decorative_rng := RandomNumberGenerator.new()
var _decorative_fish: TextureRect
var _decorative_fish_tween: Tween
var _decorative_presentation_ready: bool = false
var _decorative_presentation_active: bool = false
var _decorative_generation: int = 0
var _decorative_bubbles: Array[TextureRect] = []
var _decorative_bubble_tweens: Dictionary[int, Tween] = {}
var _pending_cluster_bubbles: int = 0
var _awaiting_start_input: bool = false
var _start_prompt_elapsed: float = 0.0
var _navigation_focus_active: bool = false
var _modal_restore_navigation_focus: bool = false
func _ready() -> void:
@ -114,20 +44,6 @@ func _ready() -> void:
%CancelConfirmButton.pressed.connect(_close_confirmation)
_settings_panel.applied.connect(_on_settings_applied)
_settings_panel.closed.connect(_on_settings_closed)
_decorative_fish_timer.timeout.connect(_on_decorative_fish_timer_timeout)
_decorative_bubble_event_timer.timeout.connect(
_on_decorative_bubble_event_timer_timeout
)
_decorative_bubble_cluster_timer.timeout.connect(
_on_decorative_bubble_cluster_timer_timeout
)
visibility_changed.connect(_on_title_visibility_changed)
resized.connect(_update_title_layout)
_start_prompt_label.resized.connect(_update_start_prompt_pivot)
_decorative_rng.randomize()
set_process(false)
call_deferred("_update_title_layout")
call_deferred("_update_start_prompt_pivot")
func setup(
@ -139,104 +55,20 @@ func setup(
Input.mouse_mode = Input.MOUSE_MODE_VISIBLE
_refresh_save_inspection()
show()
_decorative_presentation_ready = true
_start_decorative_presentation()
_begin_title_entry()
call_deferred("_focus_initial_button")
func reopen() -> void:
Input.mouse_mode = Input.MOUSE_MODE_VISIBLE
_prepare_awaiting_start_input()
_close_confirmation()
_settings_panel.hide()
_refresh_save_inspection()
show()
_start_decorative_presentation()
_start_entry_prompt_animation()
func is_awaiting_start_input() -> bool:
return _awaiting_start_input
func is_decorative_presentation_active() -> bool:
return _decorative_presentation_active
func get_active_decorative_fish_count() -> int:
return 1 if is_instance_valid(_decorative_fish) else 0
func get_active_decorative_bubble_count() -> int:
return _decorative_bubbles.size()
func is_decorative_bubble_scheduler_active() -> bool:
return (
_decorative_presentation_active
and (
not _decorative_bubble_event_timer.is_stopped()
or not _decorative_bubble_cluster_timer.is_stopped()
)
)
func _update_title_layout() -> void:
if not is_node_ready():
return
var available_width: float = maxf(1.0, size.x - TITLE_HORIZONTAL_MARGIN)
var logo_width: float = minf(
clampf(
size.x * LOGO_WIDTH_FACTOR,
LOGO_MIN_WIDTH,
LOGO_MAX_WIDTH
),
available_width
)
_title_logo.custom_minimum_size = Vector2(
logo_width,
logo_width / LOGO_ASPECT_RATIO
)
_button_stack.custom_minimum_size = Vector2(
minf(BUTTON_COLUMN_WIDTH, available_width),
0.0
)
func _process(delta: float) -> void:
if (
not _awaiting_start_input
or not visible
or not _start_prompt_center.visible
):
return
_start_prompt_elapsed = fmod(
_start_prompt_elapsed + delta,
START_PROMPT_CYCLE_SECONDS
)
var phase: float = _start_prompt_elapsed / START_PROMPT_CYCLE_SECONDS
var pulse_weight: float = (sin(phase * TAU - PI * 0.5) + 1.0) * 0.5
var prompt_scale: float = lerpf(
START_PROMPT_MIN_SCALE,
START_PROMPT_MAX_SCALE,
pulse_weight
)
_start_prompt_label.scale = Vector2.ONE * prompt_scale
call_deferred("_focus_initial_button")
func _input(event: InputEvent) -> void:
if not visible:
return
if _awaiting_start_input:
if not _is_start_prompt_reveal_event(event):
return
_reveal_primary_menu()
get_viewport().set_input_as_handled()
return
if _handle_primary_menu_focus_input(event):
get_viewport().set_input_as_handled()
return
if not event.is_action_pressed("ui_cancel"):
if not visible or not event.is_action_pressed("ui_cancel"):
return
if _confirmation_panel.visible:
_close_confirmation()
@ -247,119 +79,6 @@ func _input(event: InputEvent) -> void:
get_viewport().set_input_as_handled()
func _is_start_prompt_reveal_event(event: InputEvent) -> bool:
if event is InputEventKey:
var key_event := event as InputEventKey
return key_event.pressed and not key_event.echo
if event is InputEventJoypadButton:
return (event as InputEventJoypadButton).pressed
if event is InputEventMouseButton:
return (event as InputEventMouseButton).pressed
return false
func _handle_primary_menu_focus_input(event: InputEvent) -> bool:
if (
not _button_center.visible
or _confirmation_panel.visible
or _settings_panel.visible
):
return false
if event is InputEventMouseMotion:
_navigation_focus_active = false
_release_primary_menu_focus()
return false
if event is InputEventKey and (event as InputEventKey).echo:
return false
var moves_forward: bool = (
event.is_action_pressed("ui_down")
or event.is_action_pressed("ui_right")
)
var moves_backward: bool = (
event.is_action_pressed("ui_up")
or event.is_action_pressed("ui_left")
)
if not moves_forward and not moves_backward:
return false
_navigation_focus_active = true
if _primary_menu_has_focus():
return false
if moves_forward:
_get_first_available_menu_button().grab_focus()
else:
_quit_button.grab_focus()
return true
func _get_first_available_menu_button() -> Button:
return _continue_button if not _continue_button.disabled else _new_game_button
func _primary_menu_has_focus() -> bool:
var focus_owner: Control = get_viewport().gui_get_focus_owner()
return focus_owner != null and _button_center.is_ancestor_of(focus_owner)
func _release_primary_menu_focus() -> void:
var focus_owner: Control = get_viewport().gui_get_focus_owner()
if focus_owner != null and _button_center.is_ancestor_of(focus_owner):
focus_owner.release_focus()
func _release_title_focus() -> void:
var focus_owner: Control = get_viewport().gui_get_focus_owner()
if focus_owner != null and is_ancestor_of(focus_owner):
focus_owner.release_focus()
func _begin_title_entry() -> void:
_prepare_awaiting_start_input()
_start_entry_prompt_animation()
func _prepare_awaiting_start_input() -> void:
_awaiting_start_input = true
_navigation_focus_active = false
_modal_restore_navigation_focus = false
_button_center.hide()
_feedback_label.hide()
_start_prompt_center.show()
var focus_owner: Control = get_viewport().gui_get_focus_owner()
if focus_owner != null and is_ancestor_of(focus_owner):
focus_owner.release_focus()
func _start_entry_prompt_animation() -> void:
_start_prompt_elapsed = 0.0
_start_prompt_label.scale = Vector2.ONE * START_PROMPT_MIN_SCALE
_update_start_prompt_pivot()
set_process(true)
func _stop_entry_prompt_animation() -> void:
set_process(false)
_start_prompt_elapsed = 0.0
_start_prompt_label.scale = Vector2.ONE
func _update_start_prompt_pivot() -> void:
if not is_node_ready():
return
_start_prompt_label.pivot_offset = _start_prompt_label.size * 0.5
func _reveal_primary_menu() -> void:
if not _awaiting_start_input:
return
_awaiting_start_input = false
_stop_entry_prompt_animation()
_start_prompt_center.hide()
_button_center.show()
_feedback_label.show()
_navigation_focus_active = false
_release_title_focus()
func _on_continue_pressed() -> void:
if (
_action_in_progress
@ -371,11 +90,11 @@ func _on_continue_pressed() -> void:
return
_action_in_progress = true
if _save_manager.load_player_data():
_feedback_label.text = "save loaded."
_feedback_label.text = "Save loaded."
gameplay_requested.emit()
else:
_refresh_save_inspection()
_feedback_label.text = "failed to load save. the original was preserved."
_feedback_label.text = "Failed to load save. The original was preserved."
_action_in_progress = false
@ -393,17 +112,17 @@ func _on_new_game_pressed() -> void:
return
if _inspection.status == SaveInspectionType.Status.UNSUPPORTED_VERSION:
_feedback_label.text = (
"this save is from a newer game version. "
+ "use delete save before starting over."
"This save is from a newer game version. "
+ "Use Delete Save before starting over."
)
return
if _inspection.status == SaveInspectionType.Status.IO_ERROR:
_feedback_label.text = "the existing save cannot be accessed safely."
_feedback_label.text = "The existing save cannot be accessed safely."
return
_open_confirmation(
ConfirmationAction.NEW_GAME,
"start a new game? existing progression will be deleted.",
"start new game"
"Start a new game? Existing progression will be deleted.",
"Start New Game"
)
@ -418,8 +137,8 @@ func _on_delete_pressed() -> void:
return
_open_confirmation(
ConfirmationAction.DELETE_SAVE,
"delete your saved progression? this cannot be undone.",
"delete"
"Delete your saved progression? This cannot be undone.",
"Delete"
)
@ -430,7 +149,7 @@ func _on_confirmation_accepted() -> void:
_close_confirmation()
_action_in_progress = true
if not _save_manager.delete_progression_save():
_feedback_label.text = "failed to delete saved progression."
_feedback_label.text = "Failed to delete saved progression."
_action_in_progress = false
_refresh_save_inspection()
return
@ -439,7 +158,7 @@ func _on_confirmation_accepted() -> void:
if action == ConfirmationAction.NEW_GAME:
gameplay_requested.emit()
else:
_feedback_label.text = "saved progression deleted."
_feedback_label.text = "Saved progression deleted."
_action_in_progress = false
@ -448,7 +167,6 @@ func _open_confirmation(
message: String,
confirm_text: String,
) -> void:
_modal_restore_navigation_focus = _navigation_focus_active
_confirmation_action = action
_confirmation_text.text = message
_confirm_button.text = confirm_text
@ -457,23 +175,14 @@ func _open_confirmation(
func _close_confirmation() -> void:
var restore_navigation_focus: bool = _modal_restore_navigation_focus
_modal_restore_navigation_focus = false
_confirmation_action = ConfirmationAction.NONE
_confirmation_panel.visible = false
if _awaiting_start_input:
return
if restore_navigation_focus:
_focus_initial_button()
else:
_navigation_focus_active = false
_release_title_focus()
func _open_settings() -> void:
if _confirmation_panel.visible or _action_in_progress:
return
_modal_restore_navigation_focus = _navigation_focus_active
_feedback_label.text = ""
_settings_panel.open_panel(_settings_manager)
@ -483,23 +192,12 @@ func _close_settings() -> void:
func _on_settings_applied() -> void:
_feedback_label.text = "settings saved."
_restore_settings_focus()
_feedback_label.text = "Settings saved."
_settings_button.grab_focus()
func _on_settings_closed() -> void:
_restore_settings_focus()
func _restore_settings_focus() -> void:
var restore_navigation_focus: bool = _modal_restore_navigation_focus
_modal_restore_navigation_focus = false
if restore_navigation_focus:
_navigation_focus_active = true
_settings_button.grab_focus()
else:
_navigation_focus_active = false
_release_title_focus()
func _refresh_save_inspection() -> void:
@ -519,18 +217,12 @@ func _refresh_save_inspection() -> void:
func _focus_initial_button() -> void:
if (
not is_node_ready()
or not visible
or _awaiting_start_input
or not _button_center.visible
):
if not is_node_ready() or not visible:
return
if not _continue_button.disabled:
_continue_button.grab_focus()
else:
_new_game_button.grab_focus()
_navigation_focus_active = true
func _on_quit_pressed() -> void:
@ -540,430 +232,3 @@ func _on_quit_pressed() -> void:
and not _settings_panel.visible
):
quit_requested.emit()
func _on_title_visibility_changed() -> void:
if not _decorative_presentation_ready:
return
if visible:
_start_decorative_presentation()
else:
_stop_entry_prompt_animation()
_navigation_focus_active = false
_modal_restore_navigation_focus = false
_stop_decorative_presentation()
func _start_decorative_presentation() -> void:
if (
not _decorative_presentation_ready
or not visible
or _decorative_presentation_active
):
return
_decorative_generation += 1
_decorative_presentation_active = true
_schedule_next_decorative_fish(
FIRST_FISH_DELAY_MIN,
FIRST_FISH_DELAY_MAX
)
_schedule_next_decorative_bubble_event(
BUBBLE_EVENT_DELAY_MIN,
BUBBLE_EVENT_DELAY_MAX
)
func _stop_decorative_presentation() -> void:
_decorative_generation += 1
_decorative_presentation_active = false
_decorative_fish_timer.stop()
if _decorative_fish_tween != null:
_decorative_fish_tween.kill()
_decorative_fish_tween = null
if is_instance_valid(_decorative_fish):
_decorative_fish.queue_free()
_decorative_fish = null
_decorative_bubble_event_timer.stop()
_decorative_bubble_cluster_timer.stop()
_pending_cluster_bubbles = 0
for bubble_tween: Tween in _decorative_bubble_tweens.values():
if bubble_tween != null:
bubble_tween.kill()
_decorative_bubble_tweens.clear()
for bubble: TextureRect in _decorative_bubbles:
if is_instance_valid(bubble):
bubble.queue_free()
_decorative_bubbles.clear()
func _schedule_next_decorative_fish(
minimum_delay: float,
maximum_delay: float,
) -> void:
if not _decorative_presentation_active or not visible:
return
_decorative_fish_timer.start(
_decorative_rng.randf_range(minimum_delay, maximum_delay)
)
func _on_decorative_fish_timer_timeout() -> void:
if (
not _decorative_presentation_active
or not visible
or is_instance_valid(_decorative_fish)
):
return
_spawn_decorative_fish(_decorative_generation)
func _spawn_decorative_fish(generation: int) -> void:
if (
generation != _decorative_generation
or not _decorative_presentation_active
or not visible
or DECORATIVE_FISH_TEXTURES.is_empty()
):
return
var layer_size: Vector2 = _decorative_fish_layer.size
if layer_size.x <= 1.0 or layer_size.y <= 1.0:
_schedule_next_decorative_fish(0.5, 1.0)
return
var texture: Texture2D = DECORATIVE_FISH_TEXTURES[
_decorative_rng.randi_range(0, DECORATIVE_FISH_TEXTURES.size() - 1)
]
var source_size: Vector2 = texture.get_size()
if source_size.x <= 0.0 or source_size.y <= 0.0:
_schedule_next_decorative_fish(
NEXT_FISH_DELAY_MIN,
NEXT_FISH_DELAY_MAX
)
return
var longest_side: float = _decorative_rng.randf_range(
FISH_LONGEST_SIDE_MIN,
FISH_LONGEST_SIDE_MAX
)
var presentation_scale: float = longest_side / maxf(
source_size.x,
source_size.y
)
var presentation_size: Vector2 = source_size * presentation_scale
var fish_control := TextureRect.new()
fish_control.name = "DecorativeFish"
fish_control.texture = texture
fish_control.expand_mode = TextureRect.EXPAND_IGNORE_SIZE
fish_control.stretch_mode = TextureRect.STRETCH_KEEP_ASPECT_CENTERED
fish_control.texture_filter = CanvasItem.TEXTURE_FILTER_NEAREST
fish_control.mouse_filter = Control.MOUSE_FILTER_IGNORE
fish_control.custom_minimum_size = presentation_size
fish_control.size = presentation_size
fish_control.modulate.a = 0.46
var direction: float = (
1.0
if _decorative_rng.randi_range(0, 1) == 1
else -1.0
)
fish_control.flip_h = direction > 0.0
var minimum_y: float = maxf(24.0, layer_size.y * 0.08)
var maximum_y: float = maxf(
minimum_y,
layer_size.y - presentation_size.y - maxf(24.0, layer_size.y * 0.08)
)
var base_y: float = _decorative_rng.randf_range(minimum_y, maximum_y)
var bob_amplitude: float = _decorative_rng.randf_range(3.0, 8.0)
var bob_speed: float = _decorative_rng.randf_range(0.65, 1.15)
var crossing_duration: float = _decorative_rng.randf_range(
CROSSING_DURATION_MIN,
CROSSING_DURATION_MAX
)
fish_control.position = Vector2(
-presentation_size.x - FISH_EDGE_MARGIN
if direction > 0.0
else layer_size.x + FISH_EDGE_MARGIN,
base_y
)
_decorative_fish_layer.add_child(fish_control)
_decorative_fish = fish_control
_decorative_fish_tween = create_tween()
_decorative_fish_tween.tween_method(
_update_decorative_fish.bind(
fish_control,
direction,
base_y,
bob_amplitude,
bob_speed,
crossing_duration,
generation
),
0.0,
1.0,
crossing_duration
)
_decorative_fish_tween.finished.connect(
_on_decorative_fish_finished.bind(fish_control, generation),
CONNECT_ONE_SHOT
)
func _update_decorative_fish(
progress: float,
fish_control: TextureRect,
direction: float,
base_y: float,
bob_amplitude: float,
bob_speed: float,
crossing_duration: float,
generation: int,
) -> void:
if (
generation != _decorative_generation
or not _decorative_presentation_active
or not is_instance_valid(fish_control)
):
return
var start_x: float = (
-fish_control.size.x - FISH_EDGE_MARGIN
if direction > 0.0
else _decorative_fish_layer.size.x + FISH_EDGE_MARGIN
)
var end_x: float = (
_decorative_fish_layer.size.x + FISH_EDGE_MARGIN
if direction > 0.0
else -fish_control.size.x - FISH_EDGE_MARGIN
)
fish_control.position.x = lerpf(start_x, end_x, progress)
var bobbed_y: float = (
base_y
+ sin(progress * crossing_duration * bob_speed) * bob_amplitude
)
fish_control.position.y = clampf(
bobbed_y,
8.0,
maxf(8.0, _decorative_fish_layer.size.y - fish_control.size.y - 8.0)
)
func _on_decorative_fish_finished(
fish_control: TextureRect,
generation: int,
) -> void:
if generation != _decorative_generation:
return
_decorative_fish_tween = null
if is_instance_valid(fish_control):
fish_control.queue_free()
if _decorative_fish == fish_control:
_decorative_fish = null
_schedule_next_decorative_fish(
NEXT_FISH_DELAY_MIN,
NEXT_FISH_DELAY_MAX
)
func _schedule_next_decorative_bubble_event(
minimum_delay: float,
maximum_delay: float,
) -> void:
if not _decorative_presentation_active or not visible:
return
_decorative_bubble_event_timer.start(
_decorative_rng.randf_range(minimum_delay, maximum_delay)
)
func _on_decorative_bubble_event_timer_timeout() -> void:
if not _decorative_presentation_active or not visible:
return
var available_slots: int = (
MAX_DECORATIVE_BUBBLES - _decorative_bubbles.size()
)
if available_slots <= 0:
_schedule_next_decorative_bubble_event(
BUBBLE_EVENT_DELAY_MIN,
BUBBLE_EVENT_DELAY_MAX
)
return
var event_size: int = _choose_decorative_bubble_event_size()
event_size = mini(event_size, available_slots)
if not _spawn_decorative_bubble(_decorative_generation):
_schedule_next_decorative_bubble_event(0.5, 1.0)
return
_pending_cluster_bubbles = event_size - 1
if _pending_cluster_bubbles > 0:
_start_decorative_bubble_cluster_timer()
else:
_schedule_next_decorative_bubble_event(
BUBBLE_EVENT_DELAY_MIN,
BUBBLE_EVENT_DELAY_MAX
)
func _choose_decorative_bubble_event_size() -> int:
var roll: float = _decorative_rng.randf()
if roll < 0.75:
return 1
if roll < 0.95:
return 2
return 3
func _start_decorative_bubble_cluster_timer() -> void:
_decorative_bubble_cluster_timer.start(
_decorative_rng.randf_range(
BUBBLE_CLUSTER_DELAY_MIN,
BUBBLE_CLUSTER_DELAY_MAX
)
)
func _on_decorative_bubble_cluster_timer_timeout() -> void:
if (
not _decorative_presentation_active
or not visible
or _pending_cluster_bubbles <= 0
):
_pending_cluster_bubbles = 0
return
if _decorative_bubbles.size() < MAX_DECORATIVE_BUBBLES:
_spawn_decorative_bubble(_decorative_generation)
_pending_cluster_bubbles -= 1
if _pending_cluster_bubbles > 0:
_start_decorative_bubble_cluster_timer()
else:
_schedule_next_decorative_bubble_event(
BUBBLE_EVENT_DELAY_MIN,
BUBBLE_EVENT_DELAY_MAX
)
func _spawn_decorative_bubble(generation: int) -> bool:
if (
generation != _decorative_generation
or not _decorative_presentation_active
or not visible
or _decorative_bubbles.size() >= MAX_DECORATIVE_BUBBLES
or DECORATIVE_BUBBLE_TEXTURES.is_empty()
):
return false
var layer_size: Vector2 = _decorative_bubble_layer.size
if layer_size.x <= 1.0 or layer_size.y <= 1.0:
return false
var texture: Texture2D = DECORATIVE_BUBBLE_TEXTURES[
_decorative_rng.randi_range(
0,
DECORATIVE_BUBBLE_TEXTURES.size() - 1
)
]
var presentation_scale: float = _decorative_rng.randf_range(
BUBBLE_SCALE_MIN,
BUBBLE_SCALE_MAX
)
var presentation_size: Vector2 = texture.get_size() * presentation_scale
var bubble := TextureRect.new()
bubble.name = "DecorativeBubble"
bubble.texture = texture
bubble.expand_mode = TextureRect.EXPAND_IGNORE_SIZE
bubble.stretch_mode = TextureRect.STRETCH_KEEP_ASPECT_CENTERED
bubble.texture_filter = CanvasItem.TEXTURE_FILTER_NEAREST
bubble.mouse_filter = Control.MOUSE_FILTER_IGNORE
bubble.size = presentation_size
bubble.modulate.a = _decorative_rng.randf_range(
BUBBLE_OPACITY_MIN,
BUBBLE_OPACITY_MAX
)
_decorative_bubble_layer.add_child(bubble)
_decorative_bubbles.append(bubble)
var normalized_x: float = _decorative_rng.randf_range(0.08, 0.92)
var drift_direction: float = (
1.0 if _decorative_rng.randi_range(0, 1) == 1 else -1.0
)
var horizontal_drift: float = (
_decorative_rng.randf_range(
BUBBLE_DRIFT_MIN,
BUBBLE_DRIFT_MAX
)
* drift_direction
)
var wobble_amplitude: float = _decorative_rng.randf_range(
BUBBLE_WOBBLE_MIN,
BUBBLE_WOBBLE_MAX
)
var wobble_cycles: float = _decorative_rng.randf_range(0.8, 1.6)
var wobble_phase: float = _decorative_rng.randf_range(0.0, TAU)
var travel_duration: float = _decorative_rng.randf_range(
BUBBLE_TRAVEL_DURATION_MIN,
BUBBLE_TRAVEL_DURATION_MAX
)
var bubble_tween: Tween = create_tween()
_decorative_bubble_tweens[bubble.get_instance_id()] = bubble_tween
bubble_tween.tween_method(
_update_decorative_bubble.bind(
bubble,
normalized_x,
horizontal_drift,
wobble_amplitude,
wobble_cycles,
wobble_phase,
generation
),
0.0,
1.0,
travel_duration
)
bubble_tween.finished.connect(
_on_decorative_bubble_finished.bind(bubble, generation),
CONNECT_ONE_SHOT
)
return true
func _update_decorative_bubble(
progress: float,
bubble: TextureRect,
normalized_x: float,
horizontal_drift: float,
wobble_amplitude: float,
wobble_cycles: float,
wobble_phase: float,
generation: int,
) -> void:
if (
generation != _decorative_generation
or not _decorative_presentation_active
or not is_instance_valid(bubble)
):
return
var layer_size: Vector2 = _decorative_bubble_layer.size
var start_y: float = (
layer_size.y + bubble.size.y + BUBBLE_EDGE_MARGIN
)
var end_y: float = -bubble.size.y - BUBBLE_EDGE_MARGIN
var base_x: float = normalized_x * layer_size.x
var wobble: float = sin(
wobble_phase + progress * TAU * wobble_cycles
) * wobble_amplitude
bubble.position = Vector2(
base_x
+ horizontal_drift * progress
+ wobble
- bubble.size.x * 0.5,
lerpf(start_y, end_y, progress)
)
func _on_decorative_bubble_finished(
bubble: TextureRect,
generation: int,
) -> void:
if generation != _decorative_generation:
return
if is_instance_valid(bubble):
_decorative_bubble_tweens.erase(bubble.get_instance_id())
_decorative_bubbles.erase(bubble)
bubble.queue_free()
func _exit_tree() -> void:
_stop_decorative_presentation()

View file

@ -1,17 +1,8 @@
[gd_scene load_steps=9 format=3]
[gd_scene load_steps=4 format=3]
[ext_resource type="Script" path="res://ui/title_screen.gd" id="1_script"]
[ext_resource type="Theme" path="res://ui/game_theme.tres" id="2_theme"]
[ext_resource type="PackedScene" path="res://ui/settings_panel.tscn" id="3_settings"]
[ext_resource type="Shader" path="res://ui/title_water_background.gdshader" id="4_water_shader"]
[ext_resource type="Texture2D" path="res://ui/assets/title/netfishing_logo.png" id="5_logo"]
[ext_resource type="Shader" path="res://ui/title_logo_underwater.gdshader" id="6_logo_shader"]
[sub_resource type="ShaderMaterial" id="ShaderMaterial_title_water"]
shader = ExtResource("4_water_shader")
[sub_resource type="ShaderMaterial" id="ShaderMaterial_title_logo"]
shader = ExtResource("6_logo_shader")
[node name="TitleScreen" type="Control"]
unique_name_in_owner = true
@ -27,78 +18,15 @@ theme = ExtResource("2_theme")
script = ExtResource("1_script")
[node name="Background" type="ColorRect" parent="."]
material = SubResource("ShaderMaterial_title_water")
layout_mode = 1
anchors_preset = 15
anchor_right = 1.0
anchor_bottom = 1.0
grow_horizontal = 2
grow_vertical = 2
mouse_filter = 2
[node name="DecorativeFishLayer" type="Control" parent="."]
unique_name_in_owner = true
z_index = 1
clip_contents = true
layout_mode = 1
anchors_preset = 15
anchor_right = 1.0
anchor_bottom = 1.0
grow_horizontal = 2
grow_vertical = 2
mouse_filter = 2
[node name="DecorativeFishTimer" type="Timer" parent="."]
unique_name_in_owner = true
one_shot = true
[node name="DecorativeBubbleLayer" type="Control" parent="."]
unique_name_in_owner = true
z_index = 2
clip_contents = true
layout_mode = 1
anchors_preset = 15
anchor_right = 1.0
anchor_bottom = 1.0
grow_horizontal = 2
grow_vertical = 2
mouse_filter = 2
[node name="DecorativeBubbleEventTimer" type="Timer" parent="."]
unique_name_in_owner = true
one_shot = true
[node name="DecorativeBubbleClusterTimer" type="Timer" parent="."]
unique_name_in_owner = true
one_shot = true
[node name="StartPromptCenter" type="CenterContainer" parent="."]
unique_name_in_owner = true
z_index = 12
layout_mode = 1
anchor_top = 1.0
anchor_right = 1.0
anchor_bottom = 1.0
offset_top = -104.0
offset_bottom = -48.0
grow_horizontal = 2
grow_vertical = 0
mouse_filter = 2
[node name="StartPromptLabel" type="Label" parent="StartPromptCenter"]
unique_name_in_owner = true
layout_mode = 2
mouse_filter = 2
theme_override_colors/font_color = Color(1, 1, 1, 1)
theme_override_colors/font_outline_color = Color(0.02, 0.075, 0.11, 0.9)
theme_override_constants/outline_size = 3
theme_override_font_sizes/font_size = 26
text = "press any key to start"
horizontal_alignment = 1
vertical_alignment = 1
color = Color(0.047, 0.102, 0.145, 1)
[node name="Center" type="CenterContainer" parent="."]
z_index = 10
layout_mode = 1
anchors_preset = 15
anchor_right = 1.0
@ -106,77 +34,75 @@ anchor_bottom = 1.0
grow_horizontal = 2
grow_vertical = 2
[node name="MainContent" type="VBoxContainer" parent="Center"]
[node name="MainPanel" type="PanelContainer" parent="Center"]
custom_minimum_size = Vector2(440, 0)
layout_mode = 2
theme_override_constants/separation = 8
alignment = 1
[node name="TitleLogo" type="TextureRect" parent="Center/MainContent"]
unique_name_in_owner = true
custom_minimum_size = Vector2(640, 190)
material = SubResource("ShaderMaterial_title_logo")
[node name="Margin" type="MarginContainer" parent="Center/MainPanel"]
layout_mode = 2
mouse_filter = 2
texture_filter = 1
texture = ExtResource("5_logo")
expand_mode = 1
stretch_mode = 5
theme_override_constants/margin_left = 36
theme_override_constants/margin_top = 30
theme_override_constants/margin_right = 36
theme_override_constants/margin_bottom = 30
[node name="PlaytestLabel" type="Label" parent="Center/MainContent"]
[node name="Content" type="VBoxContainer" parent="Center/MainPanel/Margin"]
layout_mode = 2
theme_override_constants/separation = 12
[node name="Title" type="Label" parent="Center/MainPanel/Margin/Content"]
layout_mode = 2
theme_override_font_sizes/font_size = 36
text = "NETFISHING"
horizontal_alignment = 1
[node name="Subtitle" type="Label" parent="Center/MainPanel/Margin/Content"]
layout_mode = 2
text = "A cozy social fishing game"
horizontal_alignment = 1
[node name="PlaytestLabel" type="Label" parent="Center/MainPanel/Margin/Content"]
layout_mode = 2
theme_override_colors/font_color = Color(0.682, 0.733, 0.761, 1)
theme_override_font_sizes/font_size = 13
text = "pre-alpha playtest 0.1"
text = "Pre-Alpha Playtest 0.1"
horizontal_alignment = 1
[node name="Spacer" type="Control" parent="Center/MainContent"]
custom_minimum_size = Vector2(0, 4)
[node name="Spacer" type="Control" parent="Center/MainPanel/Margin/Content"]
custom_minimum_size = Vector2(0, 12)
layout_mode = 2
[node name="ButtonCenter" type="CenterContainer" parent="Center/MainContent"]
unique_name_in_owner = true
visible = false
layout_mode = 2
[node name="ButtonStack" type="VBoxContainer" parent="Center/MainContent/ButtonCenter"]
unique_name_in_owner = true
custom_minimum_size = Vector2(360, 0)
layout_mode = 2
theme_override_constants/separation = 8
[node name="ContinueButton" type="Button" parent="Center/MainContent/ButtonCenter/ButtonStack"]
[node name="ContinueButton" type="Button" parent="Center/MainPanel/Margin/Content"]
unique_name_in_owner = true
custom_minimum_size = Vector2(0, 44)
layout_mode = 2
text = "continue"
text = "Continue"
[node name="NewGameButton" type="Button" parent="Center/MainContent/ButtonCenter/ButtonStack"]
[node name="NewGameButton" type="Button" parent="Center/MainPanel/Margin/Content"]
unique_name_in_owner = true
custom_minimum_size = Vector2(0, 44)
layout_mode = 2
text = "new game"
text = "New Game"
[node name="SettingsButton" type="Button" parent="Center/MainContent/ButtonCenter/ButtonStack"]
[node name="SettingsButton" type="Button" parent="Center/MainPanel/Margin/Content"]
unique_name_in_owner = true
custom_minimum_size = Vector2(0, 44)
layout_mode = 2
text = "settings"
text = "Settings"
[node name="DeleteSaveButton" type="Button" parent="Center/MainContent/ButtonCenter/ButtonStack"]
[node name="DeleteSaveButton" type="Button" parent="Center/MainPanel/Margin/Content"]
unique_name_in_owner = true
custom_minimum_size = Vector2(0, 44)
layout_mode = 2
text = "delete save"
text = "Delete Save"
[node name="QuitButton" type="Button" parent="Center/MainContent/ButtonCenter/ButtonStack"]
[node name="QuitButton" type="Button" parent="Center/MainPanel/Margin/Content"]
unique_name_in_owner = true
custom_minimum_size = Vector2(0, 44)
layout_mode = 2
text = "quit"
text = "Quit"
[node name="FeedbackLabel" type="Label" parent="Center/MainContent"]
[node name="FeedbackLabel" type="Label" parent="Center/MainPanel/Margin/Content"]
unique_name_in_owner = true
visible = false
custom_minimum_size = Vector2(0, 42)
layout_mode = 2
text = ""
@ -215,7 +141,7 @@ theme_override_constants/separation = 14
[node name="ConfirmationText" type="Label" parent="ConfirmationPanel/Margin/Content"]
unique_name_in_owner = true
layout_mode = 2
text = "confirm?"
text = "Confirm?"
horizontal_alignment = 1
autowrap_mode = 2
@ -228,13 +154,13 @@ alignment = 1
unique_name_in_owner = true
custom_minimum_size = Vector2(150, 42)
layout_mode = 2
text = "confirm"
text = "Confirm"
[node name="CancelConfirmButton" type="Button" parent="ConfirmationPanel/Margin/Content/Buttons"]
unique_name_in_owner = true
custom_minimum_size = Vector2(120, 42)
layout_mode = 2
text = "cancel"
text = "Cancel"
[node name="SettingsPanel" parent="." instance=ExtResource("3_settings")]
unique_name_in_owner = true

View file

@ -1,95 +0,0 @@
shader_type canvas_item;
uniform vec4 surface_color : source_color = vec4(0.10, 0.45, 0.62, 1.0);
uniform vec4 middle_color : source_color = vec4(0.055, 0.285, 0.40, 1.0);
uniform vec4 depth_color : source_color = vec4(0.02, 0.16, 0.27, 1.0);
uniform vec2 virtual_pixel_density = vec2(320.0, 180.0);
uniform float color_step_count : hint_range(2.0, 16.0, 1.0) = 14.0;
uniform float continuous_depth_blend : hint_range(0.0, 0.25, 0.01) = 0.12;
uniform float wave_speed : hint_range(0.0, 0.5, 0.01) = 0.10;
uniform float wave_scale : hint_range(0.5, 8.0, 0.1) = 2.4;
uniform float wave_strength : hint_range(0.0, 0.05, 0.001) = 0.012;
uniform float caustic_strength : hint_range(0.0, 0.08, 0.001) = 0.018;
void fragment() {
vec2 grid_size = max(virtual_pixel_density, vec2(1.0));
vec2 pixel_uv = (floor(UV * grid_size) + vec2(0.5)) / grid_size;
float motion = TIME * wave_speed;
float broad_wave = sin(
pixel_uv.x * wave_scale + motion
);
float secondary_wave = sin(
pixel_uv.x * 1.1 + pixel_uv.y * 1.6 - motion * 0.65
);
float movement_falloff = 1.0 - pixel_uv.y * 0.75;
float depth_displacement = (
(broad_wave * 0.65 + secondary_wave * 0.35)
* wave_strength
* movement_falloff
);
float continuous_depth = clamp(
pixel_uv.y
+ depth_displacement,
0.0,
1.0
);
float depth_steps = max(2.0, floor(color_step_count));
float band_index = floor(
continuous_depth * (depth_steps - 1.0) + 0.5
);
float band_depth = band_index / (depth_steps - 1.0);
float display_depth = mix(
band_depth,
continuous_depth,
clamp(continuous_depth_blend, 0.0, 0.25)
);
vec3 water_color;
if (display_depth < 0.5) {
water_color = mix(
surface_color.rgb,
middle_color.rgb,
display_depth * 2.0
);
} else {
water_color = mix(
middle_color.rgb,
depth_color.rgb,
(display_depth - 0.5) * 2.0
);
}
float caustic_wave = clamp(
(
sin(
pixel_uv.x * 4.0
+ pixel_uv.y * 1.2
+ motion * 0.7
)
* sin(
pixel_uv.x * 1.7
- pixel_uv.y * 2.2
- motion * 0.55
)
- 0.25
) / 0.75,
0.0,
1.0
);
float caustic_level = floor(
caustic_wave * 3.0
) / 3.0;
float upper_water_mask = 1.0 - smoothstep(
0.18,
0.34,
pixel_uv.y
);
water_color += (
vec3(0.05, 0.12, 0.14)
* caustic_level
* upper_water_mask
* caustic_strength
);
COLOR = vec4(clamp(water_color, vec3(0.0), vec3(1.0)), 1.0);
}

View file

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

View file

@ -60,7 +60,7 @@ material_override = SubResource("PrimaryMaterial")
[node name="ShopName" type="Label3D" parent="Visuals"]
position = Vector3(0, 3.7, 0)
text = "fishing shop"
text = "Fishing Shop"
font_size = 42
outline_size = 9
billboard = 1

View file

@ -35,7 +35,7 @@ material_override = SubResource("WoodMaterial")
[node name="Sign" type="Label3D" parent="Visuals"]
position = Vector3(0, 2.55, 0)
text = "sell to pelicans\nfrom the cooler"
text = "Sell to Pelicans\nfrom the Cooler"
font_size = 30
outline_size = 8
billboard = 1
@ -43,7 +43,7 @@ no_depth_test = true
[node name="ConvenienceLabel" type="Label3D" parent="Visuals"]
position = Vector3(0, 1.65, -1.15)
text = "available anywhere • 0.25x"
text = "Available anywhere • 0.25x"
font_size = 20
outline_size = 6
billboard = 1

View file

@ -82,7 +82,7 @@ mesh = SubResource("CraneArmMesh")
material_override = SubResource("BeaconMaterial")
[node name="CoastLabel" type="Label3D" parent="Visuals"]
position = Vector3(25, 3.2, 23)
text = "coast"
text = "COAST"
font_size = 38
outline_size = 8
billboard = 1

View file

@ -71,7 +71,7 @@ mesh = SubResource("DockRampMesh")
material_override = SubResource("DockMaterial")
[node name="LakeLabel" type="Label3D" parent="Visuals"]
position = Vector3(-25, 3, 20)
text = "lake"
text = "LAKE"
font_size = 38
outline_size = 8
billboard = 1

View file

@ -90,7 +90,7 @@ mesh = SubResource("ReedMesh")
material_override = SubResource("ReedMaterial")
[node name="MarshLabel" type="Label3D" parent="Visuals"]
position = Vector3(21, 3, 17)
text = "marsh"
text = "MARSH"
font_size = 36
outline_size = 8
billboard = 1

View file

@ -76,7 +76,7 @@ mesh = SubResource("RailMesh")
material_override = SubResource("RailMaterial")
[node name="RiverLabel" type="Label3D" parent="Visuals"]
position = Vector3(-16, 3, 20)
text = "river"
text = "RIVER"
font_size = 34
outline_size = 7
billboard = 1

View file

@ -54,7 +54,7 @@ mesh = SubResource("WaterMesh")
material_override = SubResource("WaterMaterial")
[node name="PondLabel" type="Label3D" parent="Visuals"]
position = Vector3(-14, 2.4, 11)
text = "pond"
text = "POND"
font_size = 34
outline_size = 7
billboard = 1

View file

@ -122,7 +122,7 @@ material_override = SubResource("RoofMaterial")
[node name="VillageLabel" type="Label3D" parent="Visuals"]
position = Vector3(0, 3.2, 7)
text = "village"
text = "VILLAGE"
font_size = 42
outline_size = 8
billboard = 1