diff --git a/fish/day_availability.tres b/fish/day_availability.tres deleted file mode 100644 index 6cf8205..0000000 --- a/fish/day_availability.tres +++ /dev/null @@ -1,10 +0,0 @@ -[gd_resource type="Resource" load_steps=2 format=3] - -[ext_resource type="Script" path="res://fish/fish_availability.gd" id="1_availability"] - -[resource] -script = ExtResource("1_availability") -allow_day = true -allow_night = false -preferred_bait_tags = Array[StringName]([&"worm"]) -preferred_bait_weight_multiplier = 1.35 diff --git a/fish/fish_availability.gd b/fish/fish_availability.gd index f46a973..f593d75 100644 --- a/fish/fish_availability.gd +++ b/fish/fish_availability.gd @@ -10,35 +10,22 @@ const FishingContextType = preload("res://fishing/fishing_context.gd") @export var allow_night: bool = true @export var require_rain: bool = false @export var forbid_rain: bool = false -@export var require_fog: bool = false -@export var forbid_fog: bool = false @export var required_bait_tags: Array[StringName] = [] @export var preferred_bait_tags: Array[StringName] = [] @export_range(0.01, 10.0, 0.01) var preferred_bait_weight_multiplier: float = 1.25 func is_available(context: FishingContextType) -> bool: - if ( - context == null - or (require_rain and forbid_rain) - or (require_fog and forbid_fog) - ): + if context == null or (require_rain and forbid_rain): return false - if context.is_day_night_transition: - if not allow_day and not allow_night: - return false - elif context.is_night and not allow_night: + if context.is_night and not allow_night: return false - elif not context.is_night and not allow_day: + if not context.is_night and not allow_day: return false if require_rain and not context.is_raining: return false if forbid_rain and context.is_raining: return false - if require_fog and not context.is_foggy: - return false - if forbid_fog and context.is_foggy: - return false if ( not allowed_location_tags.is_empty() and not _has_any_match(allowed_location_tags, context.location_tags) diff --git a/fish/species/bluegill/bluegill.tres b/fish/species/bluegill/bluegill.tres index 9e98a5b..0c72a89 100644 --- a/fish/species/bluegill/bluegill.tres +++ b/fish/species/bluegill/bluegill.tres @@ -2,7 +2,7 @@ [ext_resource type="Script" path="res://fish/fish_data.gd" id="1_fish_data"] [ext_resource type="Resource" path="res://fishing/common_catch_profile.tres" id="2_profile"] -[ext_resource type="Resource" path="res://fish/day_availability.tres" id="3_availability"] +[ext_resource type="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"] [resource] diff --git a/fish/species/catfish_blue/catfish_blue.tres b/fish/species/catfish_blue/catfish_blue.tres index 67065dd..9867c69 100644 --- a/fish/species/catfish_blue/catfish_blue.tres +++ b/fish/species/catfish_blue/catfish_blue.tres @@ -8,8 +8,6 @@ [sub_resource type="Resource" id="BlueCatfishAvailability"] script = ExtResource("3_availability_script") allowed_location_tags = Array[StringName]([&"starter_pond"]) -allow_day = false -allow_night = true preferred_bait_tags = Array[StringName]([&"worm", &"minnow"]) preferred_bait_weight_multiplier = 1.35 diff --git a/fish/species/catfish_channel/catfish_channel.tres b/fish/species/catfish_channel/catfish_channel.tres index ae8b0a4..679d947 100644 --- a/fish/species/catfish_channel/catfish_channel.tres +++ b/fish/species/catfish_channel/catfish_channel.tres @@ -8,8 +8,6 @@ [sub_resource type="Resource" id="ChannelCatfishAvailability"] script = ExtResource("3_availability_script") allowed_location_tags = Array[StringName]([&"starter_pond"]) -allow_day = false -allow_night = true preferred_bait_tags = Array[StringName]([&"worm"]) preferred_bait_weight_multiplier = 1.35 diff --git a/fish/species/catfish_flathead/catfish_flathead.tres b/fish/species/catfish_flathead/catfish_flathead.tres index d2cb8a7..c927bc3 100644 --- a/fish/species/catfish_flathead/catfish_flathead.tres +++ b/fish/species/catfish_flathead/catfish_flathead.tres @@ -8,8 +8,6 @@ [sub_resource type="Resource" id="FlatheadCatfishAvailability"] script = ExtResource("3_availability_script") allowed_location_tags = Array[StringName]([&"starter_pond"]) -allow_day = false -allow_night = true preferred_bait_tags = Array[StringName]([&"worm", &"minnow"]) preferred_bait_weight_multiplier = 1.35 diff --git a/fish/species/catfish_white/catfish_white.tres b/fish/species/catfish_white/catfish_white.tres index 2238d1f..7655346 100644 --- a/fish/species/catfish_white/catfish_white.tres +++ b/fish/species/catfish_white/catfish_white.tres @@ -8,8 +8,6 @@ [sub_resource type="Resource" id="WhiteCatfishAvailability"] script = ExtResource("3_availability_script") allowed_location_tags = Array[StringName]([&"starter_pond"]) -allow_day = false -allow_night = true preferred_bait_tags = Array[StringName]([&"worm"]) preferred_bait_weight_multiplier = 1.35 diff --git a/fish/species/sunfish/sunfish.tres b/fish/species/sunfish/sunfish.tres index 14d2ced..c1d6200 100644 --- a/fish/species/sunfish/sunfish.tres +++ b/fish/species/sunfish/sunfish.tres @@ -2,7 +2,7 @@ [ext_resource type="Script" path="res://fish/fish_data.gd" id="1_fish_data"] [ext_resource type="Resource" path="res://fishing/common_catch_profile.tres" id="2_profile"] -[ext_resource type="Resource" path="res://fish/day_availability.tres" id="3_availability"] +[ext_resource type="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] diff --git a/fishing/fishing_context.gd b/fishing/fishing_context.gd index b3f1acd..b5b3984 100644 --- a/fishing/fishing_context.gd +++ b/fishing/fishing_context.gd @@ -6,6 +6,4 @@ var water_type: WaterType.Type = WaterType.Type.FRESH_WATER var active_event_tags: Array[StringName] = [] var active_bait_tags: Array[StringName] = [] var is_night: bool = false -var is_day_night_transition: bool = false var is_raining: bool = false -var is_foggy: bool = false diff --git a/fishing/fishing_spot.gd b/fishing/fishing_spot.gd index 0dcaf1f..bdeec55 100644 --- a/fishing/fishing_spot.gd +++ b/fishing/fishing_spot.gd @@ -37,10 +37,6 @@ const FishingSurfaceResolverType = preload( "res://fishing/fishing_surface_resolver.gd" ) const ArtShopStockType = preload("res://economy/art_shop_stock.gd") -const WorldTimeServiceType = preload("res://world/world_time_service.gd") -const WorldWeatherServiceType = preload( - "res://world/world_weather_service.gd" -) signal status_changed(status: String) signal catch_display_changed( @@ -112,6 +108,8 @@ const NETWORK_INPUT_RESEND_INTERVAL_SECONDS: float = 0.1 @export var deterministic_selection_seed: int = 24680 @export_category("Context Testing") +@export var context_is_night: bool = false +@export var context_is_raining: bool = false @export var context_event_tags: Array[StringName] = [] @export var context_bait_tags: Array[StringName] = [] @@ -134,8 +132,6 @@ var _network_item_use: NetworkItemUseService var _cooler_capacity: PlayerCoolerCapacityType var _network_session: NetworkSessionType var _network_fishing: NetworkFishingServiceType -var _world_time: WorldTimeServiceType -var _world_weather: WorldWeatherServiceType var _active_player: PlayerType var _state_time_remaining: float = 0.0 var _cast_charge: float = 0.0 @@ -194,8 +190,6 @@ func setup( cooler_capacity: PlayerCoolerCapacityType, network_session: NetworkSessionType = null, network_fishing: NetworkFishingServiceType = null, - world_time: WorldTimeServiceType = null, - world_weather: WorldWeatherServiceType = null, ) -> void: _local_player = local_player _local_inventory = local_inventory @@ -213,8 +207,6 @@ func setup( _cooler_capacity = cooler_capacity _network_session = network_session _network_fishing = network_fishing - _world_time = world_time - _world_weather = world_weather if _network_fishing != null: _network_fishing.local_cast_accepted.connect( _on_network_cast_accepted @@ -1304,12 +1296,8 @@ func _build_fishing_context( context.water_type = region.water_type context.active_event_tags = context_event_tags.duplicate() context.active_bait_tags = context_bait_tags.duplicate() - if _world_time != null: - context.is_night = _world_time.is_night_period() - context.is_day_night_transition = _world_time.is_transition() - if _world_weather != null: - context.is_raining = _world_weather.is_raining() - context.is_foggy = _world_weather.is_foggy() + context.is_night = context_is_night + context.is_raining = context_is_raining return context diff --git a/main/main.gd b/main/main.gd index a08a53e..d05639f 100644 --- a/main/main.gd +++ b/main/main.gd @@ -77,17 +77,6 @@ const PlayerAppearanceStoreType = preload( const NetworkProfileServiceType = preload( "res://network/network_profile_service.gd" ) -const WorldTimeServiceType = preload("res://world/world_time_service.gd") -const WorldTimeVisualControllerType = preload( - "res://world/world_time_visual_controller.gd" -) -const NetworkWorldTimeServiceType = preload( - "res://network/network_world_time_service.gd" -) -const WorldWeatherServiceType = preload("res://world/world_weather_service.gd") -const NetworkWorldWeatherServiceType = preload( - "res://network/network_world_weather_service.gd" -) const TITLE_MUSIC_SILENCE_DB: float = -80.0 const PLAYER_MENU_PATTERN_SCALE: float = 0.85 @@ -119,17 +108,6 @@ const SHOP_PATTERN_SCALE: float = 1.75 %WorldPixelationPostprocess ) @onready var _network_session: NetworkSessionType = %NetworkSession -@onready var _world_time: WorldTimeServiceType = %WorldTimeService -@onready var _world_time_visuals: WorldTimeVisualControllerType = ( - %WorldTimeVisualController -) -@onready var _network_world_time: NetworkWorldTimeServiceType = ( - %NetworkWorldTimeService -) -@onready var _world_weather: WorldWeatherServiceType = %WorldWeatherService -@onready var _network_world_weather: NetworkWorldWeatherServiceType = ( - %NetworkWorldWeatherService -) @onready var _data_root: PlayerDataRoot = %PlayerDataRoot @onready var _identity_backups: IdentityBackupService = %IdentityBackupService @onready var _network_profile: NetworkProfilePreferencesType = ( @@ -285,15 +263,6 @@ func _initialize_after_data_root() -> void: _server_trust, _host_bans, ) - _world_time_visuals.setup( - _world_time, - _test_world.get_world_environment(), - _test_world.get_sun(), - _world_weather, - _player, - ) - _network_world_time.setup(_network_session, _world_time) - _network_world_weather.setup(_network_session, _world_weather) _identity_backups.setup(_data_root, _player_identity, _host_identity) _network_profile_service.setup( _network_session, @@ -459,9 +428,7 @@ func _initialize_after_data_root() -> void: _network_item_use, _player.cooler_capacity, _network_session, - _network_fishing, - _world_time, - _world_weather, + _network_fishing ) _game_ui.setup( _player, @@ -493,8 +460,6 @@ func _initialize_after_data_root() -> void: _settings_manager, _network_surface_drawing, _player.art_unlocks, - _world_time, - _world_weather, ) _game_ui.setup_data_and_identity( _data_root, diff --git a/main/main.tscn b/main/main.tscn index 1e60b8d..dd7c804 100644 --- a/main/main.tscn +++ b/main/main.tscn @@ -46,11 +46,6 @@ [ext_resource type="PackedScene" uid="uid://w7n4gjqq1juc" path="res://art/exported/characters/base/netfishing_base_character.glb" id="44_4pcu1"] [ext_resource type="Script" path="res://network/network_fish_showcase_service.gd" id="45_fish_showcase"] [ext_resource type="Script" path="res://network/network_surface_drawing_service.gd" id="46_surface_drawing"] -[ext_resource type="Script" path="res://world/world_time_service.gd" id="47_world_time"] -[ext_resource type="Script" path="res://world/world_time_visual_controller.gd" id="48_world_time_visuals"] -[ext_resource type="Script" path="res://network/network_world_time_service.gd" id="49_network_world_time"] -[ext_resource type="Script" path="res://world/world_weather_service.gd" id="50_world_weather"] -[ext_resource type="Script" path="res://network/network_world_weather_service.gd" id="51_network_world_weather"] [sub_resource type="ShaderMaterial" id="ShaderMaterial_title_water_native"] shader = ExtResource("40_title_water") @@ -118,26 +113,6 @@ mouse_filter = 2 unique_name_in_owner = true script = ExtResource("39_fonts") -[node name="WorldTimeService" type="Node" parent="."] -unique_name_in_owner = true -script = ExtResource("47_world_time") - -[node name="WorldTimeVisualController" type="Node" parent="."] -unique_name_in_owner = true -script = ExtResource("48_world_time_visuals") - -[node name="NetworkWorldTimeService" type="Node" parent="."] -unique_name_in_owner = true -script = ExtResource("49_network_world_time") - -[node name="WorldWeatherService" type="Node" parent="."] -unique_name_in_owner = true -script = ExtResource("50_world_weather") - -[node name="NetworkWorldWeatherService" type="Node" parent="."] -unique_name_in_owner = true -script = ExtResource("51_network_world_weather") - [node name="PlayerDataRoot" type="Node" parent="." unique_id=281868360] unique_name_in_owner = true script = ExtResource("37_data_root") diff --git a/network/network_protocol.gd b/network/network_protocol.gd index ee1c277..1e60145 100644 --- a/network/network_protocol.gd +++ b/network/network_protocol.gd @@ -20,8 +20,6 @@ const MAIL_RELIABLE_CHANNEL: int = 9 const ENET_CHANNEL_COUNT: int = 10 const SURFACE_DRAWING_CAPABILITY: String = "surface_drawing_v2" const ART_SHOP_CAPABILITY: String = "art_shop_v1" -const WORLD_TIME_CAPABILITY: String = "world_time_v1" -const WORLD_WEATHER_CAPABILITY: String = "world_weather_v1" enum RejectionCode { NONE, @@ -89,8 +87,6 @@ static func make_client_hello( "client_nonce": client_nonce, "capability_flags": PackedStringArray([ SURFACE_DRAWING_CAPABILITY, - WORLD_TIME_CAPABILITY, - WORLD_WEATHER_CAPABILITY, ]), "cosmetic_snapshot": cosmetic_snapshot, "identity_fingerprint": identity_fingerprint, @@ -208,8 +204,6 @@ static func make_server_hello( "equipment_v1", "fish_showcase_v1", SURFACE_DRAWING_CAPABILITY, - WORLD_TIME_CAPABILITY, - WORLD_WEATHER_CAPABILITY, "chat_v1", "mail_v1", "profile_v1", diff --git a/network/network_session.gd b/network/network_session.gd index d7ecb6d..4ccb371 100644 --- a/network/network_session.gd +++ b/network/network_session.gd @@ -168,11 +168,7 @@ func start_private_host(port: int = DEFAULT_PORT) -> bool: NetworkProtocol.PROTOCOL_VERSION, _player_identity.fingerprint, _player_identity.public_pem, - PackedStringArray([ - NetworkProtocol.SURFACE_DRAWING_CAPABILITY, - NetworkProtocol.WORLD_TIME_CAPABILITY, - NetworkProtocol.WORLD_WEATHER_CAPABILITY, - ]), + PackedStringArray([NetworkProtocol.SURFACE_DRAWING_CAPABILITY]), ) _registry.update_appearance(1, _local_appearance_snapshot) var host_profile_hello := NetworkProtocol.make_client_hello( @@ -368,8 +364,6 @@ func supports_server_capability(capability: StringName) -> bool: NetworkProtocol.ART_SHOP_CAPABILITY, "item_use_v1", "equipment_v1", "fish_showcase_v1", NetworkProtocol.SURFACE_DRAWING_CAPABILITY, - NetworkProtocol.WORLD_TIME_CAPABILITY, - NetworkProtocol.WORLD_WEATHER_CAPABILITY, "chat_v1", "mail_v1", "profile_v1", @@ -1080,11 +1074,7 @@ func receive_server_hello(data: Dictionary) -> void: NetworkProtocol.PROTOCOL_VERSION, _player_identity.fingerprint, _player_identity.public_pem, - PackedStringArray([ - NetworkProtocol.SURFACE_DRAWING_CAPABILITY, - NetworkProtocol.WORLD_TIME_CAPABILITY, - NetworkProtocol.WORLD_WEATHER_CAPABILITY, - ]), + PackedStringArray([NetworkProtocol.SURFACE_DRAWING_CAPABILITY]), ) _registry.update_appearance(local_peer_id, _local_appearance_snapshot) var local_record := _registry.get_peer(local_peer_id) diff --git a/network/network_world_time_service.gd b/network/network_world_time_service.gd deleted file mode 100644 index ad50255..0000000 --- a/network/network_world_time_service.gd +++ /dev/null @@ -1,135 +0,0 @@ -class_name NetworkWorldTimeService -extends Node - -const SYNC_INTERVAL_SECONDS: float = 5.0 -const MAX_SESSION_ID_LENGTH: int = 96 - -var _session: NetworkSession -var _world_time: WorldTimeService -var _sync_elapsed: float = 0.0 -var _sequence: int = 0 -var _last_received_sequence: int = -1 -var _active_session_id: String = "" - - -func setup(session: NetworkSession, world_time: WorldTimeService) -> void: - _session = session - _world_time = world_time - _session.state_changed.connect(_on_session_state_changed) - _session.peer_authenticated.connect(_on_peer_authenticated) - set_process(true) - - -func _process(delta: float) -> void: - if _session == null or _world_time == null or not _session.is_host(): - return - _sync_elapsed += delta - if _sync_elapsed < SYNC_INTERVAL_SECONDS: - return - _sync_elapsed = 0.0 - _broadcast_snapshot() - - -func _on_session_state_changed(state: NetworkSession.State) -> void: - if _world_time == null or _session == null: - return - if state in [NetworkSession.State.PRIVATE_HOST, NetworkSession.State.OPEN_HOST]: - if _active_session_id != _session.get_session_id(): - _active_session_id = _session.get_session_id() - _sequence = 0 - _last_received_sequence = -1 - _sync_elapsed = 0.0 - _world_time.begin_session() - return - if state == NetworkSession.State.JOINED_CLIENT: - _active_session_id = _session.get_session_id() - _last_received_sequence = -1 - _sync_elapsed = 0.0 - if _session.supports_server_capability( - NetworkProtocol.WORLD_TIME_CAPABILITY - ): - _world_time.begin_session() - else: - _world_time.end_session() - return - if state in [ - NetworkSession.State.INACTIVE, - NetworkSession.State.DISCONNECTING, - NetworkSession.State.CONNECTION_FAILED, - NetworkSession.State.SERVER_LOST, - ]: - _active_session_id = "" - _sequence = 0 - _last_received_sequence = -1 - _sync_elapsed = 0.0 - _world_time.end_session() - - -func _on_peer_authenticated(peer_id: int, _display_name: String) -> void: - if ( - _session == null - or not _session.is_host() - or not _session.peer_supports_capability( - peer_id, NetworkProtocol.WORLD_TIME_CAPABILITY - ) - ): - return - _send_snapshot(peer_id) - - -func _broadcast_snapshot() -> void: - if _session == null or not _session.is_host(): - return - for peer_id: int in _session.get_authenticated_peer_ids(): - if peer_id == _session.get_local_peer_id(): - continue - if _session.peer_supports_capability( - peer_id, NetworkProtocol.WORLD_TIME_CAPABILITY - ): - _send_snapshot(peer_id) - - -func _send_snapshot(peer_id: int) -> void: - _sequence += 1 - receive_world_time_snapshot.rpc_id(peer_id, { - "session_id": _session.get_session_id(), - "time_hours": _world_time.get_time_hours(), - "sequence": _sequence, - }) - - -@rpc("authority", "call_remote", "reliable", 0) -func receive_world_time_snapshot(data: Dictionary) -> void: - if ( - _session == null - or _world_time == null - or not _session.is_joined_client() - or not _session.supports_server_capability( - NetworkProtocol.WORLD_TIME_CAPABILITY - ) - or not validate_snapshot(data) - or str(data["session_id"]) != _session.get_session_id() - ): - return - var sequence: int = int(data["sequence"]) - if sequence <= _last_received_sequence: - return - _last_received_sequence = sequence - _world_time.synchronize_time(float(data["time_hours"])) - - -static func validate_snapshot(data: Variant) -> bool: - if typeof(data) != TYPE_DICTIONARY: - return false - var value: Dictionary = data - return ( - typeof(value.get("session_id")) == TYPE_STRING - and not str(value["session_id"]).is_empty() - and str(value["session_id"]).length() <= MAX_SESSION_ID_LENGTH - and typeof(value.get("time_hours")) in [TYPE_FLOAT, TYPE_INT] - and is_finite(float(value["time_hours"])) - and float(value["time_hours"]) >= 0.0 - and float(value["time_hours"]) < WorldTimeService.HOURS_PER_DAY - and typeof(value.get("sequence")) == TYPE_INT - and int(value["sequence"]) >= 0 - ) diff --git a/network/network_world_time_service.gd.uid b/network/network_world_time_service.gd.uid deleted file mode 100644 index a68e332..0000000 --- a/network/network_world_time_service.gd.uid +++ /dev/null @@ -1 +0,0 @@ -uid://blig76aes25j diff --git a/network/network_world_weather_service.gd b/network/network_world_weather_service.gd deleted file mode 100644 index ec0cab7..0000000 --- a/network/network_world_weather_service.gd +++ /dev/null @@ -1,156 +0,0 @@ -class_name NetworkWorldWeatherService -extends Node - -const SYNC_INTERVAL_SECONDS: float = 15.0 -const MAX_SESSION_ID_LENGTH: int = 96 -const MAX_REMAINING_SECONDS: float = 1800.0 - -var _session: NetworkSession -var _world_weather: WorldWeatherService -var _sync_elapsed: float = 0.0 -var _sequence: int = 0 -var _last_received_sequence: int = -1 -var _active_session_id: String = "" - - -func setup( - session: NetworkSession, - world_weather: WorldWeatherService, -) -> void: - _session = session - _world_weather = world_weather - _session.state_changed.connect(_on_session_state_changed) - _session.peer_authenticated.connect(_on_peer_authenticated) - _world_weather.weather_changed.connect(_on_weather_changed) - set_process(true) - - -func _process(delta: float) -> void: - if _session == null or _world_weather == null or not _session.is_host(): - return - _sync_elapsed += delta - if _sync_elapsed < SYNC_INTERVAL_SECONDS: - return - _sync_elapsed = 0.0 - _broadcast_snapshot() - - -func _on_session_state_changed(state: NetworkSession.State) -> void: - if _world_weather == null or _session == null: - return - if state in [NetworkSession.State.PRIVATE_HOST, NetworkSession.State.OPEN_HOST]: - if _active_session_id != _session.get_session_id(): - _active_session_id = _session.get_session_id() - _sequence = 0 - _last_received_sequence = -1 - _sync_elapsed = 0.0 - _world_weather.begin_authoritative_session( - _active_session_id.hash() - ) - return - if state == NetworkSession.State.JOINED_CLIENT: - _active_session_id = _session.get_session_id() - _last_received_sequence = -1 - _sync_elapsed = 0.0 - if _session.supports_server_capability( - NetworkProtocol.WORLD_WEATHER_CAPABILITY - ): - _world_weather.begin_remote_session() - else: - _world_weather.end_session() - return - if state in [ - NetworkSession.State.INACTIVE, - NetworkSession.State.DISCONNECTING, - NetworkSession.State.CONNECTION_FAILED, - NetworkSession.State.SERVER_LOST, - ]: - _active_session_id = "" - _sequence = 0 - _last_received_sequence = -1 - _sync_elapsed = 0.0 - _world_weather.end_session() - - -func _on_peer_authenticated(peer_id: int, _display_name: String) -> void: - if ( - _session == null - or not _session.is_host() - or not _session.peer_supports_capability( - peer_id, NetworkProtocol.WORLD_WEATHER_CAPABILITY - ) - ): - return - _send_snapshot(peer_id) - - -func _on_weather_changed( - _weather: WorldWeatherService.Weather, - _seconds_remaining: float, -) -> void: - if _session != null and _session.is_host(): - _broadcast_snapshot() - - -func _broadcast_snapshot() -> void: - if _session == null or not _session.is_host(): - return - for peer_id: int in _session.get_authenticated_peer_ids(): - if peer_id == _session.get_local_peer_id(): - continue - if _session.peer_supports_capability( - peer_id, NetworkProtocol.WORLD_WEATHER_CAPABILITY - ): - _send_snapshot(peer_id) - - -func _send_snapshot(peer_id: int) -> void: - _sequence += 1 - receive_world_weather_snapshot.rpc_id(peer_id, { - "session_id": _session.get_session_id(), - "weather": int(_world_weather.get_weather()), - "seconds_remaining": _world_weather.get_seconds_remaining(), - "sequence": _sequence, - }) - - -@rpc("authority", "call_remote", "reliable", 0) -func receive_world_weather_snapshot(data: Dictionary) -> void: - if ( - _session == null - or _world_weather == null - or not _session.is_joined_client() - or not _session.supports_server_capability( - NetworkProtocol.WORLD_WEATHER_CAPABILITY - ) - or not validate_snapshot(data) - or str(data["session_id"]) != _session.get_session_id() - ): - return - var sequence: int = int(data["sequence"]) - if sequence <= _last_received_sequence: - return - _last_received_sequence = sequence - _world_weather.apply_authoritative_snapshot( - int(data["weather"]) as WorldWeatherService.Weather, - float(data["seconds_remaining"]), - ) - - -static func validate_snapshot(data: Variant) -> bool: - if typeof(data) != TYPE_DICTIONARY: - return false - var value: Dictionary = data - return ( - typeof(value.get("session_id")) == TYPE_STRING - and not str(value["session_id"]).is_empty() - and str(value["session_id"]).length() <= MAX_SESSION_ID_LENGTH - and typeof(value.get("weather")) == TYPE_INT - and WorldWeatherService.is_valid_weather(int(value["weather"])) - and typeof(value.get("seconds_remaining")) in [TYPE_FLOAT, TYPE_INT] - and is_finite(float(value["seconds_remaining"])) - and float(value["seconds_remaining"]) >= 0.0 - and float(value["seconds_remaining"]) <= MAX_REMAINING_SECONDS - and typeof(value.get("sequence")) == TYPE_INT - and int(value["sequence"]) >= 0 - ) diff --git a/network/network_world_weather_service.gd.uid b/network/network_world_weather_service.gd.uid deleted file mode 100644 index ffb9a1e..0000000 --- a/network/network_world_weather_service.gd.uid +++ /dev/null @@ -1 +0,0 @@ -uid://llj46fx0oco7 diff --git a/tests/fish_catalog_content_validation.gd b/tests/fish_catalog_content_validation.gd index 2a74663..5972da2 100644 --- a/tests/fish_catalog_content_validation.gd +++ b/tests/fish_catalog_content_validation.gd @@ -107,17 +107,12 @@ func _validate_catalog_and_pools() -> void: var pond_context := FishingContextType.new() pond_context.location_tags = [&"starter_pond"] pond_context.water_type = WaterType.Type.FRESH_WATER - var night_pond_context := FishingContextType.new() - night_pond_context.location_tags = [&"starter_pond"] - night_pond_context.water_type = WaterType.Type.FRESH_WATER - night_pond_context.is_night = true var ocean_context := FishingContextType.new() ocean_context.location_tags = [&"coast", &"ocean"] ocean_context.water_type = WaterType.Type.SALT_WATER for fish_id: StringName in CATFISH_IDS: var fish: FishDataType = Catalog.get_fish_by_id(fish_id) - assert(not fish.availability.is_available(pond_context)) - assert(fish.availability.is_available(night_pond_context)) + assert(fish.availability.is_available(pond_context)) assert(not fish.availability.is_available(ocean_context)) var single_species_pool := FishPoolType.new() single_species_pool.candidates = [fish] @@ -127,7 +122,7 @@ func _validate_catalog_and_pools() -> void: selector.begin_roll() assert( selector.select_fish( - single_species_pool, night_pond_context, collection + single_species_pool, pond_context, collection ) == fish ) collection.free() diff --git a/tests/logbook_runtime_validation.gd b/tests/logbook_runtime_validation.gd index b201b9a..d3bc3c9 100644 --- a/tests/logbook_runtime_validation.gd +++ b/tests/logbook_runtime_validation.gd @@ -11,11 +11,7 @@ func _initialize() -> void: func _run() -> void: var main := MainScene.instantiate() root.add_child(main) - for _frame: int in 4: - await process_frame - if not bool(main.get("_application_initialized")): - main.call("_activate_selected_data_path", "", true) - for _frame: int in 8: + for _frame: int in 6: await process_frame assert(bool(main.get("_application_initialized"))) var save_manager := main.get("_save_manager") as PlayerSaveManager @@ -49,7 +45,7 @@ func _run() -> void: assert(not hotbar.visible) var entry_buttons: Dictionary = logbook.get("_entry_buttons") - assert(entry_buttons.size() == 6) + assert(entry_buttons.size() == 0) await _capture_if_requested("-unknown") logbook.call( "_select_category", WaterType.Type.FRESH_WATER diff --git a/tests/world_time_multiplayer_validation.gd b/tests/world_time_multiplayer_validation.gd deleted file mode 100644 index 6f1c455..0000000 --- a/tests/world_time_multiplayer_validation.gd +++ /dev/null @@ -1,186 +0,0 @@ -extends SceneTree - -const MainScene = preload("res://main/main.tscn") -const TEST_PORT: int = 17983 -const INITIAL_HOST_TIME: float = 19.75 -const UPDATED_HOST_TIME: float = 20.75 -const TIME_TOLERANCE_HOURS: float = 0.05 - - -func _initialize() -> void: - call_deferred("_run") - - -func _run() -> void: - var arguments: PackedStringArray = OS.get_cmdline_user_args() - if arguments.has("host"): - await _run_host() - return - if arguments.has("client"): - await _run_client() - return - push_error("World time multiplayer validation needs host or client mode.") - quit(1) - - -func _run_host() -> void: - var main: Node = await _create_initialized_main() - var session := main.get_node("%NetworkSession") as NetworkSession - var world_time := main.get_node("%WorldTimeService") as WorldTimeService - var world_weather := ( - main.get_node("%WorldWeatherService") as WorldWeatherService - ) - assert(session.start_private_host(TEST_PORT)) - world_time.synchronize_time(INITIAL_HOST_TIME) - world_weather.apply_authoritative_snapshot( - WorldWeatherService.Weather.RAINY, 300.0 - ) - assert(session.set_host_open(true)) - - var remote_peer_id: int = 0 - var join_deadline: int = Time.get_ticks_msec() + 20000 - while Time.get_ticks_msec() < join_deadline and remote_peer_id == 0: - await process_frame - for peer_id: int in session.get_authenticated_peer_ids(): - if peer_id != session.get_local_peer_id(): - remote_peer_id = peer_id - break - assert(remote_peer_id > 1) - assert(session.peer_supports_capability( - remote_peer_id, NetworkProtocol.WORLD_TIME_CAPABILITY - )) - assert(session.peer_supports_capability( - remote_peer_id, NetworkProtocol.WORLD_WEATHER_CAPABILITY - )) - assert(world_time.get_phase() == WorldTimeService.Phase.DUSK) - - await create_timer(1.0).timeout - world_time.synchronize_time(UPDATED_HOST_TIME) - world_weather.apply_authoritative_snapshot( - WorldWeatherService.Weather.FOGGY, 300.0 - ) - var disconnect_deadline: int = Time.get_ticks_msec() + 12000 - while ( - Time.get_ticks_msec() < disconnect_deadline - and session.is_authenticated_peer(remote_peer_id) - ): - await process_frame - assert(not session.is_authenticated_peer(remote_peer_id)) - print("World time multiplayer host validation: PASS") - session.disconnect_session("") - main.queue_free() - await process_frame - quit() - - -func _run_client() -> void: - var main: Node = await _create_initialized_main() - main.call( - "_on_title_join_game_requested", - "127.0.0.1:%d" % TEST_PORT, - ) - var session := main.get_node("%NetworkSession") as NetworkSession - var world_time := main.get_node("%WorldTimeService") as WorldTimeService - var world_weather := ( - main.get_node("%WorldWeatherService") as WorldWeatherService - ) - var join_deadline: int = Time.get_ticks_msec() + 20000 - while Time.get_ticks_msec() < join_deadline: - await process_frame - if session.state == NetworkSession.State.VERIFYING_SERVER_IDENTITY: - main.call("_confirm_server_trust") - if session.is_joined_client() and bool(main.get("_gameplay_started")): - break - assert(session.is_joined_client()) - assert(session.supports_server_capability( - NetworkProtocol.WORLD_TIME_CAPABILITY - )) - assert(session.supports_server_capability( - NetworkProtocol.WORLD_WEATHER_CAPABILITY - )) - - var initial_deadline: int = Time.get_ticks_msec() + 8000 - while ( - Time.get_ticks_msec() < initial_deadline - and _wrapped_time_difference( - world_time.get_time_hours(), INITIAL_HOST_TIME - ) > TIME_TOLERANCE_HOURS - ): - await process_frame - assert(_wrapped_time_difference( - world_time.get_time_hours(), INITIAL_HOST_TIME - ) <= TIME_TOLERANCE_HOURS) - assert(world_time.get_phase() == WorldTimeService.Phase.DUSK) - var weather_deadline: int = Time.get_ticks_msec() + 8000 - while ( - Time.get_ticks_msec() < weather_deadline - and not world_weather.is_raining() - ): - await process_frame - assert(world_weather.is_raining()) - var game_ui := main.get_node("%GameUI") as CanvasLayer - var chat_ui := game_ui.get_node("%ChatUI") as Control - var clock_panel := chat_ui.get_node("WorldClockPanel") as PanelContainer - var clock_label := clock_panel.get_node("WorldClockLabel") as Label - var weather_icon := chat_ui.get_node("WorldWeatherIcon") as WeatherIcon - var chat_panel := chat_ui.get_node("ChatPanel") as PanelContainer - assert(clock_panel.visible) - assert(clock_label.text == world_time.get_clock_text()) - assert(clock_label.text.ends_with(" pm")) - assert(weather_icon.visible) - assert(weather_icon.get_weather() == WorldWeatherService.Weather.RAINY) - assert(clock_panel.position.y + clock_panel.size.y < chat_panel.position.y) - - var update_deadline: int = Time.get_ticks_msec() + 10000 - while ( - Time.get_ticks_msec() < update_deadline - and _wrapped_time_difference( - world_time.get_time_hours(), UPDATED_HOST_TIME - ) > TIME_TOLERANCE_HOURS - ): - await process_frame - assert(_wrapped_time_difference( - world_time.get_time_hours(), UPDATED_HOST_TIME - ) <= TIME_TOLERANCE_HOURS) - assert(world_time.get_phase() == WorldTimeService.Phase.NIGHT) - assert(clock_label.text == world_time.get_clock_text()) - var fog_deadline: int = Time.get_ticks_msec() + 8000 - while ( - Time.get_ticks_msec() < fog_deadline - and not world_weather.is_foggy() - ): - await process_frame - assert(world_weather.is_foggy()) - assert(weather_icon.get_weather() == WorldWeatherService.Weather.FOGGY) - chat_ui.call("set_dock_right", true) - await process_frame - assert(clock_panel.position.x > 1000.0) - assert(weather_icon.position.x < clock_panel.position.x) - chat_ui.call("set_dock_right", false) - await process_frame - assert(clock_panel.position.x < 20.0) - assert(weather_icon.position.x > clock_panel.position.x) - print("World time multiplayer client validation: PASS") - session.disconnect_session("") - main.queue_free() - await process_frame - quit() - - -func _create_initialized_main() -> Node: - root.size = Vector2i(1280, 720) - var main := MainScene.instantiate() - root.add_child(main) - for _frame: int in 4: - await process_frame - if not bool(main.get("_application_initialized")): - main.call("_activate_selected_data_path", "", true) - for _frame: int in 8: - await process_frame - assert(bool(main.get("_application_initialized"))) - return main - - -func _wrapped_time_difference(left: float, right: float) -> float: - var direct: float = absf(left - right) - return minf(direct, WorldTimeService.HOURS_PER_DAY - direct) diff --git a/tests/world_time_multiplayer_validation.gd.uid b/tests/world_time_multiplayer_validation.gd.uid deleted file mode 100644 index 3e17cd7..0000000 --- a/tests/world_time_multiplayer_validation.gd.uid +++ /dev/null @@ -1 +0,0 @@ -uid://6fal0btp6sgr diff --git a/tests/world_time_validation.gd b/tests/world_time_validation.gd deleted file mode 100644 index 666831e..0000000 --- a/tests/world_time_validation.gd +++ /dev/null @@ -1,238 +0,0 @@ -extends SceneTree - -const WorldTimeServiceType = preload("res://world/world_time_service.gd") -const WorldTimeVisualControllerType = preload( - "res://world/world_time_visual_controller.gd" -) -const NetworkWorldTimeServiceType = preload( - "res://network/network_world_time_service.gd" -) -const FishingContextType = preload("res://fishing/fishing_context.gd") -const FishDataType = preload("res://fish/fish_data.gd") -const FishPoolType = preload("res://fish/fish_pool.gd") -const FishingSpotType = preload("res://fishing/fishing_spot.gd") -const FishableWaterRegionType = preload( - "res://world/fishable_water_region.gd" -) -const WeatherIconType = preload("res://ui/weather_icon.gd") - -const Catalog: FishPoolType = preload("res://fish/pools/fish_catalog.tres") - - -func _initialize() -> void: - call_deferred("_run") - - -func _run() -> void: - _validate_clock_boundaries_and_duration() - _validate_fishing_availability() - _validate_fishing_spot_context() - _validate_network_snapshot_bounds() - _validate_environment_presentation() - _validate_weather_clock_icon() - print("World time validation: PASS") - quit() - - -func _validate_clock_boundaries_and_duration() -> void: - assert(WorldTimeServiceType.REAL_SECONDS_PER_CYCLE == 3600.0) - assert( - WorldTimeServiceType.phase_for_hour(7.49) - == WorldTimeServiceType.Phase.NIGHT - ) - assert( - WorldTimeServiceType.phase_for_hour(7.5) - == WorldTimeServiceType.Phase.DAWN - ) - assert( - WorldTimeServiceType.phase_for_hour(8.49) - == WorldTimeServiceType.Phase.DAWN - ) - assert( - WorldTimeServiceType.phase_for_hour(8.5) - == WorldTimeServiceType.Phase.DAY - ) - assert( - WorldTimeServiceType.phase_for_hour(19.5) - == WorldTimeServiceType.Phase.DUSK - ) - assert( - WorldTimeServiceType.phase_for_hour(20.5) - == WorldTimeServiceType.Phase.NIGHT - ) - assert(WorldTimeServiceType.format_clock_time(0.0) == "12:00 am") - assert(WorldTimeServiceType.format_clock_time(8.0) == "8:00 am") - assert(WorldTimeServiceType.format_clock_time(20.5) == "8:30 pm") - - var clock := WorldTimeServiceType.new() - root.add_child(clock) - clock.begin_session(8.0) - clock.advance_time(1800.0) - assert(is_equal_approx(clock.get_time_hours(), 20.0)) - assert(clock.is_night_period()) - assert(clock.is_transition()) - clock.advance_time(1800.0) - assert(is_equal_approx(clock.get_time_hours(), 8.0)) - assert(not clock.is_night_period()) - assert(clock.is_transition()) - clock.queue_free() - - -func _validate_fishing_availability() -> void: - var day_context := FishingContextType.new() - day_context.location_tags = [&"starter_pond"] - day_context.is_night = false - var night_context := FishingContextType.new() - night_context.location_tags = [&"starter_pond"] - night_context.is_night = true - var transition_context := FishingContextType.new() - transition_context.location_tags = [&"starter_pond"] - transition_context.is_night = true - transition_context.is_day_night_transition = true - - for fish_id: StringName in [&"bluegill", &"sunfish"]: - var day_fish: FishDataType = Catalog.get_fish_by_id(fish_id) - assert(day_fish.availability.is_available(day_context)) - assert(not day_fish.availability.is_available(night_context)) - assert(day_fish.availability.is_available(transition_context)) - for fish_id: StringName in [ - &"catfish_blue", - &"catfish_channel", - &"catfish_flathead", - &"catfish_white", - ]: - var night_fish: FishDataType = Catalog.get_fish_by_id(fish_id) - assert(not night_fish.availability.is_available(day_context)) - assert(night_fish.availability.is_available(night_context)) - assert(night_fish.availability.is_available(transition_context)) - for fish_id: StringName in [&"bass", &"carp"]: - var all_time_fish: FishDataType = Catalog.get_fish_by_id(fish_id) - assert(all_time_fish.availability.is_available(day_context)) - assert(all_time_fish.availability.is_available(night_context)) - assert(all_time_fish.availability.is_available(transition_context)) - - -func _validate_fishing_spot_context() -> void: - var clock := WorldTimeServiceType.new() - clock.begin_session(20.25) - var fishing_spot := FishingSpotType.new() - fishing_spot.set("_world_time", clock) - var region := FishableWaterRegionType.new() - region.location_tags = [&"starter_pond"] - region.water_type = WaterType.Type.FRESH_WATER - var dusk_context: FishingContext = fishing_spot.build_network_context(region) - assert(dusk_context.is_night) - assert(dusk_context.is_day_night_transition) - clock.synchronize_time(14.0) - var day_context: FishingContext = fishing_spot.build_network_context(region) - assert(not day_context.is_night) - assert(not day_context.is_day_night_transition) - region.free() - fishing_spot.free() - clock.free() - - -func _validate_network_snapshot_bounds() -> void: - assert(NetworkWorldTimeServiceType.validate_snapshot({ - "session_id": "session", - "time_hours": 8.25, - "sequence": 1, - })) - assert(not NetworkWorldTimeServiceType.validate_snapshot({ - "session_id": "session", - "time_hours": 24.0, - "sequence": 1, - })) - assert(not NetworkWorldTimeServiceType.validate_snapshot({ - "session_id": "", - "time_hours": 8.0, - "sequence": 1, - })) - - -func _validate_environment_presentation() -> void: - var world_root := Node3D.new() - root.add_child(world_root) - var world_environment := WorldEnvironment.new() - var environment := Environment.new() - var sky := Sky.new() - var sky_material := ShaderMaterial.new() - sky_material.shader = preload( - "res://world/environment/netfishing_sky.gdshader" - ) - sky.sky_material = sky_material - environment.sky = sky - environment.adjustment_enabled = true - environment.fog_enabled = true - world_environment.environment = environment - world_root.add_child(world_environment) - var sun := DirectionalLight3D.new() - world_root.add_child(sun) - var clock := WorldTimeServiceType.new() - world_root.add_child(clock) - var visuals := WorldTimeVisualControllerType.new() - world_root.add_child(visuals) - visuals.setup(clock, world_environment, sun) - visuals.apply_time_immediately(12.0) - var runtime_environment: Environment = world_environment.environment - var runtime_sky_material := ( - runtime_environment.sky.sky_material as ShaderMaterial - ) - var day_horizon_value: Variant = runtime_sky_material.get_shader_parameter( - "sky_horizon_color" - ) - assert(day_horizon_value is Color) - var day_horizon: Color = day_horizon_value as Color - var day_sun_visibility: float = float( - runtime_sky_material.get_shader_parameter("sun_visibility") - ) - var day_sun_direction: Vector3 = ( - runtime_sky_material.get_shader_parameter("sun_direction") as Vector3 - ) - assert(day_sun_visibility > 0.99) - assert(day_sun_direction.y > 0.85) - assert( - float(runtime_sky_material.get_shader_parameter("moon_visibility")) - < 0.01 - ) - var day_ambient_energy: float = runtime_environment.ambient_light_energy - visuals.apply_time_immediately(0.0) - var night_horizon: Color = runtime_sky_material.get_shader_parameter( - "sky_horizon_color" - ) as Color - assert(night_horizon != day_horizon) - assert( - float(runtime_sky_material.get_shader_parameter("sun_visibility")) - < 0.01 - ) - var night_moon_visibility: float = float( - runtime_sky_material.get_shader_parameter("moon_visibility") - ) - var night_moon_direction: Vector3 = ( - runtime_sky_material.get_shader_parameter("moon_direction") as Vector3 - ) - var night_sun_direction: Vector3 = ( - runtime_sky_material.get_shader_parameter("sun_direction") as Vector3 - ) - assert(night_moon_visibility > 0.99) - assert(night_moon_direction.y > 0.85) - assert(night_moon_direction.is_equal_approx(-night_sun_direction)) - assert(runtime_environment.ambient_light_energy < day_ambient_energy) - visuals.apply_time_immediately(20.0) - var dusk_horizon: Color = runtime_sky_material.get_shader_parameter( - "sky_horizon_color" - ) as Color - assert(dusk_horizon.r > day_horizon.r) - world_root.queue_free() - - -func _validate_weather_clock_icon() -> void: - var weather_icon := WeatherIconType.new() - root.add_child(weather_icon) - assert(weather_icon.tooltip_text == "clear") - assert(not weather_icon.is_nighttime()) - weather_icon.set_nighttime(true) - assert(weather_icon.is_nighttime()) - weather_icon.set_weather(WorldWeatherService.Weather.CLOUDY) - assert(weather_icon.tooltip_text == "cloudy") - weather_icon.queue_free() diff --git a/tests/world_time_validation.gd.uid b/tests/world_time_validation.gd.uid deleted file mode 100644 index b84e544..0000000 --- a/tests/world_time_validation.gd.uid +++ /dev/null @@ -1 +0,0 @@ -uid://buiitafgrgm4b diff --git a/tests/world_weather_validation.gd b/tests/world_weather_validation.gd deleted file mode 100644 index 1238fc4..0000000 --- a/tests/world_weather_validation.gd +++ /dev/null @@ -1,194 +0,0 @@ -extends SceneTree - -const WorldWeatherServiceType = preload( - "res://world/world_weather_service.gd" -) -const NetworkWorldWeatherServiceType = preload( - "res://network/network_world_weather_service.gd" -) -const WorldTimeServiceType = preload("res://world/world_time_service.gd") -const WorldTimeVisualControllerType = preload( - "res://world/world_time_visual_controller.gd" -) -const FishingContextType = preload("res://fishing/fishing_context.gd") -const FishingSpotType = preload("res://fishing/fishing_spot.gd") -const FishAvailabilityType = preload("res://fish/fish_availability.gd") -const FishableWaterRegionType = preload( - "res://world/fishable_water_region.gd" -) - - -func _initialize() -> void: - call_deferred("_run") - - -func _run() -> void: - _validate_weather_scheduler() - _validate_snapshot_bounds() - _validate_fishing_weather_seams() - _validate_fishing_spot_context() - _validate_weather_presentation() - print("World weather validation: PASS") - quit() - - -func _validate_weather_scheduler() -> void: - var weather := WorldWeatherServiceType.new() - root.add_child(weather) - weather.begin_authoritative_session(20260802) - assert(weather.get_weather() == WorldWeatherServiceType.Weather.SUNNY) - assert(_duration_is_valid(weather)) - weather.advance_weather(weather.get_seconds_remaining() + 0.01) - assert(weather.get_weather() == WorldWeatherServiceType.Weather.CLOUDY) - assert(_duration_is_valid(weather)) - weather.advance_weather(weather.get_seconds_remaining() + 0.01) - assert(weather.get_weather() in [ - WorldWeatherServiceType.Weather.SUNNY, - WorldWeatherServiceType.Weather.RAINY, - WorldWeatherServiceType.Weather.FOGGY, - ]) - assert(_duration_is_valid(weather)) - weather.apply_authoritative_snapshot( - WorldWeatherServiceType.Weather.RAINY, 120.0 - ) - assert(weather.is_raining()) - assert(not weather.is_foggy()) - weather.apply_authoritative_snapshot( - WorldWeatherServiceType.Weather.FOGGY, 120.0 - ) - assert(weather.is_foggy()) - assert(not weather.is_raining()) - weather.queue_free() - - -func _duration_is_valid(weather: WorldWeatherServiceType) -> bool: - var seconds: float = weather.get_seconds_remaining() - match weather.get_weather(): - WorldWeatherServiceType.Weather.SUNNY: - return seconds >= 480.0 and seconds <= 900.0 - WorldWeatherServiceType.Weather.CLOUDY: - return seconds >= 300.0 and seconds <= 720.0 - WorldWeatherServiceType.Weather.RAINY, WorldWeatherServiceType.Weather.FOGGY: - return seconds >= 300.0 and seconds <= 600.0 - return false - - -func _validate_snapshot_bounds() -> void: - assert(NetworkWorldWeatherServiceType.validate_snapshot({ - "session_id": "session", - "weather": int(WorldWeatherServiceType.Weather.FOGGY), - "seconds_remaining": 300.0, - "sequence": 2, - })) - assert(not NetworkWorldWeatherServiceType.validate_snapshot({ - "session_id": "session", - "weather": 99, - "seconds_remaining": 300.0, - "sequence": 2, - })) - assert(not NetworkWorldWeatherServiceType.validate_snapshot({ - "session_id": "session", - "weather": int(WorldWeatherServiceType.Weather.RAINY), - "seconds_remaining": 1801.0, - "sequence": 2, - })) - - -func _validate_fishing_weather_seams() -> void: - var clear_context := FishingContextType.new() - var rain_context := FishingContextType.new() - rain_context.is_raining = true - var fog_context := FishingContextType.new() - fog_context.is_foggy = true - fog_context.is_night = true - var rain_only := FishAvailabilityType.new() - rain_only.require_rain = true - assert(not rain_only.is_available(clear_context)) - assert(rain_only.is_available(rain_context)) - var fog_only := FishAvailabilityType.new() - fog_only.allow_day = false - fog_only.require_fog = true - assert(not fog_only.is_available(clear_context)) - assert(fog_only.is_available(fog_context)) - var no_fog := FishAvailabilityType.new() - no_fog.forbid_fog = true - assert(no_fog.is_available(clear_context)) - assert(not no_fog.is_available(fog_context)) - - -func _validate_fishing_spot_context() -> void: - var weather := WorldWeatherServiceType.new() - weather.begin_remote_session() - weather.apply_authoritative_snapshot( - WorldWeatherServiceType.Weather.FOGGY, 200.0 - ) - var fishing_spot := FishingSpotType.new() - fishing_spot.set("_world_weather", weather) - var region := FishableWaterRegionType.new() - region.location_tags = [&"starter_pond"] - var context: FishingContext = fishing_spot.build_network_context(region) - assert(context.is_foggy) - assert(not context.is_raining) - weather.apply_authoritative_snapshot( - WorldWeatherServiceType.Weather.RAINY, 200.0 - ) - context = fishing_spot.build_network_context(region) - assert(context.is_raining) - assert(not context.is_foggy) - region.free() - fishing_spot.free() - weather.free() - - -func _validate_weather_presentation() -> void: - var world_root := Node3D.new() - root.add_child(world_root) - var world_environment := WorldEnvironment.new() - var environment := Environment.new() - var sky := Sky.new() - var sky_material := ShaderMaterial.new() - sky_material.shader = preload( - "res://world/environment/netfishing_sky.gdshader" - ) - sky.sky_material = sky_material - environment.sky = sky - environment.adjustment_enabled = true - environment.fog_enabled = true - world_environment.environment = environment - world_root.add_child(world_environment) - var sun := DirectionalLight3D.new() - world_root.add_child(sun) - var clock := WorldTimeServiceType.new() - world_root.add_child(clock) - clock.begin_session(14.0) - var weather := WorldWeatherServiceType.new() - world_root.add_child(weather) - weather.begin_remote_session() - var rain_target := Node3D.new() - world_root.add_child(rain_target) - var visuals := WorldTimeVisualControllerType.new() - world_root.add_child(visuals) - visuals.setup( - clock, world_environment, sun, weather, rain_target - ) - visuals.apply_weather_immediately( - WorldWeatherServiceType.Weather.FOGGY - ) - var runtime_environment: Environment = world_environment.environment - assert(runtime_environment.fog_depth_begin <= 4.01) - assert(runtime_environment.fog_depth_end <= 42.01) - assert(runtime_environment.fog_sky_affect >= 0.93) - assert(runtime_environment.fog_aerial_perspective >= 0.91) - assert(runtime_environment.adjustment_saturation < 0.70) - visuals.apply_weather_immediately( - WorldWeatherServiceType.Weather.RAINY - ) - var rain := visuals.get_node("LocalRain") as GPUParticles3D - assert(rain != null) - assert(rain.emitting) - assert(rain.amount_ratio > 0.99) - visuals.apply_weather_immediately( - WorldWeatherServiceType.Weather.SUNNY - ) - assert(not rain.emitting) - world_root.queue_free() diff --git a/tests/world_weather_validation.gd.uid b/tests/world_weather_validation.gd.uid deleted file mode 100644 index 8694186..0000000 --- a/tests/world_weather_validation.gd.uid +++ /dev/null @@ -1 +0,0 @@ -uid://cdbmbag58okjb diff --git a/ui/chat_ui.gd b/ui/chat_ui.gd index fce081a..71001f3 100644 --- a/ui/chat_ui.gd +++ b/ui/chat_ui.gd @@ -20,16 +20,6 @@ const HANDLE_SIZE := Vector2(34, 42) const HANDLE_GAP: float = 6.0 const COLLAPSED_REVEAL_WIDTH: float = 8.0 const HINT_EDGE_MARGIN: float = 4.0 -const CLOCK_SIZE := Vector2(116.0, 34.0) -const CLOCK_EDGE_MARGIN: float = 10.0 -const CLOCK_PANEL_GAP: float = 8.0 -const WEATHER_ICON_SIZE := Vector2(34.0, 34.0) -const WEATHER_ICON_GAP: float = 6.0 -const WorldTimeServiceType = preload("res://world/world_time_service.gd") -const WorldWeatherServiceType = preload( - "res://world/world_weather_service.gd" -) -const WeatherIconType = preload("res://ui/weather_icon.gd") enum PresentationState { COLLAPSED, @@ -54,9 +44,6 @@ var _height_button: Button var _unread_indicator: Label var _hint: Label var _speech_layer: Control -var _clock_panel: PanelContainer -var _clock_label: Label -var _weather_icon: WeatherIconType var _speech: Dictionary[int, Dictionary] = {} var _draft_save_timer: Timer var _opacity_tween: Tween @@ -75,8 +62,6 @@ var _prior_movement: bool = true var _prior_camera: bool = true var _output_scale: float = 1.0 var _dock_right: bool = false -var _world_time: WorldTimeServiceType -var _world_weather: WorldWeatherServiceType func _ready() -> void: @@ -94,8 +79,6 @@ func setup( player: Player, fishing_spot: FishingSpot, settings: PlayerSettingsManager, - world_time: WorldTimeServiceType, - world_weather: WorldWeatherServiceType, ) -> void: _service = service _session = session @@ -103,29 +86,6 @@ func setup( _player = player _fishing_spot = fishing_spot _settings = settings - _world_time = world_time - _world_weather = world_weather - if ( - _world_time != null - and not _world_time.time_changed.is_connected( - _on_world_time_changed - ) - ): - _world_time.time_changed.connect(_on_world_time_changed) - _on_world_time_changed( - _world_time.get_time_hours(), _world_time.get_phase() - ) - if ( - _world_weather != null - and not _world_weather.weather_changed.is_connected( - _on_world_weather_changed - ) - ): - _world_weather.weather_changed.connect(_on_world_weather_changed) - _on_world_weather_changed( - _world_weather.get_weather(), - _world_weather.get_seconds_remaining(), - ) set_dock_right(_settings.current_settings.chat_dock_right) _service.message_received.connect(_on_message) _service.local_message_confirmed.connect(_on_local_message_confirmed) @@ -224,31 +184,6 @@ func _exit_tree() -> void: func _build_ui() -> void: - _clock_panel = PanelContainer.new() - _clock_panel.name = "WorldClockPanel" - _clock_panel.size = CLOCK_SIZE - _clock_panel.mouse_filter = Control.MOUSE_FILTER_IGNORE - _clock_panel.add_theme_stylebox_override( - "panel", _clock_panel_style() - ) - add_child(_clock_panel) - _clock_label = Label.new() - _clock_label.name = "WorldClockLabel" - _clock_label.text = "8:00 am" - _clock_label.horizontal_alignment = HORIZONTAL_ALIGNMENT_CENTER - _clock_label.vertical_alignment = VERTICAL_ALIGNMENT_CENTER - _clock_label.mouse_filter = Control.MOUSE_FILTER_IGNORE - _clock_label.add_theme_font_override("font", UtilityPageStyle.TuffyFont) - _clock_label.add_theme_font_size_override("font_size", 18) - _clock_label.add_theme_color_override( - "font_color", UtilityPageStyle.OCEAN_TEXT_PRIMARY - ) - _clock_panel.add_child(_clock_label) - _weather_icon = WeatherIconType.new() - _weather_icon.name = "WorldWeatherIcon" - _weather_icon.size = WEATHER_ICON_SIZE - _weather_icon.mouse_filter = Control.MOUSE_FILTER_STOP - add_child(_weather_icon) _panel = PanelContainer.new() _panel.name = "ChatPanel" _panel.mouse_filter = Control.MOUSE_FILTER_STOP @@ -391,8 +326,6 @@ func _build_ui() -> void: _draft_save_timer.timeout.connect(_flush_draft) add_child(_draft_save_timer) _panel.hide() - _clock_panel.hide() - _weather_icon.hide() _collapse_button.hide() _height_button.hide() _unread_indicator.hide() @@ -413,16 +346,6 @@ func _chat_panel_style() -> StyleBoxFlat: return style -func _clock_panel_style() -> StyleBoxFlat: - var style := _borderless_style(Color(0.025, 0.13, 0.19, 0.94)) - style.set_corner_radius_all(12) - style.content_margin_left = 10 - style.content_margin_right = 10 - style.content_margin_top = 4 - style.content_margin_bottom = 4 - return style - - func _borderless_style(color: Color) -> StyleBoxFlat: var style := StyleBoxFlat.new() style.bg_color = color @@ -602,15 +525,11 @@ func _refresh_history() -> void: func _refresh_visibility() -> void: if not _available: _panel.hide() - _clock_panel.hide() - _weather_icon.hide() _collapse_button.hide() _height_button.hide() _unread_indicator.hide() _hint.hide() return - _clock_panel.show() - _weather_icon.show() _collapse_button.show() var collapsed := _presentation_state == PresentationState.COLLAPSED _panel.show() @@ -818,25 +737,6 @@ func _layout_presentation(animate: bool) -> void: panel_x = -(PANEL_WIDTH - COLLAPSED_REVEAL_WIDTH) if collapsed else 0.0 var target_position := Vector2(panel_x, bottom - height) var target_size := Vector2(PANEL_WIDTH, height) - var clock_x: float = ( - viewport_width - CLOCK_SIZE.x - CLOCK_EDGE_MARGIN - if _dock_right else CLOCK_EDGE_MARGIN - ) - var clock_position := Vector2( - clock_x, - maxf( - CLOCK_EDGE_MARGIN, - target_position.y - CLOCK_SIZE.y - CLOCK_PANEL_GAP, - ), - ) - var weather_icon_x: float = ( - clock_position.x - WEATHER_ICON_GAP - WEATHER_ICON_SIZE.x - if _dock_right - else clock_position.x + CLOCK_SIZE.x + WEATHER_ICON_GAP - ) - var weather_icon_position := Vector2( - weather_icon_x, clock_position.y - ) var handle_stack_height := HANDLE_SIZE.y * 2.0 + HANDLE_GAP var handle_stack_top := ( bottom - COMPACT_HEIGHT * 0.5 - handle_stack_height * 0.5 @@ -866,10 +766,6 @@ func _layout_presentation(animate: bool) -> void: if not animate: _panel.position = target_position _panel.size = target_size - _clock_panel.position = clock_position - _clock_panel.size = CLOCK_SIZE - _weather_icon.position = weather_icon_position - _weather_icon.size = WEATHER_ICON_SIZE _collapse_button.position = collapse_position _height_button.position = height_position _unread_indicator.position = unread_position @@ -883,18 +779,6 @@ func _layout_presentation(animate: bool) -> void: _height_tween.tween_property( _panel, "size", target_size, UIMotion.CHAT_RESIZE_DURATION ).set_trans(Tween.TRANS_QUAD).set_ease(Tween.EASE_OUT) - _height_tween.tween_property( - _clock_panel, - "position", - clock_position, - UIMotion.CHAT_RESIZE_DURATION, - ).set_trans(Tween.TRANS_QUAD).set_ease(Tween.EASE_OUT) - _height_tween.tween_property( - _weather_icon, - "position", - weather_icon_position, - UIMotion.CHAT_RESIZE_DURATION, - ).set_trans(Tween.TRANS_QUAD).set_ease(Tween.EASE_OUT) _height_tween.tween_property( _collapse_button, "position", @@ -952,24 +836,6 @@ func _on_viewport_resized() -> void: _layout_presentation(false) -func _on_world_time_changed( - _time_hours: float, - _phase: WorldTimeService.Phase, -) -> void: - if _clock_label != null and _world_time != null: - _clock_label.text = _world_time.get_clock_text() - if _weather_icon != null and _world_time != null: - _weather_icon.set_nighttime(_world_time.is_night_period()) - - -func _on_world_weather_changed( - weather: WorldWeatherService.Weather, - _seconds_remaining: float, -) -> void: - if _weather_icon != null: - _weather_icon.set_weather(weather) - - func _on_exterior_handle_entered() -> void: _panel_hovered = true _update_panel_opacity(true) diff --git a/ui/game_ui.gd b/ui/game_ui.gd index f7e2d09..d1796d6 100644 --- a/ui/game_ui.gd +++ b/ui/game_ui.gd @@ -39,10 +39,6 @@ const SurfaceDrawingToolbarType = preload( const PlayerSettingsManagerType = preload( "res://settings/player_settings_manager.gd" ) -const WorldTimeServiceType = preload("res://world/world_time_service.gd") -const WorldWeatherServiceType = preload( - "res://world/world_weather_service.gd" -) signal pixelation_settings_visibility_changed(is_visible: bool) signal crisp_reset_focus_requested @@ -149,15 +145,13 @@ func setup( settings_manager: PlayerSettingsManagerType, surface_drawing: NetworkSurfaceDrawingService, art_unlocks: PlayerArtUnlocks, - world_time: WorldTimeServiceType, - world_weather: WorldWeatherServiceType, ) -> void: _player = player _fishing_spot = fishing_spot _item_effects = item_effects _chat_ui.setup( network_chat_service, network_session, spawn_service, player, - fishing_spot, settings_manager, world_time, world_weather + fishing_spot, settings_manager ) _title_settings_panel.setup_network_profile( network_profile, network_session diff --git a/ui/weather_icon.gd b/ui/weather_icon.gd deleted file mode 100644 index 894e140..0000000 --- a/ui/weather_icon.gd +++ /dev/null @@ -1,110 +0,0 @@ -class_name WeatherIcon -extends Control - -const BUBBLE_COLOR := Color(0.025, 0.13, 0.19, 0.94) -const ICON_COLOR := Color(0.78, 0.91, 0.95) -const SUN_COLOR := Color(0.98, 0.82, 0.34) -const MOON_COLOR := Color(0.78, 0.88, 1.0) -const RAIN_COLOR := Color(0.28, 0.73, 0.82) - -var _weather: WorldWeatherService.Weather = WorldWeatherService.Weather.SUNNY -var _is_nighttime: bool = false - - -func _ready() -> void: - mouse_filter = Control.MOUSE_FILTER_PASS - focus_mode = Control.FOCUS_NONE - custom_minimum_size = Vector2(34.0, 34.0) - _update_tooltip() - queue_redraw() - - -func set_weather(weather: WorldWeatherService.Weather) -> void: - if _weather == weather: - _update_tooltip() - return - _weather = weather - _update_tooltip() - queue_redraw() - - -func set_nighttime(is_nighttime: bool) -> void: - if _is_nighttime == is_nighttime: - return - _is_nighttime = is_nighttime - queue_redraw() - - -func is_nighttime() -> bool: - return _is_nighttime - - -func get_weather() -> WorldWeatherService.Weather: - return _weather - - -func _draw() -> void: - var center: Vector2 = size * 0.5 - var radius: float = minf(size.x, size.y) * 0.5 - draw_circle(center, radius, BUBBLE_COLOR) - match _weather: - WorldWeatherService.Weather.SUNNY: - if _is_nighttime: - _draw_moon(center) - else: - _draw_sun(center) - WorldWeatherService.Weather.CLOUDY: - _draw_cloud(center + Vector2(0.0, 1.0)) - WorldWeatherService.Weather.RAINY: - _draw_cloud(center + Vector2(0.0, -2.0)) - for x_offset: float in PackedFloat32Array([-6.0, 0.0, 6.0]): - draw_line( - center + Vector2(x_offset + 1.0, 5.0), - center + Vector2(x_offset - 1.0, 9.0), - RAIN_COLOR, - 2.0, - true, - ) - WorldWeatherService.Weather.FOGGY: - for y_offset: float in PackedFloat32Array([-6.0, 0.0, 6.0]): - draw_line( - center + Vector2(-9.0, y_offset), - center + Vector2(9.0, y_offset), - ICON_COLOR, - 2.0, - true, - ) - - -func _draw_sun(center: Vector2) -> void: - draw_circle(center, 5.0, SUN_COLOR) - for index: int in 8: - var angle: float = TAU * float(index) / 8.0 - var direction := Vector2(cos(angle), sin(angle)) - draw_line( - center + direction * 8.0, - center + direction * 11.0, - SUN_COLOR, - 2.0, - true, - ) - - -func _draw_moon(center: Vector2) -> void: - draw_circle(center, 8.0, MOON_COLOR) - draw_circle(center + Vector2(4.0, -2.0), 7.0, BUBBLE_COLOR) - - -func _update_tooltip() -> void: - tooltip_text = ( - "clear" - if _weather == WorldWeatherService.Weather.SUNNY - else WorldWeatherService.weather_name(_weather) - ) - - -func _draw_cloud(center: Vector2) -> void: - draw_circle(center + Vector2(-5.0, 0.0), 5.0, ICON_COLOR) - draw_circle(center + Vector2(0.0, -3.0), 6.0, ICON_COLOR) - draw_circle(center + Vector2(6.0, 0.0), 5.0, ICON_COLOR) - draw_rect(Rect2(center + Vector2(-9.0, 0.0), Vector2(18.0, 5.0)), ICON_COLOR) diff --git a/ui/weather_icon.gd.uid b/ui/weather_icon.gd.uid deleted file mode 100644 index 0be0e28..0000000 --- a/ui/weather_icon.gd.uid +++ /dev/null @@ -1 +0,0 @@ -uid://crcognj0s8ntq diff --git a/world/environment/netfishing_sky.gdshader b/world/environment/netfishing_sky.gdshader deleted file mode 100644 index b70ab79..0000000 --- a/world/environment/netfishing_sky.gdshader +++ /dev/null @@ -1,83 +0,0 @@ -shader_type sky; - -uniform vec4 sky_top_color : source_color = vec4(0.204, 0.498, 0.643, 1.0); -uniform vec4 sky_horizon_color : source_color = vec4(0.663, 0.843, 0.847, 1.0); -uniform vec4 ground_bottom_color : source_color = vec4(0.157, 0.361, 0.439, 1.0); -uniform vec4 ground_horizon_color : source_color = vec4(0.549, 0.749, 0.765, 1.0); - -uniform vec3 sun_direction = vec3(0.0, 1.0, 0.0); -uniform vec4 sun_color : source_color = vec4(1.0, 0.86, 0.46, 1.0); -uniform float sun_visibility : hint_range(0.0, 1.0) = 1.0; -uniform float sun_radius : hint_range(0.005, 0.15) = 0.055; -uniform float sun_edge_softness : hint_range(0.001, 0.03) = 0.003; -uniform float sun_halo_radius : hint_range(0.02, 0.35) = 0.14; -uniform float sun_halo_strength : hint_range(0.0, 1.0) = 0.16; - -uniform vec3 moon_direction = vec3(0.0, -1.0, 0.0); -uniform vec4 moon_color : source_color = vec4(0.78, 0.88, 1.0, 1.0); -uniform float moon_visibility : hint_range(0.0, 1.0) = 0.0; -uniform float moon_radius : hint_range(0.005, 0.15) = 0.045; -uniform float moon_edge_softness : hint_range(0.001, 0.03) = 0.003; -uniform float moon_halo_radius : hint_range(0.02, 0.35) = 0.11; -uniform float moon_halo_strength : hint_range(0.0, 1.0) = 0.12; - - -void sky() { - vec3 view_direction = normalize(EYEDIR); - float above_horizon = max(view_direction.y, 0.0); - float below_horizon = max(-view_direction.y, 0.0); - vec3 sky_color = mix( - sky_horizon_color.rgb, - sky_top_color.rgb, - pow(above_horizon, 0.72) - ); - vec3 ground_color = mix( - ground_horizon_color.rgb, - ground_bottom_color.rgb, - pow(below_horizon, 0.16) - ); - vec3 base_color = mix( - ground_color, - sky_color, - smoothstep(-0.01, 0.01, view_direction.y) - ); - - float alignment = dot(view_direction, normalize(sun_direction)); - float disc_outer = cos(sun_radius + sun_edge_softness); - float disc_inner = cos(sun_radius); - float halo_outer = cos(sun_halo_radius); - float sun_disc = smoothstep(disc_outer, disc_inner, alignment); - float sun_halo = smoothstep(halo_outer, disc_outer, alignment); - float horizon_visibility = smoothstep(-0.035, 0.02, sun_direction.y); - float visible_strength = sun_visibility * horizon_visibility; - base_color += sun_color.rgb * sun_halo * sun_halo_strength * visible_strength; - base_color = mix(base_color, sun_color.rgb, sun_disc * visible_strength); - - float moon_alignment = dot(view_direction, normalize(moon_direction)); - float moon_disc_outer = cos(moon_radius + moon_edge_softness); - float moon_disc_inner = cos(moon_radius); - float moon_halo_outer = cos(moon_halo_radius); - float moon_disc = smoothstep( - moon_disc_outer, moon_disc_inner, moon_alignment - ); - float moon_halo = smoothstep( - moon_halo_outer, moon_disc_outer, moon_alignment - ); - float moon_horizon_visibility = smoothstep( - -0.035, 0.02, moon_direction.y - ); - float moon_visible_strength = ( - moon_visibility * moon_horizon_visibility - ); - base_color += ( - moon_color.rgb - * moon_halo - * moon_halo_strength - * moon_visible_strength - ); - base_color = mix( - base_color, moon_color.rgb, moon_disc * moon_visible_strength - ); - - COLOR = base_color; -} diff --git a/world/environment/netfishing_sky.gdshader.uid b/world/environment/netfishing_sky.gdshader.uid deleted file mode 100644 index 784558b..0000000 --- a/world/environment/netfishing_sky.gdshader.uid +++ /dev/null @@ -1 +0,0 @@ -uid://chbcdlhui3ro4 diff --git a/world/environment/netfishing_sky.tres b/world/environment/netfishing_sky.tres index 3661dee..1b43a5e 100644 --- a/world/environment/netfishing_sky.tres +++ b/world/environment/netfishing_sky.tres @@ -1,9 +1,13 @@ -[gd_resource type="Sky" load_steps=3 format=3 uid="uid://dersyay71dyat"] +[gd_resource type="Sky" format=3 uid="uid://dersyay71dyat"] -[ext_resource type="Shader" path="res://world/environment/netfishing_sky.gdshader" id="1_sky_shader"] - -[sub_resource type="ShaderMaterial" id="ShaderMaterial_netfishing"] -shader = ExtResource("1_sky_shader") +[sub_resource type="ProceduralSkyMaterial" id="ProceduralSkyMaterial_netfishing"] +sky_top_color = Color(0.203922, 0.498039, 0.643137, 1) +sky_horizon_color = Color(0.662745, 0.843137, 0.847059, 1) +ground_bottom_color = Color(0.156863, 0.360784, 0.439216, 1) +ground_horizon_color = Color(0.54902, 0.74902, 0.764706, 1) +ground_curve = 0.08 +sun_angle_max = 18.0 +sun_curve = 0.22 [resource] -sky_material = SubResource("ShaderMaterial_netfishing") +sky_material = SubResource("ProceduralSkyMaterial_netfishing") diff --git a/world/test_world.gd b/world/test_world.gd index 189c119..d136724 100644 --- a/world/test_world.gd +++ b/world/test_world.gd @@ -15,8 +15,6 @@ const FishingShopInteractionType = preload( @onready var _fishing_shop: FishingShopInteractionType = ( _starter_island.get_fishing_shop() ) -@onready var _world_environment: WorldEnvironment = $Environment/WorldEnvironment -@onready var _sun: DirectionalLight3D = $Environment/Sun func get_player_water_triggers() -> Array[PlayerWaterTrigger]: @@ -42,14 +40,6 @@ func get_player_spawn_transform() -> Transform3D: return _starter_island.get_player_spawn_transform() -func get_world_environment() -> WorldEnvironment: - return _world_environment - - -func get_sun() -> DirectionalLight3D: - return _sun - - func get_fishable_water_regions() -> Array[FishableWaterRegion]: var waters: Array[FishableWaterRegion] = [] for region: WorldRegion in _get_regions(): diff --git a/world/world_time_service.gd b/world/world_time_service.gd deleted file mode 100644 index aef3054..0000000 --- a/world/world_time_service.gd +++ /dev/null @@ -1,126 +0,0 @@ -class_name WorldTimeService -extends Node - -signal time_changed(time_hours: float, phase: Phase) -signal phase_changed(phase: Phase) - -enum Phase { - DAWN, - DAY, - DUSK, - NIGHT, -} - -const HOURS_PER_DAY: float = 24.0 -const REAL_SECONDS_PER_CYCLE: float = 60.0 * 60.0 -const HOURS_PER_REAL_SECOND: float = HOURS_PER_DAY / REAL_SECONDS_PER_CYCLE -const DAY_START_HOUR: float = 8.0 -const NIGHT_START_HOUR: float = 20.0 -const TRANSITION_HALF_HOURS: float = 0.5 -const DAWN_START_HOUR: float = DAY_START_HOUR - TRANSITION_HALF_HOURS -const DAWN_END_HOUR: float = DAY_START_HOUR + TRANSITION_HALF_HOURS -const DUSK_START_HOUR: float = NIGHT_START_HOUR - TRANSITION_HALF_HOURS -const DUSK_END_HOUR: float = NIGHT_START_HOUR + TRANSITION_HALF_HOURS -const DEFAULT_START_HOUR: float = DAY_START_HOUR - -var _time_hours: float = DEFAULT_START_HOUR -var _running: bool = false -var _phase: Phase = Phase.DAWN - - -func _ready() -> void: - set_process(false) - - -func _process(delta: float) -> void: - advance_time(delta) - - -func begin_session(start_hour: float = DEFAULT_START_HOUR) -> void: - _running = true - set_process(true) - _set_time_hours(start_hour, true) - - -func end_session() -> void: - _running = false - set_process(false) - _set_time_hours(DEFAULT_START_HOUR, true) - - -func advance_time(real_seconds: float) -> void: - if not _running or real_seconds <= 0.0: - return - _set_time_hours( - _time_hours + real_seconds * HOURS_PER_REAL_SECOND, - false, - ) - - -func synchronize_time(authoritative_time_hours: float) -> void: - if not is_finite(authoritative_time_hours): - return - _set_time_hours(authoritative_time_hours, true) - - -func get_time_hours() -> float: - return _time_hours - - -func get_phase() -> Phase: - return _phase - - -func is_night_period() -> bool: - return _time_hours < DAY_START_HOUR or _time_hours >= NIGHT_START_HOUR - - -func is_transition() -> bool: - return _phase in [Phase.DAWN, Phase.DUSK] - - -func get_clock_text() -> String: - return format_clock_time(_time_hours) - - -static func phase_for_hour(time_hours: float) -> Phase: - var hour: float = _normalized_hour(time_hours) - if hour >= DAWN_START_HOUR and hour < DAWN_END_HOUR: - return Phase.DAWN - if hour >= DAWN_END_HOUR and hour < DUSK_START_HOUR: - return Phase.DAY - if hour >= DUSK_START_HOUR and hour < DUSK_END_HOUR: - return Phase.DUSK - return Phase.NIGHT - - -static func format_clock_time(time_hours: float) -> String: - var hour: float = _normalized_hour(time_hours) - var total_minutes: int = floori(hour * 60.0) - var hour_24: int = floori(float(total_minutes) / 60.0) - var minute: int = total_minutes % 60 - var hour_12: int = hour_24 % 12 - if hour_12 == 0: - hour_12 = 12 - return "%d:%02d %s" % [ - hour_12, - minute, - "am" if hour_24 < 12 else "pm", - ] - - -func _set_time_hours(time_hours: float, force_emit: bool) -> void: - var normalized: float = _normalized_hour(time_hours) - var next_phase: Phase = phase_for_hour(normalized) - var phase_was_changed: bool = next_phase != _phase - var time_was_changed: bool = not is_equal_approx(normalized, _time_hours) - _time_hours = normalized - _phase = next_phase - if phase_was_changed: - phase_changed.emit(_phase) - if force_emit or time_was_changed or phase_was_changed: - time_changed.emit(_time_hours, _phase) - - -static func _normalized_hour(time_hours: float) -> float: - return fposmod(time_hours, HOURS_PER_DAY) diff --git a/world/world_time_service.gd.uid b/world/world_time_service.gd.uid deleted file mode 100644 index 29e3163..0000000 --- a/world/world_time_service.gd.uid +++ /dev/null @@ -1 +0,0 @@ -uid://5lceyulaglpf diff --git a/world/world_time_visual_controller.gd b/world/world_time_visual_controller.gd deleted file mode 100644 index 611e60b..0000000 --- a/world/world_time_visual_controller.gd +++ /dev/null @@ -1,414 +0,0 @@ -class_name WorldTimeVisualController -extends Node - -const UPDATE_INTERVAL_SECONDS: float = 0.1 -const SUN_YAW_DEGREES: float = -32.0 -const WEATHER_TRANSITION_SECONDS: float = 10.0 -const RAIN_EMITTER_OFFSET := Vector3(0.0, 7.0, 0.0) - -const DAY_SKY_TOP := Color(0.204, 0.498, 0.643) -const DAY_SKY_HORIZON := Color(0.663, 0.843, 0.847) -const DAY_GROUND_BOTTOM := Color(0.157, 0.361, 0.439) -const DAY_GROUND_HORIZON := Color(0.549, 0.749, 0.765) -const DAY_AMBIENT := Color(0.82, 0.90, 0.92) -const DAY_FOG := Color(0.549, 0.749, 0.765) -const DAY_SUN := Color(0.88, 0.94, 0.96) -const DAY_SUN_DISC := Color(1.0, 0.86, 0.46) - -const NIGHT_SKY_TOP := Color(0.026, 0.050, 0.105) -const NIGHT_SKY_HORIZON := Color(0.10, 0.17, 0.28) -const NIGHT_GROUND_BOTTOM := Color(0.020, 0.040, 0.080) -const NIGHT_GROUND_HORIZON := Color(0.075, 0.135, 0.22) -const NIGHT_AMBIENT := Color(0.28, 0.35, 0.48) -const NIGHT_FOG := Color(0.13, 0.21, 0.32) -const NIGHT_SUN := Color(0.35, 0.44, 0.62) - -const WARM_SKY_TOP := Color(0.31, 0.30, 0.42) -const WARM_SKY_HORIZON := Color(0.96, 0.48, 0.22) -const WARM_GROUND_BOTTOM := Color(0.20, 0.14, 0.16) -const WARM_GROUND_HORIZON := Color(0.82, 0.34, 0.18) -const WARM_AMBIENT := Color(0.92, 0.58, 0.40) -const WARM_FOG := Color(0.74, 0.39, 0.27) -const WARM_SUN := Color(1.0, 0.58, 0.30) -const WARM_SUN_DISC := Color(1.0, 0.45, 0.16) -const MOON_DISC := Color(0.78, 0.88, 1.0) - -const CLOUDY_TINT := Color(0.53, 0.61, 0.66) -const RAINY_TINT := Color(0.31, 0.42, 0.50) -const FOGGY_TINT := Color(0.62, 0.70, 0.72) - -var _time_service: WorldTimeService -var _weather_service: WorldWeatherService -var _world_environment: WorldEnvironment -var _sun: DirectionalLight3D -var _rain_target: Node3D -var _environment: Environment -var _sky_material: ShaderMaterial -var _rain: GPUParticles3D -var _elapsed: float = 0.0 -var _weather_from: WorldWeatherService.Weather = ( - WorldWeatherService.Weather.SUNNY -) -var _weather_to: WorldWeatherService.Weather = ( - WorldWeatherService.Weather.SUNNY -) -var _weather_transition: float = 1.0 - - -func setup( - time_service: WorldTimeService, - world_environment: WorldEnvironment, - sun: DirectionalLight3D, - weather_service: WorldWeatherService = null, - rain_target: Node3D = null, -) -> void: - _time_service = time_service - _weather_service = weather_service - _world_environment = world_environment - _sun = sun - _rain_target = rain_target - if not _prepare_runtime_environment(): - set_process(false) - return - _prepare_rain() - if _weather_service != null: - _weather_from = _weather_service.get_weather() - _weather_to = _weather_from - _weather_transition = 1.0 - if not _weather_service.weather_changed.is_connected( - _on_weather_changed - ): - _weather_service.weather_changed.connect(_on_weather_changed) - set_process(true) - _apply_time(_time_service.get_time_hours()) - - -func _process(delta: float) -> void: - if _time_service == null: - return - _update_rain_position() - if _weather_transition < 1.0: - _weather_transition = minf( - _weather_transition + delta / WEATHER_TRANSITION_SECONDS, - 1.0, - ) - _elapsed += delta - if _elapsed < UPDATE_INTERVAL_SECONDS: - return - _elapsed = 0.0 - _apply_time(_time_service.get_time_hours()) - - -func apply_time_immediately(time_hours: float) -> void: - _apply_time(time_hours) - - -func apply_weather_immediately( - weather: WorldWeatherService.Weather, -) -> void: - _weather_from = weather - _weather_to = weather - _weather_transition = 1.0 - if _time_service != null: - _apply_time(_time_service.get_time_hours()) - - -func _prepare_runtime_environment() -> bool: - if ( - _time_service == null - or _world_environment == null - or _world_environment.environment == null - or _sun == null - ): - push_error("World time visuals require an environment and sun.") - return false - _environment = _world_environment.environment.duplicate(true) as Environment - if _environment == null or _environment.sky == null: - push_error("World time visuals could not duplicate the world environment.") - return false - var runtime_sky: Sky = _environment.sky.duplicate(true) as Sky - if runtime_sky == null or runtime_sky.sky_material == null: - push_error("World time visuals require a sky material.") - return false - _sky_material = ( - runtime_sky.sky_material.duplicate(true) as ShaderMaterial - ) - if _sky_material == null: - push_error("World time visuals require the NETfishing sky material.") - return false - runtime_sky.sky_material = _sky_material - _environment.sky = runtime_sky - _world_environment.environment = _environment - return true - - -func _prepare_rain() -> void: - _rain = GPUParticles3D.new() - _rain.name = "LocalRain" - _rain.amount = 480 - _rain.amount_ratio = 0.0 - _rain.lifetime = 1.25 - _rain.fixed_fps = 30 - _rain.local_coords = false - _rain.visibility_aabb = AABB( - Vector3(-7.0, -9.0, -7.0), Vector3(14.0, 12.0, 14.0) - ) - var process_material := ParticleProcessMaterial.new() - process_material.emission_shape = ( - ParticleProcessMaterial.EMISSION_SHAPE_BOX - ) - process_material.emission_box_extents = Vector3(6.5, 1.0, 6.5) - process_material.direction = Vector3.DOWN - process_material.spread = 5.0 - process_material.initial_velocity_min = 11.0 - process_material.initial_velocity_max = 15.0 - process_material.gravity = Vector3(0.0, -2.0, 0.0) - _rain.process_material = process_material - var drop_mesh := BoxMesh.new() - drop_mesh.size = Vector3(0.018, 0.42, 0.018) - var drop_material := StandardMaterial3D.new() - drop_material.transparency = BaseMaterial3D.TRANSPARENCY_ALPHA - drop_material.shading_mode = BaseMaterial3D.SHADING_MODE_UNSHADED - drop_material.albedo_color = Color(0.48, 0.78, 0.90, 0.72) - drop_mesh.material = drop_material - _rain.draw_pass_1 = drop_mesh - add_child(_rain) - _update_rain_position() - - -func _apply_time(time_hours: float) -> void: - if _environment == null or _sky_material == null or _sun == null: - return - var hour: float = fposmod(time_hours, WorldTimeService.HOURS_PER_DAY) - var daylight: float = _daylight_amount(hour) - var warmth: float = _transition_warmth(hour) - var sky_top: Color = _blended_color( - NIGHT_SKY_TOP, DAY_SKY_TOP, WARM_SKY_TOP, daylight, warmth - ) - var sky_horizon: Color = _blended_color( - NIGHT_SKY_HORIZON, - DAY_SKY_HORIZON, - WARM_SKY_HORIZON, - daylight, - warmth, - ) - var ground_bottom: Color = _blended_color( - NIGHT_GROUND_BOTTOM, - DAY_GROUND_BOTTOM, - WARM_GROUND_BOTTOM, - daylight, - warmth, - ) - var ground_horizon: Color = _blended_color( - NIGHT_GROUND_HORIZON, - DAY_GROUND_HORIZON, - WARM_GROUND_HORIZON, - daylight, - warmth, - ) - var ambient: Color = _blended_color( - NIGHT_AMBIENT, DAY_AMBIENT, WARM_AMBIENT, daylight, warmth - ) - var fog_color: Color = _blended_color( - NIGHT_FOG, DAY_FOG, WARM_FOG, daylight, warmth - ) - var weather_tint: Color = _weather_color() - var tint_strength: float = _weather_value( - 0.0, 0.30, 0.48, 0.62 - ) - _sky_material.set_shader_parameter( - "sky_top_color", sky_top.lerp(weather_tint, tint_strength) - ) - _sky_material.set_shader_parameter( - "sky_horizon_color", sky_horizon.lerp(weather_tint, tint_strength) - ) - _sky_material.set_shader_parameter( - "ground_bottom_color", - ground_bottom.lerp(weather_tint, tint_strength * 0.62), - ) - _sky_material.set_shader_parameter( - "ground_horizon_color", - ground_horizon.lerp(weather_tint, tint_strength * 0.76), - ) - _environment.background_energy_multiplier = ( - lerpf(0.52, 0.85, daylight) - * _weather_value(1.0, 0.82, 0.66, 0.74) - ) - _environment.ambient_light_color = ambient.lerp( - weather_tint, tint_strength * 0.45 - ) - _environment.ambient_light_energy = ( - lerpf(0.66, 1.08, daylight) - * _weather_value(1.0, 0.88, 0.76, 0.82) - ) - _environment.fog_light_color = fog_color.lerp( - weather_tint, tint_strength * 0.82 - ) - _environment.fog_light_energy = ( - lerpf(0.56, 0.82, daylight) - * _weather_value(1.0, 0.92, 0.82, 1.05) - ) - _environment.fog_aerial_perspective = _weather_value( - 0.35, 0.48, 0.62, 0.92 - ) - _environment.fog_sky_affect = _weather_value( - 0.35, 0.48, 0.68, 0.94 - ) - _environment.fog_depth_curve = _weather_value( - 1.6, 1.5, 1.4, 1.18 - ) - _environment.fog_depth_begin = _weather_value( - 42.0, 32.0, 20.0, 4.0 - ) - _environment.fog_depth_end = _weather_value( - 170.0, 140.0, 95.0, 42.0 - ) - _environment.adjustment_brightness = ( - lerpf(0.93, 0.98, daylight) - * _weather_value(1.0, 0.97, 0.92, 0.96) - ) - _environment.adjustment_saturation = ( - lerpf(0.82, 0.91, daylight) - * _weather_value(1.0, 0.88, 0.78, 0.70) - ) - _sun.rotation_degrees = Vector3( - -360.0 * fposmod(hour - WorldTimeService.DAY_START_HOUR, 24.0) / 24.0, - SUN_YAW_DEGREES, - 0.0, - ) - _sky_material.set_shader_parameter( - "sun_direction", _sun.global_transform.basis.z.normalized() - ) - _sky_material.set_shader_parameter( - "sun_color", DAY_SUN_DISC.lerp(WARM_SUN_DISC, warmth) - ) - _sky_material.set_shader_parameter( - "sun_visibility", - daylight * _weather_value(1.0, 0.65, 0.28, 0.15), - ) - _sky_material.set_shader_parameter( - "moon_direction", -_sun.global_transform.basis.z.normalized() - ) - _sky_material.set_shader_parameter("moon_color", MOON_DISC) - _sky_material.set_shader_parameter( - "moon_visibility", - (1.0 - daylight) * _weather_value(1.0, 0.72, 0.36, 0.20), - ) - _sun.light_color = _blended_color( - NIGHT_SUN, DAY_SUN, WARM_SUN, daylight, warmth - ) - _sun.light_energy = ( - lerpf(0.0, 0.10, daylight) - * _weather_value(1.0, 0.55, 0.25, 0.35) - ) - _update_rain_amount() - - -func _on_weather_changed( - weather: WorldWeatherService.Weather, - _seconds_remaining: float, -) -> void: - if weather == _weather_to: - return - _weather_from = _weather_to - _weather_to = weather - _weather_transition = 0.0 - - -func _weather_value( - sunny: float, - cloudy: float, - rainy: float, - foggy: float, -) -> float: - return lerpf( - _weather_state_value(_weather_from, sunny, cloudy, rainy, foggy), - _weather_state_value(_weather_to, sunny, cloudy, rainy, foggy), - _weather_transition, - ) - - -func _weather_state_value( - weather: WorldWeatherService.Weather, - sunny: float, - cloudy: float, - rainy: float, - foggy: float, -) -> float: - match weather: - WorldWeatherService.Weather.CLOUDY: - return cloudy - WorldWeatherService.Weather.RAINY: - return rainy - WorldWeatherService.Weather.FOGGY: - return foggy - return sunny - - -func _weather_color() -> Color: - return _weather_state_color(_weather_from).lerp( - _weather_state_color(_weather_to), _weather_transition - ) - - -func _weather_state_color( - weather: WorldWeatherService.Weather, -) -> Color: - match weather: - WorldWeatherService.Weather.CLOUDY: - return CLOUDY_TINT - WorldWeatherService.Weather.RAINY: - return RAINY_TINT - WorldWeatherService.Weather.FOGGY: - return FOGGY_TINT - return Color.WHITE - - -func _update_rain_amount() -> void: - if _rain == null: - return - var rain_amount: float = _weather_value(0.0, 0.0, 1.0, 0.0) - _rain.amount_ratio = rain_amount - _rain.emitting = rain_amount > 0.01 - - -func _update_rain_position() -> void: - if _rain == null or _rain_target == null: - return - _rain.global_position = _rain_target.global_position + RAIN_EMITTER_OFFSET - - -func _daylight_amount(hour: float) -> float: - if hour >= WorldTimeService.DAWN_START_HOUR and hour < WorldTimeService.DAWN_END_HOUR: - return smoothstep( - WorldTimeService.DAWN_START_HOUR, - WorldTimeService.DAWN_END_HOUR, - hour, - ) - if hour >= WorldTimeService.DAWN_END_HOUR and hour < WorldTimeService.DUSK_START_HOUR: - return 1.0 - if hour >= WorldTimeService.DUSK_START_HOUR and hour < WorldTimeService.DUSK_END_HOUR: - return 1.0 - smoothstep( - WorldTimeService.DUSK_START_HOUR, - WorldTimeService.DUSK_END_HOUR, - hour, - ) - return 0.0 - - -func _transition_warmth(hour: float) -> float: - if hour >= WorldTimeService.DAWN_START_HOUR and hour < WorldTimeService.DAWN_END_HOUR: - return 1.0 - absf(hour - WorldTimeService.DAY_START_HOUR) / WorldTimeService.TRANSITION_HALF_HOURS - if hour >= WorldTimeService.DUSK_START_HOUR and hour < WorldTimeService.DUSK_END_HOUR: - return 1.0 - absf(hour - WorldTimeService.NIGHT_START_HOUR) / WorldTimeService.TRANSITION_HALF_HOURS - return 0.0 - - -func _blended_color( - night_color: Color, - day_color: Color, - warm_color: Color, - daylight: float, - warmth: float, -) -> Color: - return night_color.lerp(day_color, daylight).lerp(warm_color, warmth) diff --git a/world/world_time_visual_controller.gd.uid b/world/world_time_visual_controller.gd.uid deleted file mode 100644 index 52bbfc3..0000000 --- a/world/world_time_visual_controller.gd.uid +++ /dev/null @@ -1 +0,0 @@ -uid://svirs8vbypad diff --git a/world/world_weather_service.gd b/world/world_weather_service.gd deleted file mode 100644 index d01728c..0000000 --- a/world/world_weather_service.gd +++ /dev/null @@ -1,152 +0,0 @@ -class_name WorldWeatherService -extends Node - -signal weather_changed(weather: Weather, seconds_remaining: float) - -enum Weather { - SUNNY, - CLOUDY, - RAINY, - FOGGY, -} - -const DEFAULT_WEATHER: Weather = Weather.SUNNY -const SUNNY_DURATION_RANGE := Vector2(480.0, 900.0) -const CLOUDY_DURATION_RANGE := Vector2(300.0, 720.0) -const RAINY_DURATION_RANGE := Vector2(300.0, 600.0) -const FOGGY_DURATION_RANGE := Vector2(300.0, 600.0) - -var _weather: Weather = DEFAULT_WEATHER -var _seconds_remaining: float = SUNNY_DURATION_RANGE.x -var _running_authority: bool = false -var _rng := RandomNumberGenerator.new() - - -func _ready() -> void: - set_process(false) - - -func _process(delta: float) -> void: - advance_weather(delta) - - -func begin_authoritative_session(seed_value: int) -> void: - _rng.seed = seed_value - _running_authority = true - set_process(true) - _set_weather(DEFAULT_WEATHER, _roll_duration(DEFAULT_WEATHER), true) - - -func begin_remote_session() -> void: - _running_authority = false - set_process(false) - _set_weather(DEFAULT_WEATHER, SUNNY_DURATION_RANGE.x, true) - - -func end_session() -> void: - _running_authority = false - set_process(false) - _set_weather(DEFAULT_WEATHER, SUNNY_DURATION_RANGE.x, true) - - -func advance_weather(real_seconds: float) -> void: - if not _running_authority or real_seconds <= 0.0: - return - _seconds_remaining -= real_seconds - while _seconds_remaining <= 0.0: - var overrun: float = -_seconds_remaining - var next_weather: Weather = _choose_next_weather() - _set_weather(next_weather, _roll_duration(next_weather), true) - _seconds_remaining -= overrun - - -func apply_authoritative_snapshot( - weather: Weather, - seconds_remaining: float, -) -> void: - if not is_valid_weather(int(weather)) or not is_finite(seconds_remaining): - return - _set_weather(weather, maxf(seconds_remaining, 0.0), false) - - -func get_weather() -> Weather: - return _weather - - -func get_seconds_remaining() -> float: - return _seconds_remaining - - -func is_raining() -> bool: - return _weather == Weather.RAINY - - -func is_foggy() -> bool: - return _weather == Weather.FOGGY - - -func get_weather_name() -> String: - return weather_name(_weather) - - -static func is_valid_weather(value: int) -> bool: - return value >= Weather.SUNNY and value <= Weather.FOGGY - - -static func weather_name(weather: Weather) -> String: - match weather: - Weather.SUNNY: - return "sunny" - Weather.CLOUDY: - return "cloudy" - Weather.RAINY: - return "rainy" - Weather.FOGGY: - return "foggy" - return "unknown" - - -func _set_weather( - weather: Weather, - seconds_remaining: float, - force_emit: bool, -) -> void: - var changed: bool = weather != _weather - _weather = weather - _seconds_remaining = maxf(seconds_remaining, 0.0) - if force_emit or changed: - weather_changed.emit(_weather, _seconds_remaining) - - -func _choose_next_weather() -> Weather: - match _weather: - Weather.SUNNY: - return Weather.CLOUDY - Weather.RAINY, Weather.FOGGY: - return Weather.CLOUDY - Weather.CLOUDY: - var roll: float = _rng.randf() - if roll < 0.45: - return Weather.SUNNY - if roll < 0.80: - return Weather.RAINY - return Weather.FOGGY - return Weather.SUNNY - - -func _roll_duration(weather: Weather) -> float: - var duration_range: Vector2 = _duration_range(weather) - return _rng.randf_range(duration_range.x, duration_range.y) - - -func _duration_range(weather: Weather) -> Vector2: - match weather: - Weather.SUNNY: - return SUNNY_DURATION_RANGE - Weather.CLOUDY: - return CLOUDY_DURATION_RANGE - Weather.RAINY: - return RAINY_DURATION_RANGE - Weather.FOGGY: - return FOGGY_DURATION_RANGE - return SUNNY_DURATION_RANGE diff --git a/world/world_weather_service.gd.uid b/world/world_weather_service.gd.uid deleted file mode 100644 index 8eed48d..0000000 --- a/world/world_weather_service.gd.uid +++ /dev/null @@ -1 +0,0 @@ -uid://v4c54isq6w74