Page MenuHomePhabricator (Chris)

No OneTemporary

Authored By
Unknown
Size
177 KB
Referenced Files
None
Subscribers
None
diff --git a/CMakeLists.txt b/CMakeLists.txt
index a015037..31c495b 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -1,96 +1,117 @@
PROJECT(meandmyshadow)
CMAKE_MINIMUM_REQUIRED(VERSION 2.6)
set(CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH} "${CMAKE_SOURCE_DIR}/cmake/Modules/")
+#Option if the game should be compiled with hardware acceleration.
+option (HARDWARE_ACCELERATION "Use openGL as rendering backend" ON)
+option (DEBUG_MODE "Compile the game with debug mode enabled" OFF)
+
+#Find the required libraries.
Find_Package(SDL REQUIRED)
Find_Package(SDL_ttf REQUIRED)
Find_Package(SDL_image REQUIRED)
Find_Package(SDL_mixer REQUIRED)
Find_Package(SDL_gfx REQUIRED)
Find_Package(CURL REQUIRED)
Find_Package(LibArchive REQUIRED)
Find_Package(OpenSSL REQUIRED)
+#Use openGL only when compiling with HARDWARE_ACCELERATION.
+if(HARDWARE_ACCELERATION)
+ Find_Package(OpenGL REQUIRED)
+endif(HARDWARE_ACCELERATION)
+#Add the include dirs of the libraries.
INCLUDE_DIRECTORIES(${SDL_INCLUDE_DIR})
INCLUDE_DIRECTORIES(${SDLIMAGE_INCLUDE_DIR})
INCLUDE_DIRECTORIES(${SDLMIXER_INCLUDE_DIR})
INCLUDE_DIRECTORIES(${SDLTTF_INCLUDE_DIR})
INCLUDE_DIRECTORIES(${SDLGFX_INCLUDE_DIR})
INCLUDE_DIRECTORIES(${CURL_INCLUDE_DIR})
INCLUDE_DIRECTORIES(${LibArchive_INCLUDE_DIR})
INCLUDE_DIRECTORIES(${OPENSSL_INCLUDE_DIR})
+#Use openGL only when compiling with HARDWARE_ACCELERATION.
+if(HARDWARE_ACCELERATION)
+ INCLUDE_DIRECTORIES(${OPENGL_INCLUDE_DIR})
+endif(HARDWARE_ACCELERATION)
if ( NOT SDL_FOUND )
message(FATAL_ERROR "SDL not found !")
endif ( NOT SDL_FOUND )
if ( NOT SDLTTF_FOUND )
message(FATAL_ERROR "SDL_ttf not found !")
endif ( NOT SDLTTF_FOUND )
if ( NOT SDLMIXER_FOUND )
message(FATAL_ERROR "SDL_mixer not found !")
endif ( NOT SDLMIXER_FOUND )
if ( NOT SDLGFX_FOUND )
message(FATAL_ERROR "SDL_gfx not found !")
endif ( NOT SDLGFX_FOUND )
if ( NOT CURL_FOUND )
message(FATAL_ERROR "CURL not found !")
endif ( NOT CURL_FOUND )
if ( NOT LibArchive_FOUND )
message(FATAL_ERROR "LIBARCHIVE not found !")
endif ( NOT LibArchive_FOUND )
if ( NOT OPENSSL_FOUND )
message(FATAL_ERROR "OpenSSL not found !")
endif ( NOT OPENSSL_FOUND )
+if ( HARDWARE_ACCELERATION AND NOT OPENGL_FOUND )
+ message(FATAL_ERROR "OpenGL not found !")
+endif ( HARDWARE_ACCELERATION AND NOT OPENGL_FOUND )
+
+#Parse the configure file.
configure_file (
"${PROJECT_SOURCE_DIR}/src/config.h.in"
"${PROJECT_BINARY_DIR}/config.h"
)
set(EXECUTABLE_OUTPUT_PATH ${PROJECT_BINARY_DIR})
set(SRC_DIR ${PROJECT_SOURCE_DIR}/src)
include_directories(
${PROJECT_BINARY_DIR}
${SDL_INCLUDE_DIR}
${SDLTTF_INCLUDE_DIR}
${SDLMIXER_INCLUDE_DIR}
${SDLIMAGE_INCLUDE_DIR}
${SDLGFX_INCLUDE_DIR}
${OPENSSL_INCLUDE_DIR}
+ ${OPENGL_INCLUDE_DIR}
)
SET(CMAKE_CXX_FLAGS "-Wall -g")
# Déclaration de l'exécutable
file(GLOB Sources ${SRC_DIR}/*.cpp)
add_executable(meandmyshadow ${Sources})
target_link_libraries(
meandmyshadow
${SDL_LIBRARY}
${SDLTTF_LIBRARY}
${SDLIMAGE_LIBRARY}
${SDLMIXER_LIBRARY}
${SDLGFX_LIBRARY}
${CURL_LIBRARY}
${LibArchive_LIBRARY}
${OPENSSL_LIBRARIES}
${CRYPTO_LIBRARY}
+ ${OPENGL_LIBRARY}
)
install(DIRECTORY ${PROJECT_SOURCE_DIR}/data DESTINATION share/meandmyshadow/)
install(TARGETS meandmyshadow RUNTIME DESTINATION bin)
if("${CMAKE_SYSTEM_NAME}" MATCHES "Linux")
install(FILES meandmyshadow.desktop DESTINATION share/applications/)
install(FILES icons/16x16/meandmyshadow.png DESTINATION share/icons/hicolor/16x16/apps/)
install(FILES icons/32x32/meandmyshadow.png DESTINATION share/icons/hicolor/32x32/apps/)
install(FILES icons/48x48/meandmyshadow.png DESTINATION share/icons/hicolor/48x48/apps/)
install(FILES icons/64x64/meandmyshadow.png DESTINATION share/icons/hicolor/64x64/apps/)
endif("${CMAKE_SYSTEM_NAME}" MATCHES "Linux")
diff --git a/src/Functions.cpp b/src/Functions.cpp
index be4a57f..49dc14d 100644
--- a/src/Functions.cpp
+++ b/src/Functions.cpp
@@ -1,951 +1,1048 @@
/****************************************************************************
** 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 <stdio.h>
#include <SDL/SDL.h>
#include <SDL/SDL_mixer.h>
#include <SDL/SDL_gfxPrimitives.h>
#include <string>
#include "Globals.h"
#include "Functions.h"
#include "FileManager.h"
#include "Objects.h"
#include "Player.h"
#include "GameObjects.h"
#include "Levels.h"
#include "TitleMenu.h"
#include "LevelEditSelect.h"
#include "LevelEditor.h"
#include "Game.h"
#include "LevelPlaySelect.h"
#include "Addons.h"
#include "ImageManager.h"
#include "MusicManager.h"
#include "ThemeManager.h"
#include "GUIListBox.h"
+
+#ifdef HARDWARE_ACCELERATION
+#include <GL/gl.h>
+#include <GL/glu.h>
+#endif
using namespace std;
#ifdef WIN32
#include <windows.h>
#include <shlobj.h>
#else
#include <strings.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <unistd.h>
#include <dirent.h>
#endif
//Initialise the imagemanager.
//The ImageManager is used to prevent loading images multiple times.
ImageManager imageManager;
//Initialise the musicManager.
//The MusicManager is used to prevent loading music files multiple times and for playing/fading music.
MusicManager musicManager;
//Pointer to the settings object.
//It is used to load and save the settings file and change the settings.
Settings* settings=0;
+#ifdef HARDWARE_ACCELERATION
+GLuint screenTexture;
+#endif
+
SDL_Surface* loadImage(string file){
//We use the imageManager to load the file.
return imageManager.loadImage(file);
}
void applySurface(int x,int y,SDL_Surface* source,SDL_Surface* dest,SDL_Rect* clip){
//The offset is needed to draw at the right location.
SDL_Rect offset;
offset.x=x;
offset.y=y;
//Let SDL do the drawing of the surface.
SDL_BlitSurface(source,clip,dest,&offset);
}
void drawRect(int x,int y,int w,int h,SDL_Surface* dest,Uint32 color){
//NOTE: We let SDL_gfx render it.
rectangleRGBA(dest,x,y,x+w,y+h,color >> 24,color >> 16,color >> 8,255);
}
//Draw a box with anti-aliased borders using SDL_gfx.
void drawGUIBox(int x,int y,int w,int h,SDL_Surface* dest,Uint32 color){
//Fill content's background color from function parameter
boxRGBA(screen,x+1,y+1,x+w-2,y+h-2,color >> 24,color >> 16,color >> 8,color >> 0);
//Draw first black borders around content and leave 1 pixel in every corner
lineRGBA(screen,x+1,y,x+w-2,y,0,0,0,255);
lineRGBA(screen,x+1,y+h-1,x+w-2,y+h-1,0,0,0,255);
lineRGBA(screen,x,y+1,x,y+h-2,0,0,0,255);
lineRGBA(screen,x+w-1,y+1,x+w-1,y+h-2,0,0,0,255);
//Fill the corners with transperent color to create anti-aliased borders
pixelRGBA(screen,x,y,0,0,0,160);
pixelRGBA(screen,x,y+h-1,0,0,0,160);
pixelRGBA(screen,x+w-1,y,0,0,0,160);
pixelRGBA(screen,x+w-1,y+h-1,0,0,0,160);
//Draw second lighter border around content
rectangleRGBA(screen,x+1,y+1,x+w-2,y+h-2,0,0,0,64);
//Create anti-aliasing in corners of second border
pixelRGBA(screen,x+1,y+1,0,0,0,50);
pixelRGBA(screen,x+1,y+h-2,0,0,0,50);
pixelRGBA(screen,x+w-2,y+1,0,0,0,50);
pixelRGBA(screen,x+w-2,y+h-2,0,0,0,50);
}
void drawLine(int x1,int y1,int x2,int y2,SDL_Surface* dest,Uint32 color){
//NOTE: We let SDL_gfx render it.
lineRGBA(dest,x1,y1,x2,y2,color >> 24,color >> 16,color >> 8,255);
}
void drawLineWithArrow(int x1,int y1,int x2,int y2,SDL_Surface* dest,Uint32 color,int spacing,int offset,int xsize,int ysize){
//Draw line first
drawLine(x1,y1,x2,y2,dest,color);
//calc delta and length
double dx=x2-x1;
double dy=y2-y1;
double length=sqrt(dx*dx+dy*dy);
if(length<0.001) return;
//calc the unit vector
dx/=length; dy/=length;
//Now draw arrows on it
for(double p=offset;p<length;p+=spacing){
drawLine(int(x1+p*dx+0.5),int(y1+p*dy+0.5),
int(x1+(p-xsize)*dx-ysize*dy+0.5),int(y1+(p-xsize)*dy+ysize*dx+0.5),dest,color);
drawLine(int(x1+p*dx+0.5),int(y1+p*dy+0.5),
int(x1+(p-xsize)*dx+ysize*dy+0.5),int(y1+(p-xsize)*dy-ysize*dx+0.5),dest,color);
}
}
bool init(){
//Initialze SDL.
if(SDL_Init(SDL_INIT_EVERYTHING)==-1) {
fprintf(stderr,"FATAL ERROR: SDL_Init failed\n");
return false;
}
//Initialze SDL_mixer (audio).
if(Mix_OpenAudio(22050,MIX_DEFAULT_FORMAT,2,512)==-1){
fprintf(stderr,"FATAL ERROR: Mix_OpenAudio failed\n");
return false;
}
//Initialze SDL_ttf (fonts).
if(TTF_Init()==-1){
fprintf(stderr,"FATAL ERROR: TTF_Init failed\n");
return false;
}
-
- //Initialise the screen.
- screen=SDL_SetVideoMode(SCREEN_WIDTH,SCREEN_HEIGHT,SCREEN_BPP,SDL_HWSURFACE | SDL_DOUBLEBUF /*|SDL_FULLSCREEN*/ );
- if(screen==NULL){
- fprintf(stderr,"FATAL ERROR: SDL_SetVideoMode failed\n");
+
+ //Set the screen_width and height.
+ SCREEN_WIDTH=atoi(settings->getValue("width").c_str());
+ SCREEN_HEIGHT=atoi(settings->getValue("height").c_str());
+
+ //Check if we should use gl or software rendering.
+ if(settings->getBoolValue("gl")){
+#ifdef HARDWARE_ACCELERATION
+ SDL_GL_SetAttribute(SDL_GL_RED_SIZE,8);
+ SDL_GL_SetAttribute(SDL_GL_GREEN_SIZE,8);
+ SDL_GL_SetAttribute(SDL_GL_BLUE_SIZE,8);
+ SDL_GL_SetAttribute(SDL_GL_ALPHA_SIZE,8);
+
+ SDL_GL_SetAttribute(SDL_GL_DEPTH_SIZE,16);
+ SDL_GL_SetAttribute(SDL_GL_BUFFER_SIZE,32);
+
+ SDL_GL_SetAttribute(SDL_GL_ACCUM_RED_SIZE,8);
+ SDL_GL_SetAttribute(SDL_GL_ACCUM_GREEN_SIZE,8);
+ SDL_GL_SetAttribute(SDL_GL_ACCUM_BLUE_SIZE,8);
+ SDL_GL_SetAttribute(SDL_GL_ACCUM_ALPHA_SIZE,8);
+
+ SDL_GL_SetAttribute(SDL_GL_MULTISAMPLEBUFFERS,0);
+ SDL_GL_SetAttribute(SDL_GL_MULTISAMPLESAMPLES,0);
+ SDL_GL_SetAttribute(SDL_GL_DOUBLEBUFFER,1);
+
+ //Set the video mode.
+ Uint32 flags=SDL_HWSURFACE | SDL_OPENGL;
+ if(settings->getBoolValue("fullscreen"))
+ flags|=SDL_FULLSCREEN;
+ if(SDL_SetVideoMode(SCREEN_WIDTH,SCREEN_HEIGHT,SCREEN_BPP,flags)==NULL){
+ fprintf(stderr,"FATAL ERROR: SDL_SetVideoMode failed\n");
+ return false;
+ }
+ //Create a screen
+ screen=SDL_CreateRGBSurface(SDL_HWSURFACE, SCREEN_WIDTH, SCREEN_HEIGHT, 32,0x00FF0000,0x0000FF00,0x000000FF,0xFF000000);
+
+ //Create a texture.
+ glGenTextures(1,&screenTexture);
+
+ //And set up gl correctly.
+ glClearColor(0, 0, 0, 0);
+ glClearDepth(1.0f);
+ glViewport(0, 0, SCREEN_WIDTH, SCREEN_HEIGHT);
+ glMatrixMode(GL_PROJECTION);
+ glLoadIdentity();
+ glOrtho(0, SCREEN_WIDTH, SCREEN_HEIGHT, 0, 1, -1);
+ glMatrixMode(GL_MODELVIEW);
+ glEnable(GL_TEXTURE_2D);
+ glLoadIdentity();
+#else
+ //NOTE: Hardware accelerated rendering requested but compiled without.
+ cerr<<"FATAL ERROR: Unable to use hardware acceleration (compiled without)."<<endl;
return false;
+#endif
+ }else{
+ Uint32 flags=SDL_HWSURFACE | SDL_DOUBLEBUF;
+ if(settings->getBoolValue("fullscreen"))
+ flags|=SDL_FULLSCREEN;
+ screen=SDL_SetVideoMode(SCREEN_WIDTH,SCREEN_HEIGHT,SCREEN_BPP,flags);
+ if(screen==NULL){
+ fprintf(stderr,"FATAL ERROR: SDL_SetVideoMode failed\n");
+ return false;
+ }
}
-
+
//Set the the window caption.
SDL_WM_SetCaption(("Me and my shadow "+version).c_str(),NULL);
SDL_EnableUNICODE(1);
//Create the types of blocks.
for(int i=0;i<TYPE_MAX;i++){
Game::blockNameMap[Game::blockName[i]]=i;
}
//Nothing went wrong so we return true.
return true;
}
bool loadFiles(){
//Load the music and play it.
if(musicManager.loadMusic((getDataPath()+"music/menu.music")).empty()){
printf("WARNING: Unable to load background music! \n");
}
musicManager.playMusic("menu",false);
//Always load the default music list for fallback.
musicManager.loadMusicList((getDataPath()+"music/default.list"));
//Load the fonts.
fontTitle=TTF_OpenFont((getDataPath()+"font/knewave.ttf").c_str(),55);
fontGUI=TTF_OpenFont((getDataPath()+"font/knewave.ttf").c_str(),32);
fontText=TTF_OpenFont((getDataPath()+"font/Blokletters-Viltstift.ttf").c_str(),16);
if(fontTitle==NULL || fontGUI==NULL || fontText==NULL){
printf("ERROR: Unable to load fonts! \n");
return false;
}
//Load the default theme.
if(objThemes.appendThemeFromFile(getDataPath()+"themes/Cloudscape/theme.mnmstheme")==NULL){
printf("ERROR: Can't load default theme file\n");
return false;
}
//Nothing failed so return true.
return true;
}
bool loadSettings(){
settings=new Settings(getUserPath(USER_CONFIG)+"meandmyshadow.cfg");
settings->parseFile();
//Always return true?
return true;
}
bool saveSettings(){
settings->save();
//Always return true?
return true;
}
Settings* getSettings(){
return settings;
}
MusicManager* getMusicManager(){
return &musicManager;
}
+void flipScreen(){
+ if(settings->getBoolValue("gl")){
+#ifdef HARDWARE_ACCELERATION
+ glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
+ glLoadIdentity();
+
+ //Create a texture from the screen surface.
+ glBindTexture(GL_TEXTURE_2D,screenTexture);
+
+ //Set the texture's stretching properties
+ glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MIN_FILTER,GL_LINEAR);
+ glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MAG_FILTER,GL_LINEAR);
+
+ glTexImage2D(GL_TEXTURE_2D,0,screen->format->BytesPerPixel,screen->w,screen->h,0,GL_BGRA,GL_UNSIGNED_BYTE,screen->pixels);
+
+ glBegin(GL_QUADS);
+ glTexCoord2f(0,0); glVertex3f(0,0,0);
+ glTexCoord2f(1,0); glVertex3f(SCREEN_WIDTH,0,0);
+ glTexCoord2f(1,1); glVertex3f(SCREEN_WIDTH,SCREEN_HEIGHT,0);
+ glTexCoord2f(0,1); glVertex3f(0,SCREEN_HEIGHT,0);
+ glEnd();
+ SDL_GL_SwapBuffers();
+#else
+ //NOTE: Trying to flip the screen using gl while compiled without.
+ cerr<<"FATAL ERROR: Unable to draw to screen using OpenGL (compiled without)."<<endl;
+#endif
+ }else{
+ SDL_Flip(screen);
+ }
+}
+
void clean(){
//We delete the settings.
if(settings){
delete settings;
settings=NULL;
}
//Get rid of the currentstate/
//NOTE: The state is probably already deleted by the changeState function.
if(currentState)
delete currentState;
//Destroy the GUI if present.
if(GUIObjectRoot){
delete GUIObjectRoot;
GUIObjectRoot=NULL;
}
//Destroy the imageManager.
imageManager.destroy();
//Destroy the musicManager.
musicManager.destroy();
//Close the fonts and quit SDL_ttf.
TTF_CloseFont(fontTitle);
TTF_CloseFont(fontGUI);
TTF_CloseFont(fontText);
TTF_Quit();
//Quit SDL.
SDL_Quit();
//And finally stop audio.
Mix_CloseAudio();
}
void setNextState(int newstate){
//Only change the state when we aren't already exiting.
if(nextState!=STATE_EXIT){
nextState=newstate;
}
}
void changeState(){
//Check if there's a nextState.
if(nextState!=STATE_NULL){
//Delete the currentState.
delete currentState;
currentState=NULL;
//Set the currentState to the nextState.
stateID=nextState;
nextState=STATE_NULL;
//Init the state.
switch(stateID){
case STATE_GAME:
currentState=new Game();
break;
case STATE_MENU:
levels.clear();
currentState=new Menu();
break;
case STATE_LEVEL_SELECT:
currentState=new LevelPlaySelect();
break;
case STATE_LEVEL_EDIT_SELECT:
currentState=new LevelEditSelect();
break;
case STATE_LEVEL_EDITOR:
currentState=new LevelEditor();
break;
case STATE_OPTIONS:
currentState=new Options();
break;
case STATE_ADDONS:
currentState=new Addons();
break;
}
//NOTE: STATE_EXIT isn't mentioned, meaning that currentState is null.
//This way the game loop will break and the program will exit.
//Fade out.
int fade=255;
SDL_BlitSurface(screen,NULL,tempSurface,NULL);
while(fade>0){
fade-=17;
if(fade<0)
fade=0;
SDL_FillRect(screen,NULL,0);
SDL_SetAlpha(tempSurface, SDL_SRCALPHA, fade);
SDL_BlitSurface(tempSurface,NULL,screen,NULL);
- SDL_Flip(screen);
+ flipScreen();
SDL_Delay(25);
}
}
}
void musicStoppedHook(){
//We just call the musicStopped method of the MusicManager.
musicManager.musicStopped();
}
bool checkCollision(const SDL_Rect& a,const SDL_Rect& b){
//Check if the left side of box a isn't past the right side of b.
if(a.x>=b.x+b.w){
return false;
}
//Check if the right side of box a isn't left of the left side of b.
if(a.x+a.w<=b.x){
return false;
}
//Check if the top side of box a isn't under the bottom side of b.
if(a.y>=b.y+b.h){
return false;
}
//Check if the bottom side of box a isn't above the top side of b.
if(a.y+a.h<=b.y){
return false;
}
//We have collision.
return true;
}
void setCamera(){
//SetCamera only works in the Level editor.
if(stateID==STATE_LEVEL_EDITOR){
//Get the mouse coordinates.
int x,y;
SDL_GetMouseState(&x,&y);
//Make sure we avoid the toolbar.
SDL_Rect mouse={x,y,0,0};
SDL_Rect toolbar={155,550,510,50};
if(checkCollision(mouse,toolbar))
return;
//Check if the mouse is near the left edge of the screen.
//Else check if the mouse is near the right edge.
if(x<50){
//We're near the left edge so move the camera.
camera.x-=5;
}else if(x>SCREEN_WIDTH-50){
//We're near the right edge so move the camera.
camera.x+=5;
}
//Check if the mouse is near the top edge of the screen.
//Else check if the mouse is near the bottom edge.
if(y<50){
//We're near the top edge so move the camera.
camera.y-=5;
}else if(y>SCREEN_HEIGHT-50){
//We're near the bottom edge so move the camera.
camera.y+=5;
}
}
}
bool parseArguments(int argc, char** argv){
//Loop through all arguments.
//We start at one since 0 is the command itself.
for(int i=1;i<argc;i++){
string argument=argv[i];
//Check if the argument is the data-dir.
if(argument=="--data-dir"){
//We need a second argument so we increase i.
i++;
if(i>=argc){
printf("ERROR: Missing argument for command '%s'\n\n",argument.c_str());
return false;
}
//Configure the dataPath with the given path.
dataPath=argv[i];
if(!getDataPath().empty()){
char c=dataPath[dataPath.size()-1];
if(c!='/'&&c!='\\') dataPath+="/";
}
}else if(argument=="--user-dir"){
//We need a second argument so we increase i.
i++;
if(i>=argc){
printf("ERROR: Missing argument for command '%s'\n\n",argument.c_str());
return false;
}
//Configure the userPath with the given path.
userPath=argv[i];
if(!userPath.empty()){
char c=userPath[userPath.size()-1];
if(c!='/'&&c!='\\') userPath+="/";
}
}else if(argument=="-v" || argument=="-version" || argument=="--version"){
//Print the version.
printf("Version: '%s'\n\n",version.c_str());
}else if(argument=="-h" || argument=="-help" || argument=="--help"){
//If the help is requested we'll return false without printing an error.
//This way the usage/help text will be printed.
return false;
}else{
//Any other argument is unknow so we return false.
printf("ERROR: Unknown argument %s\n\n",argument.c_str());
return false;
}
}
//If everything went well we can return true.
return true;
}
//Special structure that will recieve the GUIEventCallbacks of the messagebox.
struct msgBoxHandler:public GUIEventCallback{
public:
//Integer containing the ret(urn) value of the messageBox.
int ret;
public:
//Constructor.
msgBoxHandler():ret(0){}
void GUIEventCallback_OnEvent(std::string name,GUIObject* obj,int eventType){
//Make sure it's a click event.
if(eventType==GUIEventClick){
//Set the return value.
ret=obj->value;
//After a click event we can delete the GUI.
if(GUIObjectRoot){
delete GUIObjectRoot;
GUIObjectRoot=NULL;
}
}
}
};
msgBoxResult msgBox(string prompt,msgBoxButtons buttons,const string& title){
//Create the event handler.
msgBoxHandler objHandler;
//The GUI objects.
GUIObject* obj;
//We keep a pointer to the original GUIObjectRoot for later.
GUIObject* tmp=GUIObjectRoot;
//Create the GUIObjectRoot, the height and y location is temp.
//It depends on the content what it will be.
GUIObjectRoot=new GUIObject(100,200,600,200,GUIObjectFrame,title.c_str());
//Integer containing the current y location used to grow dynamic depending on the content.
int y=50;
//Now process the prompt.
{
//Pointer to the string.
char* lps=(char*)prompt.c_str();
//Pointer to a character.
char* lp=NULL;
//We keep looping forever.
//The only way out is with the break statement.
for(;;){
//As long as it's still the same sentence we continue.
//It will stop when there's a newline or end of line.
for(lp=lps;*lp!='\n'&&*lp!='\r'&&*lp!=0;lp++);
//Store the character we stopped on. (End or newline)
char c=*lp;
//Set the character in the string to 0, making lps a string containing one sentence.
*lp=0;
//Integer used to center the sentence horizontally.
int x;
TTF_SizeText(fontText,lps,&x,NULL);
x=(600-x)/2;
//Add a GUIObjectLabel with the sentence.
GUIObjectRoot->childControls.push_back(new GUIObject(x,y,584,25,GUIObjectLabel,lps));
//Increase y with 25, about the height of the text.
y+=25;
//Check the stored character if it was a stop.
if(c==0){
//It was so break out of the for loop.
lps=lp;
break;
}
//It wasn't meaning more will follow.
//We set lps to point after the "newline" forming a new string.
lps=lp+1;
}
}
//Add 70 to y to leave some space between the content and the buttons.
y+=70;
//Recalc the size of the message box.
GUIObjectRoot->top=(SCREEN_HEIGHT-y)/2;
GUIObjectRoot->height=y;
//Now we need to add the buttons.
//Integer containing the number of buttons to add.
int count=0;
//Array with the return codes for the buttons.
int value[3]={0};
//Array containing the captation for the buttons.
string button[3]={"","",""};
switch(buttons){
case MsgBoxOKCancel:
count=2;
button[0]="OK";value[0]=MsgBoxOK;
button[1]="Cancel";value[1]=MsgBoxCancel;
break;
case MsgBoxAbortRetryIgnore:
count=3;
button[0]="Abort";value[0]=MsgBoxAbort;
button[1]="Retry";value[1]=MsgBoxRetry;
button[2]="Ignore";value[2]=MsgBoxIgnore;
break;
case MsgBoxYesNoCancel:
count=3;
button[0]="Yes";value[0]=MsgBoxYes;
button[1]="No";value[1]=MsgBoxNo;
button[2]="Cancel";value[2]=MsgBoxCancel;
break;
case MsgBoxYesNo:
count=2;
button[0]="Yes";value[0]=MsgBoxYes;
button[1]="No";value[1]=MsgBoxNo;
break;
case MsgBoxRetryCancel:
count=2;
button[0]="Retry";value[0]=MsgBoxRetry;
button[1]="Cancel";value[1]=MsgBoxCancel;
break;
default:
count=1;
button[0]="OK";value[0]=MsgBoxOK;
break;
}
//Now we start making the buttons.
{
//Calculate the x location (centered).
int x=302-count*50;
//Reduce y so that the buttons fit inside the frame.
y-=40;
//Loop to add the buttons.
for(int i=0;i<count;i++,x+=100){
obj=new GUIObject(x,y,96,36,GUIObjectButton,button[i].c_str(),value[i]);
obj->eventCallback=&objHandler;
GUIObjectRoot->childControls.push_back(obj);
}
}
//Now we dim the screen and keep the GUI rendering/updating.
SDL_FillRect(tempSurface,NULL,0);
SDL_SetAlpha(tempSurface,SDL_SRCALPHA,155);
SDL_BlitSurface(tempSurface,NULL,screen,NULL);
while(GUIObjectRoot){
while(SDL_PollEvent(&event)){
GUIObjectHandleEvents(true);
//Also check for the return, escape or backspace button.
//escape = KEYUP.
//backspace and return = KEYDOWN.
if(count==1 && ((event.type==SDL_KEYUP && event.key.keysym.sym==SDLK_ESCAPE) ||
(event.type==SDL_KEYDOWN && (event.key.keysym.sym==SDLK_RETURN || event.key.keysym.sym==SDLK_BACKSPACE)))){
delete GUIObjectRoot;
GUIObjectRoot=NULL;
}
}
//Render the gui.
if(GUIObjectRoot)
GUIObjectRoot->render();
- SDL_Flip(screen);
+ flipScreen();
SDL_Delay(30);
}
//We're done so set the original GUIObjectRoot back.
GUIObjectRoot=tmp;
//And return the result.
return (msgBoxResult)objHandler.ret;
}
struct fileDialogHandler:public GUIEventCallback{
public:
//The ret(urn) value, true=ok and false=cancel
bool ret;
//Boolean if it's a save dialog.
bool isSave;
//Boolean if the file should be verified.
bool verifyFile;
//Boolean if files should be listed instead of directories.
bool files;
//Pointer to the textfield containing the filename.
GUIObject* txtName;
//Pointer to the listbox containing the different files.
GUIListBox* lstFile;
//The extension the files listed should have.
const char* extension;
//The current filename.
string fileName;
//The current search path.
string path;
//Vector containing the search paths.
vector<string> searchPath;
public:
//Constructor.
fileDialogHandler(bool isSave=false,bool verifyFile=false, bool files=true):ret(false),
isSave(isSave),verifyFile(verifyFile),
files(files),txtName(NULL){}
void GUIEventCallback_OnEvent(std::string name,GUIObject* obj,int eventType){
//Check for the ok event.
if(name=="cmdOK"){
//Get the entered fileName from the text field.
std::string s=txtName->caption;
//If it doesn't contain a slash we need to add the path to the fileName.
if(s.find_first_of("/")==string::npos)
s=path+s;
//If the string empty we return.
if(s.empty() || s.find_first_of("*?")!=string::npos)
return;
//We only need to check for extensions if it isn't a folder dialog.
if(files){
//If there isn't right extension add it.
size_t found=s.find_first_of(".");
if(found!=string::npos)
s.replace(s.begin()+found+1,s.end(),extension);
else if (s.substr(found+1)!=extension)
s.append(string(".")+extension);
}
//Check if we should save or load the file.
//
if(isSave){
//Open the file with read permission to check if it already exists.
FILE* f;
f=fopen(processFileName(s).c_str(),"rb");
//Check if it exists.
if(f){
//Close the file.
fclose(f);
//Let the currentState render once to prevent multiple GUI overlapping and prevent the screen from going black.
currentState->render();
//Prompt the user with a Yes or No question.
if(msgBox(s+" already exists.\nDo you want to overwrite it?",MsgBoxYesNo,"Overwrite Prompt")!=MsgBoxYes){
//He answered no, so we return.
return;
}
}
//Check if we should verify the file.
//Verifying only applies to files not to directories.
if(verifyFile && files){
//Open the file with write permission.
f=fopen(processFileName(s).c_str(),"wb");
//Check if their aren't problems.
if(f){
//Close the file.
fclose(f);
}else{
//Let the currentState render once to prevent multiple GUI overlapping and prevent the screen from going black.
currentState->render();
//The file can't be opened so tell the user.
msgBox("Can't open file "+s+".",MsgBoxOKOnly,"Error");
return;
}
}
}else if(verifyFile && files){
//We need to verify a file for opening.
FILE *f;
f=fopen(processFileName(s).c_str(),"rb");
//Check if it didn't fail.
if(f){
//Succes, so close the file.
fclose(f);
}else{
//Let the currentState render once to prevent multiple GUI overlapping and prevent the screen from going black.
currentState->render();
//Unable to open file so tell the user.
msgBox("Can't open file "+s+".",MsgBoxOKOnly,"Error");
return;
}
}
//If we haven't returned then it's fine.
//Set the fileName to the chosen file.
fileName=s;
//Delete the GUI.
if(GUIObjectRoot){
delete GUIObjectRoot;
GUIObjectRoot=NULL;
}
//Set return to true.
ret=true;
}else if(name=="cmdCancel"){
//Cancel means we can kill the gui.
if(GUIObjectRoot){
delete GUIObjectRoot;
GUIObjectRoot=NULL;
}
}else if(name=="lstFile"){
//Get a pointer to the listbox.
GUIListBox* obj1=lstFile;
//Make sure the option exist and change textfield to it.
if(obj1!=NULL && txtName!=NULL && obj1->value>=0 && obj1->value<(int)obj1->item.size()){
txtName->caption=obj1->item[obj1->value];
}
}else if(name=="lstSearchIn"){
//Get the searchpath listbox.
GUISingleLineListBox *obj1=dynamic_cast<GUISingleLineListBox*>(obj);
//Check if the entry exists.
if(obj1!=NULL && lstFile!=NULL && obj1->value>=0 && obj1->value<(int)searchPath.size()){
//Temp string.
string s;
//Get the new search path.
path=searchPath[obj1->value];
//Make sure it isn't empty.
if(!path.empty()){
//Process the filename.
s=processFileName(path);
}else{
//It's empty so we give the userpath.
s=getUserPath();
}
//Fill the list with files or directories.
if(files) {
lstFile->item=enumAllFiles(s,extension);
}else
lstFile->item=enumAllDirs(s);
//Remove any selection from the list.
lstFile->value=-1;
}
}
}
};
bool fileDialog(string& fileName,const char* title,const char* extension,const char* path,bool isSave,bool verifyFile,bool files){
//Pointer to GUIObject to make the GUI with.
GUIObject* obj;
//Pointer to the current GUIObjectRoot.
//We keep it so we can put it back after closing the fileDialog.
GUIObject* tmp=GUIObjectRoot;
//Create the fileDialogHandler, used for event handling.
fileDialogHandler objHandler(isSave,verifyFile,files);
//Vector containing the pathNames.
vector<string> pathNames;
//Set the extension of the objHandler.
objHandler.extension=extension;
//We now need to splits the given path into multiple path names.
if(path && path[0]){
//The string isn't empty.
//Pointer to the paths string.
char* lp=(char*)path;
//Pointer to the first newline.
char* lps=strchr(lp,'\n');
//Pointer used for checking if their's another newline.
//It will indicate if it's the last set or not.
char* lpe;
//Check for a newline.
if(lps){
//We have newline(s) so loop forever.
//We can only break out of the loop when the string ends.
for(;;){
//Add the first searchpath.
//This is the beginning of the string (lp) to the first newline. (lps)
objHandler.searchPath.push_back(string(lp,lps-lp));
//We should have another newline so search for it.
lpe=strchr(lps+1,'\n');
if(lpe){
//We found it so we add that to the pathname.
pathNames.push_back(string(lps+1,lpe-lps-1));
//And start over again by setting lp to the start of a new set of searchPath/pathName.
lp=lpe+1;
}else{
//There is no newline anymore, meaning the last entry, the rest of the string must be the pathName.
pathNames.push_back(string(lps+1));
//And break out of the loop.
break;
}
//We haven't broken out so search for a newline.
lps=strchr(lp,'\n');
//If there isn't a newline break.
if(!lps)
break;
}
}else{
//There is no newline thus the whole string is the searchPath.
objHandler.searchPath.push_back(path);
}
}else{
//Empty so put an empty string as searchPath.
objHandler.searchPath.push_back(string());
}
//It's time to create the GUI.
//If there are more than one pathNames we need to add a GUISingleLineListBox.
int base_y=pathNames.size()>0?60:20;
//Create the frame.
GUIObjectRoot=new GUIObject(100,100-base_y/2,600,400+base_y,GUIObjectFrame,title?title:(isSave?"Save File":"Load File"));
//Create the search path list box if needed.
if(pathNames.size()>0){
GUIObjectRoot->childControls.push_back(new GUIObject(8,40,184,36,GUIObjectLabel,"Search In"));
GUISingleLineListBox* obj1=new GUISingleLineListBox(160,40,432,36);
obj1->item=pathNames;
obj1->value=0;
obj1->name="lstSearchIn";
obj1->eventCallback=&objHandler;
GUIObjectRoot->childControls.push_back(obj1);
}
//Add the FileName label and textfield.
GUIObjectRoot->childControls.push_back(new GUIObject(8,20+base_y,184,36,GUIObjectLabel,"File Name"));
{
//Fill the textbox with the given fileName.
string s=fileName;
if(!isSave){
//But only if it isn't empty.
if(s.empty() && extension && extension[0])
s=string("*.")+string(extension);
}
//Create the textbox and add it to the GUI.
objHandler.txtName=new GUIObject(160,20+base_y,432,36,GUIObjectTextBox,s.c_str());
GUIObjectRoot->childControls.push_back(objHandler.txtName);
}
//Now we add the ListBox containing the files or directories.
{
GUIListBox* obj1=new GUIListBox(8,60+base_y,584,292);
//Get the searchPath.
string s=objHandler.searchPath[0];
//Make sure it isn't empty.
if(!s.empty()){
objHandler.path=s;
s=processFileName(s);
}else{
s=getUserPath();
}
//Check if we should list files or directories.
if(files){
//Fill the list with files.
obj1->item=enumAllFiles(s,extension);
}else{
//Fill the list with directories.
obj1->item=enumAllDirs(s);
}
obj1->name="lstFile";
obj1->eventCallback=&objHandler;
GUIObjectRoot->childControls.push_back(obj1);
objHandler.lstFile=obj1;
}
//Now create the OK and Cancel buttons.
obj=new GUIObject(200,360+base_y,192,36,GUIObjectButton,"OK");
obj->name="cmdOK";
obj->eventCallback=&objHandler;
GUIObjectRoot->childControls.push_back(obj);
obj=new GUIObject(400,360+base_y,192,36,GUIObjectButton,"Cancel");
obj->name="cmdCancel";
obj->eventCallback=&objHandler;
GUIObjectRoot->childControls.push_back(obj);
//Now we keep rendering and updating the GUI.
SDL_FillRect(tempSurface,NULL,0);
SDL_SetAlpha(tempSurface,SDL_SRCALPHA,155);
SDL_BlitSurface(tempSurface,NULL,screen,NULL);
while(GUIObjectRoot){
while(SDL_PollEvent(&event))
GUIObjectHandleEvents(true);
if(GUIObjectRoot)
GUIObjectRoot->render();
- SDL_Flip(screen);
+ flipScreen();
SDL_Delay(30);
}
//The while loop ended meaning we can restore the previous GUI.
GUIObjectRoot=tmp;
//Now determine what the return value is (and if there is one).
if(objHandler.ret)
fileName=objHandler.fileName;
return objHandler.ret;
}
diff --git a/src/Functions.h b/src/Functions.h
index 0d14d89..93ef1bf 100644
--- a/src/Functions.h
+++ b/src/Functions.h
@@ -1,199 +1,202 @@
/****************************************************************************
** 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/>.
**
****************************************************************************/
#ifndef FUNCTIONS_H
#define FUNCTIONS_H
#include "Settings.h"
#include "MusicManager.h"
#include <SDL/SDL.h>
#include <string>
#include <vector>
//Loads an image.
//file: The image file to load.
//Returns: The SDL_surface containing the image.
SDL_Surface* loadImage(std::string file);
//Method for drawing an SDL_Surface onto another.
//x: The x location to draw the source on the desination.
//y: The y location to draw the source on the desination.
//source: The SDL_Surface to draw.
//dest: The SDL_Surface to draw on.
//clip: Rectangle which part of the source should be drawn.
void applySurface(int x,int y,SDL_Surface* source,SDL_Surface* dest,SDL_Rect* clip);
//Method used to draw an rectangle.
//x: The top left x location of the rectangle.
//y: The top left y location of the rectangle.
//w: The width of the rectangle,
//h: The height of the rectangle.
//dest: The SDL_Surface to draw on.
//color: The color of the rectangle border to draw.
void drawRect(int x,int y,int w,int h,SDL_Surface* dest,Uint32 color=0);
//Method used to draw filled boxes with an anti-alliased border.
//Mostly used for GUI components.
//x: The top left x location of the box.
//y: The top left y location of the box.
//w: The width of the box,
//h: The height of the box.
//dest: The SDL_Surface to draw on.
//alpha: The alpha of the box to draw.
void drawGUIBox(int x,int y,int w,int h,SDL_Surface* dest,Uint32 color);
//Method used to draw a line.
//x1: The x location of the start point.
//y1: The y location of the start point.
//x2: The x location of the end point.
//y2: The y location of the end point.
//dest: The SDL_Surface to draw on.
//color: The color of the line to draw.
void drawLine(int x1,int y1,int x2,int y2,SDL_Surface* dest,Uint32 color=0);
//Method used to draw a line with some arrows on it.
//x1: The x location of the start point.
//y1: The y location of the start point.
//x2: The x location of the end point.
//y2: The y location of the end point.
//dest: The SDL_Surface to draw on.
//color: The color of the line to draw.
//spacing: The spacing between arrows.
//offset: Offset of first arrow relative to the start point.
//xize, ysize: The size of arrow.
void drawLineWithArrow(int x1,int y1,int x2,int y2,SDL_Surface* dest,Uint32 color=0,int spacing=16,int offset=0,int xsize=5,int ysize=5);
//Initialises the game. This is done almost at the beginning of the program.
//It initialises: SDL, SDL_Mixer, SDL_ttf, the screen and the block types.
//Returns: True if everything goes well.
bool init();
//Loads some important files, like the background music and the default theme.
//Returns: True if everything goes well.
bool loadFiles();
//This method will load the settings from the settings file.
//Returns: False if there's an error while loading.
bool loadSettings();
//This method will save the settings to the settings file.
//Returns: False if there's an error while saving.
bool saveSettings();
//Method used to get a pointer to the settings object.
//Returns: A pointer to the settings object.
Settings* getSettings();
//Method used to get a pointer to the MusicManager object.
//Returns: A pointer to the MusicManager object.
MusicManager* getMusicManager();
+//Method that will, depending on the rendering backend, draw the screen surface to the screen.
+void flipScreen();
+
//Method used to clean up before quiting meandmyshadow.
void clean();
//Sets what the nextState will be.
//newstate: Integer containing the id of the newstate.
void setNextState(int newstate);
//Method that will perform the state change.
//It will fade out and in.
void changeState();
//This method is called when music is stopped.
//NOTE: This method is outside the MusicManager because it couldn't be called otherwise.
//Do not call this method anywhere in the code!
void musicStoppedHook();
//Checks collision between two SDL_Rects.
//a: The first rectangle.
//b: The second rectangle.
//Returns: True if the two rectangles collide.
bool checkCollision(const SDL_Rect& a,const SDL_Rect& b);
//This method will check if the mouse is near a screen edge.
//If so it will move the camera.
//Note: This function only works with the leveleditor.
void setCamera();
//Parse the commandline arguments.
//argc: Integer containing the number of aruguments there are.
//argv: The arguments.
//Returns: False if something goes wrong while parsing.
bool parseArguments(int argc, char** argv);
//From http://en.wikipedia.org/wiki/Clamping_(graphics)
//x: The value to clamp.
//min: The minimum x can be.
//max: The maximum x can be.
//Returns: Integer containing the clamped value.
int inline clamp(int x,int min,int max){
return (x>max)?max:(x<min)?min:x;
}
//Enumeration containing the different messagebox button combinations.
enum msgBoxButtons{
//Only one button with the text OK.
MsgBoxOKOnly=0,
//Two buttons, one saying OK, the other Cancel.
MsgBoxOKCancel=1,
//Three buttons, Abort, Retry, Ignore.
MsgBoxAbortRetryIgnore=2,
//Three buttons, Yes, No or Cancel.
MsgBoxYesNoCancel=3,
//Two buttons, one saying Yes, the other No.
MsgBoxYesNo=4,
//Two buttons, one saying Retry, the other Cancel.
MsgBoxRetryCancel=5,
};
//Enumeration containing the different result that can be retrieved from a messagebox.
//It represents the button that has been pressed.
enum msgBoxResult{
//The OK button.
MsgBoxOK=1,
//The cancel button.
MsgBoxCancel=2,
//The abort button.
MsgBoxAbort=3,
//The retry button.
MsgBoxRetry=4,
//The ignore button.
MsgBoxIgnore=5,
//The yes button.
MsgBoxYes=6,
//The no button.
MsgBoxNo=7,
};
//Method that prompts the user with a notification and/or question.
//prompt: The message the user is prompted with.
//buttons: Which buttons the messagebox should have.
//title: The title of the message box.
//Returns: A msgBoxResult which button has been pressed.
msgBoxResult msgBox(std::string prompt,msgBoxButtons buttons,const std::string& title);
//This method will show a file dialog in which the user can select a file.
//NOTE: It doesn't support entering folders.
//fileName: String that will contain the result, it can also be used to already chose the file.
//title: The title of the fileDialog window.
//extension: The extension the files must have, leave empty for all files.
//path: The path to list the files of.
//isSave: If the dialog is for saving files, and not loading.
//verifyFile: Boolean if the selected should be verified.
//files: Boolean if the fileDialog should display files, if not it will display directories.
bool fileDialog(std::string& fileName,const char* title=NULL,const char* extension=NULL,const char* path=NULL,bool isSave=false,bool verifyFile=false,bool files=true);
#endif
diff --git a/src/Globals.cpp b/src/Globals.cpp
index ee6a251..5dbac9f 100644
--- a/src/Globals.cpp
+++ b/src/Globals.cpp
@@ -1,54 +1,58 @@
/****************************************************************************
** 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 "Globals.h"
#include <SDL/SDL.h>
#include <SDL/SDL_mixer.h>
#include <SDL/SDL_ttf.h>
+//Set the defautl value for the screen width and height.
+int SCREEN_WIDTH=800;
+int SCREEN_HEIGHT=600;
+
//Set the default value for the level width and height.
int LEVEL_HEIGHT=0;
int LEVEL_WIDTH=0;
//Set both the screen and the tempSurface to NULL.
SDL_Surface* screen=NULL;
SDL_Surface* tempSurface=NULL;
//Font that is used for titles.
//Knewave large.
TTF_Font* fontTitle=NULL;
//Font that is used for captions of buttons and other GUI elements.
//Knewave small.
TTF_Font* fontGUI=NULL;
//Font that is used for (long) text.
//Blokletter-Viltstift small.
TTF_Font* fontText=NULL;
//Set the current stateID and the nextState.
int stateID=STATE_NULL;
int nextState=STATE_NULL;
//The name of the current level.
std::string levelName;
//Initialise the camera.
//Start location is 0, size is the same as the screen size.
SDL_Rect camera={0,0,SCREEN_WIDTH,SCREEN_HEIGHT};
//The SDL_Event object.
SDL_Event event;
diff --git a/src/Globals.h b/src/Globals.h
index e434c70..efa2ba6 100644
--- a/src/Globals.h
+++ b/src/Globals.h
@@ -1,163 +1,163 @@
/****************************************************************************
** 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/>.
**
****************************************************************************/
#ifndef GLOBALS_H
#define GLOBALS_H
#include <SDL/SDL.h>
#include <SDL/SDL_mixer.h>
#include <SDL/SDL_ttf.h>
#include <string>
#ifdef WIN32
//#define DATA_PATH
#else
#include "config.h"
#endif
//Global constants
//The width of the screen.
-const int SCREEN_WIDTH=800;
+extern int SCREEN_WIDTH;
//The height of the screen.
-const int SCREEN_HEIGHT=600;
+extern int SCREEN_HEIGHT;
//The depth of the screen.
const int SCREEN_BPP=32;
//Strin containing the
const std::string version="V0.3";
//The height of the current level.
extern int LEVEL_HEIGHT;
//The width of the current level.
extern int LEVEL_WIDTH;
//The target frames per seconds.
const int g_FPS=40;
//The screen surface, it's used to draw on before it's drawn to the real screen.
extern SDL_Surface* screen;
//SDL_Surface with the same dimensions as screen which can be used for all kinds of (temp) drawing.
extern SDL_Surface* tempSurface;
//Font that is used for titles.
//Knewave large.
extern TTF_Font* fontTitle;
//Font that is used for captions of buttons and other GUI elements.
//Knewave small.
extern TTF_Font* fontGUI;
//Font that is used for (long) text.
//Blokletter-Viltstift small.
extern TTF_Font* fontText;
//Event, used for event handling.
extern SDL_Event event;
//GUI
class GUIObject;
extern GUIObject *GUIObjectRoot;
//The state id of the current state.
extern int stateID;
//Integer containing what the next state will be.
extern int nextState;
//String containing the name of the current level.
extern std::string levelName;
//SDL rectangle used to store the camera.
//x is the x location of the camera.
//y is the y location of the camera.
//w is the width of the camera. (equal to SCREEN_WIDTH)
//h is the height of the camera. (equal to SCREEN_HEIGHT)
extern SDL_Rect camera;
//Enumeration containing the ids of the game states.
enum GameStates
{
//State null is a special state used to indicate no state.
//This is used when no next state is defined.
STATE_NULL,
//This state is before the actual leveleditor used to make levelpacks.
STATE_LEVEL_EDIT_SELECT,
//This state is for the level editor.
STATE_LEVEL_EDITOR,
//This state is for the main menu.
STATE_MENU,
//This state is for the actual game.
STATE_GAME,
//Special state used when exiting meandmyshadow.
STATE_EXIT,
//This state is for the help screen.
STATE_LEVEL_SELECT,
//This state is for the options screen.
STATE_OPTIONS,
//This state is for the addon screen.
STATE_ADDONS
};
//Enumeration containing the ids of the different block types.
enum GameTileType{
//The normal solid block.
TYPE_BLOCK=0,
//Block representing the start location of the player.
TYPE_START_PLAYER,
//Block representing the start location of the shadow.
TYPE_START_SHADOW,
//The exit of the level.
TYPE_EXIT,
//The shadow block which is only solid for the shadow.
TYPE_SHADOW_BLOCK,
//Block that can kill both the player and the shadow.
TYPE_SPIKES,
//Special point where the player can save.
TYPE_CHECKPOINT,
//Block that will switch the location of the player and the shadow when invoked.
TYPE_SWAP,
//Block that will crumble to dust when stepped on it for the third time.
TYPE_FRAGILE,
//Normal block that moves along a path.
TYPE_MOVING_BLOCK,
//Shadow block that moves along a path.
TYPE_MOVING_SHADOW_BLOCK,
//A spike block that moves along a path.
TYPE_MOVING_SPIKES,
//Special block which, once entered, moves the player/shadow to a different portal.
TYPE_PORTAL,
//A block with a button which can activate or stop moving blocks, converyor belts
TYPE_BUTTON,
//A switch which can activate or stop moving blocks, converyor belts
TYPE_SWITCH,
//Solid block which works like
TYPE_CONVEYOR_BELT,
TYPE_SHADOW_CONVEYOR_BELT,
//Block that contains a message that can be read.
TYPE_NOTIFICATION_BLOCK,
//The (max) number of tiles.
TYPE_MAX
};
#endif
diff --git a/src/InputManager.cpp b/src/InputManager.cpp
index f8e0b93..39c8121 100644
--- a/src/InputManager.cpp
+++ b/src/InputManager.cpp
@@ -1,442 +1,442 @@
/****************************************************************************
** Copyright (C) 2012 me and my shadow developers
**
**
** 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 "InputManager.h"
#include "Globals.h"
#include "Settings.h"
#include "Functions.h"
#include "GUIObject.h"
#include "GUIListBox.h"
#include <stdlib.h>
#include <stdio.h>
#include <string>
using namespace std;
InputManager inputMgr;
//the order must be the same as InputManagerKeys
static const char* keySettingNames[INPUTMGR_MAX]={
"key_up","key_down","key_left","key_right","key_jump","key_action","key_space",
"key_escape","key_restart","key_tab","key_save","key_load","key_swap",
"key_teleport","key_suicide","key_shift","key_next","key_previous"
};
//the order must be the same as InputManagerKeys
static const char* keySettingDescription[INPUTMGR_MAX]={
"Up (in menu)","Down (in menu)","Left","Right","Jump","Action","Space (Record)",
"Escape","Restart","Tab (View shadow/Level prop.)","Save game (in editor)","Load game","Swap (in editor)",
"Teleport (in editor)","Suicide (in editor)","Shift (in editor)","Next block type","Previous block type"
};
class InputDialogHandler:public GUIEventCallback{
private:
//the list box which contains keys.
GUIListBox* listBox;
//the parent object.
InputManager* parent;
//check if it's alternative key
bool isAlternativeKey;
//update specified key config item
void updateConfigItem(int index){
//get the description
std::string s=keySettingDescription[index];
s+=": ";
//get key code name
int keyCode=parent->getKeyCode((InputManagerKeys)index,isAlternativeKey);
s+=InputManager::getKeyCodeName(keyCode);
//show it
listBox->item[index]=s;
}
public:
//Constructor.
InputDialogHandler(GUIListBox* listBox,InputManager* parent):listBox(listBox),parent(parent),isAlternativeKey(false){
//load the avaliable keys to the list box.
for(int i=0;i<INPUTMGR_MAX;i++){
//get the description
std::string s=keySettingDescription[i];
s+=": ";
//get key code name
int keyCode=parent->getKeyCode((InputManagerKeys)i,false);
s+=InputManager::getKeyCodeName(keyCode);
//add item
listBox->item.push_back(s);
}
}
//when a key is pressed call this to set the key to currently-selected item
void onKeyDown(int keyCode){
//check if an item is selected.
int index=listBox->value;
if(index<0 || index>=INPUTMGR_MAX) return;
//set it.
parent->setKeyCode((InputManagerKeys)index,keyCode,isAlternativeKey);
updateConfigItem(index);
}
void GUIEventCallback_OnEvent(std::string name,GUIObject* obj,int eventType){
//Make sure it's a click event.
if(eventType==GUIEventClick){
if(name=="cmdOK"){
//config is done, exiting
delete GUIObjectRoot;
GUIObjectRoot=NULL;
}else if(name=="cmdUnset"){
onKeyDown(0);
}else if(name=="lstType"){
isAlternativeKey=(obj->value==1);
for(int i=0;i<INPUTMGR_MAX;i++){
updateConfigItem(i);
}
}
/*else if(name=="cmdEscape"){
//set a key to Escape
//simply call onKeyDown().
onKeyDown(SDLK_ESCAPE);
}*/
}
}
};
int InputManager::getKeyCode(InputManagerKeys key,bool isAlternativeKey){
if(isAlternativeKey) return alternativeKeys[key];
else return keys[key];
}
void InputManager::setKeyCode(InputManagerKeys key,int keyCode,bool isAlternativeKey){
if(isAlternativeKey) alternativeKeys[key]=keyCode;
else keys[key]=keyCode;
}
void InputManager::loadConfig(){
int i;
for(i=0;i<INPUTMGR_MAX;i++){
string s=keySettingNames[i];
keys[i]=atoi(getSettings()->getValue(s).c_str());
s+="2";
alternativeKeys[i]=atoi(getSettings()->getValue(s).c_str());
}
}
void InputManager::saveConfig(){
int i;
char c[32];
for(i=0;i<INPUTMGR_MAX;i++){
string s=keySettingNames[i];
sprintf(c,"%d",keys[i]);
getSettings()->setValue(s,c);
s+="2";
sprintf(c,"%d",alternativeKeys[i]);
getSettings()->setValue(s,c);
}
}
void InputManager::showConfig(){
//Save the old GUI.
GUIObject* tmp=GUIObjectRoot;
//Create the new GUI.
GUIObjectRoot=new GUIObject((SCREEN_WIDTH-600)/2,(SCREEN_HEIGHT-400)/2,600,400,GUIObjectFrame,"Config Keys");
GUIObject* obj;
obj=new GUIObject(20,36,560,36,GUIObjectLabel,"Select an item and press a key to config it.");
GUIObjectRoot->childControls.push_back(obj);
//The list box.
GUIListBox *listBox=new GUIListBox(20,116,560,220);
//Event handler.
InputDialogHandler handler(listBox,this);
GUIObjectRoot->childControls.push_back(listBox);
//another box to select key type
GUISingleLineListBox *listBox0=new GUISingleLineListBox(120,72,360,36);
listBox0->name="lstType";
listBox0->item.push_back("Primary key");
listBox0->item.push_back("Alternative key");
listBox0->value=0;
listBox0->eventCallback=&handler;
GUIObjectRoot->childControls.push_back(listBox0);
//two buttons
obj=new GUIObject(20,360,360,36,GUIObjectButton,"Unset the key");
obj->name="cmdUnset";
obj->eventCallback=&handler;
GUIObjectRoot->childControls.push_back(obj);
/*
obj=new GUIObject(20,360,360,36,GUIObjectButton,"Set to ESCAPE key");
obj->name="cmdEscape";
obj->eventCallback=&handler;
GUIObjectRoot->childControls.push_back(obj);
*/
obj=new GUIObject(460,360,120,36,GUIObjectButton,"OK");
obj->name="cmdOK";
obj->eventCallback=&handler;
GUIObjectRoot->childControls.push_back(obj);
//Now we keep rendering and updating the GUI.
SDL_FillRect(tempSurface,NULL,0);
SDL_SetAlpha(tempSurface,SDL_SRCALPHA,155);
SDL_BlitSurface(tempSurface,NULL,screen,NULL);
while(GUIObjectRoot){
while(SDL_PollEvent(&event)){
//check if some key is down.
if(event.type==SDL_KEYDOWN){
handler.onKeyDown(event.key.keysym.sym);
}
//Joystick
else if(event.type==SDL_JOYAXISMOTION){
if(event.jaxis.value>3200){
handler.onKeyDown(0x00010001 | (int(event.jaxis.axis)<<8));
}else if(event.jaxis.value<-3200){
handler.onKeyDown(0x000100FF | (int(event.jaxis.axis)<<8));
}
}
else if(event.type==SDL_JOYBUTTONDOWN){
handler.onKeyDown(0x00020000 | (int(event.jbutton.button)<<8));
}
else if(event.type==SDL_JOYHATMOTION){
if(event.jhat.value & SDL_HAT_LEFT){
handler.onKeyDown(0x00030000 | (int(event.jhat.hat)<<8) | SDL_HAT_LEFT);
}else if(event.jhat.value & SDL_HAT_RIGHT){
handler.onKeyDown(0x00030000 | (int(event.jhat.hat)<<8) | SDL_HAT_RIGHT);
}else if(event.jhat.value & SDL_HAT_UP){
handler.onKeyDown(0x00030000 | (int(event.jhat.hat)<<8) | SDL_HAT_UP);
}else if(event.jhat.value & SDL_HAT_DOWN){
handler.onKeyDown(0x00030000 | (int(event.jhat.hat)<<8) | SDL_HAT_DOWN);
}
}
//now process GUI events.
GUIObjectHandleEvents(true);
}
if(GUIObjectRoot)
GUIObjectRoot->render();
- SDL_Flip(screen);
+ flipScreen();
SDL_Delay(30);
}
//Restore the old GUI.
GUIObjectRoot=tmp;
}
//get key name from key code
std::string InputManager::getKeyCodeName(int keyCode){
char c[64];
if(keyCode>0 && keyCode <0x1000){
//keyboard
char* s=SDL_GetKeyName((SDLKey)keyCode);
if(s!=NULL){
return s;
}else{
sprintf(c,"(Key %d)",keyCode);
return c;
}
}else if(keyCode>0x1000){
//Joystick. first set it to invalid value
sprintf(c,"(Joystick 0x%08X)",keyCode);
//check type
switch((keyCode & 0x00FF0000)>>16){
case 1:
//axis
switch(keyCode & 0xFF){
case 1:
sprintf(c,"Joystick axis %d +",(keyCode & 0x0000FF00)>>8);
break;
case 0xFF:
sprintf(c,"Joystick axis %d -",(keyCode & 0x0000FF00)>>8);
break;
}
break;
case 2:
//button
sprintf(c,"Joystick button %d",(keyCode & 0x0000FF00)>>8);
break;
case 3:
//hat
switch(keyCode & 0xFF){
case SDL_HAT_LEFT:
sprintf(c,"Joystick hat %d left",(keyCode & 0x0000FF00)>>8);
break;
case SDL_HAT_RIGHT:
sprintf(c,"Joystick hat %d right",(keyCode & 0x0000FF00)>>8);
break;
case SDL_HAT_UP:
sprintf(c,"Joystick hat %d up",(keyCode & 0x0000FF00)>>8);
break;
case SDL_HAT_DOWN:
sprintf(c,"Joystick hat %d down",(keyCode & 0x0000FF00)>>8);
break;
}
break;
}
return c;
}else{
//unknown??
return "(Not set)";
}
}
InputManager::InputManager(){
//clear the array.
for(int i=0;i<INPUTMGR_MAX;i++){
keys[i]=alternativeKeys[i]=keyFlags[i]=0;
}
}
InputManager::~InputManager(){
closeAllJoysticks();
}
int InputManager::getKeyState(int keyCode,int oldState,bool hasEvent){
int state=0;
if(keyCode>0 && keyCode<0x1000){
//keyboard
if(hasEvent){
if(event.type==SDL_KEYDOWN && event.key.keysym.sym==keyCode){
state|=0x2;
}
if(event.type==SDL_KEYUP && event.key.keysym.sym==keyCode){
state|=0x4;
}
}
if(keyCode<SDLK_LAST && SDL_GetKeyState(NULL)[keyCode]){
state|=0x1;
}
}else if(keyCode>0x1000){
//Joystick
int index=(keyCode & 0x0000FF00)>>8;
int value=keyCode & 0xFF;
int i,v;
switch((keyCode & 0x00FF0000)>>16){
case 1:
//axis
if(hasEvent){
if(event.type==SDL_JOYAXISMOTION && event.jaxis.axis==index){
if((value==1 && event.jaxis.value>3200) || (value==0xFF && event.jaxis.value<-3200)){
if((oldState & 0x1)==0) state|=0x2;
}else{
if(oldState & 0x1) state|=0x4;
}
}
}
for(i=0;i<(int)joysticks.size();i++){
v=SDL_JoystickGetAxis(joysticks[i],index);
if((value==1 && v>3200) || (value==0xFF && v<-3200)){
state|=0x1;
break;
}
}
break;
case 2:
//button
if(hasEvent){
if(event.type==SDL_JOYBUTTONDOWN && event.jbutton.button==index){
state|=0x2;
}
if(event.type==SDL_JOYBUTTONUP && event.jbutton.button==index){
state|=0x4;
}
}
for(i=0;i<(int)joysticks.size();i++){
v=SDL_JoystickGetButton(joysticks[i],index);
if(v){
state|=0x1;
break;
}
}
break;
case 3:
//hat
if(hasEvent){
if(event.type==SDL_JOYHATMOTION && event.jhat.hat==index){
if(event.jhat.value & value){
if((oldState & 0x1)==0) state|=0x2;
}else{
if(oldState & 0x1) state|=0x4;
}
}
}
for(i=0;i<(int)joysticks.size();i++){
v=SDL_JoystickGetHat(joysticks[i],index);
if(v & value){
state|=0x1;
break;
}
}
break;
}
}
return state;
}
//update the key state, according to current SDL event, etc.
void InputManager::updateState(bool hasEvent){
for(int i=0;i<INPUTMGR_MAX;i++){
keyFlags[i]=getKeyState(keys[i],keyFlags[i],hasEvent)|getKeyState(alternativeKeys[i],keyFlags[i],hasEvent);
}
}
//check if there is KeyDown event.
bool InputManager::isKeyDownEvent(InputManagerKeys key){
return keyFlags[key]&0x2;
}
//check if there is KeyUp event.
bool InputManager::isKeyUpEvent(InputManagerKeys key){
return keyFlags[key]&0x4;
}
//check if specified key is down.
bool InputManager::isKeyDown(InputManagerKeys key){
return keyFlags[key]&0x1;
}
//open all joysticks.
void InputManager::openAllJoysitcks(){
int i,m;
//First close previous joysticks.
closeAllJoysticks();
//open all joysticks.
m=SDL_NumJoysticks();
for(i=0;i<m;i++){
SDL_Joystick *j=SDL_JoystickOpen(i);
if(j==NULL){
printf("ERROR: Couldn't open Joystick %d\n",i);
}else{
joysticks.push_back(j);
}
}
}
//close all joysticks.
void InputManager::closeAllJoysticks(){
for(int i=0;i<(int)joysticks.size();i++){
SDL_JoystickClose(joysticks[i]);
}
joysticks.clear();
}
diff --git a/src/LevelEditSelect.cpp b/src/LevelEditSelect.cpp
index efd1beb..fe45dd8 100644
--- a/src/LevelEditSelect.cpp
+++ b/src/LevelEditSelect.cpp
@@ -1,702 +1,702 @@
/****************************************************************************
** 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 "LevelEditSelect.h"
#include "GameState.h"
#include "Functions.h"
#include "FileManager.h"
#include "Globals.h"
#include "Objects.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 <algorithm>
#include <string>
#include <iostream>
using namespace std;
LevelEditSelect::LevelEditSelect():LevelSelect("Map Editor"){
//The levelpack name text field.
levelpackName=new GUIObject(280,104,240,32,GUIObjectTextBox);
levelpackName->eventCallback=this;
levelpackName->visible=false;
GUIObjectRoot->childControls.push_back(levelpackName);
//Create the six buttons at the bottom of the screen.
GUIObject* obj=new GUIObject(20,480,260,32,GUIObjectButton,"New Levelpack");
obj->name="cmdNewLvlpack";
obj->eventCallback=this;
GUIObjectRoot->childControls.push_back(obj);
propertiesPack=new GUIObject(280,480,280,32,GUIObjectButton,"Pack Properties");
propertiesPack->name="cmdLvlpackProp";
propertiesPack->eventCallback=this;
GUIObjectRoot->childControls.push_back(propertiesPack);
removePack=new GUIObject(540,480,240,32,GUIObjectButton,"Remove Pack");
removePack->name="cmdRmLvlpack";
removePack->eventCallback=this;
GUIObjectRoot->childControls.push_back(removePack);
move=new GUIObject(20,540,240,32,GUIObjectButton,"Move Map");
move->name="cmdMoveMap";
move->eventCallback=this;
move->enabled=false;
GUIObjectRoot->childControls.push_back(move);
remove=new GUIObject(280,540,240,32,GUIObjectButton,"Remove Map");
remove->name="cmdRmMap";
remove->eventCallback=this;
remove->enabled=false;
GUIObjectRoot->childControls.push_back(remove);
edit=new GUIObject(540,540,240,32,GUIObjectButton,"Edit Map");
edit->name="cmdEdit";
edit->eventCallback=this;
edit->enabled=false;
GUIObjectRoot->childControls.push_back(edit);
//Set the levelEditGUIObjectRoot.
levelEditGUIObjectRoot=GUIObjectRoot;
//NOTE: We are changing the available list of levelpacks to prevent editing the main/addons levelpacks.
listPacks();
//show level list
refresh();
}
LevelEditSelect::~LevelEditSelect(){
selectedNumber=NULL;
}
void LevelEditSelect::listPacks(){
levelpackLocations.clear();
levelpacks->item.clear();
levelpacks->value=0;
vector<string> v=enumAllDirs(getUserPath(USER_DATA)+"custom/levelpacks/");
v.push_back("Levels");
for(vector<string>::iterator i=v.begin(); i!=v.end(); ++i){
levelpackLocations[*i]=getUserPath(USER_DATA)+"custom/levelpacks/"+*i;
//Check if we can find the lastlevelpack.
if(*i==getSettings()->getValue("lastlevelpack")){
levelpacks->value=i-v.begin();
packName=*i;
}
}
levelpacks->item=v;
//And call changePack since we changed the levelpack.
changePack();
}
void LevelEditSelect::changePack(){
packName=levelpacks->item[levelpacks->value];
if(packName=="Levels"){
//Clear the current levels.
levels.clear();
levels.setCurrentLevel(0);
levels.levelpackPath="";
//List the custom levels and add them one for one.
vector<string> v=enumAllFiles(getUserPath(USER_DATA)+"custom/levels/");
for(vector<string>::iterator i=v.begin(); i!=v.end(); ++i){
levels.addLevel(getUserPath(USER_DATA)+"custom/levels/"+*i);
levels.setLocked(levels.getLevelCount()-1);
}
//Disable some levelpack buttons.
propertiesPack->enabled=false;
removePack->enabled=false;
}else{
//Load the levelpack in the normal way.
if(!levels.loadLevels(levelpackLocations[packName]+"/levels.lst")){
msgBox("Can't load level pack:\n"+packName,MsgBoxOKOnly,"Error");
}
//Enable some levelpack buttons.
propertiesPack->enabled=true;
removePack->enabled=true;
}
}
void LevelEditSelect::packProperties(){
//Open a message popup.
//NOTE: We can always point GUIObjectRoot to the main gui by using levelEditGUIObjectRoot.
GUIObjectRoot=new GUIObject(100,(SCREEN_HEIGHT-320)/2,600,320,GUIObjectFrame,"Properties");
GUIObject* obj;
obj=new GUIObject(40,50,240,36,GUIObjectLabel,"Name:");
GUIObjectRoot->childControls.push_back(obj);
obj=new GUIObject(60,80,480,36,GUIObjectTextBox,packName.c_str());
obj->name="LvlpackName";
GUIObjectRoot->childControls.push_back(obj);
obj=new GUIObject(40,120,240,36,GUIObjectLabel,"Description:");
GUIObjectRoot->childControls.push_back(obj);
obj=new GUIObject(60,150,480,36,GUIObjectTextBox,levels.levelpackDescription.c_str());
obj->name="LvlpackDescription";
GUIObjectRoot->childControls.push_back(obj);
obj=new GUIObject(40,190,240,36,GUIObjectLabel,"Congratulation text:");
GUIObjectRoot->childControls.push_back(obj);
obj=new GUIObject(60,220,480,36,GUIObjectTextBox,levels.congratulationText.c_str());
obj->name="LvlpackCongratulation";
GUIObjectRoot->childControls.push_back(obj);
obj=new GUIObject(100,320-44,150,36,GUIObjectButton,"OK");
obj->name="cfgOK";
obj->eventCallback=this;
GUIObjectRoot->childControls.push_back(obj);
obj=new GUIObject(350,320-44,150,36,GUIObjectButton,"Cancel");
obj->name="cfgCancel";
obj->eventCallback=this;
GUIObjectRoot->childControls.push_back(obj);
//Dim the screen using the tempSurface.
SDL_FillRect(tempSurface,NULL,0);
SDL_SetAlpha(tempSurface,SDL_SRCALPHA,155);
SDL_BlitSurface(tempSurface,NULL,screen,NULL);
while(GUIObjectRoot){
while(SDL_PollEvent(&event)) GUIObjectHandleEvents(true);
if(GUIObjectRoot) GUIObjectRoot->render();
- SDL_Flip(screen);
+ flipScreen();
SDL_Delay(30);
}
//We're done so set the original GUIObjectRoot back.
GUIObjectRoot=levelEditGUIObjectRoot;
}
void LevelEditSelect::addLevel(){
//Open a message popup.
//NOTE: We can always point GUIObjectRoot to the main gui by using levelEditGUIObjectRoot.
GUIObjectRoot=new GUIObject(100,(SCREEN_HEIGHT-200)/2,600,200,GUIObjectFrame,"Add level");
GUIObject* obj;
obj=new GUIObject(40,80,240,36,GUIObjectLabel,"File name:");
GUIObjectRoot->childControls.push_back(obj);
char s[64];
sprintf(s,"map%02d.map",levels.getLevelCount()+1);
obj=new GUIObject(300,80,240,36,GUIObjectTextBox,s);
obj->name="LvlFile";
GUIObjectRoot->childControls.push_back(obj);
obj=new GUIObject(100,200-44,150,36,GUIObjectButton,"OK");
obj->name="cfgAddOK";
obj->eventCallback=this;
GUIObjectRoot->childControls.push_back(obj);
obj=new GUIObject(350,200-44,150,36,GUIObjectButton,"Cancel");
obj->name="cfgAddCancel";
obj->eventCallback=this;
GUIObjectRoot->childControls.push_back(obj);
//Dim the screen using the tempSurface.
SDL_FillRect(tempSurface,NULL,0);
SDL_SetAlpha(tempSurface,SDL_SRCALPHA,155);
SDL_BlitSurface(tempSurface,NULL,screen,NULL);
while(GUIObjectRoot){
while(SDL_PollEvent(&event)) GUIObjectHandleEvents(true);
if(GUIObjectRoot) GUIObjectRoot->render();
- SDL_Flip(screen);
+ flipScreen();
SDL_Delay(30);
}
//We're done so set the original GUIObjectRoot back.
GUIObjectRoot=levelEditGUIObjectRoot;
}
void LevelEditSelect::moveLevel(){
//Open a message popup.
//NOTE: We can always point GUIObjectRoot to the main gui by using levelEditGUIObjectRoot.
GUIObjectRoot=new GUIObject(100,(SCREEN_HEIGHT-200)/2,600,200,GUIObjectFrame,"Move level");
GUIObject* obj;
obj=new GUIObject(40,80,240,36,GUIObjectLabel,"Level: ");
GUIObjectRoot->childControls.push_back(obj);
obj=new GUIObject(300,80,240,36,GUIObjectTextBox,"1");
obj->name="MoveLevel";
GUIObjectRoot->childControls.push_back(obj);
obj=new GUISingleLineListBox(40,120,240,36);
obj->name="lstPlacement";
vector<string> v;
v.push_back("Before");
v.push_back("After");
v.push_back("Swap");
(dynamic_cast<GUISingleLineListBox*>(obj))->item=v;
obj->value=0;
GUIObjectRoot->childControls.push_back(obj);
obj=new GUIObject(100,200-44,150,36,GUIObjectButton,"OK");
obj->name="cfgMoveOK";
obj->eventCallback=this;
GUIObjectRoot->childControls.push_back(obj);
obj=new GUIObject(350,200-44,150,36,GUIObjectButton,"Cancel");
obj->name="cfgMoveCancel";
obj->eventCallback=this;
GUIObjectRoot->childControls.push_back(obj);
//Dim the screen using the tempSurface.
SDL_FillRect(tempSurface,NULL,0);
SDL_SetAlpha(tempSurface,SDL_SRCALPHA,155);
SDL_BlitSurface(tempSurface,NULL,screen,NULL);
while(GUIObjectRoot){
while(SDL_PollEvent(&event)) GUIObjectHandleEvents(true);
if(GUIObjectRoot) GUIObjectRoot->render();
- SDL_Flip(screen);
+ flipScreen();
SDL_Delay(30);
}
//We're done so set the original GUIObjectRoot back.
GUIObjectRoot=levelEditGUIObjectRoot;
}
void LevelEditSelect::refresh(){
int m=levels.getLevelCount();
numbers.clear();
//clear the selected level
if(selectedNumber!=NULL){
selectedNumber=NULL;
}
//Disable the level specific buttons.
move->enabled=false;
remove->enabled=false;
edit->enabled=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);
}
SDL_Rect box={(m%10)*64+80,(m/10)*64+184,0,0};
numbers[m].init("+",box);
m++; //including the "+" button
if(m>LEVELS_DISPLAYED_IN_SCREEN){
levelScrollBar->maxValue=(m-LEVELS_DISPLAYED_IN_SCREEN+9)/10;
levelScrollBar->visible=true;
}else{
levelScrollBar->maxValue=0;
levelScrollBar->visible=false;
}
levelpackDescription->caption=levels.levelpackDescription;
int width,height;
TTF_SizeText(fontText,levels.levelpackDescription.c_str(),&width,&height);
levelpackDescription->left=(800-width)/2;
}
void LevelEditSelect::selectNumber(unsigned int number,bool selected){
if(selected){
levels.setCurrentLevel(number);
setNextState(STATE_LEVEL_EDITOR);
//Pick music from the current music list.
getMusicManager()->pickMusic();
}else{
if(number==numbers.size()-1){
addLevel();
}else if(number>=0 && number<numbers.size()){
selectedNumber=&numbers[number];
//Enable the level specific buttons.
//NOTE: We check if 'remove levelpack' is enabled, if not then it's the Levels levelpack.
if(removePack->enabled)
move->enabled=true;
remove->enabled=true;
edit->enabled=true;
}
}
}
void LevelEditSelect::render(){
//Let the levelselect render.
LevelSelect::render();
}
void LevelEditSelect::renderTooltip(unsigned int number,int dy){
SDL_Color fg={0,0,0};
SDL_Surface* name;
if(number==(unsigned)levels.getLevelCount()){
//Render the name of the level.
name=TTF_RenderText_Blended(fontText,"Add level",fg);
}else{
//Render the name of the level.
name=TTF_RenderText_Blended(fontText,levels.getLevelName(number).c_str(),fg);
}
//Check if name isn't null.
if(name==NULL)
return;
//Now draw a square the size of the three texts combined.
SDL_Rect r=numbers[number].box;
r.y-=dy*64;
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[number].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);
}
//And free the surfaces.
SDL_FreeSurface(name);
}
void LevelEditSelect::GUIEventCallback_OnEvent(std::string name,GUIObject* obj,int eventType){
//NOTE: We check for the levelpack change to enable/disable some levelpack buttons.
if(name=="cmdLvlPack"){
//Set the name of the levelpack.
packName=((GUISingleLineListBox*)obj)->item[obj->value];
//Check if it matches the Levels levelpack.
if(((GUISingleLineListBox*)obj)->item[obj->value]=="Levels"){
//Disable some levelpack buttons.
propertiesPack->enabled=false;
removePack->enabled=false;
getSettings()->setValue("lastlevelpack","Levels");
//We call changepack and return to prevent the LevelSelect to undo what we did.
changePack();
refresh();
return;
}else{
//Enable some levelpack buttons.
propertiesPack->enabled=true;
removePack->enabled=true;
}
}
//Let the level select handle his GUI events.
LevelSelect::GUIEventCallback_OnEvent(name,obj,eventType);
//Check for the edit button.
if(name=="cmdNewLvlpack"){
//Clear the current pack.
packName.clear();
levels.clear();
//Create a new pack.
packProperties();
}else if(name=="cmdLvlpackProp"){
//Show the pack properties.
packProperties();
}else if(name=="cmdRmLvlpack"){
//Show an "are you sure" message.
if(msgBox("Are you sure?",MsgBoxYesNo,"Remove prompt")==MsgBoxYes){
//Remove the directory.
if(!removeDirectory(levelpackLocations[packName].c_str())){
cerr<<"ERROR: Unable to remove levelpack directory "<<levelpackLocations[packName]<<endl;
}
//Remove it from the vector (levelpack list).
vector<string>::iterator it;
it=find(levelpacks->item.begin(),levelpacks->item.end(),packName);
if(it!=levelpacks->item.end()){
levelpacks->item.erase(it);
}
//And call changePack.
levelpacks->value=levelpacks->item.size()-1;
changePack();
refresh();
}
}else if(name=="cmdMoveMap"){
if(selectedNumber!=NULL){
moveLevel();
}
}else if(name=="cmdRmMap"){
if(selectedNumber!=NULL){
if(packName!="Levels"){
if(!removeFile((levelpackLocations[packName]+"/"+levels.getLevel(selectedNumber->getNumber())->file).c_str())){
cerr<<"ERROR: Unable to remove level "<<(levelpackLocations[packName]+"/"+levels.getLevel(selectedNumber->getNumber())->file).c_str()<<endl;
}
levels.removeLevel(selectedNumber->getNumber());
levels.saveLevels(levelpackLocations[packName]+"/levels.lst");
}else{
//This is the levels levelpack so we just remove the file.
if(!removeFile(levels.getLevel(selectedNumber->getNumber())->file.c_str())){
cerr<<"ERROR: Unable to remove level "<<(levelpackLocations[packName]+"/"+levels.getLevel(selectedNumber->getNumber())->file).c_str()<<endl;
}
changePack();
}
//And refresh the selection screen.
refresh();
}
}else if(name=="cmdEdit"){
if(selectedNumber!=NULL){
levels.setCurrentLevel(selectedNumber->getNumber());
setNextState(STATE_LEVEL_EDITOR);
//Pick music from the current music list.
getMusicManager()->pickMusic();
}
}
//Check for levelpack properties events.
if(name=="cfgOK"){
//Now loop throught the children of the GUIObjectRoot in search of the fields.
for(unsigned int i=0;i<GUIObjectRoot->childControls.size();i++){
if(GUIObjectRoot->childControls[i]->name=="LvlpackName"){
//Check if the name changed.
if(packName!=GUIObjectRoot->childControls[i]->caption){
//Delete the old one.
if(!packName.empty()){
if(!renameDirectory((getUserPath(USER_DATA)+"custom/levelpacks/"+packName).c_str(),(getUserPath(USER_DATA)+"custom/levelpacks/"+GUIObjectRoot->childControls[i]->caption).c_str())){
cerr<<"ERROR: Unable to move levelpack directory "<<(getUserPath(USER_DATA)+"custom/levelpacks/"+packName)<<" to "<<(getUserPath(USER_DATA)+"custom/levelpacks/"+GUIObjectRoot->childControls[i]->caption)<<endl;
}
//Also change the levelpack location.
map<string,string>::iterator it;
it=levelpackLocations.find(packName);
if(it!=levelpackLocations.end()){
levelpackLocations.erase(it);
}
//And the levelpack list.
vector<string>::iterator it1;
it1=find(levelpacks->item.begin(),levelpacks->item.end(),packName);
if(it1!=levelpacks->item.end()){
levelpacks->item.erase(it1);
if((unsigned)levelpacks->value>levelpacks->item.size())
levelpacks->value=levelpacks->item.size()-1;
}
//Also add the levelpack location
levelpackLocations[GUIObjectRoot->childControls[i]->caption]=(getUserPath(USER_DATA)+"custom/levelpacks/"+GUIObjectRoot->childControls[i]->caption);
levelpacks->item.push_back(GUIObjectRoot->childControls[i]->caption);
levelpacks->value=levelpacks->item.size()-1;
}else{
if(!createDirectory((getUserPath(USER_DATA)+"custom/levelpacks/"+GUIObjectRoot->childControls[i]->caption).c_str())){
cerr<<"ERROR: Unable to create levelpack directory "<<(getUserPath(USER_DATA)+"custom/levelpacks/"+GUIObjectRoot->childControls[i]->caption)<<endl;
}
if(!createFile((getUserPath(USER_DATA)+"custom/levelpacks/"+GUIObjectRoot->childControls[i]->caption+"/levels.lst").c_str())){
cerr<<"ERROR: Unable to create levelpack file "<<(getUserPath(USER_DATA)+"custom/levelpacks/"+GUIObjectRoot->childControls[i]->caption+"/levels.lst")<<endl;
}
//Also add the levelpack location.
levelpackLocations[GUIObjectRoot->childControls[i]->caption]=(getUserPath(USER_DATA)+"custom/levelpacks/"+GUIObjectRoot->childControls[i]->caption);
levelpacks->item.push_back(GUIObjectRoot->childControls[i]->caption);
levelpacks->value=levelpacks->item.size()-1;
}
//And set the new name.
packName=GUIObjectRoot->childControls[i]->caption;
changePack();
}
}
if(GUIObjectRoot->childControls[i]->name=="LvlpackDescription"){
levels.levelpackDescription=GUIObjectRoot->childControls[i]->caption;
}
if(GUIObjectRoot->childControls[i]->name=="LvlpackCongratulation"){
levels.congratulationText=GUIObjectRoot->childControls[i]->caption;
}
}
//Refresh the leveleditselect to show the correct information.
refresh();
//Save the configuration.
levels.saveLevels(getUserPath(USER_DATA)+"custom/levelpacks/"+packName+"/levels.lst");
getSettings()->setValue("lastlevelpack",packName);
//Clear the gui.
if(GUIObjectRoot){
delete GUIObjectRoot;
GUIObjectRoot=NULL;
}
}else if(name=="cfgCancel"){
//Check if packName is empty, if so it was a new levelpack and we need to revert to an existing one.
if(packName.empty()){
packName=levelpacks->item[levelpacks->value];
changePack();
}
//Clear the gui.
if(GUIObjectRoot){
delete GUIObjectRoot;
GUIObjectRoot=NULL;
}
}
//Check for add level events.
if(name=="cfgAddOK"){
//Check if the file name isn't null.
//Now loop throught the children of the GUIObjectRoot in search of the fields.
for(unsigned int i=0;i<GUIObjectRoot->childControls.size();i++){
if(GUIObjectRoot->childControls[i]->name=="LvlFile"){
if(GUIObjectRoot->childControls[i]->caption.empty()){
msgBox("No file name given for the new level.",MsgBoxOKOnly,"Missing file name");
return;
}else{
string tmp_caption = GUIObjectRoot->childControls[i]->caption;
//Replace all spaces with a underline.
size_t j;
for(;(j=tmp_caption.find(" "))!=string::npos;){
tmp_caption.replace(j,1,"_");
}
//If there isn't ".map" extension add it.
size_t found=tmp_caption.find_first_of(".");
if(found!=string::npos)
tmp_caption.replace(tmp_caption.begin()+found+1,tmp_caption.end(),"map");
else if (tmp_caption.substr(found+1)!="map")
tmp_caption.append(".map");
/* Create path and file in it */
string path=(levelpackLocations[packName]+"/"+tmp_caption);
if(packName=="Levels"){
path=(getUserPath(USER_DATA)+"/custom/levels/"+tmp_caption);
}
//First check if the file doesn't exist already.
FILE* f;
f=fopen(path.c_str(),"rb");
//Check if it exists.
if(f){
//Close the file.
fclose(f);
//Let the currentState render once to prevent multiple GUI overlapping and prevent the screen from going black.
currentState->render();
levelEditGUIObjectRoot->render();
//Notify the user.
msgBox(("The file "+tmp_caption+" already exists.").c_str(),MsgBoxOKOnly,"Error");
return;
}
if(!createFile(path.c_str())){
cerr<<"ERROR: Unable to create level file "<<path<<endl;
}
levels.addLevel(path);
if(packName!="Levels")
levels.saveLevels(getUserPath(USER_DATA)+"custom/levelpacks/"+packName+"/levels.lst");
refresh();
//Clear the gui.
if(GUIObjectRoot){
delete GUIObjectRoot;
GUIObjectRoot=NULL;
return;
}
}
}
}
}else if(name=="cfgAddCancel"){
//Clear the gui.
if(GUIObjectRoot){
delete GUIObjectRoot;
GUIObjectRoot=NULL;
}
}
//Check for move level events.
if(name=="cfgMoveOK"){
//Check if the entered level number is valid.
//Now loop throught the children of the GUIObjectRoot in search of the fields.
int level=0;
int placement=0;
for(unsigned int i=0;i<GUIObjectRoot->childControls.size();i++){
if(GUIObjectRoot->childControls[i]->name=="MoveLevel"){
level=atoi(GUIObjectRoot->childControls[i]->caption.c_str());
if(level<=0 || level>levels.getLevelCount()){
msgBox("The entered level number isn't valid!",MsgBoxOKOnly,"Illegal number");
return;
}
}
if(GUIObjectRoot->childControls[i]->name=="lstPlacement"){
placement=GUIObjectRoot->childControls[i]->value;
}
}
//Now we execute the swap/move.
//Check for the place before.
if(placement==0){
//We place the selected level before the entered level.
levels.moveLevel(selectedNumber->getNumber(),level-1);
}else if(placement==1){
//We place the selected level after the entered level.
if(level<selectedNumber->getNumber())
levels.moveLevel(selectedNumber->getNumber(),level);
else
levels.moveLevel(selectedNumber->getNumber(),level+1);
}else if(placement==2){
//We swap the selected level with the entered level.
levels.swapLevel(selectedNumber->getNumber(),level-1);
}
//And save the change.
if(packName!="Levels")
levels.saveLevels(getUserPath(USER_DATA)+"custom/levelpacks/"+packName+"/levels.lst");
refresh();
//Clear the gui.
if(GUIObjectRoot){
delete GUIObjectRoot;
GUIObjectRoot=NULL;
}
}else if(name=="cfgMoveCancel"){
//Clear the gui.
if(GUIObjectRoot){
delete GUIObjectRoot;
GUIObjectRoot=NULL;
}
}
}
diff --git a/src/LevelEditor.cpp b/src/LevelEditor.cpp
index e4fc741..4e9e274 100644
--- a/src/LevelEditor.cpp
+++ b/src/LevelEditor.cpp
@@ -1,2703 +1,2703 @@
/****************************************************************************
** 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 "Levels.h"
#include "LevelEditor.h"
#include "TreeStorageNode.h"
#include "POASerializer.h"
#include "GUIListBox.h"
#include "GUITextArea.h"
#include "InputManager.h"
#include <fstream>
#include <iostream>
#include <vector>
#include <algorithm>
#include <string.h>
#include <stdlib.h>
#include <stdio.h>
#ifdef WIN32
#include <windows.h>
#include <shlobj.h>
#else
#include <strings.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <unistd.h>
#include <dirent.h>
#endif
using namespace std;
static int levelTime,levelRecordings;
static GUIObject *levelTimeProperty,*levelRecordingsProperty;
/////////////////MovingPosition////////////////////////////
MovingPosition::MovingPosition(int x,int y,int time){
this->x=x;
this->y=y;
this->time=time;
}
MovingPosition::~MovingPosition(){}
void MovingPosition::updatePosition(int x,int y){
this->x=x;
this->y=y;
}
/////////////////LEVEL EDITOR//////////////////////////////
LevelEditor::LevelEditor():Game(true){
//Get the target time and recordings.
levelTime=levels.getLevel()->targetTime;
levelRecordings=levels.getLevel()->targetRecordings;
//This will set some default settings.
reset();
//The level is loaded by the game, so do postLoad.
postLoad();
//Load the toolbar.
toolbar=loadImage(getDataPath()+"gfx/menu/toolbar.png");
SDL_Rect tmp={170,555,460,50};
toolbarRect=tmp;
//Load the selectionMark.
selectionMark=loadImage(getDataPath()+"gfx/menu/selection.png");
//Load the movingMark.
movingMark=loadImage(getDataPath()+"gfx/menu/moving.png");
//Create the semi transparent surface.
placement=SDL_CreateRGBSurface(SDL_SWSURFACE|SDL_SRCALPHA,800,600,32,0x000000FF,0x0000FF00,0x00FF0000,0);
SDL_SetColorKey(placement,SDL_SRCCOLORKEY|SDL_RLEACCEL,SDL_MapRGB(placement->format,255,0,255));
SDL_SetAlpha(placement,SDL_SRCALPHA,125);
}
LevelEditor::~LevelEditor(){
//Loop through the levelObjects and delete them.
for(unsigned int i=0;i<levelObjects.size();i++)
delete levelObjects[i];
levelObjects.clear();
selection.clear();
//Free the placement surface.
SDL_FreeSurface(placement);
//Reset the camera.
camera.x=0;
camera.y=0;
}
void LevelEditor::reset(){
//Set some default values.
playMode=false;
tool=ADD;
currentType=0;
pressedShift=false;
pressedLeftMouse=false;
dragging=false;
selectionDrag=false;
dragCenter=NULL;
camera.x=0;
camera.y=0;
cameraXvel=0;
cameraYvel=0;
objectProperty=NULL;
secondObjectProperty=NULL;
configuredObject=NULL;
linking=false;
linkingTrigger=NULL;
currentId=0;
movingBlock=NULL;
moving=false;
movingSpeed=10;
tooltip=-1;
//Set the player and shadow to their starting position.
player.setPosition(player.fx,player.fy);
shadow.setPosition(shadow.fx,shadow.fy);
selection.clear();
clipboard.clear();
triggers.clear();
movingBlocks.clear();
}
void LevelEditor::loadLevelFromNode(TreeStorageNode* obj, const std::string& fileName){
//call the method of base class.
Game::loadLevelFromNode(obj,fileName);
//now do our own stuff.
string s=editorData["time"];
if(s.empty() || !(s[0]>='0' && s[0]<='9')){
levelTime=-1;
}else{
levelTime=atoi(s.c_str());
}
s=editorData["recordings"];
if(s.empty() || !(s[0]>='0' && s[0]<='9')){
levelRecordings=-1;
}else{
levelRecordings=atoi(s.c_str());
}
}
void LevelEditor::saveLevel(string fileName){
//Create the output stream and check if it starts.
std::ofstream save(fileName.c_str());
if(!save) return;
//The dimensions of the level.
int maxX=0;
int maxY=0;
//The storageNode to put the level data in before writing it away.
TreeStorageNode node;
char s[64];
//The name of the level.
if(!levelName.empty())
node.attributes["name"].push_back(levelName);
//The leveltheme.
if(!levelTheme.empty())
node.attributes["theme"].push_back(levelTheme);
//target time and recordings.
{
char c[32];
if(levelTime>=0){
sprintf(c,"%d",levelTime);
node.attributes["time"].push_back(c);
}
if(levelRecordings>=0){
sprintf(c,"%d",levelRecordings);
node.attributes["recordings"].push_back(c);
}
}
//The width of the level.
maxX=LEVEL_WIDTH;
sprintf(s,"%d",maxX);
node.attributes["size"].push_back(s);
//The height of the level.
maxY=LEVEL_HEIGHT;
sprintf(s,"%d",maxY);
node.attributes["size"].push_back(s);
//Loop through the gameObjects and save them.
for(int o=0;o<(signed)levelObjects.size();o++){
int objectType=levelObjects[o]->type;
//Check if it's a legal gameObject type.
if(objectType>=0 && objectType<TYPE_MAX){
TreeStorageNode* obj1=new TreeStorageNode;
node.subNodes.push_back(obj1);
//It's a tile so name the node tile.
obj1->name="tile";
//Write away the type of the gameObject.
sprintf(s,"%d",objectType);
obj1->value.push_back(blockName[objectType]);
//Get the box for the location of the gameObject.
SDL_Rect box=levelObjects[o]->getBox(BoxType_Base);
//Put the location in the storageNode.
sprintf(s,"%d",box.x);
obj1->value.push_back(s);
sprintf(s,"%d",box.y);
obj1->value.push_back(s);
//Loop through the editor data and save it also.
vector<pair<string,string> > obj;
levelObjects[o]->getEditorData(obj);
for(unsigned int i=0;i<obj.size();i++){
if((!obj[i].first.empty()) && (!obj[i].second.empty())){
obj1->attributes[obj[i].first].push_back(obj[i].second);
}
}
}
}
//Create a POASerializer and write away the level node.
POASerializer objSerializer;
objSerializer.writeNode(&node,save,true,true);
}
///////////////EVENT///////////////////
void LevelEditor::handleEvents(){
//Check if we need to quit, if so we enter the exit state.
if(event.type==SDL_QUIT){
setNextState(STATE_EXIT);
}
//If playing/testing we should the game handle the events.
if(playMode){
Game::handleEvents();
//Also check if we should exit the playMode.
if(inputMgr.isKeyDownEvent(INPUTMGR_ESCAPE)){
//Reset the game and disable playMode.
Game::reset(true);
playMode=false;
camera.x=cameraSave.x;
camera.y=cameraSave.y;
//NOTE: To prevent the mouse to still "be pressed" we set it to false.
pressedLeftMouse=false;
}
}else{
//Also check if we should exit the editor.
if(inputMgr.isKeyDownEvent(INPUTMGR_ESCAPE)){
//Before we quit ask a make sure question.
if(msgBox("Are you sure you want to quit?",MsgBoxYesNo,"Quit prompt")==MsgBoxYes){
//We exit the level editor.
if(GUIObjectRoot){
delete GUIObjectRoot;
GUIObjectRoot=NULL;
}
setNextState(STATE_LEVEL_EDIT_SELECT);
//Play the menu music again.
getMusicManager()->playMusic("menu");
}
}
//Also check if we should exit the editor.
if(inputMgr.isKeyDownEvent(INPUTMGR_SHIFT)){
pressedShift=true;
}
if(inputMgr.isKeyUpEvent(INPUTMGR_SHIFT)){
pressedShift=false;
}
//Check if delete is pressed.
if(event.type==SDL_KEYDOWN && event.key.keysym.sym==SDLK_DELETE){
if(!selection.empty()){
//Loop through the selected game objects.
while(!selection.empty()){
//Remove the objects in the selection.
removeObject(selection[0]);
}
//And clear the selection vector.
selection.clear();
dragCenter=NULL;
selectionDrag=false;
}
}
//Check for copy (Ctrl+c) or cut (Ctrl+x).
if(event.type==SDL_KEYDOWN && (event.key.keysym.sym==SDLK_c || event.key.keysym.sym==SDLK_x) && (event.key.keysym.mod & KMOD_CTRL)){
//Clear the current clipboard.
clipboard.clear();
//Check if the selection isn't empty.
if(!selection.empty()){
//Loop through the selection to find the left-top block.
int x=selection[0]->getBox().x;
int y=selection[0]->getBox().y;
for(unsigned int o=1; o<selection.size(); o++){
if(selection[o]->getBox().x<x || selection[o]->getBox().y<y){
x=selection[o]->getBox().x;
y=selection[o]->getBox().y;
}
}
//Loop through the selection for the actual copying.
for(unsigned int o=0; o<selection.size(); o++){
//Get the editor data of the object.
vector<pair<string,string> > obj;
selection[o]->getEditorData(obj);
//Loop through the editor data and convert it.
map<string,string> objMap;
for(unsigned int i=0;i<obj.size();i++){
objMap[obj[i].first]=obj[i].second;
}
//Add some entries to the map.
char s[64];
sprintf(s,"%d",selection[o]->getBox().x-x);
objMap["x"]=s;
sprintf(s,"%d",selection[o]->getBox().y-y);
objMap["y"]=s;
sprintf(s,"%d",selection[o]->type);
objMap["type"]=s;
//Overwrite the id to prevent triggers, portals, buttons, movingblocks, etc. from malfunctioning.
//We give an empty string as id, which is invalid and thus suitable.
objMap["id"]="";
//Do the same for destination if the type is portal.
if(selection[o]->type==TYPE_PORTAL){
objMap["destination"]="";
}
//And add the map to the clipboard vector.
clipboard.push_back(objMap);
if(event.key.keysym.sym==SDLK_x){
//Cutting means deleting the game object.
removeObject(selection[o]);
o--;
}
}
//Only clear the selection when Ctrl+x;
if(event.key.keysym.sym==SDLK_x){
selection.clear();
dragCenter=NULL;
selectionDrag=false;
}
}
}
//Check for paste (Ctrl+v).
if(event.type==SDL_KEYDOWN && event.key.keysym.sym==SDLK_v && (event.key.keysym.mod & KMOD_CTRL)){
//First make sure that the clipboard isn't empty.
if(!clipboard.empty()){
//Clear the current selection.
selection.clear();
//Get the current mouse location.
int x,y;
SDL_GetMouseState(&x,&y);
x+=camera.x;
y+=camera.y;
//Apply snap to grid.
if(!pressedShift){
snapToGrid(&x,&y);
}else{
x-=25;
y-=25;
}
//Integers containing the diff of the x that occurs when placing a block outside the level size on the top or left.
//We use it to compensate the corrupted x and y locations of the other clipboard blocks.
int diffX=0;
int diffY=0;
//Loop through the clipboard.
for(unsigned int o=0;o<clipboard.size();o++){
Block* block=new Block(0,0,atoi(clipboard[o]["type"].c_str()),this);
block->setPosition(atoi(clipboard[o]["x"].c_str())+x+diffX,atoi(clipboard[o]["y"].c_str())+y+diffY);
block->setEditorData(clipboard[o]);
if(block->getBox().x<0){
//A block on the left side of the level, meaning we need to shift everything.
//First calc the difference.
diffX+=(0-(block->getBox().x));
}
if(block->getBox().y<0){
//A block on the left side of the level, meaning we need to shift everything.
//First calc the difference.
diffY+=(0-(block->getBox().y));
}
//And add the object using the addObject method.
addObject(block);
//Also add the block to the selection.
selection.push_back(block);
}
}
}
//Check if the return button is pressed.
//If so run the configure tool.
if(event.type==SDL_KEYDOWN && event.key.keysym.sym==SDLK_RETURN){
//Get the current mouse location.
int x,y;
SDL_GetMouseState(&x,&y);
//Create the rectangle.
SDL_Rect mouse={x+camera.x,y+camera.y,0,0};
//Loop through the selected game objects.
for(unsigned int o=0; o<levelObjects.size(); o++){
//Check for collision.
if(checkCollision(mouse,levelObjects[o]->getBox())){
tool=CONFIGURE;
//Invoke the onEnterObject.
onEnterObject(levelObjects[o]);
//Break out of the for loop.
break;
}
}
}
//Check for the arrow keys, used for moving the camera when playMode=false.
cameraXvel=0;
cameraYvel=0;
if(inputMgr.isKeyDown(INPUTMGR_RIGHT)){
if(pressedShift){
cameraXvel+=10;
}else{
cameraXvel+=5;
}
}
if(inputMgr.isKeyDown(INPUTMGR_LEFT)){
if(pressedShift){
cameraXvel-=10;
}else{
cameraXvel-=5;
}
}
if(inputMgr.isKeyDown(INPUTMGR_UP)){
if(pressedShift){
cameraYvel-=10;
}else{
cameraYvel-=5;
}
}
if(inputMgr.isKeyDown(INPUTMGR_DOWN)){
if(pressedShift){
cameraYvel+=10;
}else{
cameraYvel+=5;
}
}
//Check if the left mouse button is pressed/holded.
if(event.type==SDL_MOUSEBUTTONDOWN && event.button.button==SDL_BUTTON_LEFT){
pressedLeftMouse=true;
}
if(event.type==SDL_MOUSEBUTTONUP && event.button.button==SDL_BUTTON_LEFT){
pressedLeftMouse=false;
//We also need to check if dragging is true.
if(dragging){
//Set dragging false and call the onDrop event.
dragging=false;
int x,y;
SDL_GetMouseState(&x,&y);
//We call the drop event.
onDrop(x+camera.x,y+camera.y);
}
}
//Check if the mouse is dragging.
if(pressedLeftMouse && event.type==SDL_MOUSEMOTION){
if(abs(event.motion.xrel)+abs(event.motion.yrel)>=2){
//Check if this is the start of the dragging.
if(!dragging){
//The mouse is moved enough so let's set dragging true.
dragging=true;
//Get the current mouse location.
int x,y;
SDL_GetMouseState(&x,&y);
//We call the dragStart event.
onDragStart(x+camera.x,y+camera.y);
}else{
//Dragging was already true meaning we call onDrag() instead of onDragStart().
onDrag(event.motion.xrel,event.motion.yrel);
}
}
}
//Check if we scroll up, meaning the currentType++;
if((event.type==SDL_MOUSEBUTTONDOWN && event.button.button==SDL_BUTTON_WHEELUP) || inputMgr.isKeyDownEvent(INPUTMGR_NEXT)){
//Only change the current type when using the add tool.
if(tool==ADD){
currentType++;
if(currentType>=EDITOR_ORDER_MAX){
currentType=0;
}
}
//When in configure mode.
if(tool==CONFIGURE){
movingSpeed++;
//The movingspeed is capped at 100.
if(movingSpeed>100){
movingSpeed=100;
}
}
}
//Check if we scroll down, meaning the currentType--;
if((event.type==SDL_MOUSEBUTTONDOWN && event.button.button==SDL_BUTTON_WHEELDOWN) || inputMgr.isKeyDownEvent(INPUTMGR_PREVIOUS)){
//Only change the current type when using the add tool.
if(tool==ADD){
currentType--;
if(currentType<0){
currentType=EDITOR_ORDER_MAX-1;
}
}
//When in configure mode.
if(tool==CONFIGURE){
movingSpeed--;
if(movingSpeed<=0){
movingSpeed=1;
}
}
}
//Check if we should enter playMode.
if(event.type==SDL_KEYDOWN && event.key.keysym.sym==SDLK_p){
playMode=true;
cameraSave.x=camera.x;
cameraSave.y=camera.y;
}
//Check for tool shortcuts.
if(event.type==SDL_KEYDOWN && event.key.keysym.sym==SDLK_a){
tool=ADD;
}
if(event.type==SDL_KEYDOWN && event.key.keysym.sym==SDLK_s){
tool=SELECT;
}
if(event.type==SDL_KEYDOWN && event.key.keysym.sym==SDLK_d){
//We clear the selection since that can't be used in the deletion tool.
selection.clear();
tool=REMOVE;
}
if(event.type==SDL_KEYDOWN && event.key.keysym.sym==SDLK_w){
tool=CONFIGURE;
}
//Check for certain events.
//Get the current mouse location.
int x,y;
SDL_GetMouseState(&x,&y);
//Create the rectangle.
SDL_Rect mouse={x,y,0,0};
//First make sure the mouse isn't above the toolbar.
if(checkCollision(mouse,toolbarRect)==false){
//We didn't hit the toolbar so convert the mouse location to ingame location.
mouse.x+=camera.x;
mouse.y+=camera.y;
//Boolean if there's a click event fired.
bool clickEvent=false;
//Check if a mouse button is pressed.
if(event.type==SDL_MOUSEBUTTONDOWN){
//Loop through the objects to check collision.
for(unsigned int o=0; o<levelObjects.size(); o++){
if(checkCollision(levelObjects[o]->getBox(),mouse)==true){
//We have collision meaning that the mouse is above an object.
std::vector<GameObject*>::iterator it;
it=find(selection.begin(),selection.end(),levelObjects[o]);
//Set event true since there's a click event.
clickEvent=true;
//Check if the clicked object is in the selection or not.
bool isSelected=(it!=selection.end());
if(event.button.button==SDL_BUTTON_LEFT){
onClickObject(levelObjects[o],isSelected);
}else if(event.button.button==SDL_BUTTON_RIGHT){
onRightClickObject(levelObjects[o],isSelected);
}
}
}
}
//If event is false then we clicked on void.
if(!clickEvent){
if(event.type==SDL_MOUSEBUTTONDOWN){
if(event.button.button==SDL_BUTTON_LEFT){
//Left mouse button on void.
onClickVoid(mouse.x,mouse.y);
}else if(event.button.button==SDL_BUTTON_RIGHT && tool==CONFIGURE){
//Stop linking.
linking=false;
linkingTrigger=NULL;
//Write the path to the moving block.
if(moving){
std::map<std::string,std::string> editorData;
char s[64], s0[64];
sprintf(s,"%d",int(movingBlocks[movingBlock].size()));
editorData["MovingPosCount"]=s;
//Loop through the positions.
for(unsigned int o=0;o<movingBlocks[movingBlock].size();o++){
sprintf(s0+1,"%d",o);
sprintf(s,"%d",movingBlocks[movingBlock][o].x);
s0[0]='x';
editorData[s0]=s;
sprintf(s,"%d",movingBlocks[movingBlock][o].y);
s0[0]='y';
editorData[s0]=s;
sprintf(s,"%d",movingBlocks[movingBlock][o].time);
s0[0]='t';
editorData[s0]=s;
}
movingBlock->setEditorData(editorData);
//Stop moving.
moving=false;
movingBlock=NULL;
}
}
}
}
}
//Check for backspace when moving to remove a movingposition.
if(moving && event.type==SDL_KEYDOWN && event.key.keysym.sym==SDLK_BACKSPACE){
if(movingBlocks[movingBlock].size()>0){
movingBlocks[movingBlock].pop_back();
}
}
//Check for the tab key, level settings.
if(inputMgr.isKeyDownEvent(INPUTMGR_TAB)){
//Show the levelSettings.
levelSettings();
}
//Check if we should a new level. (Ctrl+n)
if(event.type==SDL_KEYDOWN && event.key.keysym.sym==SDLK_n && (event.key.keysym.mod & KMOD_CTRL)){
reset();
loadLevel(getDataPath()+"misc/Empty.map");
}
//Check if we should save the level (Ctrl+s) or save levelpack (Ctrl+Shift+s).
if(event.type==SDL_KEYDOWN && event.key.keysym.sym==SDLK_s && (event.key.keysym.mod & KMOD_CTRL)){
saveLevel(levelFile);
//And give feedback to the user.
if(levelName.empty())
msgBox("Level \""+fileNameFromPath(levelFile)+"\" saved",MsgBoxOKOnly,"Saved");
else
msgBox("Level \""+levelName+"\" saved",MsgBoxOKOnly,"Saved");
}
}
}
void LevelEditor::levelSettings(){
//It isn't so open a popup asking for a name.
//First delete any existing gui.
if(GUIObjectRoot){
delete GUIObjectRoot;
GUIObjectRoot=NULL;
}
GUIObjectRoot=new GUIObject(100,(SCREEN_HEIGHT-300)/2,600,300,GUIObjectFrame,"Level settings");
GUIObject* obj;
//NOTE: We reuse the objectProperty and secondProperty.
obj=new GUIObject(40,50,240,36,GUIObjectLabel,"Name:");
GUIObjectRoot->childControls.push_back(obj);
obj=new GUIObject(140,50,410,36,GUIObjectTextBox,levelName.c_str());
objectProperty=obj;
GUIObjectRoot->childControls.push_back(obj);
obj=new GUIObject(40,100,240,36,GUIObjectLabel,"Theme:");
GUIObjectRoot->childControls.push_back(obj);
obj=new GUIObject(140,100,410,36,GUIObjectTextBox,levelTheme.c_str());
secondObjectProperty=obj;
GUIObjectRoot->childControls.push_back(obj);
//target time and recordings.
{
char c[32];
if(levelTime>=0){
sprintf(c,"%-.2f",levelTime/40.0f);
}else{
c[0]='\0';
}
obj=new GUIObject(40,150,240,36,GUIObjectLabel,"Target time (s):");
GUIObjectRoot->childControls.push_back(obj);
obj=new GUIObject(290,150,260,36,GUIObjectTextBox,c);
levelTimeProperty=obj;
GUIObjectRoot->childControls.push_back(obj);
if(levelRecordings>=0){
sprintf(c,"%d",levelRecordings);
}else{
c[0]='\0';
}
obj=new GUIObject(40,200,240,36,GUIObjectLabel,"Target recordings:");
GUIObjectRoot->childControls.push_back(obj);
obj=new GUIObject(290,200,260,36,GUIObjectTextBox,c);
levelRecordingsProperty=obj;
GUIObjectRoot->childControls.push_back(obj);
}
//Ok and cancel buttons.
obj=new GUIObject(100,300-44,150,36,GUIObjectButton,"OK");
obj->name="lvlSettingsOK";
obj->eventCallback=this;
GUIObjectRoot->childControls.push_back(obj);
obj=new GUIObject(350,300-44,150,36,GUIObjectButton,"Cancel");
obj->name="lvlSettingsCancel";
obj->eventCallback=this;
GUIObjectRoot->childControls.push_back(obj);
//Now we keep rendering and updating the GUI.
SDL_FillRect(tempSurface,NULL,0);
SDL_SetAlpha(tempSurface,SDL_SRCALPHA,155);
SDL_BlitSurface(tempSurface,NULL,screen,NULL);
while(GUIObjectRoot){
while(SDL_PollEvent(&event))
GUIObjectHandleEvents(true);
if(GUIObjectRoot)
GUIObjectRoot->render();
- SDL_Flip(screen);
+ flipScreen();
SDL_Delay(30);
}
}
void LevelEditor::postLoad(){
//We need to find the triggers.
for(unsigned int o=0;o<levelObjects.size();o++){
//Get the editor data.
vector<pair<string,string> > objMap;
levelObjects[o]->getEditorData(objMap);
//Check for the highest id.
for(unsigned int i=0;i<objMap.size();i++){
if(objMap[i].first=="id"){
unsigned int id=atoi(objMap[i].second.c_str());
if(id>=currentId){
currentId=id+1;
}
}
}
switch(levelObjects[o]->type){
case TYPE_BUTTON:
case TYPE_SWITCH:
{
//Add the object to the triggers vector.
vector<GameObject*> linked;
triggers[levelObjects[o]]=linked;
//Now loop through the levelObjects in search for objects with the same id.
for(unsigned int oo=0;oo<levelObjects.size();oo++){
//Check if it isn't the same object but has the same id.
if(o!=oo && (dynamic_cast<Block*>(levelObjects[o]))->id==(dynamic_cast<Block*>(levelObjects[oo]))->id){
//Add the object to the link vector of the trigger.
triggers[levelObjects[o]].push_back(levelObjects[oo]);
}
}
break;
}
case TYPE_PORTAL:
{
//Add the object to the triggers vector.
vector<GameObject*> linked;
triggers[levelObjects[o]]=linked;
//If the destination is empty we return.
if((dynamic_cast<Block*>(levelObjects[o]))->destination.empty()){
return;
}
//Now loop through the levelObjects in search for objects with the same id as destination.
for(unsigned int oo=0;oo<levelObjects.size();oo++){
//Check if it isn't the same object but has the same id.
if(o!=oo && (dynamic_cast<Block*>(levelObjects[o]))->destination==(dynamic_cast<Block*>(levelObjects[oo]))->id){
//Add the object to the link vector of the trigger.
triggers[levelObjects[o]].push_back(levelObjects[oo]);
}
}
break;
}
case TYPE_MOVING_BLOCK:
case TYPE_MOVING_SHADOW_BLOCK:
case TYPE_MOVING_SPIKES:
{
//Add the object to the movingBlocks vector.
vector<MovingPosition> positions;
movingBlocks[levelObjects[o]]=positions;
//Get the number of entries of the editor data.
int m=objMap.size();
//Check if the editor data isn't empty.
if(m>0){
//Integer containing the positions.
int pos=0;
int currentPos=0;
//Get the number of movingpositions.
pos=atoi(objMap[1].second.c_str());
while(currentPos<pos){
int x=atoi(objMap[currentPos*3+4].second.c_str());
int y=atoi(objMap[currentPos*3+5].second.c_str());
int t=atoi(objMap[currentPos*3+6].second.c_str());
//Create a new movingPosition.
MovingPosition position(x,y,t);
movingBlocks[levelObjects[o]].push_back(position);
//Increase currentPos by one.
currentPos++;
}
}
break;
}
default:
break;
}
}
}
void LevelEditor::snapToGrid(int* x,int* y){
//Check if the x location is negative.
if(*x<0){
*x=-((abs(*x-50)/50)*50);
}else{
*x=(*x/50)*50;
}
//Now the y location.
if(*y<0){
*y=-((abs(*y-50)/50)*50);
}else{
*y=(*y/50)*50;
}
}
void LevelEditor::onClickObject(GameObject* obj,bool selected){
switch(tool){
//NOTE: We put CONFIGURE above ADD and SELECT to use the same method of selection.
//Meaning there's no break at the end of CONFIGURE.
case CONFIGURE:
{
//Check if we are linking.
if(linking){
//Check if the obj is valid to link to.
switch(obj->type){
case TYPE_CONVEYOR_BELT:
case TYPE_SHADOW_CONVEYOR_BELT:
case TYPE_MOVING_BLOCK:
case TYPE_MOVING_SHADOW_BLOCK:
case TYPE_MOVING_SPIKES:
{
//It's only valid when not linking a portal.
if(linkingTrigger->type==TYPE_PORTAL){
//You can't link a portal to moving blocks, etc.
//Stop linking and return.
linkingTrigger=NULL;
linking=false;
return;
}
break;
}
case TYPE_PORTAL:
{
//Make sure that the linkingTrigger is also a portal.
if(linkingTrigger->type!=TYPE_PORTAL){
//The linkingTrigger isn't a portal so stop linking and return.
linkingTrigger=NULL;
linking=false;
return;
}
break;
}
default:
//It isn't valid so stop linking and return.
linkingTrigger=NULL;
linking=false;
return;
break;
}
//Check if the linkingTrigger can handle multiple or only one link.
switch(linkingTrigger->type){
case TYPE_PORTAL:
{
//Portals can only link to one so remove all existing links.
triggers[linkingTrigger].clear();
triggers[linkingTrigger].push_back(obj);
break;
}
default:
{
//The most can handle multiple links.
triggers[linkingTrigger].push_back(obj);
break;
}
}
//Check if it's a portal.
if(linkingTrigger->type==TYPE_PORTAL){
//Portals need to get the id of the other instead of give it's own id.
vector<pair<string,string> > objMap;
obj->getEditorData(objMap);
int m=objMap.size();
if(m>0){
std::map<std::string,std::string> editorData;
char s[64];
sprintf(s,"%d",atoi(objMap[0].second.c_str()));
editorData["destination"]=s;
linkingTrigger->setEditorData(editorData);
}
}else{
//Give the object the same id as the trigger.
vector<pair<string,string> > objMap;
linkingTrigger->getEditorData(objMap);
int m=objMap.size();
if(m>0){
std::map<std::string,std::string> editorData;
char s[64];
sprintf(s,"%d",atoi(objMap[0].second.c_str()));
editorData["id"]=s;
obj->setEditorData(editorData);
}
}
//We return to prevent configuring stuff like conveyor belts, etc...
linking=false;
linkingTrigger=NULL;
return;
}
//If we're moving add a movingposition.
if(moving){
//Get the current mouse location.
int x,y;
SDL_GetMouseState(&x,&y);
x+=camera.x;
y+=camera.y;
//Apply snap to grid.
if(!pressedShift){
snapToGrid(&x,&y);
}else{
x-=25;
y-=25;
}
x-=movingBlock->getBox().x;
y-=movingBlock->getBox().y;
//Calculate the length.
//First get the delta x and y.
int dx,dy;
if(movingBlocks[movingBlock].empty()){
dx=x;
dy=y;
}else{
dx=x-movingBlocks[movingBlock].back().x;
dy=y-movingBlocks[movingBlock].back().y;
}
double length=sqrt(double(dx*dx+dy*dy));
movingBlocks[movingBlock].push_back(MovingPosition(x,y,(int)(length*(10/(double)movingSpeed))));
}
}
case SELECT:
case ADD:
{
//Check if object is already selected.
if(!selected){
//First check if shift is pressed or not.
if(!pressedShift){
//Clear the selection.
selection.clear();
}
//Add the object to the selection.
selection.push_back(obj);
}
break;
}
case REMOVE:
{
//Remove the object.
removeObject(obj);
break;
}
default:
break;
}
}
void LevelEditor::onRightClickObject(GameObject* obj,bool selected){
switch(tool){
case CONFIGURE:
{
//Make sure we aren't doing anything special.
if(moving || linking)
break;
//Check if it's a trigger.
if(obj->type==TYPE_PORTAL || obj->type==TYPE_BUTTON || obj->type==TYPE_SWITCH){
//Set linking true.
linking=true;
linkingTrigger=obj;
}
//Check if it's a moving block.
if(obj->type==TYPE_MOVING_BLOCK || obj->type==TYPE_MOVING_SHADOW_BLOCK || obj->type==TYPE_MOVING_SPIKES){
//Set moving true.
moving=true;
movingBlock=obj;
}
break;
}
case SELECT:
case ADD:
{
//We deselect the object if it's selected.
if(selected){
std::vector<GameObject*>::iterator it;
it=find(selection.begin(),selection.end(),obj);
//Remove the object from selection.
if(it!=selection.end()){
selection.erase(it);
}
}else{
//It wasn't a selected object so switch to configure mode.
//Check if it's the right type of object.
if(obj->type==TYPE_MOVING_BLOCK || obj->type==TYPE_MOVING_SHADOW_BLOCK || obj->type==TYPE_MOVING_SPIKES ||
obj->type==TYPE_PORTAL || obj->type==TYPE_BUTTON || obj->type==TYPE_SWITCH){
tool=CONFIGURE;
onRightClickObject(obj,selected);
}
}
break;
}
default:
break;
}
}
void LevelEditor::onClickVoid(int x,int y){
switch(tool){
case SELECT:
{
//We need to clear the selection.
selection.clear();
break;
}
case ADD:
{
//We need to clear the selection.
selection.clear();
//Now place an object.
//Apply snap to grid.
if(!pressedShift){
snapToGrid(&x,&y);
}else{
x-=25;
y-=25;
}
addObject(new Block(x,y,editorTileOrder[currentType],this));
break;
}
case CONFIGURE:
{
//We need to clear the selection.
selection.clear();
//If we're linking we should stop, user abort.
if(linking){
linking=false;
linkingTrigger=NULL;
//And return.
return;
}
//If we're moving we should add a point.
if(moving){
//Apply snap to grid.
if(!pressedShift){
snapToGrid(&x,&y);
}else{
x-=25;
y-=25;
}
x-=movingBlock->getBox().x;
y-=movingBlock->getBox().y;
//Calculate the length.
//First get the delta x and y.
int dx,dy;
if(movingBlocks[movingBlock].empty()){
dx=x;
dy=y;
}else{
dx=x-movingBlocks[movingBlock].back().x;
dy=y-movingBlocks[movingBlock].back().y;
}
double length=sqrt(double(dx*dx+dy*dy));
movingBlocks[movingBlock].push_back(MovingPosition(x,y,(int)(length*(10/(double)movingSpeed))));
//And return.
return;
}
break;
}
default:
break;
}
}
void LevelEditor::onDragStart(int x,int y){
switch(tool){
case SELECT:
case ADD:
case CONFIGURE:
{
//We can drag the selection so check if the selection isn't empty.
if(!selection.empty()){
//The selection isn't empty so search the dragCenter.
//Create a mouse rectangle.
SDL_Rect mouse={x,y,0,0};
//Loop through the objects to check collision.
for(unsigned int o=0; o<selection.size(); o++){
if(checkCollision(selection[o]->getBox(),mouse)==true){
//We have collision so set the dragCenter.
dragCenter=selection[o];
selectionDrag=true;
}
}
}
break;
}
default:
break;
}
}
void LevelEditor::onDrag(int dx,int dy){
switch(tool){
case REMOVE:
{
//No matter what we delete the item the mouse is above.
//Get the current mouse location.
int x,y;
SDL_GetMouseState(&x,&y);
//Create the rectangle.
SDL_Rect mouse={x+camera.x,y+camera.y,0,0};
//Loop through the objects to check collision.
for(unsigned int o=0; o<levelObjects.size(); o++){
if(checkCollision(levelObjects[o]->getBox(),mouse)==true){
//Remove the object.
removeObject(levelObjects[o]);
}
}
break;
}
default:
break;
}
}
void LevelEditor::onDrop(int x,int y){
switch(tool){
case SELECT:
case ADD:
case CONFIGURE:
{
//Check if the drag center isn't null.
if(dragCenter==NULL) return;
//The location of the dragCenter.
SDL_Rect r=dragCenter->getBox();
//Apply snap to grid.
if(!pressedShift){
snapToGrid(&x,&y);
}else{
x-=25;
y-=25;
}
//Loop through the selection.
for(unsigned int o=0; o<selection.size(); o++){
SDL_Rect r1=selection[o]->getBox();
//We need to place the object at his drop place.
moveObject(selection[o],(r1.x-r.x)+x,(r1.y-r.y)+y);
}
//Make sure the dragCenter is null and set selectionDrag false.
dragCenter=NULL;
selectionDrag=false;
break;
}
default:
break;
}
}
void LevelEditor::onCameraMove(int dx,int dy){
switch(tool){
case REMOVE:
{
//Only delete when the left mouse button is pressed.
if(pressedLeftMouse){
//Get the current mouse location.
int x,y;
SDL_GetMouseState(&x,&y);
//Create the rectangle.
SDL_Rect mouse={x+camera.x,y+camera.y,0,0};
//Loop through the objects to check collision.
for(unsigned int o=0; o<levelObjects.size(); o++){
if(checkCollision(levelObjects[o]->getBox(),mouse)==true){
//Remove the object.
removeObject(levelObjects[o]);
}
}
}
break;
}
default:
break;
}
}
void LevelEditor::onEnterObject(GameObject* obj){
switch(tool){
case CONFIGURE:
{
//Check if the type is an moving block.
if(obj->type==TYPE_MOVING_BLOCK || obj->type==TYPE_MOVING_SHADOW_BLOCK || obj->type==TYPE_MOVING_SPIKES){
//Open a message popup.
//First delete any existing gui.
if(GUIObjectRoot){
delete GUIObjectRoot;
GUIObjectRoot=NULL;
}
//Get the properties.
vector<pair<string,string> > objMap;
obj->getEditorData(objMap);
int m=objMap.size();
if(m>0){
//Set the object we configure.
configuredObject=obj;
//Check if the moving block has a path..
char s1[64];
bool path=false;
if(!movingBlocks[obj].empty()){
sprintf(s1,"Defined");
path=true;
}else{
sprintf(s1,"None");
}
//Now create the GUI.
string s;
switch(obj->type){
case TYPE_MOVING_BLOCK:
s="Moving block";
break;
case TYPE_MOVING_SHADOW_BLOCK:
s="Moving shadow block";
break;
case TYPE_MOVING_SPIKES:
s="Moving spikes";
break;
}
GUIObjectRoot=new GUIObject(100,(SCREEN_HEIGHT-200)/2,600,200,GUIObjectFrame,s.c_str());
GUIObject* obj;
obj=new GUIObject(70,50,280,36,GUIObjectCheckBox,"Enabled",(objMap[2].second!="1"));
obj->name="cfgMovingBlockEnabled";
obj->eventCallback=this;
objectProperty=obj;
GUIObjectRoot->childControls.push_back(obj);
obj=new GUIObject(70,80,280,36,GUIObjectCheckBox,"Loop",(objMap[3].second!="0"));
obj->name="cfgMovingBlockLoop";
obj->eventCallback=this;
secondObjectProperty=obj;
GUIObjectRoot->childControls.push_back(obj);
obj=new GUIObject(70,110,280,36,GUIObjectLabel,"Path");
GUIObjectRoot->childControls.push_back(obj);
obj=new GUIObject(330,110,280,36,GUIObjectLabel,s1);
GUIObjectRoot->childControls.push_back(obj);
if(path){
obj=new GUIObject(400,110,36,36,GUIObjectButton,"x");
obj->name="cfgMovingBlockClrPath";
obj->eventCallback=this;
GUIObjectRoot->childControls.push_back(obj);
}else{
//NOTE: The '+' is translated 5 pixels down to align with the 'x'.
obj=new GUIObject(400,115,36,36,GUIObjectButton,"+");
obj->name="cfgMovingBlockMakePath";
obj->eventCallback=this;
GUIObjectRoot->childControls.push_back(obj);
}
obj=new GUIObject(100,200-44,150,36,GUIObjectButton,"OK");
obj->name="cfgMovingBlockOK";
obj->eventCallback=this;
GUIObjectRoot->childControls.push_back(obj);
obj=new GUIObject(350,200-44,150,36,GUIObjectButton,"Cancel");
obj->name="cfgCancel";
obj->eventCallback=this;
GUIObjectRoot->childControls.push_back(obj);
//Dim the screen using the tempSurface.
SDL_FillRect(tempSurface,NULL,0);
SDL_SetAlpha(tempSurface,SDL_SRCALPHA,155);
SDL_BlitSurface(tempSurface,NULL,screen,NULL);
while(GUIObjectRoot){
while(SDL_PollEvent(&event)) GUIObjectHandleEvents(true);
if(GUIObjectRoot) GUIObjectRoot->render();
- SDL_Flip(screen);
+ flipScreen();
SDL_Delay(30);
}
}
}
//Check which type of object it is.
if(obj->type==TYPE_NOTIFICATION_BLOCK){
//Open a message popup.
//First delete any existing gui.
if(GUIObjectRoot){
delete GUIObjectRoot;
GUIObjectRoot=NULL;
}
//Get the properties.
vector<pair<string,string> > objMap;
obj->getEditorData(objMap);
int m=objMap.size();
if(m>0){
//Set the object we configure.
configuredObject=obj;
//Now create the GUI.
GUIObjectRoot=new GUIObject(100,(SCREEN_HEIGHT-250)/2,600,250,GUIObjectFrame,"Notification block");
GUIObject* obj;
obj=new GUIObject(40,50,240,36,GUIObjectLabel,"Enter message here:");
GUIObjectRoot->childControls.push_back(obj);
obj=new GUITextArea(50,90,500,100);
string tmp=objMap[1].second.c_str();
//Change \n with the characters '\n'.
while(tmp.find("\\n")!=string::npos){
tmp=tmp.replace(tmp.find("\\n"),2,"\n");
}
obj->caption=tmp.c_str();
//Set the textField.
objectProperty=obj;
GUIObjectRoot->childControls.push_back(obj);
obj=new GUIObject(100,250-44,150,36,GUIObjectButton,"OK");
obj->name="cfgNotificationBlockOK";
obj->eventCallback=this;
GUIObjectRoot->childControls.push_back(obj);
obj=new GUIObject(350,250-44,150,36,GUIObjectButton,"Cancel");
obj->name="cfgCancel";
obj->eventCallback=this;
GUIObjectRoot->childControls.push_back(obj);
//Dim the screen using the tempSurface.
SDL_FillRect(tempSurface,NULL,0);
SDL_SetAlpha(tempSurface,SDL_SRCALPHA,155);
SDL_BlitSurface(tempSurface,NULL,screen,NULL);
while(GUIObjectRoot){
while(SDL_PollEvent(&event)) GUIObjectHandleEvents(true);
if(GUIObjectRoot) GUIObjectRoot->render();
- SDL_Flip(screen);
+ flipScreen();
SDL_Delay(30);
}
}
}
if(obj->type==TYPE_CONVEYOR_BELT || obj->type==TYPE_SHADOW_CONVEYOR_BELT){
//Open a message popup.
//First delete any existing gui.
if(GUIObjectRoot){
delete GUIObjectRoot;
GUIObjectRoot=NULL;
}
//Get the properties and check if
vector<pair<string,string> > objMap;
obj->getEditorData(objMap);
int m=objMap.size();
if(m>0){
//Set the object we configure.
configuredObject=obj;
//Now create the GUI.
string s;
if(obj->type==TYPE_CONVEYOR_BELT){
s="Shadow Conveyor belt";
}else{
s="Conveyor belt";
}
GUIObjectRoot=new GUIObject(100,(SCREEN_HEIGHT-200)/2,600,200,GUIObjectFrame,s.c_str());
GUIObject* obj;
obj=new GUIObject(40,60,220,36,GUIObjectCheckBox,"Enabled",(objMap[1].second!="1"));
obj->name="cfgConveyorBlockEnabled";
obj->eventCallback=this;
objectProperty=obj;
GUIObjectRoot->childControls.push_back(obj);
obj=new GUIObject(40,100,240,36,GUIObjectLabel,"Enter speed here:");
GUIObjectRoot->childControls.push_back(obj);
obj=new GUIObject(240,100,320,36,GUIObjectTextBox,objMap[2].second.c_str());
//Set the textField.
secondObjectProperty=obj;
GUIObjectRoot->childControls.push_back(obj);
obj=new GUIObject(100,200-44,150,36,GUIObjectButton,"OK");
obj->name="cfgConveyorBlockOK";
obj->eventCallback=this;
GUIObjectRoot->childControls.push_back(obj);
obj=new GUIObject(350,200-44,150,36,GUIObjectButton,"Cancel");
obj->name="cfgCancel";
obj->eventCallback=this;
GUIObjectRoot->childControls.push_back(obj);
//Dim the screen using the tempSurface.
SDL_FillRect(tempSurface,NULL,0);
SDL_SetAlpha(tempSurface,SDL_SRCALPHA,155);
SDL_BlitSurface(tempSurface,NULL,screen,NULL);
while(GUIObjectRoot){
while(SDL_PollEvent(&event)) GUIObjectHandleEvents(true);
if(GUIObjectRoot) GUIObjectRoot->render();
- SDL_Flip(screen);
+ flipScreen();
SDL_Delay(30);
}
}
}
if(obj->type==TYPE_PORTAL){
//Open a message popup.
//First delete any existing gui.
if(GUIObjectRoot){
delete GUIObjectRoot;
GUIObjectRoot=NULL;
}
//Get the properties and check if
vector<pair<string,string> > objMap;
obj->getEditorData(objMap);
int m=objMap.size();
if(m>0){
//Set the object we configure.
configuredObject=obj;
//Check how many targets there are for this object.
char s1[64];
bool target=false;
if(!triggers[obj].empty()){
sprintf(s1,"Defined");
target=true;
}else{
sprintf(s1,"None");
}
//Now create the GUI.
GUIObjectRoot=new GUIObject(100,(SCREEN_HEIGHT-200)/2,600,200,GUIObjectFrame,"Portal");
GUIObject* obj;
obj=new GUIObject(70,60,310,36,GUIObjectCheckBox,"Activate on touch",(objMap[1].second=="1"));
obj->name="cfgPortalAutomatic";
obj->eventCallback=this;
objectProperty=obj;
GUIObjectRoot->childControls.push_back(obj);
obj=new GUIObject(70,100,240,36,GUIObjectLabel,"Targets:");
GUIObjectRoot->childControls.push_back(obj);
obj=new GUIObject(360,100,100,36,GUIObjectLabel,s1);
GUIObjectRoot->childControls.push_back(obj);
//NOTE: The '+' is translated 5 pixels down to align with the 'x'.
obj=new GUIObject(460,105,36,36,GUIObjectButton,"+");
obj->name="cfgPortalLink";
obj->eventCallback=this;
GUIObjectRoot->childControls.push_back(obj);
//Check if there are targets defined.
if(target){
obj=new GUIObject(500,100,36,36,GUIObjectButton,"x");
obj->name="cfgPortalUnlink";
obj->eventCallback=this;
GUIObjectRoot->childControls.push_back(obj);
}
obj=new GUIObject(100,200-44,150,36,GUIObjectButton,"OK");
obj->name="cfgPortalOK";
obj->eventCallback=this;
GUIObjectRoot->childControls.push_back(obj);
obj=new GUIObject(350,200-44,150,36,GUIObjectButton,"Cancel");
obj->name="cfgCancel";
obj->eventCallback=this;
GUIObjectRoot->childControls.push_back(obj);
//Dim the screen using the tempSurface.
SDL_FillRect(tempSurface,NULL,0);
SDL_SetAlpha(tempSurface,SDL_SRCALPHA,155);
SDL_BlitSurface(tempSurface,NULL,screen,NULL);
while(GUIObjectRoot){
while(SDL_PollEvent(&event))
GUIObjectHandleEvents(true);
if(GUIObjectRoot)
GUIObjectRoot->render();
- SDL_Flip(screen);
+ flipScreen();
SDL_Delay(30);
}
}
}
if(obj->type==TYPE_BUTTON || obj->type==TYPE_SWITCH){
//Open a message popup.
//First delete any existing gui.
if(GUIObjectRoot){
delete GUIObjectRoot;
GUIObjectRoot=NULL;
}
//Get the properties and check if
vector<pair<string,string> > objMap;
obj->getEditorData(objMap);
int m=objMap.size();
if(m>0){
//Set the object we configure.
configuredObject=obj;
//Check how many targets there are for this object.
char s1[64];
bool targets=false;
if(!triggers[obj].empty()){
sprintf(s1,"%d Defined",(int)triggers[obj].size());
targets=true;
}else{
sprintf(s1,"None");
}
//Now create the GUI.
string s;
if(obj->type==TYPE_BUTTON){
s="Button";
}else{
s="Switch";
}
GUIObjectRoot=new GUIObject(100,(SCREEN_HEIGHT-200)/2,600,200,GUIObjectFrame,s.c_str());
GUIObject* obj;
obj=new GUIObject(70,60,240,36,GUIObjectLabel,"Behaviour:");
GUIObjectRoot->childControls.push_back(obj);
obj=new GUISingleLineListBox(250,60,300,36);
obj->name="lstBehaviour";
vector<string> v;
v.push_back("On");
v.push_back("Off");
v.push_back("Toggle");
(dynamic_cast<GUISingleLineListBox*>(obj))->item=v;
//Get the current behaviour.
if(objMap[1].second=="on"){
obj->value=0;
}else if(objMap[1].second=="off"){
obj->value=1;
}else{
//There's no need to check for the last one, since it's also the default.
obj->value=2;
}
objectProperty=obj;
GUIObjectRoot->childControls.push_back(obj);
obj=new GUIObject(70,100,240,36,GUIObjectLabel,"Targets:");
GUIObjectRoot->childControls.push_back(obj);
obj=new GUIObject(250,100,100,36,GUIObjectLabel,s1);
GUIObjectRoot->childControls.push_back(obj);
//NOTE: The '+' is translated 5 pixels down to align with the 'x'.
obj=new GUIObject(350,105,36,36,GUIObjectButton,"+");
obj->name="cfgTriggerLink";
obj->eventCallback=this;
GUIObjectRoot->childControls.push_back(obj);
//Check if there are targets defined.
if(targets){
obj=new GUIObject(390,100,36,36,GUIObjectButton,"x");
obj->name="cfgTriggerUnlink";
obj->eventCallback=this;
GUIObjectRoot->childControls.push_back(obj);
}
obj=new GUIObject(100,200-44,150,36,GUIObjectButton,"OK");
obj->name="cfgTriggerOK";
obj->eventCallback=this;
GUIObjectRoot->childControls.push_back(obj);
obj=new GUIObject(350,200-44,150,36,GUIObjectButton,"Cancel");
obj->name="cfgCancel";
obj->eventCallback=this;
GUIObjectRoot->childControls.push_back(obj);
//Dim the screen using the tempSurface.
SDL_FillRect(tempSurface,NULL,0);
SDL_SetAlpha(tempSurface,SDL_SRCALPHA,155);
SDL_BlitSurface(tempSurface,NULL,screen,NULL);
while(GUIObjectRoot){
while(SDL_PollEvent(&event))
GUIObjectHandleEvents(true);
if(GUIObjectRoot)
GUIObjectRoot->render();
- SDL_Flip(screen);
+ flipScreen();
SDL_Delay(30);
}
}
}
if(obj->type==TYPE_FRAGILE){
//First delete any existing gui.
if(GUIObjectRoot){
delete GUIObjectRoot;
GUIObjectRoot=NULL;
}
//Get the properties and check if it contains the state data.
vector<pair<string,string> > objMap;
obj->getEditorData(objMap);
int m=objMap.size();
if(m>0){
//Set the object we configure.
configuredObject=obj;
//Create the GUI.
GUIObjectRoot=new GUIObject(100,(SCREEN_HEIGHT-200)/2,600,200,GUIObjectFrame,"Fragile");
GUIObject* obj;
obj=new GUIObject(70,60,240,36,GUIObjectLabel,"State:");
GUIObjectRoot->childControls.push_back(obj);
obj=new GUISingleLineListBox(250,60,300,36);
obj->name="lstBehaviour";
vector<string> v;
v.push_back("Complete");
v.push_back("One step");
v.push_back("Two steps");
v.push_back("Gone");
(dynamic_cast<GUISingleLineListBox*>(obj))->item=v;
//Get the current state.
obj->value=atoi(objMap[1].second.c_str());
objectProperty=obj;
GUIObjectRoot->childControls.push_back(obj);
obj=new GUIObject(100,200-44,150,36,GUIObjectButton,"OK");
obj->name="cfgFragileOK";
obj->eventCallback=this;
GUIObjectRoot->childControls.push_back(obj);
obj=new GUIObject(350,200-44,150,36,GUIObjectButton,"Cancel");
obj->name="cfgCancel";
obj->eventCallback=this;
GUIObjectRoot->childControls.push_back(obj);
//Dim the screen using the tempSurface.
SDL_FillRect(tempSurface,NULL,0);
SDL_SetAlpha(tempSurface,SDL_SRCALPHA,155);
SDL_BlitSurface(tempSurface,NULL,screen,NULL);
while(GUIObjectRoot){
while(SDL_PollEvent(&event))
GUIObjectHandleEvents(true);
if(GUIObjectRoot)
GUIObjectRoot->render();
- SDL_Flip(screen);
+ flipScreen();
SDL_Delay(30);
}
}
}
break;
}
default:
break;
}
}
void LevelEditor::addObject(GameObject* obj){
//If it's a player or shadow start then we need to remove the previous one.
if(obj->type==TYPE_START_PLAYER || obj->type==TYPE_START_SHADOW){
//Loop through the levelObjects.
for(unsigned int o=0; o<levelObjects.size(); o++){
//Check if the type is the same.
if(levelObjects[o]->type==obj->type){
removeObject(levelObjects[o]);
}
}
}
//Add it to the levelObjects.
levelObjects.push_back(obj);
//Check if the object is inside the level dimensions, etc.
//Just call moveObject() to perform this.
moveObject(obj,obj->getBox().x,obj->getBox().y);
//GameObject type specific stuff.
switch(obj->type){
case TYPE_BUTTON:
case TYPE_SWITCH:
case TYPE_PORTAL:
{
//Add the object to the triggers.
vector<GameObject*> linked;
triggers[obj]=linked;
//Give it it's own id.
std::map<std::string,std::string> editorData;
char s[64];
sprintf(s,"%d",currentId);
currentId++;
editorData["id"]=s;
obj->setEditorData(editorData);
break;
}
case TYPE_MOVING_BLOCK:
case TYPE_MOVING_SHADOW_BLOCK:
case TYPE_MOVING_SPIKES:
{
//Add the object to the moving blocks.
vector<MovingPosition> positions;
movingBlocks[obj]=positions;
//Get the editor data.
vector<pair<string,string> > objMap;
obj->getEditorData(objMap);
//Get the number of entries of the editor data.
int m=objMap.size();
//Check if the editor data isn't empty.
if(m>0){
//Integer containing the positions.
int pos=0;
int currentPos=0;
//Get the number of movingpositions.
pos=atoi(objMap[1].second.c_str());
while(currentPos<pos){
int x=atoi(objMap[currentPos*3+4].second.c_str());
int y=atoi(objMap[currentPos*3+5].second.c_str());
int t=atoi(objMap[currentPos*3+6].second.c_str());
//Create a new movingPosition.
MovingPosition position(x,y,t);
movingBlocks[obj].push_back(position);
//Increase currentPos by one.
currentPos++;
}
}
//Give it it's own id.
std::map<std::string,std::string> editorData;
char s[64];
sprintf(s,"%d",currentId);
currentId++;
editorData["id"]=s;
obj->setEditorData(editorData);
break;
}
default:
break;
}
}
void LevelEditor::moveObject(GameObject* obj,int x,int y){
//Set the obj at it's new position.
obj->setPosition(x,y);
//Check if the object is inside the level dimensions.
//If not let the level grow.
if(obj->getBox().x+50>LEVEL_WIDTH){
LEVEL_WIDTH=obj->getBox().x+50;
}
if(obj->getBox().y+50>LEVEL_HEIGHT){
LEVEL_HEIGHT=obj->getBox().y+50;
}
if(obj->getBox().x<0 || obj->getBox().y<0){
//A block on the left (or top) side of the level, meaning we need to shift everything.
//First calc the difference.
int diffx=(0-(obj->getBox().x));
int diffy=(0-(obj->getBox().y));
if(diffx<0) diffx=0;
if(diffy<0) diffy=0;
//Change the level size first.
//The level grows with the difference, 0-(x+50).
LEVEL_WIDTH+=diffx;
LEVEL_HEIGHT+=diffy;
//cout<<"x:"<<diffx<<",y:"<<diffy<<endl; //debug
camera.x+=diffx;
camera.y+=diffy;
//Set the position of player and shadow
//(although it's unnecessary if there is player and shadow start)
player.setPosition(player.getBox().x+diffx,player.getBox().y+diffy);
shadow.setPosition(shadow.getBox().x+diffx,shadow.getBox().y+diffy);
for(unsigned int o=0; o<levelObjects.size(); o++){
//FIXME: shouldn't recuesive call me (to prevent stack overflow bugs)
moveObject(levelObjects[o],levelObjects[o]->getBox().x+diffx,levelObjects[o]->getBox().y+diffy);
}
}
//If the object is a player or shadow start then change the start position of the player or shadow.
if(obj->type==TYPE_START_PLAYER){
//Center the player horizontally.
player.fx=obj->getBox().x+(50-23)/2;
player.fy=obj->getBox().y;
//Now reset the player to get him to it's new start position.
player.reset(true);
}
if(obj->type==TYPE_START_SHADOW){
//Center the shadow horizontally.
shadow.fx=obj->getBox().x+(50-23)/2;
shadow.fy=obj->getBox().y;
//Now reset the shadow to get him to it's new start position.
shadow.reset(true);
}
}
void LevelEditor::removeObject(GameObject* obj){
std::vector<GameObject*>::iterator it;
std::map<GameObject*,vector<GameObject*> >::iterator mapIt;
//Check if the object is in the selection.
it=find(selection.begin(),selection.end(),obj);
if(it!=selection.end()){
//It is so we delete it.
selection.erase(it);
}
//Check if the object is in the triggers.
mapIt=triggers.find(obj);
if(mapIt!=triggers.end()){
//It is so we remove it.
triggers.erase(mapIt);
}
//Boolean if it could be a target.
if(obj->type==TYPE_MOVING_BLOCK || obj->type==TYPE_MOVING_SHADOW_BLOCK || obj->type==TYPE_MOVING_SPIKES
|| obj->type==TYPE_CONVEYOR_BELT || obj->type==TYPE_SHADOW_CONVEYOR_BELT || obj->type==TYPE_PORTAL){
for(mapIt=triggers.begin();mapIt!=triggers.end();++mapIt){
//Now loop the target vector.
for(unsigned int o=0;o<(*mapIt).second.size();o++){
//Check if the obj is in the target vector.
if((*mapIt).second[o]==obj){
(*mapIt).second.erase(find((*mapIt).second.begin(),(*mapIt).second.end(),obj));
o--;
}
}
}
}
//Check if the object is in the movingObjects.
std::map<GameObject*,vector<MovingPosition> >::iterator movIt;
movIt=movingBlocks.find(obj);
if(movIt!=movingBlocks.end()){
//It is so we remove it.
movingBlocks.erase(movIt);
}
//Now we remove the object from the levelObjects.
it=find(levelObjects.begin(),levelObjects.end(),obj);
if(it!=levelObjects.end()){
levelObjects.erase(it);
}
delete obj;
obj=NULL;
}
void LevelEditor::GUIEventCallback_OnEvent(std::string name,GUIObject* obj,int eventType){
//Check for GUI events.
//Notification block configure events.
if(name=="cfgNotificationBlockOK"){
if(GUIObjectRoot){
//Set the message of the notification block.
std::map<std::string,std::string> editorData;
editorData["message"]=objectProperty->caption;
configuredObject->setEditorData(editorData);
//And delete the GUI.
objectProperty=NULL;
secondObjectProperty=NULL;
configuredObject=NULL;
delete GUIObjectRoot;
GUIObjectRoot=NULL;
}
}
//Conveyor belt block configure events.
if(name=="cfgConveyorBlockOK"){
if(GUIObjectRoot){
//Set the message of the notification block.
std::map<std::string,std::string> editorData;
editorData["speed"]=secondObjectProperty->caption;
editorData["disabled"]=(objectProperty->value==0)?"1":"0";
configuredObject->setEditorData(editorData);
//And delete the GUI.
objectProperty=NULL;
secondObjectProperty=NULL;
configuredObject=NULL;
delete GUIObjectRoot;
GUIObjectRoot=NULL;
}
}
//Moving block configure events.
if(name=="cfgMovingBlockOK"){
if(GUIObjectRoot){
//Set if the moving block is enabled/disabled.
std::map<std::string,std::string> editorData;
editorData["disabled"]=(objectProperty->value==0)?"1":"0";
editorData["loop"]=(secondObjectProperty->value==1)?"1":"0";
configuredObject->setEditorData(editorData);
//And delete the GUI.
objectProperty=NULL;
secondObjectProperty=NULL;
configuredObject=NULL;
delete GUIObjectRoot;
GUIObjectRoot=NULL;
}
}
if(name=="cfgMovingBlockClrPath"){
if(GUIObjectRoot){
//Set the message of the notification block.
std::map<std::string,std::string> editorData;
editorData["MovingPosCount"]="0";
configuredObject->setEditorData(editorData);
std::map<GameObject*,vector<MovingPosition> >::iterator it;
it=movingBlocks.find(configuredObject);
if(it!=movingBlocks.end()){
(*it).second.clear();
}
//And delete the GUI.
objectProperty=NULL;
secondObjectProperty=NULL;
configuredObject=NULL;
delete GUIObjectRoot;
GUIObjectRoot=NULL;
}
}
if(name=="cfgMovingBlockMakePath"){
if(GUIObjectRoot){
//Set moving.
moving=true;
movingBlock=configuredObject;
//And delete the GUI.
objectProperty=NULL;
secondObjectProperty=NULL;
configuredObject=NULL;
delete GUIObjectRoot;
GUIObjectRoot=NULL;
}
}
//Portal block configure events.
if(name=="cfgPortalOK"){
if(GUIObjectRoot){
//Set the message of the notification block.
std::map<std::string,std::string> editorData;
editorData["automatic"]=(objectProperty->value==1)?"1":"0";
configuredObject->setEditorData(editorData);
//And delete the GUI.
objectProperty=NULL;
secondObjectProperty=NULL;
configuredObject=NULL;
delete GUIObjectRoot;
GUIObjectRoot=NULL;
}
}
if(name=="cfgPortalLink"){
//We set linking true.
linking=true;
linkingTrigger=configuredObject;
//And delete the GUI.
objectProperty=NULL;
secondObjectProperty=NULL;
configuredObject=NULL;
if(GUIObjectRoot){
delete GUIObjectRoot;
}
GUIObjectRoot=NULL;
}
if(name=="cfgPortalUnlink"){
std::map<GameObject*,vector<GameObject*> >::iterator it;
it=triggers.find(configuredObject);
if(it!=triggers.end()){
//Remove the targets.
(*it).second.clear();
}
//We reset the destination.
std::map<std::string,std::string> editorData;
editorData["destination"]="";
configuredObject->setEditorData(editorData);
//And delete the GUI.
objectProperty=NULL;
secondObjectProperty=NULL;
configuredObject=NULL;
if(GUIObjectRoot){
delete GUIObjectRoot;
}
GUIObjectRoot=NULL;
}
//Trigger block configure events.
if(name=="cfgTriggerOK"){
if(GUIObjectRoot){
//Set the message of the notification block.
std::map<std::string,std::string> editorData;
editorData["behaviour"]=(dynamic_cast<GUISingleLineListBox*>(objectProperty))->item[objectProperty->value];
configuredObject->setEditorData(editorData);
//And delete the GUI.
objectProperty=NULL;
secondObjectProperty=NULL;
configuredObject=NULL;
delete GUIObjectRoot;
GUIObjectRoot=NULL;
}
}
if(name=="cfgTriggerLink"){
//We set linking true.
linking=true;
linkingTrigger=configuredObject;
//And delete the GUI.
objectProperty=NULL;
secondObjectProperty=NULL;
configuredObject=NULL;
if(GUIObjectRoot){
delete GUIObjectRoot;
}
GUIObjectRoot=NULL;
}
if(name=="cfgTriggerUnlink"){
std::map<GameObject*,vector<GameObject*> >::iterator it;
it=triggers.find(configuredObject);
if(it!=triggers.end()){
//Remove the targets.
(*it).second.clear();
}
//We give the trigger a new id to prevent activating unlinked targets.
std::map<std::string,std::string> editorData;
char s[64];
sprintf(s,"%d",currentId);
currentId++;
editorData["id"]=s;
configuredObject->setEditorData(editorData);
//And delete the GUI.
objectProperty=NULL;
secondObjectProperty=NULL;
configuredObject=NULL;
if(GUIObjectRoot){
delete GUIObjectRoot;
}
GUIObjectRoot=NULL;
}
//Fragile configuration.
if(name=="cfgFragileOK"){
std::map<std::string,std::string> editorData;
char s[64];
sprintf(s,"%d",objectProperty->value);
editorData["state"]=s;
configuredObject->setEditorData(editorData);
//And delete the GUI.
objectProperty=NULL;
secondObjectProperty=NULL;
configuredObject=NULL;
if(GUIObjectRoot){
delete GUIObjectRoot;
}
GUIObjectRoot=NULL;
}
//Cancel.
if(name=="cfgCancel"){
if(GUIObjectRoot){
//Delete the GUI.
objectProperty=NULL;
secondObjectProperty=NULL;
configuredObject=NULL;
delete GUIObjectRoot;
GUIObjectRoot=NULL;
}
}
//LevelSetting events.
if(name=="lvlSettingsOK"){
levelName=objectProperty->caption;
levelTheme=secondObjectProperty->caption;
//target time and recordings.
string s=levelTimeProperty->caption;
if(s.empty() || !(s[0]>='0' && s[0]<='9')){
levelTime=-1;
}else{
levelTime=int(atof(s.c_str())*40.0+0.5);
}
s=levelRecordingsProperty->caption;
if(s.empty() || !(s[0]>='0' && s[0]<='9')){
levelRecordings=-1;
}else{
levelRecordings=atoi(s.c_str());
}
//And delete the GUI.
if(GUIObjectRoot){
objectProperty=NULL;
secondObjectProperty=NULL;
delete GUIObjectRoot;
GUIObjectRoot=NULL;
}
}
if(name=="lvlSettingsCancel"){
if(GUIObjectRoot){
//Delete the GUI.
objectProperty=NULL;
secondObjectProperty=NULL;
delete GUIObjectRoot;
GUIObjectRoot=NULL;
}
}
}
////////////////LOGIC////////////////////
void LevelEditor::logic(){
if(playMode){
//PlayMode so let the game do it's logic.
Game::logic();
}else{
//Move the camera.
if(cameraXvel!=0 || cameraYvel!=0){
camera.x+=cameraXvel;
camera.y+=cameraYvel;
//Call the onCameraMove event.
onCameraMove(cameraXvel,cameraYvel);
}
//Move the camera with the mouse.
setCamera();
//It isn't playMode so the mouse should be checked.
tooltip=-1;
//Get the mouse location.
int x,y;
SDL_GetMouseState(&x,&y);
SDL_Rect mouse={x,y,0,0};
//We loop through the number of tools + the number of buttons.
for(int t=0; t<NUMBER_TOOLS+6; t++){
SDL_Rect toolRect={180+(t*40)+(t*10),555,40,40};
//Check for collision.
if(checkCollision(mouse,toolRect)==true){
//Set the tooltip tool.
tooltip=t;
//Check if there's a mouse click.
if(event.type==SDL_MOUSEBUTTONDOWN && event.button.button==SDL_BUTTON_LEFT){
if(t<NUMBER_TOOLS){
tool=(Tools)t;
}else{
//The selected button isn't a tool.
//Now check which button it is.
if(t==NUMBER_TOOLS){
playMode=true;
cameraSave.x=camera.x;
cameraSave.y=camera.y;
if(tool==CONFIGURE){
//Also stop linking or moving.
if(linking){
linking=false;
linkingTrigger=NULL;
}
if(moving){
//Write the path to the moving block.
std::map<std::string,std::string> editorData;
char s[64], s0[64];
sprintf(s,"%d",int(movingBlocks[movingBlock].size()));
editorData["MovingPosCount"]=s;
//Loop through the positions.
for(unsigned int o=0;o<movingBlocks[movingBlock].size();o++){
sprintf(s0+1,"%d",o);
sprintf(s,"%d",movingBlocks[movingBlock][o].x);
s0[0]='x';
editorData[s0]=s;
sprintf(s,"%d",movingBlocks[movingBlock][o].y);
s0[0]='y';
editorData[s0]=s;
sprintf(s,"%d",movingBlocks[movingBlock][o].time);
s0[0]='t';
editorData[s0]=s;
}
movingBlock->setEditorData(editorData);
moving=false;
movingBlock=NULL;
}
}
}
if(t==NUMBER_TOOLS+2){
//Open up level settings dialog
levelSettings();
}
if(t==NUMBER_TOOLS+4){
//Go back to the level selection screen of Level Editor
setNextState(STATE_LEVEL_EDIT_SELECT);
//Change the music back to menu music.
getMusicManager()->playMusic("menu");
}
if(t==NUMBER_TOOLS+3){
//Save current level
saveLevel(levelFile);
//And give feedback to the user.
if(levelName.empty())
msgBox("Level \""+fileNameFromPath(levelFile)+"\" saved",MsgBoxOKOnly,"Saved");
else
msgBox("Level \""+levelName+"\" saved",MsgBoxOKOnly,"Saved");
}
}
}
}
}
}
}
/////////////////RENDER//////////////////////
void LevelEditor::render(){
//Always let the game render the game.
Game::render();
//Only render extra stuff like the toolbar, selection, etc.. when not in playMode.
if(!playMode){
//Render the selectionmarks.
//TODO: Check if block is in sight.
for(unsigned int o=0; o<selection.size(); o++){
//Get the location to draw.
SDL_Rect r=selection[o]->getBox();
r.x-=camera.x;
r.y-=camera.y;
//Draw the selectionMarks.
applySurface(r.x,r.y,selectionMark,screen,NULL);
applySurface(r.x+r.w-5,r.y,selectionMark,screen,NULL);
applySurface(r.x,r.y+r.h-5,selectionMark,screen,NULL);
applySurface(r.x+r.w-5,r.y+r.h-5,selectionMark,screen,NULL);
}
//Clear the placement surface.
SDL_FillRect(placement,NULL,0x00FF00FF);
//Draw the dark areas marking the outside of the level.
SDL_Rect r;
if(camera.x<0){
//Draw left side.
r.x=0;
r.y=0;
r.w=0-camera.x;
r.h=600;
SDL_FillRect(placement,&r,0);
}
if(camera.x>LEVEL_WIDTH-800){
//Draw right side.
r.x=LEVEL_WIDTH-camera.x;
r.y=0;
r.w=800-(LEVEL_WIDTH-camera.x);
r.h=600;
SDL_FillRect(placement,&r,0);
}
if(camera.y<0){
//Draw the top.
r.x=0;
r.y=0;
r.w=800;
r.h=0-camera.y;
SDL_FillRect(placement,&r,0);
}
if(camera.y>LEVEL_HEIGHT-600){
//Draw the bottom.
r.x=0;
r.y=LEVEL_HEIGHT-camera.y;
r.w=800;
r.h=600-(LEVEL_HEIGHT-camera.y);
SDL_FillRect(placement,&r,0);
}
//Check if we should draw on the placement surface.
if(selectionDrag){
showSelectionDrag();
}else{
if(tool==ADD){
showCurrentObject();
}
if(tool==CONFIGURE){
showConfigure();
}
}
//Draw the level borders.
drawRect(-camera.x,-camera.y,LEVEL_WIDTH,LEVEL_HEIGHT,screen);
//Render the placement surface.
applySurface(0,0,placement,screen,NULL);
//Render the hud layer.
renderHUD();
//On top of all render the toolbar.
applySurface(170,550,toolbar,screen,NULL);
//Now render a tooltip.
if(tooltip>=0){
//The back and foreground colors.
SDL_Color fg={0,0,0};
//Tool specific text.
SDL_Surface* tip=NULL;
switch(tooltip){
case 0:
tip=TTF_RenderText_Blended(fontText,"Select",fg);
break;
case 1:
tip=TTF_RenderText_Blended(fontText,"Add",fg);
break;
case 2:
tip=TTF_RenderText_Blended(fontText,"Delete",fg);
break;
case 3:
tip=TTF_RenderText_Blended(fontText,"Configure",fg);
break;
case 4:
tip=TTF_RenderText_Blended(fontText,"Play",fg);
break;
case 6:
tip=TTF_RenderText_Blended(fontText,"Level settings",fg);
break;
case 7:
tip=TTF_RenderText_Blended(fontText,"Save level",fg);
break;
case 8:
tip=TTF_RenderText_Blended(fontText,"Back to menu",fg);
break;
default:
break;
}
//Draw only if there's a tooltip available
if(tip!=NULL){
SDL_Rect r={180+(tooltip*40)+(tooltip*10),555,40,40};
r.y=550-tip->h;
if(r.x+tip->w>SCREEN_WIDTH-50)
r.x=SCREEN_WIDTH-50-tip->w;
//Draw borders around text
Uint32 color=0xFFFFFF00|230;
drawGUIBox(r.x-2,r.y-2,tip->w+4,tip->h+4,screen,color);
//Draw tooltip's text
SDL_BlitSurface(tip,NULL,screen,&r);
SDL_FreeSurface(tip);
}
}
//Draw a rectangle around the current tool.
Uint32 color=0xFFFFFF00;
drawGUIBox(180+(tool*40)+(tool*10),554,42,42,screen,color);
}
}
void LevelEditor::renderHUD(){
//Switch the tool.
switch(tool){
case CONFIGURE:
//If moving show the moving speed in the top right corner.
if(moving){
SDL_Rect r={620,0,180,30};
SDL_FillRect(screen,&r,0);
//Shrink the rectangle by one pixel and fill with white leaving an one pixel border.
r.x+=1;
r.w-=2;
r.h-=1;
SDL_FillRect(screen,&r,0xFFFFFF);
//Now render the text.
SDL_Color black={0,0,0,0};
SDL_Color white={255,255,255,255};
char s[64];
sprintf(s,"%d",movingSpeed);
SDL_Surface* bm=TTF_RenderText_Shaded(fontText,("Movespeed: "+string(s)).c_str(),black,white);
r.x+=2;
r.y+=2;
//Draw the text and free the surface.
SDL_BlitSurface(bm,NULL,screen,&r);
SDL_FreeSurface(bm);
}
break;
default:
break;
}
}
void LevelEditor::showCurrentObject(){
//Get the current mouse location.
int x,y;
SDL_GetMouseState(&x,&y);
x+=camera.x;
y+=camera.y;
//Check if we should snap the block to grid or not.
if(!pressedShift){
snapToGrid(&x,&y);
}else{
x-=25;
y-=25;
}
//Check if the currentType is a legal type.
if(currentType>=0 && currentType<EDITOR_ORDER_MAX){
ThemeBlock* obj=objThemes.getBlock(editorTileOrder[currentType]);
if(obj){
obj->editorPicture.draw(placement,x-camera.x,y-camera.y);
}
}
}
void LevelEditor::showSelectionDrag(){
//Get the current mouse location.
int x,y;
SDL_GetMouseState(&x,&y);
//Create the rectangle.
x+=camera.x;
y+=camera.y;
//Check if we should snap the block to grid or not.
if(!pressedShift){
snapToGrid(&x,&y);
}else{
x-=25;
y-=25;
}
//Check if the drag center isn't null.
if(dragCenter==NULL) return;
//The location of the dragCenter.
SDL_Rect r=dragCenter->getBox();
//Loop through the selection.
//TODO: Check if block is in sight.
for(unsigned int o=0; o<selection.size(); o++){
ThemeBlock* obj=objThemes.getBlock(selection[o]->type);
if(obj){
SDL_Rect r1=selection[o]->getBox();
obj->editorPicture.draw(placement,(r1.x-r.x)+x-camera.x,(r1.y-r.y)+y-camera.y);
}
}
}
void LevelEditor::showConfigure(){
//arrow animation value. go through 0-65535 and loops.
static unsigned short arrowAnimation=0;
arrowAnimation++;
//Draw the trigger lines.
{
map<GameObject*,vector<GameObject*> >::iterator it;
for(it=triggers.begin();it!=triggers.end();it++){
//Check if the trigger has linked targets.
if(!(*it).second.empty()){
//The location of the trigger.
SDL_Rect r=(*it).first->getBox();
//Loop through the targets.
for(unsigned int o=0;o<(*it).second.size();o++){
//Get the location of the target.
SDL_Rect r1=(*it).second[o]->getBox();
//Draw the line from the center of the trigger to the center of the target.
drawLineWithArrow(r.x-camera.x+25,r.y-camera.y+25,r1.x-camera.x+25,r1.y-camera.y+25,placement,0,32,arrowAnimation%32);
//Also draw two selection marks.
applySurface(r.x-camera.x+25-2,r.y-camera.y+25-2,selectionMark,screen,NULL);
applySurface(r1.x-camera.x+25-2,r1.y-camera.y+25-2,selectionMark,screen,NULL);
}
}
}
//Draw a line to the mouse from the linkingTrigger when linking.
if(linking){
//Get the current mouse location.
int x,y;
SDL_GetMouseState(&x,&y);
//Draw the line from the center of the trigger to mouse.
drawLineWithArrow(linkingTrigger->getBox().x-camera.x+25,linkingTrigger->getBox().y-camera.y+25,x,y,placement,0,32,arrowAnimation%32);
}
}
//Draw the moving positions.
map<GameObject*,vector<MovingPosition> >::iterator it;
for(it=movingBlocks.begin();it!=movingBlocks.end();it++){
//Check if the block has positions.
if(!(*it).second.empty()){
//The location of the moving block.
SDL_Rect block=(*it).first->getBox();
block.x+=25-camera.x;
block.y+=25-camera.y;
//The location of the previous position.
//The first time it's the moving block's position self.
SDL_Rect r=block;
//Loop through the positions.
for(unsigned int o=0;o<(*it).second.size();o++){
//Draw the line from the center of the previous position to the center of the position.
//x and y are the coordinates for the current moving position.
int x=block.x+(*it).second[o].x;
int y=block.y+(*it).second[o].y;
//Check if we need to draw line
double dx=r.x-x;
double dy=r.y-y;
double d=sqrt(dx*dx+dy*dy);
if(d>0.001f){
if(it->second[o].time>0){
//Calculate offset to contain the moving speed.
int offset=int(d*arrowAnimation/it->second[o].time)%32;
drawLineWithArrow(r.x,r.y,x,y,placement,0,32,offset);
}else{
//time==0 ???? so don't draw arrow at all
drawLine(r.x,r.y,x,y,placement);
}
}
//And draw a marker at the end.
applySurface(x-13,y-13,movingMark,screen,NULL);
//Get the box of the previous position.
SDL_Rect tmp={x,y,0,0};
r=tmp;
}
}
}
//Draw a line to the mouse from the previous moving pos.
if(moving){
//Get the current mouse location.
int x,y;
SDL_GetMouseState(&x,&y);
//Check if we should snap the block to grid or not.
if(!pressedShift){
x+=camera.x;
y+=camera.y;
snapToGrid(&x,&y);
x-=camera.x;
y-=camera.y;
}else{
x-=25;
y-=25;
}
int posX,posY;
//Check if there are moving positions for the moving block.
if(!movingBlocks[movingBlock].empty()){
//Draw the line from the center of the previouse moving positions to mouse.
posX=movingBlocks[movingBlock].back().x;
posY=movingBlocks[movingBlock].back().y;
posX-=camera.x;
posY-=camera.y;
posX+=movingBlock->getBox().x;
posY+=movingBlock->getBox().y;
}else{
//Draw the line from the center of the movingblock to mouse.
posX=movingBlock->getBox().x-camera.x;
posY=movingBlock->getBox().y-camera.y;
}
//Calculate offset to contain the moving speed.
int offset=int(double(arrowAnimation)*movingSpeed/10.0)%32;
drawLineWithArrow(posX+25,posY+25,x+25,y+25,placement,0,32,offset);
applySurface(x+12,y+12,movingMark,screen,NULL);
}
}
//Filling the order array
const int LevelEditor::editorTileOrder[EDITOR_ORDER_MAX]={
TYPE_BLOCK,
TYPE_SHADOW_BLOCK,
TYPE_SPIKES,
TYPE_FRAGILE,
TYPE_MOVING_BLOCK,
TYPE_MOVING_SHADOW_BLOCK,
TYPE_MOVING_SPIKES,
TYPE_CONVEYOR_BELT,
TYPE_SHADOW_CONVEYOR_BELT,
TYPE_BUTTON,
TYPE_SWITCH,
TYPE_PORTAL,
TYPE_SWAP,
TYPE_CHECKPOINT,
TYPE_NOTIFICATION_BLOCK,
TYPE_START_PLAYER,
TYPE_START_SHADOW,
TYPE_EXIT
};
diff --git a/src/Main.cpp b/src/Main.cpp
index a68322c..e6d56a5 100644
--- a/src/Main.cpp
+++ b/src/Main.cpp
@@ -1,179 +1,182 @@
/****************************************************************************
** 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 "Functions.h"
#include "Timer.h"
#include "Objects.h"
#include "Globals.h"
#include "TitleMenu.h"
#include "GUIObject.h"
#include "InputManager.h"
#include "MD5.h"
#include <SDL/SDL.h>
#include <stdlib.h>
#include <time.h>
#include <stdio.h>
#include <string.h>
+#ifdef HARDWARE_ACCELERATION
+#include <GL/gl.h>
+#include <GL/glu.h>
+#endif
+
//Variables for recording.
//To enable picture recording uncomment the next line:
//#define RECORD_PICUTRE_SEQUENCE
#ifdef RECORD_PICUTRE_SEQUENCE
bool recordPictureSequence=false;
int recordPictureIndex=0;
#endif
-
int main(int argc, char** argv) {
#ifdef _MSC_VER
//Fix the non-latin file name bug under Visual Studio
setlocale(LC_ALL,"");
#endif
//First parse the comand line arguments.
if(!parseArguments(argc,argv)){
printf("Usage: %s [OPTIONS] ...\n",argv[0]);
printf("Avaliable options:\n");
printf(" %-20s %s\n","--data-dir <dir>","Specifies the data directory.");
printf(" %-20s %s\n","--user-dir <dir>","Specifies the user preferences directory.");
printf(" %-20s %s\n","--version","Display the version and quit.");
printf(" %-20s %s\n","--help","Display this help.");
return 0;
}
- //Initialise some stuff like SDL, the window, SDL_Mixer.
- if(init()==false) {
- fprintf(stderr,"FATAL ERROR: Failed to initalize game.\n");
- return 1;
- }
//Try to configure the dataPath, userPath, etc...
if(configurePaths()==false){
fprintf(stderr,"FATAL ERROR: Failed to configure paths.\n");
return 1;
}
- //Load some important files like the background music, default theme.
- if(loadFiles()==false){
- fprintf(stderr,"FATAL ERROR: Failed to load necessary files.\n");
- return 1;
- }
//Load the settings.
if(loadSettings()==false){
fprintf(stderr,"FATAL ERROR: Failed to load config file.\n");
return 1;
}
+ //Initialise some stuff like SDL, the window, SDL_Mixer.
+ if(init()==false) {
+ fprintf(stderr,"FATAL ERROR: Failed to initalize game.\n");
+ return 1;
+ }
+ //Load some important files like the background music, default theme.
+ if(loadFiles()==false){
+ fprintf(stderr,"FATAL ERROR: Failed to load necessary files.\n");
+ return 1;
+ }
+
//Load key config. Then initalize joystick support.
inputMgr.loadConfig();
inputMgr.openAllJoysitcks();
//Load the configured music list.
getMusicManager()->loadMusicList((getDataPath()+"music/"+getSettings()->getValue("musiclist")+".list"));
getMusicManager()->setMusicList(getSettings()->getValue("musiclist"));
//Set the currentState id to the main menu and create it.
stateID=STATE_MENU;
currentState=new Menu();
//Seed random.
srand((unsigned)time(NULL));
//Check if sound is enabled.
if(getSettings()->getBoolValue("music"))
getMusicManager()->setEnabled();
-
- //Check if we should go fullscreen.
- if(getSettings()->getBoolValue("fullscreen"))
- SDL_SetVideoMode(screen->w,screen->h,screen->format->BitsPerPixel, SDL_FULLSCREEN | SDL_HWSURFACE);
+ //Create the temp surface, just a replica of the screen surface.
tempSurface=SDL_CreateRGBSurface(SDL_HWSURFACE|SDL_SRCALPHA,
screen->w,screen->h,screen->format->BitsPerPixel,
screen->format->Rmask,screen->format->Gmask,screen->format->Bmask,0);
int fadeIn=0;
//Start the game loop.
while(stateID!=STATE_EXIT){
//We start the timer.
FPS.start();
//Loop the SDL events.
while(SDL_PollEvent(&event)){
#ifdef RECORD_PICUTRE_SEQUENCE
if(event.type==SDL_KEYDOWN && event.key.keysym.sym==SDLK_F10){
recordPictureSequence=!recordPictureSequence;
printf("Record Picture Sequence %s\n",recordPictureSequence?"ON":"OFF");
}
#endif
//Let the input manager handle the events.
inputMgr.updateState(true);
//Let the currentState handle the events.
currentState->handleEvents();
//Also pass the events to the GUI.
GUIObjectHandleEvents();
}
//update input state (??)
inputMgr.updateState(false);
//Now it's time for the state to do his logic.
currentState->logic();
currentState->render();
//TODO: Shouldn't the gamestate take care of rendering the GUI?
if(GUIObjectRoot) GUIObjectRoot->render();
if(fadeIn>0&&fadeIn<255){
SDL_BlitSurface(screen,NULL,tempSurface,NULL);
SDL_FillRect(screen,NULL,0);
SDL_SetAlpha(tempSurface, SDL_SRCALPHA, fadeIn);
SDL_BlitSurface(tempSurface,NULL,screen,NULL);
fadeIn+=17;
}
#ifdef RECORD_PICUTRE_SEQUENCE
if(recordPictureSequence){
char s[64];
recordPictureIndex++;
sprintf(s,"pic%08d.bmp",recordPictureIndex);
printf("Save screen to %s\n",s);
SDL_SaveBMP(screen,(getUserPath(USER_CACHE)+s).c_str());
}
#endif
- SDL_Flip(screen);
+ //And draw the screen surface to the
+ flipScreen();
if(nextState!=STATE_NULL){
fadeIn=17;
changeState();
}
int t=FPS.getTicks();
t=(1000/g_FPS)-t;
if(t>0){
SDL_Delay(t);
}
}
//close all joysticks.
inputMgr.closeAllJoysticks();
//The game has ended, save the settings just to be sure.
saveSettings();
SDL_FreeSurface(tempSurface);
levels.saveLevelProgress();
clean();
//End of program.
return 0;
}
diff --git a/src/Settings.cpp b/src/Settings.cpp
index 110e923..467963b 100644
--- a/src/Settings.cpp
+++ b/src/Settings.cpp
@@ -1,226 +1,233 @@
/****************************************************************************
** 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 "Settings.h"
#include <SDL/SDL.h>
#include <string>
#include <stdio.h>
using namespace std;
Settings::Settings(const string fileName): fileName(fileName){
char s[32];
settings["sound"]="1";
settings["music"]="1";
settings["musiclist"]="default";
settings["fullscreen"]="0";
+ settings["width"]="800";
+ settings["height"]="600";
+#ifdef HARDWARE_ACCELERATION
+ settings["gl"]="1";
+#else
+ settings["gl"]="0";
+#endif
settings["theme"]="Cloudscape";
settings["leveltheme"]="1";
settings["internet"]="1";
settings["lastlevelpack"]="tutorial";
settings["internet-proxy"]="";
//key settings
sprintf(s,"%d",(int)SDLK_UP);
settings["key_up"]=settings["key_jump"]=s;
sprintf(s,"%d",(int)SDLK_DOWN);
settings["key_down"]=settings["key_action"]=s; //SDLK_DOWN
sprintf(s,"%d",(int)SDLK_LEFT);
settings["key_left"]=s; //SDLK_LEFT
sprintf(s,"%d",(int)SDLK_RIGHT);
settings["key_right"]=s; //SDLK_RIGHT
sprintf(s,"%d",(int)SDLK_SPACE);
settings["key_space"]=s; //SDLK_SPACE
sprintf(s,"%d",(int)SDLK_ESCAPE);
settings["key_escape"]=s; //SDLK_ESCAPE
sprintf(s,"%d",(int)SDLK_r);
settings["key_restart"]=s; //SDLK_r
sprintf(s,"%d",(int)SDLK_TAB);
settings["key_tab"]=s;
sprintf(s,"%d",(int)SDLK_F2);
settings["key_save"]=s; //SDLK_F2
sprintf(s,"%d",(int)SDLK_F3);
settings["key_load"]=s; //SDLK_F3
sprintf(s,"%d",(int)SDLK_F4);
settings["key_swap"]=s; //SDLK_F4
sprintf(s,"%d",(int)SDLK_F5);
settings["key_teleport"]=s; //SDLK_F5
sprintf(s,"%d",(int)SDLK_F12);
settings["key_suicide"]=s;
sprintf(s,"%d",(int)SDLK_RSHIFT);
settings["key_shift"]=s; //SDLK_RSHIFT
sprintf(s,"%d",(int)SDLK_PAGEUP);
settings["key_next"]=s; //SDLK_PAGEUP
sprintf(s,"%d",(int)SDLK_PAGEDOWN);
settings["key_previous"]=s; //SDLK_PAGEDOWN
settings["key_up2"]=settings["key_down2"]=settings["key_left2"]=settings["key_right2"]=
settings["key_jump2"]=settings["key_action2"]=
settings["key_space2"]=settings["key_escape2"]=settings["key_restart2"]=settings["key_tab2"]=
settings["key_save2"]=settings["key_load2"]=settings["key_swap2"]=settings["key_teleport2"]=
settings["key_suicide2"]=settings["key_shift2"]=settings["key_next2"]=settings["key_previous2"]="0";
}
void Settings::parseFile(){
//We open the settings file.
ifstream file;
file.open(fileName.c_str());
if(!file){
cout<<"Can't find config file!"<<endl;
createFile();
}
//Now we're going to walk throught the file line by line.
string line;
while(getline(file,line)){
string temp = line;
unComment(temp);
if(temp.empty() || empty(temp))
continue;
//The line is good so we parse it.
parseLine(temp);
}
//And close the file.
file.close();
}
void Settings::parseLine(const string &line){
if((line.find('=') == line.npos) || !validLine(line))
cout<<"Warning illegal line in config file!"<<endl;
string temp = line;
temp.erase(0, temp.find_first_not_of("\t "));
int seperator = temp.find('=');
//Get the key and trim it.
string key, value;
key = line.substr(0, seperator);
if(key.find('\t')!=line.npos || key.find(' ')!=line.npos)
key.erase(key.find_first_of("\t "));
//Get the value and trim it.
value = line.substr(seperator + 1);
value.erase(0, value.find_first_not_of("\t "));
value.erase(value.find_last_not_of("\t ") + 1);
//Add the setting to the settings map.
setValue(key,value);
}
bool Settings::validLine(const string &line){
string temp = line;
temp.erase(0, temp.find_first_not_of("\t "));
if(temp[0] == '=')
return false;
for(size_t i = temp.find('=') + 1; i < temp.length(); i++)
return true;
return false;
}
void Settings::unComment(string &line){
if (line.find('#') != line.npos)
line.erase(line.find('#'));
}
bool Settings::empty(const string &line){
return (line.find_first_not_of(' ')==line.npos);
}
string Settings::getValue(const string &key){
if(settings.find(key) == settings.end()){
cout<<"Key "<<key<<" couldn't be found!"<<endl;
return "";
}
return settings[key];
}
bool Settings::getBoolValue(const string &key){
if(settings.find(key) == settings.end()){
cout<<"Key "<<key<<" couldn't be found!"<<endl;
return false;
}
return (settings[key] == "1");
}
void Settings::setValue(const string &key, const string &value){
if(settings.find(key) == settings.end()){
cout<<"Key "<<key<<" couldn't be found!"<<endl;
return;
}
settings[key]=value;
}
void Settings::createFile(){
ofstream file;
file.open(fileName.c_str());
//Default Config file.
file<<"#MeAndMyShadow config file. Created on "<<endl;
map<string, string>::iterator iter;
for(iter = settings.begin(); iter != settings.end(); ++iter){
file << iter->first << " = " << iter->second << endl;
}
//And close the file.
file.close();
}
void Settings::save(){
ofstream file;
file.open(fileName.c_str());
//First get the date and time.
time_t rawtime;
struct tm* timedate;
time(&rawtime);
timedate=localtime(&rawtime);
//Now write it to the first line of the config file.
//Note: There's no endl at the end since that's already in asctime(timeinfo).
file<<"#MeAndMyShadow config file. Created on "<<asctime(timedate);
//Loop through the settings and save them.
map<string,string>::const_iterator iter;
for(iter=settings.begin(); iter!=settings.end(); ++iter){
file<<iter->first<<" = "<<iter->second<<endl;
}
file.close();
}
diff --git a/src/config.h.in b/src/config.h.in
index cdf6f45..1507ac9 100644
--- a/src/config.h.in
+++ b/src/config.h.in
@@ -1,10 +1,13 @@
#ifndef CONFIG_H
#define CONFIG_H
#if defined(WIN32)
//#define DATA_PATH
#else
#define DATA_PATH "@CMAKE_INSTALL_PREFIX@/share/meandmyshadow/data/"
#endif
+#cmakedefine HARDWARE_ACCELERATION
+#cmakedefine DEBUG_MODE
+
#endif
\ No newline at end of file

File Metadata

Mime Type
text/x-diff
Expires
Tue, Jun 16, 12:57 AM (2 w, 1 d ago)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
71609
Default Alt Text
(177 KB)

Event Timeline