Compare commits
6 commits
deab51a18a
...
692b6eeb2a
| Author | SHA1 | Date | |
|---|---|---|---|
| 692b6eeb2a | |||
| 1284619079 | |||
| ff536e62a6 | |||
| 9e6536adcd | |||
| d73fc76698 | |||
| 93c1cd4421 |
35 changed files with 415 additions and 88 deletions
3
.gitignore
vendored
3
.gitignore
vendored
|
|
@ -2,3 +2,6 @@
|
|||
.godot/
|
||||
/android/
|
||||
/builds/
|
||||
|
||||
# Blender backup files
|
||||
*.blend1
|
||||
|
|
|
|||
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
BIN
audio/music/world/craft.mp3
Normal file
BIN
audio/music/world/craft.mp3
Normal file
Binary file not shown.
19
audio/music/world/craft.mp3.import
Normal file
19
audio/music/world/craft.mp3.import
Normal file
|
|
@ -0,0 +1,19 @@
|
|||
[remap]
|
||||
|
||||
importer="mp3"
|
||||
type="AudioStreamMP3"
|
||||
uid="uid://c4y8puv0n6xk8"
|
||||
path="res://.godot/imported/craft.mp3-ba8d0c0c16ba90585f5d8eb4f37eb87d.mp3str"
|
||||
|
||||
[deps]
|
||||
|
||||
source_file="res://audio/music/world/craft.mp3"
|
||||
dest_files=["res://.godot/imported/craft.mp3-ba8d0c0c16ba90585f5d8eb4f37eb87d.mp3str"]
|
||||
|
||||
[params]
|
||||
|
||||
loop=false
|
||||
loop_offset=0
|
||||
bpm=0
|
||||
beat_count=0
|
||||
bar_beats=4
|
||||
|
|
@ -340,13 +340,13 @@ func _sample_cell_transform(
|
|||
tangent = Vector3.RIGHT
|
||||
var bitangent: Vector3 = normal.cross(tangent).normalized()
|
||||
var pixel_size: float = cell_size * clampf(fill, 0.05, 1.0)
|
||||
var basis := Basis(
|
||||
var cell_basis := Basis(
|
||||
tangent * pixel_size,
|
||||
bitangent * pixel_size,
|
||||
normal,
|
||||
)
|
||||
return Transform3D(
|
||||
basis,
|
||||
cell_basis,
|
||||
to_local(point + normal * _surface_offset()),
|
||||
)
|
||||
|
||||
|
|
|
|||
|
|
@ -143,7 +143,7 @@ func start_authoritative_encounter(
|
|||
profile: CatchDifficultyProfileType,
|
||||
reel_speed: float,
|
||||
click_power: int,
|
||||
seed: int,
|
||||
encounter_seed_value: int,
|
||||
fish_quality: int = FishQualityType.Tier.BORING,
|
||||
fish_rarity: int = 0,
|
||||
fish_weight_percentile: float = 0.0,
|
||||
|
|
@ -151,7 +151,7 @@ func start_authoritative_encounter(
|
|||
var previous_test_mode: bool = use_deterministic_test_seed
|
||||
var previous_seed: int = deterministic_test_seed
|
||||
use_deterministic_test_seed = true
|
||||
deterministic_test_seed = seed
|
||||
deterministic_test_seed = encounter_seed_value
|
||||
start_encounter(
|
||||
profile,
|
||||
reel_speed,
|
||||
|
|
|
|||
|
|
@ -363,15 +363,15 @@ func handle_escape_action() -> bool:
|
|||
|
||||
|
||||
func set_local_menu_input_suppressed(
|
||||
owner: StringName,
|
||||
input_owner: StringName,
|
||||
suppressed: bool,
|
||||
) -> void:
|
||||
if owner.is_empty():
|
||||
if input_owner.is_empty():
|
||||
return
|
||||
if suppressed:
|
||||
_local_menu_input_owners[owner] = true
|
||||
_local_menu_input_owners[input_owner] = true
|
||||
else:
|
||||
_local_menu_input_owners.erase(owner)
|
||||
_local_menu_input_owners.erase(input_owner)
|
||||
if suppressed and state == FishingState.WAITING_FOR_BITE:
|
||||
_withdrawal_input_held = false
|
||||
_stop_reeling_audio()
|
||||
|
|
@ -627,6 +627,7 @@ func _begin_aiming(player: PlayerType) -> void:
|
|||
_cast_charge = 0.0
|
||||
_cast_target = _calculate_cast_target(minimum_cast_distance)
|
||||
state = FishingState.AIMING_CAST
|
||||
_active_player.set_casting_visual()
|
||||
status_changed.emit("")
|
||||
_cast_path_is_clear = is_cast_path_clear(
|
||||
_get_cast_launch_position(),
|
||||
|
|
@ -813,6 +814,8 @@ func _confirm_cast() -> void:
|
|||
# released so the active bobber/fishing event remains anchored.
|
||||
_active_player.set_movement_enabled(false)
|
||||
state = FishingState.CASTING
|
||||
if _active_player != null:
|
||||
_active_player.set_release_visual()
|
||||
status_changed.emit("")
|
||||
_presentation.begin_cast(
|
||||
_cast_target,
|
||||
|
|
@ -1102,7 +1105,7 @@ func _on_catch_encounter_updated(
|
|||
barrier_health: PackedInt32Array,
|
||||
barrier_max_health: PackedInt32Array,
|
||||
active_barrier_index: int,
|
||||
visible: bool,
|
||||
encounter_visible: bool,
|
||||
) -> void:
|
||||
catch_display_changed.emit(
|
||||
progress,
|
||||
|
|
@ -1111,9 +1114,9 @@ func _on_catch_encounter_updated(
|
|||
barrier_health,
|
||||
barrier_max_health,
|
||||
active_barrier_index,
|
||||
visible
|
||||
encounter_visible
|
||||
)
|
||||
if state != FishingState.FIGHTING or not visible:
|
||||
if state != FishingState.FIGHTING or not encounter_visible:
|
||||
return
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -62,6 +62,7 @@ func show_cast(origin: Vector3, target: Vector3) -> void:
|
|||
_bobber.visible = true
|
||||
_line.visible = true
|
||||
_owner.set_active_item_is_rod(true)
|
||||
_owner.set_release_visual()
|
||||
_redraw_line()
|
||||
_cast_tween = create_tween()
|
||||
_cast_tween.set_trans(Tween.TRANS_SINE)
|
||||
|
|
@ -99,6 +100,7 @@ func show_bite() -> void:
|
|||
func play_return(showcase_catch: FishCatch = null) -> void:
|
||||
if _owner != null and is_instance_valid(_owner):
|
||||
_owner.set_fighting_visual(false)
|
||||
_owner.set_fishing_visual(false)
|
||||
if not _active or _bobber == null:
|
||||
cleanup()
|
||||
return_completed.emit()
|
||||
|
|
@ -135,6 +137,7 @@ func cleanup() -> void:
|
|||
_bobber_idle_elapsed = 0.0
|
||||
if _owner != null and is_instance_valid(_owner):
|
||||
_owner.set_fighting_visual(false)
|
||||
_owner.set_fishing_visual(false)
|
||||
_owner.end_catch_showcase(Callable(), true)
|
||||
if _bobber != null:
|
||||
_bobber.visible = false
|
||||
|
|
@ -162,6 +165,8 @@ func _set_cast_sample(
|
|||
func _on_cast_finished() -> void:
|
||||
_cast_tween = null
|
||||
_bobber_idle_elapsed = 0.0
|
||||
if _owner != null and is_instance_valid(_owner):
|
||||
_owner.set_fishing_visual(true)
|
||||
_apply_bobber_idle_motion()
|
||||
_redraw_line()
|
||||
|
||||
|
|
|
|||
BIN
items/icons/shop/64_power_plus.png
Normal file
BIN
items/icons/shop/64_power_plus.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 5.2 KiB |
40
items/icons/shop/64_power_plus.png.import
Normal file
40
items/icons/shop/64_power_plus.png.import
Normal file
|
|
@ -0,0 +1,40 @@
|
|||
[remap]
|
||||
|
||||
importer="texture"
|
||||
type="CompressedTexture2D"
|
||||
uid="uid://xh2g4eytrjep"
|
||||
path="res://.godot/imported/64_power_plus.png-98b7e06d3090a4411d56e2046b8c9d4b.ctex"
|
||||
metadata={
|
||||
"vram_texture": false
|
||||
}
|
||||
|
||||
[deps]
|
||||
|
||||
source_file="res://items/icons/shop/64_power_plus.png"
|
||||
dest_files=["res://.godot/imported/64_power_plus.png-98b7e06d3090a4411d56e2046b8c9d4b.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
|
||||
73
main/main.gd
73
main/main.gd
|
|
@ -96,6 +96,7 @@ const PlayerJobServiceType = preload("res://jobs/player_job_service.gd")
|
|||
const NetworkJobServiceType = preload("res://network/network_job_service.gd")
|
||||
|
||||
const TITLE_MUSIC_SILENCE_DB: float = -80.0
|
||||
const TIME_CROSSING_EPSILON_HOURS: float = 0.000001
|
||||
const PLAYER_MENU_PATTERN_SCALE: float = 0.85
|
||||
const PLAYER_MENU_PATTERN_SCROLL_VELOCITY := Vector2(-7.0, -5.0)
|
||||
const SHOP_PATTERN_SCALE: float = 1.75
|
||||
|
|
@ -120,6 +121,7 @@ const SHOP_PATTERN_SCALE: float = 1.75
|
|||
)
|
||||
@onready var _interface_fonts: InterfaceFontController = %InterfaceFontController
|
||||
@onready var _title_music: AudioStreamPlayer = %TitleMusic
|
||||
@onready var _dusk_music: AudioStreamPlayer = %DuskMusic
|
||||
@onready var _ui_pixelation: UIPixelationPresenterType = %UIPresentation
|
||||
@onready var _pixelation_reset: PixelationResetOverlayType = (
|
||||
%PixelationResetOverlay
|
||||
|
|
@ -191,6 +193,7 @@ var _shop_interaction: FishingShopInteractionType
|
|||
var _title_music_tween: Tween
|
||||
var _title_music_transition_generation: int = 0
|
||||
var _title_music_requested: bool = false
|
||||
var _dusk_music_played_for_natural_day: bool = false
|
||||
var _quit_in_progress: bool = false
|
||||
var _join_requested_from_title: bool = false
|
||||
var _join_requested_from_pause: bool = false
|
||||
|
|
@ -316,6 +319,12 @@ func _initialize_after_data_root() -> void:
|
|||
_world_weather,
|
||||
_player,
|
||||
)
|
||||
if not _world_time.natural_time_advanced.is_connected(
|
||||
_on_natural_time_advanced
|
||||
):
|
||||
_world_time.natural_time_advanced.connect(
|
||||
_on_natural_time_advanced
|
||||
)
|
||||
_network_world_time.setup(_network_session, _world_time)
|
||||
_network_world_weather.setup(_network_session, _world_weather)
|
||||
_player_jobs.setup(
|
||||
|
|
@ -1184,9 +1193,18 @@ func _reset_pixelation() -> void:
|
|||
|
||||
|
||||
func _set_gameplay_active(active: bool) -> void:
|
||||
var was_gameplay_started: bool = _gameplay_started
|
||||
_gameplay_started = active
|
||||
_shoreline_ambience.set_active(active)
|
||||
_rain_ambience.set_active(active)
|
||||
if active and not was_gameplay_started:
|
||||
var current_hour: float = _world_time.get_time_hours()
|
||||
_dusk_music_played_for_natural_day = (
|
||||
current_hour >= WorldTimeServiceType.DUSK_START_HOUR
|
||||
or current_hour < WorldTimeServiceType.DAWN_START_HOUR
|
||||
)
|
||||
elif not active:
|
||||
_dusk_music.stop()
|
||||
_title_background.visible = not active
|
||||
_world_pixelation.set_gameplay_active(active)
|
||||
_ui_pixelation.set_gameplay_active(active)
|
||||
|
|
@ -1208,11 +1226,55 @@ func _set_gameplay_active(active: bool) -> void:
|
|||
_set_shop_backdrop_visible(false)
|
||||
|
||||
|
||||
func _set_player_menu_backdrop_visible(is_visible: bool) -> void:
|
||||
func _on_natural_time_advanced(advanced_hours: float) -> void:
|
||||
if not _gameplay_started or advanced_hours <= 0.0:
|
||||
return
|
||||
var current_hour: float = _world_time.get_time_hours()
|
||||
var previous_hour: float = fposmod(
|
||||
current_hour - advanced_hours,
|
||||
WorldTimeServiceType.HOURS_PER_DAY,
|
||||
)
|
||||
if _natural_interval_crosses_hour(
|
||||
previous_hour,
|
||||
advanced_hours,
|
||||
WorldTimeServiceType.DAWN_START_HOUR,
|
||||
):
|
||||
_dusk_music_played_for_natural_day = false
|
||||
if (
|
||||
not _dusk_music_played_for_natural_day
|
||||
and _natural_interval_crosses_hour(
|
||||
previous_hour,
|
||||
advanced_hours,
|
||||
WorldTimeServiceType.DUSK_START_HOUR,
|
||||
)
|
||||
):
|
||||
_dusk_music_played_for_natural_day = true
|
||||
_dusk_music.play(0.0)
|
||||
|
||||
|
||||
static func _natural_interval_crosses_hour(
|
||||
previous_hour: float,
|
||||
advanced_hours: float,
|
||||
target_hour: float,
|
||||
) -> bool:
|
||||
if advanced_hours >= WorldTimeServiceType.HOURS_PER_DAY:
|
||||
return true
|
||||
var distance_to_target: float = fposmod(
|
||||
target_hour - previous_hour,
|
||||
WorldTimeServiceType.HOURS_PER_DAY,
|
||||
)
|
||||
return (
|
||||
distance_to_target > 0.0
|
||||
and distance_to_target
|
||||
<= advanced_hours + TIME_CROSSING_EPSILON_HOURS
|
||||
)
|
||||
|
||||
|
||||
func _set_player_menu_backdrop_visible(requested_visible: bool) -> void:
|
||||
if _player_menu_backdrop_tween != null:
|
||||
_player_menu_backdrop_tween.kill()
|
||||
_player_menu_backdrop_tween = null
|
||||
var should_show: bool = is_visible and _gameplay_started
|
||||
var should_show: bool = requested_visible and _gameplay_started
|
||||
if should_show:
|
||||
var was_visible: bool = _player_menu_backdrop.visible
|
||||
_player_menu_backdrop.visible = true
|
||||
|
|
@ -1243,11 +1305,11 @@ func _set_player_menu_backdrop_visible(is_visible: bool) -> void:
|
|||
)
|
||||
|
||||
|
||||
func _set_shop_backdrop_visible(is_visible: bool) -> void:
|
||||
func _set_shop_backdrop_visible(requested_visible: bool) -> void:
|
||||
if _shop_backdrop_tween != null:
|
||||
_shop_backdrop_tween.kill()
|
||||
_shop_backdrop_tween = null
|
||||
var should_show: bool = is_visible and _gameplay_started
|
||||
var should_show: bool = requested_visible and _gameplay_started
|
||||
if should_show:
|
||||
var was_visible: bool = _shop_backdrop.visible
|
||||
_shop_backdrop.visible = true
|
||||
|
|
@ -1687,6 +1749,9 @@ func _exit_tree() -> void:
|
|||
if is_instance_valid(_title_music):
|
||||
_title_music.stop()
|
||||
_title_music.stream = null
|
||||
if is_instance_valid(_dusk_music):
|
||||
_dusk_music.stop()
|
||||
_dusk_music.stream = null
|
||||
|
||||
|
||||
func _on_shop_range_changed(in_range: bool) -> void:
|
||||
|
|
|
|||
|
|
@ -13,6 +13,7 @@
|
|||
[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" uid="uid://bgkfluumuen4m" path="res://audio/music/title/as_in_four_wolves.ogg" id="13_title_music"]
|
||||
[ext_resource type="AudioStream" path="res://audio/music/world/craft.mp3" id="dusk_music"]
|
||||
[ext_resource type="Script" uid="uid://bdtb05vkiicd" path="res://ui/ui_pixelation_presenter.gd" id="14_pixelation"]
|
||||
[ext_resource type="PackedScene" path="res://ui/pixelation_reset_overlay.tscn" id="15_reset"]
|
||||
[ext_resource type="PackedScene" path="res://main/world_pixelation_postprocess.tscn" id="16_world_pixelation"]
|
||||
|
|
@ -398,6 +399,11 @@ stream = ExtResource("13_title_music")
|
|||
volume_db = -80.0
|
||||
bus = &"Music"
|
||||
|
||||
[node name="DuskMusic" type="AudioStreamPlayer" parent="."]
|
||||
unique_name_in_owner = true
|
||||
stream = ExtResource("dusk_music")
|
||||
bus = &"Music"
|
||||
|
||||
[node name="ShorelineAmbience" type="Node" parent="." unique_id=1948037177]
|
||||
unique_name_in_owner = true
|
||||
script = ExtResource("54_shoreline_ambience")
|
||||
|
|
|
|||
|
|
@ -1,6 +1,7 @@
|
|||
class_name NetworkTransport
|
||||
extends Node
|
||||
|
||||
@warning_ignore("unused_signal")
|
||||
signal transport_error(message: String)
|
||||
|
||||
var _peer: MultiplayerPeer
|
||||
|
|
|
|||
|
|
@ -41,10 +41,19 @@ const CHARACTER_IDLE_SIT_ANIMATION: StringName = &"idle_sit"
|
|||
const CHARACTER_IDLE_SIT_SHOW_ANIMATION: StringName = &"idle_sit_show"
|
||||
const CHARACTER_WALKING_ANIMATION: StringName = &"walking"
|
||||
const CHARACTER_WALKING_SHOW_ANIMATION: StringName = &"walking_show"
|
||||
const CHARACTER_CASTING_ANIMATION: StringName = &"casting"
|
||||
const CHARACTER_RELEASE_ANIMATION: StringName = &"release"
|
||||
const CHARACTER_FISHING_ANIMATION: StringName = &"fishing"
|
||||
const CHARACTER_FISHING_SIT_ANIMATION: StringName = &"fishing_sit"
|
||||
const CHARACTER_FIGHTING_ANIMATION: StringName = &"fighting"
|
||||
const CHARACTER_FIGHTING_SIT_ANIMATION: StringName = &"fighting_sit"
|
||||
|
||||
enum FishingVisualPhase {
|
||||
NONE,
|
||||
CASTING,
|
||||
RELEASE,
|
||||
FISHING,
|
||||
}
|
||||
const FIGHTING_EYES_ID: String = "alligator_eyes"
|
||||
const BASE_REEL_SPEED: float = 0.16
|
||||
# The target Android handheld exposes its physical right trigger through
|
||||
|
|
@ -121,9 +130,24 @@ func set_fighting_visual(active: bool) -> void:
|
|||
|
||||
|
||||
func set_fishing_visual(active: bool) -> void:
|
||||
if _fishing_visual_active == active:
|
||||
_set_fishing_visual_phase(
|
||||
FishingVisualPhase.FISHING if active else FishingVisualPhase.NONE
|
||||
)
|
||||
|
||||
|
||||
func set_casting_visual() -> void:
|
||||
_set_fishing_visual_phase(FishingVisualPhase.CASTING)
|
||||
|
||||
|
||||
func set_release_visual() -> void:
|
||||
_set_fishing_visual_phase(FishingVisualPhase.RELEASE)
|
||||
|
||||
|
||||
func _set_fishing_visual_phase(phase: FishingVisualPhase) -> void:
|
||||
if _fishing_visual_phase == phase:
|
||||
return
|
||||
_fishing_visual_active = active
|
||||
_fishing_visual_phase = phase
|
||||
_fishing_visual_active = phase != FishingVisualPhase.NONE
|
||||
_character_animation_name = &""
|
||||
_update_character_animation()
|
||||
|
||||
|
|
@ -283,8 +307,12 @@ var _held_fish_visible: bool = false
|
|||
var _showcase_animation_active: bool = false
|
||||
var _fighting_visual_active: bool = false
|
||||
var _fishing_visual_active: bool = false
|
||||
var _fishing_visual_phase: FishingVisualPhase = FishingVisualPhase.NONE
|
||||
var _fishing_rod: Node3D
|
||||
var _fishing_rod_tip: Marker3D
|
||||
var _fishing_rod_skeleton: Skeleton3D
|
||||
var _fishing_rod_hand_bone: int = -1
|
||||
var _fishing_rod_relative_basis: Basis = Basis.IDENTITY
|
||||
var _controller_mapping_manager: ControllerMappingManagerType
|
||||
|
||||
|
||||
|
|
@ -334,6 +362,38 @@ func _initialize_fishing_rod() -> void:
|
|||
_fishing_rod_tip = attachment.get_node(
|
||||
"FishingRod/FishingRodTip"
|
||||
) as Marker3D
|
||||
_fishing_rod_skeleton = skeleton
|
||||
_fishing_rod_hand_bone = skeleton.find_bone(&"hand.R")
|
||||
if _fishing_rod_hand_bone < 0:
|
||||
push_error("Player character right-hand bone is unavailable.")
|
||||
return
|
||||
_fishing_rod_relative_basis = (
|
||||
skeleton.global_basis.orthonormalized().inverse()
|
||||
* _fishing_rod.global_basis.orthonormalized()
|
||||
)
|
||||
_fishing_rod.top_level = true
|
||||
_sync_fishing_rod_transform()
|
||||
|
||||
|
||||
func _sync_fishing_rod_transform() -> void:
|
||||
if (
|
||||
_fishing_rod == null
|
||||
or _fishing_rod_skeleton == null
|
||||
or _fishing_rod_hand_bone < 0
|
||||
):
|
||||
return
|
||||
var skeleton_transform := _fishing_rod_skeleton.global_transform
|
||||
var hand_pose := _fishing_rod_skeleton.get_bone_global_pose(
|
||||
_fishing_rod_hand_bone
|
||||
)
|
||||
var fixed_basis := (
|
||||
skeleton_transform.basis.orthonormalized()
|
||||
* _fishing_rod_relative_basis
|
||||
)
|
||||
_fishing_rod.global_transform = Transform3D(
|
||||
fixed_basis.scaled(skeleton_transform.basis.get_scale().abs()),
|
||||
(skeleton_transform * hand_pose).origin,
|
||||
)
|
||||
|
||||
|
||||
func _initialize_held_item_attachment() -> void:
|
||||
|
|
@ -363,6 +423,7 @@ func _initialize_held_item_attachment() -> void:
|
|||
|
||||
|
||||
func _physics_process(delta: float) -> void:
|
||||
_sync_fishing_rod_transform()
|
||||
if _network_interpolation_enabled:
|
||||
_update_network_interpolation(delta)
|
||||
return
|
||||
|
|
@ -455,6 +516,7 @@ func _physics_process(delta: float) -> void:
|
|||
|
||||
|
||||
func _process(delta: float) -> void:
|
||||
_sync_fishing_rod_transform()
|
||||
_update_character_animation()
|
||||
# The hand bone supplies the attachment position, but its animated wrist
|
||||
# rotation should not turn the flat fish/catch artwork edge-on. Keep each
|
||||
|
|
@ -588,10 +650,24 @@ func _update_character_animation() -> void:
|
|||
&"idle_sit_loop",
|
||||
]
|
||||
else:
|
||||
requested_animation = [
|
||||
CHARACTER_FISHING_ANIMATION,
|
||||
CHARACTER_IDLE_ANIMATION,
|
||||
]
|
||||
match _fishing_visual_phase:
|
||||
FishingVisualPhase.CASTING:
|
||||
requested_animation = [
|
||||
CHARACTER_CASTING_ANIMATION,
|
||||
CHARACTER_FISHING_ANIMATION,
|
||||
CHARACTER_IDLE_ANIMATION,
|
||||
]
|
||||
FishingVisualPhase.RELEASE:
|
||||
requested_animation = [
|
||||
CHARACTER_RELEASE_ANIMATION,
|
||||
CHARACTER_FISHING_ANIMATION,
|
||||
CHARACTER_IDLE_ANIMATION,
|
||||
]
|
||||
_:
|
||||
requested_animation = [
|
||||
CHARACTER_FISHING_ANIMATION,
|
||||
CHARACTER_IDLE_ANIMATION,
|
||||
]
|
||||
elif _sitting:
|
||||
if _held_fish_visible:
|
||||
requested_animation = [
|
||||
|
|
|
|||
|
|
@ -54,7 +54,7 @@ FISHING
|
|||
- Coffee improves movement; other supplies temporarily improve fishing.
|
||||
- The Cooler starts at 12 fish; buy permanent capacity expansions at the shop.
|
||||
- Reel Speed upgrades increase authoritative green reeling progress.
|
||||
- Barrier Power upgrades increase damage per valid barrier action.
|
||||
- Rod Power upgrades increase damage per valid barrier action.
|
||||
- Shop upgrades persist in the local progression save.
|
||||
- Escape closes shop confirmations before closing the shop itself.
|
||||
|
||||
|
|
|
|||
|
|
@ -913,7 +913,7 @@ func _add_button_action_binding(action: StringName, value: Variant) -> void:
|
|||
return
|
||||
var event := InputEventJoypadButton.new()
|
||||
event.device = _active_device_id
|
||||
event.button_index = int(binding.get("button", -1))
|
||||
event.button_index = int(binding.get("button", -1)) as JoyButton
|
||||
InputMap.action_add_event(action, event)
|
||||
|
||||
|
||||
|
|
@ -932,7 +932,7 @@ func _add_axis_action_binding(
|
|||
)
|
||||
var event := InputEventJoypadMotion.new()
|
||||
event.device = _active_device_id
|
||||
event.axis = int(binding.get("axis", -1))
|
||||
event.axis = int(binding.get("axis", -1)) as JoyAxis
|
||||
event.axis_value = logical_direction * -captured_negative_direction
|
||||
InputMap.action_add_event(action, event)
|
||||
|
||||
|
|
|
|||
|
|
@ -34,13 +34,13 @@ func _ready() -> void:
|
|||
|
||||
|
||||
func speak_text(
|
||||
owner: Node,
|
||||
tween_owner: Node,
|
||||
text: String,
|
||||
voice_key: String,
|
||||
voice_profile_id: String = VoiceProfilesType.DEFAULT_ID,
|
||||
characters_per_second: float = -1.0,
|
||||
) -> Tween:
|
||||
var speech_tween := owner.create_tween()
|
||||
var speech_tween := tween_owner.create_tween()
|
||||
var resolved_characters_per_second := (
|
||||
characters_per_second
|
||||
if characters_per_second > 0.0
|
||||
|
|
@ -108,7 +108,8 @@ func _play_character(
|
|||
- 0.5
|
||||
) * 0.10
|
||||
var question_lift := 0.0
|
||||
if full_text.ends_with("?") and character_index >= full_text.length() * 3 / 4:
|
||||
var question_lift_start: int = floori(float(full_text.length()) * 0.75)
|
||||
if full_text.ends_with("?") and character_index >= question_lift_start:
|
||||
var final_progress := (
|
||||
float(character_index) / maxf(float(full_text.length() - 1), 1.0)
|
||||
)
|
||||
|
|
|
|||
|
|
@ -93,14 +93,14 @@ func _initialize_motion() -> void:
|
|||
_set_visual_y(_target_y())
|
||||
|
||||
|
||||
func _on_toggled(pressed: bool) -> void:
|
||||
if _selected and not pressed:
|
||||
func _on_toggled(is_pressed: bool) -> void:
|
||||
if _selected and not is_pressed:
|
||||
# A selected organizer tab is a page marker, not a collapsible toggle.
|
||||
# Restore without another signal before any lower-state frame is drawn.
|
||||
set_pressed_no_signal(true)
|
||||
refresh_state(false)
|
||||
return
|
||||
_selected = pressed
|
||||
_selected = is_pressed
|
||||
refresh_state()
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -389,22 +389,22 @@ func _accept_captured_binding(binding: Dictionary) -> void:
|
|||
ControllerMappingManagerType.ROLE_ORDER[_auto_map_index]
|
||||
)
|
||||
return
|
||||
var saved: bool = _mapping_manager.replace_active_bindings(
|
||||
var profile_saved: bool = _mapping_manager.replace_active_bindings(
|
||||
_auto_map_draft
|
||||
)
|
||||
_cancel_capture()
|
||||
_progress_label.text = (
|
||||
"controller mapped successfully"
|
||||
if saved else "could not save the controller mapping"
|
||||
if profile_saved else "could not save the controller mapping"
|
||||
)
|
||||
_refresh_bindings()
|
||||
return
|
||||
var role: StringName = _capturing_role
|
||||
var saved: bool = _mapping_manager.set_binding(role, binding)
|
||||
var binding_saved: bool = _mapping_manager.set_binding(role, binding)
|
||||
_cancel_capture()
|
||||
_progress_label.text = (
|
||||
"updated " + _mapping_manager.get_role_label(role)
|
||||
if saved else "could not save that controller input"
|
||||
if binding_saved else "could not save that controller input"
|
||||
)
|
||||
_refresh_bindings()
|
||||
|
||||
|
|
|
|||
|
|
@ -525,7 +525,7 @@ func _refresh_supplies() -> void:
|
|||
FishingShopStockType.get_price(item_id),
|
||||
owned,
|
||||
]
|
||||
var tooltip_text: String = item.description
|
||||
var item_tooltip_text: String = item.description
|
||||
if bait_topoff:
|
||||
if item.icon != null:
|
||||
button.custom_minimum_size = Vector2(72, 72)
|
||||
|
|
@ -543,7 +543,7 @@ func _refresh_supplies() -> void:
|
|||
item.max_stack,
|
||||
]
|
||||
)
|
||||
tooltip_text = (
|
||||
item_tooltip_text = (
|
||||
"%s\nunlock $%d • fills to %d/%d\n%s" % [
|
||||
item.display_name,
|
||||
total_cost,
|
||||
|
|
@ -573,7 +573,7 @@ func _refresh_supplies() -> void:
|
|||
button.tooltip_text = (
|
||||
"Purchases are unavailable in this session."
|
||||
if _network_shop == null or not _network_shop.can_request_purchase()
|
||||
else tooltip_text
|
||||
else item_tooltip_text
|
||||
)
|
||||
UtilityPageStyleType.apply_ocean_button(button)
|
||||
button.pressed.connect(_purchase_supply.bind(item_id))
|
||||
|
|
|
|||
|
|
@ -3,7 +3,7 @@
|
|||
[ext_resource type="Script" path="res://ui/fishing_shop.gd" id="1_script"]
|
||||
[ext_resource type="Theme" path="res://ui/game_theme.tres" id="2_theme"]
|
||||
[ext_resource type="Texture2D" path="res://items/icons/placeholder/reel_speed_upgrade.svg" id="3_reel"]
|
||||
[ext_resource type="Texture2D" path="res://items/icons/placeholder/barrier_power_upgrade.svg" id="4_barrier"]
|
||||
[ext_resource type="Texture2D" path="res://items/icons/shop/64_power_plus.png" id="4_barrier"]
|
||||
[ext_resource type="Texture2D" path="res://items/icons/placeholder/fish_coin.svg" id="5_coin"]
|
||||
[ext_resource type="Texture2D" path="res://items/icons/equipment/64_cooler_plus.png" id="6_cooler"]
|
||||
|
||||
|
|
@ -277,7 +277,7 @@ 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 = "rod power"
|
||||
theme_override_font_sizes/font_size = 22
|
||||
|
||||
[node name="BarrierLevel" type="Label" parent="ShopPanel/Margin/Layout/Body/Upgrades/BarrierCard/Margin/Row/Data"]
|
||||
|
|
|
|||
|
|
@ -125,7 +125,6 @@ const SHOP_NPC_SPEECH_COOLDOWN_MILLISECONDS: int = 5000
|
|||
|
||||
|
||||
@onready var _canonical_stage: Control = %CanonicalStage
|
||||
@onready var _ui_root: Control = %UIRoot
|
||||
@onready var _player_menu: PlayerMenuType = %PlayerMenu
|
||||
@onready var _screen_fade: ScreenFade = %ScreenFade
|
||||
@onready var _title_screen: TitleScreenType = %TitleScreen
|
||||
|
|
@ -1458,12 +1457,12 @@ func get_fishing_shop() -> FishingShopType:
|
|||
|
||||
|
||||
func set_shop_prompt_visible(
|
||||
is_visible: bool,
|
||||
requested_visible: bool,
|
||||
world_anchor: Vector3 = Vector3(0.0, INF, 0.0),
|
||||
) -> void:
|
||||
_apply_shop_prompt_style()
|
||||
_shop_prompt.visible = (
|
||||
is_visible
|
||||
requested_visible
|
||||
and _gameplay_ui_enabled
|
||||
and not _system_menu_open
|
||||
and not _player_menu_open
|
||||
|
|
@ -1688,8 +1687,8 @@ func _refresh_fishing_panel_visibility() -> void:
|
|||
)
|
||||
|
||||
|
||||
func _on_bite_prompt_changed(is_visible: bool) -> void:
|
||||
_bite_prompt_button.visible = is_visible
|
||||
func _on_bite_prompt_changed(prompt_visible: bool) -> void:
|
||||
_bite_prompt_button.visible = prompt_visible
|
||||
_refresh_fishing_panel_visibility()
|
||||
|
||||
|
||||
|
|
@ -1705,10 +1704,10 @@ func _on_catch_display_changed(
|
|||
barrier_health: PackedInt32Array,
|
||||
_barrier_max_health: PackedInt32Array,
|
||||
active_barrier_index: int,
|
||||
visible: bool,
|
||||
encounter_requested_visible: bool,
|
||||
) -> void:
|
||||
var encounter_visible: bool = (
|
||||
visible
|
||||
encounter_requested_visible
|
||||
and _fishing_spot != null
|
||||
and _fishing_spot.is_fighting()
|
||||
)
|
||||
|
|
@ -1789,10 +1788,10 @@ func _on_showcase_changed(
|
|||
rarity_name: String,
|
||||
weight_lb: float,
|
||||
quality: int,
|
||||
visible: bool,
|
||||
showcase_visible: bool,
|
||||
) -> void:
|
||||
_showcase_active = visible
|
||||
if not visible:
|
||||
_showcase_active = showcase_visible
|
||||
if not showcase_visible:
|
||||
_set_fishing_panel_showcase_position(false)
|
||||
_showcase_details.text = ""
|
||||
_showcase_details.visible = false
|
||||
|
|
@ -2195,10 +2194,10 @@ func _on_controller_hotbar_management_ended() -> void:
|
|||
|
||||
|
||||
func _on_hotbar_presentation_transition_finished(
|
||||
is_visible: bool,
|
||||
presentation_visible: bool,
|
||||
) -> void:
|
||||
_hotbar_ui.set_drag_enabled(
|
||||
is_visible
|
||||
presentation_visible
|
||||
and _player_menu_open
|
||||
and _player_menu_hotbar_visible
|
||||
and not _system_menu_open
|
||||
|
|
|
|||
|
|
@ -355,7 +355,7 @@ func _make_scroll_indicator(
|
|||
return indicator
|
||||
|
||||
|
||||
func _on_catalog_scroll_value_changed(value: float) -> void:
|
||||
func _on_catalog_scroll_value_changed(_value: float) -> void:
|
||||
if _snapping_catalog_scroll:
|
||||
return
|
||||
_catalog_scroll_snap_timer.start()
|
||||
|
|
|
|||
|
|
@ -1,8 +1,6 @@
|
|||
class_name MailPage
|
||||
extends Control
|
||||
|
||||
signal focus_requested
|
||||
|
||||
const GREETING_LABELS := {
|
||||
"dear": "Dear",
|
||||
"to": "To",
|
||||
|
|
|
|||
|
|
@ -214,7 +214,6 @@ const SALE_CONFIRMATION_SIZE := Vector2(520.0, 190.0)
|
|||
@onready var _bag_item_field: Control = %BagItemField
|
||||
@onready var _bag_empty_state: Label = %BagEmptyState
|
||||
@onready var _bag_detail_constellation: Control = %BagDetailConstellation
|
||||
@onready var _bag_sprite_detail_bubble: Control = %BagDetailBubble
|
||||
@onready var _bag_sprite_detail_texture: TextureRect = %BagSpriteDetailTexture
|
||||
@onready var _bag_sprite_detail_name: Label = %BagSpriteDetailName
|
||||
@onready var _bag_sprite_detail_data: Label = %BagSpriteDetailData
|
||||
|
|
@ -228,7 +227,6 @@ const SALE_CONFIRMATION_SIZE := Vector2(520.0, 190.0)
|
|||
@onready var _book_spread: BoxContainer = %BookSpread
|
||||
@onready var _left_page: PanelContainer = %LeftPage
|
||||
@onready var _right_page: PanelContainer = %RightPage
|
||||
@onready var _book_gutter: ColorRect = %BookGutter
|
||||
@onready var _left_heading: Label = %LeftHeading
|
||||
@onready var _right_heading: Label = %RightHeading
|
||||
@onready var _left_entry_field: BoxContainer = %LeftEntryField
|
||||
|
|
@ -259,7 +257,6 @@ const SALE_CONFIRMATION_SIZE := Vector2(520.0, 190.0)
|
|||
@onready var _offer_status: BubbleStatusBubbleType = %OfferStatus
|
||||
@onready var _inventory_section: Control = %InventorySection
|
||||
@onready var _bag_section: Control = %BagSection
|
||||
@onready var _logbook_section: Control = %LogbookSection
|
||||
@onready var _bag_empty: Label = %BagEmpty
|
||||
@onready var _bag_grid: GridContainer = %BagGrid
|
||||
@onready var _bag_list: Control = %BagList
|
||||
|
|
@ -272,9 +269,6 @@ const SALE_CONFIRMATION_SIZE := Vector2(520.0, 190.0)
|
|||
@onready var _held_value: Label = %HeldValue
|
||||
@onready var _cooler_count: Label = %CoolerCount
|
||||
@onready var _inventory_empty: Label = %InventoryEmpty
|
||||
@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
|
||||
|
|
@ -285,7 +279,6 @@ const SALE_CONFIRMATION_SIZE := Vector2(520.0, 190.0)
|
|||
@onready var _confirm_sale_button: Button = %ConfirmSaleButton
|
||||
@onready var _cancel_sale_button: Button = %CancelSaleButton
|
||||
@onready var _logbook_empty: Label = %LogbookEmpty
|
||||
@onready var _logbook_grid: GridContainer = %LogbookGrid
|
||||
|
||||
var _compact_layout: bool = false
|
||||
var _player: PlayerType
|
||||
|
|
@ -1196,11 +1189,9 @@ func _collect_visible_toggle_buttons(
|
|||
or not navigation_cluster.is_ancestor_of(button)
|
||||
)
|
||||
):
|
||||
var group_key: Variant = (
|
||||
button.button_group
|
||||
if button.button_group != null
|
||||
else button.get_parent()
|
||||
)
|
||||
var group_key: Variant = button.get_parent()
|
||||
if button.button_group != null:
|
||||
group_key = button.button_group
|
||||
if not grouped_buttons.has(group_key):
|
||||
grouped_buttons[group_key] = []
|
||||
var buttons := grouped_buttons[group_key] as Array
|
||||
|
|
@ -3355,7 +3346,7 @@ func _update_bag_detail() -> void:
|
|||
item_state = "equippable"
|
||||
elif item.usable:
|
||||
item_state = "usable"
|
||||
_bag_detail_texture.texture = item.icon if item != null else null
|
||||
_bag_detail_texture.texture = null
|
||||
_bag_detail_name.text = item.display_name if item != null else ""
|
||||
_bag_detail_data.text = (
|
||||
"%s\nquantity: %d\n%s\n%s\n%s"
|
||||
|
|
@ -3369,7 +3360,10 @@ func _update_bag_detail() -> void:
|
|||
if item != null
|
||||
else "select a bag item for details."
|
||||
)
|
||||
_bag_sprite_detail_texture.texture = item.icon if item != null else null
|
||||
_bag_sprite_detail_texture.texture = null
|
||||
if item != null:
|
||||
_bag_detail_texture.texture = item.icon
|
||||
_bag_sprite_detail_texture.texture = item.icon
|
||||
_bag_sprite_detail_name.text = (
|
||||
item.display_name if item != null else ""
|
||||
)
|
||||
|
|
@ -3857,11 +3851,6 @@ func _update_inventory_detail(fish_catch: FishCatchType) -> void:
|
|||
_favorite_bubble.refresh_ink_state()
|
||||
_detail_constellation.visible = _current_section == Section.COOLER
|
||||
_cooler_sort_controls.visible = true
|
||||
var detail_interactive: bool = (
|
||||
_detail_constellation.visible
|
||||
and not _transitioning
|
||||
and not _page_transitioning
|
||||
)
|
||||
_refresh_cooler_notepad_action_interactivity()
|
||||
_configure_cooler_fish_focus()
|
||||
|
||||
|
|
@ -3881,16 +3870,12 @@ func _refresh_cooler_notepad_action_interactivity() -> void:
|
|||
_sell_all_bubble,
|
||||
]:
|
||||
var action_interactive: bool = detail_interactive and not action.disabled
|
||||
action.focus_mode = (
|
||||
Control.FOCUS_ALL
|
||||
if action_interactive
|
||||
else Control.FOCUS_NONE
|
||||
)
|
||||
action.mouse_filter = (
|
||||
Control.MOUSE_FILTER_STOP
|
||||
if action_interactive
|
||||
else Control.MOUSE_FILTER_IGNORE
|
||||
)
|
||||
if action_interactive:
|
||||
action.focus_mode = Control.FOCUS_ALL
|
||||
action.mouse_filter = Control.MOUSE_FILTER_STOP
|
||||
else:
|
||||
action.focus_mode = Control.FOCUS_NONE
|
||||
action.mouse_filter = Control.MOUSE_FILTER_IGNORE
|
||||
action.refresh_ink_state()
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -720,7 +720,7 @@ func _identity_operation_allowed() -> bool:
|
|||
return true
|
||||
|
||||
|
||||
func _on_identity_operation_finished(success: bool, message: String) -> void:
|
||||
func _on_identity_operation_finished(_success: bool, message: String) -> void:
|
||||
_feedback.text = message
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -39,10 +39,10 @@ func set_weather(weather: WorldWeatherService.Weather) -> void:
|
|||
queue_redraw()
|
||||
|
||||
|
||||
func set_nighttime(is_nighttime: bool) -> void:
|
||||
if _is_nighttime == is_nighttime:
|
||||
func set_nighttime(nighttime: bool) -> void:
|
||||
if _is_nighttime == nighttime:
|
||||
return
|
||||
_is_nighttime = is_nighttime
|
||||
_is_nighttime = nighttime
|
||||
queue_redraw()
|
||||
|
||||
|
||||
|
|
|
|||
44
world/materials/foliage_wind.gdshader
Normal file
44
world/materials/foliage_wind.gdshader
Normal file
|
|
@ -0,0 +1,44 @@
|
|||
shader_type spatial;
|
||||
render_mode cull_back, depth_draw_opaque;
|
||||
|
||||
uniform vec4 albedo_color : source_color = vec4(1.0);
|
||||
uniform float material_roughness = 1.0;
|
||||
uniform float material_metallic = 0.0;
|
||||
uniform float local_min_y = 0.0;
|
||||
uniform float local_height = 1.0;
|
||||
uniform float local_wind_strength = 0.08;
|
||||
uniform float wind_speed = 0.9;
|
||||
uniform float wind_phase = 0.0;
|
||||
|
||||
|
||||
void vertex() {
|
||||
float height_ratio = clamp(
|
||||
(VERTEX.y - local_min_y) / max(local_height, 0.001),
|
||||
0.0,
|
||||
1.0
|
||||
);
|
||||
float bend_mask = smoothstep(0.08, 1.0, height_ratio);
|
||||
vec3 world_position = (MODEL_MATRIX * vec4(VERTEX, 1.0)).xyz;
|
||||
float broad_wave = sin(
|
||||
TIME * wind_speed
|
||||
+ dot(world_position.xz, vec2(0.23, 0.17))
|
||||
+ wind_phase
|
||||
);
|
||||
float leaf_flutter = sin(
|
||||
TIME * wind_speed * 2.17
|
||||
+ dot(world_position.xz, vec2(-0.41, 0.37))
|
||||
+ wind_phase * 1.7
|
||||
);
|
||||
float displacement = (
|
||||
broad_wave * 0.72 + leaf_flutter * 0.28
|
||||
) * local_wind_strength * bend_mask;
|
||||
VERTEX.x += displacement;
|
||||
VERTEX.z += displacement * 0.42;
|
||||
}
|
||||
|
||||
|
||||
void fragment() {
|
||||
ALBEDO = albedo_color.rgb;
|
||||
ROUGHNESS = material_roughness;
|
||||
METALLIC = material_metallic;
|
||||
}
|
||||
1
world/materials/foliage_wind.gdshader.uid
Normal file
1
world/materials/foliage_wind.gdshader.uid
Normal file
|
|
@ -0,0 +1 @@
|
|||
uid://c3q7l8m9n2v4x
|
||||
|
|
@ -5,6 +5,14 @@ extends WorldRegion
|
|||
const FishingShopInteractionType = preload(
|
||||
"res://world/fishing_shop_interaction.gd"
|
||||
)
|
||||
const FOLIAGE_WIND_SHADER: Shader = preload(
|
||||
"res://world/materials/foliage_wind.gdshader"
|
||||
)
|
||||
const FOLIAGE_MATERIAL_NAMES: Array[StringName] = [
|
||||
&"leaf",
|
||||
&"leaf_light",
|
||||
&"leaf_dark",
|
||||
]
|
||||
|
||||
@export_group("Owned Nodes")
|
||||
@export_node_path("MeshInstance3D")
|
||||
|
|
@ -25,10 +33,14 @@ var fishing_shop_path: NodePath = (
|
|||
)
|
||||
@export_group("Terrain Collision")
|
||||
@export var rebuild_terrain_collision_on_ready: bool = false
|
||||
@export_group("Foliage Wind")
|
||||
@export_range(0.0, 0.4, 0.005) var foliage_wind_strength: float = 0.12
|
||||
@export_range(0.0, 4.0, 0.05) var foliage_wind_speed: float = 0.9
|
||||
|
||||
func _ready() -> void:
|
||||
if rebuild_terrain_collision_on_ready or not has_terrain_collision():
|
||||
_build_terrain_collision()
|
||||
_apply_foliage_wind()
|
||||
if Engine.is_editor_hint():
|
||||
update_configuration_warnings()
|
||||
|
||||
|
|
@ -107,6 +119,73 @@ func _collect_terrain_mesh_instances(root: Node) -> Array[MeshInstance3D]:
|
|||
return mesh_instances
|
||||
|
||||
|
||||
func _apply_foliage_wind() -> void:
|
||||
var terrain_root: Node = get_node_or_null(terrain_visual_root_path)
|
||||
if terrain_root == null:
|
||||
return
|
||||
for mesh_instance: MeshInstance3D in _collect_terrain_mesh_instances(
|
||||
terrain_root
|
||||
):
|
||||
_apply_foliage_wind_to_mesh(mesh_instance)
|
||||
|
||||
|
||||
func _apply_foliage_wind_to_mesh(mesh_instance: MeshInstance3D) -> void:
|
||||
if mesh_instance.mesh == null:
|
||||
return
|
||||
var bounds: AABB = mesh_instance.get_aabb()
|
||||
var mesh_global_scale: Vector3 = mesh_instance.global_basis.get_scale().abs()
|
||||
var horizontal_scale: float = maxf(
|
||||
(mesh_global_scale.x + mesh_global_scale.z) * 0.5,
|
||||
0.001,
|
||||
)
|
||||
var local_strength: float = foliage_wind_strength / horizontal_scale
|
||||
var phase: float = fposmod(
|
||||
mesh_instance.global_position.x * 0.73
|
||||
+ mesh_instance.global_position.z * 0.41,
|
||||
TAU,
|
||||
)
|
||||
for surface_index: int in mesh_instance.mesh.get_surface_count():
|
||||
var source_material: Material = mesh_instance.get_active_material(
|
||||
surface_index
|
||||
)
|
||||
if not source_material is StandardMaterial3D:
|
||||
continue
|
||||
if not FOLIAGE_MATERIAL_NAMES.has(
|
||||
StringName(source_material.resource_name)
|
||||
):
|
||||
continue
|
||||
var source_standard := source_material as StandardMaterial3D
|
||||
var wind_material := ShaderMaterial.new()
|
||||
wind_material.shader = FOLIAGE_WIND_SHADER
|
||||
wind_material.set_shader_parameter(
|
||||
"albedo_color",
|
||||
source_standard.albedo_color,
|
||||
)
|
||||
wind_material.set_shader_parameter(
|
||||
"material_roughness",
|
||||
source_standard.roughness,
|
||||
)
|
||||
wind_material.set_shader_parameter(
|
||||
"material_metallic",
|
||||
source_standard.metallic,
|
||||
)
|
||||
wind_material.set_shader_parameter("local_min_y", bounds.position.y)
|
||||
wind_material.set_shader_parameter(
|
||||
"local_height",
|
||||
maxf(bounds.size.y, 0.001),
|
||||
)
|
||||
wind_material.set_shader_parameter(
|
||||
"local_wind_strength",
|
||||
local_strength,
|
||||
)
|
||||
wind_material.set_shader_parameter("wind_speed", foliage_wind_speed)
|
||||
wind_material.set_shader_parameter("wind_phase", phase)
|
||||
mesh_instance.set_surface_override_material(
|
||||
surface_index,
|
||||
wind_material,
|
||||
)
|
||||
|
||||
|
||||
func _get_configuration_warnings() -> PackedStringArray:
|
||||
var warnings := PackedStringArray()
|
||||
var visual_mesh := get_node_or_null(visual_mesh_path) as MeshInstance3D
|
||||
|
|
|
|||
|
|
@ -481,4 +481,6 @@ static func _mesh_triangle_count(mesh: ArrayMesh) -> int:
|
|||
if mesh.get_surface_count() == 0:
|
||||
return 0
|
||||
var arrays := mesh.surface_get_arrays(0)
|
||||
return (arrays[Mesh.ARRAY_INDEX] as PackedInt32Array).size() / 3
|
||||
return floori(
|
||||
float((arrays[Mesh.ARRAY_INDEX] as PackedInt32Array).size()) / 3.0
|
||||
)
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue