Page MenuHomePhabricator (Chris)

No OneTemporary

Authored By
Unknown
Size
77 KB
Referenced Files
None
Subscribers
None
diff --git a/src/Game.cpp b/src/Game.cpp
index 5a97cf3..9a1ae6c 100644
--- a/src/Game.cpp
+++ b/src/Game.cpp
@@ -1,757 +1,755 @@
/****************************************************************************
** 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 "GameState.h"
#include "Globals.h"
#include "Functions.h"
#include "FileManager.h"
#include "GameObjects.h"
#include "ThemeManager.h"
#include "Objects.h"
#include "Game.h"
#include "TreeStorageNode.h"
#include "POASerializer.h"
#include "InputManager.h"
#include <fstream>
#include <iostream>
#include <sstream>
#include <vector>
#include <map>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
using namespace std;
const char* Game::blockName[TYPE_MAX]={"Block","PlayerStart","ShadowStart",
"Exit","ShadowBlock","Spikes",
"Checkpoint","Swap","Fragile",
"MovingBlock","MovingShadowBlock","MovingSpikes",
"Teleporter","Button","Switch",
"ConveyorBelt","ShadowConveyorBelt","NotificationBlock",
};
map<string,int> Game::blockNameMap;
string Game::recordFile;
Game::Game(bool loadLevel):isReset(false)
,currentLevelNode(NULL)
,customTheme(NULL)
,background(NULL)
,gameTipIndex(0)
,time(0),timeSaved(0)
,recordings(0),recordingsSaved(0)
,shadowCam(false)
,player(this),shadow(this),objLastCheckPoint(NULL){
//Reserve the memory for the GameObject tips.
memset(bmTips,0,sizeof(bmTips));
action=loadImage(getDataPath()+"gfx/actions.png");
//Check if we should load record file.
if(!recordFile.empty()){
loadRecord(recordFile.c_str());
recordFile.clear();
return;
}
//If we should load the level then load it.
if(loadLevel){
this->loadLevel(levels.getLevelpackPath()+levels.getLevelFile());
levels.saveLevelProgress();
}
}
Game::~Game(){
//Simply call our destroy method.
destroy();
}
void Game::destroy(){
//Loop through the levelObjects and delete them.
for(unsigned int i=0;i<levelObjects.size();i++)
delete levelObjects[i];
//Done now clear the levelObjects vector.
levelObjects.clear();
//Clear the name and the editor data.
levelName.clear();
levelFile.clear();
editorData.clear();
//Loop through the tips.
for(int i=0;i<TYPE_MAX;i++){
//If it exist free the SDL_Surface.
if(bmTips[i])
SDL_FreeSurface(bmTips[i]);
}
memset(bmTips,0,sizeof(bmTips));
//Remove everything from the themeManager.
background=NULL;
if(customTheme)
objThemes.removeTheme();
customTheme=NULL;
//delete current level (if any)
if(currentLevelNode){
delete currentLevelNode;
currentLevelNode=NULL;
}
//Reset the time.
time=timeSaved=0;
recordings=recordingsSaved=0;
}
void Game::loadLevelFromNode(TreeStorageNode* obj,const string& fileName){
//Make sure there's nothing left from any previous levels.
destroy();
//set current level to loaded one.
currentLevelNode=obj;
//Temp var used for block locations.
SDL_Rect box;
//Set the level dimensions to the default, it will probably be changed by the editorData,
//but 800x600 is a fallback.
LEVEL_WIDTH=800;
LEVEL_HEIGHT=600;
//Load the additional data.
for(map<string,vector<string> >::iterator i=obj->attributes.begin();i!=obj->attributes.end();i++){
if(i->first=="size"){
//We found the size attribute.
if(i->second.size()>=2){
//Set the dimensions of the level.
LEVEL_WIDTH=atoi(i->second[0].c_str());
LEVEL_HEIGHT=atoi(i->second[1].c_str());
}
}else if(i->second.size()>0){
//Any other data will be put into the editorData.
editorData[i->first]=i->second[0];
}
}
//Get the theme.
{
//If a theme is configured then load it.
string theme=processFileName(getSettings()->getValue("theme"));
//Check if it isn't the default theme, because if it is it's already loaded.
if(fileNameFromPath(theme)!="Cloudscape") {
customTheme=objThemes.appendThemeFromFile(theme+"/theme.mnmstheme");
}
//Check if level themes are enabled.
if(getSettings()->getBoolValue("leveltheme")) {
string &s=editorData["theme"];
if(!s.empty()){
customTheme=objThemes.appendThemeFromFile(processFileName(theme)+"/theme.mnmstheme");
}
}
//Set the Appearance of the player and the shadow.
objThemes.getCharacter(false)->createInstance(&player.appearance);
objThemes.getCharacter(true)->createInstance(&shadow.appearance);
}
for(unsigned int i=0;i<obj->subNodes.size();i++){
TreeStorageNode* obj1=obj->subNodes[i];
if(obj1==NULL) continue;
if(obj1->name=="tile" && obj1->value.size()>=3){
int objectType=blockNameMap[obj1->value[0]];
box.x=atoi(obj1->value[1].c_str());
box.y=atoi(obj1->value[2].c_str());
map<string,string> obj;
for(map<string,vector<string> >::iterator i=obj1->attributes.begin();i!=obj1->attributes.end();i++){
if(i->second.size()>0) obj[i->first]=i->second[0];
}
levelObjects.push_back( new Block ( box.x, box.y, objectType, this) );
levelObjects.back()->setEditorData(obj);
}
}
//Set the levelName to the name of the current level.
levelName=editorData["name"];
levelFile=fileName;
//Some extra stuff only needed when not in the levelEditor.
if(stateID!=STATE_LEVEL_EDITOR){
//We create a text with the text "Level <levelno> <levelName>".
//It will be shown in the left bottom corner of the screen.
stringstream s;
if(levels.getLevelCount()>1){
s<<"Level "<<(levels.getCurrentLevel()+1)<<" ";
}
s<<editorData["name"];
SDL_Color fg={0,0,0,0};
SDL_Color bg={255,255,255,0};
bmTips[0]=TTF_RenderText_Shaded(fontText,s.str().c_str(),fg,bg);
if(bmTips[0])
SDL_SetAlpha(bmTips[0],SDL_SRCALPHA,160);
}
//Get the background
background=objThemes.getBackground();
if(background)
background->resetAnimation(true);
}
void Game::loadLevel(string fileName){
//Create a TreeStorageNode that will hold the loaded data.
TreeStorageNode *obj=new TreeStorageNode();
{
POASerializer objSerializer;
string s=fileName;
//Parse the file.
if(!objSerializer.loadNodeFromFile(s.c_str(),obj,true)){
cout<<"Can't load level file "<<s<<endl;
delete obj;
return;
}
}
//Now call another function.
loadLevelFromNode(obj,fileName);
}
//save current game record to the file.
void Game::saveRecord(const char* fileName){
//check if current level is NULL (which should be impossible)
if(currentLevelNode==NULL) return;
TreeStorageNode obj;
POASerializer objSerializer;
//put current level to the node.
currentLevelNode->name="map";
obj.subNodes.push_back(currentLevelNode);
//serialize the game record using RLE compression.
#define PUSH_BACK \
if(j>0){ \
if(j>1){ \
sprintf(c,"%d*%d",last,j); \
}else{ \
sprintf(c,"%d",last); \
} \
v.push_back(c); \
}
vector<string> &v=obj.attributes["record"];
vector<int> *record=player.getRecord();
char c[64];
int i,j=0,last;
for(i=0;i<(int)record->size();i++){
int currentKey=(*record)[i];
if(j==0 || currentKey!=last){
PUSH_BACK;
last=currentKey;
j=1;
}else{
j++;
}
}
PUSH_BACK;
#undef PUSH_BACK
#ifdef RECORD_FILE_DEBUG
//add record file debug data.
{
obj.attributes["recordKeyPressLog"].push_back(player.keyPressLog());
vector<SDL_Rect> &playerPosition=player.playerPosition();
string s;
char c[32];
sprintf(c,"%d\n",playerPosition.size());
s=c;
for(unsigned int i=0;i<playerPosition.size();i++){
SDL_Rect& r=playerPosition[i];
sprintf(c,"%d %d\n",r.x,r.y);
s+=c;
}
obj.attributes["recordPlayerPosition"].push_back(s);
}
#endif
//save it
objSerializer.saveNodeToFile(fileName,&obj,true,true);
//remove current level from node to prevent delete it.
obj.subNodes.clear();
}
//load game record (and its level) from file and play it.
void Game::loadRecord(const char* fileName){
//Create a TreeStorageNode that will hold the loaded data.
TreeStorageNode obj;
{
POASerializer objSerializer;
string s=fileName;
//Parse the file.
if(!objSerializer.loadNodeFromFile(s.c_str(),&obj,true)){
cout<<"Can't load record file "<<s<<endl;
return;
}
}
//find the node named 'map'.
bool loaded=false;
for(unsigned int i=0;i<obj.subNodes.size();i++){
if(obj.subNodes[i]->name=="map"){
//load the level. (fileName=???)
loadLevelFromNode(obj.subNodes[i],"???");
//remove this node to prevent delete it.
obj.subNodes[i]=NULL;
//over
loaded=true;
break;
}
}
if(!loaded){
cout<<"ERROR: Can't find subnode named 'map' from record file"<<endl;
return;
}
//load the record.
{
vector<int> *record=player.getRecord();
record->clear();
vector<string> &v=obj.attributes["record"];
for(unsigned int i=0;i<v.size();i++){
string &s=v[i];
string::size_type pos=s.find_first_of('*');
if(pos==string::npos){
//1 item only.
int i=atoi(s.c_str());
record->push_back(i);
}else{
//contains many items.
int i=atoi(s.substr(0,pos).c_str());
int j=atoi(s.substr(pos+1).c_str());
for(;j>0;j--){
record->push_back(i);
}
}
}
}
#ifdef RECORD_FILE_DEBUG
//load the debug data
{
vector<string> &v=obj.attributes["recordPlayerPosition"];
vector<SDL_Rect> &playerPosition=player.playerPosition();
playerPosition.clear();
if(!v.empty()){
if(!v[0].empty()){
stringstream st(v[0]);
int m;
st>>m;
for(int i=0;i<m;i++){
SDL_Rect r;
st>>r.x>>r.y;
r.w=0;
r.h=0;
playerPosition.push_back(r);
}
}
}
}
#endif
//play the record.
//TODO: tell the level manager don't save the level progress.
player.playRecord();
shadow.playRecord(); //???
}
/////////////EVENT///////////////
void Game::handleEvents(){
//First of all let the player handle input.
player.handleInput(&shadow);
//Check for an SDL_QUIT event.
if(event.type==SDL_QUIT){
//We need to quit so enter STATE_EXIT.
setNextState(STATE_EXIT);
}
//Check for the escape key.
if(inputMgr.isKeyUpEvent(INPUTMGR_ESCAPE)){
//Escape means we go one level up, to the level select state.
setNextState(STATE_LEVEL_SELECT);
//Save the progress.
levels.saveLevelProgress();
//And change the music back to the menu music.
getMusicManager()->playMusic("menu");
}
//Check if 'r' is pressed.
if(inputMgr.isKeyDownEvent(INPUTMGR_RESTART) && !player.isPlayFromRecord()){
//Set reset true.
isReset=true;
}
//Check if tab is pressed.
if(inputMgr.isKeyDownEvent(INPUTMGR_TAB)){
shadowCam=!shadowCam;
}
}
/////////////////LOGIC///////////////////
void Game::logic(){
//Add one tick to the time.
- //if(stateID!=STATE_LEVEL_EDITOR)
- time++;
+ time++;
//Let the player store his move, if recording.
player.shadowSetState();
//Let the player give his recording to the shadow, if configured.
player.shadowGiveState(&shadow);
//Let the player jump.
player.jump();
//Let him move.
player.move(levelObjects);
//And let the camera follow him.
if(!shadowCam){
player.setMyCamera();
}else{
shadow.setMyCamera();
}
//Now let the shadow decide his move, if he's playing a recording.
shadow.moveLogic();
//Let the shadow jump.
shadow.jump();
//Let the shadow move.
shadow.move(levelObjects);
//Some levelObjects can move so update them.
for(unsigned int i=0;i<levelObjects.size();i++){
levelObjects[i]->move();
}
//Process any event in the queue.
for(unsigned int idx=0;idx<eventQueue.size();idx++){
//Get the event from the queue.
typeGameObjectEvent &e=eventQueue[idx];
//Check if the it has an id attached to it.
if(e.flags|1){
//Loop through the levelObjects and give them the event if they have the right id.
for(unsigned int i=0;i<levelObjects.size();i++){
if(e.objectType<0 || levelObjects[i]->type==e.objectType){
Block *obj=dynamic_cast<Block*>(levelObjects[i]);
if(obj!=NULL && obj->id==e.id){
levelObjects[i]->onEvent(e.eventType);
}
}
}
}else{
//Loop through the levelObjects and give them the event.
for(unsigned int i=0;i<levelObjects.size();i++){
if(e.objectType<0 || levelObjects[i]->type==e.objectType){
levelObjects[i]->onEvent(e.eventType);
}
}
}
}
//Done processing the events so clear the queue.
eventQueue.clear();
//Check collision and stuff for the shadow and player.
- player.otherCheck(&shadow);
- shadow.otherCheck(&player);
+ player.otherCheck(&shadow);
//Check if we should reset.
if(isReset)
reset(false);
isReset=false;
}
/////////////////RENDER//////////////////
void Game::render(){
//First of all render the background.
{
//Get a pointer to the background.
ThemeBackground* bg=background;
//Check if the background is null, but there are themes.
if(bg==NULL && objThemes.themeCount()>0){
//Get the background from the first theme in the stack.
bg=objThemes[0]->getBackground();
}
//Check if the background isn't null.
if(bg){
//It isn't so draw it.
bg->draw(screen);
//And if it's the loaded background then also update the animation.
//FIXME: Updating the animation in the render method?
if(bg==background)
bg->updateAnimation();
}else{
//There's no background so fill the screen with white.
SDL_Rect r={0,0,SCREEN_WIDTH,SCREEN_HEIGHT};
SDL_FillRect(screen,&r,-1);
}
}
//Now we draw the levelObjects.
for(unsigned int o=0; o<levelObjects.size(); o++){
levelObjects[o]->show();
}
//Followed by the player and the shadow.
player.show();
shadow.show();
//Show the levelName if it isn't the level editor.
if(stateID!=STATE_LEVEL_EDITOR && bmTips[0]!=NULL){
applySurface(0,SCREEN_HEIGHT-bmTips[0]->h,bmTips[0],screen,NULL);
}
//Check if there's a tooltip.
//NOTE: gameTipIndex 0 is used for the levelName, 1 for restart text, 2 for restart+checkpoint.
if(gameTipIndex>2 && gameTipIndex<TYPE_MAX){
//Check if there's a tooltip for the type.
if(bmTips[gameTipIndex]==NULL){
//There isn't thus make it.
const char* s=NULL;
switch(gameTipIndex){
case TYPE_CHECKPOINT:
s="Press DOWN key to save the game.";
break;
case TYPE_SWAP:
s="Press DOWN key to swap the position of player and shadow.";
break;
case TYPE_SWITCH:
s="Press DOWN key to activate the switch.";
break;
case TYPE_PORTAL:
s="Press DOWN key to teleport.";
break;
case TYPE_NOTIFICATION_BLOCK:
s="Press DOWN key to read the message.";
break;
}
//If we have a string then it's a supported GameObject type.
if(s!=NULL){
SDL_Color fg={0,0,0,0},bg={255,255,255,0};
bmTips[gameTipIndex]=TTF_RenderText_Shaded(fontText,s,fg,bg);
SDL_SetAlpha(bmTips[gameTipIndex],SDL_SRCALPHA,160);
}
}
//We already have a gameTip for this type so draw it.
if(bmTips[gameTipIndex]!=NULL){
applySurface(0,0,bmTips[gameTipIndex],screen,NULL);
}
}
//Set the gameTip to 0.
gameTipIndex=0;
//Check if the player is dead, meaning we draw
if(player.dead){
//The player is dead, check if there's a state that can be loaded.
SDL_Surface* bm=NULL;
if(player.canLoadState()){
//Now check if the tip is already made, if not make it.
if(bmTips[2]==NULL){
SDL_Color fg={0,0,0,0},bg={255,255,255,0};
bmTips[2]=TTF_RenderText_Shaded(fontText,
"Press R to restart current level or press F3 to load the game.",
fg,bg);
SDL_SetAlpha(bmTips[2],SDL_SRCALPHA,160);
}
bm=bmTips[2];
}else{
//Now check if the tip is already made, if not make it.
if(bmTips[1]==NULL){
SDL_Color fg={0,0,0,0},bg={255,255,255,0};
bmTips[1]=TTF_RenderText_Shaded(fontText,
"Press R to restart current level.",
fg,bg);
SDL_SetAlpha(bmTips[1],SDL_SRCALPHA,160);
}
bm=bmTips[1];
}
//Draw the tip.
if(bm!=NULL)
applySurface(0,0,bm,screen,NULL);
}
//show time and records used in level editor.
if(stateID==STATE_LEVEL_EDITOR && time>0){
char c[32];
SDL_Color fg={0,0,0,0},bg={255,255,255,0};
SDL_Surface *bm;
int y=SCREEN_HEIGHT;
sprintf(c,"%d recordings",recordings);
bm=TTF_RenderText_Shaded(fontText,c,fg,bg);
SDL_SetAlpha(bm,SDL_SRCALPHA,160);
y-=bm->h;
applySurface(0,y,bm,screen,NULL);
SDL_FreeSurface(bm);
sprintf(c,"%-.2fs",time/40.0f);
bm=TTF_RenderText_Shaded(fontText,c,fg,bg);
SDL_SetAlpha(bm,SDL_SRCALPHA,160);
y-=bm->h;
applySurface(0,y,bm,screen,NULL);
SDL_FreeSurface(bm);
}
//Draw the current action in the upper right corner.
if(player.record){
applySurface(750,0,action,screen,NULL);
}else if(shadow.state!=0){
SDL_Rect r={50,0,50,50};
applySurface(750,0,action,screen,&r);
}
//if the game is play from record then draw something indicates it
if((time & 0x10)==0x10 && player.isPlayFromRecord()){
SDL_Rect r={50,0,50,50};
applySurface(0,0,action,screen,&r);
applySurface(0,SCREEN_HEIGHT-50,action,screen,&r);
applySurface(SCREEN_WIDTH-50,SCREEN_HEIGHT-50,action,screen,&r);
}
}
bool Game::saveState(){
//Check if the player and shadow can save the current state.
if(player.canSaveState() && shadow.canSaveState()){
//Let the player and the shadow save their state.
player.saveState();
shadow.saveState();
//Save the stats.
timeSaved=time;
recordingsSaved=recordings;
//Save other state, for example moving blocks.
for(unsigned int i=0;i<levelObjects.size();i++){
levelObjects[i]->saveState();
}
//Also save the background animation, if any.
if(background)
background->saveAnimation();
//Return true.
return true;
}
//We can't save the state so return false.
return false;
}
bool Game::loadState(){
//Check if there's a state that can be loaded.
if(player.canLoadState() && shadow.canLoadState()){
//Let the player and the shadow load their state.
player.loadState();
shadow.loadState();
//Load the stats.
time=timeSaved;
recordings=recordingsSaved;
//Load other state, for example moving blocks.
for(unsigned int i=0;i<levelObjects.size();i++){
levelObjects[i]->loadState();
}
//Also load the background animation, if any.
if(background)
background->loadAnimation();
//Return true.
return true;
}
//We can't load the state so return false.
return false;
}
void Game::reset(bool save){
//We need to reset the game so we also reset the player and the shadow.
player.reset(save);
shadow.reset(save);
//Reset the stats.
time=0;
recordings=0;
//There is no last checkpoint so set it to NULL.
if(save)
objLastCheckPoint=NULL;
//Reset other state, for example moving blocks.
for(unsigned int i=0;i<levelObjects.size();i++){
levelObjects[i]->reset(save);
}
//Also reset the background animation, if any.
if(background)
background->resetAnimation(save);
}
void Game::broadcastObjectEvent(int eventType,int objectType,const char* id){
//Create a typeGameObjectEvent that can be put into the queue.
typeGameObjectEvent e;
//Set the event type.
e.eventType=eventType;
//Set the object type.
e.objectType=objectType;
//By default flags=0.
e.flags=0;
//Unless there's an id.
if(id){
//Set flags to 0x1 and set the id.
e.flags|=1;
e.id=id;
}
//Add the event to the queue.
eventQueue.push_back(e);
}
void Game::getCurrentLevelAutoSaveRecordPath(std::string &bestTimeFilePath,std::string &bestRecordingFilePath,bool createPath){
levels.getLevelAutoSaveRecordPath(-1,bestTimeFilePath,bestRecordingFilePath,createPath);
}
diff --git a/src/LevelSelect.cpp b/src/LevelSelect.cpp
index 3724d14..b6514d9 100644
--- a/src/LevelSelect.cpp
+++ b/src/LevelSelect.cpp
@@ -1,691 +1,697 @@
/****************************************************************************
** 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 "GameState.h"
#include "Functions.h"
#include "FileManager.h"
#include "Globals.h"
#include "Objects.h"
#include "LevelSelect.h"
#include "GUIObject.h"
#include "GUIListBox.h"
#include "GUIScrollBar.h"
#include "InputManager.h"
#include "Game.h"
#include <SDL/SDL_ttf.h>
#include <SDL/SDL.h>
#include <stdio.h>
#include <string>
#include <sstream>
#include <iostream>
using namespace std;
static SDL_Surface* playButtonImage=NULL;
////////////////////NUMBER////////////////////////
Number::Number(){
image=NULL;
background=NULL;
number=0;
medal=0;
selected=false;
//Set the default dimensions.
box.x=0;
box.y=0;
box.h=50;
box.w=50;
//Load the medals image.
medals=loadImage(getDataPath()+"gfx/medals.png");
}
Number::~Number(){
//We only need to free the SDLSurface.
if(image) SDL_FreeSurface(image);
}
void Number::init(int number,SDL_Rect box){
//First set the number and update our status.
this->number=number;
update();
//Write our text, number+1 since the counting doens't start with 0, but with 1.
std::stringstream text;
number++;
text<<number;
//Create the text image.
SDL_Color black={0,0,0};
if(image) SDL_FreeSurface(image);
//Create the text image.
//Also check which font to use, if the number is higher than 100 use the small font.
image=TTF_RenderText_Blended(fontGUI,text.str().c_str(),black);
//Set the new location of the number.
this->box.x=box.x;
this->box.y=box.y;
}
void Number::show(int dy){
//First draw the background, also apply the yOffset(dy).
applySurface(box.x,box.y-dy,background,screen,NULL);
//Now draw the text image over the background.
//We draw it centered inside the box.
applySurface((box.x+25-(image->w/2)),box.y+((TTF_FontAscent(fontGUI)+TTF_FontDescent(fontGUI))/2)-dy,image,screen,NULL);
//Draw the selection mark.
if(selected){
drawGUIBox(box.x,box.y-dy,50,50,screen,0x00000000);
}
//Draw the medal.
if(medal>0){
SDL_Rect r={(medal-1)*30,0,30,30};
applySurface(box.x+30,(box.y+30)-dy,medals,screen,&r);
}
}
void Number::update(){
//Check if the level is locked, if so change the background to the locked image.
if(levels.getLocked(number)==false){
background=loadImage(getDataPath()+"gfx/level.png");
}else{
background=loadImage(getDataPath()+"gfx/levellocked.png");
}
//Set the medal.
medal=levels.getLevel(number)->won;
if(medal){
if(levels.getLevel(number)->targetTime<0 || levels.getLevel(number)->time<=levels.getLevel(number)->targetTime)
medal++;
if(levels.getLevel(number)->targetRecordings<0 || levels.getLevel(number)->recordings<=levels.getLevel(number)->targetRecordings)
medal++;
}
}
/////////////////////LEVEL SELECT/////////////////////
static GUIScrollBar* levelScrollBar=NULL;
static GUIObject* levelpackDescription=NULL;
static string levelDescription,levelMedal,levelMedal2,levelMedal3;
static string bestTimeFilePath,bestRecordingFilePath;
LevelSelect::LevelSelect(){
//clear the selected level
selectedNumber=NULL;
//Set the play button to NULL.
play=NULL;
//Load the background image.
background=loadImage(getDataPath()+"gfx/menu/background.png");
if(playButtonImage==NULL){
playButtonImage=loadImage(getDataPath()+"gfx/playbutton.png");
}
//Render the title.
SDL_Color black={0,0,0};
title=TTF_RenderText_Blended(fontTitle,"Select Level",black);
//create GUI (test only)
GUIObject* obj;
if(GUIObjectRoot){
delete GUIObjectRoot;
GUIObjectRoot=NULL;
}
GUIObjectRoot=new GUIObject(0,0,800,600);
//the level select scroll bar
levelScrollBar=new GUIScrollBar(768,184,16,242,ScrollBarVertical,0,0,0,1,4,true,false);
GUIObjectRoot->childControls.push_back(levelScrollBar);
//level pack description
levelpackDescription=new GUIObject(60,140,800,32,GUIObjectLabel);
GUIObjectRoot->childControls.push_back(levelpackDescription);
GUISingleLineListBox* levelpacks=new GUISingleLineListBox(150,104,500,32);
levelpacks->name="cmdLvlPack";
levelpacks->eventCallback=this;
vector<string> v=enumAllDirs(getDataPath()+"levelpacks/");
for(vector<string>::iterator i=v.begin(); i!=v.end(); ++i){
levelpackLocations[*i]=getDataPath()+"levelpacks/"+*i;
}
vector<string> v2=enumAllDirs(getUserPath()+"levelpacks/");
for(vector<string>::iterator i=v2.begin(); i!=v2.end(); ++i){
levelpackLocations[*i]=getUserPath()+"levelpacks/"+*i;
}
vector<string> v3=enumAllDirs(getUserPath()+"custom/levelpacks/");
for(vector<string>::iterator i=v3.begin(); i!=v3.end(); ++i){
levelpackLocations[*i]=getUserPath()+"custom/levelpacks/"+*i;
}
v.insert(v.end(),v2.begin(),v2.end());
v.insert(v.end(),v3.begin(),v3.end());
//Now we add a special levelpack that will contain the levels not in a levelpack.
v.push_back("Levels");
levelpacks->item=v;
levelpacks->value=0;
//Check if we can find the lastlevelpack.
for(vector<string>::iterator i=v.begin(); i!=v.end(); ++i){
if(*i==getSettings()->getValue("lastlevelpack")){
levelpacks->value=i-v.begin();
string s1=getUserPath()+"progress/"+*i+".progress";
//Check if this is the special Levels levelpack.
if(*i=="Levels"){
//Clear the current levels.
levels.clear();
levels.setCurrentLevel(0);
//List the custom levels and add them one for one.
vector<string> v=enumAllFiles(getUserPath()+"custom/levels/");
for(vector<string>::iterator i=v.begin(); i!=v.end(); ++i){
levels.addLevel(getUserPath()+"custom/levels/"+*i);
levels.setLocked(levels.getLevelCount()-1);
}
//List the addon levels and add them one for one.
v=enumAllFiles(getUserPath()+"levels/");
for(vector<string>::iterator i=v.begin(); i!=v.end(); ++i){
levels.addLevel(getUserPath()+"levels/"+*i);
levels.setLocked(levels.getLevelCount()-1);
}
}else{
//This isn't so load the levelpack in the normal way.
if(!levels.loadLevels(levelpackLocations[*i]+"/levels.lst")){
msgBox("Can't load level pack:\n"+*i,MsgBoxOKOnly,"Error");
}
}
//Load the progress.
levels.loadProgress(s1);
}
}
GUIObjectRoot->childControls.push_back(levelpacks);
obj=new GUIObject(20,540,240,32,GUIObjectButton,"Back");
obj->name="cmdBack";
obj->eventCallback=this;
GUIObjectRoot->childControls.push_back(obj);
play=new GUIObject(560,540,240,32,GUIObjectButton,"Play");
play->name="cmdPlay";
play->eventCallback=this;
play->visible=false;
GUIObjectRoot->childControls.push_back(play);
//show level list
refresh();
}
void LevelSelect::refresh(){
int m=levels.getLevelCount();
numbers.clear();
//clear the selected level
if(selectedNumber!=NULL){
delete selectedNumber;
selectedNumber=NULL;
}
//Hide the play button.
play->visible=false;
for(int n=0; n<m; n++){
numbers.push_back(Number());
}
for(int n=0; n<m; n++){
SDL_Rect box={(n%10)*64+80,(n/10)*64+184,0,0};
numbers[n].init(n,box);
}
if(m>40){
levelScrollBar->maxValue=(m-41)/10;
levelScrollBar->visible=true;
}else{
levelScrollBar->maxValue=0;
levelScrollBar->visible=false;
}
levelpackDescription->caption=levels.levelpackDescription;
int width,height;
TTF_SizeText(fontGUI,levels.levelpackDescription.c_str(),&width,&height);
levelpackDescription->left=(800-width)/2;
}
LevelSelect::~LevelSelect(){
if(GUIObjectRoot){
delete GUIObjectRoot;
GUIObjectRoot=NULL;
}
play=NULL;
levelScrollBar=NULL;
levelpackDescription=NULL;
if(selectedNumber!=NULL)
delete selectedNumber;
//Free the rendered title surface.
SDL_FreeSurface(title);
}
void LevelSelect::handleEvents(){
//Check for an SDL_QUIT event.
if(event.type==SDL_QUIT){
setNextState(STATE_EXIT);
}
//Check for a mouse click.
if(event.type==SDL_MOUSEBUTTONUP && event.button.button==SDL_BUTTON_LEFT){
checkMouse();
}
//Check if escape is pressed.
if(inputMgr.isKeyUpEvent(INPUTMGR_ESCAPE)){
setNextState(STATE_MENU);
}
//Check for scrolling down and up.
if(event.type==SDL_MOUSEBUTTONDOWN && event.button.button==SDL_BUTTON_WHEELDOWN && levelScrollBar){
if(levelScrollBar->value<levelScrollBar->maxValue) levelScrollBar->value++;
return;
}else if(event.type==SDL_MOUSEBUTTONDOWN && event.button.button==SDL_BUTTON_WHEELUP && levelScrollBar){
if(levelScrollBar->value>0) levelScrollBar->value--;
return;
}
}
void LevelSelect::checkMouse(){
int x,y,dy=0,m=levels.getLevelCount();
//Get the current mouse location.
SDL_GetMouseState(&x,&y);
//Check if we should replay the record.
if(selectedNumber!=NULL){
SDL_Rect mouse={x,y,0,0};
if(!bestTimeFilePath.empty()){
- SDL_Rect box={380,440,272,32};
+ SDL_Rect box={380,440,372,32};
if(checkCollision(box,mouse)){
Game::recordFile=bestTimeFilePath;
levels.setCurrentLevel(selectedNumber->getNumber());
setNextState(STATE_GAME);
//Pick music from the current music list.
getMusicManager()->pickMusic();
return;
}
}
if(!bestRecordingFilePath.empty()){
- SDL_Rect box={380,472,272,32};
+ SDL_Rect box={380,472,372,32};
if(checkCollision(box,mouse)){
Game::recordFile=bestRecordingFilePath;
levels.setCurrentLevel(selectedNumber->getNumber());
setNextState(STATE_GAME);
//Pick music from the current music list.
getMusicManager()->pickMusic();
return;
}
}
}
//Check if there's a scrollbar, if so get the value.
if(levelScrollBar)
dy=levelScrollBar->value;
if(m>dy*10+50)
m=dy*10+50;
y+=dy*64;
SDL_Rect mouse={x,y,0,0};
for(int n=dy*10; n<m; n++){
if(levels.getLocked(n)==false){
if(checkCollision(mouse,numbers[n].box)==true){
if(numbers[n].selected){
//Current level was selected, so play it
levels.setCurrentLevel(n);
setNextState(STATE_GAME);
//Pick music from the current music list.
getMusicManager()->pickMusic();
}else{
//Select current level
for(int i=0;i<levels.getLevelCount();i++){
numbers[i].selected=(i==n);
}
//Display level info
displayLevelInfo(n);
}
break;
}
}
}
}
void LevelSelect::displayLevelInfo(int number){
//Update currently selected level
if(selectedNumber==NULL){
selectedNumber=new Number();
}
SDL_Rect box={40,440,50,50};
selectedNumber->init(number,box);
//Show level description
levelDescription=levels.getLevelName(number);
//Show level medal
int medal=levels.getLevel(number)->won;
int time=levels.getLevel(number)->time;
int targetTime=levels.getLevel(number)->targetTime;
int recordings=levels.getLevel(number)->recordings;
int targetRecordings=levels.getLevel(number)->targetRecordings;
if(medal){
if(targetTime<0 && targetTime<0){
medal=-1;
}else{
if(targetTime<0 || time<=targetTime)
medal++;
if(targetRecordings<0 || recordings<=targetRecordings)
medal++;
}
}
switch(medal){
case 0:
levelMedal="You haven't finished this level ";//"Medal: None";
break;
case 1:
levelMedal="Medal: Bronze";
break;
case 2:
levelMedal="Medal: Silver";
break;
case 3:
levelMedal="Medal: Gold";
break;
default:
levelMedal="Medal: Not avaliable for this level";
break;
}
//Show best time and recordings
if(medal){
char s[64];
if(time>0)
- sprintf(s,"%-.2fs / %-.2fs",time/40.0f,targetTime/40.0f);
+ if(targetTime>0)
+ sprintf(s,"%-.2fs / %-.2fs",time/40.0f,targetTime/40.0f);
+ else
+ sprintf(s,"%-.2fs",time/40.0f);
else
s[0]='\0';
levelMedal2=string("Time: ")+s;
if(recordings>=0)
- sprintf(s,"%5d / %d",recordings,targetRecordings);
+ if(targetRecordings>=0)
+ sprintf(s,"%5d / %d",recordings,targetRecordings);
+ else
+ sprintf(s,"%d",recordings);
else
s[0]='\0';
levelMedal3=string("Recordings: ")+s;
}else{
levelMedal2.clear();
levelMedal3.clear();
}
//Show the play button.
play->visible=true;
//Check if there is auto record file
levels.getLevelAutoSaveRecordPath(number,bestTimeFilePath,bestRecordingFilePath,false);
if(!bestTimeFilePath.empty()){
FILE *f;
f=fopen(bestTimeFilePath.c_str(),"rb");
if(f==NULL){
bestTimeFilePath.clear();
}else{
fclose(f);
}
}
if(!bestRecordingFilePath.empty()){
FILE *f;
f=fopen(bestRecordingFilePath.c_str(),"rb");
if(f==NULL){
bestRecordingFilePath.clear();
}else{
fclose(f);
}
}
}
void LevelSelect::logic(){}
void LevelSelect::render(){
int x,y,dy=0,m=levels.getLevelCount();
int idx=-1;
//Get the current mouse location.
SDL_GetMouseState(&x,&y);
if(levelScrollBar)
dy=levelScrollBar->value;
if(m>dy*10+40)
m=dy*10+40;
y+=dy*64;
SDL_Rect mouse={x,y,0,0};
//Draw the background.
applySurface(0,0,background,screen,NULL);
//Draw the title.
applySurface((800-title->w)/2,40,title,screen,NULL);
//Loop through the level blocks and draw them.
for(int n=dy*10; n<m;n++){
numbers[n].show(dy*64);
if(levels.getLocked(n)==false && checkCollision(mouse,numbers[n].box)==true)
idx=n;
}
//Show currently selected level (if any)
if(selectedNumber!=NULL){
//Draw a background for the stats.
drawGUIBox(0,420,SCREEN_WIDTH,SCREEN_HEIGHT-420,screen,0xFFFFFF80);
selectedNumber->show(0);
SDL_Color fg={0,0,0};
SDL_Surface* bm;
if(!levelDescription.empty()){
bm=TTF_RenderText_Blended(fontText,levelDescription.c_str(),fg);
applySurface(100,440,bm,screen,NULL);
SDL_FreeSurface(bm);
}
if(!levelMedal.empty()){
bm=TTF_RenderText_Blended(fontText,levelMedal.c_str(),fg);
applySurface(40,504,bm,screen,NULL);
SDL_FreeSurface(bm);
}
//Only show the replay if the level is completed (won).
if(levels.getLevel(selectedNumber->getNumber())->won){
if(!bestTimeFilePath.empty()){
SDL_Rect r={0,0,32,32};
- SDL_Rect box={380,440,272,32};
+ SDL_Rect box={380,440,372,32};
if(checkCollision(box,mouse)){
r.x=32;
SDL_FillRect(screen,&box,0xFFCCCCCC);
}
applySurface(720,440,playButtonImage,screen,&r);
}
if(!bestRecordingFilePath.empty()){
SDL_Rect r={0,0,32,32};
- SDL_Rect box={380,472,272,32};
+ SDL_Rect box={380,472,372,32};
if(checkCollision(box,mouse)){
r.x=32;
SDL_FillRect(screen,&box,0xFFCCCCCC);
}
applySurface(720,472,playButtonImage,screen,&r);
}
}
if(!levelMedal2.empty()){
bm=TTF_RenderText_Blended(fontText,levelMedal2.c_str(),fg);
applySurface(380,440+(32-bm->h)/2,bm,screen,NULL);
SDL_FreeSurface(bm);
}
if(!levelMedal3.empty()){
bm=TTF_RenderText_Blended(fontText,levelMedal3.c_str(),fg);
applySurface(380,472+(32-bm->h)/2,bm,screen,NULL);
SDL_FreeSurface(bm);
}
}
//Show the tool tip text.
if(idx>=0){
SDL_Color fg={0,0,0};
char s[64];
//Render the name of the level.
SDL_Surface* name=TTF_RenderText_Blended(fontText,levels.getLevelName(idx).c_str(),fg);
SDL_Surface* time=NULL;
SDL_Surface* recordings=NULL;
//The time it took.
if(levels.getLevel(idx)->time>0){
sprintf(s,"%-.2fs",levels.getLevel(idx)->time/40.0f);
time=TTF_RenderText_Blended(fontText,(string("Time: ")+s).c_str(),fg);
}
//The number of recordings it took.
if(levels.getLevel(idx)->recordings>=0){
sprintf(s,"%d",levels.getLevel(idx)->recordings);
recordings=TTF_RenderText_Blended(fontText,(string("Recordings: ")+s).c_str(),fg);
}
//Now draw a square the size of the three texts combined.
SDL_Rect r=numbers[idx].box;
r.y-=dy*80;
if(time!=NULL && recordings!=NULL){
r.w=(name->w)>time->w?(name->w)>recordings->w?name->w:recordings->w:(time->w)>recordings->w?time->w:recordings->w;
r.h=name->h+5+time->h+recordings->h;
}else{
r.w=name->w;
r.h=name->h;
}
//Make sure the tooltip doesn't go outside the window.
if(r.y>SCREEN_HEIGHT-200){
r.y-=name->h+4;
}else{
r.y+=numbers[idx].box.h+2;
}
if(r.x+r.w>SCREEN_WIDTH-50)
r.x=SCREEN_WIDTH-50-r.w;
//Draw a rectange
Uint32 color=0xFFFFFF00|240;
drawGUIBox(r.x-5,r.y-5,r.w+10,r.h+10,screen,color);
//Calc the position to draw.
SDL_Rect r2=r;
//Now we render the name if the surface isn't null.
if(name!=NULL){
//Draw the name.
SDL_BlitSurface(name,NULL,screen,&r2);
}
//Increase the height to leave a gap between name and stats.
r2.y+=5;
if(time!=NULL){
//Now draw the time.
r2.y+=name->h;
SDL_BlitSurface(time,NULL,screen,&r2);
}
if(recordings!=NULL){
//Now draw the recordings.
r2.y+=time->h;
SDL_BlitSurface(recordings,NULL,screen,&r2);
}
//And free the surfaces.
SDL_FreeSurface(name);
SDL_FreeSurface(time);
SDL_FreeSurface(recordings);
}
}
void LevelSelect::GUIEventCallback_OnEvent(std::string Name,GUIObject* obj,int nEventType){
string s;
if(Name=="cmdLvlPack"){
s=levelpackLocations[((GUISingleLineListBox*)obj)->item[obj->value]];
getSettings()->setValue("lastlevelpack",((GUISingleLineListBox*)obj)->item[obj->value]);
}else if(Name=="cmdBack"){
setNextState(STATE_MENU);
return;
}else if(Name=="cmdPlay"){
if(selectedNumber!=NULL){
levels.setCurrentLevel(selectedNumber->getNumber());
setNextState(STATE_GAME);
//Pick music from the current music list.
getMusicManager()->pickMusic();
}
return;
}else{
return;
}
string s1=getUserPath()+"progress/"+((GUISingleLineListBox*)obj)->item[obj->value]+".progress";
//Check if this is the special Levels levelpack.
if(((GUISingleLineListBox*)obj)->item[obj->value]=="Levels"){
//Clear the current levels.
levels.clear();
levels.setCurrentLevel(0);
//List the custom levels and add them one for one.
vector<string> v=enumAllFiles(getUserPath()+"custom/levels/");
for(vector<string>::iterator i=v.begin(); i!=v.end(); ++i){
levels.addLevel(getUserPath()+"custom/levels/"+*i);
levels.setLocked(levels.getLevelCount()-1);
}
//List the addon levels and add them one for one.
v=enumAllFiles(getUserPath()+"levels/");
for(vector<string>::iterator i=v.begin(); i!=v.end(); ++i){
levels.addLevel(getUserPath()+"levels/"+*i);
levels.setLocked(levels.getLevelCount()-1);
}
}else{
//This isn't so load the levelpack in the normal way.
if(!levels.loadLevels(levelpackLocations[((GUISingleLineListBox*)obj)->item[obj->value]]+"/levels.lst")){
msgBox("Can't load level pack:\n"+((GUISingleLineListBox*)obj)->item[obj->value],MsgBoxOKOnly,"Error");
}
}
//Load the progress file.
levels.loadProgress(s1);
//And refresh the numbers.
refresh();
}
diff --git a/src/Player.cpp b/src/Player.cpp
index 7cb550e..06c88cd 100644
--- a/src/Player.cpp
+++ b/src/Player.cpp
@@ -1,1192 +1,1238 @@
/****************************************************************************
** 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 "InputManager.h"
#include "MD5.h"
#include <iostream>
#include <fstream>
#include <SDL/SDL_mixer.h>
#include <SDL/SDL.h>
#include <SDL/SDL_ttf.h>
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){
//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;
downKeyPressed=false;
spaceKeyPressed=false;
recordIndex=-1;
#ifdef RECORD_FILE_DEBUG
recordKeyPressLog.clear();
recordKeyPressLog_saved.clear();
recordPlayerPosition.clear();
recordPlayerPosition_saved.clear();
#endif
//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);
}
}
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;
-}
+
+#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.
//if(stateID!=STATE_LEVEL_EDITOR)
objParent->recordings++;
}
}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.
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.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.
if(objParent)
objParent->saveState();
}
}else if(inputMgr.isKeyDownEvent(INPUTMGR_LOAD)){
//F3 is used to load the last state.
if(objParent)
objParent->loadState();
}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;
//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())){
//Check if it's playing game record (?)
if(recordIndex>=0){
msgBox("Game replay is done.",MsgBoxOKOnly,"Game Replay");
//Go to the level select menu.
setNextState(STATE_LEVEL_SELECT);
//And change the music back to the menu music.
getMusicManager()->playMusic("menu");
return;
}
//TEST ONLY: save the current game record to file.
//objParent->saveRecord("test.mnmsrec");
//the string to store auto-save record path.
string bestTimeFilePath,bestRecordingFilePath;
//and if we can't get thest path.
bool filePathError=false;
//Set the current level won.
levels.getLevel()->won=true;
if(levels.getLevel()->time==-1 || levels.getLevel()->time>objParent->time){
levels.getLevel()->time=objParent->time;
//save the best-time game record.
if(bestTimeFilePath.empty()){
objParent->getCurrentLevelAutoSaveRecordPath(bestTimeFilePath,bestRecordingFilePath,true);
}
if(bestTimeFilePath.empty()){
cout<<"ERROR: Couldn't get auto-save record file path"<<endl;
filePathError=true;
}else{
objParent->saveRecord(bestTimeFilePath.c_str());
}
}
if(levels.getLevel()->recordings==-1 || levels.getLevel()->recordings>objParent->recordings){
levels.getLevel()->recordings=objParent->recordings;
//save the best-recordings game record.
if(bestRecordingFilePath.empty() && !filePathError){
objParent->getCurrentLevelAutoSaveRecordPath(bestTimeFilePath,bestRecordingFilePath,true);
}
if(bestRecordingFilePath.empty()){
cout<<"ERROR: Couldn't get auto-save record file path"<<endl;
filePathError=true;
}else{
objParent->saveRecord(bestRecordingFilePath.c_str());
}
}
//Goto the next level.
levels.nextLevel();
//Check if the level exists.
if(levels.getCurrentLevel()<levels.getLevelCount()){
//It does so unlock the levels.
levels.setLocked(levels.getCurrentLevel());
//And enter the GameState to start the new level.
setNextState(STATE_GAME);
//Don't forget the music.
getMusicManager()->pickMusic();
}else{
//Show the congratulations messagebox.
if(!levels.congratulationText.empty()){
msgBox(levels.congratulationText,MsgBoxOKOnly,"Congratulations");
}else{
msgBox("You have finished the levelpack!",MsgBoxOKOnly,"Congratulations");
}
//Save the progress.
levels.saveLevelProgress();
//Go to the level select menu.
setNextState(STATE_LEVEL_SELECT);
//And change the music back to the menu music.
getMusicManager()->playMusic("menu");
}
}
//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(){
int currentKey=0;
//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.
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.
+ //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.
+ //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;
}
-
- //Make sure the other isn't dead.
- if(!other->dead){
- //Get the box of the shadow.
- SDL_Rect boxShadow=other->getBox();
+ }
+ //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);
- //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");
- }
+ //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{
- other->holdingOther=false;
}
+ }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 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;
downKeyPressed=false;
spaceKeyPressed=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();
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;
//Let the appearance save.
appearance.saveAnimation();
//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 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();
//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);
}
}
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");
}
}

File Metadata

Mime Type
text/x-diff
Expires
Sat, Jun 20, 8:20 PM (1 w, 1 d ago)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
72994
Default Alt Text
(77 KB)

Event Timeline