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())