extends KinematicBody2D

export(bool) var is_bot = true
export(String) var nickname setget set_nickname
export(Color) var skin_color setget set_skin_color
export(Color) var helment_color setget set_helment_color
export(Color) var armor_color setget set_armor_color

signal on_health_change(value)
signal on_kill(who, whom, headshot, weapon_name)
signal on_change_weapon(weapon_name, shot_count)
signal on_shoot(shot_count)

const GRAVITY = 700
const ACC_X = 300
const ACC_Y = 1150
const VEL_X_MAX = 250
const ACC_DROP_X = 1200
const MAX_JUMP_TIME = 0.36
const MAX_HEALTH = 100
const SPAWN_TIME = 3.0
const MIN_SHOT_RATE = 0.4
const MAX_SHOT_RATE = 5
const DEFAULT_WEAPON_NAME = "Knife"

enum {ACTION_IDLE, 
	  ACTION_JUMP_RIGHT, 
	  ACTION_JUMP_LEFT, 
	  ACTION_RIGHT, 
	  ACTION_LEFT, 
	  ACTION_JUMP}

enum {SPAWN_COUNTDOWN, SPAWN_ANIMATION, SPAWN_FINISH}

var id = 0
var velocity = Vector2()
var acceleration = Vector2()

var direction = 0
var is_moving = false
var is_jumping = false

var spawn_state = SPAWN_COUNTDOWN
var spawn_time = 0.0

var jump_time = 0.0
var jump_time_query = 0.0

var weapon_name
var shot_time = 0
var shot_rate = MIN_SHOT_RATE
var shots = 0

var bot_action = 0
var bot_jump_time = 0.0
var bot_action_time = 0
var bot_action_time_limit = 0.1

var health = 0
var deaths = 0
var kills = 0
var kills_in_row = 0
var last_kill_time = 0

var healthbar_time = 0

var has_helment setget set_helment
var has_armor setget set_armor

var target

func reset():
	direction = 0
	is_moving = false
	is_jumping = false
	shot_time = 0
	shot_rate = 0.5
	jump_time = 0
	jump_time_query = 0
	velocity = Vector2()
	acceleration = Vector2()
	last_kill_time = OS.get_ticks_msec()
	kills_in_row = 0
	self.has_armor = false
	self.has_helment = false
	set_weapon(DEFAULT_WEAPON_NAME, 0)
	
func _ready():
	set_spawn_state(SPAWN_COUNTDOWN, 0)

func set_nickname(value):
	nickname = value
	
	if nickname:
		$Nickname.text = nickname

func set_skin_color(value):
	skin_color = value
	
	if skin_color:
		$Sprite.modulate = skin_color	

func set_helment_color(value):
	helment_color = value
	
	if helment_color:
		$Helment.modulate = helment_color

func set_armor_color(value):
	armor_color = value
	
	if armor_color:
		$Armor.modulate = armor_color

func set_style(style):
	set_skin_color(style.skin)
	set_helment_color(style.helment)
	set_armor_color(style.armor)

func set_properties(id, nickname):
	self.id = id
	set_nickname(nickname)
	set_collision_layer_bit(id, true)

func set_spawn_state(state, need_time = 0.0):
	spawn_state = state

	match spawn_state:
		SPAWN_COUNTDOWN:
			spawn_time = need_time
				
		SPAWN_ANIMATION:
			$Anim.play("Spawn")
			
			if !is_bot:
				$Arrow/Anim.play("Arrow")
				
			position = global.get_spawn_point()
			
		SPAWN_FINISH:
			reset()
			add_health(MAX_HEALTH)
			
			if is_bot:
				bot_rand_action()

func set_helment(value):
	has_helment = value
	$Helment.visible = value

func set_armor(value):
	has_armor = value
	$Armor.visible = value

func set_weapon(name, shot_count):
	weapon_name = name
	shots = shot_count
	
	if !is_bot:
		emit_signal("on_change_weapon", name, shot_count)

func add_health(value):
	health += value
	health = clamp(health, 0, MAX_HEALTH)
	
	if !is_bot:
		emit_signal("on_health_change", health)
	
	if health == 0:
		deaths += 1
		visible = false
		$ShapeBody.disabled = true
		$ShapeHead.disabled = true
		$HealthBar.visible = false
		set_spawn_state(SPAWN_COUNTDOWN, SPAWN_TIME)
	else:
		$HealthBar.visible = true
		$HealthBar.value = health
		healthbar_time = 0

func can_move():
	return spawn_state == SPAWN_FINISH

func can_shoot():
	return shot_time >= shot_rate

func bot_set_action(action, time_limit, time_for_jump):
	bot_action = action
	bot_action_time_limit = time_limit
	bot_jump_time = time_for_jump
	bot_action_time = 0

func bot_rand_action():
	bot_set_action(int(rand_range(1 - int(global.easy_bots), 4)), rand_range(1, 4), rand_range(0.1, MAX_JUMP_TIME))

func set_shot_rate(rate):
	shot_time = 0
	shot_rate = rate
		
func move(dir):
	var prev_moving = is_moving
	is_moving = dir != 0
	
	if jump_time == 0:
		if !prev_moving and is_moving:
			$Anim.play("Run")
	
		if dir == 0:
			$Anim.play("Idle")
	
	if direction != dir and dir != 0:
		velocity.x = velocity.x / 3
	
	if dir != 0:
		var flipped = dir < 0
		$Sprite.flip_h = flipped
		$Helment.flip_h = flipped
		$Armor.flip_h = flipped
		direction = dir

func idle():
	move(0)

func move_left():
	move(-1)

func move_right():
	move(1)

func jump(time, force = false):
	if time < jump_time_query:
		return
		
	if !is_jumping:
		$Anim.play("Jump")

	if force:
		jump_time = 0
		velocity.y = 0
		
		if is_bot:
			bot_set_action(ACTION_JUMP, time, time)
	
	is_jumping = true
	jump_time_query = time

func fall():
	$Anim.play("Fall")
	is_jumping = false
	acceleration.y = 0
	jump_time_query = 0

func fire():
	if spawn_state != SPAWN_FINISH or !target or shot_time < shot_rate:
		return
	
	if shots > 0:
		shots -= 1
		
		if !is_bot:
			emit_signal("on_shoot", shots)
			
		if shots == 0:
			set_weapon(DEFAULT_WEAPON_NAME, 0)

	global.add_shot(self, target, weapon_name)
	
	if is_bot:
		set_shot_rate(rand_range(MIN_SHOT_RATE, MAX_SHOT_RATE))
	else:
		set_shot_rate(MIN_SHOT_RATE)

func intersect_ray(character):
	var space_state = get_world_2d().direct_space_state
	var result = space_state.intersect_ray(position, character.position, [self])
    
	return result and result.collider == character 

func youkilled(headshot):
	kills += 1
	
	if !is_bot and headshot:
		global.slowmo.start(0.1, 1.0, 2.0, null, null, null)
	
	var current_time = OS.get_ticks_msec()
	
	if current_time - last_kill_time <= 10000:
		kills_in_row += 1
	else:
		kills_in_row = 1
	
	last_kill_time = current_time

func apply_damage(pos_from, body_shape, killer, value, weapon_name):
	var headshot = body_shape == 1
	var damage_pos = Vector2(position.x, pos_from.y)
	var damage_angle = global.angle_between(position, pos_from)
	
	if has_helment or has_armor:
		global.add_particles("Sparks", damage_pos, 0)
	else:
		global.add_particles("Blood", damage_pos, damage_angle + PI)
	
	if headshot:
		if has_helment:
			self.has_helment = false
		else:
			add_health(-MAX_HEALTH)
	else:
		if has_armor:
			self.has_armor = false
		else:
			add_health(-value)
		
	if health == 0:
		killer.youkilled(headshot)
		global.add_gibs(position, Vector2(10, 10))
		emit_signal("on_kill", killer, self, headshot, weapon_name)

func powerup_health(value):
	if health != MAX_HEALTH:
		add_health(value)
		return true
	
	return false

func powerup_armor(value):
	if has_helment and has_armor:
		return false
	else:
		self.has_helment = true
		self.has_armor = true
		return true

func powerup_axe(value):
	if weapon_name == "Axe" and shots == 3:
		return false
	else:
		set_weapon("Axe", 3)
		return true

func foot_fx(fx_name, only_floor):
	if spawn_state != SPAWN_FINISH or (only_floor and !is_on_floor()):
		return

	global.add_particles(fx_name, position, 0, direction > 0)

func _process(delta):
	match spawn_state:
		SPAWN_COUNTDOWN:
			spawn_time -= delta
		
			if spawn_time <= 0:
				set_spawn_state(SPAWN_ANIMATION)
			
			return
			
		SPAWN_ANIMATION:
			return
		
	shot_time += delta
	
	if $HealthBar.visible:
		healthbar_time += delta

		if healthbar_time >= 3.0:
			$HealthBar.visible = false
	
	if is_bot:
		bot_action_time += delta
		
		match bot_action:
			ACTION_IDLE:
				idle()
				
			ACTION_JUMP_RIGHT: 
				jump(bot_jump_time)
				move_right()
				
			ACTION_JUMP_LEFT: 
				jump(bot_jump_time)
				move_left()
				
			ACTION_RIGHT:
				move_right()
				
			ACTION_LEFT: 
				move_left()
				
			ACTION_JUMP:
				jump(bot_jump_time)
		
		if bot_action_time >= bot_action_time_limit:
			bot_rand_action()
		
func _physics_process(delta):
	if !can_move():
		return
		
	if is_moving:
		acceleration.x = ACC_X * direction
	else:
		var xvel = abs(velocity.x)
		xvel -= ACC_DROP_X * delta
		
		if xvel < 0:
			xvel = 0
		
		velocity.x = xvel * direction
		acceleration.x = 0
	
	if is_jumping:
		jump_time += delta
		
		if jump_time < jump_time_query:
			acceleration.y = -GRAVITY - (ACC_Y * (1 - min(jump_time * 1, 1)))
		else:
			fall()
	else:
		acceleration.y = 0
	
	velocity += (acceleration + Vector2(0, GRAVITY)) * delta
	velocity.x = clamp(velocity.x, -VEL_X_MAX, VEL_X_MAX)
	velocity = move_and_slide(velocity, Vector2(0, -1))
	
	if is_on_ceiling():
		var collider = get_slide_collision(0)

		if collider is KinematicCollision2D:
			bot_rand_action()
			fall()
		
	if is_on_floor():
		if jump_time > 0:
			idle()
			
		jump_time = 0
		jump_time_query = 0
	
	if is_on_wall() and is_bot:
		bot_rand_action()

func _on_Sprite_frame_changed():
	$Helment.frame = $Sprite.frame
	$Helment.offset = $Sprite.offset
	$Armor.frame = $Sprite.frame
	$Armor.offset = $Sprite.offset