function player_move_and_collide()
{
	if global.hitstop > 0
		return;
		
	y_spd *= global.time_scale;
	x_spd *= global.time_scale;
	
    if place_meeting(x, y + y_spd, oParentSolid)
	{
        while !place_meeting(x, y + sign(y_spd), oParentSolid)
			y += sign(y_spd);
        y_spd = 0; 
    }
    y += y_spd;
	
	if place_meeting(x + x_spd, y, oParentSolid)
	{
		var _max_step = (y_spd == 0) ? 16 : 8; //前者地面容错，后者空中容错
        var _stepped = false;

        for(var i = 0; i < _max_step; i++)
            if !place_meeting(x + x_spd, y - i, oParentSolid)
			{
                y -= i;
                _stepped = true;
                break;
            }
		if !_stepped
		{
            while !place_meeting(x + sign(x_spd), y, oParentSolid)
                x += sign(x_spd);
            x_spd = 0;
        }
	}
    x += x_spd;
	
	y_spd /= global.time_scale;
	x_spd /= global.time_scale;
}
function player_status_update()
{
	global.playerHP = clamp(global.playerHP, 0, global.save_data.player.maxHP);
	global.playerINK = clamp(global.playerINK, 0, global.save_data.player.maxINK);
	
	current_attacker = instance_place(x, y, oEnemyHitbox);
	current_hazard = instance_place(x, y, oParentHazard);
	current_door = instance_place(x, y, oDoor);
	
	if state != state_attack && instance_exists(current_hb)
		instance_destroy(current_hb);
	
	if !instance_exists(current_hb)
		current_hb = noone;
	
	if state != state_focus
		INK_blend = 0;

	image_index += animation_spd * global.time_scale;
	
	if oMain._dash
		dash_buffer = dash_buffer_max;
		
	if oMain._attack
		attack_buffer = attack_buffer_max;
		
	if oMain._jump_p
		jump_buffer_timer = jump_buffer_max;

	_move_dir = oMain._right - oMain._left;

	_on_ground = place_meeting(x, y + 1, oParentSolid)
	
	_on_wall = place_meeting(x + 1, y, oBlockClimbable)
			- place_meeting(x - 1, y, oBlockClimbable);
			
	var _no_cliff = place_meeting(x - sprite_width, y + 1, oParentSolid)
	&& place_meeting(x + sprite_width, y + 1, oParentSolid)
	
	var _no_hazard = !(place_meeting(x - sprite_width, y, oParentHazard)
	|| place_meeting(x + sprite_width, y, oParentHazard))
			
	if _on_ground && _no_cliff && _no_hazard
	{
		last_safe_x = x;
		last_safe_y = y;
	}

	if _on_ground || (_on_wall != 0)
		can_dash = true;

	if _on_ground
	{
		jump_cnt = 0;
		coyote_timer = coyote_max;
	}
	else if coyote_timer <= 0 && jump_cnt == 0
		jump_cnt = 1;
		
	if dash_cooldown > 0
		dash_cooldown -= global.time_scale;

	if dash_buffer > 0
		dash_buffer -= global.time_scale;
	
	if attack_buffer > 0
		attack_buffer -= global.time_scale;
	
	if move_lock_timer > 0
		move_lock_timer -= global.time_scale;
	
	if coyote_timer > 0
		coyote_timer -= global.time_scale;
		
	if invincible_timer > 0
		invincible_timer -= global.time_scale;
		
	if enter_room_timer > 0
		enter_room_timer -= global.time_scale;
	
	if jump_buffer_timer > 0
		jump_buffer_timer -= global.time_scale;
		
	if flash_timer > 0
		flash_timer -= global.time_scale;
}

function player_check_dash()
{
	if dash_buffer > 0 && dash_cooldown <= 0 && can_dash
	{   
		dash_buffer = 0;
        state = state_dash;
		set_sprite(sPlayerDash);
        dash_cooldown = 36;
        x_spd = image_xscale * walk_spd * 3;
        y_spd = 0;
		can_dash = false;
    }
}
function player_check_movement()
{
    if move_lock_timer <= 0
        x_spd = _move_dir * walk_spd;
}
function player_check_jump()
{
	if jump_buffer_timer > 0
	{
		if coyote_timer > 0
		{
			y_spd = jump_spd;
			jump_cnt = 1;
			jump_buffer_timer = 0; 
			coyote_timer = 0;
		}
		else if _on_wall != 0
		{
			y_spd = jump_spd;
			x_spd = -_on_wall * walk_spd * 1.5;
			jump_cnt = 1;
		    move_lock_timer = 10;
		    jump_buffer_timer = 0;
		}
		else if jump_cnt < jump_max
		{
			icl(oDoubleJumpEffect);
            y_spd = jump_spd * 0.9;
            jump_cnt++;
            jump_buffer_timer = 0;
		}
	}
	if oMain._jump_r && y_spd < 0
		y_spd *= 0.4;
}
function player_check_attack()
{
	if attack_buffer > 0
	{
        attack_buffer = 0;
        state = state_attack;
		
		set_sprite(sPlayerAttack);
    }
}
function player_check_dodge()
{
	if (oMain._down || oMain._up) && _on_ground
	&& (oMain._dash || state == state_dash)
	{
		state = state_dodge;
		set_sprite(sPlayerDodgeWait);
		
		dodge_phase = "WAIT";
		dodge_timer = 0;
		marked_target = noone;
		is_marked = false;
		is_perfect = false;
		
		var _hb = icl(oPlayerHitboxMark, x + facing * 60, y + 40);
		_hb.owner = id;
		_hb.x_offset = 60 * facing;
		_hb.y_offset = 40;
	}
}
function player_check_death()
{
	if state == state_death || state == state_locked
		return;
	
	if global.playerHP <= 0
	{
		/*
		var _drop = round(random_range(0.4 * global.save_data.player.Credit,
		0.6 * global.save_data.player.Credit));
		
		global.save_data.player.Credit -= _drop;
		 
		global.save_data.player.corpse.droppedCredit = _drop;
		global.save_data.player.corpse.targetRoom = room;
		global.save_data.player.corpse.xPos = x; //lastsafe
		global.save_data.player.corpse.yPos = y; //lastsafe
		*/
		
		save_game_to_disk();
		state = state_death;
		set_sprite(sPlayerDeath);
		screen_shake(30);
		
		var _cam = view_camera[0];
		var	_cam_x = camera_get_view_x(_cam);
		var _cam_y = camera_get_view_y(_cam);

		var _ripple = icl(oRippleDeath);
		_ripple.x = (x - _cam_x);
		_ripple.y = (y - _cam_y);
	}
}
function player_check_focus()
{
	if oMain._focus && global.playerINK >= 9
	{
		set_sprite(sPlayerFocus);
		state = state_focus;
		
		repeat(40)
			icl(oFocusingEffect);
	}
}

function player_check_door()
{
	if current_door == noone
	|| state == state_enter_door
	|| state == state_exit_door
		return;

	global.target_door_id = current_door.target_door_id;
	global.target_room = current_door.target_room;
	global.door_direction = current_door.door_direction;
		
	var _fade = icl(oFade);
	_fade._callback = function()
	{
		room_goto(global.target_room);
		camera_snap();
	};
	state = state_enter_door;
	x_spd = 0;
	y_spd = 0;
}
function state_enter_door()
{
	switch(global.door_direction)
	{
		case "UP":
			y_spd += player_calc_gravity();
			set_sprite(sPlayerJump);
			image_index = 1;
			break;
			
		case "DOWN":
			y_spd = min(-3, y_spd);
			set_sprite(sPlayerJump);
			image_index = 0;
			break;
			
		case "LEFT":
			facing = 1;
			x_spd = facing * walk_spd;
			set_sprite(sPlayerWalk);
			break;
		case "RIGHT":
			facing = -1;
			x_spd = facing * walk_spd;
			set_sprite(sPlayerWalk);
			break;
	}
}
function state_exit_door()
{	
	y_spd += 0.5 * player_calc_gravity();
	
	switch(global.door_direction)
	{
		case "LEFT":
			facing = -1;
			x_spd = facing * walk_spd;
			set_sprite(sPlayerWalk);
			break;
		case "RIGHT":
			facing = 1;
			x_spd = facing * walk_spd;
			set_sprite(sPlayerWalk);
			break;
	}
	if enter_room_timer <= 0
	{
		state = state_free;
		global.target_door_id = -1;
		global.target_room = noone;
		global.door_direction = undefined;
	}
}
function player_check_attacked()
{
	if current_attacker == noone || invincible_timer > 0
	|| state == state_death
	//|| (state == state_dodge && dodge_phase == "WAIT")
		return;
		
	if state == state_focus
	{
		global.playerINK -= 9;
		global.hitstop = 24;
		INK_blend = 0;
		flash_duration = 24;
		flash_timer = 24;
		screen_shake(20);
	}
	else
	{
		global.hitstop = 12;
		screen_shake(10);
	}
	
	var _dir = sign(x - current_attacker.x); 
    if _dir == 0
		_dir = facing;
	
	repeat(10)
	{
		var _pe = icl(oPlayerAttackedEffect, x, y - sprite_height / 2);
		_pe.x_spd *= _dir;
	}
	
	global.playerHP -= current_attacker.damage;
	invincible_timer = 80;
	
	x_spd = _dir * 30;
    y_spd = -5;
	
	state = state_hitstun_attacked;
	
	set_sprite(sPlayerHitstunAttacked);
}
function player_check_hazard()
{
	if current_hazard == noone ||
	state == state_hitstun_hazard || state == state_death || state == state_locked
		return;
    
    global.playerHP -= current_hazard.damage;
	
	invincible_timer = 150;
		
	var _dir = sign(x - current_hazard.x); 
	if _dir == 0
	_dir = facing;
		
	global.hitstop = 18;
	screen_shake(10);
	
	x_spd = _dir * 10;
	y_spd = -10;
		
	state = state_hitstun_hazard;
	
	set_sprite(sPlayerHitstunHazard);
}
function state_hitstun_attacked()
{
	x_spd = lerp(x_spd, 0, 0.1);	
	y_spd += player_calc_gravity();
	
	if animation_end()
		state = state_free;
	
	player_check_death();
}
function state_hitstun_hazard()
{
	x_spd = lerp(x_spd, 0, 0.1);
	y_spd += player_calc_gravity();
	
	if animation_end()
	{
		state = state_locked;
		locked_timer = 60;
		var _fade = icl(oFade);
		_fade._callback = function()
		{
			oPlayer.x = oPlayer.last_safe_x;
			oPlayer.y = oPlayer.last_safe_y;
			//camera_snap();
		};
	}
	player_check_death();
}

function state_free()
{
	y_spd += player_calc_gravity();
	
	if _on_wall != 0 && y_spd > 0
		y_spd = min(y_spd, 3);
	
	/*下面是动画与视觉*/
	animation_spd = 0.25;
	
	if _move_dir != 0 //有输入
	{
		facing = _move_dir;
		if _on_ground
			set_sprite(sPlayerWalk); //走路
	}
	else set_sprite(sPlayerIdle); //待机
	
	if !_on_ground
	{	
		if _on_wall == facing
			set_sprite(sPlayerWall);
		else
		{
			set_sprite(sPlayerJump);
			image_index = (y_spd < 0) ? 0 : 1;
		}
	}
	
	/*
	player_check_dodge();
	if state == state_dodge return;
	*/
	player_check_movement();
	player_check_dash();
	player_check_jump();
	player_check_attack();
	player_check_focus();
}
function state_dash()
{
	
    y_spd += 0.2 * player_calc_gravity();
    var _inst = icl(oPlayerAfterImage);
	_inst.image_xscale = facing;
	
    if animation_end()
	{
		if !_on_ground
			jump_cnt = max(jump_cnt, 1);
			
		state = state_free;
        player_check_attack();
    }
	
	//player_check_dodge();
	player_check_focus();
}
function state_attack()
{
	player_check_movement();
	player_check_jump();
	player_check_dash();
	player_check_focus();
	
    y_spd += player_calc_gravity();
	
    if image_index == 1
	{
		if oMain._down && !_on_ground
			current_hb = icl(oPlayerHitboxDown);
		else if oMain._up
		{
			current_hb = icl(oPlayerHitboxUp, x, y - 128);
			current_hb.y_offset = -128;
		}
		else
		{
			current_hb = icl(oPlayerHitboxHor, x, y - 64);
			current_hb.image_xscale = facing;
			current_hb.y_offset = -64;
			
			current_hb.hit_info.kbFactorX *= facing;
		}
		current_hb.owner = id;
	}
	// 攻击后摇结束
	if current_hb != noone
		if _on_ground && current_hb.object_index == oPlayerHitboxDown
			state = state_free;
		
	if facing = -_move_dir
		state = state_free;
	
    if animation_end()
        state = state_free;
}
function state_locked()
{
	locked_timer -= global.time_scale;
	
	x_spd = 0;
	y_spd += player_calc_gravity();
	
	if locked_timer <= 0
		state = state_free;
}
function state_death()
{	
	x_spd = 0;
	y_spd = 0;
	invincible_timer = 150;
	if animation_end()
	{
		
		state = state_locked;
		invincible_timer = 150;
		locked_timer = 90;
		
		var _fade = icl(oFade);
		_fade._callback = function()
		{
			room_goto(asset_get_index(global.save_data.world.current_room));
			global.playerHP = global.save_data.player.maxHP;
			global.playerINK = 0;
		}
	}
}
function state_focus()
{
	x_spd = 0;
	y_spd = 0;
	if animation_end()
	{
		player_add_INK(-9);
		global.playerHP += 3;
		global.playerHP = clamp(global.playerHP, 0, global.save_data.player.maxHP);
		state = state_free;
		
		flash_timer = 15;
		flash_duration = 15;
		
		INK_blend = 0;
		
		repeat(30)
			icl(oFocusEndEffect, x, y - sprite_height);
	}
}

/// @desc 返回基于玩家当前状态的重力值，注意不会自动赋值
function player_calc_gravity()
{
	var _g = global.g;
	
	if y_spd > 0
		_g *= 1.2; // 下坠
	
	if abs(y_spd) < 0.5
		_g *= 0.75; //滞空
	
	return _g; // * global.time_scale;
}

/*
function state_dodge()
{
	if dodge_phase == "WAIT"
	{
		x_spd = 0;

		if current_attacker != noone || animation_end()
		{
			y_spd = jump_spd;
			dodge_phase = "DODGE";
			
			set_sprite(sPlayerDodge);
			
			if current_attacker != noone && is_marked
			{
                is_perfect = true;
				invincible_timer = 150;
				
                global.time_scale = 0.2;
				global.hitstop = 20;
				
            }
		}
	}
	else if dodge_phase == "DODGE"
	{		
        y_spd += global.g;
		
        if is_perfect icl(oPlayerAfterImage);
		
        if y_spd > 0
		{
			timer = 0;
			y_spd = 0;
			if is_perfect
			{
				state = state_arc_slash;
				set_sprite(sPlayerArcSlash);
			}
			else
			{
				state = state_dodge_ending;
				set_sprite(sPlayerDodgeEnding);
			}
		}
	}
}
function state_arc_slash()
{
    if image_index == 12
	{
		var _hb = icd(oPlayerHitboxArc);
        _hb.owner = id;
        
        if facing == 1
		{
            _hb.image_xscale = 1;
			_hb.image_angle = 30;
			_hb.kb_factor_x *= 1;
		}
		else
		{
            _hb.image_xscale = -1;
			_hb.image_angle = -30;
			_hb.kb_factor_x *= -1;
		}	
    }
	else if image_index > 12
		y_spd += global.g;
	if animation_end()
	{
        global.time_scale = 1.0;
        state = state_free;
    }
}
function state_dodge_ending()
{
    if animation_end()
		state = state_free;
}
*/