Improve PortMaster performance and controls
This commit is contained in:
parent
9afa60f6a5
commit
57df54d82d
14 changed files with 328 additions and 126 deletions
|
|
@ -15,13 +15,17 @@ directory. The templates in `scripts/portmaster/` are authoritative.
|
|||
|
||||
## Release contract
|
||||
|
||||
- `port.json` uses schema version 4 and names `netfishing.zip`.
|
||||
- `port.json` uses schema version 4, names `netfishing.zip`, and marks the
|
||||
self-contained package ready to run.
|
||||
- `NETfishing.sh` starts with `# PORTMASTER: netfishing.zip, NETfishing.sh`.
|
||||
- The executable is `netfishing/NETfishing.aarch64`.
|
||||
- The package declares AArch64, two analog sticks, GLIBC 2.28, and
|
||||
`weston_pkg_0.2.squashfs`.
|
||||
- The launcher must not start GPTOKEYB. Godot and NETfishing's controller
|
||||
mapping manager handle controller input directly.
|
||||
- The launcher starts GPTOKEYB in exit-only mode without a `-c` mapping file.
|
||||
PortMaster therefore owns only its device-specific force-quit chord while
|
||||
Godot and NETfishing's controller mapping manager handle gameplay input.
|
||||
- The launcher calls `pm_platform_helper` for the game executable after
|
||||
starting GPTOKEYB and calls `pm_finish` after the game exits.
|
||||
- Persistent device data remains under `netfishing/conf/data`,
|
||||
`netfishing/conf/config`, and `netfishing/conf/cache`.
|
||||
- The archive must not contain `conf/`, saves, identities, logs, source files,
|
||||
|
|
@ -69,11 +73,14 @@ Before upgrading an existing installation, preserve
|
|||
`/mnt/mmc/ports/netfishing/conf/` on the device. After installation:
|
||||
|
||||
1. Confirm the installed executable and PCK hashes match the staged release.
|
||||
2. Confirm the installed launcher contains the canonical PortMaster header and
|
||||
does not contain `GPTOKEYB`.
|
||||
2. Confirm the installed launcher contains the canonical PortMaster header,
|
||||
starts GPTOKEYB without a `-c` mapping file, and calls
|
||||
`pm_platform_helper` for the game executable.
|
||||
3. Confirm `conf/` was not replaced or removed.
|
||||
4. Launch the installed port through the normal muOS menu.
|
||||
5. Verify the displayed game version and controller face-button mapping.
|
||||
6. Verify PortMaster's force-quit chord exits the game. This is Start+Select on
|
||||
most devices.
|
||||
|
||||
Installing the public PortMaster catalog entry can still install an older
|
||||
catalog build until the upstream `netfishing.zip` is updated. A local release
|
||||
|
|
@ -127,6 +134,11 @@ as `SDL_GAMECONTROLLERCONFIG` in the game command after Weston initializes;
|
|||
Weston sources PortMaster's control file internally and otherwise restores the
|
||||
SDL2 value.
|
||||
|
||||
Do not pass a `.gptk` controller mapping to GPTOKEYB. Its exit-only process
|
||||
must retain PortMaster's controller configuration, while the verified Godot
|
||||
mapping remains scoped to the game command. This prevents duplicate or
|
||||
translated gameplay events while preserving the system force-quit chord.
|
||||
|
||||
Do not call `Input.add_joy_mapping(..., true)` for a recognized connected muOS
|
||||
controller. Updating this virtual controller after connection can stop Godot
|
||||
from delivering its standardized controller events until restart.
|
||||
|
|
@ -162,7 +174,9 @@ also:
|
|||
- disables the additional full-screen world-pixelation pass;
|
||||
- replaces animated depth-aware water shaders with opaque, per-vertex water;
|
||||
- disables ocean surface motion;
|
||||
- reduces rain to 256 particles simulated at 15 FPS; and
|
||||
- disables foliage wind;
|
||||
- keeps the title water and full-window menu patterns static;
|
||||
- reduces rain to 128 particles simulated at 12 FPS; and
|
||||
- replaces procedural sky clouds and 81 moving local cloud patches with one
|
||||
flat cloud ceiling.
|
||||
|
||||
|
|
|
|||
72
main/main.gd
72
main/main.gd
|
|
@ -259,6 +259,9 @@ func _ready() -> void:
|
|||
_test_world.set_light_performance_profile(
|
||||
_performance_profile.is_light()
|
||||
)
|
||||
_configure_presentation_performance_profile(
|
||||
_performance_profile.is_light()
|
||||
)
|
||||
DisplayServer.window_set_title("NETfishing")
|
||||
_rain_ambience = RainAmbienceType.new()
|
||||
_rain_ambience.name = "RainAmbience"
|
||||
|
|
@ -268,28 +271,6 @@ func _ready() -> void:
|
|||
_player,
|
||||
_test_world.get_saltwater_shoreline_mesh(),
|
||||
)
|
||||
var menu_pattern_material := (
|
||||
_player_menu_backdrop.material as ShaderMaterial
|
||||
)
|
||||
if menu_pattern_material != null:
|
||||
menu_pattern_material.set_shader_parameter(
|
||||
"display_scale",
|
||||
PLAYER_MENU_PATTERN_SCALE,
|
||||
)
|
||||
menu_pattern_material.set_shader_parameter(
|
||||
"scroll_velocity_pixels",
|
||||
PLAYER_MENU_PATTERN_SCROLL_VELOCITY,
|
||||
)
|
||||
var shop_pattern_material := _shop_backdrop.material as ShaderMaterial
|
||||
if shop_pattern_material != null:
|
||||
shop_pattern_material.set_shader_parameter(
|
||||
"display_scale",
|
||||
SHOP_PATTERN_SCALE,
|
||||
)
|
||||
shop_pattern_material.set_shader_parameter(
|
||||
"scroll_velocity_pixels",
|
||||
PLAYER_MENU_PATTERN_SCROLL_VELOCITY,
|
||||
)
|
||||
get_window().size_changed.connect(_resize_native_overlays)
|
||||
_resize_native_overlays()
|
||||
if not _settings_manager.settings_changed.is_connected(
|
||||
|
|
@ -305,6 +286,53 @@ func _ready() -> void:
|
|||
_show_data_root_setup()
|
||||
|
||||
|
||||
func _configure_presentation_performance_profile(light_profile: bool) -> void:
|
||||
var title_water_material := _title_background.material as ShaderMaterial
|
||||
if title_water_material != null:
|
||||
title_water_material.set_shader_parameter(
|
||||
"animation_enabled",
|
||||
not light_profile,
|
||||
)
|
||||
var menu_pattern_material := (
|
||||
_player_menu_backdrop.material as ShaderMaterial
|
||||
)
|
||||
if menu_pattern_material != null:
|
||||
menu_pattern_material.set_shader_parameter(
|
||||
"animation_enabled",
|
||||
not light_profile,
|
||||
)
|
||||
menu_pattern_material.set_shader_parameter(
|
||||
"display_scale",
|
||||
PLAYER_MENU_PATTERN_SCALE,
|
||||
)
|
||||
menu_pattern_material.set_shader_parameter(
|
||||
"scroll_velocity_pixels",
|
||||
(
|
||||
Vector2.ZERO
|
||||
if light_profile
|
||||
else PLAYER_MENU_PATTERN_SCROLL_VELOCITY
|
||||
),
|
||||
)
|
||||
var shop_pattern_material := _shop_backdrop.material as ShaderMaterial
|
||||
if shop_pattern_material != null:
|
||||
shop_pattern_material.set_shader_parameter(
|
||||
"animation_enabled",
|
||||
not light_profile,
|
||||
)
|
||||
shop_pattern_material.set_shader_parameter(
|
||||
"display_scale",
|
||||
SHOP_PATTERN_SCALE,
|
||||
)
|
||||
shop_pattern_material.set_shader_parameter(
|
||||
"scroll_velocity_pixels",
|
||||
(
|
||||
Vector2.ZERO
|
||||
if light_profile
|
||||
else PLAYER_MENU_PATTERN_SCROLL_VELOCITY
|
||||
),
|
||||
)
|
||||
|
||||
|
||||
func _is_dedicated_server_runtime() -> bool:
|
||||
if OS.has_feature("dedicated_server"):
|
||||
return true
|
||||
|
|
|
|||
|
|
@ -144,6 +144,11 @@ PortMaster launches use the light performance profile by default. Put the word
|
|||
\`normal\` in \`netfishing/conf/performance_profile\` to opt a capable device
|
||||
into the normal rendering profile. See \`netfishing/licenses/\` for bundled
|
||||
credits and license information.
|
||||
|
||||
## Controls
|
||||
|
||||
NETfishing reads the handheld controller directly. PortMaster's system
|
||||
force-quit chord remains available; this is Start+Select on most devices.
|
||||
EOF
|
||||
|
||||
(
|
||||
|
|
@ -163,14 +168,26 @@ if [[ "${TOP_LEVELS}" != "${EXPECTED_TOP_LEVELS}" ]]; then
|
|||
fi
|
||||
grep -q '"version": 4' "${STAGE_ROOT}/port.json"
|
||||
grep -q '"name": "netfishing.zip"' "${STAGE_ROOT}/port.json"
|
||||
grep -q '"rtr": true' "${STAGE_ROOT}/port.json"
|
||||
grep -q '^# PORTMASTER: netfishing.zip, NETfishing.sh$' \
|
||||
"${STAGE_ROOT}/NETfishing.sh"
|
||||
grep -Fq 'PROFILE_PATH="$CONFDIR/performance_profile"' \
|
||||
"${STAGE_ROOT}/NETfishing.sh"
|
||||
grep -Fq 'NETFISHING_PERFORMANCE_PROFILE=light' \
|
||||
"${STAGE_ROOT}/NETfishing.sh"
|
||||
if grep -q 'GPTOKEYB' "${STAGE_ROOT}/NETfishing.sh"; then
|
||||
echo "GPTOKEYB must not be enabled for NETfishing." >&2
|
||||
grep -Fq '$GPTOKEYB "NETfishing.aarch64" &' \
|
||||
"${STAGE_ROOT}/NETfishing.sh"
|
||||
grep -Fq 'pm_platform_helper "$GAME_EXECUTABLE"' \
|
||||
"${STAGE_ROOT}/NETfishing.sh"
|
||||
grep -Fq 'pm_finish' "${STAGE_ROOT}/NETfishing.sh"
|
||||
if [[ "$(grep -Ec '^[[:space:]]*\$GPTOKEYB[[:space:]]' \
|
||||
"${STAGE_ROOT}/NETfishing.sh")" -ne 1 ]]; then
|
||||
echo "NETfishing must start exactly one GPTOKEYB exit handler." >&2
|
||||
exit 1
|
||||
fi
|
||||
if grep -Eq '\$GPTOKEYB.*[[:space:]]-c([[:space:]]|$)' \
|
||||
"${STAGE_ROOT}/NETfishing.sh"; then
|
||||
echo "GPTOKEYB controller mappings must not be enabled for NETfishing." >&2
|
||||
exit 1
|
||||
fi
|
||||
if unzip -Z1 "${ARCHIVE}" | grep -E \
|
||||
|
|
|
|||
|
|
@ -117,6 +117,12 @@ case "$PERFORMANCE_PROFILE" in
|
|||
;;
|
||||
esac
|
||||
|
||||
# Keep NETfishing's native controller input separate from PortMaster's exit
|
||||
# handling. Without a -c mapping file, GPTOKEYB only watches for the
|
||||
# device-specific force-quit chord and does not inject gameplay inputs.
|
||||
$GPTOKEYB "NETfishing.aarch64" &
|
||||
pm_platform_helper "$GAME_EXECUTABLE"
|
||||
|
||||
$ESUDO env CRUSTY_RESOLUTION="${DISPLAY_WIDTH}x${DISPLAY_HEIGHT}" \
|
||||
"$WESTON_DIR/westonwrap.sh" headless noop kiosk crusty_x11egl \
|
||||
XDG_DATA_HOME="$CONFDIR/data" \
|
||||
|
|
|
|||
|
|
@ -20,7 +20,7 @@
|
|||
"simulation"
|
||||
],
|
||||
"image": {},
|
||||
"rtr": false,
|
||||
"rtr": true,
|
||||
"exp": false,
|
||||
"runtime": [
|
||||
"weston_pkg_0.2.squashfs"
|
||||
|
|
|
|||
|
|
@ -216,8 +216,7 @@ func _run() -> void:
|
|||
) as EmoteRadialMenu
|
||||
assert(quick_menu != null and emote_menu != null)
|
||||
assert(QuickRadialMenu.ACTIONS.size() == QuickRadialMenu.SECTOR_COUNT)
|
||||
assert(QuickRadialMenu.ACTIONS.has(&"freecam"))
|
||||
assert(QuickRadialMenu.ACTIONS.has(&"hud"))
|
||||
assert(QuickRadialMenu.ACTIONS == [&"chat", &"freecam", &"hud"])
|
||||
game_ui.call("_on_quick_action_selected", &"freecam")
|
||||
assert(player.is_free_camera_active())
|
||||
var free_camera := player.get_active_gameplay_camera()
|
||||
|
|
@ -544,10 +543,6 @@ func _run() -> void:
|
|||
await process_frame
|
||||
assert(player.hotbar.get_selected_slot() == 1)
|
||||
assert(not service.is_active() and not toolbar.visible)
|
||||
game_ui.call("_on_quick_action_selected", &"paint")
|
||||
await process_frame
|
||||
assert(player.hotbar.get_selected_slot() == 0)
|
||||
assert(service.is_active() and toolbar.visible)
|
||||
|
||||
print("Art tools validation: PASS")
|
||||
var session := main.get_node("%NetworkSession") as NetworkSession
|
||||
|
|
|
|||
|
|
@ -211,12 +211,17 @@ func _validate_portmaster_launcher() -> String:
|
|||
"leftstick:b9",
|
||||
"rightstick:b12",
|
||||
"netfishing_controllerconfig=\"$GODOT_MUOS_MAPPING\"",
|
||||
"$GPTOKEYB \"NETfishing.aarch64\" &",
|
||||
"pm_platform_helper \"$GAME_EXECUTABLE\"",
|
||||
"SDL_GAMECONTROLLERCONFIG=\"$netfishing_controllerconfig\" \\",
|
||||
"pm_finish",
|
||||
]:
|
||||
if expected_fragment not in launcher:
|
||||
return "PortMaster launcher omitted " + expected_fragment
|
||||
if "$'\\n'" in launcher:
|
||||
return "PortMaster launcher appends mappings across a Weston-unsafe newline"
|
||||
if "$GPTOKEYB \"NETfishing.aarch64\" -c" in launcher:
|
||||
return "PortMaster launcher injects duplicate GPTOKEYB gameplay mappings"
|
||||
return ""
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -4,6 +4,9 @@ const MainScene: PackedScene = preload("res://main/main.tscn")
|
|||
const RuntimePerformanceProfileType = preload(
|
||||
"res://main/runtime_performance_profile.gd"
|
||||
)
|
||||
const FOLIAGE_WIND_SHADER: Shader = preload(
|
||||
"res://world/materials/foliage_wind.gdshader"
|
||||
)
|
||||
|
||||
|
||||
func _initialize() -> void:
|
||||
|
|
@ -28,12 +31,27 @@ func _run() -> void:
|
|||
assert(bool(main.get("_application_initialized")))
|
||||
_validate_main_profile(main)
|
||||
_validate_minimal_weather(main)
|
||||
_stop_audio_players(main)
|
||||
main.queue_free()
|
||||
for _frame: int in 4:
|
||||
for _frame: int in 8:
|
||||
await process_frame
|
||||
OS.unset_environment(
|
||||
RuntimePerformanceProfileType.PROFILE_ENVIRONMENT_VARIABLE
|
||||
)
|
||||
var normal_main: Node = MainScene.instantiate()
|
||||
root.add_child(normal_main)
|
||||
for _frame: int in 4:
|
||||
await process_frame
|
||||
if not bool(normal_main.get("_application_initialized")):
|
||||
normal_main.call("_activate_selected_data_path", "", true)
|
||||
for _frame: int in 8:
|
||||
await process_frame
|
||||
assert(bool(normal_main.get("_application_initialized")))
|
||||
_validate_normal_profile(normal_main)
|
||||
_stop_audio_players(normal_main)
|
||||
normal_main.queue_free()
|
||||
for _frame: int in 8:
|
||||
await process_frame
|
||||
print("Light performance profile validation: PASS")
|
||||
quit()
|
||||
|
||||
|
|
@ -70,6 +88,131 @@ func _validate_main_profile(main: Node) -> void:
|
|||
assert(not pond.material_override is ShaderMaterial)
|
||||
assert(not ocean.material_override is ShaderMaterial)
|
||||
assert(not ocean.is_processing())
|
||||
_validate_ocean_fishing_coverage(main, ocean)
|
||||
var island := main.get_node(
|
||||
"TestWorld/Regions/StarterIslandRegion"
|
||||
) as StarterIslandRegion
|
||||
assert(island != null and not island.is_foliage_wind_enabled())
|
||||
assert(_count_foliage_wind_overrides(
|
||||
island.get_node("Terrain/Visual")
|
||||
) == 0)
|
||||
|
||||
var title_background := main.get_node("%TitleBackground") as ColorRect
|
||||
var title_material := title_background.material as ShaderMaterial
|
||||
assert(title_material != null)
|
||||
assert(not bool(title_material.get_shader_parameter(
|
||||
"animation_enabled"
|
||||
)))
|
||||
for backdrop_name: StringName in [
|
||||
&"PlayerMenuBackdrop",
|
||||
&"ShopBackdrop",
|
||||
]:
|
||||
var backdrop := main.get_node("%%%s" % backdrop_name) as ColorRect
|
||||
var backdrop_material := backdrop.material as ShaderMaterial
|
||||
assert(backdrop_material != null)
|
||||
assert(not bool(backdrop_material.get_shader_parameter(
|
||||
"animation_enabled"
|
||||
)))
|
||||
assert(
|
||||
backdrop_material.get_shader_parameter(
|
||||
"scroll_velocity_pixels"
|
||||
) == Vector2.ZERO
|
||||
)
|
||||
|
||||
|
||||
func _validate_normal_profile(main: Node) -> void:
|
||||
var profile: RuntimePerformanceProfile = main.get("_performance_profile")
|
||||
assert(profile != null and not profile.is_light())
|
||||
assert(is_equal_approx(root.scaling_3d_scale, 1.0))
|
||||
var pond := main.get_node(
|
||||
"TestWorld/Regions/StarterIslandRegion/WaterBodies/Pond/VisualWater"
|
||||
) as MeshInstance3D
|
||||
var ocean := main.get_node(
|
||||
"TestWorld/Regions/StarterIslandRegion/WaterBodies/Ocean/VisualWater"
|
||||
) as MeshInstance3D
|
||||
assert(pond.material_override is ShaderMaterial)
|
||||
assert(ocean.material_override is ShaderMaterial)
|
||||
assert(not ocean.is_processing())
|
||||
_validate_ocean_fishing_coverage(main, ocean)
|
||||
var island := main.get_node(
|
||||
"TestWorld/Regions/StarterIslandRegion"
|
||||
) as StarterIslandRegion
|
||||
assert(island != null and island.is_foliage_wind_enabled())
|
||||
assert(_count_foliage_wind_overrides(
|
||||
island.get_node("Terrain/Visual")
|
||||
) > 0)
|
||||
|
||||
var title_background := main.get_node("%TitleBackground") as ColorRect
|
||||
var title_material := title_background.material as ShaderMaterial
|
||||
assert(title_material != null)
|
||||
assert(bool(title_material.get_shader_parameter("animation_enabled")))
|
||||
for backdrop_name: StringName in [
|
||||
&"PlayerMenuBackdrop",
|
||||
&"ShopBackdrop",
|
||||
]:
|
||||
var backdrop := main.get_node("%%%s" % backdrop_name) as ColorRect
|
||||
var backdrop_material := backdrop.material as ShaderMaterial
|
||||
assert(backdrop_material != null)
|
||||
assert(bool(backdrop_material.get_shader_parameter(
|
||||
"animation_enabled"
|
||||
)))
|
||||
assert(
|
||||
backdrop_material.get_shader_parameter(
|
||||
"scroll_velocity_pixels"
|
||||
) != Vector2.ZERO
|
||||
)
|
||||
var visuals: WorldTimeVisualController = main.get_node(
|
||||
"%WorldTimeVisualController"
|
||||
)
|
||||
var rain := visuals.get_node("LocalRain") as GPUParticles3D
|
||||
assert(rain.amount == WorldTimeVisualController.RAIN_PARTICLE_AMOUNT)
|
||||
assert(rain.fixed_fps == 30)
|
||||
|
||||
|
||||
func _validate_ocean_fishing_coverage(
|
||||
main: Node,
|
||||
ocean: MeshInstance3D,
|
||||
) -> void:
|
||||
var ocean_mesh := ocean.mesh as PlaneMesh
|
||||
assert(ocean_mesh != null)
|
||||
var shape_node := main.get_node(
|
||||
"TestWorld/Regions/StarterIslandRegion/WaterBodies/Ocean/"
|
||||
+ "FishingRegions/OceanFishingRegion/Shape"
|
||||
) as CollisionShape3D
|
||||
assert(shape_node != null)
|
||||
var fishing_shape := shape_node.shape as BoxShape3D
|
||||
assert(fishing_shape != null)
|
||||
assert(fishing_shape.size.is_equal_approx(Vector3(
|
||||
ocean_mesh.size.x,
|
||||
2.0,
|
||||
ocean_mesh.size.y,
|
||||
)))
|
||||
|
||||
|
||||
func _count_foliage_wind_overrides(root_node: Node) -> int:
|
||||
var count: int = 0
|
||||
var mesh_instance := root_node as MeshInstance3D
|
||||
if mesh_instance != null and mesh_instance.mesh != null:
|
||||
for surface_index: int in mesh_instance.mesh.get_surface_count():
|
||||
var material := mesh_instance.get_surface_override_material(
|
||||
surface_index
|
||||
) as ShaderMaterial
|
||||
if material != null and material.shader == FOLIAGE_WIND_SHADER:
|
||||
count += 1
|
||||
for child: Node in root_node.get_children():
|
||||
count += _count_foliage_wind_overrides(child)
|
||||
return count
|
||||
|
||||
|
||||
func _stop_audio_players(root_node: Node) -> void:
|
||||
if root_node is AudioStreamPlayer:
|
||||
(root_node as AudioStreamPlayer).stop()
|
||||
elif root_node is AudioStreamPlayer2D:
|
||||
(root_node as AudioStreamPlayer2D).stop()
|
||||
elif root_node is AudioStreamPlayer3D:
|
||||
(root_node as AudioStreamPlayer3D).stop()
|
||||
for child: Node in root_node.get_children():
|
||||
_stop_audio_players(child)
|
||||
|
||||
|
||||
func _validate_minimal_weather(main: Node) -> void:
|
||||
|
|
@ -79,6 +222,8 @@ func _validate_minimal_weather(main: Node) -> void:
|
|||
var rain := visuals.get_node("LocalRain") as GPUParticles3D
|
||||
assert(rain.amount == WorldTimeVisualController.LIGHT_RAIN_PARTICLE_AMOUNT)
|
||||
assert(rain.fixed_fps == WorldTimeVisualController.LIGHT_RAIN_FIXED_FPS)
|
||||
assert(rain.amount == 128)
|
||||
assert(rain.fixed_fps == 12)
|
||||
var clouds := visuals.get_node("LocalStormClouds") as LocalStormCloudLayer
|
||||
assert(clouds.get_patch_count() == 1)
|
||||
var ceiling := clouds.get_node("CloudCeiling") as MeshInstance3D
|
||||
|
|
|
|||
|
|
@ -122,7 +122,6 @@ const SHOP_ANIMALESE_VOICE_ID: String = "natural"
|
|||
const SHOP_ANIMALESE_BASE_PITCH: float = 1.08
|
||||
const SHOP_SPEECH_CHARACTERS_PER_SECOND: float = 28.0
|
||||
const SHOP_NPC_SPEECH_COOLDOWN_MILLISECONDS: int = 5000
|
||||
const ART_KIT_ITEM_ID: StringName = &"art_kit"
|
||||
|
||||
|
||||
@onready var _canonical_stage: Control = %CanonicalStage
|
||||
|
|
@ -612,20 +611,6 @@ func _on_emote_selected(emote_id: StringName) -> void:
|
|||
|
||||
func _on_quick_action_selected(action_id: StringName) -> void:
|
||||
match action_id:
|
||||
&"stuff":
|
||||
_player_menu.open_section(PlayerMenuType.Section.COOLER)
|
||||
&"logbook":
|
||||
_player_menu.open_section(PlayerMenuType.Section.LOGBOOK)
|
||||
&"fishnet":
|
||||
_player_menu.open_section(PlayerMenuType.Section.NET)
|
||||
&"mail":
|
||||
_player_menu.open_section(PlayerMenuType.Section.MAIL)
|
||||
&"profile":
|
||||
_player_menu.open_section(PlayerMenuType.Section.PROFILE)
|
||||
&"online":
|
||||
_player_menu.open_section(PlayerMenuType.Section.PLAYERS)
|
||||
&"paint":
|
||||
_select_art_kit_hotbar_slot()
|
||||
&"chat":
|
||||
_chat_ui.open_chat()
|
||||
&"freecam":
|
||||
|
|
@ -1116,15 +1101,6 @@ static func get_virtual_mouse_window_bounds(window_size: Vector2) -> Rect2:
|
|||
return Rect2(cursor_margin, bounds_size)
|
||||
|
||||
|
||||
func _select_art_kit_hotbar_slot() -> void:
|
||||
if _hotbar == null:
|
||||
return
|
||||
for slot_index: int in range(PlayerHotbarType.SLOT_COUNT):
|
||||
if _hotbar.get_item_id(slot_index) == ART_KIT_ITEM_ID:
|
||||
_hotbar.select_slot(slot_index)
|
||||
return
|
||||
|
||||
|
||||
func set_surface_drawing_hotbar_selected(is_selected: bool) -> void:
|
||||
_surface_drawing_hotbar_selected = is_selected
|
||||
_refresh_surface_drawing_activation()
|
||||
|
|
|
|||
|
|
@ -4,10 +4,14 @@ uniform sampler2D pattern_texture : source_color, repeat_enable, filter_nearest;
|
|||
uniform vec2 source_tile_size = vec2(128.0, 128.0);
|
||||
uniform float display_scale : hint_range(0.5, 2.0, 0.05) = 0.85;
|
||||
uniform vec2 scroll_velocity_pixels = vec2(-7.0, -5.0);
|
||||
uniform bool animation_enabled = true;
|
||||
|
||||
void fragment() {
|
||||
vec2 viewport_pixels = 1.0 / SCREEN_PIXEL_SIZE;
|
||||
vec2 display_tile_size = max(source_tile_size * display_scale, vec2(1.0));
|
||||
vec2 moving_pixels = UV * viewport_pixels + TIME * scroll_velocity_pixels;
|
||||
vec2 moving_pixels = UV * viewport_pixels;
|
||||
if (animation_enabled) {
|
||||
moving_pixels += TIME * scroll_velocity_pixels;
|
||||
}
|
||||
COLOR = texture(pattern_texture, moving_pixels / display_tile_size);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -16,30 +16,16 @@ const ControllerMappingManagerType = preload(
|
|||
"res://settings/controller_mapping_manager.gd"
|
||||
)
|
||||
const ACTIONS: Array[StringName] = [
|
||||
&"stuff",
|
||||
&"logbook",
|
||||
&"fishnet",
|
||||
&"mail",
|
||||
&"profile",
|
||||
&"online",
|
||||
&"paint",
|
||||
&"chat",
|
||||
&"freecam",
|
||||
&"hud",
|
||||
]
|
||||
const LABELS: Array[String] = [
|
||||
"stuff",
|
||||
"logbook",
|
||||
"fishnet",
|
||||
"mail",
|
||||
"profile",
|
||||
"online",
|
||||
"paint",
|
||||
"chat",
|
||||
"freecam",
|
||||
"hide hud",
|
||||
]
|
||||
const SECTOR_COUNT: int = 10
|
||||
const SECTOR_COUNT: int = 3
|
||||
|
||||
var _buttons: Array[BubbleButton] = []
|
||||
var _is_open: bool = false
|
||||
|
|
|
|||
|
|
@ -10,12 +10,15 @@ uniform float wave_speed : hint_range(0.0, 0.5, 0.01) = 0.10;
|
|||
uniform float wave_scale : hint_range(0.5, 8.0, 0.1) = 2.4;
|
||||
uniform float wave_strength : hint_range(0.0, 0.05, 0.001) = 0.012;
|
||||
uniform float caustic_strength : hint_range(0.0, 0.08, 0.001) = 0.018;
|
||||
uniform bool animation_enabled = true;
|
||||
|
||||
void fragment() {
|
||||
vec2 grid_size = max(virtual_pixel_density, vec2(1.0));
|
||||
vec2 pixel_uv = (floor(UV * grid_size) + vec2(0.5)) / grid_size;
|
||||
float continuous_depth = pixel_uv.y;
|
||||
float caustic_level = 0.0;
|
||||
if (animation_enabled) {
|
||||
float motion = TIME * wave_speed;
|
||||
|
||||
float broad_wave = sin(
|
||||
pixel_uv.x * wave_scale + motion
|
||||
);
|
||||
|
|
@ -28,12 +31,31 @@ void fragment() {
|
|||
* wave_strength
|
||||
* movement_falloff
|
||||
);
|
||||
float continuous_depth = clamp(
|
||||
continuous_depth = clamp(
|
||||
pixel_uv.y
|
||||
+ depth_displacement,
|
||||
0.0,
|
||||
1.0
|
||||
);
|
||||
float caustic_wave = clamp(
|
||||
(
|
||||
sin(
|
||||
pixel_uv.x * 4.0
|
||||
+ pixel_uv.y * 1.2
|
||||
+ motion * 0.7
|
||||
)
|
||||
* sin(
|
||||
pixel_uv.x * 1.7
|
||||
- pixel_uv.y * 2.2
|
||||
- motion * 0.55
|
||||
)
|
||||
- 0.25
|
||||
) / 0.75,
|
||||
0.0,
|
||||
1.0
|
||||
);
|
||||
caustic_level = floor(caustic_wave * 3.0) / 3.0;
|
||||
}
|
||||
float depth_steps = max(2.0, floor(color_step_count));
|
||||
float band_index = floor(
|
||||
continuous_depth * (depth_steps - 1.0) + 0.5
|
||||
|
|
@ -60,26 +82,6 @@ void fragment() {
|
|||
);
|
||||
}
|
||||
|
||||
float caustic_wave = clamp(
|
||||
(
|
||||
sin(
|
||||
pixel_uv.x * 4.0
|
||||
+ pixel_uv.y * 1.2
|
||||
+ motion * 0.7
|
||||
)
|
||||
* sin(
|
||||
pixel_uv.x * 1.7
|
||||
- pixel_uv.y * 2.2
|
||||
- motion * 0.55
|
||||
)
|
||||
- 0.25
|
||||
) / 0.75,
|
||||
0.0,
|
||||
1.0
|
||||
);
|
||||
float caustic_level = floor(
|
||||
caustic_wave * 3.0
|
||||
) / 3.0;
|
||||
float upper_water_mask = 1.0 - smoothstep(
|
||||
0.18,
|
||||
0.34,
|
||||
|
|
|
|||
|
|
@ -47,10 +47,13 @@ var fishing_shop_path: NodePath = (
|
|||
@export_range(0.0, 0.4, 0.005) var foliage_wind_strength: float = 0.36
|
||||
@export_range(0.0, 4.0, 0.05) var foliage_wind_speed: float = 0.9
|
||||
|
||||
var _foliage_wind_enabled: bool = false
|
||||
|
||||
|
||||
func _ready() -> void:
|
||||
if rebuild_terrain_collision_on_ready or not has_terrain_collision():
|
||||
_build_terrain_collision()
|
||||
_apply_foliage_wind()
|
||||
_set_foliage_wind_enabled(true)
|
||||
if Engine.is_editor_hint():
|
||||
update_configuration_warnings()
|
||||
|
||||
|
|
@ -69,6 +72,7 @@ func get_saltwater_shoreline_mesh() -> MeshInstance3D:
|
|||
|
||||
|
||||
func set_light_performance_profile(enabled: bool) -> void:
|
||||
_set_foliage_wind_enabled(not enabled)
|
||||
var pond := get_node_or_null(
|
||||
^"WaterBodies/Pond/VisualWater"
|
||||
) as MeshInstance3D
|
||||
|
|
@ -93,9 +97,10 @@ func set_light_performance_profile(enabled: bool) -> void:
|
|||
if enabled
|
||||
else NORMAL_SALT_WATER_MATERIAL
|
||||
)
|
||||
var surface_motion := ocean as WaterSurfaceMotion
|
||||
if surface_motion != null:
|
||||
surface_motion.set_motion_enabled(not enabled)
|
||||
|
||||
|
||||
func is_foliage_wind_enabled() -> bool:
|
||||
return _foliage_wind_enabled
|
||||
|
||||
|
||||
func _create_light_water_material(
|
||||
|
|
@ -254,14 +259,33 @@ func _collect_terrain_mesh_instances(root: Node) -> Array[MeshInstance3D]:
|
|||
return mesh_instances
|
||||
|
||||
|
||||
func _apply_foliage_wind() -> void:
|
||||
func _set_foliage_wind_enabled(enabled: bool) -> void:
|
||||
_foliage_wind_enabled = enabled
|
||||
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
|
||||
):
|
||||
if enabled:
|
||||
_apply_foliage_wind_to_mesh(mesh_instance)
|
||||
else:
|
||||
_remove_foliage_wind_from_mesh(mesh_instance)
|
||||
|
||||
|
||||
func _remove_foliage_wind_from_mesh(mesh_instance: MeshInstance3D) -> void:
|
||||
if mesh_instance.mesh == null:
|
||||
return
|
||||
for surface_index: int in mesh_instance.mesh.get_surface_count():
|
||||
var override_material: Material = (
|
||||
mesh_instance.get_surface_override_material(surface_index)
|
||||
)
|
||||
var shader_material := override_material as ShaderMaterial
|
||||
if (
|
||||
shader_material != null
|
||||
and shader_material.shader == FOLIAGE_WIND_SHADER
|
||||
):
|
||||
mesh_instance.set_surface_override_material(surface_index, null)
|
||||
|
||||
|
||||
func _apply_foliage_wind_to_mesh(mesh_instance: MeshInstance3D) -> void:
|
||||
|
|
|
|||
|
|
@ -12,8 +12,8 @@ const RAIN_VISIBILITY_AABB := AABB(
|
|||
Vector3(27.0, 12.0, 27.0),
|
||||
)
|
||||
const RAIN_PARTICLE_AMOUNT: int = 2240
|
||||
const LIGHT_RAIN_PARTICLE_AMOUNT: int = 256
|
||||
const LIGHT_RAIN_FIXED_FPS: int = 15
|
||||
const LIGHT_RAIN_PARTICLE_AMOUNT: int = 128
|
||||
const LIGHT_RAIN_FIXED_FPS: int = 12
|
||||
const RAIN_VELOCITY_MIN: float = 16.0
|
||||
const RAIN_VELOCITY_MAX: float = 20.0
|
||||
const RAIN_DROP_SIZE := Vector3(0.014, 0.34, 0.014)
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue