Refine character animation and fishing presentation
This commit is contained in:
parent
81f5010ad2
commit
c596b2aee7
15 changed files with 403 additions and 402 deletions
Binary file not shown.
Binary file not shown.
BIN
art/source/characters/base/netfishing_base_character.blend1
Normal file
BIN
art/source/characters/base/netfishing_base_character.blend1
Normal file
Binary file not shown.
|
|
@ -0,0 +1,45 @@
|
|||
[remap]
|
||||
|
||||
importer="scene"
|
||||
importer_version=1
|
||||
type="PackedScene"
|
||||
uid="uid://bfhnup14638r7"
|
||||
path="res://.godot/imported/netfishing_base_character.glb-cbdc33eaa4fcd1d2d2a8f4a463b228d6.scn"
|
||||
|
||||
[deps]
|
||||
|
||||
source_file="res://art/source/characters/base/netfishing_base_character.glb"
|
||||
dest_files=["res://.godot/imported/netfishing_base_character.glb-cbdc33eaa4fcd1d2d2a8f4a463b228d6.scn"]
|
||||
|
||||
[params]
|
||||
|
||||
nodes/root_type=""
|
||||
nodes/root_name=""
|
||||
nodes/root_script=null
|
||||
mesh_library/use_node_names_as_mesh_names=false
|
||||
array_mesh/deduplicate_surfaces=true
|
||||
nodes/apply_root_scale=true
|
||||
nodes/root_scale=1.0
|
||||
nodes/import_as_skeleton_bones=false
|
||||
nodes/use_name_suffixes=true
|
||||
nodes/use_node_type_suffixes=true
|
||||
meshes/ensure_tangents=true
|
||||
meshes/generate_lods=true
|
||||
meshes/create_shadow_meshes=true
|
||||
meshes/light_baking=1
|
||||
meshes/lightmap_texel_size=0.2
|
||||
meshes/force_disable_compression=false
|
||||
skins/use_named_skins=true
|
||||
animation/import=true
|
||||
animation/fps=30
|
||||
animation/trimming=false
|
||||
animation/remove_immutable_tracks=true
|
||||
animation/import_rest_as_RESET=false
|
||||
import_script/path=""
|
||||
materials/extract=0
|
||||
materials/extract_format=0
|
||||
materials/extract_path=""
|
||||
_subresources={}
|
||||
gltf/naming_version=2
|
||||
gltf/embedded_image_handling=1
|
||||
gltf/texture_map_mode=1
|
||||
|
|
@ -23,20 +23,10 @@ enum LineMode {
|
|||
@export_range(0.1, 10.0, 0.1) var pickup_distance_from_player: float = 5.5
|
||||
|
||||
@export_category("Fishing Line")
|
||||
@export_flags_3d_physics var line_obstacle_mask: int = 1
|
||||
@export_range(1.0, 30.0, 0.5) var routing_probe_height: float = 8.0
|
||||
@export_range(0.1, 2.0, 0.05) var obstruction_sample_spacing: float = 0.4
|
||||
@export_range(0.01, 1.0, 0.01) var terrain_clearance: float = 0.1
|
||||
@export_range(1, 3, 1) var maximum_obstruction_ranges: int = 1
|
||||
@export_range(2, 12, 1) var maximum_contact_anchors_per_range: int = 5
|
||||
@export_range(0.0, 3.0, 0.05) var route_lead_in_distance: float = 0.8
|
||||
@export_range(0.0, 3.0, 0.05) var route_lead_out_distance: float = 0.8
|
||||
@export_range(0.05, 2.0, 0.05) var fallback_arch_height: float = 0.35
|
||||
@export_range(0.0, 0.5, 0.01) var slack_amount: float = 0.08
|
||||
@export_range(0.0, 2.0, 0.01) var minimum_slack: float = 0.08
|
||||
@export_range(0.0, 3.0, 0.01) var maximum_slack: float = 0.65
|
||||
@export_range(2, 20, 1) var clear_span_interpolation_count: int = 8
|
||||
@export_range(1, 8, 1) var contact_span_interpolation_count: int = 2
|
||||
@export_range(2, 20, 1) var sag_interpolation_count: int = 8
|
||||
@export_range(0.1, 30.0, 0.1) var line_transition_speed: float = 7.0
|
||||
|
||||
@export_category("Cast")
|
||||
|
|
@ -59,9 +49,6 @@ enum LineMode {
|
|||
var _mode: VisualMode = VisualMode.NONE
|
||||
var _line_mode: LineMode = LineMode.HIDDEN
|
||||
var _line_mesh: ImmediateMesh = ImmediateMesh.new()
|
||||
var _route_points: PackedVector3Array = PackedVector3Array()
|
||||
var _contact_start_anchor_index: int = -1
|
||||
var _contact_end_anchor_index: int = -1
|
||||
var _current_sag: float = 0.0
|
||||
var _rod: Node3D
|
||||
var _rod_tip: Marker3D
|
||||
|
|
@ -109,9 +96,6 @@ func set_line_mode(mode: LineMode) -> void:
|
|||
_line.visible = mode != LineMode.HIDDEN
|
||||
if mode == LineMode.HIDDEN:
|
||||
_current_sag = 0.0
|
||||
_route_points.clear()
|
||||
_contact_start_anchor_index = -1
|
||||
_contact_end_anchor_index = -1
|
||||
_line_mesh.clear_surfaces()
|
||||
|
||||
|
||||
|
|
@ -399,10 +383,10 @@ func _update_line(delta: float) -> void:
|
|||
)
|
||||
var sag_weight: float = 1.0 - exp(-line_transition_speed * delta)
|
||||
_current_sag = lerpf(_current_sag, target_sag, sag_weight)
|
||||
_route_points = _build_line_route(rod_tip_position, bobber_position)
|
||||
var rendered_points: PackedVector3Array = _build_rendered_line_points(
|
||||
_route_points,
|
||||
_current_sag
|
||||
var rendered_points: PackedVector3Array = _build_sagged_line_points(
|
||||
rod_tip_position,
|
||||
bobber_position,
|
||||
_current_sag,
|
||||
)
|
||||
|
||||
_line_mesh.clear_surfaces()
|
||||
|
|
@ -415,307 +399,21 @@ func _update_line(delta: float) -> void:
|
|||
_line_mesh.surface_end()
|
||||
|
||||
|
||||
func _build_line_route(start: Vector3, end: Vector3) -> PackedVector3Array:
|
||||
_contact_start_anchor_index = -1
|
||||
_contact_end_anchor_index = -1
|
||||
if _raycast_obstacle(start, end).is_empty():
|
||||
return PackedVector3Array([start, end])
|
||||
|
||||
var horizontal_delta := Vector2(end.x - start.x, end.z - start.z)
|
||||
var horizontal_length: float = horizontal_delta.length()
|
||||
if horizontal_length <= 0.001:
|
||||
return _build_fallback_arch(start, end)
|
||||
|
||||
var sample_count: int = clampi(
|
||||
ceili(horizontal_length / obstruction_sample_spacing),
|
||||
2,
|
||||
64
|
||||
)
|
||||
var obstructed_samples := PackedByteArray()
|
||||
for sample_index: int in range(sample_count + 1):
|
||||
var progress: float = float(sample_index) / float(sample_count)
|
||||
var base_point: Vector3 = start.lerp(end, progress)
|
||||
var surface_hit: Dictionary = _probe_surface(base_point)
|
||||
var is_obstructed: bool = false
|
||||
if not surface_hit.is_empty():
|
||||
var surface_position: Vector3 = surface_hit["position"]
|
||||
var required_height: float = surface_position.y + terrain_clearance
|
||||
if required_height > base_point.y:
|
||||
is_obstructed = true
|
||||
obstructed_samples.append(1 if is_obstructed else 0)
|
||||
|
||||
var obstruction_ranges: Array[Vector2i] = _find_obstruction_ranges(
|
||||
obstructed_samples
|
||||
)
|
||||
if obstruction_ranges.is_empty():
|
||||
return _build_fallback_arch(start, end)
|
||||
if obstruction_ranges.size() > maximum_obstruction_ranges:
|
||||
return _build_fallback_arch(start, end)
|
||||
|
||||
var first_sample: int = obstruction_ranges[0].x
|
||||
var last_sample: int = obstruction_ranges[obstruction_ranges.size() - 1].y
|
||||
var first_progress: float = float(first_sample) / float(sample_count)
|
||||
var last_progress: float = float(last_sample) / float(sample_count)
|
||||
var lead_in_progress: float = maxf(
|
||||
0.0,
|
||||
first_progress - route_lead_in_distance / horizontal_length
|
||||
)
|
||||
var lead_out_progress: float = minf(
|
||||
1.0,
|
||||
last_progress + route_lead_out_distance / horizontal_length
|
||||
)
|
||||
|
||||
var route := PackedVector3Array([start])
|
||||
_append_unique_route_point(route, start.lerp(end, lead_in_progress))
|
||||
_contact_start_anchor_index = route.size() - 1
|
||||
_append_contact_anchors(
|
||||
route,
|
||||
start,
|
||||
end,
|
||||
first_sample,
|
||||
last_sample,
|
||||
sample_count
|
||||
)
|
||||
_append_unique_route_point(route, start.lerp(end, lead_out_progress))
|
||||
_contact_end_anchor_index = route.size() - 1
|
||||
_append_unique_route_point(route, end)
|
||||
route = _remove_near_duplicate_route_points(route)
|
||||
_update_contact_indices_after_reduction(route)
|
||||
return _validate_reduced_route(route, start, end)
|
||||
|
||||
|
||||
func _build_rendered_line_points(
|
||||
route: PackedVector3Array,
|
||||
func _build_sagged_line_points(
|
||||
start: Vector3,
|
||||
end: Vector3,
|
||||
sag: float,
|
||||
) -> PackedVector3Array:
|
||||
var points := PackedVector3Array()
|
||||
if route.is_empty():
|
||||
return points
|
||||
if (
|
||||
_contact_start_anchor_index < 0
|
||||
or _contact_end_anchor_index < _contact_start_anchor_index
|
||||
or _contact_end_anchor_index >= route.size()
|
||||
):
|
||||
_append_clear_span(points, route[0], route[route.size() - 1], sag)
|
||||
return points
|
||||
|
||||
_append_clear_span(
|
||||
points,
|
||||
route[0],
|
||||
route[_contact_start_anchor_index],
|
||||
sag
|
||||
)
|
||||
for anchor_index: int in range(
|
||||
_contact_start_anchor_index,
|
||||
_contact_end_anchor_index
|
||||
):
|
||||
_append_contact_span(
|
||||
points,
|
||||
route[anchor_index],
|
||||
route[anchor_index + 1]
|
||||
)
|
||||
_append_clear_span(
|
||||
points,
|
||||
route[_contact_end_anchor_index],
|
||||
route[route.size() - 1],
|
||||
sag
|
||||
)
|
||||
return points
|
||||
|
||||
|
||||
func _raycast_obstacle(from: Vector3, to: Vector3) -> Dictionary:
|
||||
if from.is_equal_approx(to):
|
||||
return {}
|
||||
var query := PhysicsRayQueryParameters3D.create(
|
||||
from,
|
||||
to,
|
||||
line_obstacle_mask
|
||||
)
|
||||
query.collide_with_areas = false
|
||||
query.collide_with_bodies = true
|
||||
return get_world_3d().direct_space_state.intersect_ray(query)
|
||||
|
||||
|
||||
func _find_obstruction_ranges(
|
||||
obstructed_samples: PackedByteArray,
|
||||
) -> Array[Vector2i]:
|
||||
var ranges: Array[Vector2i] = []
|
||||
var range_start: int = -1
|
||||
for sample_index: int in range(obstructed_samples.size()):
|
||||
var is_obstructed: bool = obstructed_samples[sample_index] != 0
|
||||
if is_obstructed and range_start < 0:
|
||||
range_start = sample_index
|
||||
elif not is_obstructed and range_start >= 0:
|
||||
ranges.append(Vector2i(range_start, sample_index - 1))
|
||||
range_start = -1
|
||||
if range_start >= 0:
|
||||
ranges.append(Vector2i(range_start, obstructed_samples.size() - 1))
|
||||
return ranges
|
||||
|
||||
|
||||
func _append_contact_anchors(
|
||||
route: PackedVector3Array,
|
||||
start: Vector3,
|
||||
end: Vector3,
|
||||
first_sample: int,
|
||||
last_sample: int,
|
||||
sample_count: int,
|
||||
) -> void:
|
||||
var available_samples: int = last_sample - first_sample + 1
|
||||
var anchor_count: int = mini(
|
||||
available_samples,
|
||||
maximum_contact_anchors_per_range
|
||||
)
|
||||
for anchor_offset: int in range(anchor_count):
|
||||
var weight: float = 0.0
|
||||
if anchor_count > 1:
|
||||
weight = float(anchor_offset) / float(anchor_count - 1)
|
||||
var sample_index: int = roundi(
|
||||
lerpf(float(first_sample), float(last_sample), weight)
|
||||
)
|
||||
var progress: float = float(sample_index) / float(sample_count)
|
||||
var contact: Vector3 = _raise_point_above_obstacle(
|
||||
start.lerp(end, progress)
|
||||
)
|
||||
_append_unique_route_point(route, contact)
|
||||
|
||||
|
||||
func _append_unique_route_point(
|
||||
route: PackedVector3Array,
|
||||
point: Vector3,
|
||||
) -> void:
|
||||
if route.is_empty() or point.distance_to(route[route.size() - 1]) > 0.03:
|
||||
route.append(point)
|
||||
|
||||
|
||||
func _remove_near_duplicate_route_points(
|
||||
route: PackedVector3Array,
|
||||
) -> PackedVector3Array:
|
||||
var cleaned := PackedVector3Array()
|
||||
for point: Vector3 in route:
|
||||
_append_unique_route_point(cleaned, point)
|
||||
return cleaned
|
||||
|
||||
|
||||
func _update_contact_indices_after_reduction(route: PackedVector3Array) -> void:
|
||||
if route.size() <= 2:
|
||||
_contact_start_anchor_index = -1
|
||||
_contact_end_anchor_index = -1
|
||||
return
|
||||
_contact_start_anchor_index = 1
|
||||
_contact_end_anchor_index = route.size() - 2
|
||||
|
||||
|
||||
func _validate_reduced_route(
|
||||
route: PackedVector3Array,
|
||||
start: Vector3,
|
||||
end: Vector3,
|
||||
) -> PackedVector3Array:
|
||||
var correction_index: int = -1
|
||||
var correction_hit: Dictionary
|
||||
for segment_index: int in range(route.size() - 1):
|
||||
var hit: Dictionary = _raycast_obstacle(
|
||||
route[segment_index],
|
||||
route[segment_index + 1]
|
||||
)
|
||||
if not hit.is_empty():
|
||||
correction_index = segment_index + 1
|
||||
correction_hit = hit
|
||||
break
|
||||
if correction_index < 0:
|
||||
return route
|
||||
|
||||
var hit_position: Vector3 = correction_hit["position"]
|
||||
var correction: Vector3 = _raise_point_above_obstacle(hit_position)
|
||||
route.insert(correction_index, correction)
|
||||
_contact_start_anchor_index = mini(
|
||||
_contact_start_anchor_index,
|
||||
correction_index
|
||||
)
|
||||
_contact_end_anchor_index = maxi(
|
||||
_contact_end_anchor_index + 1,
|
||||
correction_index
|
||||
)
|
||||
for segment_index: int in range(route.size() - 1):
|
||||
if not _raycast_obstacle(
|
||||
route[segment_index],
|
||||
route[segment_index + 1]
|
||||
).is_empty():
|
||||
return _build_fallback_arch(start, end)
|
||||
return route
|
||||
|
||||
|
||||
func _append_clear_span(
|
||||
points: PackedVector3Array,
|
||||
start: Vector3,
|
||||
end: Vector3,
|
||||
sag: float,
|
||||
) -> void:
|
||||
if points.is_empty():
|
||||
points.append(start)
|
||||
elif not points[points.size() - 1].is_equal_approx(start):
|
||||
points.append(start)
|
||||
var subdivisions: int = 1 if sag <= 0.001 else clear_span_interpolation_count
|
||||
var subdivisions: int = 1 if sag <= 0.001 else sag_interpolation_count
|
||||
points.append(start)
|
||||
for step: int in range(1, subdivisions + 1):
|
||||
var progress: float = float(step) / float(subdivisions)
|
||||
var point: Vector3 = start.lerp(end, progress)
|
||||
if step < subdivisions and sag > 0.0:
|
||||
point.y -= 4.0 * progress * (1.0 - progress) * sag
|
||||
point.y = maxf(
|
||||
point.y,
|
||||
get_water_surface_height() + terrain_clearance * 0.25
|
||||
)
|
||||
point = _raise_point_above_obstacle(point)
|
||||
points.append(point)
|
||||
|
||||
|
||||
func _append_contact_span(
|
||||
points: PackedVector3Array,
|
||||
start: Vector3,
|
||||
end: Vector3,
|
||||
) -> void:
|
||||
if points.is_empty():
|
||||
points.append(start)
|
||||
elif not points[points.size() - 1].is_equal_approx(start):
|
||||
points.append(start)
|
||||
for step: int in range(1, contact_span_interpolation_count + 1):
|
||||
var progress: float = (
|
||||
float(step) / float(contact_span_interpolation_count)
|
||||
)
|
||||
var point: Vector3 = start.lerp(end, progress)
|
||||
if step < contact_span_interpolation_count:
|
||||
point = _raise_point_above_obstacle(point)
|
||||
points.append(point)
|
||||
|
||||
|
||||
func _build_fallback_arch(
|
||||
start: Vector3,
|
||||
end: Vector3,
|
||||
) -> PackedVector3Array:
|
||||
var route := PackedVector3Array([start])
|
||||
for progress: float in [0.25, 0.5, 0.75]:
|
||||
var point: Vector3 = start.lerp(end, progress)
|
||||
point.y += sin(progress * PI) * fallback_arch_height
|
||||
route.append(_raise_point_above_obstacle(point))
|
||||
route.append(end)
|
||||
_contact_start_anchor_index = 1
|
||||
_contact_end_anchor_index = route.size() - 2
|
||||
return route
|
||||
|
||||
|
||||
func _probe_surface(point: Vector3) -> Dictionary:
|
||||
var probe_start: Vector3 = point + Vector3.UP * routing_probe_height
|
||||
var probe_end: Vector3 = point - Vector3.UP * routing_probe_height
|
||||
return _raycast_obstacle(probe_start, probe_end)
|
||||
|
||||
|
||||
func _raise_point_above_obstacle(point: Vector3) -> Vector3:
|
||||
var hit: Dictionary = _probe_surface(point)
|
||||
if hit.is_empty():
|
||||
return point
|
||||
var surface_position: Vector3 = hit["position"]
|
||||
point.y = maxf(point.y, surface_position.y + terrain_clearance)
|
||||
return point
|
||||
return points
|
||||
|
||||
|
||||
func _begin_rod_release() -> void:
|
||||
|
|
|
|||
|
|
@ -909,7 +909,6 @@ func _on_outcome_completed(outcome: StringName) -> void:
|
|||
_pending_catch.weight_lb,
|
||||
true
|
||||
)
|
||||
status_changed.emit("left click or escape to put away")
|
||||
|
||||
|
||||
func _put_away_catch() -> void:
|
||||
|
|
@ -921,7 +920,6 @@ func _put_away_catch() -> void:
|
|||
or _pending_catch == null
|
||||
):
|
||||
return
|
||||
var caught_name: String = _pending_catch.fish.display_name
|
||||
_local_inventory.add_catch(_pending_catch)
|
||||
_local_collection_log.mark_discovered(_pending_catch.fish_id)
|
||||
_pending_catch = null
|
||||
|
|
@ -934,7 +932,7 @@ func _put_away_catch() -> void:
|
|||
_active_player.end_catch_showcase(
|
||||
_finish_showcase_put_away.bind(
|
||||
restore_generation,
|
||||
"caught %s!" % caught_name
|
||||
""
|
||||
)
|
||||
)
|
||||
|
||||
|
|
@ -1018,9 +1016,9 @@ func _return_to_ready() -> void:
|
|||
|
||||
func _cancel_attempt() -> void:
|
||||
if _network_fishing != null and _network_fishing.has_local_attempt():
|
||||
_network_fishing.cancel_local_attempt("Fishing cancelled.")
|
||||
_network_fishing.cancel_local_attempt("")
|
||||
return
|
||||
_cleanup_attempt("fishing cancelled.", &"cancel")
|
||||
_cleanup_attempt("", &"cancel")
|
||||
|
||||
|
||||
func _cancel_from_withdrawal() -> void:
|
||||
|
|
@ -1284,7 +1282,7 @@ func _on_network_attempt_ended(
|
|||
]:
|
||||
return
|
||||
_cleanup_attempt(
|
||||
message if not message.is_empty() else "Fishing attempt ended.",
|
||||
message,
|
||||
&"escape" if outcome == &"escape" else &"cancel"
|
||||
)
|
||||
|
||||
|
|
|
|||
|
|
@ -320,7 +320,7 @@ func _update_waiting_attempt(
|
|||
flat_offset.length() - _fishing_spot.withdrawal_cancel_distance
|
||||
)
|
||||
if withdrawable_distance <= 0.0:
|
||||
_cancel_attempt(attempt.owner_peer_id, "Fishing cancelled.")
|
||||
_cancel_attempt(attempt.owner_peer_id, "")
|
||||
return
|
||||
attempt.withdrawal_progress = minf(
|
||||
attempt.withdrawal_progress
|
||||
|
|
@ -463,7 +463,7 @@ func submit_cancel_request(attempt_id: String) -> void:
|
|||
or attempt.attempt_id != attempt_id
|
||||
):
|
||||
return
|
||||
_cancel_attempt(sender_id, "Fishing cancelled.")
|
||||
_cancel_attempt(sender_id, "")
|
||||
|
||||
|
||||
func _on_encounter_updated(
|
||||
|
|
|
|||
|
|
@ -18,6 +18,10 @@ const PlayerCoolerCapacityType = preload(
|
|||
"res://progression/player_cooler_capacity.gd"
|
||||
)
|
||||
|
||||
const CHARACTER_IDLE_ANIMATION: StringName = &"idle"
|
||||
const CHARACTER_WALKING_ANIMATION: StringName = &"walking"
|
||||
const CHARACTER_SITTING_ANIMATION: StringName = &"sitting"
|
||||
|
||||
var appearance_snapshot: Dictionary = (
|
||||
CharacterCustomizationCatalog.default_snapshot()
|
||||
)
|
||||
|
|
@ -81,6 +85,9 @@ class ShowcaseCameraSnapshot:
|
|||
@export var zoom_smoothing: float = 12.0
|
||||
|
||||
@onready var _visuals: Node3D = %Visuals
|
||||
@onready var _character_animation_player: AnimationPlayer = (
|
||||
get_node_or_null("Visuals/CharacterRig/AnimationPlayer") as AnimationPlayer
|
||||
)
|
||||
@onready var _camera_yaw: Node3D = %CameraYaw
|
||||
@onready var _camera_pitch: Node3D = %CameraPitch
|
||||
@onready var _spring_arm: SpringArm3D = %SpringArm3D
|
||||
|
|
@ -133,6 +140,8 @@ var _network_target_position: Vector3
|
|||
var _network_target_velocity: Vector3
|
||||
var _network_target_visual_yaw: float = 0.0
|
||||
var _network_snapshot_ready: bool = false
|
||||
var _character_animation_name: StringName = &""
|
||||
var _sitting: bool = false
|
||||
|
||||
|
||||
func _ready() -> void:
|
||||
|
|
@ -145,6 +154,9 @@ func _physics_process(delta: float) -> void:
|
|||
if _network_interpolation_enabled:
|
||||
_update_network_interpolation(delta)
|
||||
return
|
||||
if _sitting:
|
||||
velocity = Vector3.ZERO
|
||||
return
|
||||
if _water_recovery_active:
|
||||
velocity = Vector3.ZERO
|
||||
return
|
||||
|
|
@ -215,6 +227,7 @@ func _physics_process(delta: float) -> void:
|
|||
|
||||
|
||||
func _process(delta: float) -> void:
|
||||
_update_character_animation()
|
||||
if _remote_recovery_presentation_active:
|
||||
_remote_recovery_elapsed += delta
|
||||
_visuals.position = (
|
||||
|
|
@ -258,6 +271,48 @@ func _process(delta: float) -> void:
|
|||
)
|
||||
|
||||
|
||||
func _update_character_animation() -> void:
|
||||
if _character_animation_player == null:
|
||||
return
|
||||
var horizontal_speed_squared: float = (
|
||||
velocity.x * velocity.x + velocity.z * velocity.z
|
||||
)
|
||||
var next_animation: StringName = (
|
||||
CHARACTER_SITTING_ANIMATION
|
||||
if _sitting
|
||||
else (
|
||||
CHARACTER_WALKING_ANIMATION
|
||||
if horizontal_speed_squared > 0.0025
|
||||
else CHARACTER_IDLE_ANIMATION
|
||||
)
|
||||
)
|
||||
if not _character_animation_player.has_animation(next_animation):
|
||||
return
|
||||
if _character_animation_name == next_animation:
|
||||
return
|
||||
_character_animation_player.play(next_animation)
|
||||
_character_animation_name = next_animation
|
||||
|
||||
|
||||
func toggle_sitting() -> void:
|
||||
if (
|
||||
_character_animation_player == null
|
||||
or not _character_animation_player.has_animation(
|
||||
CHARACTER_SITTING_ANIMATION
|
||||
)
|
||||
):
|
||||
return
|
||||
_sitting = not _sitting
|
||||
if _sitting:
|
||||
velocity = Vector3.ZERO
|
||||
_character_animation_name = &""
|
||||
_update_character_animation()
|
||||
|
||||
|
||||
func is_sitting() -> bool:
|
||||
return _sitting
|
||||
|
||||
|
||||
func _unhandled_input(event: InputEvent) -> void:
|
||||
if not local_control_enabled or not _camera_input_enabled:
|
||||
return
|
||||
|
|
|
|||
|
|
@ -63,21 +63,24 @@ cast_shadow = 0
|
|||
mesh = SubResource("GroundShadowMesh")
|
||||
|
||||
[node name="CharacterRig" parent="Visuals" instance=ExtResource("13_character")]
|
||||
rotation_degrees = Vector3(0, 180, 0)
|
||||
|
||||
[node name="AnimationPlayer" parent="Visuals/CharacterRig"]
|
||||
autoplay = "idle"
|
||||
|
||||
[node name="FishingRod" type="Node3D" parent="Visuals"]
|
||||
unique_name_in_owner = true
|
||||
position = Vector3(0.16, 0.38, 0.02)
|
||||
rotation = Vector3(-0.785398, 0, 0)
|
||||
[node name="FishingRodAttachment" type="BoneAttachment3D" parent="Visuals/CharacterRig/CharacterRig/Skeleton3D"]
|
||||
bone_name = "hand.R"
|
||||
|
||||
[node name="RodMesh" type="MeshInstance3D" parent="Visuals/FishingRod"]
|
||||
[node name="FishingRod" type="Node3D" parent="Visuals/CharacterRig/CharacterRig/Skeleton3D/FishingRodAttachment"]
|
||||
unique_name_in_owner = true
|
||||
rotation = Vector3(-0.785398, -0.007863, 3.141593)
|
||||
|
||||
[node name="RodMesh" type="MeshInstance3D" parent="Visuals/CharacterRig/CharacterRig/Skeleton3D/FishingRodAttachment/FishingRod"]
|
||||
position = Vector3(0, 0.42, 0)
|
||||
mesh = SubResource("FishingRodMesh")
|
||||
material_override = SubResource("FishingRodMaterial")
|
||||
|
||||
[node name="FishingRodTip" type="Marker3D" parent="Visuals/FishingRod"]
|
||||
[node name="FishingRodTip" type="Marker3D" parent="Visuals/CharacterRig/CharacterRig/Skeleton3D/FishingRodAttachment/FishingRod"]
|
||||
unique_name_in_owner = true
|
||||
position = Vector3(0, 0.95, 0)
|
||||
|
||||
|
|
|
|||
|
|
@ -7,12 +7,25 @@ const PlayerScene: PackedScene = preload("res://player/player.tscn")
|
|||
static func instantiate_visuals() -> Node3D:
|
||||
var source: Player = PlayerScene.instantiate()
|
||||
var visuals := source.get_node("Visuals") as Node3D
|
||||
var source_rod_attachment: Node = visuals.find_child(
|
||||
"FishingRodAttachment", true, false
|
||||
)
|
||||
var source_rod_parent: Node = null
|
||||
var source_rod_index: int = -1
|
||||
if source_rod_attachment != null:
|
||||
source_rod_parent = source_rod_attachment.get_parent()
|
||||
source_rod_index = source_rod_attachment.get_index()
|
||||
source_rod_attachment.owner = null
|
||||
source_rod_parent.remove_child(source_rod_attachment)
|
||||
var presentation := visuals.duplicate(
|
||||
Node.DUPLICATE_SIGNALS
|
||||
| Node.DUPLICATE_GROUPS
|
||||
| Node.DUPLICATE_SCRIPTS
|
||||
| Node.DUPLICATE_USE_INSTANTIATION
|
||||
) as Node3D
|
||||
if source_rod_attachment != null and source_rod_parent != null:
|
||||
source_rod_parent.add_child(source_rod_attachment)
|
||||
source_rod_parent.move_child(source_rod_attachment, source_rod_index)
|
||||
source.free()
|
||||
presentation.name = "PlayerVisuals"
|
||||
return presentation
|
||||
|
|
|
|||
110
ui/emote_radial_menu.gd
Normal file
110
ui/emote_radial_menu.gd
Normal file
|
|
@ -0,0 +1,110 @@
|
|||
class_name EmoteRadialMenu
|
||||
extends Control
|
||||
|
||||
signal emote_selected(emote_id: StringName)
|
||||
|
||||
const BubbleButtonScene: PackedScene = preload(
|
||||
"res://ui/components/bubble_menu/bubble_button.tscn"
|
||||
)
|
||||
const BubbleProfile: BubbleMenuProfile = preload(
|
||||
"res://ui/components/bubble_menu/bubble_menu_profile.tres"
|
||||
)
|
||||
const SECTOR_COUNT: int = 8
|
||||
const SIT_SECTOR: int = 0
|
||||
const RING_RADIUS: float = 172.0
|
||||
const BUBBLE_SIZE: Vector2 = Vector2(92.0, 88.0)
|
||||
|
||||
var _buttons: Array[BubbleButton] = []
|
||||
var _is_open: bool = false
|
||||
var _selected_sector: int = SIT_SECTOR
|
||||
|
||||
|
||||
func _ready() -> void:
|
||||
visible = false
|
||||
mouse_filter = Control.MOUSE_FILTER_IGNORE
|
||||
for sector: int in SECTOR_COUNT:
|
||||
var bubble: BubbleButton = BubbleButtonScene.instantiate() as BubbleButton
|
||||
bubble.profile = BubbleProfile
|
||||
bubble.focus_mode = Control.FOCUS_NONE
|
||||
bubble.mouse_filter = Control.MOUSE_FILTER_IGNORE
|
||||
bubble.text = "Sit" if sector == SIT_SECTOR else ""
|
||||
add_child(bubble)
|
||||
_buttons.append(bubble)
|
||||
_apply_selection_styles()
|
||||
|
||||
|
||||
func handle_input(event: InputEvent, can_open: bool) -> bool:
|
||||
var key_event: InputEventKey = event as InputEventKey
|
||||
if key_event == null or key_event.echo:
|
||||
return false
|
||||
if key_event.physical_keycode != KEY_C:
|
||||
return false
|
||||
if key_event.pressed:
|
||||
if _is_open or not can_open:
|
||||
return _is_open
|
||||
open_menu()
|
||||
return true
|
||||
if not _is_open:
|
||||
return false
|
||||
var selected: int = _selected_sector
|
||||
close_menu()
|
||||
if selected == SIT_SECTOR:
|
||||
emote_selected.emit(&"sit")
|
||||
return true
|
||||
|
||||
|
||||
func open_menu() -> void:
|
||||
_is_open = true
|
||||
_selected_sector = SIT_SECTOR
|
||||
visible = true
|
||||
_layout_bubbles()
|
||||
_apply_selection_styles()
|
||||
|
||||
|
||||
func close_menu() -> void:
|
||||
_is_open = false
|
||||
visible = false
|
||||
|
||||
|
||||
func is_open() -> bool:
|
||||
return _is_open
|
||||
|
||||
|
||||
func _process(_delta: float) -> void:
|
||||
if not _is_open:
|
||||
return
|
||||
_layout_bubbles()
|
||||
_update_mouse_selection()
|
||||
|
||||
|
||||
func _layout_bubbles() -> void:
|
||||
var center: Vector2 = size * 0.5
|
||||
for sector: int in SECTOR_COUNT:
|
||||
var angle: float = -PI * 0.5 + TAU * float(sector) / float(SECTOR_COUNT)
|
||||
var bubble_center: Vector2 = center + Vector2.from_angle(angle) * RING_RADIUS
|
||||
var bubble: BubbleButton = _buttons[sector]
|
||||
bubble.position = bubble_center - BUBBLE_SIZE * 0.5
|
||||
bubble.size = BUBBLE_SIZE
|
||||
bubble.pivot_offset = BUBBLE_SIZE * 0.5
|
||||
|
||||
|
||||
func _update_mouse_selection() -> void:
|
||||
var offset: Vector2 = get_local_mouse_position() - size * 0.5
|
||||
if offset.length() < 24.0:
|
||||
return
|
||||
var angle: float = fposmod(offset.angle() + PI * 0.5 + PI / 8.0, TAU)
|
||||
var sector: int = int(floor(angle / (TAU / float(SECTOR_COUNT))))
|
||||
if sector == _selected_sector:
|
||||
return
|
||||
_selected_sector = sector
|
||||
_apply_selection_styles()
|
||||
|
||||
|
||||
func _apply_selection_styles() -> void:
|
||||
for sector: int in _buttons.size():
|
||||
var bubble: BubbleButton = _buttons[sector]
|
||||
bubble.apply_profile()
|
||||
if sector == _selected_sector:
|
||||
bubble.add_theme_stylebox_override(
|
||||
"normal", BubbleProfile.make_hover_style()
|
||||
)
|
||||
1
ui/emote_radial_menu.gd.uid
Normal file
1
ui/emote_radial_menu.gd.uid
Normal file
|
|
@ -0,0 +1 @@
|
|||
uid://d3emoteradialmenu
|
||||
14
ui/emote_radial_menu.tscn
Normal file
14
ui/emote_radial_menu.tscn
Normal file
|
|
@ -0,0 +1,14 @@
|
|||
[gd_scene load_steps=2 format=3]
|
||||
|
||||
[ext_resource type="Script" path="res://ui/emote_radial_menu.gd" id="1_emote"]
|
||||
|
||||
[node name="EmoteRadialMenu" type="Control"]
|
||||
layout_mode = 3
|
||||
anchors_preset = 15
|
||||
anchor_right = 1.0
|
||||
anchor_bottom = 1.0
|
||||
grow_horizontal = 2
|
||||
grow_vertical = 2
|
||||
mouse_filter = 2
|
||||
z_index = 90
|
||||
script = ExtResource("1_emote")
|
||||
101
ui/game_ui.gd
101
ui/game_ui.gd
|
|
@ -32,6 +32,7 @@ const PlayerCoolerCapacityType = preload(
|
|||
"res://progression/player_cooler_capacity.gd"
|
||||
)
|
||||
const ChatUIType = preload("res://ui/chat_ui.gd")
|
||||
const EmoteRadialMenuType = preload("res://ui/emote_radial_menu.gd")
|
||||
const PlayerSettingsManagerType = preload(
|
||||
"res://settings/player_settings_manager.gd"
|
||||
)
|
||||
|
|
@ -49,7 +50,8 @@ signal shop_backdrop_visibility_changed(is_visible: bool)
|
|||
@onready var _green_catch_progress: ProgressBar = %GreenCatchProgress
|
||||
@onready var _red_chase_progress: ProgressBar = %RedChaseProgress
|
||||
@onready var _barrier_markers: Control = %BarrierMarkers
|
||||
@onready var _barrier_summary: Label = %BarrierSummary
|
||||
@onready var _barrier_prompt_panel: PanelContainer = %BarrierPromptPanel
|
||||
@onready var _barrier_prompt: Label = %BarrierPrompt
|
||||
@onready var _barrier_health: Label = %BarrierHealth
|
||||
@onready var _showcase_details: Label = %ShowcaseDetails
|
||||
@onready var _fishing_panel: PanelContainer = %FishingPanel
|
||||
|
|
@ -62,6 +64,7 @@ signal shop_backdrop_visibility_changed(is_visible: bool)
|
|||
@onready var _shop_prompt: PanelContainer = %ShopPrompt
|
||||
@onready var _effect_status: Label = %EffectStatus
|
||||
@onready var _chat_ui: ChatUIType = %ChatUI
|
||||
@onready var _emote_radial_menu: EmoteRadialMenuType = %EmoteRadialMenu
|
||||
@onready var _title_settings_panel: SettingsPanelType = (
|
||||
$UIRoot/TitleScreen/ResponsiveTitleStage/TitlePresentationScaleRoot/SettingsPanel
|
||||
)
|
||||
|
|
@ -70,6 +73,7 @@ signal shop_backdrop_visibility_changed(is_visible: bool)
|
|||
)
|
||||
|
||||
var _showcase_active: bool = false
|
||||
var _player: PlayerType
|
||||
var _player_menu_open: bool = false
|
||||
var _gameplay_ui_enabled: bool = false
|
||||
var _fishing_spot: FishingSpotType
|
||||
|
|
@ -83,6 +87,7 @@ var _shop_interaction: ShopInteractionType
|
|||
|
||||
|
||||
func _ready() -> void:
|
||||
_emote_radial_menu.emote_selected.connect(_on_emote_selected)
|
||||
_chat_ui.text_entry_ownership_changed.connect(
|
||||
_on_chat_text_entry_ownership_changed
|
||||
)
|
||||
|
|
@ -132,6 +137,7 @@ func setup(
|
|||
network_player_list: NetworkPlayerListService,
|
||||
settings_manager: PlayerSettingsManagerType,
|
||||
) -> void:
|
||||
_player = player
|
||||
_fishing_spot = fishing_spot
|
||||
_item_effects = item_effects
|
||||
_chat_ui.setup(
|
||||
|
|
@ -202,6 +208,26 @@ func setup(
|
|||
_shop_interaction = shop_interaction
|
||||
|
||||
|
||||
func _input(event: InputEvent) -> void:
|
||||
if _emote_radial_menu == null:
|
||||
return
|
||||
var can_open: bool = (
|
||||
_gameplay_ui_enabled
|
||||
and not _system_menu_open
|
||||
and not _player_menu_open
|
||||
and not _shop_open
|
||||
and not _chat_input_open
|
||||
and not _showcase_active
|
||||
)
|
||||
if _emote_radial_menu.handle_input(event, can_open):
|
||||
get_viewport().set_input_as_handled()
|
||||
|
||||
|
||||
func _on_emote_selected(emote_id: StringName) -> void:
|
||||
if emote_id == &"sit" and _player != null:
|
||||
_player.toggle_sitting()
|
||||
|
||||
|
||||
func setup_data_and_identity(
|
||||
data_root: PlayerDataRoot,
|
||||
identity_backups: IdentityBackupService,
|
||||
|
|
@ -359,6 +385,17 @@ func _on_fishing_status_changed(status: String) -> void:
|
|||
return
|
||||
if _fishing_spot != null and _fishing_spot.is_fighting():
|
||||
return
|
||||
var normalized_status: String = status.strip_edges().to_lower()
|
||||
if normalized_status in [
|
||||
"fishing cancelled.",
|
||||
"fishing cancelled",
|
||||
"fishing failed.",
|
||||
"fishing failed",
|
||||
"fishing attempt ended.",
|
||||
"fishing attempt ended",
|
||||
]:
|
||||
_set_fishing_status("")
|
||||
return
|
||||
_set_fishing_status(status)
|
||||
|
||||
|
||||
|
|
@ -373,9 +410,6 @@ func _refresh_fishing_panel_visibility() -> void:
|
|||
var has_content: bool = (
|
||||
not _status_label.text.strip_edges().is_empty()
|
||||
or not _showcase_details.text.strip_edges().is_empty()
|
||||
or _catch_track.visible
|
||||
or _barrier_summary.visible
|
||||
or _barrier_health.visible
|
||||
)
|
||||
_fishing_panel.visible = (
|
||||
_gameplay_ui_enabled
|
||||
|
|
@ -388,7 +422,7 @@ func _on_catch_display_changed(
|
|||
chase_progress: float,
|
||||
barrier_positions: PackedFloat32Array,
|
||||
barrier_health: PackedInt32Array,
|
||||
barrier_max_health: PackedInt32Array,
|
||||
_barrier_max_health: PackedInt32Array,
|
||||
active_barrier_index: int,
|
||||
visible: bool,
|
||||
) -> void:
|
||||
|
|
@ -401,12 +435,13 @@ func _on_catch_display_changed(
|
|||
_green_catch_progress.value = progress * 100.0
|
||||
_red_chase_progress.value = maxf(chase_progress, 0.0) * 100.0
|
||||
_catch_track.visible = encounter_visible
|
||||
_barrier_summary.visible = encounter_visible
|
||||
_barrier_health.visible = encounter_visible
|
||||
_barrier_prompt_panel.visible = false
|
||||
_barrier_prompt.visible = false
|
||||
_barrier_health.visible = false
|
||||
if not encounter_visible:
|
||||
_barrier_health.visible = false
|
||||
_barrier_prompt.visible = false
|
||||
_clear_barrier_markers()
|
||||
_barrier_summary.text = ""
|
||||
_barrier_health.text = ""
|
||||
_refresh_fishing_panel_visibility()
|
||||
return
|
||||
|
|
@ -418,45 +453,18 @@ func _on_catch_display_changed(
|
|||
barrier_health,
|
||||
active_barrier_index
|
||||
)
|
||||
var barrier_labels: PackedStringArray = []
|
||||
for barrier_index: int in range(barrier_positions.size()):
|
||||
var defeated: bool = (
|
||||
barrier_index < barrier_health.size()
|
||||
and barrier_health[barrier_index] <= 0
|
||||
)
|
||||
var marker: String = "✓" if defeated else "●"
|
||||
if barrier_index == active_barrier_index:
|
||||
marker = "[%s]" % marker
|
||||
barrier_labels.append(
|
||||
"%d%% %s"
|
||||
% [roundi(barrier_positions[barrier_index] * 100.0), marker]
|
||||
)
|
||||
_barrier_summary.text = "barriers: %s" % " ".join(barrier_labels)
|
||||
|
||||
if (
|
||||
active_barrier_index >= 0
|
||||
and active_barrier_index < barrier_health.size()
|
||||
and active_barrier_index < barrier_max_health.size()
|
||||
):
|
||||
_barrier_health.text = (
|
||||
"barrier: %d / %d"
|
||||
% [
|
||||
barrier_health[active_barrier_index],
|
||||
barrier_max_health[active_barrier_index],
|
||||
]
|
||||
)
|
||||
_barrier_prompt_panel.visible = true
|
||||
_barrier_prompt.visible = true
|
||||
_barrier_health.visible = true
|
||||
_barrier_health.text = "%d" % barrier_health[active_barrier_index]
|
||||
else:
|
||||
_barrier_health.text = "hold left click to reel"
|
||||
|
||||
var chase_gap: float = progress - chase_progress
|
||||
if chase_gap <= 0.05:
|
||||
_barrier_health.text = (
|
||||
"%s%s"
|
||||
% [
|
||||
_barrier_health.text,
|
||||
"\nred is closing in!",
|
||||
]
|
||||
).strip_edges()
|
||||
_barrier_prompt.visible = false
|
||||
_barrier_health.visible = false
|
||||
_barrier_health.text = ""
|
||||
_refresh_fishing_panel_visibility()
|
||||
|
||||
|
||||
|
|
@ -508,15 +516,18 @@ func _on_showcase_changed(
|
|||
_set_fishing_status("")
|
||||
return
|
||||
_catch_track.visible = false
|
||||
_barrier_summary.visible = false
|
||||
_barrier_prompt_panel.visible = false
|
||||
_barrier_prompt.visible = false
|
||||
_barrier_health.visible = false
|
||||
_clear_barrier_markers()
|
||||
_showcase_details.text = (
|
||||
"%.1f lb • %s\nleft click or escape to put away"
|
||||
"%.1f lb • %s"
|
||||
% [weight_lb, rarity_name]
|
||||
)
|
||||
_showcase_details.visible = true
|
||||
_set_fishing_status("you caught a %s!" % fish_name)
|
||||
_status_label.text = "You caught a %s!" % fish_name
|
||||
_status_label.visible = true
|
||||
_refresh_fishing_panel_visibility()
|
||||
|
||||
|
||||
func _on_player_menu_visibility_changed(is_open: bool) -> void:
|
||||
|
|
|
|||
113
ui/game_ui.tscn
113
ui/game_ui.tscn
|
|
@ -1,4 +1,4 @@
|
|||
[gd_scene load_steps=14 format=3]
|
||||
[gd_scene load_steps=15 format=3]
|
||||
|
||||
[ext_resource type="Script" path="res://ui/game_ui.gd" id="1_ui"]
|
||||
[ext_resource type="PackedScene" path="res://ui/player_menu.tscn" id="2_menu"]
|
||||
|
|
@ -9,27 +9,27 @@
|
|||
[ext_resource type="PackedScene" path="res://ui/hotbar.tscn" id="7_hotbar"]
|
||||
[ext_resource type="PackedScene" path="res://ui/fishing_shop.tscn" id="8_shop"]
|
||||
[ext_resource type="Script" path="res://ui/chat_ui.gd" id="9_chat"]
|
||||
[ext_resource type="PackedScene" path="res://ui/emote_radial_menu.tscn" id="10_emote"]
|
||||
|
||||
[sub_resource type="StyleBoxFlat" id="StyleBox_chase_background"]
|
||||
bg_color = Color(0.055, 0.105, 0.125, 1)
|
||||
corner_radius_top_left = 6
|
||||
corner_radius_top_right = 6
|
||||
corner_radius_bottom_right = 6
|
||||
corner_radius_bottom_left = 6
|
||||
|
||||
[sub_resource type="StyleBoxFlat" id="StyleBox_chase_fill"]
|
||||
bg_color = Color(0.937, 0.357, 0.384, 0.95)
|
||||
corner_radius_top_left = 6
|
||||
corner_radius_top_right = 6
|
||||
corner_radius_bottom_right = 6
|
||||
corner_radius_bottom_left = 6
|
||||
|
||||
[sub_resource type="StyleBoxFlat" id="StyleBox_catch_fill"]
|
||||
bg_color = Color(0.275, 0.784, 0.471, 0.92)
|
||||
corner_radius_top_left = 6
|
||||
corner_radius_top_right = 6
|
||||
corner_radius_bottom_right = 6
|
||||
corner_radius_bottom_left = 6
|
||||
|
||||
[sub_resource type="StyleBoxFlat" id="StyleBox_fishing_panel"]
|
||||
bg_color = Color(0.055, 0.105, 0.125, 0.96)
|
||||
border_width_left = 0
|
||||
border_width_top = 0
|
||||
border_width_right = 0
|
||||
border_width_bottom = 0
|
||||
corner_radius_top_left = 12
|
||||
corner_radius_top_right = 12
|
||||
corner_radius_bottom_right = 12
|
||||
corner_radius_bottom_left = 12
|
||||
|
||||
[sub_resource type="StyleBoxFlat" id="StyleBox_transparent"]
|
||||
bg_color = Color(0, 0, 0, 0)
|
||||
|
|
@ -64,23 +64,24 @@ anchor_left = 0.5
|
|||
anchor_top = 1.0
|
||||
anchor_right = 0.5
|
||||
anchor_bottom = 1.0
|
||||
offset_left = -205.0
|
||||
offset_top = -216.0
|
||||
offset_right = 205.0
|
||||
offset_bottom = -128.0
|
||||
offset_left = -260.0
|
||||
offset_top = -150.0
|
||||
offset_right = 260.0
|
||||
offset_bottom = -68.0
|
||||
grow_horizontal = 2
|
||||
grow_vertical = 0
|
||||
mouse_filter = 2
|
||||
theme = ExtResource("3_theme")
|
||||
theme_override_styles/panel = SubResource("StyleBox_fishing_panel")
|
||||
|
||||
[node name="MarginContainer" type="MarginContainer" parent="UIRoot/CanonicalStage/GameplayTransientHUD/FishingPanel"]
|
||||
theme_override_constants/margin_left = 14
|
||||
theme_override_constants/margin_top = 8
|
||||
theme_override_constants/margin_right = 14
|
||||
theme_override_constants/margin_bottom = 8
|
||||
theme_override_constants/margin_left = 0
|
||||
theme_override_constants/margin_top = 0
|
||||
theme_override_constants/margin_right = 0
|
||||
theme_override_constants/margin_bottom = 0
|
||||
|
||||
[node name="VBoxContainer" type="VBoxContainer" parent="UIRoot/CanonicalStage/GameplayTransientHUD/FishingPanel/MarginContainer"]
|
||||
theme_override_constants/separation = 4
|
||||
theme_override_constants/separation = 2
|
||||
|
||||
[node name="StatusLabel" type="Label" parent="UIRoot/CanonicalStage/GameplayTransientHUD/FishingPanel/MarginContainer/VBoxContainer"]
|
||||
unique_name_in_owner = true
|
||||
|
|
@ -94,12 +95,25 @@ unique_name_in_owner = true
|
|||
visible = false
|
||||
horizontal_alignment = 1
|
||||
|
||||
[node name="CatchTrack" type="Control" parent="UIRoot/CanonicalStage/GameplayTransientHUD/FishingPanel/MarginContainer/VBoxContainer"]
|
||||
[node name="CatchTrack" type="Control" parent="UIRoot/CanonicalStage/GameplayTransientHUD"]
|
||||
unique_name_in_owner = true
|
||||
visible = false
|
||||
custom_minimum_size = Vector2(0, 20)
|
||||
z_index = 60
|
||||
layout_mode = 1
|
||||
anchors_preset = 7
|
||||
anchor_left = 0.5
|
||||
anchor_top = 1.0
|
||||
anchor_right = 0.5
|
||||
anchor_bottom = 1.0
|
||||
offset_left = -420.0
|
||||
offset_top = -160.0
|
||||
offset_right = 420.0
|
||||
offset_bottom = -124.0
|
||||
grow_horizontal = 2
|
||||
grow_vertical = 0
|
||||
mouse_filter = 2
|
||||
|
||||
[node name="GreenCatchProgress" type="ProgressBar" parent="UIRoot/CanonicalStage/GameplayTransientHUD/FishingPanel/MarginContainer/VBoxContainer/CatchTrack"]
|
||||
[node name="GreenCatchProgress" type="ProgressBar" parent="UIRoot/CanonicalStage/GameplayTransientHUD/CatchTrack"]
|
||||
unique_name_in_owner = true
|
||||
layout_mode = 1
|
||||
anchors_preset = 15
|
||||
|
|
@ -113,7 +127,7 @@ theme_override_styles/fill = SubResource("StyleBox_catch_fill")
|
|||
value = 0.0
|
||||
show_percentage = false
|
||||
|
||||
[node name="RedChaseProgress" type="ProgressBar" parent="UIRoot/CanonicalStage/GameplayTransientHUD/FishingPanel/MarginContainer/VBoxContainer/CatchTrack"]
|
||||
[node name="RedChaseProgress" type="ProgressBar" parent="UIRoot/CanonicalStage/GameplayTransientHUD/CatchTrack"]
|
||||
unique_name_in_owner = true
|
||||
layout_mode = 1
|
||||
anchors_preset = 15
|
||||
|
|
@ -127,7 +141,7 @@ theme_override_styles/fill = SubResource("StyleBox_chase_fill")
|
|||
value = 0.0
|
||||
show_percentage = false
|
||||
|
||||
[node name="BarrierMarkers" type="Control" parent="UIRoot/CanonicalStage/GameplayTransientHUD/FishingPanel/MarginContainer/VBoxContainer/CatchTrack"]
|
||||
[node name="BarrierMarkers" type="Control" parent="UIRoot/CanonicalStage/GameplayTransientHUD/CatchTrack"]
|
||||
unique_name_in_owner = true
|
||||
layout_mode = 1
|
||||
anchors_preset = 15
|
||||
|
|
@ -137,15 +151,51 @@ grow_horizontal = 2
|
|||
grow_vertical = 2
|
||||
mouse_filter = 2
|
||||
|
||||
[node name="BarrierSummary" type="Label" parent="UIRoot/CanonicalStage/GameplayTransientHUD/FishingPanel/MarginContainer/VBoxContainer"]
|
||||
[node name="BarrierPromptPanel" type="PanelContainer" parent="UIRoot/CanonicalStage/GameplayTransientHUD"]
|
||||
unique_name_in_owner = true
|
||||
visible = false
|
||||
horizontal_alignment = 1
|
||||
z_index = 61
|
||||
anchors_preset = 7
|
||||
anchor_left = 0.5
|
||||
anchor_top = 1.0
|
||||
anchor_right = 0.5
|
||||
anchor_bottom = 1.0
|
||||
offset_left = -170.0
|
||||
offset_top = -116.0
|
||||
offset_right = 170.0
|
||||
offset_bottom = -8.0
|
||||
grow_horizontal = 2
|
||||
grow_vertical = 0
|
||||
pivot_offset = Vector2(170, 55)
|
||||
rotation = 0.025
|
||||
mouse_filter = 2
|
||||
theme = ExtResource("3_theme")
|
||||
theme_override_styles/panel = SubResource("StyleBox_fishing_panel")
|
||||
|
||||
[node name="BarrierHealth" type="Label" parent="UIRoot/CanonicalStage/GameplayTransientHUD/FishingPanel/MarginContainer/VBoxContainer"]
|
||||
[node name="Margin" type="MarginContainer" parent="UIRoot/CanonicalStage/GameplayTransientHUD/BarrierPromptPanel"]
|
||||
theme_override_constants/margin_left = 18
|
||||
theme_override_constants/margin_top = 4
|
||||
theme_override_constants/margin_right = 18
|
||||
theme_override_constants/margin_bottom = 4
|
||||
|
||||
[node name="Layout" type="VBoxContainer" parent="UIRoot/CanonicalStage/GameplayTransientHUD/BarrierPromptPanel/Margin"]
|
||||
theme_override_constants/separation = 0
|
||||
|
||||
[node name="BarrierPrompt" type="Label" parent="UIRoot/CanonicalStage/GameplayTransientHUD/BarrierPromptPanel/Margin/Layout"]
|
||||
unique_name_in_owner = true
|
||||
visible = false
|
||||
text = "MASH!"
|
||||
horizontal_alignment = 1
|
||||
theme_override_colors/font_color = Color(1, 0.84, 0.3, 1)
|
||||
theme_override_font_sizes/font_size = 63
|
||||
|
||||
[node name="BarrierHealth" type="Label" parent="UIRoot/CanonicalStage/GameplayTransientHUD/BarrierPromptPanel/Margin/Layout"]
|
||||
unique_name_in_owner = true
|
||||
visible = false
|
||||
text = "0"
|
||||
horizontal_alignment = 1
|
||||
theme_override_colors/font_color = Color(0.95, 0.98, 1, 1)
|
||||
theme_override_font_sizes/font_size = 34
|
||||
|
||||
[node name="PlayerMenu" parent="UIRoot/CanonicalStage" instance=ExtResource("2_menu")]
|
||||
unique_name_in_owner = true
|
||||
|
|
@ -153,6 +203,9 @@ unique_name_in_owner = true
|
|||
[node name="Hotbar" parent="UIRoot/CanonicalStage" instance=ExtResource("7_hotbar")]
|
||||
unique_name_in_owner = true
|
||||
|
||||
[node name="EmoteRadialMenu" parent="UIRoot/CanonicalStage" instance=ExtResource("10_emote")]
|
||||
unique_name_in_owner = true
|
||||
|
||||
[node name="ShopPrompt" type="PanelContainer" parent="UIRoot/CanonicalStage/GameplayTransientHUD"]
|
||||
unique_name_in_owner = true
|
||||
visible = false
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue