Page MenuHomePhabricator (Chris)

No OneTemporary

Authored By
Unknown
Size
53 KB
Referenced Files
None
Subscribers
None
diff --git a/src/Player.cpp b/src/Player.cpp
index 5665507..47bc1fc 100644
--- a/src/Player.cpp
+++ b/src/Player.cpp
@@ -1,1469 +1,1462 @@
/*
* Copyright (C) 2011-2012 Me and My Shadow
*
* This file is part of Me and My Shadow.
*
* Me and My Shadow is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* Me and My Shadow is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with Me and My Shadow. 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 "InputManager.h"
#include "StatisticsManager.h"
#include "MD5.h"
#include <iostream>
#include <fstream>
#include <SDL/SDL.h>
#ifdef __APPLE__
#include <SDL_mixer/SDL_mixer.h>
#include <SDL_ttf/SDL_ttf.h>
#else
#include <SDL/SDL_mixer.h>
#include <SDL/SDL_ttf.h>
#endif
using namespace std;
#ifdef RECORD_FILE_DEBUG
string recordKeyPressLog,recordKeyPressLog_saved;
vector<SDL_Rect> recordPlayerPosition,recordPlayerPosition_saved;
#endif
Player::Player(Game* objParent):xVelBase(0),yVelBase(0),objParent(objParent),recordSaved(false),
inAirSaved(false),isJumpSaved(false),onGroundSaved(false),canMoveSaved(false),holdingOtherSaved(false){
//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;
//Set some default values.
inAir=true;
isJump=false;
onGround=true;
shadowCall=false;
shadow=false;
canMove=true;
holdingOther=false;
dead=false;
record=false;
downKeyPressed=false;
spaceKeyPressed=false;
recordIndex=-1;
#ifdef RECORD_FILE_DEBUG
recordKeyPressLog.clear();
recordKeyPressLog_saved.clear();
recordPlayerPosition.clear();
recordPlayerPosition_saved.clear();
#endif
objNotificationBlock=NULL;
//Some default values for animation variables.
direction=0;
jumpTime=0;
state=stateSaved=0;
//xVelSaved is used to store if there's a state saved or not.
xVelSaved=yVelSaved=0x80000000;
objCurrentStand=objLastStand=objLastTeleport=objShadowBlock=NULL;
}
Player::~Player(){
//Do nothing here
}
bool Player::isPlayFromRecord(){
return recordIndex>=0; // && recordIndex<(int)recordButton.size();
}
//get the game record object.
std::vector<int>* Player::getRecord(){
return &recordButton;
}
#ifdef RECORD_FILE_DEBUG
string& Player::keyPressLog(){
return recordKeyPressLog;
}
vector<SDL_Rect>& Player::playerPosition(){
return recordPlayerPosition;
}
#endif
//play the record.
void Player::playRecord(){
//TODO:
recordIndex=0;
}
void Player::spaceKeyDown(class Shadow* shadow){
//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;
//We start a recording meaning we need to increase recordings by one.
objParent->recordings++;
//Update statistics.
if(!dead && !objParent->player.isPlayFromRecord() && !objParent->interlevel){
statsMgr.recordTimes++;
if(statsMgr.recordTimes==100) statsMgr.newAchievement("record100");
if(statsMgr.recordTimes==1000) statsMgr.newAchievement("record1k");
}
}
}else{
//The player is recording so stop recording and call the shadow.
record=false;
shadowCall=true;
}
}
void Player::handleInput(class Shadow* shadow){
//Check if we should read the input from record file.
//Actually, we read input from record file in
//another function shadowSetState.
bool readFromRecord=false;
if(recordIndex>=0 && recordIndex<(int)recordButton.size()) readFromRecord=true;
if(!readFromRecord){
//Reset horizontal velocity.
xVel=0;
if(inputMgr.isKeyDown(INPUTMGR_RIGHT)){
//Walking to the right.
xVel+=7;
}
if(inputMgr.isKeyDown(INPUTMGR_LEFT)){
//Walking to the left.
if(xVel!=0 && !dead && !objParent->player.isPlayFromRecord() && !objParent->interlevel){
//Horizontal confusion achievement :)
statsMgr.newAchievement("horizontal");
}
xVel-=7;
}
//Check if a key has been released.
if(/*event.type==SDL_KEYUP || */!inputMgr.isKeyDown(INPUTMGR_ACTION)){
//It has so downKeyPressed can't be true.
downKeyPressed=false;
}
/*
//Don't reset spaceKeyPressed or when you press the space key
//and release another key then the bug occurs. (ticket #44)
if(event.type==SDL_KEYUP || !inputMgr.isKeyDown(INPUTMGR_SPACE)){
spaceKeyPressed=false;
}*/
}
//Check if a key is pressed (down).
if(inputMgr.isKeyDownEvent(INPUTMGR_JUMP) && !readFromRecord){
//The up key, if we aren't in the air we start jumping.
//Fixed a potential bug
if(!inAir && !isJump){
#ifdef RECORD_FILE_DEBUG
char c[64];
sprintf(c,"[%05d] Jump key pressed\n",objParent->time);
cout<<c;
recordKeyPressLog+=c;
#endif
isJump=true;
}
}else if(inputMgr.isKeyDownEvent(INPUTMGR_SPACE) && !readFromRecord){
//Fixed a potential bug
if(!spaceKeyPressed){
#ifdef RECORD_FILE_DEBUG
char c[64];
sprintf(c,"[%05d] Space key pressed\n",objParent->time);
cout<<c;
recordKeyPressLog+=c;
#endif
spaceKeyDown(shadow);
spaceKeyPressed=true;
}
}else if(inputMgr.isKeyUpEvent(INPUTMGR_SPACE) && !readFromRecord){
if(record && getSettings()->getBoolValue("quickrecord")){
spaceKeyDown(shadow);
spaceKeyPressed=true;
}
}else if(record && !readFromRecord && inputMgr.isKeyDownEvent(INPUTMGR_CANCELRECORDING)){
//Cancel current recording
//Search the recorded button and clear the last space key press
int i=recordButton.size()-1;
for(;i>=0;i--){
if(recordButton[i] & PlayerButtonSpace){
recordButton[i] &= ~PlayerButtonSpace;
break;
}
}
if(i>=0){
//Clear the recording at the player's side.
playerButton.clear();
line.clear();
//reset the record flag
record=false;
//decrese the record count
objParent->recordings--;
}else{
cout<<"Failed to find last recording"<<endl;
}
}else if(inputMgr.isKeyDownEvent(INPUTMGR_ACTION)){
//Downkey is pressed.
//Fixed a potential bug
if(!downKeyPressed){
#ifdef RECORD_FILE_DEBUG
char c[64];
sprintf(c,"[%05d] Action key pressed\n",objParent->time);
cout<<c;
recordKeyPressLog+=c;
#endif
downKeyPressed=true;
}
}else if(inputMgr.isKeyDownEvent(INPUTMGR_SAVE)){
//F2 only works in the level editor.
if(!(dead || shadow->dead) && stateID==STATE_LEVEL_EDITOR){
//Save the state. (delayed)
if(objParent)
objParent->saveStateNextTime=true;
}
}else if(inputMgr.isKeyDownEvent(INPUTMGR_LOAD) && !readFromRecord){
//F3 is used to load the last state.
if(objParent)
objParent->loadStateNextTime=true;
}else if(inputMgr.isKeyDownEvent(INPUTMGR_SWAP)){
//F4 will swap the player and the shadow, but only in the level editor.
if(!(dead || shadow->dead) && stateID==STATE_LEVEL_EDITOR){
swapState(shadow);
}
}else if(inputMgr.isKeyDownEvent(INPUTMGR_TELEPORT)){
//F5 will revive and teleoprt the player to the cursor. Only works in the level editor.
//Shift+F5 teleports the shadow.
if(stateID==STATE_LEVEL_EDITOR){
//get the position of the cursor.
int x,y;
SDL_GetMouseState(&x,&y);
x+=camera.x;
y+=camera.y;
if(inputMgr.isKeyDown(INPUTMGR_SHIFT)){
//teleports the shadow.
shadow->dead=false;
shadow->box.x=x;
shadow->box.y=y;
}else{
//teleports the player.
dead=false;
box.x=x;
box.y=y;
}
//play sound?
if(getSettings()->getBoolValue("sound")){
Mix_PlayChannel(-1,swapSound,0);
}
}
}else if(inputMgr.isKeyDownEvent(INPUTMGR_SUICIDE)){
//F12 is suicide and only works in the leveleditor.
if(stateID==STATE_LEVEL_EDITOR){
die();
shadow->die();
}
}
}
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;
//Set the objNotificationBlock to NULL.
objNotificationBlock=NULL;
//Set the object the player is currently standing to NULL.
objCurrentStand=NULL;
-
+
//Check if the player is still alive.
if(dead==false){
- //Add gravity
+ //Add gravity acceleration to the vertical velocity.
+ //NOTE: The x velocity is already set during the input handling, so is the jumping.
if(inAir==true){
yVel+=1;
//Cap fall speed to 13.
if(yVel>13){
yVel=13;
}
}
+
+ //An array that will hold all the GameObjects that are involved in the collision/movement.
+ vector<GameObject*> objects;
+
+ //Determine the collision frame.
+ SDL_Rect frame={box.x,box.y,box.w,box.h};
+ //Keep the horizontal movement of the player in mind.
+ if(xVel+xVelBase>=0) {
+ frame.w+=(xVel+xVelBase);
+ }else{
+ frame.x+=(xVel+xVelBase);
+ frame.w-=(xVel+xVelBase);
+ }
+ //And the vertical movement.
+ if(yVel+yVelBase>=0) {
+ frame.h+=(yVel+yVelBase);
+ }else{
+ frame.y+=(yVel+yVelBase);
+ frame.h-=(yVel+yVelBase);
+ }
+
+ //Loop through the game objects.
+ for(unsigned int o=0; o<levelObjects.size(); o++){
+ //Check if the player can collide with this game object.
+ if(!levelObjects[o]->queryProperties(GameObjectProperty_PlayerCanWalkOn,this))
+ continue;
+
+ //Check if the block is inside the frame.
+ if(checkCollision(frame,levelObjects[o]->getBox())){
+ objects.push_back(levelObjects[o]);
+ continue;
+ }
+ //Additional checks need to be made for moving blocks.
+ if(levelObjects[o]->type==TYPE_MOVING_BLOCK || levelObjects[o]->type==TYPE_MOVING_SHADOW_BLOCK || levelObjects[o]->type==TYPE_MOVING_SPIKES) {
+ //Check the movement of these blocks to see if they will collide.
+ SDL_Rect v=levelObjects[o]->getBox(BoxType_Velocity);
+ SDL_Rect r=levelObjects[o]->getBox();
+ r.x+=v.x;
+ r.y+=v.y;
+ if(checkCollision(frame,r)) {
+ objects.push_back(levelObjects[o]);
+ }
+ }
+ }
+
//Boolean if the player is moved, used for squash detection.
bool playerMoved=false;
//Indicates player or shadow is traveling, we should add it to traveling distance
bool isTraveling=true;
//The current location of the player, used to set the player back if he's squashed to prevent displacement.
int lastX=box.x;
int lastY=box.y;
//Check if the player can move.
if(canMove==true){
- //Check if the player is moving or not.
+ //Determine the correct theme state depending on the horizontal movement.
+ //NOTE: This has to be done here, because the die method checks the direction depending on the currentState.
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){
- //The left side of the block.
- if(xVel+xVelBase>v.x){
- if(box.x>r.x-box.w){
- box.x=r.x-box.w;
-
- playerMoved=true;
- }
+ //Loop through the objects related to the collision/movement.
+ for(unsigned int o=0; o<objects.size(); o++){
+ //Get the collision box of the levelobject.
+ SDL_Rect r=objects[o]->getBox();
+
+ //Check collision with the player.
+ //NOTE: Although the object is inside the collision frame we need to check if it collides with the player box (xVel applied).
+ if(checkCollision(box,r)){
+ //We have collision, get the velocity of the box.
+ SDL_Rect v=objects[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){
+ //The left side of the block.
+ if(xVel+xVelBase>v.x){
+ if(box.x>r.x-box.w){
+ box.x=r.x-box.w;
+
+ playerMoved=true;
}
- }else{
- //The right side of the block.
- if(xVel+xVelBase<v.x){
- if(box.x<r.x+r.w){
- box.x=r.x+r.w;
+ }
+ }else{
+ //The right side of the block.
+ if(xVel+xVelBase<v.x){
+ if(box.x<r.x+r.w){
+ box.x=r.x+r.w;
- playerMoved=true;
- }
+ playerMoved=true;
}
}
}
}
}
}
//Now apply the yVel. (gravity, jumping, etc..)
box.y+=yVel;
//Pointer to the object the player standed on.
GameObject* lastStand=NULL;
- //???
+ //Assume we are in air and are able to move unless proven otherwise (???).
inAir=true;
canMove=true;
//Boolean if the player can teleport.
bool canTeleport=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);
+ //Loop through all the objects.
+ for(unsigned int o=0; o<objects.size(); o++){
+ //Get the collision box of the levelobject.
+ SDL_Rect r=objects[o]->getBox();
+ //NOTE: Although the object is inside the collision frame we need to check if it collides with the player box (yVel applied).
+ if(checkCollision(box,r)){
+ //Get the velocity of the gameobject.
+ SDL_Rect v=objects[o]->getBox(BoxType_Delta);
+
+ //Check which side of the object the player is.
+ 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=objects[o];
+ lastStand->onEvent(GameObjectEvent_PlayerIsOn);
+
+ //The player is moved, if it's a moving block check for squating.
+ if(v.y!=0){
+ playerMoved=true;
+ }
+ }
+ }else{
+ //FIXME: The player can have a yVel of 0 and get squashed if he is standing on the other.
+ bool holding=objParent->shadow.holdingOther;
+ if(shadow)
+ holding=objParent->player.holdingOther;
+ if(yVel<=v.y+1 || holding){
+ yVel=v.y>0?v.y:0;
+ if(box.y<r.y+r.h){
+ if(!holding)
+ box.y=r.y+r.h;
//The player is moved, if it's a moving block check for squating.
if(v.y!=0){
playerMoved=true;
}
}
- }else{
- //FIXME: The player can have a yVel of 0 and get squashed if he is standing on the other.
- bool holding=objParent->shadow.holdingOther;
- if(shadow)
- holding=objParent->player.holdingOther;
- if(yVel<=v.y+1 || holding){
- yVel=v.y>0?v.y:0;
- if(box.y<r.y+r.h){
- if(!holding)
- box.y=r.y+r.h;
-
- //The player is moved, if it's a moving block check for squating.
- if(v.y!=0){
- playerMoved=true;
- }
- }
- }
- }
- }
- }
-
- //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())){
- //Check to see if we have enough keys to finish the level
- if (objParent->currentCollectables>=objParent->totalCollectables){
- //We can't just handle the winning here (in the middle of the update cycle)/
- //So set won in Game true.
- objParent->won=true;
- }
- }
-
- //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;
-
- //We don't count it to traveling distance.
- isTraveling=false;
-
- //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);
- }
-
- //Update statistics.
- if(!dead && !objParent->player.isPlayFromRecord() && !objParent->interlevel){
- statsMgr.switchTimes++;
- //TODO: achievements
- }
-
- 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 shadow block, only if we are the player.
- 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 a notification block, only if we are the player.
- if(levelObjects[o]->type==TYPE_NOTIFICATION_BLOCK && checkCollision(box,levelObjects[o]->getBox()) && !shadow){
- objNotificationBlock=levelObjects[o];
- }
-
- //Check if the object is a collectable
- if(levelObjects[o]->type==TYPE_COLLECTABLE && checkCollision(box,levelObjects[o]->getBox())){
- //Check if collectable is active (if it's not it's equal to 1(inactive))
- if (levelObjects[o]->queryProperties(GameObjectProperty_Flags, this)==0) {
- //Toggle an event
- levelObjects[o]->onEvent(GameObjectEvent_OnToggle);
- //Increase the current number of collectables
- objParent->currentCollectables++;
- if(getSettings()->getBoolValue("sound"))
- Mix_PlayChannel(-1,collectSound,0);
- //Open exit(s)
- if (objParent->currentCollectables>=objParent->totalCollectables){
- for(unsigned int i=0;i<levelObjects.size();i++){
- if(levelObjects[i]->type==TYPE_EXIT){
- Block *obj=dynamic_cast<Block*>(levelObjects[i]);
- if(obj!=NULL){
- levelObjects[i]->onEvent(GameObjectEvent_OnSwitchOn);
- }
- }
- }
- }
- }
- }
-
- //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)){
- //Now make sure we don't collide with a different block.
- 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){
- //Nothing found so call the die method.
- die();
- break;
- }
-
- //Check if the second (oo) object is a block.
- if(levelObjects[oo]->queryProperties(GameObjectProperty_PlayerCanWalkOn,this)){
- //Get the collision box.
- SDL_Rect r2=levelObjects[oo]->getBox();
-
- if(checkCollision(box,r2)){
- //Check if the top isn't covered.
- if(r2.y>r.y){
- //It isn't covered so create a box for collision detection
- SDL_Rect tmp={r2.x,r2.y,r2.w,r2.y-r.y};
- if(checkCollision(box,tmp)){
- //We hit spikes so die?
- die();
- break;
- }
- }
- //Check if the left side isn't covered.
- if(r2.x>r.x){
- //It isn't covered so create a box for collision detection
- SDL_Rect tmp={r2.x,r2.y,r2.x-r.x,r2.h};
- if(checkCollision(box,tmp)){
- //We hit spikes so die?
- die();
- break;
- }
- }
- //Check if the right side isn't covered.
- if(r2.x+r2.w>r.x+r.w){
- //It isn't covered so create a box for collision detection
- SDL_Rect tmp={r.x+r.w,r2.y,(r2.x+r2.w)-(r.x+r.w),r2.h};
- if(checkCollision(box,tmp)){
- //We hit spikes so die?
- die();
- break;
- }
- }
- //Check if the bottom isn't covered.
- if(r2.y+r2.h>r.y+r.h){
- //It isn't covered so create a box for collision detection
- SDL_Rect tmp={r2.x,r.y+r.h,r2.w,(r2.y+r2.h)-(r.y+r.h)};
- if(checkCollision(box,tmp)){
- //We hit spikes so die?
- die();
- break;
- }
- }
-
- //Check collision with the player and the block and with the block and the spikes.
- if(checkCollision(box,r)){
- //We break the loop to prevent going round (and calling the die() method).
- break;
- }
- }
- }
-
- //Increase oo.
- oo++;
}
}
}
}
//Check if the player was moved, if so check if the player is squashed.
if(playerMoved){
for(unsigned int o=0;o<levelObjects.size();o++){
SDL_Rect r2=levelObjects[o]->getBox();
if(levelObjects[o]->queryProperties(GameObjectProperty_PlayerCanWalkOn,this) && checkCollision(box,r2)){
//The player is squashed so first move him back.
box.x=lastX;
box.y=lastY;
//Update statistics.
if(!dead && !objParent->player.isPlayFromRecord() && !objParent->interlevel){
if(shadow) statsMgr.shadowSquashed++;
else statsMgr.playerSquashed++;
switch(statsMgr.playerSquashed+statsMgr.shadowSquashed){
case 1:
statsMgr.newAchievement("squash1");
break;
case 50:
statsMgr.newAchievement("squash50");
break;
}
}
//Now call the die method.
die();
}
}
}
//Check if the player fell of the level, if so let him die but without animation.
if(box.y>LEVEL_HEIGHT)
die(false);
//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;
}
}
}
+ //Now check the functional blocks.
+ for(unsigned int o=0;o<levelObjects.size();o++){
+ //Check for collision.
+ if(checkCollision(box,levelObjects[o]->getBox())){
+ //Now switch the type.
+ switch(levelObjects[o]->type){
+ case TYPE_CHECKPOINT:
+ {
+ //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];
+ break;
+ }
+ case TYPE_SWAP:
+ {
+ //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];
+ break;
+ }
+ case TYPE_EXIT:
+ {
+ //Make sure we're not in the leveleditor.
+ if(stateID==STATE_LEVEL_EDITOR)
+ break;
+
+ //Check to see if we have enough keys to finish the level
+ if(objParent->currentCollectables>=objParent->totalCollectables){
+ //We can't just handle the winning here (in the middle of the update cycle)/
+ //So set won in Game true.
+ objParent->won=true;
+ }
+ break;
+ }
+ case TYPE_PORTAL:
+ {
+ //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;
+
+ //We don't count it to traveling distance.
+ isTraveling=false;
+
+ //Check if music/sound is enabled.
+ if(getSettings()->getBoolValue("sound")){
+ Mix_PlayChannel(-1,swapSound,0);
+ }
+ break;
+ }
+ }
+
+ //Increase oo.
+ oo++;
+ }
+ }
+ }
+ break;
+ }
+ case TYPE_SWITCH:
+ {
+ //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);
+ }
+
+ //Update statistics.
+ if(!dead && !objParent->player.isPlayFromRecord() && !objParent->interlevel){
+ statsMgr.switchTimes++;
+ //TODO: achievements
+ }
+
+ 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;
+ }
+ }
+ }
+ break;
+ }
+ case TYPE_SHADOW_BLOCK:
+ case TYPE_MOVING_SHADOW_BLOCK:
+ {
+ //This only applies to the player.
+ if(!shadow)
+ objShadowBlock=levelObjects[o];
+ break;
+ }
+ case TYPE_NOTIFICATION_BLOCK:
+ {
+ //This only applies to the player.
+ if(!shadow)
+ objNotificationBlock=levelObjects[o];
+ break;
+ }
+ case TYPE_COLLECTABLE:
+ {
+ //Check if collectable is active (if it's not it's equal to 1(inactive))
+ if(levelObjects[o]->queryProperties(GameObjectProperty_Flags, this)==0) {
+ //Toggle an event
+ levelObjects[o]->onEvent(GameObjectEvent_OnToggle);
+ //Increase the current number of collectables
+ objParent->currentCollectables++;
+ if(getSettings()->getBoolValue("sound"))
+ Mix_PlayChannel(-1,collectSound,0);
+ //Open exit(s)
+ if(objParent->currentCollectables>=objParent->totalCollectables){
+ for(unsigned int i=0;i<levelObjects.size();i++){
+ if(levelObjects[i]->type==TYPE_EXIT){
+ Block *obj=dynamic_cast<Block*>(levelObjects[i]);
+ if(obj!=NULL){
+ levelObjects[i]->onEvent(GameObjectEvent_OnSwitchOn);
+ }
+ }
+ }
+ }
+ }
+ break;
+ }
+ }
+
+ //Now check for the spike property.
+ 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 can teleport.
if(canTeleport)
objLastTeleport=NULL;
//Check the checkpoint pointer only if the downkey is pressed.
//new: don't save the game if playing game record
if(objParent!=NULL && downKeyPressed && objCheckPoint!=NULL && !isPlayFromRecord()){
//Checkpoint thus save the state.
if(objParent->canSaveState()){
objParent->saveStateNextTime=true;
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);
//We don't count it to traveling distance.
isTraveling=false;
//Note: Statistics updated in swapState() function.
}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);
//We don't count it to traveling distance.
isTraveling=false;
//Note: Statistics updated in swapState() function.
}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");
}
}
}
//Update traveling distance statistics.
if(isTraveling && (lastX!=box.x || lastY!=box.y) && !objParent->player.isPlayFromRecord() && !objParent->interlevel){
float dx=float(lastX-box.x),dy=float(lastY-box.y);
float d0=statsMgr.playerTravelingDistance+statsMgr.shadowTravelingDistance,
d=sqrtf(dx*dx+dy*dy)/50.0f;
if(shadow) statsMgr.shadowTravelingDistance+=d;
else statsMgr.playerTravelingDistance+=d;
//Update achievement
d+=d0;
if(d0<=100.0f && d>=100.0f) statsMgr.newAchievement("travel100");
if(d0<=1000.0f && d>=1000.0f) statsMgr.newAchievement("travel1k");
if(d0<=10000.0f && d>=10000.0f) statsMgr.newAchievement("travel10k");
if(d0<=42195.0f && d>=42195.0f) statsMgr.newAchievement("travel42k");
}
}
//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++;
//Update statistics
if(!objParent->player.isPlayFromRecord() && !objParent->interlevel){
if(shadow) statsMgr.shadowJumps++;
else statsMgr.playerJumps++;
if(statsMgr.playerJumps+statsMgr.shadowJumps==1000) statsMgr.newAchievement("frog");
}
//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(){
int currentKey=0;
/*//debug
extern int block_test_count;
extern bool block_test_only;
if(SDL_GetKeyState(NULL)[SDLK_p]){
block_test_count=recordButton.size();
}
if(block_test_count==(int)recordButton.size()){
block_test_only=true;
}*/
//Check if we should read the input from record file.
if(recordIndex>=0){ // && recordIndex<(int)recordButton.size()){
//read the input from record file
if(recordIndex<(int)recordButton.size()){
currentKey=recordButton[recordIndex];
recordIndex++;
}
//Reset horizontal velocity.
xVel=0;
if(currentKey&PlayerButtonRight){
//Walking to the right.
xVel+=7;
}
if(currentKey&PlayerButtonLeft){
//Walking to the left.
xVel-=7;
}
if(currentKey&PlayerButtonJump){
//The up key, if we aren't in the air we start jumping.
if(inAir==false){
isJump=true;
}else{
//Shouldn't go here
cout<<"Replay BUG"<<endl;
}
}
//check the down key
downKeyPressed=(currentKey&PlayerButtonDown)!=0;
//check the space key
if(currentKey&PlayerButtonSpace){
spaceKeyDown(&objParent->shadow);
}
}else{
//read the input from keyboard.
recordIndex=-1;
//Check for xvelocity.
if(xVel>0)
currentKey|=PlayerButtonRight;
if(xVel<0)
currentKey|=PlayerButtonLeft;
//Check for jumping.
if(isJump){
#ifdef RECORD_FILE_DEBUG
char c[64];
sprintf(c,"[%05d] Jump key recorded\n",objParent->time-1);
cout<<c;
recordKeyPressLog+=c;
#endif
currentKey|=PlayerButtonJump;
}
//Check if the downbutton is pressed.
if(downKeyPressed){
#ifdef RECORD_FILE_DEBUG
char c[64];
sprintf(c,"[%05d] Action key recorded\n",objParent->time-1);
cout<<c;
recordKeyPressLog+=c;
#endif
currentKey|=PlayerButtonDown;
}
if(spaceKeyPressed){
#ifdef RECORD_FILE_DEBUG
char c[64];
sprintf(c,"[%05d] Space key recorded\n",objParent->time-1);
cout<<c;
recordKeyPressLog+=c;
#endif
currentKey|=PlayerButtonSpace;
}
//Record it.
recordButton.push_back(currentKey);
}
#ifdef RECORD_FILE_DEBUG
if(recordIndex>=0){
if(recordIndex>0 && recordIndex<=int(recordPlayerPosition.size())/2){
SDL_Rect &r1=recordPlayerPosition[recordIndex*2-2];
SDL_Rect &r2=recordPlayerPosition[recordIndex*2-1];
if(r1.x!=box.x || r1.y!=box.y || r2.x!=objParent->shadow.box.x || r2.y!=objParent->shadow.box.y){
char c[192];
sprintf(c,"Replay ERROR [%05d] %d %d %d %d Expected: %d %d %d %d\n",
objParent->time-1,box.x,box.y,objParent->shadow.box.x,objParent->shadow.box.y,r1.x,r1.y,r2.x,r2.y);
cout<<c;
}
}
}else{
recordPlayerPosition.push_back(box);
recordPlayerPosition.push_back(objParent->shadow.box);
}
#endif
//reset spaceKeyPressed.
spaceKeyPressed=false;
//Only add an entry if the player is recording.
if(record){
//Add the action.
if(!dead){
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){
//First make sure the player isn't dead.
//And check for velocity of the block the player is standing on.
if(!dead){
if(objCurrentStand!=NULL){
//Now get the velocity of the object the player is standing on.
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;
}
}
//Now do the same for the shadow.
if(!other->dead){
if(other->objCurrentStand!=NULL){
//Now get the velocity of the object the shadow is standing on.
SDL_Rect v=other->objCurrentStand->getBox(BoxType_Velocity);
//Set the base velocity to the velocity of the object.
other->xVelBase=v.x;
other->yVelBase=v.y;
//Already move the shadow box.
other->box.x+=v.x;
other->box.y+=v.y;
}
}
//Now check if the player is on the shadow.
//First make sure they are both alive.
if(!dead && !other->dead){
//Get the box of the shadow.
SDL_Rect boxShadow=other->getBox();
//Check if the player is on top of the shadow.
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");
}
//Apply the velocity the shadow has.
box.x+=other->xVelBase;
box.y+=other->yVelBase;
}
}else if(boxShadow.y+boxShadow.h<=box.y+13 && !inAir){
int yVelocity=other->yVel-1;
if(yVelocity>0){
other->box.y-=other->yVel;
other->box.y+=box.y-(other->box.y+other->box.h);
other->inAir=false;
other->canMove=false;
other->onGround=true;
holdingOther=true;
appearance.changeState("holding");
//Change our own appearance to standing.
if(other->direction==1){
other->appearance.changeState("standleft");
}else{
other->appearance.changeState("standright");
}
//Apply the velocity the shadow has.
other->box.x+=xVelBase;
other->box.y+=yVelBase;
}
}
}else{
holdingOther=false;
other->holdingOther=false;
}
}
//And set currentStand to null.
objCurrentStand=NULL;
other->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 level fit's horizontally inside the camera.
if(camera.w>LEVEL_WIDTH){
camera.x=-(camera.w-LEVEL_WIDTH)/2;
}else{
//Check if the player is halfway pass the halfright of the screen.
if(box.x>camera.x+(SCREEN_WIDTH/2+50)){
//It is so ease the camera to the right.
camera.x+=(box.x-camera.x-(SCREEN_WIDTH/2))>>4;
//Check if the camera isn't going too far.
if(box.x<camera.x+(SCREEN_WIDTH/2+50)){
camera.x=box.x-(SCREEN_WIDTH/2+50);
}
}
//Check if the player is halfway pass the halfleft of the screen.
if(box.x<camera.x+(SCREEN_WIDTH/2-50)){
//It is so ease the camera to the left.
camera.x+=(box.x-camera.x-(SCREEN_WIDTH/2))>>4;
//Check if the camera isn't going too far.
if(box.x>camera.x+(SCREEN_WIDTH/2-50)){
camera.x=box.x-(SCREEN_WIDTH/2-50);
}
}
//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 level fit's vertically inside the camera.
if(camera.h>LEVEL_HEIGHT){
//We don't centre vertical because the bottom line of the level (deadly) will be mid air.
camera.y=-(camera.h-LEVEL_HEIGHT);
}else{
//Check if the player is halfway pass the lower half of the screen.
if(box.y>camera.y+(SCREEN_HEIGHT/2+50)){
//If is so ease the camera down.
camera.y+=(box.y-camera.y-(SCREEN_HEIGHT/2))>>4;
//Check if the camera isn't going too far.
if(box.y<camera.y+(SCREEN_HEIGHT/2+50)){
camera.y=box.y-(SCREEN_HEIGHT/2+50);
}
}
//Check if the player is halfway pass the upper half of the screen.
if(box.y<camera.y+(SCREEN_HEIGHT/2-50)){
//It is so ease the camera up.
camera.y+=(box.y-camera.y-(SCREEN_HEIGHT/2))>>4;
//Check if the camera isn't going too far.
if(box.y>camera.y+(SCREEN_HEIGHT/2-50)){
camera.y=box.y-(SCREEN_HEIGHT/2-50);
}
}
//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;
downKeyPressed=false;
spaceKeyPressed=false;
//Some animation variables.
appearance.resetAnimation(save);
appearance.changeState("standright");
direction=0;
state=0;
xVel=0; //??? fixed a strange bug in game replay
yVel=0;
objCurrentStand=NULL;
objNotificationBlock=NULL;
//Clear the recording.
line.clear();
playerButton.clear();
recordButton.clear();
recordIndex=-1;
#ifdef RECORD_FILE_DEBUG
recordKeyPressLog.clear();
recordPlayerPosition.clear();
#endif
//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;
stateSaved=state;
//Let the appearance save.
appearance.saveAnimation();
//Save any recording stuff.
recordSaved=record;
playerButtonSaved=playerButton;
lineSaved=line;
//Save the record
savedRecordButton=recordButton;
#ifdef RECORD_FILE_DEBUG
recordKeyPressLog_saved=recordKeyPressLog;
recordPlayerPosition_saved=recordPlayerPosition;
#endif
//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 the saved values.
inAir=inAirSaved;
isJump=isJumpSaved;
onGround=onGroundSaved;
canMove=canMoveSaved;
holdingOther=holdingOtherSaved;
dead=false;
record=false;
shadowCall=false;
state=stateSaved;
//Restore the appearance.
appearance.loadAnimation();
//Restore any recorded stuff.
record=recordSaved;
playerButton=playerButtonSaved;
line=lineSaved;
//Load the previously saved record
recordButton=savedRecordButton;
#ifdef RECORD_FILE_DEBUG
recordKeyPressLog=recordKeyPressLog_saved;
recordPlayerPosition=recordPlayerPosition_saved;
#endif
}
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);
}
//Update statistics.
if(!dead && !objParent->player.isPlayFromRecord() && !objParent->interlevel){
statsMgr.swapTimes++;
//TODO: achievements
}
}
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(bool animation){
//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 (if animation is true).
if(animation){
if(appearance.currentStateName.find("right")!=std::string::npos){
appearance.changeState("dieright");
}else{
appearance.changeState("dieleft");
}
}
//Update statistics
if(!objParent->player.isPlayFromRecord() && !objParent->interlevel){
if(shadow) statsMgr.shadowDies++;
else statsMgr.playerDies++;
switch(statsMgr.playerDies+statsMgr.shadowDies){
case 1:
statsMgr.newAchievement("die1");
break;
case 50:
statsMgr.newAchievement("die50");
break;
case 1000:
statsMgr.newAchievement("die1000");
break;
}
if(objParent->player.dead && objParent->shadow.dead) statsMgr.newAchievement("doubleKill");
}
}
//We set the jumpTime to 120 when this is the shadow.
//That's the countdown for the "Your shadow has died." message.
if(shadow){
jumpTime=80;
}
}

File Metadata

Mime Type
text/x-diff
Expires
Sat, May 16, 3:39 AM (12 h, 22 m)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
64006
Default Alt Text
(53 KB)

Event Timeline