Refine casting, rod feedback, and withdrawal
This commit is contained in:
parent
bba93eb3e5
commit
e3d86b1cb8
7 changed files with 700 additions and 12 deletions
374
fishing/fishing_presentation.gd
Normal file
374
fishing/fishing_presentation.gd
Normal file
|
|
@ -0,0 +1,374 @@
|
||||||
|
class_name FishingPresentation
|
||||||
|
extends Node3D
|
||||||
|
|
||||||
|
signal cast_completed
|
||||||
|
|
||||||
|
enum VisualMode {
|
||||||
|
NONE,
|
||||||
|
AIMING,
|
||||||
|
CASTING,
|
||||||
|
FISHING,
|
||||||
|
OUTCOME,
|
||||||
|
}
|
||||||
|
|
||||||
|
@export_category("Water")
|
||||||
|
@export var water_surface_offset_y: float = -0.72
|
||||||
|
@export_range(0.1, 10.0, 0.1) var pickup_distance_from_player: float = 5.5
|
||||||
|
|
||||||
|
@export_category("Cast")
|
||||||
|
@export_range(0.1, 30.0, 0.1) var target_movement_smoothing: float = 14.0
|
||||||
|
@export_range(0.1, 3.0, 0.05) var cast_travel_duration: float = 0.65
|
||||||
|
@export_range(0.1, 5.0, 0.1) var cast_arc_height: float = 2.0
|
||||||
|
@export_range(0.0, 90.0, 1.0) var maximum_rod_cock_degrees: float = 50.0
|
||||||
|
@export_range(0.0, 60.0, 1.0) var rod_forward_swing_degrees: float = 22.0
|
||||||
|
@export_range(0.05, 1.0, 0.01) var rod_forward_swing_duration: float = 0.12
|
||||||
|
@export_range(0.05, 1.0, 0.01) var rod_recovery_duration: float = 0.22
|
||||||
|
|
||||||
|
@onready var _target_marker: MeshInstance3D = %CastTargetMarker
|
||||||
|
@onready var _bobber: MeshInstance3D = %Bobber
|
||||||
|
@onready var _line: MeshInstance3D = %FishingLine
|
||||||
|
|
||||||
|
var _mode: VisualMode = VisualMode.NONE
|
||||||
|
var _line_mesh: ImmediateMesh = ImmediateMesh.new()
|
||||||
|
var _rod: Node3D
|
||||||
|
var _rod_tip: Marker3D
|
||||||
|
var _rod_neutral_rotation: Vector3
|
||||||
|
var _target_goal: Vector3
|
||||||
|
var _cast_position: Vector3
|
||||||
|
var _pickup_position: Vector3
|
||||||
|
var _active_tween: Tween
|
||||||
|
var _rod_tween: Tween
|
||||||
|
|
||||||
|
|
||||||
|
func _ready() -> void:
|
||||||
|
_line.mesh = _line_mesh
|
||||||
|
cleanup()
|
||||||
|
|
||||||
|
|
||||||
|
func _process(delta: float) -> void:
|
||||||
|
if _mode == VisualMode.AIMING:
|
||||||
|
var weight: float = 1.0 - exp(-target_movement_smoothing * delta)
|
||||||
|
_target_marker.global_position = _target_marker.global_position.lerp(
|
||||||
|
_target_goal,
|
||||||
|
weight
|
||||||
|
)
|
||||||
|
if _line.visible:
|
||||||
|
_update_line()
|
||||||
|
|
||||||
|
|
||||||
|
func get_water_surface_height() -> float:
|
||||||
|
return global_position.y + water_surface_offset_y
|
||||||
|
|
||||||
|
|
||||||
|
func begin_aim(
|
||||||
|
rod_tip: Marker3D,
|
||||||
|
rod: Node3D,
|
||||||
|
minimum_target: Vector3,
|
||||||
|
) -> void:
|
||||||
|
cleanup()
|
||||||
|
if rod_tip == null or rod == null:
|
||||||
|
return
|
||||||
|
|
||||||
|
_mode = VisualMode.AIMING
|
||||||
|
_rod = rod
|
||||||
|
_rod_tip = rod_tip
|
||||||
|
_rod_neutral_rotation = _rod.rotation
|
||||||
|
_target_goal = minimum_target
|
||||||
|
_target_marker.global_position = minimum_target
|
||||||
|
_target_marker.scale = Vector3.ONE
|
||||||
|
_target_marker.visible = true
|
||||||
|
|
||||||
|
|
||||||
|
func update_rod_charge(charge: float) -> void:
|
||||||
|
if _mode != VisualMode.AIMING or _rod == null:
|
||||||
|
return
|
||||||
|
|
||||||
|
var cock_rotation: Vector3 = _rod_neutral_rotation
|
||||||
|
cock_rotation.x += deg_to_rad(maximum_rod_cock_degrees) * clampf(charge, 0.0, 1.0)
|
||||||
|
_rod.rotation = cock_rotation
|
||||||
|
|
||||||
|
|
||||||
|
func update_aim_target(target: Vector3, charge: float) -> void:
|
||||||
|
if _mode != VisualMode.AIMING:
|
||||||
|
return
|
||||||
|
|
||||||
|
_target_goal = target
|
||||||
|
var maximum_response: float = smoothstep(0.9, 1.0, clampf(charge, 0.0, 1.0))
|
||||||
|
_target_marker.scale = Vector3.ONE * lerpf(1.0, 1.15, maximum_response)
|
||||||
|
|
||||||
|
|
||||||
|
func begin_cast(target: Vector3) -> void:
|
||||||
|
if _mode != VisualMode.AIMING or _rod_tip == null:
|
||||||
|
return
|
||||||
|
|
||||||
|
_kill_active_tween()
|
||||||
|
_mode = VisualMode.CASTING
|
||||||
|
_target_marker.visible = false
|
||||||
|
_bobber.visible = true
|
||||||
|
_bobber.scale = Vector3.ONE
|
||||||
|
_line.visible = true
|
||||||
|
_cast_position = _with_water_height(target)
|
||||||
|
_pickup_position = _calculate_pickup_position(_cast_position)
|
||||||
|
|
||||||
|
var cast_start: Vector3 = _rod_tip.global_position
|
||||||
|
_bobber.global_position = cast_start
|
||||||
|
_begin_rod_release()
|
||||||
|
_active_tween = create_tween()
|
||||||
|
_active_tween.set_trans(Tween.TRANS_SINE)
|
||||||
|
_active_tween.set_ease(Tween.EASE_IN_OUT)
|
||||||
|
_active_tween.tween_method(
|
||||||
|
_set_cast_sample.bind(cast_start, _cast_position),
|
||||||
|
0.0,
|
||||||
|
1.0,
|
||||||
|
cast_travel_duration
|
||||||
|
)
|
||||||
|
_active_tween.finished.connect(_on_cast_tween_finished)
|
||||||
|
|
||||||
|
|
||||||
|
func show_bite() -> void:
|
||||||
|
if _mode != VisualMode.FISHING:
|
||||||
|
return
|
||||||
|
|
||||||
|
_kill_active_tween()
|
||||||
|
var bite_position: Vector3 = _with_water_height(_bobber.global_position)
|
||||||
|
_bobber.global_position = bite_position
|
||||||
|
_active_tween = create_tween()
|
||||||
|
_active_tween.set_trans(Tween.TRANS_QUAD)
|
||||||
|
_active_tween.tween_property(
|
||||||
|
_bobber,
|
||||||
|
"global_position:y",
|
||||||
|
get_water_surface_height() - 0.25,
|
||||||
|
0.11
|
||||||
|
)
|
||||||
|
_active_tween.tween_property(
|
||||||
|
_bobber,
|
||||||
|
"global_position:y",
|
||||||
|
get_water_surface_height() + 0.05,
|
||||||
|
0.14
|
||||||
|
)
|
||||||
|
_active_tween.tween_property(
|
||||||
|
_bobber,
|
||||||
|
"global_position:y",
|
||||||
|
bite_position.y,
|
||||||
|
0.12
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
func show_withdrawal_position(position: Vector3) -> void:
|
||||||
|
if _mode != VisualMode.FISHING:
|
||||||
|
return
|
||||||
|
|
||||||
|
_kill_active_tween()
|
||||||
|
_bobber.global_position = _with_water_height(position)
|
||||||
|
|
||||||
|
|
||||||
|
func begin_reeling(pickup_position: Vector3) -> void:
|
||||||
|
if _mode != VisualMode.FISHING:
|
||||||
|
return
|
||||||
|
|
||||||
|
_kill_active_tween()
|
||||||
|
_cast_position = _with_water_height(_bobber.global_position)
|
||||||
|
_pickup_position = _with_water_height(pickup_position)
|
||||||
|
|
||||||
|
|
||||||
|
func show_reel_progress(progress: float, _input_held: bool) -> void:
|
||||||
|
if _mode != VisualMode.FISHING:
|
||||||
|
return
|
||||||
|
|
||||||
|
_kill_active_tween()
|
||||||
|
_bobber.scale = Vector3.ONE
|
||||||
|
_bobber.global_position = _cast_position.lerp(
|
||||||
|
_pickup_position,
|
||||||
|
clampf(progress, 0.0, 1.0)
|
||||||
|
)
|
||||||
|
_bobber.global_position.y = get_water_surface_height()
|
||||||
|
|
||||||
|
|
||||||
|
func play_outcome(outcome: StringName) -> void:
|
||||||
|
if _mode == VisualMode.NONE:
|
||||||
|
cleanup()
|
||||||
|
return
|
||||||
|
|
||||||
|
_kill_active_tween()
|
||||||
|
_kill_rod_tween()
|
||||||
|
_restore_rod_neutral()
|
||||||
|
_mode = VisualMode.OUTCOME
|
||||||
|
_active_tween = create_tween()
|
||||||
|
_active_tween.set_trans(Tween.TRANS_QUAD)
|
||||||
|
_active_tween.set_ease(Tween.EASE_IN)
|
||||||
|
|
||||||
|
if _target_marker.visible and not _bobber.visible:
|
||||||
|
_active_tween.tween_property(_target_marker, "scale", Vector3.ZERO, 0.18)
|
||||||
|
else:
|
||||||
|
_target_marker.visible = false
|
||||||
|
match outcome:
|
||||||
|
&"catch":
|
||||||
|
var catch_target: Vector3 = _bobber.global_position
|
||||||
|
if _rod_tip != null:
|
||||||
|
catch_target = _rod_tip.global_position
|
||||||
|
_active_tween.set_parallel(true)
|
||||||
|
_active_tween.tween_property(_bobber, "global_position", catch_target, 0.35)
|
||||||
|
_active_tween.tween_property(_bobber, "scale", Vector3.ZERO, 0.35)
|
||||||
|
&"escape":
|
||||||
|
var start_x: float = _bobber.global_position.x
|
||||||
|
_active_tween.tween_property(
|
||||||
|
_bobber,
|
||||||
|
"global_position:x",
|
||||||
|
start_x - 0.18,
|
||||||
|
0.08
|
||||||
|
)
|
||||||
|
_active_tween.tween_property(
|
||||||
|
_bobber,
|
||||||
|
"global_position:x",
|
||||||
|
start_x + 0.18,
|
||||||
|
0.08
|
||||||
|
)
|
||||||
|
_active_tween.tween_property(
|
||||||
|
_bobber,
|
||||||
|
"global_position:y",
|
||||||
|
get_water_surface_height() - 0.4,
|
||||||
|
0.2
|
||||||
|
)
|
||||||
|
&"miss":
|
||||||
|
_active_tween.set_parallel(true)
|
||||||
|
_active_tween.tween_property(
|
||||||
|
_bobber,
|
||||||
|
"global_position:y",
|
||||||
|
get_water_surface_height() - 0.4,
|
||||||
|
0.3
|
||||||
|
)
|
||||||
|
_active_tween.tween_property(_bobber, "scale", Vector3.ONE * 0.65, 0.3)
|
||||||
|
&"invalid":
|
||||||
|
var return_target: Vector3 = _bobber.global_position
|
||||||
|
if _rod_tip != null:
|
||||||
|
return_target = _rod_tip.global_position
|
||||||
|
_active_tween.set_parallel(true)
|
||||||
|
_active_tween.tween_property(_bobber, "global_position", return_target, 0.28)
|
||||||
|
_active_tween.tween_property(_bobber, "scale", Vector3.ZERO, 0.28)
|
||||||
|
&"withdrawal":
|
||||||
|
var withdrawal_target: Vector3 = _bobber.global_position
|
||||||
|
if _rod_tip != null:
|
||||||
|
withdrawal_target = _rod_tip.global_position
|
||||||
|
_active_tween.set_parallel(true)
|
||||||
|
_active_tween.tween_property(
|
||||||
|
_bobber,
|
||||||
|
"global_position",
|
||||||
|
withdrawal_target,
|
||||||
|
0.28
|
||||||
|
)
|
||||||
|
_active_tween.tween_property(_bobber, "scale", Vector3.ZERO, 0.28)
|
||||||
|
_:
|
||||||
|
_active_tween.tween_property(_bobber, "scale", Vector3.ZERO, 0.18)
|
||||||
|
|
||||||
|
_active_tween.finished.connect(cleanup)
|
||||||
|
|
||||||
|
|
||||||
|
func cleanup() -> void:
|
||||||
|
_kill_active_tween()
|
||||||
|
_kill_rod_tween()
|
||||||
|
_restore_rod_neutral()
|
||||||
|
_mode = VisualMode.NONE
|
||||||
|
_rod = null
|
||||||
|
_rod_tip = null
|
||||||
|
_target_marker.visible = false
|
||||||
|
_target_marker.scale = Vector3.ONE
|
||||||
|
_bobber.visible = false
|
||||||
|
_bobber.scale = Vector3.ONE
|
||||||
|
_line.visible = false
|
||||||
|
_line_mesh.clear_surfaces()
|
||||||
|
|
||||||
|
|
||||||
|
func _set_cast_sample(
|
||||||
|
progress: float,
|
||||||
|
start: Vector3,
|
||||||
|
target: Vector3,
|
||||||
|
) -> void:
|
||||||
|
var sample: Vector3 = start.lerp(target, progress)
|
||||||
|
sample.y += sin(progress * PI) * cast_arc_height
|
||||||
|
_bobber.global_position = sample
|
||||||
|
|
||||||
|
|
||||||
|
func _on_cast_tween_finished() -> void:
|
||||||
|
if _mode != VisualMode.CASTING:
|
||||||
|
return
|
||||||
|
|
||||||
|
_active_tween = null
|
||||||
|
_bobber.global_position = _cast_position
|
||||||
|
_mode = VisualMode.FISHING
|
||||||
|
cast_completed.emit()
|
||||||
|
|
||||||
|
|
||||||
|
func _calculate_pickup_position(target: Vector3) -> Vector3:
|
||||||
|
var player_on_water: Vector3 = target
|
||||||
|
if _rod_tip != null:
|
||||||
|
player_on_water = _with_water_height(_rod_tip.global_position)
|
||||||
|
var outward: Vector3 = target - player_on_water
|
||||||
|
outward.y = 0.0
|
||||||
|
if outward.is_zero_approx():
|
||||||
|
outward = Vector3.FORWARD
|
||||||
|
else:
|
||||||
|
outward = outward.normalized()
|
||||||
|
var target_distance: float = player_on_water.distance_to(target)
|
||||||
|
var pickup_distance: float = minf(
|
||||||
|
pickup_distance_from_player,
|
||||||
|
target_distance * 0.8
|
||||||
|
)
|
||||||
|
return player_on_water + outward * pickup_distance
|
||||||
|
|
||||||
|
|
||||||
|
func _with_water_height(position: Vector3) -> Vector3:
|
||||||
|
return Vector3(position.x, get_water_surface_height(), position.z)
|
||||||
|
|
||||||
|
|
||||||
|
func _update_line() -> void:
|
||||||
|
if _rod_tip == null or not is_instance_valid(_rod_tip):
|
||||||
|
cleanup()
|
||||||
|
return
|
||||||
|
|
||||||
|
_line_mesh.clear_surfaces()
|
||||||
|
_line_mesh.surface_begin(Mesh.PRIMITIVE_LINES)
|
||||||
|
_line_mesh.surface_add_vertex(to_local(_rod_tip.global_position))
|
||||||
|
_line_mesh.surface_add_vertex(to_local(_bobber.global_position))
|
||||||
|
_line_mesh.surface_end()
|
||||||
|
|
||||||
|
|
||||||
|
func _begin_rod_release() -> void:
|
||||||
|
_kill_rod_tween()
|
||||||
|
if _rod == null:
|
||||||
|
return
|
||||||
|
|
||||||
|
var forward_rotation: Vector3 = _rod_neutral_rotation
|
||||||
|
forward_rotation.x -= deg_to_rad(rod_forward_swing_degrees)
|
||||||
|
_rod_tween = create_tween()
|
||||||
|
_rod_tween.set_trans(Tween.TRANS_QUAD)
|
||||||
|
_rod_tween.set_ease(Tween.EASE_OUT)
|
||||||
|
_rod_tween.tween_property(
|
||||||
|
_rod,
|
||||||
|
"rotation",
|
||||||
|
forward_rotation,
|
||||||
|
rod_forward_swing_duration
|
||||||
|
)
|
||||||
|
_rod_tween.set_ease(Tween.EASE_IN_OUT)
|
||||||
|
_rod_tween.tween_property(
|
||||||
|
_rod,
|
||||||
|
"rotation",
|
||||||
|
_rod_neutral_rotation,
|
||||||
|
rod_recovery_duration
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
func _restore_rod_neutral() -> void:
|
||||||
|
if _rod != null and is_instance_valid(_rod):
|
||||||
|
_rod.rotation = _rod_neutral_rotation
|
||||||
|
|
||||||
|
|
||||||
|
func _kill_active_tween() -> void:
|
||||||
|
if _active_tween != null and _active_tween.is_valid():
|
||||||
|
_active_tween.kill()
|
||||||
|
_active_tween = null
|
||||||
|
|
||||||
|
|
||||||
|
func _kill_rod_tween() -> void:
|
||||||
|
if _rod_tween != null and _rod_tween.is_valid():
|
||||||
|
_rod_tween.kill()
|
||||||
|
_rod_tween = null
|
||||||
1
fishing/fishing_presentation.gd.uid
Normal file
1
fishing/fishing_presentation.gd.uid
Normal file
|
|
@ -0,0 +1 @@
|
||||||
|
uid://dmwwhk48mpxn4
|
||||||
|
|
@ -2,6 +2,7 @@ class_name FishingSpot
|
||||||
extends Area3D
|
extends Area3D
|
||||||
|
|
||||||
const FishDataType = preload("res://fish/fish_data.gd")
|
const FishDataType = preload("res://fish/fish_data.gd")
|
||||||
|
const FishingPresentationType = preload("res://fishing/fishing_presentation.gd")
|
||||||
const PlayerType = preload("res://player/player.gd")
|
const PlayerType = preload("res://player/player.gd")
|
||||||
|
|
||||||
signal status_changed(status: String)
|
signal status_changed(status: String)
|
||||||
|
|
@ -9,12 +10,24 @@ signal reel_progress_changed(progress: float, visible: bool)
|
||||||
|
|
||||||
enum FishingState {
|
enum FishingState {
|
||||||
READY,
|
READY,
|
||||||
|
AIMING_CAST,
|
||||||
|
CASTING,
|
||||||
WAITING_FOR_BITE,
|
WAITING_FOR_BITE,
|
||||||
BITE_ACTIVE,
|
BITE_ACTIVE,
|
||||||
REELING,
|
REELING,
|
||||||
COOLDOWN,
|
COOLDOWN,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@export_category("Cast")
|
||||||
|
@export_range(0.1, 30.0, 0.05) var minimum_cast_distance: float = 0.5
|
||||||
|
@export_range(0.1, 30.0, 0.1) var maximum_cast_distance: float = 14.0
|
||||||
|
@export_range(0.1, 10.0, 0.1) var cast_charge_duration: float = 2.75
|
||||||
|
@export_flags_3d_physics var fishable_surface_mask: int = 4
|
||||||
|
|
||||||
|
@export_category("Withdrawal")
|
||||||
|
@export_range(0.1, 20.0, 0.1) var withdrawal_rate: float = 4.9
|
||||||
|
@export_range(0.1, 10.0, 0.1) var withdrawal_cancel_distance: float = 1.0
|
||||||
|
|
||||||
@export_category("Timing")
|
@export_category("Timing")
|
||||||
@export_range(0.1, 30.0, 0.1) var wait_time: float = 2.0
|
@export_range(0.1, 30.0, 0.1) var wait_time: float = 2.0
|
||||||
@export_range(0.1, 10.0, 0.1) var bite_window_duration: float = 1.5
|
@export_range(0.1, 10.0, 0.1) var bite_window_duration: float = 1.5
|
||||||
|
|
@ -26,10 +39,21 @@ enum FishingState {
|
||||||
@export_category("Catch")
|
@export_category("Catch")
|
||||||
@export var catchable_fish: FishDataType
|
@export var catchable_fish: FishDataType
|
||||||
|
|
||||||
|
@onready var _presentation: FishingPresentationType = %FishingPresentation
|
||||||
|
|
||||||
var state: FishingState = FishingState.READY
|
var state: FishingState = FishingState.READY
|
||||||
var _nearby_player: PlayerType
|
var _nearby_player: PlayerType
|
||||||
var _active_player: PlayerType
|
var _active_player: PlayerType
|
||||||
var _state_time_remaining: float = 0.0
|
var _state_time_remaining: float = 0.0
|
||||||
|
var _cast_charge: float = 0.0
|
||||||
|
var _cast_direction: Vector3 = Vector3.FORWARD
|
||||||
|
var _cast_origin_position: Vector3
|
||||||
|
var _cast_target: Vector3
|
||||||
|
var _cast_landing_is_fishable: bool = false
|
||||||
|
var _withdrawal_endpoint: Vector3
|
||||||
|
var _withdrawal_progress: float = 0.0
|
||||||
|
var _withdrawal_input_held: bool = false
|
||||||
|
var _new_cast_press_armed: bool = true
|
||||||
var _reel_progress: float = 0.0
|
var _reel_progress: float = 0.0
|
||||||
var _reel_input_held: bool = false
|
var _reel_input_held: bool = false
|
||||||
var _cooldown_status: String = ""
|
var _cooldown_status: String = ""
|
||||||
|
|
@ -38,14 +62,30 @@ var _cooldown_status: String = ""
|
||||||
func _ready() -> void:
|
func _ready() -> void:
|
||||||
body_entered.connect(_on_body_entered)
|
body_entered.connect(_on_body_entered)
|
||||||
body_exited.connect(_on_body_exited)
|
body_exited.connect(_on_body_exited)
|
||||||
|
_presentation.cast_completed.connect(_on_cast_completed)
|
||||||
|
|
||||||
|
|
||||||
func _process(delta: float) -> void:
|
func _process(delta: float) -> void:
|
||||||
|
if state == FishingState.READY and not Input.is_action_pressed("fish_primary"):
|
||||||
|
_new_cast_press_armed = true
|
||||||
|
|
||||||
match state:
|
match state:
|
||||||
|
FishingState.AIMING_CAST:
|
||||||
|
_update_cast_charge(delta)
|
||||||
|
if not Input.is_action_pressed("fish_primary"):
|
||||||
|
_confirm_cast()
|
||||||
FishingState.WAITING_FOR_BITE:
|
FishingState.WAITING_FOR_BITE:
|
||||||
_state_time_remaining -= delta
|
_state_time_remaining -= delta
|
||||||
if _state_time_remaining <= 0.0:
|
if _state_time_remaining <= 0.0:
|
||||||
_activate_bite()
|
_activate_bite()
|
||||||
|
else:
|
||||||
|
if (
|
||||||
|
_withdrawal_input_held
|
||||||
|
and not Input.is_action_pressed("fish_primary")
|
||||||
|
):
|
||||||
|
_withdrawal_input_held = false
|
||||||
|
if _withdrawal_input_held:
|
||||||
|
_update_withdrawal(delta)
|
||||||
FishingState.BITE_ACTIVE:
|
FishingState.BITE_ACTIVE:
|
||||||
_state_time_remaining -= delta
|
_state_time_remaining -= delta
|
||||||
if _state_time_remaining <= 0.0:
|
if _state_time_remaining <= 0.0:
|
||||||
|
|
@ -61,6 +101,21 @@ func _process(delta: float) -> void:
|
||||||
|
|
||||||
|
|
||||||
func _unhandled_input(event: InputEvent) -> void:
|
func _unhandled_input(event: InputEvent) -> void:
|
||||||
|
if (
|
||||||
|
event.is_action_pressed("ui_cancel")
|
||||||
|
and _active_player != null
|
||||||
|
and state in [
|
||||||
|
FishingState.AIMING_CAST,
|
||||||
|
FishingState.CASTING,
|
||||||
|
FishingState.WAITING_FOR_BITE,
|
||||||
|
FishingState.BITE_ACTIVE,
|
||||||
|
FishingState.REELING,
|
||||||
|
]
|
||||||
|
):
|
||||||
|
_cancel_attempt()
|
||||||
|
get_viewport().set_input_as_handled()
|
||||||
|
return
|
||||||
|
|
||||||
if _nearby_player == null or not _nearby_player.is_local_control_enabled():
|
if _nearby_player == null or not _nearby_player.is_local_control_enabled():
|
||||||
return
|
return
|
||||||
if not event.is_action("fish_primary"):
|
if not event.is_action("fish_primary"):
|
||||||
|
|
@ -69,7 +124,11 @@ func _unhandled_input(event: InputEvent) -> void:
|
||||||
if event.is_pressed():
|
if event.is_pressed():
|
||||||
match state:
|
match state:
|
||||||
FishingState.READY:
|
FishingState.READY:
|
||||||
_begin_fishing(_nearby_player)
|
if not _new_cast_press_armed:
|
||||||
|
return
|
||||||
|
_begin_aiming(_nearby_player)
|
||||||
|
FishingState.WAITING_FOR_BITE:
|
||||||
|
_withdrawal_input_held = true
|
||||||
FishingState.BITE_ACTIVE:
|
FishingState.BITE_ACTIVE:
|
||||||
_confirm_hook()
|
_confirm_hook()
|
||||||
FishingState.REELING:
|
FishingState.REELING:
|
||||||
|
|
@ -77,29 +136,112 @@ func _unhandled_input(event: InputEvent) -> void:
|
||||||
_:
|
_:
|
||||||
return
|
return
|
||||||
get_viewport().set_input_as_handled()
|
get_viewport().set_input_as_handled()
|
||||||
|
elif state == FishingState.AIMING_CAST:
|
||||||
|
_confirm_cast()
|
||||||
|
get_viewport().set_input_as_handled()
|
||||||
|
elif state == FishingState.WAITING_FOR_BITE:
|
||||||
|
_withdrawal_input_held = false
|
||||||
|
get_viewport().set_input_as_handled()
|
||||||
elif state == FishingState.REELING:
|
elif state == FishingState.REELING:
|
||||||
_reel_input_held = false
|
_reel_input_held = false
|
||||||
get_viewport().set_input_as_handled()
|
get_viewport().set_input_as_handled()
|
||||||
|
|
||||||
|
|
||||||
func _begin_fishing(player: PlayerType) -> void:
|
func _begin_aiming(player: PlayerType) -> void:
|
||||||
if state != FishingState.READY or _active_player != null:
|
if state != FishingState.READY or _active_player != null:
|
||||||
return
|
return
|
||||||
|
|
||||||
_active_player = player
|
_active_player = player
|
||||||
_active_player.set_movement_enabled(false)
|
_active_player.set_movement_enabled(false)
|
||||||
|
_cast_direction = _capture_cast_direction(_active_player)
|
||||||
|
_cast_origin_position = _active_player.get_cast_origin_position()
|
||||||
|
_cast_charge = 0.0
|
||||||
|
_cast_target = _calculate_cast_target(minimum_cast_distance)
|
||||||
|
state = FishingState.AIMING_CAST
|
||||||
|
status_changed.emit("Hold left click to aim • Release to cast")
|
||||||
|
_presentation.begin_aim(
|
||||||
|
_active_player.get_fishing_rod_tip(),
|
||||||
|
_active_player.get_fishing_rod(),
|
||||||
|
_cast_target
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
func _update_cast_charge(delta: float) -> void:
|
||||||
|
if state != FishingState.AIMING_CAST:
|
||||||
|
return
|
||||||
|
|
||||||
|
_cast_charge = minf(_cast_charge + delta / cast_charge_duration, 1.0)
|
||||||
|
var maximum_distance: float = maxf(minimum_cast_distance, maximum_cast_distance)
|
||||||
|
var distance: float = lerpf(minimum_cast_distance, maximum_distance, _cast_charge)
|
||||||
|
_cast_target = _calculate_cast_target(distance)
|
||||||
|
_presentation.update_aim_target(_cast_target, _cast_charge)
|
||||||
|
_presentation.update_rod_charge(_cast_charge)
|
||||||
|
|
||||||
|
|
||||||
|
func _confirm_cast() -> void:
|
||||||
|
if state != FishingState.AIMING_CAST:
|
||||||
|
return
|
||||||
|
|
||||||
|
_cast_landing_is_fishable = _is_landing_fishable(_cast_target)
|
||||||
|
state = FishingState.CASTING
|
||||||
|
status_changed.emit("Casting...")
|
||||||
|
_presentation.begin_cast(_cast_target)
|
||||||
|
|
||||||
|
|
||||||
|
func _on_cast_completed() -> void:
|
||||||
|
if state != FishingState.CASTING:
|
||||||
|
return
|
||||||
|
|
||||||
|
if not _cast_landing_is_fishable:
|
||||||
|
_cleanup_attempt("Can't fish there.", &"invalid")
|
||||||
|
return
|
||||||
|
|
||||||
state = FishingState.WAITING_FOR_BITE
|
state = FishingState.WAITING_FOR_BITE
|
||||||
_state_time_remaining = wait_time
|
_state_time_remaining = wait_time
|
||||||
|
_withdrawal_progress = 0.0
|
||||||
|
_withdrawal_input_held = false
|
||||||
|
_withdrawal_endpoint = Vector3(
|
||||||
|
_cast_origin_position.x + _cast_direction.x * withdrawal_cancel_distance,
|
||||||
|
_presentation.get_water_surface_height(),
|
||||||
|
_cast_origin_position.z + _cast_direction.z * withdrawal_cancel_distance
|
||||||
|
)
|
||||||
status_changed.emit("Waiting for a bite...")
|
status_changed.emit("Waiting for a bite...")
|
||||||
|
|
||||||
|
|
||||||
|
func _update_withdrawal(delta: float) -> void:
|
||||||
|
if state != FishingState.WAITING_FOR_BITE:
|
||||||
|
return
|
||||||
|
|
||||||
|
var landing_offset: Vector3 = _cast_target - _cast_origin_position
|
||||||
|
landing_offset.y = 0.0
|
||||||
|
var landing_distance: float = landing_offset.length()
|
||||||
|
var withdrawable_distance: float = landing_distance - withdrawal_cancel_distance
|
||||||
|
if withdrawable_distance <= 0.0:
|
||||||
|
_cancel_from_withdrawal()
|
||||||
|
return
|
||||||
|
|
||||||
|
_withdrawal_progress = minf(
|
||||||
|
_withdrawal_progress + withdrawal_rate * delta / withdrawable_distance,
|
||||||
|
1.0
|
||||||
|
)
|
||||||
|
var current_position: Vector3 = _cast_target.lerp(
|
||||||
|
_withdrawal_endpoint,
|
||||||
|
_withdrawal_progress
|
||||||
|
)
|
||||||
|
_presentation.show_withdrawal_position(current_position)
|
||||||
|
if is_equal_approx(_withdrawal_progress, 1.0):
|
||||||
|
_cancel_from_withdrawal()
|
||||||
|
|
||||||
|
|
||||||
func _activate_bite() -> void:
|
func _activate_bite() -> void:
|
||||||
if state != FishingState.WAITING_FOR_BITE:
|
if state != FishingState.WAITING_FOR_BITE:
|
||||||
return
|
return
|
||||||
|
|
||||||
state = FishingState.BITE_ACTIVE
|
state = FishingState.BITE_ACTIVE
|
||||||
_state_time_remaining = bite_window_duration
|
_state_time_remaining = bite_window_duration
|
||||||
|
_withdrawal_input_held = false
|
||||||
status_changed.emit("Bite! Left click to hook")
|
status_changed.emit("Bite! Left click to hook")
|
||||||
|
_presentation.show_bite()
|
||||||
|
|
||||||
|
|
||||||
func _confirm_hook() -> void:
|
func _confirm_hook() -> void:
|
||||||
|
|
@ -112,6 +254,8 @@ func _confirm_hook() -> void:
|
||||||
_reel_input_held = true
|
_reel_input_held = true
|
||||||
status_changed.emit("Hold left click to reel")
|
status_changed.emit("Hold left click to reel")
|
||||||
reel_progress_changed.emit(_reel_progress, true)
|
reel_progress_changed.emit(_reel_progress, true)
|
||||||
|
_presentation.begin_reeling(_withdrawal_endpoint)
|
||||||
|
_presentation.show_reel_progress(_reel_progress, _reel_input_held)
|
||||||
|
|
||||||
|
|
||||||
func _update_reel_progress(delta: float) -> void:
|
func _update_reel_progress(delta: float) -> void:
|
||||||
|
|
@ -123,6 +267,7 @@ func _update_reel_progress(delta: float) -> void:
|
||||||
else:
|
else:
|
||||||
_reel_progress = maxf(_reel_progress - reel_decay_rate * delta, 0.0)
|
_reel_progress = maxf(_reel_progress - reel_decay_rate * delta, 0.0)
|
||||||
reel_progress_changed.emit(_reel_progress, true)
|
reel_progress_changed.emit(_reel_progress, true)
|
||||||
|
_presentation.show_reel_progress(_reel_progress, _reel_input_held)
|
||||||
if is_equal_approx(_reel_progress, 1.0):
|
if is_equal_approx(_reel_progress, 1.0):
|
||||||
_resolve_catch()
|
_resolve_catch()
|
||||||
elif is_zero_approx(_reel_progress):
|
elif is_zero_approx(_reel_progress):
|
||||||
|
|
@ -135,7 +280,7 @@ func _resolve_catch() -> void:
|
||||||
return
|
return
|
||||||
|
|
||||||
_active_player.inventory.add_fish(catchable_fish)
|
_active_player.inventory.add_fish(catchable_fish)
|
||||||
_cleanup_attempt("Caught %s!" % catchable_fish.display_name)
|
_cleanup_attempt("Caught %s!" % catchable_fish.display_name, &"catch")
|
||||||
|
|
||||||
|
|
||||||
func _resolve_escape() -> void:
|
func _resolve_escape() -> void:
|
||||||
|
|
@ -145,26 +290,41 @@ func _resolve_escape() -> void:
|
||||||
var fish_name: String = "The fish"
|
var fish_name: String = "The fish"
|
||||||
if catchable_fish != null and not catchable_fish.display_name.is_empty():
|
if catchable_fish != null and not catchable_fish.display_name.is_empty():
|
||||||
fish_name = "The %s" % catchable_fish.display_name
|
fish_name = "The %s" % catchable_fish.display_name
|
||||||
_cleanup_attempt("%s got away!" % fish_name)
|
_cleanup_attempt("%s got away!" % fish_name, &"escape")
|
||||||
|
|
||||||
|
|
||||||
func _miss_bite() -> void:
|
func _miss_bite() -> void:
|
||||||
if state != FishingState.BITE_ACTIVE:
|
if state != FishingState.BITE_ACTIVE:
|
||||||
return
|
return
|
||||||
_cleanup_attempt("The fish got away.")
|
_cleanup_attempt("The fish got away.", &"miss")
|
||||||
|
|
||||||
|
|
||||||
func _cleanup_attempt(cooldown_message: String = "") -> void:
|
func _cleanup_attempt(
|
||||||
|
cooldown_message: String = "",
|
||||||
|
visual_outcome: StringName = &"",
|
||||||
|
) -> void:
|
||||||
if _active_player != null:
|
if _active_player != null:
|
||||||
_active_player.set_movement_enabled(true)
|
_active_player.set_movement_enabled(true)
|
||||||
_active_player = null
|
_active_player = null
|
||||||
|
_state_time_remaining = 0.0
|
||||||
|
_cast_charge = 0.0
|
||||||
|
_cast_direction = Vector3.FORWARD
|
||||||
|
_cast_origin_position = Vector3.ZERO
|
||||||
|
_cast_target = Vector3.ZERO
|
||||||
|
_cast_landing_is_fishable = false
|
||||||
|
_withdrawal_endpoint = Vector3.ZERO
|
||||||
|
_withdrawal_progress = 0.0
|
||||||
|
_withdrawal_input_held = false
|
||||||
_reel_input_held = false
|
_reel_input_held = false
|
||||||
_reel_progress = 0.0
|
_reel_progress = 0.0
|
||||||
reel_progress_changed.emit(0.0, false)
|
reel_progress_changed.emit(0.0, false)
|
||||||
|
if visual_outcome.is_empty():
|
||||||
|
_presentation.cleanup()
|
||||||
|
else:
|
||||||
|
_presentation.play_outcome(visual_outcome)
|
||||||
|
|
||||||
if cooldown_message.is_empty():
|
if cooldown_message.is_empty():
|
||||||
state = FishingState.READY
|
state = FishingState.READY
|
||||||
_state_time_remaining = 0.0
|
|
||||||
_cooldown_status = ""
|
_cooldown_status = ""
|
||||||
if _nearby_player != null:
|
if _nearby_player != null:
|
||||||
status_changed.emit("Left click to cast")
|
status_changed.emit("Left click to cast")
|
||||||
|
|
@ -178,11 +338,56 @@ func _cleanup_attempt(cooldown_message: String = "") -> void:
|
||||||
|
|
||||||
|
|
||||||
func _return_to_ready() -> void:
|
func _return_to_ready() -> void:
|
||||||
_cleanup_attempt()
|
state = FishingState.READY
|
||||||
|
_state_time_remaining = 0.0
|
||||||
|
_cooldown_status = ""
|
||||||
|
if _nearby_player != null:
|
||||||
|
status_changed.emit("Left click to cast")
|
||||||
|
else:
|
||||||
|
status_changed.emit("")
|
||||||
|
|
||||||
|
|
||||||
func _cancel_attempt() -> void:
|
func _cancel_attempt() -> void:
|
||||||
_cleanup_attempt()
|
_cleanup_attempt("Fishing cancelled.", &"cancel")
|
||||||
|
|
||||||
|
|
||||||
|
func _cancel_from_withdrawal() -> void:
|
||||||
|
if state != FishingState.WAITING_FOR_BITE:
|
||||||
|
return
|
||||||
|
_new_cast_press_armed = false
|
||||||
|
_cleanup_attempt("", &"withdrawal")
|
||||||
|
|
||||||
|
|
||||||
|
func _capture_cast_direction(player: PlayerType) -> Vector3:
|
||||||
|
var direction: Vector3 = player.get_facing_direction()
|
||||||
|
direction.y = 0.0
|
||||||
|
if direction.is_zero_approx():
|
||||||
|
direction = -player.global_basis.z
|
||||||
|
direction.y = 0.0
|
||||||
|
if direction.is_zero_approx():
|
||||||
|
return Vector3.FORWARD
|
||||||
|
return direction.normalized()
|
||||||
|
|
||||||
|
|
||||||
|
func _calculate_cast_target(distance: float) -> Vector3:
|
||||||
|
return Vector3(
|
||||||
|
_cast_origin_position.x + _cast_direction.x * distance,
|
||||||
|
_presentation.get_water_surface_height(),
|
||||||
|
_cast_origin_position.z + _cast_direction.z * distance
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
func _is_landing_fishable(target: Vector3) -> bool:
|
||||||
|
var query := PhysicsPointQueryParameters3D.new()
|
||||||
|
query.position = target
|
||||||
|
query.collision_mask = fishable_surface_mask
|
||||||
|
query.collide_with_areas = true
|
||||||
|
query.collide_with_bodies = false
|
||||||
|
var results: Array[Dictionary] = get_world_3d().direct_space_state.intersect_point(
|
||||||
|
query,
|
||||||
|
1
|
||||||
|
)
|
||||||
|
return not results.is_empty()
|
||||||
|
|
||||||
|
|
||||||
func _on_body_entered(body: Node3D) -> void:
|
func _on_body_entered(body: Node3D) -> void:
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,8 @@
|
||||||
[gd_scene load_steps=7 format=3]
|
[gd_scene load_steps=13 format=3]
|
||||||
|
|
||||||
[ext_resource type="Script" path="res://fishing/fishing_spot.gd" id="1_spot"]
|
[ext_resource type="Script" path="res://fishing/fishing_spot.gd" id="1_spot"]
|
||||||
[ext_resource type="Resource" path="res://fish/bluegill.tres" id="2_bluegill"]
|
[ext_resource type="Resource" path="res://fish/bluegill.tres" id="2_bluegill"]
|
||||||
|
[ext_resource type="Script" path="res://fishing/fishing_presentation.gd" id="3_presentation"]
|
||||||
|
|
||||||
[sub_resource type="SphereShape3D" id="RangeShape"]
|
[sub_resource type="SphereShape3D" id="RangeShape"]
|
||||||
radius = 3.5
|
radius = 3.5
|
||||||
|
|
@ -22,6 +23,30 @@ emission_enabled = true
|
||||||
emission = Color(0.3, 0.2, 0.01, 1)
|
emission = Color(0.3, 0.2, 0.01, 1)
|
||||||
emission_energy_multiplier = 0.7
|
emission_energy_multiplier = 0.7
|
||||||
|
|
||||||
|
[sub_resource type="SphereMesh" id="BobberMesh"]
|
||||||
|
radius = 0.12
|
||||||
|
height = 0.24
|
||||||
|
|
||||||
|
[sub_resource type="StandardMaterial3D" id="BobberMaterial"]
|
||||||
|
albedo_color = Color(0.92, 0.12, 0.08, 1)
|
||||||
|
roughness = 0.45
|
||||||
|
|
||||||
|
[sub_resource type="StandardMaterial3D" id="LineMaterial"]
|
||||||
|
shading_mode = 0
|
||||||
|
albedo_color = Color(0.9, 0.9, 0.82, 1)
|
||||||
|
|
||||||
|
[sub_resource type="CylinderMesh" id="CastTargetMesh"]
|
||||||
|
top_radius = 0.28
|
||||||
|
bottom_radius = 0.28
|
||||||
|
height = 0.035
|
||||||
|
|
||||||
|
[sub_resource type="StandardMaterial3D" id="CastTargetMaterial"]
|
||||||
|
shading_mode = 0
|
||||||
|
albedo_color = Color(0.3, 0.95, 0.72, 0.9)
|
||||||
|
emission_enabled = true
|
||||||
|
emission = Color(0.08, 0.35, 0.22, 1)
|
||||||
|
emission_energy_multiplier = 0.8
|
||||||
|
|
||||||
[node name="FishingSpot" type="Area3D"]
|
[node name="FishingSpot" type="Area3D"]
|
||||||
collision_layer = 0
|
collision_layer = 0
|
||||||
collision_mask = 2
|
collision_mask = 2
|
||||||
|
|
@ -42,3 +67,21 @@ material_override = SubResource("MarkerMaterial")
|
||||||
position = Vector3(0, 0.03, 0)
|
position = Vector3(0, 0.03, 0)
|
||||||
mesh = SubResource("RangeMarkerMesh")
|
mesh = SubResource("RangeMarkerMesh")
|
||||||
material_override = SubResource("MarkerMaterial")
|
material_override = SubResource("MarkerMaterial")
|
||||||
|
|
||||||
|
[node name="FishingPresentation" type="Node3D" parent="."]
|
||||||
|
unique_name_in_owner = true
|
||||||
|
script = ExtResource("3_presentation")
|
||||||
|
|
||||||
|
[node name="CastTargetMarker" type="MeshInstance3D" parent="FishingPresentation"]
|
||||||
|
unique_name_in_owner = true
|
||||||
|
mesh = SubResource("CastTargetMesh")
|
||||||
|
material_override = SubResource("CastTargetMaterial")
|
||||||
|
|
||||||
|
[node name="FishingLine" type="MeshInstance3D" parent="FishingPresentation"]
|
||||||
|
unique_name_in_owner = true
|
||||||
|
material_override = SubResource("LineMaterial")
|
||||||
|
|
||||||
|
[node name="Bobber" type="MeshInstance3D" parent="FishingPresentation"]
|
||||||
|
unique_name_in_owner = true
|
||||||
|
mesh = SubResource("BobberMesh")
|
||||||
|
material_override = SubResource("BobberMaterial")
|
||||||
|
|
|
||||||
|
|
@ -31,6 +31,9 @@ const FishInventoryType = preload("res://inventory/fish_inventory.gd")
|
||||||
@onready var _spring_arm: SpringArm3D = %SpringArm3D
|
@onready var _spring_arm: SpringArm3D = %SpringArm3D
|
||||||
@onready var _camera: Camera3D = %Camera3D
|
@onready var _camera: Camera3D = %Camera3D
|
||||||
@onready var inventory: FishInventoryType = %Inventory
|
@onready var inventory: FishInventoryType = %Inventory
|
||||||
|
@onready var _cast_origin: Marker3D = %CastOrigin
|
||||||
|
@onready var _fishing_rod: Node3D = %FishingRod
|
||||||
|
@onready var _fishing_rod_tip: Marker3D = %FishingRodTip
|
||||||
|
|
||||||
var _gravity: float = float(ProjectSettings.get_setting("physics/3d/default_gravity"))
|
var _gravity: float = float(ProjectSettings.get_setting("physics/3d/default_gravity"))
|
||||||
var _camera_dragging: bool = false
|
var _camera_dragging: bool = false
|
||||||
|
|
@ -171,3 +174,26 @@ func set_movement_enabled(enabled: bool) -> void:
|
||||||
if not enabled:
|
if not enabled:
|
||||||
velocity.x = 0.0
|
velocity.x = 0.0
|
||||||
velocity.z = 0.0
|
velocity.z = 0.0
|
||||||
|
|
||||||
|
|
||||||
|
func get_fishing_rod_tip() -> Marker3D:
|
||||||
|
return _fishing_rod_tip
|
||||||
|
|
||||||
|
|
||||||
|
func get_fishing_rod() -> Node3D:
|
||||||
|
return _fishing_rod
|
||||||
|
|
||||||
|
|
||||||
|
func get_cast_origin_position() -> Vector3:
|
||||||
|
return _cast_origin.global_position
|
||||||
|
|
||||||
|
|
||||||
|
func get_facing_direction() -> Vector3:
|
||||||
|
var facing: Vector3 = -_visuals.global_basis.z
|
||||||
|
facing.y = 0.0
|
||||||
|
if facing.is_zero_approx():
|
||||||
|
facing = -global_basis.z
|
||||||
|
facing.y = 0.0
|
||||||
|
if facing.is_zero_approx():
|
||||||
|
return Vector3.FORWARD
|
||||||
|
return facing.normalized()
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,4 @@
|
||||||
[gd_scene load_steps=8 format=3]
|
[gd_scene load_steps=10 format=3]
|
||||||
|
|
||||||
[ext_resource type="Script" path="res://player/player.gd" id="1_script"]
|
[ext_resource type="Script" path="res://player/player.gd" id="1_script"]
|
||||||
[ext_resource type="Script" path="res://inventory/fish_inventory.gd" id="2_inventory"]
|
[ext_resource type="Script" path="res://inventory/fish_inventory.gd" id="2_inventory"]
|
||||||
|
|
@ -20,6 +20,15 @@ size = Vector3(0.24, 0.2, 0.4)
|
||||||
[sub_resource type="StandardMaterial3D" id="FacingMarkerMaterial"]
|
[sub_resource type="StandardMaterial3D" id="FacingMarkerMaterial"]
|
||||||
albedo_color = Color(0.12, 0.18, 0.28, 1)
|
albedo_color = Color(0.12, 0.18, 0.28, 1)
|
||||||
|
|
||||||
|
[sub_resource type="CylinderMesh" id="FishingRodMesh"]
|
||||||
|
top_radius = 0.025
|
||||||
|
bottom_radius = 0.045
|
||||||
|
height = 1.2
|
||||||
|
|
||||||
|
[sub_resource type="StandardMaterial3D" id="FishingRodMaterial"]
|
||||||
|
albedo_color = Color(0.22, 0.12, 0.06, 1)
|
||||||
|
roughness = 0.8
|
||||||
|
|
||||||
[node name="Player" type="CharacterBody3D"]
|
[node name="Player" type="CharacterBody3D"]
|
||||||
collision_layer = 2
|
collision_layer = 2
|
||||||
collision_mask = 1
|
collision_mask = 1
|
||||||
|
|
@ -42,6 +51,24 @@ position = Vector3(0, 1.0, -0.55)
|
||||||
mesh = SubResource("FacingMarkerMesh")
|
mesh = SubResource("FacingMarkerMesh")
|
||||||
material_override = SubResource("FacingMarkerMaterial")
|
material_override = SubResource("FacingMarkerMaterial")
|
||||||
|
|
||||||
|
[node name="FishingRod" type="Node3D" parent="Visuals"]
|
||||||
|
unique_name_in_owner = true
|
||||||
|
position = Vector3(0.42, 1.05, -0.2)
|
||||||
|
rotation = Vector3(-0.436332, 0, 0)
|
||||||
|
|
||||||
|
[node name="RodMesh" type="MeshInstance3D" parent="Visuals/FishingRod"]
|
||||||
|
position = Vector3(0, 0.6, 0)
|
||||||
|
mesh = SubResource("FishingRodMesh")
|
||||||
|
material_override = SubResource("FishingRodMaterial")
|
||||||
|
|
||||||
|
[node name="FishingRodTip" type="Marker3D" parent="Visuals/FishingRod"]
|
||||||
|
unique_name_in_owner = true
|
||||||
|
position = Vector3(0, 1.2, 0)
|
||||||
|
|
||||||
|
[node name="CastOrigin" type="Marker3D" parent="."]
|
||||||
|
unique_name_in_owner = true
|
||||||
|
position = Vector3(0, 0.9, 0)
|
||||||
|
|
||||||
[node name="Inventory" type="Node" parent="."]
|
[node name="Inventory" type="Node" parent="."]
|
||||||
unique_name_in_owner = true
|
unique_name_in_owner = true
|
||||||
script = ExtResource("2_inventory")
|
script = ExtResource("2_inventory")
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,4 @@
|
||||||
[gd_scene load_steps=10 format=3]
|
[gd_scene load_steps=11 format=3]
|
||||||
|
|
||||||
[sub_resource type="BoxShape3D" id="GroundShape"]
|
[sub_resource type="BoxShape3D" id="GroundShape"]
|
||||||
size = Vector3(30, 1, 30)
|
size = Vector3(30, 1, 30)
|
||||||
|
|
@ -21,6 +21,9 @@ albedo_color = Color(0.42, 0.25, 0.12, 1)
|
||||||
[sub_resource type="PlaneMesh" id="WaterMesh"]
|
[sub_resource type="PlaneMesh" id="WaterMesh"]
|
||||||
size = Vector2(40, 25)
|
size = Vector2(40, 25)
|
||||||
|
|
||||||
|
[sub_resource type="BoxShape3D" id="FishableWaterShape"]
|
||||||
|
size = Vector3(40, 0.5, 19.5)
|
||||||
|
|
||||||
[sub_resource type="StandardMaterial3D" id="WaterMaterial"]
|
[sub_resource type="StandardMaterial3D" id="WaterMaterial"]
|
||||||
transparency = 1
|
transparency = 1
|
||||||
albedo_color = Color(0.1, 0.45, 0.7, 0.72)
|
albedo_color = Color(0.1, 0.45, 0.7, 0.72)
|
||||||
|
|
@ -65,3 +68,12 @@ material_override = SubResource("DockMaterial")
|
||||||
position = Vector3(0, 0.2, -27)
|
position = Vector3(0, 0.2, -27)
|
||||||
mesh = SubResource("WaterMesh")
|
mesh = SubResource("WaterMesh")
|
||||||
material_override = SubResource("WaterMaterial")
|
material_override = SubResource("WaterMaterial")
|
||||||
|
|
||||||
|
[node name="FishableWater" type="Area3D" parent="."]
|
||||||
|
position = Vector3(0, 0.2, -29.75)
|
||||||
|
collision_layer = 4
|
||||||
|
collision_mask = 0
|
||||||
|
monitoring = false
|
||||||
|
|
||||||
|
[node name="CollisionShape3D" type="CollisionShape3D" parent="FishableWater"]
|
||||||
|
shape = SubResource("FishableWaterShape")
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue