NyoomMod/Scenes/Entities/Player/player.gd

2344 lines
72 KiB
GDScript3
Raw Normal View History

2024-10-22 15:07:57 -04:00
extends Actor
enum STATES{DEFAULT, BUSY, OBTAIN, EMOTING, FREECAM, FISHING_CHARGE, FISHING_CAST, FISHING, FISHING_CANCEL, FISHING_STRUGGLE, SHOVEL_CAST, SHOVEL_STRUGGLE, SHOVEL_CANCEL, \
NET_CAST, NET_STRUGGLE, NET_CANCEL, SHOWCASE, CONSUME_ITEM, METAL_DETECTOR, GUITAR, GAMBLING}
const GRAVITY = 32.0
const BAIT_DATA = {
"": {"catch": 0.0, "max_tier": 0, "quality": []},
"worms": {"catch": 0.06, "max_tier": 1, "quality": [1.0]},
"cricket": {"catch": 0.06, "max_tier": 2, "quality": [1.0, 0.05]},
"leech": {"catch": 0.06, "max_tier": 2, "quality": [1.0, 0.15, 0.05]},
"minnow": {"catch": 0.06, "max_tier": 2, "quality": [1.0, 0.5, 0.25, 0.05]},
"squid": {"catch": 0.06, "max_tier": 2, "quality": [1.0, 0.8, 0.45, 0.15, 0.05]},
"nautilus": {"catch": 0.06, "max_tier": 2, "quality": [1.0, 0.98, 0.75, 0.55, 0.25, 0.05]},
}
const PARTICLE_DATA = {
"dust_run": preload("res://Scenes/Particles/dust_running.tscn"),
"dust_land": preload("res://Scenes/Particles/dust_land.tscn"),
"splash": preload("res://Scenes/Particles/water_splash.tscn"),
"small_splash": preload("res://Scenes/Particles/small_splash.tscn"),
"music": preload("res://Scenes/Particles/music_particle.tscn"),
"kiss": preload("res://Scenes/Particles/kiss.tscn"),
}
export (NodePath) var hand_sprite_node
export (NodePath) var hand_bone_node
export var NPC_body = false
export var NPC_cosmetics = {"species": "species_cat", "pattern": "pattern_none", "primary_color": "pcolor_white", "secondary_color": "scolor_tan", "hat": "hat_none", "undershirt": "shirt_none", "overshirt": "overshirt_none", "title": "title_rank_1", "bobber": "bobber_default", "eye": "eye_halfclosed", "nose": "nose_cat", "mouth": "mouth_default", "accessory": [], "tail": "tail_cat"}
export var NPC_name = "NPC Test"
export var NPC_title = "npc title here"
var camera_zoom = 5.0
var direction = Vector3()
var gravity_vec = Vector3()
var dive_vec = Vector3()
var velocity = Vector3()
var player_scale = 1.0
var player_scale_y = 1.0
var cam_orbit_node = null
var cam_follow_node = null
var cam_push = 0.0
var cam_push_cur = 0.0
var cam_return = 1.0
var force_cam_look = false
var mouse_world_pos = Vector3()
var mouse_look = false
var state = STATES.DEFAULT
var walk_speed = 3.2
var slow_walk_speed = 1.0
var sprint_speed = 6.88
var sneak_speed = 1.3
var jump_height = 7.5
var jump_leap = 0.0
var dive_distance = 15.0
var accel = 64.0
var sprinting = false
var slow_walking = false
var sneaking = false
var diving = false
var request_jump = false
var ignore_snap = 0
var snapped = false
var sitting = false
var locked = false
var busy = false
var in_air = false
var gravity_disable = false
var hud
var int_text = ""
var interact_cooldown = 60
var xp_buildup = 0
var showcase_ref
var held_item = PlayerData.FALLBACK_ITEM
var caught_item = PlayerData.FALLBACK_ITEM
var held_item_weight = 1.0
var previous_item = 0
var primary_hold_timer = 0
var fish_zone_data = {}
var rod_cast_data = ""
var casted_bait = ""
var rod_cast_dist = 0.0
var rod_depth = 0
var failed_casts = 0
var recent_reel = 0.0
var item_scene = null
var bobber_hpos = Vector3()
var bobber_vpos = 0
var bobber_control = true
var last_valid_pos = Vector3()
var retract_splash = false
var show_local = false
var bait_warn = 2
var in_rain = false
var consume_on_state_change = - 1
var item_cooldown = 0
var rod_damage = 1.0
var rod_spd = 1.0
var rod_chance = 0.0
var boost_timer = 0
var boost_amt = 1.3
var boost_mult = 1.0
var catch_drink_timer = 0
var catch_drink_boost = 1.0
var catch_drink_reel = 1.0
var catch_drink_xp = 1.0
var catch_drink_gold_add = Vector2(0, 0)
var catch_drink_gold_percent = 0.0
var catch_drink_tier = 0
var drunk_timer = 0
var drunk_tier = 0
var drunk_wander_length = 0
var drunk_wander_dir = 0
var death_counter = 0
var metal_detect_flop = false
var metal_detect_alert_level = 0
var metal_detect_alert_cd = 0
var emoting = false
var emote_full = false
var emote_locked = false
var emote_looping = false
var animation_timer = 0
var animation_goal = 99
var buffer_state = - 1
var animation_data = {
"moving": false,
"sprinting": false,
"sneaking": false,
"emoting": false,
"emote_full": false,
"diving": false,
"sitting": false,
"alert": false,
"emote": "",
"arm_state": "",
"caught_item": {},
"arm_value": 0.0,
"item_bend": 0.0,
"busy": false,
"land": 0.0,
"talking": 0.0,
"recent_reel": 0.0,
"bobber_position": Vector3(),
"bobber_visible": false,
"caught_fish": false,
"player_scale": 1.0,
"player_scale_y": 1.0,
"mushroom": false,
"run_mult": 1.0,
"walk_mult": 1.25,
"drunk_tier": 0,
"wagging": false,
"emote_timescale": 1.0,
"back_bend": 0.0,
"dive_scrape": false,
"reel_slow": false,
"reel_fast": false,
"state": state,
}
var custom_held_item = ""
var cosmetic_data = {}
var selected_build_object
var old_rot = Vector3()
var rot_diff = 0.0
var prop_ids = []
var cam_move = false
var freecamming = false
onready var cam_base = $cam_base
onready var cam_pivot = $cam_base / cam_pivot
onready var camera = $Camera
onready var camera_point = $cam_base / cam_pivot / SpringArm / camera_point
onready var cam_arm = $cam_base / cam_pivot / SpringArm
onready var body = $body
onready var body_mesh = $body / player_body / Armature / Skeleton / body_main
onready var rot_help = $rot_help
onready var interact_range = $interact_range
onready var anim_tree = $body / AnimationTree
onready var skeleton = $body / player_body / Armature / Skeleton
onready var title = $Viewport / player_label
onready var item_sprite = get_node(hand_sprite_node)
onready var hand_bone = get_node(hand_bone_node)
onready var sound_emit = $sound_emit
onready var face = $body / player_body / Armature / Skeleton / face / player_face
onready var tail = $body / player_body / Armature / Skeleton / tail / holder / tail
onready var freecam_anchor = $camera_freecam_anchor
onready var sound_manager = $sound_manager
onready var fish_detect = $detection_zones / fishing_detect
onready var fishing_update = $detection_zones / fishing_update
onready var fishing_area = $detection_zones / fishing_detect / fishing_area
onready var fish_timer = $fish_catch_timer
onready var bobber_preview = $bobber_preview
onready var bobber = $bobber
onready var bobber_mesh = $bobber / bobber_mesh
onready var bobber_line = $bobber / line
onready var ripples = $bobber / ripples
onready var caught_fish = $bobber / caught_item
onready var shovel_area = $detection_zones / shovel_detect
onready var net_area = $detection_zones / net_detect
onready var safe_check = $safe_check
onready var lvlparticle = $emotion_particles / lvl_particles
onready var lvlparticleb = $emotion_particles / lvl_particles2
signal _animation_finished
signal _primary_release
signal _state_change
signal _menu_closed
func _ready():
add_to_group("player")
rot_help.set_as_toplevel(true)
cam_base.set_as_toplevel(true)
camera.set_as_toplevel(true)
freecam_anchor.set_as_toplevel(true)
anim_tree.tree_root = anim_tree.tree_root.duplicate(true)
_update_cosmetics(cosmetic_data)
title.visible = not dead_actor
if NPC_body:
camera.queue_free()
remove_from_group("player")
yield (get_tree(), "idle_frame")
var new = NPC_cosmetics
Network._send_actor_action(actor_id, "_update_cosmetics", [new])
_update_cosmetics(new)
yield (get_tree(), "idle_frame")
title.visible = true
title.label = NPC_name
title.title = NPC_title
func _setup_controlled():
if NPC_body: return
add_to_group("controlled_player")
camera = $Camera
camera.current = true
hud = load("res://Scenes/HUD/playerhud.tscn").instance()
hud.player = self
hud.connect("_player_sit", self, "_toggle_sit")
hud.connect("_play_emote", self, "_play_emote")
hud.connect("_menu_entered", self, "_hud_menu_entered")
hud.connect("_message_sent", self, "_message_sent")
hud.connect("_freecam_toggle", self, "_toggle_freecam")
get_tree().get_root().add_child(hud)
PlayerData.connect("_clear_all_props", self, "_clear_all_props")
PlayerData.connect("_place_prop", self, "_create_prop")
PlayerData.connect("_play_sfx", self, "_play_sfx")
PlayerData.connect("_play_guitar", self, "_strum_guitar")
PlayerData.connect("_hammer_guitar", self, "_hammer_string")
PlayerData.connect("_return_to_spawn", self, "_return_to_spawn")
PlayerData.connect("_punched", self, "_punched")
PlayerData.connect("_item_removal", self, "_item_removal")
var delayed_update = get_tree().create_timer(1.0).connect("timeout", self, "_change_cosmetics")
PlayerData.connect("_wag_toggle", self, "_wag")
PlayerData.connect("_kiss", self, "_kiss")
Network.connect("_user_connected", self, "_refresh_cosmetics")
func _setup_not_controlled():
camera = $Camera
camera.queue_free()
$bobber_preview.visible = false
$local_range.visible = false
$detection_zones / metal_detect_consume.monitoring = false
$detection_zones / metal_detect_consume.monitorable = false
$detection_zones / prop_detect.monitoring = false
$detection_zones / metal_detect_far.monitoring = false
$detection_zones / metal_detect.monitoring = false
$detection_zones / metal_detect_close.monitoring = false
$detection_zones / metal_detect_veryclose.monitoring = false
$detection_zones / punch.monitoring = false
$interact_range.monitoring = false
$water_detect.monitoring = false
$raincloud_check.monitoring = false
func _physics_process(delta):
_process_animation()
_process_sounds()
if ( not in_zone != $CollisionShape.disabled):
$CollisionShape.disabled = not in_zone
func _process(delta):
if controlled: $paint_node._paint_process(delta)
func _controlled_process(delta):
_get_input()
_process_movement(delta)
_process_timers()
_interact_check()
_update_animation_data()
_camera_update()
_freecam_input(delta)
body.visible = camera_zoom > 0.5 or cam_orbit_node != null
current_zone = world.active_zone
current_zone_owner = world.active_zone_owner
fish_detect.translation.z = - rod_cast_dist
bobber_preview.translation.z = - clamp(primary_hold_timer * 0.06, 1.5, 9.0)
if bobber_preview.is_colliding(): $"%bobber_prev_mesh".global_transform.origin = bobber_preview.get_collision_point() + Vector3(0, 0.05, 0)
bobber_preview.visible = state == STATES.FISHING_CHARGE
if recent_reel > 0: recent_reel -= 1
if interact_cooldown > 0: interact_cooldown -= 1
animation_data["reel_slow"] = recent_reel > 8
animation_data["reel_fast"] = state == STATES.FISHING_STRUGGLE
if (packet_send_interval == - 1 or Engine.get_physics_frames() % packet_send_interval == 0):
Network._send_actor_animation_update(actor_id, animation_data)
Network.MESSAGE_ORIGIN = global_transform.origin
$local_range.visible = show_local
$paint_node.global_transform.origin = mouse_world_pos
$metaldetect_dot.modulate.a = lerp($metaldetect_dot.modulate.a, 0.0, 0.1)
func _camera_update():
cam_push_cur = lerp(cam_push_cur, cam_push * rod_cast_dist, 0.2)
var push = global_transform.basis.z * cam_push_cur
camera_zoom = clamp(camera_zoom, 0.0, 20.0)
var cam_zoom = camera_zoom
var cam_zoom_lerp = 0.4
var cam_follow_point = true
var cam_follow_pos = Vector3()
var cam_follow_rot = Vector3()
var sit_add = Vector3(0, - 0.2, 0.0) if sitting else Vector3.ZERO
var cam_base_pos = global_transform.origin + push + sit_add
var desired_fov = 50
if sprinting and direction != Vector3.ZERO:
desired_fov += 2
if boost_timer > 0: desired_fov += 2 * boost_amt
if animation_data["mushroom"]: desired_fov += 10
camera.fov = lerp(camera.fov, desired_fov, 0.2)
if is_instance_valid(cam_orbit_node) and cam_orbit_node.is_visible_in_tree():
cam_base_pos = cam_orbit_node.global_transform.origin
if is_instance_valid(cam_follow_node) and cam_follow_node.is_visible_in_tree():
cam_follow_point = false
cam_follow_pos = cam_follow_node.global_transform.origin
cam_follow_rot = cam_follow_node.global_rotation
var cam_speed = 0.08
if cam_follow_point:
cam_return = lerp(cam_return, 1.0, cam_speed * 0.5)
camera.global_transform.origin = lerp(camera.global_transform.origin, camera_point.global_transform.origin, cam_return)
camera.rotation.x = lerp_angle(camera.rotation.x, camera_point.global_rotation.x, cam_return)
camera.rotation.y = lerp_angle(camera.rotation.y, camera_point.global_rotation.y, cam_return)
camera.rotation.z = lerp_angle(camera.rotation.z, camera_point.global_rotation.z, cam_return)
else :
cam_return = 0.0
camera.global_transform.origin = lerp(camera.global_transform.origin, cam_follow_pos, cam_speed)
camera.rotation.x = lerp_angle(camera.rotation.x, cam_follow_rot.x, cam_speed)
camera.rotation.y = lerp_angle(camera.rotation.y, cam_follow_rot.y, cam_speed)
camera.rotation.z = lerp_angle(camera.rotation.z, cam_follow_rot.z, cam_speed)
cam_base.global_transform.origin = cam_base_pos
cam_arm.spring_length = lerp(cam_arm.spring_length, cam_zoom, cam_zoom_lerp)
func _interact_check():
if not controlled: return
var in_range = false
for area in interact_range.get_overlapping_areas():
if area.is_in_group("interactable") and area.is_visible_in_tree():
int_text = area.text
in_range = true
break
if hud:
hud.interact = in_range
hud.int_text = int_text
func _hud_menu_entered(menu):
if menu == 0:
if not freecamming:
cam_orbit_node = null
cam_follow_node = null
force_cam_look = false
_exit_showcase()
emit_signal("_menu_closed")
func _process_timers():
if catch_drink_timer > 0:
catch_drink_timer -= 1
if catch_drink_timer <= 0:
catch_drink_boost = 1.0
catch_drink_reel = 1.0
catch_drink_xp = 1.0
catch_drink_gold_add = Vector2(0, 0)
catch_drink_gold_percent = 0.0
drunk_tier = 0
if drunk_timer > 0:
drunk_timer -= 1
if drunk_timer >= 19000: drunk_tier = 3
elif drunk_timer >= 10000: drunk_tier = 2
elif drunk_timer > 0: drunk_tier = 1
else : drunk_tier = 0
if item_cooldown > 0: item_cooldown -= 1
func _get_input():
direction = Vector3.ZERO
if Input.is_action_just_released("primary_action"): _primary_action_release()
if Input.is_action_pressed("primary_action"): _primary_action_hold()
else : primary_hold_timer = 0
if busy:
Input.set_mouse_mode(Input.MOUSE_MODE_VISIBLE)
return
if Input.is_action_pressed("secondary_action") or camera_zoom <= 0.0:
if Input.mouse_mode != Input.MOUSE_MODE_CAPTURED:
PlayerData.original_mouse_position = get_tree().get_nodes_in_group("world_viewport")[0].get_mouse_position()
Input.set_mouse_mode(Input.MOUSE_MODE_CAPTURED)
else :
if Input.mouse_mode != Input.MOUSE_MODE_VISIBLE:
Input.set_mouse_mode(Input.MOUSE_MODE_VISIBLE)
Input.warp_mouse_position(PlayerData.original_mouse_position)
if Input.is_action_just_released("zoom_in"): camera_zoom -= 0.5
if Input.is_action_just_released("zoom_out"): camera_zoom += 0.5
if Input.is_action_just_pressed("interact"): _interact()
if Input.is_action_just_pressed("bind_1"): _equip_hotbar(0)
if Input.is_action_just_pressed("bind_2"): _equip_hotbar(1)
if Input.is_action_just_pressed("bind_3"): _equip_hotbar(2)
if Input.is_action_just_pressed("bind_4"): _equip_hotbar(3)
if Input.is_action_just_pressed("bind_5"): _equip_hotbar(4)
if Input.is_action_just_pressed("bark"):
_bark()
if Input.is_action_just_pressed("kiss"):
_kiss()
if locked: return
if is_instance_valid(camera):
var camera_cam = camera
var ray_length = 1000
var mouse_pos = get_tree().get_nodes_in_group("world_viewport")[0].get_mouse_position() / Globals.pixelize_amount
var from = camera_cam.project_ray_origin(mouse_pos)
var to = from + camera_cam.project_ray_normal(mouse_pos) * ray_length
var space_state = get_world().get_direct_space_state()
var result = space_state.intersect_ray(from, to, [])
if result.has("position"):
mouse_world_pos = result["position"]
mouse_world_pos.y = global_transform.origin.y
if freecamming: return
if Input.is_action_just_pressed("move_jump"): request_jump = true
mouse_look = false
if sitting: return
if Input.is_action_pressed("move_forward"): direction -= cam_base.transform.basis.z
if Input.is_action_pressed("move_back"): direction += cam_base.transform.basis.z
if Input.is_action_pressed("move_right"): direction += cam_base.transform.basis.x
if Input.is_action_pressed("move_left"): direction -= cam_base.transform.basis.x
if drunk_wander_length > 0:
direction += drunk_wander_dir
drunk_wander_length -= 1
mouse_look = Input.is_action_pressed("mouse_look")
sprinting = not Input.is_action_pressed("move_sneak") and Input.is_action_pressed("move_sprint")
sneaking = Input.is_action_pressed("move_sneak") and not Input.is_action_pressed("move_sprint")
slow_walking = Input.is_action_pressed("move_walk")
if emote_locked:
request_jump = false
direction = Vector3.ZERO
func _input(event):
if not controlled: return
var mouse_sens = OptionsMenu.mouse_sens
var invert = Vector2(1, 1)
if OptionsMenu.mouse_invert == 1: invert.x = - 1
elif OptionsMenu.mouse_invert == 2: invert.y = - 1
elif OptionsMenu.mouse_invert == 3: invert = Vector2( - 1, - 1)
if event is InputEventMouseMotion and Input.get_mouse_mode() == Input.MOUSE_MODE_CAPTURED:
cam_base.rotation_degrees.y -= event.relative.x * mouse_sens * invert.x
cam_pivot.rotation_degrees.x -= event.relative.y * mouse_sens * invert.y
cam_pivot.rotation_degrees.x = clamp(cam_pivot.rotation_degrees.x, - 80, 80)
func _unhandled_input(event):
if not controlled: return
if event.is_action_pressed("primary_action"): _primary_action()
func _process_movement(delta):
var snap_vec = Vector3(0, - 0.4, 0)
var y_slow = 0.02
if is_on_floor() and in_air:
if gravity_vec.y <= - 12.0 and not diving:
diving = true
dive_vec = gravity_vec.length() * - transform.basis.z * 0.2
player_scale_y = 1.0 - clamp(((abs(gravity_vec.y) / 24.0) * 6.0), 0.0, 0.6)
in_air = false
_sync_particle("dust_land", Vector3(0, - 1, 0))
_sync_sfx("land")
animation_data["land"] = 0.3
elif gravity_vec.y < - 6.0:
in_air = true
player_scale_y = lerp(player_scale_y, 1.0, 0.35)
if is_on_floor() and ignore_snap <= 0:
y_slow = 0.2
gravity_vec = get_floor_normal() * - 1.0
snapped = true
elif snapped and ignore_snap <= 0:
gravity_vec = Vector3.ZERO
snapped = false
else :
snap_vec = Vector3.ZERO
var grav = GRAVITY * Vector3.DOWN * delta
gravity_vec += grav
if request_jump:
request_jump = false
snap_vec = Vector3.ZERO
if is_on_floor():
_sync_sfx("jump")
snapped = false
diving = false
gravity_vec = Vector3(0, jump_height, 0)
if sprinting and jump_leap > 0.0:
gravity_vec += - transform.basis.z.normalized() * jump_leap
elif not diving:
_sync_sfx("dive_woosh")
snapped = false
diving = true
dive_vec = - transform.basis.z.normalized() * dive_distance
dive_vec.y = 0
gravity_vec += Vector3(0, jump_height * 1, 0)
_toggle_sit(true)
if ignore_snap > 0:
snap_vec = Vector3.ZERO
ignore_snap -= 1
snapped = false
boost_mult = 1.0
if boost_timer > 0:
boost_timer -= 1
boost_mult = boost_amt
var _speed = walk_speed
if sprinting: _speed = sprint_speed * boost_mult
elif sneaking: _speed = sneak_speed
elif slow_walking: _speed = slow_walk_speed
var _accel = accel if is_on_floor() else accel * 0.35
var speed_mult = 1.35
speed_mult = clamp(speed_mult - ((held_item_weight / 25.0) * 0.05), 0.15, 1.0)
if diving: speed_mult = 1.5
if gravity_disable: gravity_vec = Vector3.ZERO
velocity = velocity.move_toward(direction.normalized() * _speed * speed_mult, delta * _accel)
move_and_slide_with_snap(velocity + gravity_vec + dive_vec, snap_vec, Vector3.UP)
dive_vec = dive_vec.move_toward(Vector3.ZERO, delta * _accel * y_slow)
if not diving: dive_vec = Vector3.ZERO
rot_help.global_transform.origin = global_transform.origin
if direction != Vector3.ZERO:
var dir = direction
if diving and dive_vec != Vector3.ZERO: dir = dive_vec.normalized()
rot_help.look_at(cam_base.global_transform.origin + dir, Vector3.UP)
rotation.y = lerp_angle(rotation.y, rot_help.rotation.y, 0.2)
elif force_cam_look:
rot_help.look_at(camera.global_transform.origin, Vector3.UP)
rotation.y = lerp_angle(rotation.y, rot_help.rotation.y, 0.2)
elif mouse_look and not busy:
rot_help.look_at(mouse_world_pos, Vector3.UP)
rot_diff = abs(rot_help.rotation.y - rotation.y)
rotation.y = lerp_angle(rotation.y, rot_help.rotation.y, 0.2)
rot_diff = lerp(rot_diff, 0.0, 0.5)
if diving and dive_vec.length() > 0.4 and is_on_floor():
animation_data["dive_scrape"] = true
if Engine.get_idle_frames() % 4 == 0:
_sync_particle("dust_run", Vector3(0, - 0.8, 0))
else :
animation_data["dive_scrape"] = false
if gravity_vec.y < - 250:
_kill()
func _enter_state(new_state):
if new_state == - 1: return
if consume_on_state_change != - 1:
PlayerData._remove_item(consume_on_state_change, false)
consume_on_state_change = - 1
state = new_state
emit_signal("_state_change")
cam_push = 0.0
animation_data["bobber_visible"] = false
match state:
STATES.DEFAULT:
animation_data["item_bend"] = 0.0
locked = false
STATES.BUSY: locked = false
STATES.OBTAIN: locked = true
STATES.SHOWCASE:
locked = true
_equip_item(PlayerData._find_item_code(showcase_ref), true, true)
STATES.FISHING:
cam_push = - 0.3
locked = true
animation_data["bobber_visible"] = true
PlayerData.emit_signal("_help_update", "hold to reel (SPRINT reels quicker)")
_enter_animation("rod_idle", true)
STATES.FISHING_CANCEL:
animation_data["item_bend"] = 0.1
cam_push = - 0.3
locked = true
rod_cast_dist = 0.0
animation_data["bobber_visible"] = true
PlayerData.emit_signal("_item_equip", held_item.ref)
_bobber_retract()
_enter_animation("rod_retract", false, true, STATES.DEFAULT)
STATES.FISHING_CAST:
cam_push = - 0.3
animation_data["bobber_visible"] = true
locked = true
STATES.FISHING_CHARGE:
cam_push = 0.0
locked = false
STATES.FISHING_STRUGGLE:
animation_data["item_bend"] = - 0.7
cam_push = - 0.3
animation_data["bobber_visible"] = true
locked = true
_enter_animation("rod_struggle", true)
STATES.GUITAR: locked = true
STATES.SHOVEL_CAST: locked = true
STATES.SHOVEL_STRUGGLE:
locked = true
_shovel_check()
STATES.SHOVEL_CANCEL:
locked = true
_enter_animation("shovel_retract", false, true, STATES.DEFAULT)
STATES.NET_CAST: locked = true
STATES.NET_STRUGGLE:
locked = true
_net_check()
STATES.NET_CANCEL:
locked = true
_enter_animation("net_retract", false, true, STATES.DEFAULT)
STATES.CONSUME_ITEM:
locked = false
_equip_item({"ref": 0}, true, true)
_enter_state(STATES.DEFAULT)
func _primary_action():
match state:
STATES.DEFAULT:
_use_item()
STATES.OBTAIN:
_enter_animation("equip", false, true, 0, false, 1.5)
func _primary_action_hold():
primary_hold_timer += 1
match state:
STATES.DEFAULT:
return
STATES.FISHING:
_enter_animation("rod_reel", true)
rod_cast_dist -= 0.04 if Input.is_action_pressed("move_sprint") else 0.01
bobber_control = true
recent_reel = 15
if rod_cast_dist < 1.5: _enter_state(STATES.FISHING_CANCEL)
func _primary_action_release():
emit_signal("_primary_release")
match state:
STATES.DEFAULT:
_release_item()
STATES.FISHING:
_enter_animation("rod_idle", true)
STATES.METAL_DETECTOR:
_enter_state(STATES.DEFAULT)
func _interact():
if not controlled or interact_cooldown > 0: return
for area in interact_range.get_overlapping_areas():
if area.is_in_group("interactable") and area.is_visible_in_tree():
area._activate(self)
interact_cooldown = 60
return
func _enter_zone(zone, entrance_id, zone_owner = - 1):
if not controlled: return
world._enter_zone(zone, zone_owner)
PlayerData.player_saved_zone = zone
PlayerData.player_saved_zone_owner = zone_owner
print("Finding entrance w id: ", entrance_id, " and owner: ", zone_owner)
for entrance in get_tree().get_nodes_in_group("area_entrance"):
print(entrance.entrance_id, ": ", entrance.entrance_owner, " ?")
if entrance.entrance_id == entrance_id and entrance.entrance_owner == zone_owner:
global_transform.origin = entrance.global_transform.origin
last_valid_pos = global_transform.origin
return
print("Fallback!")
global_transform.origin = world.map.spawn_position.global_transform.origin
world._enter_zone("main_zone", - 1)
PlayerData.player_saved_zone = "main_zone"
PlayerData.player_saved_zone_owner = - 1
last_valid_pos = global_transform.origin
func _obtain_item(ref, bonus_text = [], journal_check = true):
old_rot = rotation
showcase_ref = ref
_equip_item({"ref": - 1}, true, true)
_enter_animation("equip", false, false, STATES.SHOWCASE, false, 1.5)
_play_sfx("strum")
var data = PlayerData._find_item_code(ref)
var text = "You caught a " + PlayerData._get_item_name(ref) + "! [color=#d5aa73](It's " + str(data["size"]) + "cm!)[/color]\n\n" + str(Globals.item_data[data["id"]]["file"].catch_blurb)
if journal_check:
var new = true
for type in PlayerData.journal_logs.keys():
for entry in PlayerData.journal_logs[type].keys():
if entry == data.id and PlayerData.journal_logs[type][entry].count > 1:
new = false
break
if new:
text = "Woah, a new creature! " + text
hud.dialogue_text = [text]
if bonus_text != []: hud.dialogue_text.append_array(bonus_text)
hud._change_menu(6)
force_cam_look = true
yield (get_tree().create_timer(0.1), "timeout")
cam_follow_node = $catch_cam_position
func _level_up():
yield (get_tree().create_timer(0.4), "timeout")
var bubble = title._create_level_bubble()
Network._send_actor_action(actor_id, "_sync_level_bubble", [], false)
GlobalAudio._play_sound("jingle_win")
_play_emote("emote_cheer", "happy")
$emotion_particles / lvl_particles.restart()
$emotion_particles / lvl_particles2.restart()
func _exit_showcase():
if state != STATES.SHOWCASE: return
_exit_animation()
_enter_state(STATES.DEFAULT)
_equip_item(PlayerData._find_item_code(previous_item), false)
get_tree().create_tween().tween_property(self, "rotation", old_rot, 0.3)
if xp_buildup > 0:
PlayerData._add_xp(ceil(xp_buildup))
xp_buildup = 0
func _equip_hotbar(slot):
if locked or not PlayerData.hotbar.keys().has(slot): return
var ref = PlayerData.hotbar[slot]
_equip_item(PlayerData._find_item_code(ref))
func _equip_item(item_data, skip_anim = false, forced = false, set_prev = true):
if set_prev and held_item["ref"] != 0: previous_item = held_item["ref"]
if (state != STATES.DEFAULT and not forced) or held_item["ref"] == item_data["ref"]: return
if not item_data.keys().has("id") or not Globals.item_data.keys().has(item_data["id"]):
item_data = PlayerData.FALLBACK_ITEM
PlayerData.emit_signal("_item_equip", item_data["ref"])
if not skip_anim:
_sync_sfx("equip")
_enter_state(STATES.BUSY)
_enter_animation("equip", false, false, STATES.DEFAULT, false, 1.5)
yield (self, "_animation_finished")
var held_data = item_data.duplicate()
hud.show_bait = Globals.item_data[item_data["id"]]["file"].show_bait
var data = Globals.item_data[item_data["id"]]["file"]
held_item_weight = item_data["size"]
if not data.uses_size: held_item_weight = 0.0
_update_held_item(held_data)
Network._send_actor_action(actor_id, "_update_held_item", [held_item], false)
func _use_item():
if held_item.empty(): return
var item_data = Globals.item_data[held_item["id"]]["file"]
if has_method(item_data.action) and item_data.action != "":
callv(item_data.action, item_data.action_params)
func _release_item():
if held_item.empty(): return
var item_data = Globals.item_data[held_item["id"]]["file"]
if has_method(item_data.release_action) and item_data.release_action != "":
call(item_data.release_action)
func _cast_fishing_rod():
rod_cast_data = PlayerData.LURE_DATA[PlayerData.lure_selected].effect_id
animation_data["caught_item"] = {}
bait_warn = 1
rod_damage = [1, 3, 10, 20, 35, 50][PlayerData.rod_power_level]
rod_spd = [0.0, 0.1, 0.24, 0.4, 0.7, 1.0][PlayerData.rod_speed_level]
rod_chance = [0.0, 0.02, 0.04, 0.06, 0.08, 0.1][PlayerData.rod_chance_level]
_enter_state(STATES.FISHING_CHARGE)
_enter_animation("rod_wind", true, false, - 1, false)
yield (self, "_primary_release")
if state != STATES.FISHING_CHARGE: return
_enter_state(STATES.FISHING_CAST)
_sync_sfx("woosh", null, 1.0, 0.4)
var strength = clamp(primary_hold_timer * 0.06, 1.5, 9.0)
rod_cast_dist = strength
fish_detect.translation.z = - strength
var is_valid_fishing_spot = false
if bobber_preview.is_colliding() and bobber_preview.get_collider().is_in_group("valid_water"):
is_valid_fishing_spot = true
_bobber_cast(rod_cast_dist, bobber_preview.get_collision_point() + Vector3(0, 0.75, 0), is_valid_fishing_spot)
rod_depth = 0
casted_bait = PlayerData.bait_selected
if casted_bait != "" and PlayerData.bait_inv[casted_bait] <= 0: casted_bait = ""
_exit_animation()
if is_valid_fishing_spot:
animation_data["item_bend"] = 0.3
retract_splash = true
_enter_animation("rod_cast", false, true, STATES.FISHING)
else :
animation_data["item_bend"] = 0.3
retract_splash = false
_enter_animation("rod_cast", false, true, STATES.FISHING_CANCEL)
yield (get_tree().create_timer(0.4), "timeout")
animation_data["item_bend"] = - 0.2
func _on_fish_catch_timer_timeout():
if not controlled: return
fish_timer.wait_time = rand_range(2.0, 3.0)
fish_timer.start()
if state != STATES.FISHING: return
var fish_type = "ocean"
var junk_mult = 1.0
fish_zone_data = {"id": - 1, "boost": 0.0}
for zone in fishing_area.get_overlapping_areas():
if zone.is_in_group("fish_zone"):
fish_zone_data["id"] = zone.id
fish_zone_data["boost"] = zone.chance_boost
junk_mult = zone.junk_mult
if zone.fish_type != "": fish_type = zone.fish_type
var fish_chance = 0.0
var base_chance = BAIT_DATA[casted_bait]["catch"]
fish_chance = base_chance
fish_chance += (base_chance * failed_casts)
fish_chance += (base_chance * rod_chance)
fish_chance += fish_zone_data["boost"] * fish_chance
if recent_reel > 0: fish_chance *= 1.1
if rod_cast_data == "attractive": fish_chance *= 1.3
if in_rain: fish_chance *= 1.1
fish_chance *= catch_drink_boost
print("Fish chance w ", fish_chance, "w type ", fish_type)
bait_warn -= 1
if bait_warn <= 0 and fish_chance <= 0.0:
bait_warn = 8
var text = ""
if casted_bait == "":
text = "[color=#ac0029]You've got no bait attached! You won't catch any fish like that...[/color]"
else :
text = "[color=#ac0029]Seems nothing is going to bite... perhaps your bait isn't for this water...[/color]"
Network._update_chat(text)
if randf() > fish_chance:
failed_casts += 0.05
return
failed_casts = 0.0
var bait_use_chance = 1.0
if rod_cast_data == "efficient": bait_use_chance = 0.8
if randf() < bait_use_chance: PlayerData._use_bait(casted_bait)
else : PlayerData._send_notification("The Efficient Lure saved your bait!")
var max_tier = BAIT_DATA[casted_bait]["max_tier"]
var double_bait = 0.0
if ["large", "sparkling", "double"].has(rod_cast_data): double_bait = 0.25
if randf() < double_bait:
PlayerData._use_bait(casted_bait)
PlayerData._send_notification("Your lure used extra bait...", 1)
if rod_cast_data == "gold":
for i in 2: PlayerData._use_bait(casted_bait)
PlayerData._send_notification("Your golden lure used extra bait...", 1)
var treasure_mult = 1.0
if rod_cast_data == "magnet": treasure_mult = 2.0
if rod_cast_data == "salty": fish_type = "ocean"
if rod_cast_data == "fresh": fish_type = "lake"
var force_av_size = false
if randf() < 0.05 * treasure_mult * junk_mult:
fish_type = "water_trash"
max_tier = 0
force_av_size = true
if in_rain and randf() < 0.08:
fish_type = "rain"
var rolls = []
for i in 3:
var roll = Globals._roll_loot_table(fish_type, max_tier)
var s = Globals._roll_item_size(roll)
rolls.append([roll, s])
var reroll_type = "none"
if rod_cast_data == "small": reroll_type = "small"
if rod_cast_data == "sparkling": reroll_type = "tier"
if rod_cast_data == "large": reroll_type = "large"
if rod_cast_data == "gold": reroll_type = "rare"
var chosen = rolls[0]
for roll in rolls:
match reroll_type:
"none":
chosen = roll
"small":
if roll[1] < chosen[1]:
chosen = roll
"large":
if roll[1] > chosen[1]:
chosen = roll
"tier":
var old_tier = Globals.item_data[chosen[0]]["file"].tier
var new_tier = Globals.item_data[roll[0]]["file"].tier
if new_tier > old_tier:
chosen = roll
"rare":
var new_rare = Globals.item_data[roll[0]]["file"].rare
if new_rare:
chosen = roll
var fish_roll = chosen[0]
var size = chosen[1]
var quality = PlayerData.ITEM_QUALITIES.NORMAL
var r = randf()
for q in PlayerData.ITEM_QUALITIES.size():
if BAIT_DATA[casted_bait]["quality"].size() - 1 < q:
print("bait does not support rarity ", q)
break
if randf() < BAIT_DATA[casted_bait]["quality"][q]:
quality = q
print("-------------------------Rolled Quality: ", quality)
if randf() < 0.02 * treasure_mult:
fish_roll = "treasure_chest"
size = 60.0
quality = 0
var data = Globals.item_data[fish_roll]["file"]
var quality_data = PlayerData.QUALITY_DATA[quality]
if force_av_size: size = data.average_size
var diff_mult = clamp(size / data.average_size, 0.7, 1.8)
var difficulty = clamp((data.catch_difficulty * diff_mult * quality_data.diff) + quality_data.bdiff, 1.0, 250.0)
var xp_mult = size / data.average_size
if xp_mult < 0.15: xp_mult = 1.25 + xp_mult
xp_mult = max(0.5, xp_mult)
var xp_add = ceil(data.obtain_xp * xp_mult * catch_drink_xp * quality_data.worth)
print("Total Rolls: ", rolls)
print("Roll fish ", fish_roll, " with size ", size, " and diff Mult: ", diff_mult)
if fish_zone_data["id"] != - 1:
_wipe_actor(fish_zone_data["id"])
Network._send_actor_action(actor_id, "_wipe_actor", [fish_zone_data["id"]])
_enter_state(STATES.FISHING_STRUGGLE)
animation_data["alert"] = true
yield (get_tree().create_timer(1.0), "timeout")
animation_data["alert"] = false
var delay_time = 0
while hud.using_chat:
yield (get_tree().create_timer(0.15), "timeout")
delay_time += 1
if delay_time > 200:
_enter_state(STATES.FISHING_CANCEL)
return
hud._open_minigame("fishing3", {"fish": fish_roll, "rod_type": rod_cast_data, "reel_mult": catch_drink_reel, "quality": quality, "damage": rod_damage, "speed": rod_spd}, difficulty)
var success = yield (hud, "_minigame_finished")
print("SUCCESS: ", success)
if success:
animation_data["caught_item"] = {"id": fish_roll, "size": size}
_enter_state(STATES.FISHING_CANCEL)
yield (self, "_state_change")
var ref
var catches = 1
var bonus_text = []
if rod_cast_data == "double" and randf() < 0.15:
catches = 2
bonus_text.append("Your Double Hook doubled the fish!")
if PlayerData.rod_luck_level > 0 and randf() < 0.15:
bonus_text.append("How lucky! You found a bonus [color=#d57900]Coin Bag[/color] aswell!")
PlayerData._add_item("luck_moneybag", - 1, randi() % 15 + 15, PlayerData.rod_luck_level)
var tags = []
for i in catches:
ref = PlayerData._add_item(fish_roll, - 1, size, quality, tags)
PlayerData._log_item(fish_roll, size, quality)
PlayerData._quest_progress("catch", fish_roll)
PlayerData._quest_progress("catch_type", data.loot_table)
PlayerData._quest_progress("catch_small", PlayerData._get_size_type(fish_roll, size))
PlayerData._quest_progress("catch_big", PlayerData._get_size_type(fish_roll, size))
if fish_roll == "treasure_chest": PlayerData._quest_progress("catch_treasure")
if in_rain: PlayerData._quest_progress("catch_rain")
if data.tier == 2: PlayerData._quest_progress("catch_hightier")
xp_buildup += xp_add
PlayerData._catch_fish()
_obtain_item(ref, bonus_text)
if rod_cast_data == "lucky":
var worth = PlayerData._get_item_worth(ref)
var gold = max(1, ceil(worth * rand_range(0.01, 0.1)))
PlayerData.money += gold
PlayerData._send_notification("Your Lucky Lure got you $" + str(gold) + "!")
if catch_drink_gold_add != Vector2(0, 0):
var gold = max(1, ceil(rand_range(catch_drink_gold_add.x, catch_drink_gold_add.y)))
PlayerData.money += gold
PlayerData._send_notification("Your Catcher's Luck got you $" + str(gold) + "!")
if catch_drink_gold_percent > 0.0:
var worth = PlayerData._get_item_worth(ref)
var gold = max(1, ceil(rand_range(worth * 0.01, worth * catch_drink_gold_percent)))
PlayerData.money += gold
PlayerData._send_notification("Your Catcher's Luck Ultra got you $" + str(gold) + "!")
else :
_enter_state(STATES.FISHING_CANCEL)
func _wipe_actor(id):
var actor = world._get_actor_by_id(id)
print("Wiping actor ", id, " : ", actor)
if is_instance_valid(actor):
actor._deinstantiate(true)
func _shovel():
_enter_state(STATES.SHOVEL_CAST)
_enter_animation("shovel_dig", false, true, STATES.SHOVEL_STRUGGLE)
func _shovel_check():
var mound = false
var area
for a in shovel_area.get_overlapping_areas():
if a.is_in_group("mound"):
mound = true
area = a
if is_instance_valid(area):
var id = area.owner.actor_id
_wipe_actor(id)
Network._send_actor_action(actor_id, "_wipe_actor", [id])
if mound:
_enter_animation("shovel_struggle", true, true)
hud._open_minigame("shoveling", {"damage": 5.0})
var success = yield (hud, "_minigame_finished")
print("SUCCESS: ", success)
if success:
_enter_state(STATES.SHOVEL_CANCEL)
yield (self, "_state_change")
var ref = PlayerData._add_item("spider")
_obtain_item(ref)
else :
_enter_state(STATES.SHOVEL_CANCEL)
else :
_enter_state(STATES.SHOVEL_CANCEL)
func _cast_net():
_enter_state(STATES.NET_CAST)
_enter_animation("net_use", false, true, STATES.NET_STRUGGLE)
func _net_check():
var bug = false
var area
for a in net_area.get_overlapping_areas():
if a.is_in_group("bug"):
bug = true
area = a
var bug_id = ""
if is_instance_valid(area):
var id = area.owner.actor_id
bug_id = area.owner.bug_id
_wipe_actor(id)
Network._send_actor_action(actor_id, "_wipe_actor", [id])
if bug:
_enter_animation("net_struggle", true, true)
hud._open_minigame("shoveling", {"damage": 5.0})
var success = yield (hud, "_minigame_finished")
print("SUCCESS: ", success)
if success:
_enter_state(STATES.NET_CANCEL)
yield (self, "_state_change")
var size = Globals._roll_item_size(bug_id)
var ref = PlayerData._add_item(bug_id, - 1, size)
_obtain_item(ref)
else :
_enter_state(STATES.NET_CANCEL)
else :
_enter_state(STATES.NET_CANCEL)
func _toggle_freecam():
if busy: return
if not freecamming:
PlayerData._send_notification("entering freecam mode")
freecamming = true
hud.freecamming = true
cam_orbit_node = freecam_anchor
freecam_anchor.global_transform.origin = global_transform.origin
else :
PlayerData._send_notification("exiting freecam mode")
freecamming = false
hud.freecamming = false
cam_orbit_node = null
func _freecam_input(delta):
if not freecamming: return
var speed = 4.0
if Input.is_action_pressed("move_sprint"):
speed = 7.0
if Input.is_action_pressed("move_sneak"):
speed = 1.0
var max_dist = 15.0
var build_dir = Vector3.ZERO
if Input.is_action_pressed("move_forward"): build_dir -= cam_base.transform.basis.z
if Input.is_action_pressed("move_back"): build_dir += cam_base.transform.basis.z
if Input.is_action_pressed("move_right"): build_dir += cam_base.transform.basis.x
if Input.is_action_pressed("move_left"): build_dir -= cam_base.transform.basis.x
if Input.is_action_pressed("move_up"): build_dir += cam_base.transform.basis.y
if Input.is_action_pressed("move_down"): build_dir -= cam_base.transform.basis.y
freecam_anchor.global_transform.origin += build_dir * speed * delta
freecam_anchor.global_transform.origin.x = clamp(freecam_anchor.global_transform.origin.x, global_transform.origin.x - max_dist, global_transform.origin.x + max_dist)
freecam_anchor.global_transform.origin.y = clamp(freecam_anchor.global_transform.origin.y, global_transform.origin.y - max_dist, global_transform.origin.y + max_dist)
freecam_anchor.global_transform.origin.z = clamp(freecam_anchor.global_transform.origin.z, global_transform.origin.z - max_dist, global_transform.origin.z + max_dist)
func _create_prop(ref, offset = Vector3(0, 1, 0)):
if not controlled: return
for prop in prop_ids:
if prop.ref == ref:
_wipe_actor(prop.id)
prop_ids.erase(prop)
PlayerData._send_notification("clearing prop", 1)
PlayerData.props_placed = prop_ids
PlayerData.emit_signal("_prop_update")
return
if $detection_zones / prop_detect.get_overlapping_bodies().size() > 0 or not is_on_floor() or not $detection_zones / prop_ray.is_colliding():
PlayerData._send_notification("invalid prop placement", 1)
return
if prop_ids.size() > 4:
PlayerData._send_notification("prop limit reached", 1)
return
var item = PlayerData._find_item_code(ref)
var data = Globals.item_data[item["id"]]["file"]
var ver_offset = Vector3(0, 1.0, 0) * (1.0 - player_scale)
print(ver_offset)
var pos = global_transform.origin + (global_transform.basis.z * - 2.0) - offset + ver_offset
var rot = rotation + Vector3(0, deg2rad(180), 0)
var prop_code = data.prop_code
var blacklist = ["island_tiny", "island_med", "island_big"]
if current_zone_owner != - 1:
if blacklist.has(prop_code):
PlayerData._send_notification("this prop cannot be spawned here...", 1)
return
var new_id = Network._sync_create_actor(prop_code, pos, current_zone, - 1, Network.STEAM_ID, {"rotation": rot, "current_zone_owner": current_zone_owner})
prop_ids.append({"id": new_id, "ref": ref, "prop_id": data.action})
PlayerData.props_placed = prop_ids
PlayerData.emit_signal("_prop_update")
PlayerData._send_notification("prop placed", 0)
_sync_particle("dust_land", pos, true)
_sync_sfx("poof", pos)
func _clear_props():
if prop_ids.size() <= 0:
PlayerData._send_notification("no props to undo", 1)
return
PlayerData._send_notification("undo prop", 0)
_wipe_actor(prop_ids[prop_ids.size() - 1][0])
prop_ids.remove(prop_ids.size() - 1)
func _clear_all_props():
if prop_ids.size() <= 0:
PlayerData._send_notification("no props to clear", 1)
return
PlayerData._send_notification("clearing all props", 0)
for prop in prop_ids:
_wipe_actor(prop.id)
prop_ids.clear()
PlayerData.props_placed = prop_ids
PlayerData.emit_signal("_prop_update")
func _item_place_prop():
if not controlled: return
_create_prop(held_item.ref)
func _update_animation_data():
if animation_data["emote"] != "":
var anim = $body / player_body / AnimationPlayer.get_animation(animation_data["emote"])
animation_timer += 1
animation_goal = floor((anim.length * 60) / animation_data["emote_timescale"])
if animation_timer >= animation_goal and emoting and animation_goal > 0 and not emote_looping:
print("Finished Emoting ", animation_data["emote"], " bufferstate: ", buffer_state)
emit_signal("_animation_finished")
emoting = false
emote_full = false
emote_locked = false
animation_data["emote"] = ""
if buffer_state != - 1: _enter_state(buffer_state)
animation_data["emoting"] = emoting
animation_data["emote_full"] = emoting and emote_full
animation_data["moving"] = direction != Vector3.ZERO or rot_diff > 0.05
animation_data["sprinting"] = sprinting and direction != Vector3.ZERO
animation_data["sneaking"] = sneaking and direction != Vector3.ZERO
animation_data["diving"] = diving
animation_data["sitting"] = sitting
animation_data["busy"] = busy and state == STATES.DEFAULT and not emoting
animation_data["land"] = lerp(animation_data["land"], 0.0, 0.04)
animation_data["talking"] = lerp(animation_data["talking"], 0.0, 0.1)
animation_data["recent_reel"] = lerp(animation_data["recent_reel"], recent_reel, 0.2)
animation_data["caught_fish"] = state == STATES.FISHING_STRUGGLE
animation_data["player_scale"] = lerp(animation_data["player_scale"], player_scale, 0.06)
animation_data["player_scale_y"] = lerp(animation_data["player_scale_y"], player_scale_y, 0.06)
animation_data["run_mult"] = 1.15 * boost_mult
animation_data["walk_mult"] = 1.25 if not slow_walking else 0.7
animation_data["drunk_tier"] = drunk_tier
animation_data["state"] = state
$emotion_particles / mushroom_trail.emitting = animation_data["mushroom"]
$emotion_particles / drunk_particles.emitting = animation_data["drunk_tier"] > 1
if is_on_floor(): animation_data["mushroom"] = false
if not bobber_control:
animation_data["bobber_position"] = Vector3(bobber_hpos.x, bobber_vpos, bobber_hpos.z)
else :
var pos = global_transform.origin + ( - rod_cast_dist * global_transform.basis.z)
pos.y = bobber_vpos + sin(OS.get_ticks_msec() * 0.002) * 0.05
animation_data["bobber_position"] = lerp(animation_data["bobber_position"], pos, 0.2)
func _process_animation():
var anim_lerp = 0.4
var emote_type = 0
if animation_data["emoting"]: emote_type = 1
if animation_data["emote_full"]: emote_type = 2
anim_tree.set("parameters/emote_full/blend_amount", lerp(anim_tree.get("parameters/emote_full/blend_amount"), float(emote_type == 2), anim_lerp))
if anim_tree.get("parameters/emote_full/blend_amount") < 0.1 and emote_type != 2: anim_tree.set("parameters/emote_full/blend_amount", 0.0)
anim_tree.set("parameters/emote_half/blend_amount", lerp(anim_tree.get("parameters/emote_half/blend_amount"), float(emote_type == 1), anim_lerp))
if anim_tree.get("parameters/emote_half/blend_amount") < 0.1 and emote_type != 1: anim_tree.set("parameters/emote_half/blend_amount", 0.0)
anim_tree.set("parameters/arms/blend_amount", lerp(anim_tree.get("parameters/arms/blend_amount"), float( not animation_data["emoting"]), anim_lerp))
anim_tree.set("parameters/movement/blend_amount", lerp(anim_tree.get("parameters/movement/blend_amount"), float(animation_data["moving"]), anim_lerp))
anim_tree.set("parameters/run_blend/blend_amount", lerp(anim_tree.get("parameters/run_blend/blend_amount"), float(animation_data["sprinting"]) - float(animation_data["sneaking"]), anim_lerp))
anim_tree.set("parameters/dive/blend_amount", lerp(anim_tree.get("parameters/dive/blend_amount"), float(animation_data["diving"]), anim_lerp))
anim_tree.set("parameters/arm_blend/blend_position", float(animation_data["arm_value"]))
anim_tree.set("parameters/sitting/blend_amount", lerp(anim_tree.get("parameters/sitting/blend_amount"), float(animation_data["sitting"]), anim_lerp))
anim_tree.set("parameters/thinking/blend_amount", lerp(anim_tree.get("parameters/thinking/blend_amount"), float(animation_data["busy"]), anim_lerp))
anim_tree.set("parameters/land/blend_amount", lerp(anim_tree.get("parameters/land/blend_amount"), float(animation_data["land"]), anim_lerp))
anim_tree.set("parameters/talking/blend_amount", lerp(anim_tree.get("parameters/talking/blend_amount"), float(animation_data["talking"]), anim_lerp))
anim_tree.set("parameters/runmodif/scale", animation_data["run_mult"])
anim_tree.set("parameters/walkmodif/scale", animation_data["walk_mult"])
anim_tree.set("parameters/emote_time/scale", animation_data["emote_timescale"])
anim_tree.set("parameters/emote_timeb/scale", animation_data["emote_timescale"])
face._show_blush(animation_data["drunk_tier"] > 1)
if animation_data["caught_item"] != caught_item:
caught_item = animation_data["caught_item"]
_update_caught_item(animation_data["caught_item"])
if item_scene:
item_scene.item_bend = lerp(item_scene.item_bend, animation_data["item_bend"], 0.4)
bobber_line.end_anchor = item_scene.get_node(item_scene.hotspot_node).global_transform.origin
bobber_line.y_drop = - 1.5 + ((animation_data["recent_reel"] / 15.0) * 0.5)
if animation_data["caught_fish"]: bobber_line.y_drop = 0
if bobber.visible: bobber.global_transform.origin = animation_data["bobber_position"] + Vector3(0, (0.0 if not animation_data["caught_fish"] else - 0.3), 0)
bobber.visible = animation_data["bobber_visible"]
bobber_line.active = bobber.visible
if animation_data["player_scale"] != scale.y:
scale = animation_data["player_scale"] * Vector3.ONE
scale.y *= animation_data["player_scale_y"]
bobber_line.line_scale = animation_data["player_scale"]
ripples.visible = animation_data["state"] == STATES.FISHING
var rip_anim = "default" if animation_data["recent_reel"] <= 1.0 else "wake"
if ripples.animation != rip_anim: ripples.animation = rip_anim
var root = anim_tree.tree_root
var node = root.get_node("emote_anim")
var node_b = root.get_node("emote_anim_b")
if node.animation != animation_data["emote"]:
anim_tree.set("parameters/emote_sync/seek_position", 0.0)
anim_tree.set("parameters/emote_half_sync/seek_position", 0.0)
node.set_animation(animation_data["emote"])
node_b.set_animation(animation_data["emote"])
$"%head_alert".visible = animation_data["alert"]
tail.sitting = animation_data["sitting"]
tail.diving = animation_data["diving"]
tail.motion = float(animation_data["moving"])
tail.wagging = animation_data["wagging"]
if animation_data["back_bend"] != 0.0:
var pose = skeleton.get_bone_pose(1)
pose = pose.rotated(Vector3(1, 0, 0), animation_data["back_bend"])
skeleton.set_bone_custom_pose(1, pose)
else :
skeleton.set_bone_custom_pose(1, Transform())
func _enter_animation(anim_name, loop = false, _locked = true, state_enter = - 1, full_anim = true, timescale = 1.0):
if anim_name == animation_data["emote"]: return
diving = false
animation_data["emote_timescale"] = timescale
emote_full = full_anim
emote_locked = _locked
emote_looping = loop
buffer_state = state_enter if not emote_looping else - 1
if emote_looping: _enter_state(state_enter)
animation_data["emote"] = anim_name
animation_timer = 0
emoting = true
func _exit_animation():
emit_signal("_animation_finished")
emoting = false
emote_full = false
emote_locked = false
emote_looping = false
animation_data["emote"] = ""
func _update_held_item(new):
if new.empty() or held_item == new: return
held_item = new
var item_data = Globals.item_data[new["id"]]["file"]
var alt_scale_mult = 1.0
item_sprite.texture = item_data.icon.duplicate() if (item_data.show_item and not item_data.show_scene) else null
if is_instance_valid(item_scene): item_scene.queue_free()
item_scene = null
if item_data.show_scene and item_data.item_scene:
item_scene = item_data.item_scene.instance()
hand_bone.add_child(item_scene)
var scale_mult = 0.07 - clamp(new["size"] * 0.01, 0.01, 0.061)
var item_scale = 1.0 if not item_data.uses_size else new["size"] * scale_mult
item_scale *= alt_scale_mult
item_sprite.scale = Vector3(item_scale, item_scale, item_scale)
var y = 0.0
var offs = 0.0
var back_bend = 0.0
if item_data.uses_size:
y = clamp(item_scale * item_scale * 0.08, 0.0, 0.36)
offs = clamp(item_scale * item_scale, 0.0, 16.0)
back_bend = clamp(item_scale * item_scale, 0.0, 55.0)
item_sprite.translation.y = y
item_sprite.offset.y = item_data.hold_offset + offs
$body / player_body / Armature / Skeleton / BoneAttachment / Spatial.rotation_degrees = Vector3(0.0, 0.0, back_bend * 0.7)
item_sprite.translation.z = 0.0
item_sprite.vel = 0.0
item_sprite.mult = clamp(item_scale * 3.5, 0.0, 1.2) if item_data.category == "fish" and item_data.alive else 0.0
item_sprite.modulate = Color("#ffffff")
item_sprite.opacity = 1.0
for child in item_sprite.get_children(): child.emitting = false
if PlayerData.QUALITY_DATA.has(new["quality"]):
item_sprite.modulate = Color(PlayerData.QUALITY_DATA[new["quality"]]["mod"])
item_sprite.opacity = PlayerData.QUALITY_DATA[new["quality"]]["op"]
if PlayerData.QUALITY_DATA[new["quality"]]["particle"] > - 1: item_sprite.get_child(PlayerData.QUALITY_DATA[new["quality"]]["particle"]).emitting = true
animation_data["arm_value"] = item_data.arm_value
animation_data["back_bend"] = deg2rad( - back_bend)
func _update_caught_item(new):
if new.empty():
caught_fish.texture = null
return
caught_item = new
var item_data = Globals.item_data[new["id"]]["file"]
caught_fish.texture = item_data.icon.duplicate() if item_data.show_item else null
var item_scale = 1.0 if not item_data.uses_size else new["size"] * 0.01
caught_fish.scale = Vector3(item_scale, item_scale, item_scale)
func _bobber_cast(dist, end, splash):
bobber_control = false
var height = dist * 0.4
height += max(0, global_transform.origin.y - end.y)
end.y -= 1
bobber_hpos = global_transform.origin
bobber_vpos = global_transform.origin.y
yield (get_tree().create_timer(0.4), "timeout")
var tween = get_tree().create_tween()
tween.set_trans(1)
tween.set_ease(1)
tween.tween_property(self, "bobber_vpos", end.y + height, dist * 0.05)
tween.set_ease(0)
tween.tween_property(self, "bobber_vpos", end.y - (height * 0.2), dist * 0.05)
if splash:
tween.tween_callback(self, "_bobber_splash")
tween.set_trans(1)
tween.set_ease(1)
tween.tween_property(self, "bobber_vpos", end.y + (height * 0.1), (dist * 0.05))
tween.set_ease(0)
tween.tween_property(self, "bobber_vpos", end.y - (height * 0.05), (dist * 0.1))
tween.set_trans(1)
tween.set_ease(1)
tween.tween_property(self, "bobber_vpos", end.y, (dist * 0.1))
var htween = get_tree().create_tween()
htween.set_trans(4)
htween.set_ease(1)
htween.tween_property(self, "bobber_hpos", end + (global_transform.basis.z * 0.5), dist * 0.1)
htween.tween_property(self, "bobber_hpos", end + (global_transform.basis.z * 0.2), dist * 0.15)
htween.tween_property(self, "bobber_hpos", end, dist * 0.1)
yield (htween, "finished")
bobber_control = true
func _bobber_retract():
bobber_control = false
bobber_hpos = bobber.global_transform.origin
bobber_vpos = bobber.global_transform.origin.y
yield (get_tree().create_timer(0.4), "timeout")
if retract_splash: _bobber_splash("splashb")
var tween = get_tree().create_tween()
tween.set_trans(1)
tween.set_ease(1)
tween.tween_property(self, "bobber_vpos", global_transform.origin.y + 2.0, 0.3)
tween.set_ease(0)
tween.tween_property(self, "bobber_vpos", global_transform.origin.y, 0.3)
var htween = get_tree().create_tween()
htween.tween_property(self, "bobber_hpos", global_transform.origin, 0.6)
func _bobber_splash(sfx = "splash"):
_sync_particle("splash", bobber.global_transform.origin, true)
_sync_sfx(sfx, Vector3(bobber_hpos.x, bobber_vpos, bobber_hpos.z))
func _toggle_sit(exit = false):
if not exit: sitting = not sitting
else : sitting = false
func _play_emote(emote_id, emotion = ""):
if emote_id == "": return
if emote_id == "sit":
_toggle_sit()
elif not emote_locked:
_enter_animation(emote_id, false, true)
if emotion != "":
_sync_face_emote(emotion)
func _change_cosmetics():
if cosmetic_data == PlayerData.cosmetics_equipped: return
var new = PlayerData.cosmetics_equipped.duplicate()
if not dead_actor: Network._send_actor_action(actor_id, "_update_cosmetics", [new])
_update_cosmetics(new)
func _update_cosmetics(data):
var valid = true
for key in PlayerData.FALLBACK_COSM.keys():
if not data.keys().has(key):
print("missing key ", key)
valid = false
for key in data.keys():
if not (data[key] is Array):
if not Globals._cosmetic_exists(data[key]):
print("cosm ", data[key], " does not exist")
valid = false
else :
for c in data[key]:
if not Globals._cosmetic_exists(c):
print("cosm ", data[key], " does not exist")
valid = false
if not valid: data = PlayerData.FALLBACK_COSM.duplicate()
cosmetic_data = data
for child in skeleton.get_children():
if child.is_in_group("cosmetic"): child.queue_free()
if data.empty(): return
title.label = str(Network._get_username_from_id(owner_id))
title.title = Globals.cosmetic_data[data["title"]]["file"].title
face._setup_face(data)
var species_id = Globals.cosmetic_data[data.species]["file"].cos_internal_id
var species = _create_cosmetic(data["species"], species_id)
_create_cosmetic(data["undershirt"], species_id)
_create_cosmetic(data["overshirt"], species_id)
_create_cosmetic(data["legs"], species_id)
_create_cosmetic(data["hat"], species_id)
for misc in data["accessory"]: _create_cosmetic(misc, species_id)
var pattern = Globals.cosmetic_data[data["pattern"]]["file"]
body_mesh.material_override.set_shader_param("texture_albedo", pattern.body_pattern[0])
var primary_color = Globals.cosmetic_data[data["primary_color"]]["file"]
var secondary_color = Globals.cosmetic_data[data["secondary_color"]]["file"] if pattern.body_pattern[0] else Globals.cosmetic_data[data["primary_color"]]["file"]
body_mesh.material_override.set_shader_param("albedo", primary_color.main_color)
body_mesh.material_override.set_shader_param("albedo_secondary", secondary_color.main_color)
var tail_data = Globals.cosmetic_data[data["tail"]]["file"]
tail._load_tail(tail_data.mesh, primary_color.main_color)
if species:
species.material_override.set_shader_param("albedo", primary_color.main_color)
species.material_override.set_shader_param("albedo_secondary", secondary_color.main_color)
match data["species"]:
"species_cat": species.material_override.set_shader_param("texture_albedo", pattern.body_pattern[1])
"species_dog": species.material_override.set_shader_param("texture_albedo", pattern.body_pattern[2])
if not data.keys().has("bobber") or data["bobber"] == "": data["bobber"] = "bobber_default"
var bobber_file = Globals.cosmetic_data[data["bobber"]]["file"]
bobber_mesh.mesh = bobber_file.mesh
bobber_mesh.set_surface_material(0, bobber_file.material)
if bobber_file.secondary_material: bobber_mesh.set_surface_material(1, bobber_file.secondary_material)
if bobber_file.third_material: bobber_mesh.set_surface_material(2, bobber_file.third_material)
func _create_cosmetic(id, species_id = 0):
if id == "": return
if not Globals.cosmetic_data.keys().has(id):
print("Failed finding cosmetic: ", id)
return
print("Creating Cosmetic: ", id)
var data = Globals.cosmetic_data[id]["file"]
if data.scene_replace:
var bone_attach = BoneAttachment.new()
skeleton.add_child(bone_attach)
bone_attach.bone_name = "pelvis"
bone_attach.add_to_group("cosmetic")
var cosm = data.scene_replace.instance()
bone_attach.add_child(cosm)
return cosm
var cosm = preload("res://Scenes/Entities/Player/cosmetic_node.tscn").instance()
cosm.mesh = data.mesh
if data.species_alt_mesh.size() > 0:
cosm.mesh = data.species_alt_mesh[species_id]
cosm.skin = data.mesh_skin
if not data.material:
cosm.material_override = preload("res://Assets/Shaders/player_skins.tres").duplicate()
cosm.material_override.set_shader_param("albedo", data.main_color)
cosm.material_override.set_shader_param("albedo_secondary", data.main_color)
cosm.material_override.set_shader_param("texture_albedo", null)
else :
cosm.material_override = null
cosm.set_surface_material(0, data.material)
if data.secondary_material: cosm.set_surface_material(1, data.secondary_material)
cosm.skeleton = skeleton.get_path()
skeleton.add_child(cosm)
return cosm
func _on_step_timer_timeout():
if not controlled: return
if randf() < 0.03 * drunk_tier and drunk_wander_length <= 0:
drunk_wander_length = randi() % 25 + 5
drunk_wander_dir = Vector3(
rand_range( - 1, 1),
0.0,
rand_range( - 1, 1))
if state == STATES.FISHING_STRUGGLE:
_sync_particle("small_splash", bobber.global_transform.origin, true)
if recent_reel > 0 and state == STATES.FISHING:
fishing_update.translation.z = - rod_cast_dist
fishing_update.force_raycast_update()
if fishing_update.is_colliding() and not fishing_update.get_collider().is_in_group("valid_water"):
_enter_state(STATES.FISHING_CANCEL)
if velocity != Vector3.ZERO:
var vol = 1.6
if sprinting: vol = 2.8
elif sneaking: vol = 0.3
if is_on_floor() and sprinting: _sync_particle("dust_run", Vector3(0, - 0.95, 0))
if is_on_floor() and safe_check.is_colliding() and safe_check.get_collision_normal() == Vector3(0, 1, 0) and not gravity_disable:
if death_counter > 0: death_counter -= 1
last_valid_pos = global_transform.origin
PlayerData.player_saved_position = global_transform.origin
func _message_sent(text):
var split = text.split(" ")
for s in split:
match s:
":(", ":[", "D:", ";_;", ";~;", ":C", ":c": _sync_face_emote("sad")
":)", ":D", ":]": _sync_face_emote("love")
"xD", "!!!", "<3", "xd", "LMAO", "LOL": _sync_face_emote("happy")
">:(", "D:<", ">:[": _sync_face_emote("angry")
":/", ":|": _sync_face_emote("flat")
":3", ":3c", ">:3c", ">:3": _sync_face_emote("cat")
"O.o", "!?!?", "?!?!", ":o", ":O": _sync_face_emote("surprised")
var bubble = title._create_speech_bubble(text, PlayerData.voice_speed)
bubble.connect("_letter_said", self, "_sync_talk")
Network._send_actor_action(actor_id, "_sync_create_bubble", [text], false)
func _sync_talk(letter):
var blacklist = ["a", "b", "c", "d", "e", "f", "g", "h", "i", "j", "k", "l", "m", "n", "o", "p", "q", "r", "s", "t", "u", "v", "w", "x", "y", "z"]
if not blacklist.has(letter.to_lower()): return
animation_data["talking"] = 0.4
_talk(letter, PlayerData.voice_pitch)
Network._send_actor_action(actor_id, "_talk", [letter, PlayerData.voice_pitch], false)
func _talk(letter, pitch = 1.5):
if not in_zone or not visible: return
face._talk()
sound_manager._construct_voice(letter, "NewVoice", pitch)
func _sync_face_emote(emotion):
_face_emote(emotion)
Network._send_actor_action(actor_id, "_face_emote", [emotion], false)
func _sync_create_bubble(text):
title._create_speech_bubble(text)
func _sync_level_bubble(text):
title._create_level_bubble(text)
$emotion_particles / lvl_particles.restart()
$emotion_particles / lvl_particles2.restart()
func _face_emote(emotion):
face._emote(emotion, 2.4)
func _sync_particle(id, offset = Vector3.ZERO, global = false):
_play_particle(id, offset, global)
Network._send_actor_action(actor_id, "_play_particle", [id, offset, global], false)
func _play_particle(id, offset, global):
if not PARTICLE_DATA.keys().has(id): return
var p = PARTICLE_DATA[id].instance()
add_child(p)
if not global:
p.translation = offset
else :
p.set_as_toplevel(true)
p.global_transform.origin = offset
p.emitting = true
yield (get_tree().create_timer(p.lifetime + 0.1), "timeout")
p.queue_free()
func _on_water_detect_area_entered(area):
if gravity_disable: return
if area.is_in_group("water"):
_kill()
func _kill(skip_anim = false):
if not controlled: return
death_counter += 1
if death_counter >= 10:
if not skip_anim: PlayerData._send_notification("too many deaths in a row! sending to spawn...", 1)
else : PlayerData._send_notification("returning to spawn", 1)
world._enter_zone("main_zone", - 1)
PlayerData.player_saved_zone = "main_zone"
PlayerData.player_saved_zone_owner = - 1
last_valid_pos = world.map.spawn_position.global_transform.origin
death_counter = 0
if state == STATES.FISHING or state == STATES.FISHING_CAST or state == STATES.FISHING_CAST:
_enter_state(STATES.FISHING_CANCEL)
cam_push = 0.0
gravity_disable = true
if not skip_anim:
_enter_animation("drown", true, true)
_sync_sfx("drown")
_sync_particle("splash", global_transform.origin, true)
SceneTransition._fake_scene_change()
yield (SceneTransition, "_finished")
global_transform.origin = last_valid_pos + Vector3(0, 0.5, 0)
yield (get_tree().create_timer(0.3), "timeout")
gravity_disable = false
_enter_state(0)
_exit_animation()
func _consume_item(id):
if state != STATES.DEFAULT: return
_enter_state(STATES.EMOTING)
consume_on_state_change = held_item["ref"]
_enter_animation("drink", false, true, STATES.CONSUME_ITEM, true)
var sfx = "drink"
match id:
"growth": player_scale = clamp(player_scale + 0.1, 0.7, 1.3)
"shrink": player_scale = clamp(player_scale - 0.1, 0.7, 1.3)
"revert":
player_scale = 1.0
drunk_timer = max(drunk_timer - 9000, 0)
sfx = "drink_nocap"
"speed":
boost_timer = 18000
boost_amt = 1.3
sfx = "drink_nocap"
"speed_burst":
boost_timer = 900
boost_amt = 4.0
sfx = "drink_nocap"
"catch":
catch_drink_timer = 18000
catch_drink_boost = 1.15
catch_drink_reel = 1.25
catch_drink_xp = 1.0
catch_drink_tier = 1
catch_drink_gold_add = Vector2(1, 10)
catch_drink_gold_percent = 0.0
"catch_big":
catch_drink_timer = 18000
catch_drink_boost = 1.3
catch_drink_reel = 1.45
catch_drink_xp = 1.0
catch_drink_tier = 2
catch_drink_gold_add = Vector2(10, 50)
catch_drink_gold_percent = 0.25
"catch_deluxe":
catch_drink_timer = 18000
catch_drink_boost = 1.3
catch_drink_reel = 1.45
catch_drink_xp = 1.25
catch_drink_tier = 3
catch_drink_gold_add = Vector2(0, 0)
catch_drink_gold_percent = 0.0
"beer":
drunk_timer += 9000
drunk_timer = clamp(drunk_timer, 0, 50000)
"beer_big":
drunk_timer += 42000
drunk_timer = clamp(drunk_timer, 0, 50000)
sfx = "drink_nocap"
_sync_sfx(sfx)
func _open_chest(rare = false):
if state != STATES.DEFAULT: return
consume_on_state_change = held_item["ref"]
_enter_state(STATES.CONSUME_ITEM)
var cosm = PlayerData._get_unowned_cosmetic()
if cosm != null: PlayerData._unlock_cosmetic(cosm)
else :
PlayerData._send_notification("You found 100 dollars inside the chest!")
PlayerData.money += 100
func _open_ringbox():
if state != STATES.DEFAULT: return
consume_on_state_change = held_item["ref"]
_enter_state(STATES.CONSUME_ITEM)
if not PlayerData.cosmetics_unlocked.has("accessory_ring"): PlayerData._unlock_cosmetic("accessory_ring")
else :
PlayerData._send_notification("You already have a ring unlocked... Obtained $1000 instead.")
PlayerData.money += 1000
func _scratch_off(type):
_enter_state(STATES.GUITAR)
hud._open_minigame("scratch_off", {"type": type})
var _win = yield (hud, "_minigame_finished")
yield (get_tree().create_timer(0.15), "timeout")
_enter_state(STATES.DEFAULT)
consume_on_state_change = held_item["ref"]
_enter_state(STATES.CONSUME_ITEM)
if not _win:
_sync_face_emote("angry")
_sync_sfx("rip")
else :
_sync_face_emote("happy")
func _mushroom_bounce():
if not controlled: return
ignore_snap = 10
var bounce_horz = 16.0 if direction != Vector3.ZERO else 0.0
var bounce_vert = 32.0
gravity_vec = Vector3.ZERO
gravity_vec.x += (direction.normalized() * bounce_horz).x
gravity_vec.z += (direction.normalized() * bounce_horz).z
gravity_vec.y += bounce_vert
animation_data["mushroom"] = true
func _sync_sfx(id, position = null, pitch = 1.0, delay = 0.0):
if not controlled: return
if delay > 0.0: yield (get_tree().create_timer(delay), "timeout")
_play_sfx(id, position, pitch)
Network._send_actor_action(actor_id, "_play_sfx", [id, position, pitch], false)
func _play_sfx(id, position = null, pitch = 1.0):
if not in_zone or not visible: return
sound_manager._play_sound(id, position, pitch)
print("playing sfx ", id)
func _process_sounds():
if $sound_manager / dive_scrape.playing != animation_data["dive_scrape"]: $sound_manager / dive_scrape.playing = animation_data["dive_scrape"] and in_zone
if $sound_manager / reel_slow.stream_paused == animation_data["reel_slow"]: $sound_manager / reel_slow.stream_paused = not animation_data["reel_slow"] and in_zone
if $sound_manager / reel_fast.playing != animation_data["reel_fast"]: $sound_manager / reel_fast.playing = animation_data["reel_fast"] and in_zone
func _on_rain_timer_timeout():
if not controlled: return
var rain = false
for child in $raincloud_check.get_overlapping_areas():
if child.is_in_group("rain_cloud"):
rain = true
if rain != in_rain:
in_rain = rain
PlayerData.emit_signal("_rain_toggle", in_rain)
if not in_rain: Network.set_rich_presence("#default")
else : Network.set_rich_presence("#rain")
func _wag():
animation_data["wagging"] = not animation_data["wagging"]
func _paint(size, color):
if not controlled: return
var in_zone = false
for area in $paint_node / Area.get_overlapping_areas():
if area.is_in_group("canvas"): in_zone = true
if not in_zone: PlayerData._send_notification("mouse must be on a drawing zone!", 1)
$paint_node.size = size
$paint_node.color = color
$paint_node.drawing = true
func _paint_stop():
if not controlled: return
$paint_node.drawing = false
PlayerData.emit_signal("_chalk_send")
func _metal_detect_begin():
pass
func _metaldetect_update():
if not controlled or held_item.id == "":
return
var idata = Globals.item_data[held_item["id"]]["file"]
if not idata.detect_item:
return
var alert_level = 0
for b in $detection_zones / metal_detect_far.get_overlapping_bodies():
if b.is_in_group("metal_spawn"):
if abs(b.global_transform.origin.y - global_transform.origin.y) > 3.5: continue
alert_level = b.global_transform.origin.distance_to(global_transform.origin)
metal_detect_alert_level = alert_level
if alert_level > 0: _metal_detect_beep()
func _metal_detect_beep():
var new_cd = ceil(max(metal_detect_alert_level * 0.5, 1))
if abs(new_cd - metal_detect_alert_cd) > 4: metal_detect_alert_cd = 0
metal_detect_alert_cd -= 1
if metal_detect_alert_cd > 0: return
metal_detect_alert_cd = new_cd
var pitch = max(6.0 - metal_detect_alert_level * 0.3, 0.5)
$metaldetect_dot.modulate.a = 1.0
if metal_detect_flop: _sync_sfx("md_beep_slowb", null, pitch)
else : _sync_sfx("md_beep_slow", null, pitch)
metal_detect_flop = not metal_detect_flop
func _on_metal_detect_consume_body_entered(b):
if not controlled or held_item.id == "": return
var idata = Globals.item_data[held_item["id"]]["file"]
if not idata.detect_item: return
if b.is_in_group("metal_spawn"):
b._reveal()
func _on_image_update_timeout():
if not controlled or dead_actor: return
_update_held_item(held_item)
Network._send_actor_action(actor_id, "_update_held_item", [held_item], false)
func _real_step(run = false):
if anim_tree.get("parameters/dive/blend_amount") > 0.2 or anim_tree.get("parameters/movement/blend_amount") < 0.8: return
if not is_on_floor(): return
var sfx = "step" if not run else "step_run"
if boost_amt > 1.0 and boost_timer > 1: sfx = "step_fastrun"
_sync_sfx(sfx)
func _play_guitar():
_enter_state(STATES.GUITAR)
hud._open_minigame("guitar")
var _win = yield (hud, "_minigame_finished")
yield (get_tree().create_timer(0.15), "timeout")
_enter_state(STATES.DEFAULT)
func _strum_guitar(string, fret, volume):
_sync_strum(string, fret, volume)
Network._send_actor_action(actor_id, "_sync_strum", [string, fret, volume])
animation_data["land"] = clamp(animation_data["land"] + 0.06, 0.0, 0.3)
_sync_face_emote("strum")
if randf() < 0.05:
_sync_particle("music")
func _sync_strum(string, fret, volume):
if not in_zone or not visible: return
$guitar_sounds.get_child(string)._play_fret(fret, false, volume)
func _hammer_string(string, fret):
_sync_hammer(string, fret)
Network._send_actor_action(actor_id, "_sync_hammer", [string, fret])
func _sync_hammer(string, fret):
if not in_zone or not visible: return
$guitar_sounds.get_child(string)._hammer_fret(fret)
func _bark():
if not controlled: return
var bark_id = "bark_dog"
bark_id = {
"species_cat": ["bark_cat", "growl_cat", "whine_cat"],
"species_dog": ["bark_dog", "growl_dog", "whine_dog"],
}[PlayerData.cosmetics_equipped.species]
var type = 0
if Input.is_action_pressed("move_sneak"):
_sync_face_emote("growl")
type = 1
elif Input.is_action_pressed("move_walk"):
_sync_face_emote("whine")
type = 2
else :
_sync_face_emote("bark")
bark_id = bark_id[type]
_sync_sfx(bark_id)
func _kiss():
if not controlled: return
animation_data["land"] = animation_data["land"] + 0.2
_sync_face_emote("kiss")
_sync_sfx("kiss")
_sync_particle("kiss")
func _punch(type = 0):
if not controlled or item_cooldown > 0: return
item_cooldown = 30
animation_data["land"] = animation_data["land"] + 1.0
for b in $detection_zones / punch.get_overlapping_bodies():
if b.is_in_group("player") and b != self and not b.controlled:
Network._send_P2P_Packet({"type": "player_punch", "from": global_transform.origin, "player": owner_id, "punch_type": type}, str(b.owner_id), 2)
_sync_face_emote("punch")
_sync_sfx("punch")
_sync_punch()
Network._send_actor_action(actor_id, "_sync_punch")
func _sync_punch():
$emotion_particles / punch_particles.restart()
$emotion_particles / punchb_particles.restart()
func _punched(from, type):
if not controlled: return
if OptionsMenu.punchable: return
var dir = (global_transform.origin - from).normalized()
ignore_snap = 10
var bounce_horz = 4.0
var bounce_vert = 8.0
match type:
0:
bounce_horz = 4.0
bounce_vert = 8.0
1:
bounce_horz = 12.0
bounce_vert = 24.0
gravity_vec = Vector3.ZERO
gravity_vec.x += (dir.normalized() * bounce_horz).x
gravity_vec.z += (dir.normalized() * bounce_horz).z
gravity_vec.y += bounce_vert
_sync_face_emote("angry")
func _tambourine():
_sync_sfx("tambourine")
animation_data["land"] = clamp(animation_data["land"] + 0.14, 0.0, 0.3)
_sync_face_emote("strum")
if randf() < 0.25:
_sync_particle("music")
func _on_cosmetic_refresh_timeout():
if not controlled: return
_refresh_cosmetics()
func _refresh_cosmetics():
if not controlled: return
yield (get_tree().create_timer(1.0), "timeout")
var new = PlayerData.cosmetics_equipped.duplicate()
if not dead_actor: Network._send_actor_action(actor_id, "_update_cosmetics", [new])
func _return_to_spawn():
if not controlled: return
death_counter = 11
_kill(true)
func _item_removal(ref):
print("Sold Item")
if held_item.keys().has("ref") and held_item["ref"] == ref: _equip_item(PlayerData.FALLBACK_ITEM.duplicate())