rewrite this so I can control it when I swap to 3d plane:

extends CharacterBody3D

const MAX_LIFEPOINTS := 200

export var speed := 5.0
export var jump_velocity := 4.5
var _lifepoints: int = MAX_LIFEPOINTS setget , get_lifepoints

var gravity: float = ProjectSettings.get_setting("physics/3d/default_gravity")
var _attackTimer: Timer
var _visual: Node3D
var _playerLifebar: ProgressBar
var _animationTree: AnimationTree

func _ready():
	_playerLifebar = get_tree().current_scene.get_node("HUD/PlayerLifeBar")
	_playerLifebar.max_value = MAX_LIFEPOINTS
	_playerLifebar.value = _lifepoints

	_attackTimer = get_node("AttackCooldown")
	_visual = get_node("Visual")
	_animationTree = get_node("AnimationTree")

func get_lifepoints() -> int:
	return _lifepoints

func _process(delta: float) -> void:
	_playerLifebar.value = _lifepoints

func _physics_process(delta: float) -> void:
	var velocity := get_velocity()

	# Add gravity
	if not is_on_floor():
		velocity.y -= gravity * delta
	
	# Handle movement and deceleration
	var input_dir := Vector2(
		Input.get_action_strength("move_right") - Input.get_action_strength("move_left"),
		Input.get_action_strength("move_back") - Input.get_action_strength("move_forward")
	)
	var direction := (global_transform.basis.xform(Vector3(input_dir.x, 0, input_dir.y))).normalized()

	if direction != Vector3.ZERO:
		velocity.x = direction.x * speed
		velocity.z = direction.z * speed
	else:
		velocity.x = lerp(velocity.x, 0, speed * delta)
		velocity.z = lerp(velocity.z, 0, speed * delta)

	set_velocity(velocity)
	move_and_slide()

	velocity.y = 0.0
	_animationTree.set("parameters/walking/blend_amount", velocity.length())

	if velocity.length() == 0.0:
		return
	_visual.look_at(get_global_transform().origin + 10 * velocity, Vector3.UP)

func _on_AttackTimeOut():
	_attackTimer.start()

func take_damages(damages: int):
	_lifepoints = clamp(_lifepoints - damages, 0, MAX_LIFEPOINTS)

func heal(heal_amount: int) -> void:
	_lifepoints = clamp(_lifepoints + heal_amount, 0, MAX_LIFEPOINTS)

func _on_SwapPlane():
	var sceneChangerScript = load("res://Scripts/Global/SceneChanger.gd") as Script
	var sceneChangerNode = get_node("SceneChanger")
	sceneChangerNode.call("change_to_2d")



my 2d plane:
extends CharacterBody2D
class_name Player2D
enum State { IDLE, RUN, JUMP, ATTACK }
var state = State.IDLE
var bullets_amount: int = 30
var attack_animation: String = "Attack1"
var player_base = PlayerBase.new()# Use PlayerBase’s set_health method¨

@export var movement_data : MovementData
@export var stats : Stats

# References
@onready var animator : AnimatedSprite2D = $AnimatedSprite2D
@onready var guns_animator : AnimationPlayer = $ShootingAnimationPlayer
@onready var hit_animator : AnimationPlayer = $HitAnimationPlayer
@onready var hand : Node2D = $Hand
@onready var pistol : Sprite2D = $Hand/Pivot/Pistol
@onready var pistol_bullet_marker : Marker2D = $Hand/Pivot/Pistol/PistolBulletMarker
@onready var attacking: bool = false
@export var camera: Camera2D

# Load Scenes
@onready var muzzle_load: PackedScene = preload("res://Scenes/Particles/muzzle.tscn")
@onready var bullet_load: PackedScene = preload("res://Scenes/Props/bullet.tscn")
@onready var death_particle_load: PackedScene = preload("res://Scenes/Particles/player_death_particle.tscn")

func _ready():
	stats.health = stats.max_health
	player_base.set_health(stats.health)
	EventManager.bullets_amount = bullets_amount
	EventManager.update_bullet_ui.emit()
	

func _physics_process(delta):
	apply_gravity(delta)
	
	var input_vector = Input.get_axis("move_left","move_right")
	if input_vector != 0:
		apply_acceleration(input_vector, delta)
	else:
		apply_friction(delta)
	if Input.is_action_just_pressed("SwapPlane"):
		_on_swapPlane()
	if Input.is_action_just_pressed("jump") and is_on_floor():
		jump()
	
	if Input.is_action_just_pressed("shoot"):
		animator.play("Attacking")
		
		if bullets_amount:
			guns_animator.play("Shoot")
		
	
	move_and_slide()
	animate(input_vector)

func apply_acceleration(input_vector, delta):
	velocity.x = move_toward(velocity.x, movement_data.max_speed * input_vector, movement_data.acceleration * delta)

func apply_friction(delta):
	velocity.x = move_toward(velocity.x, 0, movement_data.friction * delta)

func apply_gravity(delta):
	if not is_on_floor():
		velocity.y += movement_data.gravity * movement_data.gravity_scale * delta

func process_gravity(delta):
	if not is_on_floor():
		velocity.y += movement_data.gravity * movement_data.gravity_scale * delta

func process_movement_input(delta):
	var input_vector = Input.get_axis("move_left", "move_right")
	if input_vector != 0:
		velocity.x = move_toward(velocity.x, movement_data.max_speed * input_vector, movement_data.acceleration * delta)
	else:
		velocity.x = move_toward(velocity.x, 0, movement_data.friction * delta)

func process_actions():
	if Input.is_action_just_pressed("jump") and is_on_floor():
		perform_jump()
	if Input.is_action_just_pressed("shoot"):
		perform_attack()

func animate(input_vector):
	var mouse_position : Vector2 = (get_global_mouse_position() - global_position).normalized()
	if mouse_position.x > 0 and animator.flip_h:
		animator.flip_h = false
	elif mouse_position.x < 0 and not animator.flip_h:
		animator.flip_h = true
	
	hand.rotation = mouse_position.angle()
	if hand.scale.y == 1 and mouse_position.x < 0:
		hand.scale.y = -1
	elif hand.scale.y == -1 and mouse_position.x > 0:
		hand.scale.y = 1
	
	if state != State.ATTACK:
		if is_on_floor():
			if input_vector != 0:
				animator.play("Run")
			else:
				animator.play("Idle")
		else:
			if velocity.y > 0:
					animator.play("Jump")
		# Check if the current animation is the attack animation


func perform_jump():
	velocity.y = -movement_data.jump_strength
	AudioManager.play_sound(AudioManager.JUMP)

func perform_attack():
	if not bullets_amount: return
	state = State.ATTACK
	animator.play("Attacking")
	guns_animator.play("Shoot")
	shoot()

func shoot():
	state = State.ATTACK
	attack_animation = "Attack" + str(randi_range(1, 2))
	animator.play(attack_animation)
	$Hand/AttackArea/CollisionShape2D.disabled = false;
	await animator.animation_looped
	EventManager.update_bullet_ui.emit()
	var mouse_position : Vector2 = (get_global_mouse_position() - global_position).normalized()
	var muzzle = muzzle_load.instantiate()
	var bullet = bullet_load.instantiate()
	pistol_bullet_marker.add_child(muzzle)
	bullet.global_position = pistol_bullet_marker.global_position
	bullet.target_vector = mouse_position
	bullet.rotation = mouse_position.angle()
	get_tree().current_scene.add_child(bullet)
	AudioManager.play_sound(AudioManager.SHOOT)

	$Hand/AttackArea/CollisionShape2D.disabled = true;
	
	state = State.IDLE

func update_animation_state():
	if state == State.ATTACK:
		return
	
	if is_on_floor():
		if velocity.x != 0:
			animator.play("Run")
		else:
			animator.play("Idle")
	else:
		animator.play("Jump") if velocity.y < 0 else animator.play("Fall")

func _on_hurtbox_area_entered(_area):
	if not _area.is_in_group("Bullet"):
		hit_animator.play("Hit")
		take_damage(1)  # If damage is to be variable, extract value from _area
		EventManager.frame_freeze.emit()

func die():
	AudioManager.play_sound(AudioManager.DEATH)
	animator.play("Died")
	spawn_death_particle()
	EventManager.player_died.emit()
	queue_free()

func spawn_death_particle():
	var death_particle = death_particle_load.instantiate()
	death_particle.global_position = global_position
	get_tree().current_scene.add_child(death_particle)

# Override PlayerBase method
func take_damage(damage: int) -> void:
	player_base.health -= damage  # Use the health property from PlayerBase
	if player_base.health <= 0:
		die()
	else:
		AudioManager.play_sound(AudioManager.HURT)
func _on_swapPlane():
	PlayerState.set_last_2d_position(global_position); 
	SceneChanger.change_to_3d(global_position) # Calls the singleton to change to 3D scene

# Implement the move method required by PlayerBase, the actual movement
# logic is in the _physics_process function above.
func move(input_dir: Vector2) -> void:
	# For Player2D, the move function is handled by process_movement_input,
	# so no additional logic is needed here.
	pass

# Implement the attack method required by PlayerBase. This is a placeholder
# and the actual logic for starting an attack is in the perform_attack function.
func attack() -> void:
	perform_attack()
	
	# Adjust the jump method to use jump_strength from PlayerBase 
func jump():
	velocity.y = -movement_data.jump_strength
	AudioManager.play_sound(AudioManager.JUMP)

func _on_health_changed(new_health: int) -> void:
	player_base.health = new_health  # Make sure to update it when health changes
	# Handle health change (e.g., update UI)