Advertisement
Not a member of Pastebin yet?
Sign Up,
it unlocks many cool features!
- extends CharacterBody2D
- class_name Player
- #
- # MOVEMENT & ANIMATION SETTINGS
- #
- @export var speed: float = 600
- @export var gravity: float = 1000
- # Ground movement
- @export var ground_acceleration: float = 1000.0
- @export var ground_deceleration: float = 2300.0
- # Air movement
- @export var air_acceleration: float = 400.0
- @export var air_turnaround_deceleration: float = 800.0
- @export var air_no_input_deceleration: float = 50.0
- # Jump
- @export var jump_force: float = 400
- @export var jump_cut_off: float = 0.5
- # Triple jump
- @export var triple_jump_timeout: float = 0.3
- @export var second_jump_multiplier: float = 1.6
- @export var third_jump_multiplier: float = 2.0
- @export var third_jump_rotation: float = 360.0
- @export var third_jump_rotation_duration: float = 0.5
- # Jump Dust Effect
- @export var jump_dust_scene: PackedScene
- # Jump Queue
- var jump_queued: bool = false
- var jump_queue_timer: float = 0.0
- @export var jump_queue_duration: float = 1.0
- #Sound effect volumes
- @export var JumpLandVolumeDB: float = -14.5
- #
- # INTERNAL STATE
- #
- var jump_chain_count: int = 0
- var elapsed_time: float = 0.0
- var last_land_time: float = 0.0
- var was_on_floor: bool = false
- var jump_land_timer: float = 0.0
- var jump_land_duration: float = 0.2
- var is_chopping: bool = false
- var current_woodpile: Node = null # Reference to the current woodpile being interacted with
- var chop_count: int = 0 # Number of chops performed on the current woodpile
- var input_enabled: bool = true
- var force_idle_time: float = 0.0
- var original_jump_chain_count: int = 0 # Variable to store the original jump chain count
- var stored_velocity: Vector2 = Vector2.ZERO
- var frozen: bool = false
- # Array to hold chop sound effects
- var chop_sounds: Array = []
- # Jump sound effects directory
- var jump_up_sfx = [
- preload("res://Assets/Sound Effects/JumpUpSFX/JumpUp1.mp3"),
- preload("res://Assets/Sound Effects/JumpUpSFX/JumpUp2.mp3"),
- preload("res://Assets/Sound Effects/JumpUpSFX/JumpUp3.mp3")
- ]
- # Signal to notify woodpile destruction
- signal woodpile_destroyed
- var active_respawn_box: RespawnBox = null
- func set_active_respawn_box(box: RespawnBox) -> void:
- active_respawn_box = box
- func freeze() -> void:
- is_respawning = true
- # Example: disable movement/input here if needed.
- # velocity = Vector2.ZERO
- func unfreeze() -> void:
- is_respawning = false
- # Re-enable movement/input here.
- func play_internal_animation_Mute() -> void:
- var LandSFX = $JumpLandSound
- var timer : Timer = Timer.new()
- add_child(timer)
- timer.wait_time = 5.0
- LandSFX.set_volume_db(-80)
- timer.timeout.connect(_Land_Sound_Unmute)
- timer.start()
- func _Land_Sound_Unmute() -> void:
- var LandSFX = $JumpLandSound
- LandSFX.set_volume_db(JumpLandVolumeDB)
- func _ready():
- # Set the initial spawn position
- spawn_position = position
- add_to_group("player")
- # Ensure the steam effect is hidden initially
- if steam_effect:
- steam_effect.visible = false
- # Connect necessary signals and initialize sound effects
- randomize()
- $ChopArea.connect("area_entered", Callable(self, "_on_chop_area_body_entered"))
- $ChopArea.connect("area_exited", Callable(self, "_on_chop_area_body_exited"))
- $AnimatedSprite2D.connect("frame_changed", Callable(self, "_on_frame_changed"))
- # Preload chop sound effects into an array
- chop_sounds = [
- preload("res://Assets/Sound Effects/ChopSFX/ChopSound1.mp3"),
- preload("res://Assets/Sound Effects/ChopSFX/ChopSound2.mp3"),
- preload("res://Assets/Sound Effects/ChopSFX/ChopSound3.mp3"),
- preload("res://Assets/Sound Effects/ChopSFX/ChopSound4.mp3"),
- preload("res://Assets/Sound Effects/ChopSFX/ChopSound5.mp3"),
- preload("res://Assets/Sound Effects/ChopSFX/ChopSound6.mp3"),
- preload("res://Assets/Sound Effects/ChopSFX/ChopSound7.mp3"),
- preload("res://Assets/Sound Effects/ChopSFX/ChopSound8.mp3"),
- ]
- var jump_count: int = 0
- var last_jump_time: float = 0.0
- func force_third_jump(launch_factor: float = 2.0) -> void:
- jump_count = 2
- last_jump_time = Time.get_ticks_msec() / 1000.0
- # Apply the jump using our custom launch_factor
- velocity.y = -(jump_force * launch_factor)
- $AnimatedSprite2D.stop()
- $AnimatedSprite2D.play("Jump Up")
- var original_scale = scale
- var stretched_scale = Vector2(original_scale.x * 0.8, original_scale.y * 1.4)
- var tween = create_tween()
- # 1) Stretch over 0.05s
- tween.tween_property(self, "scale", stretched_scale, 0.1)
- # 2) After 0.05s, shrink back over 0.05s
- tween.tween_property(self, "scale", original_scale, 0.05).set_delay(0.05)
- func set_input_enabled(enabled: bool) -> void:
- input_enabled = enabled
- func play_idle_animation() -> void:
- $AnimatedSprite2D.play("Idle")
- func force_idle() -> void:
- velocity = Vector2.ZERO
- $AnimatedSprite2D.stop()
- $AnimatedSprite2D.play("Idle")
- # Force the Idle animation for 0.5 seconds, overriding falling logic.
- force_idle_time = 0.2
- func _physics_process(delta):
- if frozen:
- # When frozen, simply update the physics without applying gravity.
- move_and_slide()
- return
- if is_respawning:
- # Ignore input or forcibly set velocity to zero here
- velocity = Vector2.ZERO
- $AnimatedSprite2D.play("Respawning")
- $RunningSound.stop()
- return
- if not input_enabled:
- # Process physics if necessary, but ignore input.
- velocity.y += gravity * delta
- move_and_slide()
- return
- # If chopping, skip all movement logic
- if is_chopping:
- move_and_slide() # Allow for gravity application while chopping
- return
- # Update timers
- elapsed_time += delta
- # Apply gravity
- velocity.y += gravity * delta
- # Handle Jump Input
- if Input.is_action_just_pressed("ui_up"):
- if is_on_floor():
- _perform_jump()
- elif is_near_ground(): # Allow jump queue when near ground
- jump_queued = true
- jump_queue_timer = jump_queue_duration
- # Handle Queued Jump
- if jump_queued:
- jump_queue_timer -= delta
- if jump_queue_timer <= 0:
- jump_queued = false # Expire the jump queue
- elif is_on_floor():
- jump_queued = false
- _perform_jump() # Properly trigger the jump, respecting the chain
- # Ground vs. Air Movement
- if is_on_floor():
- _handle_ground_movement(delta)
- else:
- _handle_air_movement(delta)
- # Short hop logic
- #if Input.is_action_just_released("ui_up") and velocity.y < 0:
- #velocity.y *= jump_cut_off
- # Move the character
- move_and_slide()
- # Landing Detection
- if is_on_floor() and not was_on_floor:
- last_land_time = elapsed_time
- if flip_tween and flip_tween.is_valid():
- flip_tween.kill()
- flip_tween = null
- rotation_degrees = 0
- # Reset jump chain count after landing
- if jump_chain_count == 3:
- jump_chain_count = 0 # Reset after completing the third jump
- self.rotation_degrees = 0
- $AnimatedSprite2D.stop()
- $AnimatedSprite2D.play("Jump Land")
- jump_land_timer = jump_land_duration
- $JumpLandSound.pitch_scale = randf_range(1, 1.45)
- $JumpLandSound.play()
- # Reset jump chain if speed < 250 or timeout exceeded
- if is_on_floor():
- if abs(velocity.x) < 250 or (elapsed_time - last_land_time > triple_jump_timeout):
- jump_chain_count = 0
- was_on_floor = is_on_floor()
- # Update animations
- _update_animation(delta)
- # Handle Chopping
- if Input.is_action_just_pressed("mouse_button_1"):
- _start_chop()
- #
- # HELPER FUNCTIONS
- #
- func is_near_ground() -> bool:
- return $RayCast2D.is_colliding()
- func _handle_ground_movement(delta):
- var input_dir = 0
- if Input.is_action_pressed("ui_left"):
- input_dir -= 1
- $AnimationPlayer.play("Left_Flip")
- $ChopArea.position = Vector2(-30, 0) # Move ChopArea to the left
- if Input.is_action_pressed("ui_right"):
- input_dir += 1
- $AnimationPlayer.play("Right_Flip")
- $ChopArea.position = Vector2(30, 0) # Move ChopArea to the right
- var target_speed = input_dir * speed
- if input_dir != 0:
- velocity.x = move_toward(velocity.x, target_speed, ground_acceleration * delta)
- else:
- velocity.x = move_toward(velocity.x, 0, ground_deceleration * delta)
- func _handle_air_movement(delta):
- var input_dir = 0
- if Input.is_action_pressed("ui_left"):
- input_dir -= 1
- if Input.is_action_pressed("ui_right"):
- input_dir += 1
- if input_dir == 0:
- velocity.x = move_toward(velocity.x, 0, air_no_input_deceleration * delta)
- else:
- var target_speed = input_dir * speed
- if sign(velocity.x) != sign(target_speed) and abs(velocity.x) > 0.1:
- velocity.x = move_toward(velocity.x, 0, air_turnaround_deceleration * delta)
- else:
- velocity.x = move_toward(velocity.x, target_speed, air_acceleration * delta)
- # Track whether the third jump flip has been triggered
- var in_third_jump_flip: bool = false
- func _perform_jump():
- # Reset the jump chain if the timeout has expired
- if elapsed_time - last_land_time > triple_jump_timeout:
- jump_chain_count = 1
- else:
- jump_chain_count += 1
- # Clamp jump chain count to 3
- if jump_chain_count > 3:
- jump_chain_count = 3
- # Determine the jump force and animation
- var this_jump_force = jump_force
- var animation_name = "1st Jump"
- if jump_chain_count == 2:
- this_jump_force *= second_jump_multiplier
- animation_name = "2nd Jump"
- elif jump_chain_count == 3:
- this_jump_force *= third_jump_multiplier
- animation_name = "3rd Jump"
- # Trigger the third jump flip
- _trigger_third_jump_flip()
- # Play the JumpWoosh sound effect
- if $JumpWoosh:
- $JumpWoosh.pitch_scale = randf_range(1.2, 2.0)
- $JumpWoosh.play()
- # Apply the jump force
- velocity.y = -this_jump_force
- _spawn_jump_dust(animation_name)
- # Play the jump animation and sound
- $AnimatedSprite2D.stop()
- $AnimatedSprite2D.play("Jump Up")
- if $JumpAudio:
- $JumpAudio.stream = jump_up_sfx[randi() % jump_up_sfx.size()]
- $JumpAudio.pitch_scale = randf_range(0.9, 1.2)
- $JumpAudio.play()
- var flip_tween: Tween = null
- func _trigger_third_jump_flip():
- if flip_tween and flip_tween.is_valid():
- flip_tween.kill()
- var flip_direction = sign(velocity.x)
- if flip_direction == 0:
- flip_direction = 1
- flip_tween = create_tween()
- flip_tween.set_ease(Tween.EASE_OUT)
- flip_tween.set_trans(Tween.TRANS_CIRC)
- var first_overshoot_rotation = flip_direction * (third_jump_rotation + 25)
- var second_overshoot_rotation = flip_direction * (third_jump_rotation - 10)
- flip_tween.tween_property(self, "rotation_degrees", first_overshoot_rotation, third_jump_rotation_duration * 0.6)
- flip_tween.tween_property(self, "rotation_degrees", second_overshoot_rotation, third_jump_rotation_duration * 0.3)
- flip_tween.set_trans(Tween.TRANS_ELASTIC)
- flip_tween.tween_property(self, "rotation_degrees", flip_direction * third_jump_rotation, third_jump_rotation_duration * 3)
- flip_tween.connect("finished", Callable(self, "_on_flip_tween_finished"))
- func _on_flip_tween_finished():
- flip_tween = null
- func _spawn_jump_dust(animation_name: String):
- if jump_dust_scene:
- var dust_instance = jump_dust_scene.instantiate()
- dust_instance.global_position = global_position
- # Adjust the vertical position for each animation
- var offset_y = 0
- match animation_name:
- "1st Jump":
- offset_y = 21
- "2nd Jump":
- offset_y = 0
- "3rd Jump":
- offset_y = -8
- dust_instance.position.y += offset_y
- get_parent().add_child(dust_instance)
- dust_instance.play(animation_name)
- func _update_animation(delta):
- # If we're forcing idle, decrement the timer and force the Idle animation.
- if force_idle_time > 0:
- force_idle_time -= delta
- $AnimatedSprite2D.play("Idle")
- return
- if $AnimatedSprite2D.animation == "Jump Land" and jump_land_timer > 0:
- jump_land_timer -= delta
- return
- # If airborne and falling, play the first frame of Jump Land
- if not is_on_floor() and velocity.y > 0: # Falling
- if $AnimatedSprite2D.animation != "Jump Land" or $AnimatedSprite2D.frame != 0:
- $AnimatedSprite2D.stop()
- $AnimatedSprite2D.play("Jump Land")
- $AnimatedSprite2D.frame = 0 # Display the first frame of Jump Land
- $AnimatedSprite2D.stop() # Pause the animation at the first frame
- $RunningSound.stop() # Stop the running sound when airborne
- return
- # Ground animations (Idle, Walking, Running)
- if is_on_floor():
- var spd = abs(velocity.x)
- if spd < 17: # any less than 17 makes running into walls look buggy.
- $AnimatedSprite2D.play("Idle")
- $RunningSound.stop()
- elif spd < 350:
- $AnimatedSprite2D.play("Walking")
- $RunningSound.stop()
- else:
- $AnimatedSprite2D.play("Running")
- if not $RunningSound.playing:
- $RunningSound.play()
- else:
- # Stop the running sound when not on the floor
- $RunningSound.stop()
- # Steam Effect Variables
- @onready var steam_effect = $AnimatedSprite2D/SteamEffect
- func _shake_woodpile(woodpile):
- var sprite = woodpile.get_parent().get_node("Sprite2D")
- # Check that the sprite is still valid.
- if not is_instance_valid(sprite):
- return
- var original_position = sprite.position
- # Defer the tween creation so that if the woodpile is freed soon after,
- # the tween won’t be started.
- call_deferred("_start_shake_tween", sprite, original_position)
- func _start_shake_tween(sprite, original_position):
- # Check again that the sprite is still valid.
- if not is_instance_valid(sprite):
- return
- var tween = create_tween()
- tween.set_trans(Tween.TRANS_SINE)
- tween.set_ease(Tween.EASE_OUT)
- tween.tween_property(sprite, "position", original_position + Vector2(-5, 0), 0.05)
- tween.tween_property(sprite, "position", original_position + Vector2(5, 0), 0.05)
- tween.tween_property(sprite, "position", original_position, 0.05)
- # Modify _on_frame_changed to trigger the steam effect
- func _on_frame_changed():
- if $AnimatedSprite2D.animation == "Chop" and $AnimatedSprite2D.frame == 4:
- # Check if the player is hitting a woodpile
- if current_woodpile:
- # Play a random chop sound with random pitch
- if chop_sounds.size() > 0:
- $ChopSounds.stream = chop_sounds[randi() % chop_sounds.size()]
- $ChopSounds.pitch_scale = randf_range(0.9, 1.2) # Random pitch variation
- $ChopSounds.play()
- # Only shake the woodpile on the first two chops
- if chop_count < 2:
- _shake_woodpile(current_woodpile)
- # Emit particles: emit extra particles on the third hit
- if chop_count == 2:
- _emit_particles(current_woodpile, 1000) # Emit more particles for the last chop
- else:
- _emit_particles(current_woodpile) # Emit default 500 particles
- chop_count += 1
- # Play steam effect
- _play_steam_effect()
- # Handle woodpile destruction on the third chop
- if chop_count >= 3:
- var woodpile_sprite = current_woodpile.get_parent().get_node("Sprite2D")
- if woodpile_sprite:
- woodpile_sprite.queue_free()
- current_woodpile.queue_free()
- current_woodpile = null
- chop_count = 0
- emit_signal("woodpile_destroyed")
- # Add a function to play and stop the steam effect
- var is_steam_playing: bool = false # Add this variable to track if the steam effect is already playing
- func _emit_particles(woodpile, amount: int = 500):
- var particles = woodpile.get_parent().get_node("CPUParticles2D")
- if particles:
- particles.amount = amount
- particles.emitting = true
- await get_tree().create_timer(0.05).timeout
- particles.emitting = false
- func _play_steam_effect():
- if steam_effect and not is_steam_playing:
- is_steam_playing = true # Set the flag to true when the animation starts
- steam_effect.visible = true
- steam_effect.play("default") # Replace "default" with the name of your steam animation
- $SteamSound.pitch_scale = randf_range(0.5, 1.2)
- $SteamSound.play()
- # Connect the animation_finished signal
- steam_effect.animation_finished.connect(_stop_steam_effect)
- func _stop_steam_effect():
- if steam_effect:
- steam_effect.visible = false
- steam_effect.stop()
- steam_effect.animation_finished.disconnect(_stop_steam_effect)
- is_steam_playing = false # Reset the flag when the animation finishes
- func _start_chop():
- if velocity.x == 0 and is_on_floor(): # Only chop when stationary and on the ground
- is_chopping = true
- $AnimatedSprite2D.stop()
- $AnimatedSprite2D.play("Chop")
- $AnimatedSprite2D.connect("animation_finished", Callable(self, "_on_chop_animation_finished"))
- $Swing.pitch_scale = randf_range(1, 0.5)
- $Swing.play()
- func _on_chop_animation_finished():
- if $AnimatedSprite2D.animation == "Chop":
- is_chopping = false
- $AnimatedSprite2D.disconnect("animation_finished", Callable(self, "_on_chop_animation_finished"))
- func _on_chop_area_body_entered(area):
- if area.name == "WoodPileArea2D":
- current_woodpile = area
- chop_count = 0
- func _on_chop_area_body_exited(area):
- if area.name == "WoodPileArea2D" and current_woodpile == area:
- current_woodpile = null
- # Add to your variables section
- var spawn_position: Vector2
- var is_respawning: bool = false
- func respawn() -> void:
- is_respawning = true
- velocity = Vector2.ZERO
- # Fade out over 0.5 seconds (adjust as desired).
- var tween = create_tween()
- var fade_out = tween.tween_property(self, "modulate", Color(1, 1, 1, 0), 0.5)
- await fade_out.finished
- # Teleport to spawn_position.
- global_position = spawn_position
- rotation_degrees = 0
- velocity = Vector2.ZERO
- # Fade in over 0.5 seconds.
- tween = create_tween()
- var fade_in = tween.tween_property(self, "modulate", Color(1, 1, 1, 1), 0.5)
- await fade_in.finished
- is_respawning = false
- func freeze_movement() -> void:
- # Store current velocity so you can restore it later.
- stored_velocity = velocity
- # Zero out the velocity.
- velocity = Vector2.ZERO
- # Set the frozen flag so gravity isn’t applied.
- frozen = true
- # Stop the animation so the sprite stays on its current frame.
- $AnimatedSprite2D.pause()
- # Disable input.
- set_input_enabled(false)
- $RunningSound.stop()
- func unfreeze_movement() -> void:
- # Restore the stored velocity.
- velocity = stored_velocity
- stored_velocity = Vector2.ZERO
- # Clear the frozen flag so gravity resumes.
- frozen = false
- # Re-enable input.
- set_input_enabled(true)
- # Resume an appropriate animation (for example, Idle).
- $AnimatedSprite2D.play()
- func _flip_left():
- $AnimationPlayer.play("Left_Flip")
- func _flip_right():
- $AnimationPlayer.play("Right_Flip")
- func _force_walk_anim():
- $AnimatedSprite2D.play("Walking")
- @onready var scene_transition_pos_marker: Marker2D = $"../NextScenePrompt/SceneTransitionAnim/SceneTransitionPosMarker"
- func scene_transition_player_postition_change():
- self.global_position = scene_transition_pos_marker.global_position
- func transition_player_movement():
- # Get the player's current position
- var original_position = self.position
- # Define the offset (for example, 100 pixels right and 50 pixels down)
- var offset_position = original_position + Vector2(500, 0)
- # Create and configure the tween
- var tween = create_tween()
- tween.set_trans(Tween.TRANS_LINEAR)
- tween.set_process_mode(Tween.TWEEN_PROCESS_IDLE)
- # Tween the player's position to the offset position over 0.5 seconds
- tween.tween_property(self, "position", offset_position, 3.5)
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement