/****************************************************************************
** Copyright (C) 2011 Luka Horvat <redreaper132 at gmail.com>
** Copyright (C) 2011 Edward Lii <edward_iii at myway.com>
** Copyright (C) 2011 O. Bahri Gordebak <gordebak at gmail.com>
**
**
** This file may be used under the terms of the GNU General Public
** License version 3.0 as published by the Free Software Foundation
** and appearing in the file LICENSE.GPL included in the packaging of
** this file.
**
** This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE
** WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
**
**  You should have received a copy of the GNU General Public License
**  along with this program.  If not, see <http://www.gnu.org/licenses/>.
**
****************************************************************************/
#include "Player.h"
#include "Game.h"
#include "Functions.h"
#include "FileManager.h"
#include "Globals.h"
#include "Objects.h"
#include <iostream>
#include <SDL/SDL_mixer.h>
#include <SDL/SDL.h>
#include <SDL/SDL_ttf.h>
using namespace std;

Player::Player(Game* objParent):xVelBase(0),yVelBase(0),objParent(objParent){
	//Set the dimensions of the player.
	//The size of the player is 21x40.
	box.x=0;
	box.y=0;
	box.w=21;
	box.h=40;

	//Set his velocity to zero.
	xVel=0;
	yVel=0;

	//Set the start position.
	fx=0;
	fy=0;

	//Check if sound is enabled.
	if(getSettings()->getBoolValue("sound")==true){
		//It is so load the sounds.
		jumpSound=Mix_LoadWAV((getDataPath()+"sfx/jump.wav").c_str());
		hitSound=Mix_LoadWAV((getDataPath()+"sfx/hit.wav").c_str());
		saveSound=Mix_LoadWAV((getDataPath()+"sfx/checkpoint.wav").c_str());
		swapSound=Mix_LoadWAV((getDataPath()+"sfx/swap.wav").c_str());
		toggleSound=Mix_LoadWAV((getDataPath()+"sfx/toggle.wav").c_str());
		errorSound=Mix_LoadWAV((getDataPath()+"sfx/error.wav").c_str());
	}
	
	//Set some default values.
	inAir=true;
	isJump=false;
	onGround=true;
	shadowCall=false;
	shadow=false;
	canMove=true;
	holdingOther=false;
	dead=false;
	record=false;

	//Some default values for animation variables.
	direction=0;
	jumpTime=0;

	state=0;

	//xVelSaved is used to store if there's a state saved or not.
	xVelSaved=0x80000000;
}

Player::~Player(){
	//We only need to clean up the sounds if they were loaded.
	if(getSettings()->getBoolValue("sound")==true){
		Mix_FreeChunk(jumpSound);
		Mix_FreeChunk(hitSound);
		Mix_FreeChunk(saveSound);
		Mix_FreeChunk(swapSound);
		Mix_FreeChunk(toggleSound);
	}
}

void Player::handleInput(class Shadow* shadow){
	//Get the current keystate.
	Uint8* keyState=SDL_GetKeyState(NULL);
	
	//Reset horizontal velocity.
	xVel=0;
	if(keyState[SDLK_RIGHT]){
		//Walking to the right.
		xVel+=7;
	}
	if(keyState[SDLK_LEFT]){
		//Walking to the left.
		xVel-=7;
	}
	
	//Check if a key has been released.
	if(event.type==SDL_KEYUP){
		//It has so downKeyPressed can't be true.
		downKeyPressed=false;
	}

	//Check if a key is pressed (down).
	if(event.type==SDL_KEYDOWN){
		//Switch which key is pressed.
		switch(event.key.keysym.sym){
			case SDLK_UP:
				//The up key, if we aren't in the air we start jumping.
				if(inAir==false){
					isJump=true;
				}
				break;
			case SDLK_SPACE:
				//Start recording or stop, depending on the recording state.
				if(record==false){
					//We start recording.
					if(shadow->called==true){
						//The shadow is still busy so first stop him before we can start recording.
						shadowCall=false;
						shadow->called=false;
						shadow->playerButton.clear();
					}else if(!dead){
						//The shadow isn't moving and we aren't dead so start recording.
						record=true;
					}
				}else{
					//The player is recording so stop recording and call the shadow.
					record=false;
					shadowCall=true;
				}
				break;				
			case SDLK_DOWN:
				//Downkey is pressed.
				downKeyPressed=true; 
				break;
			case SDLK_F2:
				//F2 only works in the level editor.
				if(!(dead || shadow->dead) && stateID==STATE_LEVEL_EDITOR){
					//Save the state.
					if(objParent)
						objParent->saveState();
				}
				break;
			case SDLK_F3:
				//F3 is used to load the last state.
				if(objParent)
					 objParent->loadState();
				break;
			case SDLK_F4:
				//F4 will swap the player and the shadow, but only in the level editor.
				if(!(dead || shadow->dead) && stateID==STATE_LEVEL_EDITOR){
					swapState(shadow);
				}
				break;
			case SDLK_F12:
				//F12 is suicide and only works in the leveleditor.
				if(stateID==STATE_LEVEL_EDITOR){
					die();
					shadow->die();
				}
			default:
				break;
		}
	}

}

void Player::setPosition(int x,int y){
	box.x=x;
	box.y=y;
}

void Player::move(vector<GameObject*> &levelObjects){
	//Pointer to a checkpoint.
	GameObject* objCheckPoint=NULL;
	//Pointer to a swap.
	GameObject* objSwap=NULL;
	
	//Set the objShadowBlock to NULL.
	//Only for swapping to prevent the shadow from swapping in a shadow block.
	objShadowBlock=NULL;
	
	//Boolean if the player can teleport.
	bool canTeleport=true;
	
	//Set the object the player is currently standing to NULL.
	objCurrentStand=NULL;

	//Check if the player is still alive.
	if(dead==false){
		//Add gravity
		if(inAir==true){
			yVel+=1;
			
			//Cap fall speed to 13.
			if(yVel>13){
				yVel=13;
			}
		}

		//Check if the player can move.
		if(canMove==true){
			//Check if the player is moving or not.
			if(xVel>0){ 
				direction=0;
				onGround=false;
				if(appearance.currentStateName!="walkright"){
					appearance.changeState("walkright");
				}
			}else if(xVel<0){ 
				direction=1;
				onGround=false;
				if(appearance.currentStateName!="walkleft"){
					appearance.changeState("walkleft");
				}
			}else if(xVel==0){
				onGround=true;
				if(direction==1){
					appearance.changeState("standleft");
				}else{
					appearance.changeState("standright");
				}
			}
			//Move the player.
			box.x+=xVel;
			
			//Loop through the levelobjects.
			for(unsigned int o=0; o<levelObjects.size(); o++){
				//Check if the player can walk on the object.
				if(levelObjects[o]->queryProperties(GameObjectProperty_PlayerCanWalkOn,this)){
					//Get the collision box of the levelobject.
					SDL_Rect r=levelObjects[o]->getBox();
					
					//Check collision with the player.
					if(checkCollision(box,r)){
						//We have collision, get the velocity of the box.
						SDL_Rect v=levelObjects[o]->getBox(BoxType_Delta);
						
						//Check on which side of the box the player is.
						if(box.x + box.w/2 <= r.x + r.w/2){
							if(xVel+xVelBase>v.x){
								if(box.x>r.x-box.w)
									box.x=r.x-box.w;	
							}
						}else{
							if(xVel+xVelBase<v.x){
								if(box.x<r.x+r.w)
									box.x=r.x+r.w;
							}
						}
					}
				}
			}
		}
		
		//Now apply the yVel. (gravity, jumping, etc..)
		box.y+=yVel;
		
		//Pointer to the object the player standed on.
		GameObject* lastStand=NULL;
		
		//???
		inAir=true;
		canMove=true;
		
		//Loop through all the levelObjects.
		for(unsigned int o=0; o<levelObjects.size(); o++){
			//Check if the object is solid.
			if(levelObjects[o]->queryProperties(GameObjectProperty_PlayerCanWalkOn,this)){
				SDL_Rect r=levelObjects[o]->getBox();
				if(checkCollision(r,box)==true){ //TODO:fix some bug
					SDL_Rect v=levelObjects[o]->getBox(BoxType_Delta);
					
					if(box.y+box.h/2<=r.y+r.h/2){
						if(yVel>=v.y || yVel>=0){
							inAir=false;
							box.y=r.y-box.h;
							yVel=1; //???
							lastStand=levelObjects[o];
							lastStand->onEvent(GameObjectEvent_PlayerIsOn);
						}
					}else{
						if(yVel<=v.y+1){ 
							yVel=v.y>0?v.y:0; 
							if(box.y<r.y+r.h)
								box.y=r.y+r.h;
						}
					}
				}
			}
			
			//Check if the object is a checkpoint.
			if(levelObjects[o]->type==TYPE_CHECKPOINT && checkCollision(box,levelObjects[o]->getBox())){
				//If we're not the shadow set the gameTip to Checkpoint.
				if(!shadow && objParent!=NULL)
					objParent->gameTipIndex=TYPE_CHECKPOINT;
				
				//And let objCheckPoint point to this object.
				objCheckPoint=levelObjects[o];
			}

			//Check if the object is a swap.
			if(levelObjects[o]->type==TYPE_SWAP && checkCollision(box,levelObjects[o]->getBox())){
				//If we're not the shadow set the gameTip to swap.
				if(!shadow && objParent!=NULL)
					objParent->gameTipIndex=TYPE_SWAP;
				
				//And let objSwap point to this object.
				objSwap=levelObjects[o];
			}
			
			//Check if the object is an exit.
			//This doesn't work if the state is Level editor.
			if(levelObjects[o]->type==TYPE_EXIT && stateID!=STATE_LEVEL_EDITOR && checkCollision(box,levelObjects[o]->getBox())){
				//Goto the next level.
				levels.nextLevel();
				
				//Check if the level exists.
				if(levels.getLevel()<levels.getLevelCount()){
					//It does so unlock the levels.
					levels.setLocked(levels.getLevel());
					//And enter the GameState to start the new level.
					setNextState(STATE_GAME);
				}else{
					//Show the congratulations messagebox.
					if(!levels.congratulationText.empty()){
						msgBox(levels.congratulationText,MsgBoxOKOnly,"Congratulations");
					}else{
						msgBox("You have finished the levelpack!",MsgBoxOKOnly,"Congratulations");
					}
					
					//Go to the level select menu.
					setNextState(STATE_LEVEL_SELECT);
				}
			}

			//Check if the object is a portal.
			if(levelObjects[o]->type==TYPE_PORTAL && checkCollision(box,levelObjects[o]->getBox())){
				//Check if the teleport id isn't empty.
				if((dynamic_cast<Block*>(levelObjects[o]))->id.empty()){
					cerr<<"Warning: Invalid teleport id!"<<endl;
					canTeleport=false;
				}
				
				//If we're not the shadow set the gameTip to portal.
				if(!shadow && objParent!=NULL)
					objParent->gameTipIndex=TYPE_PORTAL;
				
				//Check if we can teleport and should (downkey -or- auto).
				if(canTeleport && (downKeyPressed || (levelObjects[o]->queryProperties(GameObjectProperty_Flags,this)&1))){
					canTeleport=false;
					if(downKeyPressed || levelObjects[o]!=objLastTeleport){
						downKeyPressed=false;
						
						//Loop the levelobjects again to find the destination.
						for(unsigned int oo=o+1;;){
							//We started at our index+1.
							//Meaning that if we reach the end of the vector then we need to start at the beginning.
							if(oo>=levelObjects.size())
								oo-=(int)levelObjects.size();
							//It also means that if we reach the same index we need to stop.
							//If the for loop breaks this way then we have no succes.
							if(oo==o){
								//Couldn't teleport so play the error sound.
								if(getSettings()->getBoolValue("sound")){
									Mix_PlayChannel(-1,errorSound,0);
								}
								break;
							}
							
							//Check if the second (oo) object is a portal.
							if(levelObjects[oo]->type==TYPE_PORTAL){
								//Check the id against the destination of the first portal.
								if((dynamic_cast<Block*>(levelObjects[o]))->destination==(dynamic_cast<Block*>(levelObjects[oo]))->id){
									//Call the event.
									levelObjects[o]->onEvent(GameObjectEvent_OnToggle);
									objLastTeleport=levelObjects[oo];
									
									//Get the destination location and teleport the player.
									SDL_Rect r=levelObjects[oo]->getBox();
									box.x=r.x+5;
									box.y=r.y+2;
									
									//Check if music/sound is enabled.
									if(getSettings()->getBoolValue("sound")){
										Mix_PlayChannel(-1,swapSound,0);
									}
									break;
								}
							}
							
							//Increase oo.
							oo++;
						}
					}
				}
			}
			
			//Check if the object is a switch.
			if(levelObjects[o]->type==TYPE_SWITCH && checkCollision(box,levelObjects[o]->getBox())){
				//If we're not the shadow set the gameTip to switch.
				if(!shadow && objParent!=NULL)
					objParent->gameTipIndex=TYPE_SWITCH;
				
				//If the down key is pressed then invoke an event.
				if(downKeyPressed){
					//Play the animation.
					levelObjects[o]->playAnimation(1);
					
					//Check if sound is enabled, if so play the toggle sound.
					if(getSettings()->getBoolValue("sound")==true){
						Mix_PlayChannel(-1,toggleSound,0);
					}
					
					if(objParent!=NULL){
						//Make sure that the id isn't emtpy.
						if(!(dynamic_cast<Block*>(levelObjects[o]))->id.empty()){
							objParent->broadcastObjectEvent(0x10000 | (levelObjects[o]->queryProperties(GameObjectProperty_Flags,this)&3),
								-1,(dynamic_cast<Block*>(levelObjects[o]))->id.c_str());
						}else{
							cerr<<"Warning: invalid switch id!"<<endl;
						}
					}
				}
			}
			
			//Check if the object is a notification block.
			if(levelObjects[o]->type==TYPE_NOTIFICATION_BLOCK && checkCollision(box,levelObjects[o]->getBox())){
				//If we're not the shadow set the gameTip to notification block.
				if(!shadow && objParent!=NULL)
					objParent->gameTipIndex=TYPE_NOTIFICATION_BLOCK;
				
				//If the down key is pressed then invoke an event.
				if(downKeyPressed==true)
					(dynamic_cast<Block*>(levelObjects[o]))->onEvent(GameObjectEvent_OnSwitchOn);
			}
			
			//Check if the object is a shadow block, only if we are the playre.
			if((levelObjects[o]->type==TYPE_SHADOW_BLOCK || levelObjects[o]->type==TYPE_MOVING_SHADOW_BLOCK) && checkCollision(box,levelObjects[o]->getBox()) && !shadow){
				objShadowBlock=levelObjects[o];
			}

			//Check if the object is deadly.
			if(levelObjects[o]->queryProperties(GameObjectProperty_IsSpikes,this)){
				//It is so get the collision box.
				SDL_Rect r=levelObjects[o]->getBox();
				
				//TODO: pixel-accuracy hit test.
				//For now we shrink the box.
				r.x+=2;
				r.y+=2;
				r.w-=4;
				r.h-=4;
				
				//Check collision, if the player collides then let him die.
				if(checkCollision(box,r))
					die();
			}
		}
		
		//Check if the player fell of the level.
		if(box.y>LEVEL_HEIGHT)
			die();

		//Check if the player changed blocks, meaning stepped onto a block.
		objCurrentStand=lastStand;
		if(lastStand!=objLastStand){
			objLastStand=lastStand;
			if(lastStand){
				//Call the walk on event of the laststand.
				lastStand->onEvent(GameObjectEvent_PlayerWalkOn);
				
				//Bugfix for Fragile blocks.
				if(lastStand->type==TYPE_FRAGILE && !lastStand->queryProperties(GameObjectProperty_PlayerCanWalkOn,this)){
					inAir=true;
					onGround=false;
					isJump=false;
				}
			}
		}
		
		//Check if the player can teleport.
		if(canTeleport)
			objLastTeleport=NULL;

		//Check the checkpoint pointer only if the downkey is pressed.
		if(objParent!=NULL && downKeyPressed && objCheckPoint!=NULL){
			//Checkpoint thus save the state.
			if(objParent->saveState())
				objParent->objLastCheckPoint=objCheckPoint;
		}
		//Check the swap pointer only if the down key is pressed.
		if(objSwap!=NULL && downKeyPressed && objParent!=NULL){
			//Now check if the shadow we're the shadow or not.
			if(shadow){
				if(!(dead || objParent->player.dead)){
					//Check if the player isn't in front of a shadow block.
					if(!objParent->player.objShadowBlock){
						objParent->player.swapState(this);
						objSwap->playAnimation(1);
					}else{
						//We can't swap so play the error sound.
						if(getSettings()->getBoolValue("sound")==true){
							Mix_PlayChannel(-1,errorSound,0);
						}
					}
				}
			}else{
				if(!(dead || objParent->shadow.dead)){
					//Check if the player isn't in front of a shadow block.
					if(!objShadowBlock){
						swapState(&objParent->shadow);
						objSwap->playAnimation(1);
					}else{
						//We can't swap so play the error sound.
						if(getSettings()->getBoolValue("sound")==true){
							Mix_PlayChannel(-1,errorSound,0);
						}
					}
				}
			}
		}
		
		//Check for jump appearance (inAir).
		if(inAir && !dead){
			if(direction==1){
				if(yVel>0){
					if(appearance.currentStateName!="fallleft")
						appearance.changeState("fallleft");
				}else{
					if(appearance.currentStateName!="jumpleft")
						appearance.changeState("jumpleft");
				}
			}else{
				if(yVel>0){
					if(appearance.currentStateName!="fallright")
						appearance.changeState("fallright");
				}else{
					if(appearance.currentStateName!="jumpright")
						appearance.changeState("jumpright");
				}
			}
		}
	}
	
	//Finally we reset some stuff.
	downKeyPressed=false;
	xVelBase=0;
	yVelBase=0;
}


void Player::jump(){
	//Check if the player is dead or not.
	if(dead==true){
		//The player can't jump if he's dead.
		isJump=false;
	}
	
	//Check if the player can jump.
	if(isJump==true && inAir==false){
		//Set the jump velocity.
		yVel=-13;
		inAir=true;
		isJump=false;
		jumpTime++;
		
		//Check if sound is enabled, if so play the jump sound.
		if(getSettings()->getBoolValue("sound")==true){
			Mix_PlayChannel(-1,jumpSound,0);
		}
	}
}

void Player::show(){
	//Check if we should render the recorded line.
	//Only do this when we're recording and we're not the shadow.
	if(shadow==false && record==true){
		//FIXME: Adding an entry not in update but in render?
		line.push_back(SDL_Rect());
		line[line.size()-1].x=box.x+11;
		line[line.size()-1].y=box.y+20;
		
		//Loop through the line dots and draw them.
		for(int l=0; l<(signed)line.size(); l++){
			appearance.drawState("line",screen,line[l].x-camera.x,line[l].y-camera.y,NULL);
		}
	}
	
	//NOTE: We do logic here, because it's only needed by the appearance.
	appearance.updateAnimation();
	appearance.draw(screen, box.x-camera.x, box.y-camera.y, NULL);
}

void Player::shadowSetState(){
	//Only add an entry if the player is recording.
	if(record){
		int currentKey=0;

		//Check for xvelocity.
		if(xVel>0)
			currentKey|=PlayerButtonRight;
		if(xVel<0)
			currentKey|=PlayerButtonLeft;
		
		//Check for jumping.
		if(isJump)
			currentKey|=PlayerButtonJump;
		
		//Check if the downbutton is pressed.
		if(downKeyPressed)
			currentKey|=PlayerButtonDown;
		
		//Add the action.
		playerButton.push_back(currentKey);
		
		//Change the state.
		state++;
	}
}

void Player::shadowGiveState(Shadow* shadow){
	//Check if the player calls the shadow.
	if(shadowCall==true){
		//Clear any recording still with the shadow.
		shadow->playerButton.clear();

		//Loop the recorded moves and add them to the one of the shadow.
		for(unsigned int s=0;s<playerButton.size();s++){
			shadow->playerButton.push_back(playerButton[s]);
		}

		//Reset the state of both the player and the shadow.
		stateReset();
		shadow->stateReset();

		//Clear the recording at the player's side.
		playerButton.clear();
		line.clear();

		//Set shadowCall false
		shadowCall=false;
		//And let the shadow know that the player called him.
		shadow->meCall();
	}
}

void Player::stateReset(){
	//Reset the state by setting it to 0.
	state=0;
}

void Player::otherCheck(class Player* other){
	//Check if the player is standing on the shadow, or vice versa.
	//First make sure the player isn't dead.
	if(!dead){
		if(objCurrentStand!=NULL){
			//Now get the velocity of the object the player is standing.
			SDL_Rect v=objCurrentStand->getBox(BoxType_Velocity);
			
			//Set the base velocity to the velocity of the object.
			xVelBase=v.x;
			yVelBase=v.y;
			
			//Already move the player box.
			box.x+=v.x;
			box.y+=v.y;
		}

		//Make sure the other isn't dead.
		if(!other->dead){
			//Get the box of the shadow.
			SDL_Rect boxShadow=other->getBox();
			
			//Check collision between the shadow and the player.
			if(checkCollision(box,boxShadow)==true){
				//We have collision now check if the other is standing on top of you.
				if(box.y+box.h<=boxShadow.y+13 && !other->inAir){
					int yVelocity=yVel-1;
					if(yVelocity>0){
						box.y-=yVel;
						box.y+=boxShadow.y-(box.y+box.h);
						inAir=false;
						canMove=false;
						onGround=true;
						other->holdingOther=true;
						other->appearance.changeState("holding");
						
						//Change our own appearance to standing.
						if(direction==1){
							appearance.changeState("standleft");
						}else{
							appearance.changeState("standright");
						}
					}
				}
			}else{
				other->holdingOther=false;
			}
		}
	}
	objCurrentStand=NULL;
}

SDL_Rect Player::getBox(){
	return box;
}

void Player::setMyCamera(){
	//Only change the camera when the player isn't dead.
	if(dead)
		return;
	
	//Check if the player is halfway pass the halfright of the screen.
	if(box.x>camera.x+450){
		//It is so ease the camera to the right.
		camera.x+=(box.x-camera.x-400)>>4;
		
		//Check if the camera isn't going too far.
		if(box.x<camera.x+450){
			camera.x=box.x-450;
		}
	}

	//Check if the player is halfway pass the halfleft of the screen.
	if(box.x<camera.x+350){
		//It is so ease the camera to the left.
		camera.x+=(box.x-camera.x-400)>>4;
		
		//Check if the camera isn't going too far.
		if(box.x>camera.x+350){
			camera.x=box.x-350;
		}
	}

	//If the camera is too far to the left we set it to 0.
	if(camera.x<0){
		camera.x=0;
	}
	//If the camera is too far to the right we set it to the max right.
	if(camera.x+camera.w>LEVEL_WIDTH){
		camera.x=LEVEL_WIDTH-camera.w;
	}

	//Check if the player is halfway pass the lower half of the screen.
	if(box.y>camera.y+350){
		//Ir is so ease the camera down.
		camera.y+=(box.y-camera.y-300)>>4;
		
		//Check if the camera isn't going too far.
		if(box.y<camera.y+350){
			camera.y=box.y-350;
		}
	}

	//Check if the player is halfway pass the upper half of the screen.
	if(box.y<camera.y+250){
		//It is so ease the camera up.
		camera.y+=(box.y-camera.y-300)>>4;
		
		//Check if the camera isn't going too far.
		if(box.y>camera.y+250){
			camera.y=box.y-250;
		}
	}

	//If the camera is too far up we set it to 0.
	if(camera.y<0){
		camera.y=0;
	}
	//If the camera is too far down we set it to the max down.
	if(camera.y+camera.h>LEVEL_HEIGHT){
		camera.y=LEVEL_HEIGHT-camera.h;
	}
}

void Player::reset(bool save){
	//Set the location of the player to it's initial state.
	box.x=fx;
	box.y=fy;

	//Reset back to default value.
	inAir=true;
	isJump=false;
	onGround=true;
	shadowCall=false;
	canMove=true;
	holdingOther=false;
	dead=false;
	record=false;

	//Some animation variables.
	appearance.resetAnimation(save);
	appearance.changeState("standright");
	direction=0;

	state=0;
	yVel=0;

	objCurrentStand=NULL;

	//Clear the recording.
	line.clear();
	playerButton.clear();

	//xVelSaved is used to indicate if there's a state saved or not.
	if(save){
		xVelSaved=0x80000000;
	}
}

void Player::saveState(){
	//We can only save the state when the player isn't dead.
	if(!dead){
		boxSaved.x=box.x;
		boxSaved.y=box.y;
		xVelSaved=xVel;
		yVelSaved=yVel;
		inAirSaved=inAir;
		isJumpSaved=isJump;
		onGroundSaved=onGround;
		canMoveSaved=canMove;
		holdingOtherSaved=holdingOther;
		
		//Let the appearance save.
		appearance.saveAnimation();
		
		//Only play the sound when it's enabled.
		if(getSettings()->getBoolValue("sound")==true){
			//To prevent playing the sound twice, only the player can cause the sound.
			if(!shadow)
				Mix_PlayChannel(-1,saveSound,0);
		}
	}
}

void Player::loadState(){
	//Check with xVelSaved if there's a saved state.
	if(xVelSaved==int(0x80000000)){
		//There isn't so reset the game to load the first initial state.
		//NOTE: There's no need in removing the saved state since there is none.
		reset(false);
		return;
	}
	
	//Restore the saved values.
	box.x=boxSaved.x;
	box.y=boxSaved.y;
	//xVel is set to 0 since it's saved counterpart is used to indicate a saved state.
	xVel=0;
	yVel=yVelSaved;
	
	//Restore teh saved values.
	inAir=inAirSaved;
	isJump=isJumpSaved;
	onGround=onGroundSaved;
	canMove=canMoveSaved;
	holdingOther=holdingOtherSaved;
	dead=false;
	record=false;
	shadowCall=false;
	stateReset();
	
	//Restore the appearance.
	appearance.loadAnimation();
	
	//Clear any recorded stuff.
	line.clear();
	playerButton.clear();
}

void Player::swapState(Player* other){
	//We need to swap the values of the player with the ones of the given player.
	swap(box.x,other->box.x);
	swap(box.y,other->box.y);
	//NOTE: xVel isn't there since it's used for something else.
	swap(yVel,other->yVel);
	swap(inAir,other->inAir);
	swap(isJump,other->isJump);
	swap(onGround,other->onGround);
	swap(canMove,other->canMove);
	swap(holdingOther,other->holdingOther);
	swap(dead,other->dead);
	
	//Also reset the state of the other.
	other->stateReset();
	
	//Play the swap sound.
	if(getSettings()->getBoolValue("sound")==true){
		Mix_PlayChannel(-1,swapSound,0);
	}
}

bool Player::canSaveState(){
	//We can only save the state if the player isn't dead.
	return !dead;
}

bool Player::canLoadState(){
	//We use xVelSaved to indicate if a state is saved or not.
	return xVelSaved != int(0x80000000);
}

void Player::die(){
	//Make sure the player isn't already dead.
	if(!dead){
		dead=true;
		//If sound is enabled run the hit sound.
		if(getSettings()->getBoolValue("sound")==true){
			Mix_PlayChannel(-1,hitSound,0);
		}
		
		//Change the apearance to die.
		appearance.changeState("die");
	}
}
