extends KinematicBody


#Objects
var bullet = preload("res://objects/Bullet.tscn")
var damagearea = preload("res://objects/level/DamageArea.tscn")

#Sounds
var footstep = preload("res://sounds/SFX/footstep.wav")
var pistol = preload("res://sounds/SFX/pistol.wav")
var click = preload("res://sounds/SFX/no_pew_pew.wav")
var ammosound = preload("res://sounds/SFX/pickups/ammo_pickup.wav")
var damage = preload("res://sounds/SFX/hurt/player_hurt.wav")
var death = preload("res://sounds/SFX/hurt/player_death.wav")
var slapsound = preload("res://sounds/SFX/slap.wav")

#Constants
const GRAVITY = -30
const AIR_FRICTION = Vector3(0.9,1,0.9)
const MAX_SLOPE_ANGLE = 75

#Camera settings
var camera_angle = 0
var mouse_sensitivity = 0.15
var camera_change = Vector2()
var cam_bob = 0
var cam_bob_max = 0
var cam_bob_multiplier = 1
var default_fov = 80
var render_distance = 100

#General Variables
var ammo = 25
var health = 100
var dead = false
var iframes = false

#Counters, Checkers, and Initializers
onready var start_time = OS.get_ticks_msec()
var cam_init
var gun_init
var canshoot = true
var canslap = true
var slapping = false
var crouches = 1
var jumping = false
var is_walking = true
var is_cutscene = false

#Interaction Variables
var action = ""
var text = ""

#Yeet
var yeet = false
var yeet_strength_v = 2.5
var yeet_strength_h = 185

#Movement Vars
var velocity = Vector3()
var dir = Vector3()
var friction = 1
var jump_height = 10

#Movement Speeds
enum MOVE_VARS {
	SPEED = 18,
	ACCEL = 8,
	DEACCEL = 11,
}

enum RUN_VARS {
	SPEED = 25,
	ACCEL = 12,
	DEACCEL = 10,
}

enum CROUCH_VARS {
	SPEED = 9,
	ACCEL = 5,
}

enum FLY_VARS {
	SPEED = 35,
	ACCEL = 5,
}

#Misc
onready var lookat : Node = $Lookingat



#Hide self, capture mouse, and initialize
func _ready():
	$MeshInstance.hide()
	Input.set_mouse_mode(Input.MOUSE_MODE_CAPTURED)
	cam_init = $Head/Camera.translation.y
	gun_init = $Head/CanvasLayer/TextureRect2.rect_position.y
	$Head/Camera.far = render_distance
	
	if globalvar.number_beans == 0:
		$Head/CanvasLayer/TopRight/BeanIndicator.visible = false
	


#Check between normal and noclip modes
func _physics_process(delta):
	if is_walking:
		if !dead:
			walk(delta)
	else:
		fly(delta)
		
		
func _input(event):
	#Aim when the mouse is moved	
	if event is InputEventMouseMotion and Input.get_mouse_mode() == Input.MOUSE_MODE_CAPTURED:
		$Head.rotate_x(deg2rad(event.relative.y * mouse_sensitivity * -1))
		self.rotate_y(deg2rad(event.relative.x * mouse_sensitivity * -1))
		
		var camera_rot = $Head.rotation_degrees
		camera_rot.x = clamp(camera_rot.x, -89, 89)
		$Head.rotation_degrees = camera_rot
	
	#Toggle between walking and noclip
	if Input.is_action_just_pressed("fly"):
		is_walking = !is_walking
		$CollisionShape.disabled = !is_walking
		
	#Shoot gun and dismiss text boxes
	if Input.is_action_just_pressed("shoot"):
		if $Head/CanvasLayer/Text.visible:
			$Head/CanvasLayer/Text.visible = false
			text = ""
		else:
			if canshoot and !dead:
				if ammo > 0:
					if randi()%3: ammo -= 1 #25% chance to not consume ammo
					$Shot.pitch_scale = rand_range(0.8,1.2)
					$Shot.stream = pistol
					$Shot.play()
					$ShootCooldown.start(0.35)
					canshoot = false
					var tempbullet = bullet.instance()
					tempbullet.global_transform = $Head/Camera/Position3D.global_transform
					if slapping:
						tempbullet.power *= 1.5
						ammo -= 0.5
					get_tree().get_root().add_child(tempbullet)
				else:
					$Shot.pitch_scale = rand_range(0.8,1.2)
					$Shot.stream = click
					$Shot.play()
			
	#Melee
	if Input.is_action_just_pressed("slap") and canslap and !dead:
		canslap = false
		slapping = true
		$Slapping.start(0.4)
		$Shot.pitch_scale = rand_range(0.8,1.2)
		$Shot.stream = slapsound
		$Shot.play()
		$ShootCooldown.start(0.35)
		
			
	#Hide the HUD
	if Input.is_action_just_pressed("hudhide"):
		$Head/CanvasLayer/HUD.visible = false
		$Head/CanvasLayer/TextureRect.visible = false
		$Head/CanvasLayer/TextureRect2.visible = false
		
	
	if Input.is_action_just_pressed("yeet"):
		yeet()
		
	if Input.is_action_just_pressed("interact"):
		if action != "":
			var interacter = lookat.get_collider()
			if interacter.has_method("interact"):
				if interacter.is_in_group("sign"):
					text = lookat.get_collider().interact()
				else:
					lookat.get_collider().interact()


func _process(delta):
	#Right Stick Aiming
	var rstick_velocity = Input.get_vector("rstick_left", "rstick_right", "rstick_up", "rstick_down")
	
	if rstick_velocity != Vector2.ZERO:
		$Head.rotate_x((-rstick_velocity.y * mouse_sensitivity * 0.75))
		self.rotate_y((-rstick_velocity.x * mouse_sensitivity * 0.75))
		
		var camera_rot = $Head.rotation_degrees
		camera_rot.x = clamp(camera_rot.x, -89, 89)
		$Head.rotation_degrees = camera_rot
	
	#Bob the camera while walking
	cam_bob = sin((OS.get_ticks_msec()-start_time)/(100/cam_bob_multiplier))*(cam_bob_max)
	$Head/Camera.translation.y = cam_init + cam_bob
	$Head/CanvasLayer/TextureRect2.rect_position = Vector2($Head/CanvasLayer/TextureRect2.rect_position.x,gun_init + -cam_bob*30)
	$Head/Slap.rotation = $Head/Camera.rotation
	$Head/Slap.translation = $Head/Camera.translation
	$Head/Slap.translation.y -= 1
	
	if lookat.is_colliding():
		if lookat.get_collider() != null:
			if lookat.get_collider().is_in_group("interactable"):
				action = lookat.get_collider().action
	else:
		action = ""

	update_hud()
	
	#Reload the scene if the player falls out of the map
	if translation.y <= -200:
		get_tree().reload_current_scene()
		
	if slapping:
		$Head/Slap/CollisionShape.disabled = false
		#if $Head/CanvasLayer/Hand.rect_rotation > -10:
		$Head/CanvasLayer/Hand.rect_rotation = lerp($Head/CanvasLayer/Hand.rect_rotation,-180,7.5*delta)

func walk(delta):
	#Reset direction every frame
	dir = Vector3()
	
	#Walk
	var cam_xform = $Head/Camera.get_global_transform()
	var input_movement_vector = Vector2()
	
	if !text:
		if Input.is_action_pressed("ui_up"):
			input_movement_vector.y += 1
			if !$Footstep.is_playing() and is_on_floor():
				$Footstep.pitch_scale = rand_range(0.8,1.2)
				$Footstep.stream = footstep
				$Footstep.play()
		if Input.is_action_pressed("ui_down"):
			input_movement_vector.y -= 1
		if Input.is_action_pressed("ui_left"):
			input_movement_vector.x -= 1
		if Input.is_action_pressed("ui_right"):
			input_movement_vector.x += 1
		
	input_movement_vector = input_movement_vector.normalized()

	dir += -cam_xform.basis.z * input_movement_vector.y
	dir += cam_xform.basis.x * input_movement_vector.x
		
	var floor_normal = $RayCast.get_collision_normal()
	
	if is_on_floor():
		jumping = false
		friction = 1
		if Input.is_action_just_pressed("ui_select"):
			velocity.y = jump_height
			velocity = move_and_slide_with_snap(velocity, Vector3.ZERO, Vector3.UP,true)
	else:
		friction = AIR_FRICTION

	dir.y = 0
	dir = dir.normalized()

	if !is_on_floor():
		velocity.y += GRAVITY * delta
	
	var hvelocity = velocity
	hvelocity.y = 0
	
	# Speed Setup
	var speed = MOVE_VARS.SPEED
	var accel
	var accelspeed = MOVE_VARS.ACCEL
	var deccelsepeed = MOVE_VARS.DEACCEL
	$Head/Camera.fov = lerp($Head/Camera.fov, default_fov, 0.5)
	set_camera_bob(0.2,1.5)

	# Run
	if Input.is_action_pressed("run") and (dir.x != 0 or dir.y != 0):
		speed = RUN_VARS.SPEED
		accelspeed = RUN_VARS.ACCEL
		deccelsepeed = RUN_VARS.DEACCEL
		$Head/Camera.fov = lerp($Head/Camera.fov, default_fov+8, 0.5)
		set_camera_bob(0.3,1.8)
		
	# Crouch
	if Input.is_action_pressed("crouch") or $CeilingCheck.is_colliding():
		set_camera_bob(0)
		$CollisionShape.scale.y = move_toward($CollisionShape.scale.y,0.5,0.1)
		$CollisionShape2.disabled = true
		speed = CROUCH_VARS.SPEED
		accelspeed = CROUCH_VARS.ACCEL
		if !is_on_floor() and crouches:
			crouches -= 1
			$CollisionShape.translation.y += 0.3
	else:
		$CollisionShape.scale.y = move_toward($CollisionShape.scale.y,1,0.1)
		$CollisionShape2.disabled = false
		$CollisionShape.translation.y = 0
		crouches = 1

	# Do final checks then move
	if dir.dot(hvelocity) > 0:
		accel = accelspeed
	else:
		accel = deccelsepeed
	
	var target = dir * speed
	
	hvelocity = hvelocity.linear_interpolate(target, accel * delta)
	velocity.x = hvelocity.x
	velocity.z = hvelocity.z
	
	#Dont bob the camera under certain conditions
	if is_on_wall() or !is_on_floor() or (input_movement_vector.x == 0 and input_movement_vector.y == 0):
		set_camera_bob(0)
	
	#Yeet when yeeting
	if yeet:
		move_and_collide(Vector3(0,1,0))
		velocity = Vector3(0,1/yeet_strength_v,0)
		velocity += Vector3(yeet_strength_h,0,yeet_strength_h)*transform.basis.z
	
	velocity = move_and_slide_with_snap(velocity*friction, -floor_normal, Vector3.UP, true, 4)
	

func fly(delta):
	set_camera_bob(0,1)
	
	#Reset direction every frame
	dir = Vector3()
	
	#Get the direction of the camera and walk relative to looking direction
	var aim = $Head/Camera.get_global_transform().basis

	if Input.is_action_pressed("ui_up"):
		dir -= aim.z
	if Input.is_action_pressed("ui_down"):
		dir += aim.z
	if Input.is_action_pressed("ui_left"):
		dir -= aim.x
	if Input.is_action_pressed("ui_right"):
		dir += aim.x
		
	dir = dir.normalized()
	
	#Do some setup then move
	var target = dir * FLY_VARS.SPEED
	velocity = velocity.linear_interpolate(target,FLY_VARS.ACCEL*delta)
	
	move_and_slide(velocity)


func take_damage(amount,type=0,source=null):
	if !dead and !iframes:
		health -= amount
		$Hurt.pitch_scale = rand_range(0.8,1.2)
		if amount < 80:
			$Hurt.stream = damage
		$Hurt.play()
		update_hud()
		if health <= -10:
			$DeathTimer.start(3)
			dead = true
			translation.y -= 1.5
			$Head/CanvasLayer/DeathOverlay.visible = true
			$Hurt.stream = death
			$Hurt.play()
			set_camera_bob(0)
		
		if health > -10 and amount >= 80:
			iframes = true
			$IFrameTimer.start(amount/60)


func give_ammo(amount):
	$SFX.pitch_scale = rand_range(0.8,1.2)
	$SFX.stream = ammosound
	$SFX.play()
	ammo += amount
	
	
func update_hud():
	#Update labels with variables
	$Head/CanvasLayer/HUD/Health.text = "Health: "+str(health)
	$Head/CanvasLayer/HUD/Ammo.text = "Ammo: "+str(ammo)
	$Head/CanvasLayer/Position.text = "Position: "+str(translation)
	
	#Show action prompt if looking at something interactable
	if action != "":
		$Head/CanvasLayer/Prompt.visible = true
		$Head/CanvasLayer/Prompt/Label2.text = action
	else:
		$Head/CanvasLayer/Prompt.visible = false
	
	#Show textbox if reading a sign	
	if text != "":
		$Head/CanvasLayer/Text.visible = true
		$Head/CanvasLayer/Text/Label.text = text
	
	#Make health and ammo labels red when they drop below a certain threshold/ colored when status effects
	if health <= 15:
		$Head/CanvasLayer/HUD/Health.add_color_override("font_color",Color.red)
	else:
		$Head/CanvasLayer/HUD/Health.add_color_override("font_color",Color.white)
		
	if has_node("Poison"):
		$Head/CanvasLayer/HUD/Health.add_color_override("font_color",Color.darkolivegreen)
		$Head/CanvasLayer/DeathOverlay.color = Color.darkolivegreen
	else:
		$Head/CanvasLayer/DeathOverlay.color = Color.red
		
	$Head/CanvasLayer/DeathOverlay.color.a = 0.25
	
	if ammo <= 10:
		$Head/CanvasLayer/HUD/Ammo.add_color_override("font_color",Color.red)
	else:
		$Head/CanvasLayer/HUD/Ammo.add_color_override("font_color",Color.white)
	
	#Update bean counter
	if globalvar.number_beans > 0:
		$Head/CanvasLayer/TopRight/BeanIndicator.visible = true
		$Head/CanvasLayer/TopRight/BeanIndicator/bean_counter.text = str(globalvar.number_beans)
	
	
	#Update progress indicator
	if globalvar.doorstop_collected:
		$Head/CanvasLayer/TopRight/Progress/doorstop.modulate = Color.white


func set_camera_bob(maximum=0,multiplier=1):
	cam_bob_max = maximum/1.5
	cam_bob_multiplier = multiplier


func _on_ShootCooldown_timeout():
	canshoot = true

func _on_SlapCooldown_timeout():
	canslap = true

#Reset slapping
func _on_Slapping_timeout():
	slapping = false
	$SlapCooldown.start(0.4)
	$Head/Slap/CollisionShape.disabled = true
	$Head/CanvasLayer/Hand.rect_rotation = 0

func _on_YeetTimer_timeout():
	yeet = false


#Make anything that is slapped take damage
func _on_Slap_body_entered(body):
	if body != null:
		if !body.is_in_group("player"):
			if body.has_method("take_damage"):
				body.take_damage(12,ext.DAMAGE_TYPES.MELEE,get_parent().get_parent())


#Throw the player with excessive force
func yeet(time=0.3,strength_v=0.8,strength_h=150):
	yeet_strength_h = strength_h
	yeet_strength_v = strength_v
	yeet = true
	$YeetTimer.start(time)



func _on_DeathTimer_timeout():
	get_tree().reload_current_scene()


func _on_IFrameTimer_timeout():
	iframes = false
	

func poison(time = 20, amount = 3, frequency = 0.8):
	if !has_node("Poison"):
		var newpoison = damagearea.instance()
		newpoison.name = "Poison"
		newpoison.time = time
		newpoison.damage = amount
		newpoison.frequency = frequency
		add_child(newpoison)
	
	
func cutscene_mode(cutscene = true, show_self = false):
	print("bruh")
	is_cutscene = cutscene
	if is_cutscene:
		hide_hud(true)		
	elif !is_cutscene:
		hide_hud(false)
		
	$MeshInstance.visible = show_self
		
		
func hide_hud(hide = true):
	if hide:
		for child in $Head/CanvasLayer.get_children():
			child.visible = false
			
	if !hide:
		for child in $Head/CanvasLayer.get_children():
			child.visible = true
			$Head/CanvasLayer/DeathOverlay.visible = false
			$Head/CanvasLayer/Prompt.visible = false
			$Head/CanvasLayer/Text.visible = false
	
func die():
	take_damage(health+10)
	
	
