From 9c511ea692d3a2339597211f08f18ea74fad35ec Mon Sep 17 00:00:00 2001 From: =?utf8?q?Tobias=20Gl=C3=A4=C3=9Fer?= Date: Tue, 20 Jul 2004 17:51:39 +0000 Subject: [PATCH] Generated SuperTux libtool library containing more general source, that could prove useful for other applications/games. Caution: It's not yet SuperTux independed, more work on this will follow, that's just the first step. The file structure isn't fixed, better ideas will surely find there way in it! SVN-Revision: 1592 --- lib/Makefile.am | 32 + lib/app/defines.h | 101 ++++ lib/app/gettext.h | 81 +++ lib/app/globals.cpp | 124 ++++ lib/app/globals.h | 83 +++ lib/app/setup.cpp | 936 +++++++++++++++++++++++++++++ lib/app/setup.h | 52 ++ lib/audio/musicref.cpp | 66 ++ lib/audio/musicref.h | 45 ++ lib/audio/sound.cpp | 77 +++ lib/audio/sound.h | 53 ++ lib/audio/sound_manager.cpp | 188 ++++++ lib/audio/sound_manager.h | 74 +++ lib/gui/button.cpp | 324 ++++++++++ lib/gui/button.h | 98 +++ lib/gui/menu.cpp | 914 ++++++++++++++++++++++++++++ lib/gui/menu.h | 194 ++++++ lib/gui/mousecursor.cpp | 96 +++ lib/gui/mousecursor.h | 61 ++ lib/math/physic.cpp | 140 +++++ lib/math/physic.h | 80 +++ lib/math/vector.cpp | 32 + lib/math/vector.h | 96 +++ lib/special/base.h | 40 ++ lib/special/game_object.cpp | 30 + lib/special/game_object.h | 71 +++ lib/special/moving_object.cpp | 31 + lib/special/moving_object.h | 61 ++ lib/special/sprite.cpp | 129 ++++ lib/special/sprite.h | 80 +++ lib/special/sprite_manager.cpp | 100 ++++ lib/special/sprite_manager.h | 47 ++ lib/special/stringlist.cpp | 119 ++++ lib/special/stringlist.h | 42 ++ lib/special/timer.cpp | 166 ++++++ lib/special/timer.h | 69 +++ lib/utils/configfile.cpp | 143 +++++ lib/utils/configfile.h | 40 ++ lib/utils/exceptions.h | 44 ++ lib/utils/lispreader.cpp | 1293 ++++++++++++++++++++++++++++++++++++++++ lib/utils/lispreader.h | 204 +++++++ lib/utils/lispwriter.cpp | 128 ++++ lib/utils/lispwriter.h | 56 ++ lib/video/drawing_context.cpp | 353 +++++++++++ lib/video/drawing_context.h | 163 +++++ lib/video/font.cpp | 267 +++++++++ lib/video/font.h | 76 +++ lib/video/screen.cpp | 299 ++++++++++ lib/video/screen.h | 64 ++ lib/video/surface.cpp | 877 +++++++++++++++++++++++++++ lib/video/surface.h | 180 ++++++ 51 files changed, 9119 insertions(+) create mode 100644 lib/Makefile.am create mode 100644 lib/app/defines.h create mode 100644 lib/app/gettext.h create mode 100644 lib/app/globals.cpp create mode 100644 lib/app/globals.h create mode 100644 lib/app/setup.cpp create mode 100644 lib/app/setup.h create mode 100644 lib/audio/musicref.cpp create mode 100644 lib/audio/musicref.h create mode 100644 lib/audio/sound.cpp create mode 100644 lib/audio/sound.h create mode 100644 lib/audio/sound_manager.cpp create mode 100644 lib/audio/sound_manager.h create mode 100644 lib/gui/button.cpp create mode 100644 lib/gui/button.h create mode 100644 lib/gui/menu.cpp create mode 100644 lib/gui/menu.h create mode 100644 lib/gui/mousecursor.cpp create mode 100644 lib/gui/mousecursor.h create mode 100644 lib/math/physic.cpp create mode 100644 lib/math/physic.h create mode 100644 lib/math/vector.cpp create mode 100644 lib/math/vector.h create mode 100644 lib/special/base.h create mode 100644 lib/special/game_object.cpp create mode 100644 lib/special/game_object.h create mode 100644 lib/special/moving_object.cpp create mode 100644 lib/special/moving_object.h create mode 100644 lib/special/sprite.cpp create mode 100644 lib/special/sprite.h create mode 100644 lib/special/sprite_manager.cpp create mode 100644 lib/special/sprite_manager.h create mode 100644 lib/special/stringlist.cpp create mode 100644 lib/special/stringlist.h create mode 100644 lib/special/timer.cpp create mode 100644 lib/special/timer.h create mode 100644 lib/utils/configfile.cpp create mode 100644 lib/utils/configfile.h create mode 100644 lib/utils/exceptions.h create mode 100644 lib/utils/lispreader.cpp create mode 100644 lib/utils/lispreader.h create mode 100644 lib/utils/lispwriter.cpp create mode 100644 lib/utils/lispwriter.h create mode 100644 lib/video/drawing_context.cpp create mode 100644 lib/video/drawing_context.h create mode 100644 lib/video/font.cpp create mode 100644 lib/video/font.h create mode 100644 lib/video/screen.cpp create mode 100644 lib/video/screen.h create mode 100644 lib/video/surface.cpp create mode 100644 lib/video/surface.h diff --git a/lib/Makefile.am b/lib/Makefile.am new file mode 100644 index 000000000..a72e6862e --- /dev/null +++ b/lib/Makefile.am @@ -0,0 +1,32 @@ +INCLUDES = +METASOURCES = AUTO +lib_LTLIBRARIES = libsupertux.la +libsupertux_la_SOURCES =app/defines.h \ + app/gettext.h \ + app/globals.h app/globals.cpp \ + app/setup.h app/setup.cpp \ + audio/musicref.h audio/musicref.cpp \ + audio/sound.h audio/sound.cpp \ + audio/sound_manager.h audio/sound_manager.cpp \ + gui/button.h gui/button.cpp \ + gui/menu.h gui/menu.cpp \ + gui/mousecursor.cpp gui/mousecursor.h \ + math/physic.h math/physic.cpp \ + math/vector.h math/vector.cpp \ + special/base.h \ + special/game_object.h special/game_object.cpp \ + special/moving_object.h special/moving_object.cpp \ + special/sprite.h special/sprite.cpp \ + special/sprite_manager.h special/sprite_manager.cpp \ + special/stringlist.h special/stringlist.cpp \ + special/timer.h special/timer.cpp \ + utils/configfile.h utils/configfile.cpp \ + utils/exceptions.h \ + utils/lispreader.h utils/lispreader.cpp \ + utils/lispwriter.h utils/lispwriter.cpp \ + video/drawing_context.h video/drawing_context.cpp \ + video/font.h video/font.cpp \ + video/screen.h video/screen.cpp \ + video/surface.h video/surface.cpp +libsupertux_la_LDFLAGS = -module + diff --git a/lib/app/defines.h b/lib/app/defines.h new file mode 100644 index 000000000..8d1e18459 --- /dev/null +++ b/lib/app/defines.h @@ -0,0 +1,101 @@ +// $Id$ +// +// SuperTux +// Copyright (C) 2000 Bill Kendrick +// Copyright (C) 2004 Tobias Glaesser +// +// This program is free software; you can redistribute it and/or +// modify it under the terms of the GNU General Public License +// as published by the Free Software Foundation; either version 2 +// of the License, or (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software +// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA +// 02111-1307, USA. + +#ifndef SUPERTUX_DEFINES_H +#define SUPERTUX_DEFINES_H 1 + +#include + +/* Version: */ + +#ifndef VERSION + #define VERSION "0.1.1" +#endif + +enum Direction { LEFT = 0, RIGHT = 1 }; + +/* Direction (keyboard/joystick) states: */ + +#define UP 0 +#define DOWN 1 + +/* Dying types: */ + +/* ---- NO 0 */ +enum DyingType { + DYING_NOT = 0, + DYING_SQUISHED = 1, + DYING_FALLING = 2 +}; + +/* Screen-related stuff */ +// +1 is needed because when tiles are wrapping around the screen there +// are two partial tiles on the screen +#define VISIBLE_TILES_X (25 +1) +#define VISIBLE_TILES_Y (19 +1) + +/* Speed constraints: */ + +#define MAX_WALK_XM 2.3 +#define MAX_RUN_XM 3.2 +#define MAX_YM 20.0 +#define MAX_JUMP_TIME 375 +#define MAX_LIVES 99 + +#define WALK_SPEED 1.0 +#define RUN_SPEED 1.5 +#define JUMP_SPEED 1.2 + +/* gameplay related defines */ + +#define START_LIVES 4 + +#define MAX_FIRE_BULLETS 2 +#define MAX_ICE_BULLETS 1 +#define FROZEN_TIME 3000 + +#define YM_FOR_JUMP 6.0 +#define WALK_ACCELERATION_X 0.03 +#define RUN_ACCELERATION_X 0.04 +#define KILL_BOUNCE_YM 8.0 + +#define SKID_XM 2.0 +#define SKID_TIME 200 + +/* Size constraints: */ + +#define X_OFFSCREEN_DISTANCE (screen->w/2) +#define Y_OFFSCREEN_DISTANCE (screen->h/2) + +/* Debugging */ + +#ifdef DEBUG + #define DEBUG_MSG( msg ) { \ + printf( msg ); printf("\n"); \ + } + #else + #define DEBUG_MSG( msg ) {} +#endif + +#define UNUSED_ARG(a) do {/* null */} while (&a == 0) + +#endif /*SUPERTUX_DEFINES_H*/ + diff --git a/lib/app/gettext.h b/lib/app/gettext.h new file mode 100644 index 000000000..c9b81e3b2 --- /dev/null +++ b/lib/app/gettext.h @@ -0,0 +1,81 @@ +/* Convenience header for conditional use of GNU . + Copyright (C) 1995-1998, 2000-2002 Free Software Foundation, Inc. + + This program is free software; you can redistribute it and/or modify it + under the terms of the GNU Library General Public License as published + by the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public + License along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, + USA. */ + +#ifndef _LIBGETTEXT_H +#define _LIBGETTEXT_H 1 + +#ifdef HAVE_GETTEXT +# define _(String) gettext(String) +# define N_(String) gettext_noop(String) +#else +# define _(String) String +# define N_(String) String +#endif + +/* NLS can be disabled through the configure --disable-nls option. */ +#if ENABLE_NLS + +/* Get declarations of GNU message catalog functions. */ +#include + +#else + +/* Solaris /usr/include/locale.h includes /usr/include/libintl.h, which + chokes if dcgettext is defined as a macro. So include it now, to make + later inclusions of a NOP. We don't include + as well because people using "gettext.h" will not include , + and also including would fail on SunOS 4, whereas + is OK. */ +#if defined(__sun) +# include +#endif + +#ifndef gettext + +/* Disabled NLS. + The casts to 'const char *' serve the purpose of producing warnings + for invalid uses of the value returned from these functions. + On pre-ANSI systems without 'const', the config.h file is supposed to + contain "#define const". */ +# define gettext(Msgid) ((const char *) (Msgid)) +# define dgettext(Domainname, Msgid) ((const char *) (Msgid)) +# define dcgettext(Domainname, Msgid, Category) ((const char *) (Msgid)) +# define ngettext(Msgid1, Msgid2, N) \ + ((N) == 1 ? (const char *) (Msgid1) : (const char *) (Msgid2)) +# define dngettext(Domainname, Msgid1, Msgid2, N) \ + ((N) == 1 ? (const char *) (Msgid1) : (const char *) (Msgid2)) +# define dcngettext(Domainname, Msgid1, Msgid2, N, Category) \ + ((N) == 1 ? (const char *) (Msgid1) : (const char *) (Msgid2)) +# define textdomain(Domainname) ((const char *) (Domainname)) +# define bindtextdomain(Domainname, Dirname) ((const char *) (Dirname)) +# define bind_textdomain_codeset(Domainname, Codeset) ((const char *) (Codeset)) + +#endif + +#endif + +/* A pseudo function call that serves as a marker for the automated + extraction of messages, but does not call gettext(). The run-time + translation is done at a different place in the code. + The argument, String, should be a literal string. Concatenated strings + and other string expressions won't work. + The macro's expansion is not parenthesized, so that it is suitable as + initializer for static 'char[]' or 'const char[]' variables. */ +#define gettext_noop(String) String + +#endif /* _LIBGETTEXT_H */ diff --git a/lib/app/globals.cpp b/lib/app/globals.cpp new file mode 100644 index 000000000..035f7b52d --- /dev/null +++ b/lib/app/globals.cpp @@ -0,0 +1,124 @@ +// $Id$ +// +// SuperTux +// Copyright (C) 2004 SuperTux Development Team, see AUTHORS for details +// +// This program is free software; you can redistribute it and/or +// modify it under the terms of the GNU General Public License +// as published by the Free Software Foundation; either version 2 +// of the License, or (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software +// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA +// 02111-1307, USA. + +#include "app/globals.h" + +/** The datadir prefix prepended when loading game data file */ +std::string datadir; + +JoystickKeymap::JoystickKeymap() +{ + a_button = 0; + b_button = 1; + start_button = 2; + + x_axis = 0; + y_axis = 1; + + dead_zone = 4096; +} + +JoystickKeymap joystick_keymap; + +SDL_Surface * screen; +Font* gold_text; +Font* blue_text; +Font* gray_text; +Font* yellow_nums; +Font* white_text; +Font* white_small_text; +Font* white_big_text; + +MouseCursor * mouse_cursor; + +bool use_gl; +bool use_joystick; +bool use_fullscreen; +bool debug_mode; +bool show_fps; +float game_speed = 1.0f; + +int joystick_num = 0; +char* level_startup_file = 0; +bool launch_leveleditor_mode = false; +bool launch_worldmap_mode = false; + +/* SuperTux directory ($HOME/.supertux) and save directory($HOME/.supertux/save) */ +char *st_dir, *st_save_dir; + +SDL_Joystick * js; + +/* Returns 1 for every button event, 2 for a quit event and 0 for no event. */ +int wait_for_event(SDL_Event& event,unsigned int min_delay, unsigned int max_delay, bool empty_events) +{ + int i; + Timer maxdelay; + Timer mindelay; + + maxdelay.init(false); + mindelay.init(false); + + if(max_delay < min_delay) + max_delay = min_delay; + + maxdelay.start(max_delay); + mindelay.start(min_delay); + + if(empty_events) + while (SDL_PollEvent(&event)) + {} + + /* Handle events: */ + + for(i = 0; maxdelay.check() || !i; ++i) + { + while (SDL_PollEvent(&event)) + { + if(!mindelay.check()) + { + if (event.type == SDL_QUIT) + { + /* Quit event - quit: */ + return 2; + } + else if (event.type == SDL_KEYDOWN) + { + /* Keypress - skip intro: */ + + return 1; + } + else if (event.type == SDL_JOYBUTTONDOWN) + { + /* Fire button - skip intro: */ + + return 1; + } + else if (event.type == SDL_MOUSEBUTTONDOWN) + { + /* Mouse button - skip intro: */ + return 1; + } + } + } + SDL_Delay(10); + } + + return 0; +} diff --git a/lib/app/globals.h b/lib/app/globals.h new file mode 100644 index 000000000..ca6528975 --- /dev/null +++ b/lib/app/globals.h @@ -0,0 +1,83 @@ +// $Id$ +// +// SuperTux +// Copyright (C) 2004 Bill Kendrick +// Tobias Glaesser +// Ingo Ruhnke +// +// This program is free software; you can redistribute it and/or +// modify it under the terms of the GNU General Public License +// as published by the Free Software Foundation; either version 2 +// of the License, or (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software +// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + +#ifndef SUPERTUX_GLOBALS_H +#define SUPERTUX_GLOBALS_H + +#include + +#include "SDL.h" + +#include "video/font.h" +#include "gui/menu.h" +#include "gui/mousecursor.h" + +extern std::string datadir; + +struct JoystickKeymap +{ + int a_button; + int b_button; + int start_button; + + int x_axis; + int y_axis; + + int dead_zone; + + JoystickKeymap(); +}; + +extern JoystickKeymap joystick_keymap; + +extern SDL_Surface* screen; +extern Font* gold_text; +extern Font* white_text; +extern Font* blue_text; +extern Font* gray_text; +extern Font* white_small_text; +extern Font* white_big_text; +extern Font* yellow_nums; + +extern MouseCursor * mouse_cursor; + +extern bool use_gl; +extern bool use_joystick; +extern bool use_fullscreen; +extern bool debug_mode; +extern bool show_fps; + +/** The number of the joystick that will be use in the game */ +extern int joystick_num; +extern char* level_startup_file; +extern bool launch_leveleditor_mode; +extern bool launch_worldmap_mode; + +/* SuperTux directory ($HOME/.supertux) and save directory($HOME/.supertux/save) */ +extern char* st_dir; +extern char* st_save_dir; + +extern float game_speed; +extern SDL_Joystick * js; + +int wait_for_event(SDL_Event& event,unsigned int min_delay = 0, unsigned int max_delay = 0, bool empty_events = false); + +#endif /* SUPERTUX_GLOBALS_H */ diff --git a/lib/app/setup.cpp b/lib/app/setup.cpp new file mode 100644 index 000000000..9218404c1 --- /dev/null +++ b/lib/app/setup.cpp @@ -0,0 +1,936 @@ +// $Id$ +// +// SuperTux - A Jump'n Run +// Copyright (C) 2000 Bill Kendrick +// +// This program is free software; you can redistribute it and/or +// modify it under the terms of the GNU General Public License +// as published by the Free Software Foundation; either version 2 +// of the License, or (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software +// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + +#include +#include +#include +#include +#include +#include +#include +#include + +#include "SDL.h" +#include "SDL_image.h" +#ifndef NOOPENGL +#include "SDL_opengl.h" +#endif + +#include +#include +#include +#ifndef WIN32 +#include +#endif + +#include + +#include "app/globals.h" +#include "app/defines.h" +#include "app/setup.h" +#include "video/screen.h" +#include "video/surface.h" +#include "gui/menu.h" +#include "utils/configfile.h" +#include "audio/sound_manager.h" +#include "app/gettext.h" + + +#ifdef WIN32 +#define mkdir(dir, mode) mkdir(dir) +// on win32 we typically don't want LFS paths +#undef DATA_PREFIX +#define DATA_PREFIX "./data/" +#endif + +/* Screen proprities: */ +/* Don't use this to test for the actual screen sizes. Use screen->w/h instead! */ +#define SCREEN_W 800 +#define SCREEN_H 600 + +/* Local function prototypes: */ + +void seticon(void); +void usage(char * prog, int ret); + +/* Does the given file exist and is it accessible? */ +int faccessible(const char *filename) +{ + struct stat filestat; + if (stat(filename, &filestat) == -1) + { + return false; + } + else + { + if(S_ISREG(filestat.st_mode)) + return true; + else + return false; + } +} + +/* Can we write to this location? */ +int fwriteable(const char *filename) +{ + FILE* fi; + fi = fopen(filename, "wa"); + if (fi == NULL) + { + return false; + } + return true; +} + +/* Makes sure a directory is created in either the SuperTux home directory or the SuperTux base directory.*/ +int fcreatedir(const char* relative_dir) +{ + char path[1024]; + snprintf(path, 1024, "%s/%s/", st_dir, relative_dir); + if(mkdir(path,0755) != 0) + { + snprintf(path, 1024, "%s/%s/", datadir.c_str(), relative_dir); + if(mkdir(path,0755) != 0) + { + return false; + } + else + { + return true; + } + } + else + { + return true; + } +} + +FILE * opendata(const char * rel_filename, const char * mode) +{ + char * filename = NULL; + FILE * fi; + + filename = (char *) malloc(sizeof(char) * (strlen(st_dir) + + strlen(rel_filename) + 1)); + + strcpy(filename, st_dir); + /* Open the high score file: */ + + strcat(filename, rel_filename); + + /* Try opening the file: */ + fi = fopen(filename, mode); + + if (fi == NULL) + { + fprintf(stderr, "Warning: Unable to open the file \"%s\" ", filename); + + if (strcmp(mode, "r") == 0) + fprintf(stderr, "for read!!!\n"); + else if (strcmp(mode, "w") == 0) + fprintf(stderr, "for write!!!\n"); + } + free( filename ); + + return(fi); +} + +/* Get all names of sub-directories in a certain directory. */ +/* Returns the number of sub-directories found. */ +/* Note: The user has to free the allocated space. */ +string_list_type dsubdirs(const char *rel_path,const char* expected_file) +{ + DIR *dirStructP; + struct dirent *direntp; + string_list_type sdirs; + char filename[1024]; + char path[1024]; + + string_list_init(&sdirs); + sprintf(path,"%s/%s",st_dir,rel_path); + if((dirStructP = opendir(path)) != NULL) + { + while((direntp = readdir(dirStructP)) != NULL) + { + char absolute_filename[1024]; + struct stat buf; + + sprintf(absolute_filename, "%s/%s", path, direntp->d_name); + + if (stat(absolute_filename, &buf) == 0 && S_ISDIR(buf.st_mode)) + { + if(expected_file != NULL) + { + sprintf(filename,"%s/%s/%s",path,direntp->d_name,expected_file); + if(!faccessible(filename)) + continue; + } + + string_list_add_item(&sdirs,direntp->d_name); + } + } + closedir(dirStructP); + } + + sprintf(path,"%s/%s",datadir.c_str(),rel_path); + if((dirStructP = opendir(path)) != NULL) + { + while((direntp = readdir(dirStructP)) != NULL) + { + char absolute_filename[1024]; + struct stat buf; + + sprintf(absolute_filename, "%s/%s", path, direntp->d_name); + + if (stat(absolute_filename, &buf) == 0 && S_ISDIR(buf.st_mode)) + { + if(expected_file != NULL) + { + sprintf(filename,"%s/%s/%s",path,direntp->d_name,expected_file); + if(!faccessible(filename)) + { + continue; + } + else + { + sprintf(filename,"%s/%s/%s/%s",st_dir,rel_path,direntp->d_name,expected_file); + if(faccessible(filename)) + continue; + } + } + + string_list_add_item(&sdirs,direntp->d_name); + } + } + closedir(dirStructP); + } + + return sdirs; +} + +string_list_type dfiles(const char *rel_path, const char* glob, const char* exception_str) +{ + DIR *dirStructP; + struct dirent *direntp; + string_list_type sdirs; + char path[1024]; + + string_list_init(&sdirs); + sprintf(path,"%s/%s",st_dir,rel_path); + if((dirStructP = opendir(path)) != NULL) + { + while((direntp = readdir(dirStructP)) != NULL) + { + char absolute_filename[1024]; + struct stat buf; + + sprintf(absolute_filename, "%s/%s", path, direntp->d_name); + + if (stat(absolute_filename, &buf) == 0 && S_ISREG(buf.st_mode)) + { + if(exception_str != NULL) + { + if(strstr(direntp->d_name,exception_str) != NULL) + continue; + } + if(glob != NULL) + if(strstr(direntp->d_name,glob) == NULL) + continue; + + string_list_add_item(&sdirs,direntp->d_name); + } + } + closedir(dirStructP); + } + + sprintf(path,"%s/%s",datadir.c_str(),rel_path); + if((dirStructP = opendir(path)) != NULL) + { + while((direntp = readdir(dirStructP)) != NULL) + { + char absolute_filename[1024]; + struct stat buf; + + sprintf(absolute_filename, "%s/%s", path, direntp->d_name); + + if (stat(absolute_filename, &buf) == 0 && S_ISREG(buf.st_mode)) + { + if(exception_str != NULL) + { + if(strstr(direntp->d_name,exception_str) != NULL) + continue; + } + if(glob != NULL) + if(strstr(direntp->d_name,glob) == NULL) + continue; + + string_list_add_item(&sdirs,direntp->d_name); + } + } + closedir(dirStructP); + } + + return sdirs; +} + +void free_strings(char **strings, int num) +{ + int i; + for(i=0; i < num; ++i) + free(strings[i]); +} + +/* --- SETUP --- */ +/* Set SuperTux configuration and save directories */ +void st_directory_setup(void) +{ + char *home; + char str[1024]; + /* Get home directory (from $HOME variable)... if we can't determine it, + use the current directory ("."): */ + if (getenv("HOME") != NULL) + home = getenv("HOME"); + else + home = "."; + + st_dir = (char *) malloc(sizeof(char) * (strlen(home) + + strlen("/.supertux") + 1)); + strcpy(st_dir, home); + strcat(st_dir, "/.supertux"); + + /* Remove .supertux config-file from old SuperTux versions */ + if(faccessible(st_dir)) + { + remove + (st_dir); + } + + st_save_dir = (char *) malloc(sizeof(char) * (strlen(st_dir) + strlen("/save") + 1)); + + strcpy(st_save_dir,st_dir); + strcat(st_save_dir,"/save"); + + /* Create them. In the case they exist they won't destroy anything. */ + mkdir(st_dir, 0755); + mkdir(st_save_dir, 0755); + + sprintf(str, "%s/levels", st_dir); + mkdir(str, 0755); + + // User has not that a datadir, so we try some magic + if (datadir.empty()) + { +#ifndef WIN32 + // Detect datadir + char exe_file[PATH_MAX]; + if (readlink("/proc/self/exe", exe_file, PATH_MAX) < 0) + { + puts("Couldn't read /proc/self/exe, using default path: " DATA_PREFIX); + datadir = DATA_PREFIX; + } + else + { + std::string exedir = std::string(dirname(exe_file)) + "/"; + + datadir = exedir + "../data"; // SuperTux run from source dir + if (access(datadir.c_str(), F_OK) != 0) + { + datadir = exedir + "../share/supertux"; // SuperTux run from PATH + if (access(datadir.c_str(), F_OK) != 0) + { // If all fails, fall back to compiled path + datadir = DATA_PREFIX; + } + } + } +#else + datadir = DATA_PREFIX; +#endif + } + printf("Datadir: %s\n", datadir.c_str()); +} + +void st_general_setup(void) +{ + /* Seed random number generator: */ + + srand(SDL_GetTicks()); + + /* Set icon image: */ + + seticon(); + + /* Unicode needed for input handling: */ + + SDL_EnableUNICODE(1); + + /* Load global images: */ + gold_text = new Font(datadir + "/images/fonts/gold.png", Font::TEXT, 16,18); + blue_text = new Font(datadir + "/images/fonts/blue.png", Font::TEXT, 16,18,3); + white_text = new Font(datadir + "/images/fonts/white.png", + Font::TEXT, 16,18); + gray_text = new Font(datadir + "/images/fonts/gray.png", + Font::TEXT, 16,18); + white_small_text = new Font(datadir + "/images/fonts/white-small.png", + Font::TEXT, 8,9, 1); + white_big_text = new Font(datadir + "/images/fonts/white-big.png", + Font::TEXT, 20,22, 3); + yellow_nums = new Font(datadir + "/images/fonts/numbers.png", + Font::NUM, 32,32); + + /* Load GUI/menu images: */ + checkbox = new Surface(datadir + "/images/status/checkbox.png", true); + checkbox_checked = new Surface(datadir + "/images/status/checkbox-checked.png", true); + back = new Surface(datadir + "/images/status/back.png", true); + arrow_left = new Surface(datadir + "/images/icons/left.png", true); + arrow_right = new Surface(datadir + "/images/icons/right.png", true); + + /* Load the mouse-cursor */ + mouse_cursor = new MouseCursor( datadir + "/images/status/mousecursor.png",1); + MouseCursor::set_current(mouse_cursor); + +} + +void st_general_free(void) +{ + + /* Free global images: */ + delete gold_text; + delete white_text; + delete blue_text; + delete gray_text; + delete white_small_text; + delete white_big_text; + delete yellow_nums; + + /* Free GUI/menu images: */ + delete checkbox; + delete checkbox_checked; + delete back; + delete arrow_left; + delete arrow_right; + + /* Free mouse-cursor */ + delete mouse_cursor; + + /* Free menus */ + delete main_menu; + delete game_menu; + delete options_menu; + delete options_keys_menu; + delete options_joystick_menu; + delete highscore_menu; + delete contrib_menu; + delete contrib_subset_menu; + delete save_game_menu; + delete load_game_menu; +} + +void st_video_setup(void) +{ + /* Init SDL Video: */ + if (SDL_Init(SDL_INIT_VIDEO) < 0) + { + fprintf(stderr, + "\nError: I could not initialize video!\n" + "The Simple DirectMedia error that occured was:\n" + "%s\n\n", SDL_GetError()); + exit(1); + } + + /* Open display: */ + if(use_gl) + st_video_setup_gl(); + else + st_video_setup_sdl(); + + Surface::reload_all(); + + /* Set window manager stuff: */ + SDL_WM_SetCaption("SuperTux " VERSION, "SuperTux"); +} + +void st_video_setup_sdl(void) +{ + if (use_fullscreen) + { + screen = SDL_SetVideoMode(SCREEN_W, SCREEN_H, 0, SDL_FULLSCREEN ) ; /* | SDL_HWSURFACE); */ + if (screen == NULL) + { + fprintf(stderr, + "\nWarning: I could not set up fullscreen video for " + "800x600 mode.\n" + "The Simple DirectMedia error that occured was:\n" + "%s\n\n", SDL_GetError()); + use_fullscreen = false; + } + } + else + { + screen = SDL_SetVideoMode(SCREEN_W, SCREEN_H, 0, SDL_HWSURFACE | SDL_DOUBLEBUF ); + + if (screen == NULL) + { + fprintf(stderr, + "\nError: I could not set up video for 800x600 mode.\n" + "The Simple DirectMedia error that occured was:\n" + "%s\n\n", SDL_GetError()); + exit(1); + } + } +} + +void st_video_setup_gl(void) +{ +#ifndef NOOPENGL + + SDL_GL_SetAttribute(SDL_GL_RED_SIZE, 5); + SDL_GL_SetAttribute(SDL_GL_GREEN_SIZE, 5); + SDL_GL_SetAttribute(SDL_GL_BLUE_SIZE, 5); + SDL_GL_SetAttribute(SDL_GL_DEPTH_SIZE, 16); + SDL_GL_SetAttribute(SDL_GL_DOUBLEBUFFER, 1); + + if (use_fullscreen) + { + screen = SDL_SetVideoMode(SCREEN_W, SCREEN_H, 0, SDL_FULLSCREEN | SDL_OPENGL) ; /* | SDL_HWSURFACE); */ + if (screen == NULL) + { + fprintf(stderr, + "\nWarning: I could not set up fullscreen video for " + "640x480 mode.\n" + "The Simple DirectMedia error that occured was:\n" + "%s\n\n", SDL_GetError()); + use_fullscreen = false; + } + } + else + { + screen = SDL_SetVideoMode(SCREEN_W, SCREEN_H, 0, SDL_OPENGL); + + if (screen == NULL) + { + fprintf(stderr, + "\nError: I could not set up video for 640x480 mode.\n" + "The Simple DirectMedia error that occured was:\n" + "%s\n\n", SDL_GetError()); + exit(1); + } + } + + /* + * Set up OpenGL for 2D rendering. + */ + glDisable(GL_DEPTH_TEST); + glDisable(GL_CULL_FACE); + + glViewport(0, 0, screen->w, screen->h); + glMatrixMode(GL_PROJECTION); + glLoadIdentity(); + glOrtho(0, screen->w, screen->h, 0, -1.0, 1.0); + + glMatrixMode(GL_MODELVIEW); + glLoadIdentity(); + glTranslatef(0.0f, 0.0f, 0.0f); + +#endif + +} + +void st_joystick_setup(void) +{ + + /* Init Joystick: */ + + use_joystick = true; + + if (SDL_Init(SDL_INIT_JOYSTICK) < 0) + { + fprintf(stderr, "Warning: I could not initialize joystick!\n" + "The Simple DirectMedia error that occured was:\n" + "%s\n\n", SDL_GetError()); + + use_joystick = false; + } + else + { + /* Open joystick: */ + if (SDL_NumJoysticks() <= 0) + { + fprintf(stderr, "Info: No joysticks were found.\n"); + + use_joystick = false; + } + else + { + js = SDL_JoystickOpen(joystick_num); + + if (js == NULL) + { + fprintf(stderr, "Warning: Could not open joystick %d.\n" + "The Simple DirectMedia error that occured was:\n" + "%s\n\n", joystick_num, SDL_GetError()); + + use_joystick = false; + } + else + { + if (SDL_JoystickNumAxes(js) < 2) + { + fprintf(stderr, + "Warning: Joystick does not have enough axes!\n"); + + use_joystick = false; + } + else + { + if (SDL_JoystickNumButtons(js) < 2) + { + fprintf(stderr, + "Warning: " + "Joystick does not have enough buttons!\n"); + + use_joystick = false; + } + } + } + } + } +} + +void st_audio_setup(void) +{ + + /* Init SDL Audio silently even if --disable-sound : */ + + if (audio_device) + { + if (SDL_Init(SDL_INIT_AUDIO) < 0) + { + /* only print out message if sound or music + was not disabled at command-line + */ + if (use_sound || use_music) + { + fprintf(stderr, + "\nWarning: I could not initialize audio!\n" + "The Simple DirectMedia error that occured was:\n" + "%s\n\n", SDL_GetError()); + } + /* keep the programming logic the same :-) + because in this case, use_sound & use_music' values are ignored + when there's no available audio device + */ + use_sound = false; + use_music = false; + audio_device = false; + } + } + + + /* Open sound silently regarless the value of "use_sound": */ + + if (audio_device) + { + if (open_audio(44100, AUDIO_S16, 2, 2048) < 0) + { + /* only print out message if sound or music + was not disabled at command-line + */ + if (use_sound || use_music) + { + fprintf(stderr, + "\nWarning: I could not set up audio for 44100 Hz " + "16-bit stereo.\n" + "The Simple DirectMedia error that occured was:\n" + "%s\n\n", SDL_GetError()); + } + use_sound = false; + use_music = false; + audio_device = false; + } + } + +} + + +/* --- SHUTDOWN --- */ + +void st_shutdown(void) +{ + close_audio(); + SDL_Quit(); + config->save(); +} + +/* --- ABORT! --- */ + +void st_abort(const std::string& reason, const std::string& details) +{ + fprintf(stderr, "\nError: %s\n%s\n\n", reason.c_str(), details.c_str()); + st_shutdown(); + abort(); +} + +/* Set Icon (private) */ + +void seticon(void) +{ +// int masklen; +// Uint8 * mask; + SDL_Surface * icon; + + + /* Load icon into a surface: */ + + icon = IMG_Load((datadir + "/images/supertux.xpm").c_str()); + if (icon == NULL) + { + fprintf(stderr, + "\nError: I could not load the icon image: %s%s\n" + "The Simple DirectMedia error that occured was:\n" + "%s\n\n", datadir.c_str(), "/images/supertux.xpm", SDL_GetError()); + exit(1); + } + + + /* Create mask: */ +/* + masklen = (((icon -> w) + 7) / 8) * (icon -> h); + mask = (Uint8*) malloc(masklen * sizeof(Uint8)); + memset(mask, 0xFF, masklen); +*/ + + /* Set icon: */ + + SDL_WM_SetIcon(icon, NULL);//mask); + + + /* Free icon surface & mask: */ + +// free(mask); + SDL_FreeSurface(icon); +} + + +/* Parse command-line arguments: */ + +void parseargs(int argc, char * argv[]) +{ + int i; + + config->load(); + + /* Parse arguments: */ + + for (i = 1; i < argc; i++) + { + if (strcmp(argv[i], "--fullscreen") == 0 || + strcmp(argv[i], "-f") == 0) + { + use_fullscreen = true; + } + else if (strcmp(argv[i], "--window") == 0 || + strcmp(argv[i], "-w") == 0) + { + use_fullscreen = false; + } + else if (strcmp(argv[i], "--joystick") == 0 || strcmp(argv[i], "-j") == 0) + { + assert(i+1 < argc); + joystick_num = atoi(argv[++i]); + } + else if (strcmp(argv[i], "--joymap") == 0) + { + assert(i+1 < argc); + if (sscanf(argv[++i], + "%d:%d:%d:%d:%d", + &joystick_keymap.x_axis, + &joystick_keymap.y_axis, + &joystick_keymap.a_button, + &joystick_keymap.b_button, + &joystick_keymap.start_button) != 5) + { + puts("Warning: Invalid or incomplete joymap, should be: 'XAXIS:YAXIS:A:B:START'"); + } + else + { + std::cout << "Using new joymap:\n" + << " X-Axis: " << joystick_keymap.x_axis << "\n" + << " Y-Axis: " << joystick_keymap.y_axis << "\n" + << " A-Button: " << joystick_keymap.a_button << "\n" + << " B-Button: " << joystick_keymap.b_button << "\n" + << " Start-Button: " << joystick_keymap.start_button << std::endl; + } + } + else if (strcmp(argv[i], "--leveleditor") == 0) + { + launch_leveleditor_mode = true; + } + else if (strcmp(argv[i], "--worldmap") == 0) + { + launch_worldmap_mode = true; + } + else if (strcmp(argv[i], "--datadir") == 0 + || strcmp(argv[i], "-d") == 0 ) + { + assert(i+1 < argc); + datadir = argv[++i]; + } + else if (strcmp(argv[i], "--show-fps") == 0) + { + /* Use full screen: */ + + show_fps = true; + } + else if (strcmp(argv[i], "--opengl") == 0 || + strcmp(argv[i], "-gl") == 0) + { +#ifndef NOOPENGL + /* Use OpengGL: */ + + use_gl = true; +#endif + } + else if (strcmp(argv[i], "--sdl") == 0) + { + use_gl = false; + } + else if (strcmp(argv[i], "--usage") == 0) + { + /* Show usage: */ + + usage(argv[0], 0); + } + else if (strcmp(argv[i], "--version") == 0) + { + /* Show version: */ + printf("SuperTux " VERSION "\n"); + exit(0); + } + else if (strcmp(argv[i], "--disable-sound") == 0) + { + /* Disable the compiled in sound feature */ + printf("Sounds disabled \n"); + use_sound = false; + audio_device = false; + } + else if (strcmp(argv[i], "--disable-music") == 0) + { + /* Disable the compiled in sound feature */ + printf("Music disabled \n"); + use_music = false; + } + else if (strcmp(argv[i], "--debug") == 0) + { + /* Enable the debug-mode */ + debug_mode = true; + + } + else if (strcmp(argv[i], "--help") == 0) + { /* Show help: */ + puts(_(" SuperTux " VERSION "\n" + " Please see the file \"README.txt\" for more details.\n")); + printf(_("Usage: %s [OPTIONS] FILENAME\n\n"), argv[0]); + puts(_("Display Options:\n" + " -f, --fullscreen Run in fullscreen mode.\n" + " -w, --window Run in window mode.\n" + " --opengl If OpenGL support was compiled in, this will tell\n" + " SuperTux to make use of it.\n" + " --sdl Use the SDL software graphical renderer\n" + "\n" + "Sound Options:\n" + " --disable-sound If sound support was compiled in, this will\n" + " disable sound for this session of the game.\n" + " --disable-music Like above, but this will disable music.\n" + "\n" + "Misc Options:\n" + " -j, --joystick NUM Use joystick NUM (default: 0)\n" + " --joymap XAXIS:YAXIS:A:B:START\n" + " Define how joystick buttons and axis should be mapped\n" + " --leveleditor Opens the leveleditor in a file.\n" + " --worldmap Opens the specified worldmap file.\n" + " -d, --datadir DIR Load Game data from DIR (default: automatic)\n" + " --debug Enables the debug mode, which is useful for developers.\n" + " --help Display a help message summarizing command-line\n" + " options, license and game controls.\n" + " --usage Display a brief message summarizing command-line options.\n" + " --version Display the version of SuperTux you're running.\n\n" + )); + exit(0); + } + else if (argv[i][0] != '-') + { + level_startup_file = argv[i]; + } + else + { + /* Unknown - complain! */ + + usage(argv[0], 1); + } + } +} + + +/* Display usage: */ + +void usage(char * prog, int ret) +{ + FILE * fi; + + + /* Determine which stream to write to: */ + + if (ret == 0) + fi = stdout; + else + fi = stderr; + + + /* Display the usage message: */ + + fprintf(fi, _("Usage: %s [--fullscreen] [--opengl] [--disable-sound] [--disable-music] [--debug] | [--usage | --help | --version] [--leveleditor] [--worldmap] FILENAME\n"), + prog); + + + /* Quit! */ + + exit(ret); +} + +std::vector read_directory(const std::string& pathname) +{ + std::vector dirnames; + + DIR* dir = opendir(pathname.c_str()); + if (dir) + { + struct dirent *direntp; + + while((direntp = readdir(dir))) + { + dirnames.push_back(direntp->d_name); + } + + closedir(dir); + } + + return dirnames; +} + +/* EOF */ diff --git a/lib/app/setup.h b/lib/app/setup.h new file mode 100644 index 000000000..fa79017d8 --- /dev/null +++ b/lib/app/setup.h @@ -0,0 +1,52 @@ +// $Id$ +// +// SuperTux - A Jump'n Run +// Copyright (C) 2000 Bill Kendrick +// +// This program is free software; you can redistribute it and/or +// modify it under the terms of the GNU General Public License +// as published by the Free Software Foundation; either version 2 +// of the License, or (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software +// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + +#ifndef SUPERTUX_SETUP_H +#define SUPERTUX_SETUP_H + +#include +#include +#include "gui/menu.h" +#include "audio/sound.h" +#include "special/base.h" + +int faccessible(const char *filename); +int fcreatedir(const char* relative_dir); +int fwriteable(const char *filename); +std::vector read_directory(const std::string& pathname); + +FILE * opendata(const char * filename, const char * mode); +string_list_type dsubdirs(const char *rel_path, const char* expected_file); +string_list_type dfiles(const char *rel_path, const char* glob, const char* exception_str); +void free_strings(char **strings, int num); +void st_directory_setup(void); +void st_general_setup(void); +void st_general_free(); +void st_video_setup_sdl(void); +void st_video_setup_gl(void); +void st_video_setup(void); +void st_audio_setup(void); +void st_joystick_setup(void); +void st_shutdown(void); +void st_abort(const std::string& reason, const std::string& details); + +void parseargs(int argc, char * argv[]); + +#endif /*SUPERTUX_SETUP_H*/ + diff --git a/lib/audio/musicref.cpp b/lib/audio/musicref.cpp new file mode 100644 index 000000000..2b5efcdf5 --- /dev/null +++ b/lib/audio/musicref.cpp @@ -0,0 +1,66 @@ +// $Id$ +// +// SuperTux - A Jump'n Run +// Copyright (C) 2000 Bill Kendrick +// Copyright (C) 2004 Matthias Braun +// +// This program is free software; you can redistribute it and/or +// modify it under the terms of the GNU General Public License +// as published by the Free Software Foundation; either version 2 +// of the License, or (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software +// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + +#include "audio/musicref.h" + +MusicRef::MusicRef() + : music(0) +{ +} + +MusicRef::MusicRef(SoundManager::MusicResource* newmusic) + : music(newmusic) +{ + if(music) + music->refcount++; +} + +MusicRef::~MusicRef() +{ + if(music) { + music->refcount--; + if(music->refcount == 0) + music->manager->free_music(music); + } +} + +MusicRef::MusicRef(const MusicRef& other) + : music(other.music) +{ + if(music) + music->refcount++; +} + +MusicRef& +MusicRef::operator =(const MusicRef& other) +{ + SoundManager::MusicResource* oldres = music; + music = other.music; + if(music) + music->refcount++; + if(oldres) { + oldres->refcount--; + if(oldres->refcount == 0) + music->manager->free_music(music); + } + + return *this; +} + diff --git a/lib/audio/musicref.h b/lib/audio/musicref.h new file mode 100644 index 000000000..b9899ed97 --- /dev/null +++ b/lib/audio/musicref.h @@ -0,0 +1,45 @@ +// $Id$ +// +// SuperTux - A Jump'n Run +// Copyright (C) 2004 Matthias Braun +// +// This program is free software; you can redistribute it and/or +// modify it under the terms of the GNU General Public License +// as published by the Free Software Foundation; either version 2 +// of the License, or (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software +// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + +#ifndef SUPERTUX_MUSICREF_H +#define SUPERTUX_MUSICREF_H + +#include "audio/sound_manager.h" + +/** This class holds a reference to a music file and maintains a correct + * refcount for that file. + */ +class MusicRef +{ +public: + MusicRef(); + MusicRef(const MusicRef& other); + ~MusicRef(); + + MusicRef& operator= (const MusicRef& other); + +private: + friend class SoundManager; + MusicRef(SoundManager::MusicResource* music); + + SoundManager::MusicResource* music; +}; + +#endif /*SUPERTUX_MUSICREF_H*/ + diff --git a/lib/audio/sound.cpp b/lib/audio/sound.cpp new file mode 100644 index 000000000..3ba275222 --- /dev/null +++ b/lib/audio/sound.cpp @@ -0,0 +1,77 @@ +// $Id$ +// +// SuperTux - A Jump'n Run +// Copyright (C) 2000 Bill Kendrick +// Copyright (C) 2004 Duong-Khang NGUYEN +// +// This program is free software; you can redistribute it and/or +// modify it under the terms of the GNU General Public License +// as published by the Free Software Foundation; either version 2 +// of the License, or (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software +// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + +#include +#include "audio/sound.h" + +/*global variable*/ +bool use_sound = true; /* handle sound on/off menu and command-line option */ +bool use_music = true; /* handle music on/off menu and command-line option */ +bool audio_device = true; /* != 0: available and initialized */ + +#include + +std::vector sounds; + +/* --- OPEN THE AUDIO DEVICE --- */ + +int open_audio (int frequency, Uint16 format, int channels, int chunksize) +{ + if (Mix_OpenAudio( frequency, format, channels, chunksize ) < 0) + return -1; + + // allocate 16 channels for mixing + if (Mix_AllocateChannels(8) != 8) + return -2; + + return 0; +} + + +/* --- CLOSE THE AUDIO DEVICE --- */ + +void close_audio( void ) +{ + if (audio_device) { + Mix_CloseAudio(); + } +} + + +/* --- LOAD A SOUND --- */ + +Mix_Chunk* load_sound(const std::string& file) +{ + if(!audio_device) + return 0; + + Mix_Chunk* snd = Mix_LoadWAV(file.c_str()); + + /*if (snd == 0) + st_abort("Can't load", file);*/ + + return(snd); +} + +void free_chunk(Mix_Chunk *chunk) +{ + Mix_FreeChunk( chunk ); +} + diff --git a/lib/audio/sound.h b/lib/audio/sound.h new file mode 100644 index 000000000..975021fbf --- /dev/null +++ b/lib/audio/sound.h @@ -0,0 +1,53 @@ +// $Id$ +// +// SuperTux - A Jump'n Run +// Copyright (C) 2000 Bill Kendrick +// Copyright (C) 2004 Duong-Khang NGUYEN +// +// This program is free software; you can redistribute it and/or +// modify it under the terms of the GNU General Public License +// as published by the Free Software Foundation; either version 2 +// of the License, or (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software +// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + +#ifndef SUPERTUX_SOUND_H +#define SUPERTUX_SOUND_H + +//#include "defines.h" /* get YES/NO defines */ +#include + +/*global variable*/ +extern bool use_sound; /* handle sound on/off menu and command-line option */ +extern bool use_music; /* handle music on/off menu and command-line */ +extern bool audio_device; /* != 0: available and initialized */ + +/* enum of different internal music types */ +enum Music_Type { + NO_MUSIC, + LEVEL_MUSIC, + HURRYUP_MUSIC, + HERRING_MUSIC +}; + +#include +#include + +/* variables for stocking the sound and music */ +extern std::vector sounds; + +/* functions handling the sound and music */ +int open_audio(int frequency, Uint16 format, int channels, int chunksize); +void close_audio( void ); + +Mix_Chunk * load_sound(const std::string& file); +void free_chunk(Mix_Chunk*chunk); + +#endif /*SUPERTUX_SOUND_H*/ diff --git a/lib/audio/sound_manager.cpp b/lib/audio/sound_manager.cpp new file mode 100644 index 000000000..1c790f1e7 --- /dev/null +++ b/lib/audio/sound_manager.cpp @@ -0,0 +1,188 @@ +// $Id$ +// +// SuperTux - A Jump'n Run +// Copyright (C) 2004 Matthias Braun +#include + +#include "audio/sound_manager.h" +#include "audio/musicref.h" +#include "audio/sound.h" +#include "app/globals.h" +#include "app/setup.h" +#include "special/moving_object.h" + +SoundManager::SoundManager() + : current_music(0), music_enabled(true) +{ +} + +SoundManager::~SoundManager() +{ + if(audio_device) + Mix_HaltMusic(); +} + +void +SoundManager::play_sound(Mix_Chunk* sound) +{ + if(!audio_device || !use_sound) + return; + + Mix_PlayChannel(-1, sound, 0); +} + +void +SoundManager::play_sound(Mix_Chunk* sound, const MovingObject* object, const Vector& pos) +{ + // TODO keep track of the object later and move the sound along with the + // object. + play_sound(sound, object->get_pos(), pos); +} + +void +SoundManager::play_sound(Mix_Chunk* sound, const Vector& pos, const Vector& pos2) +{ + if(!audio_device || !use_sound) + return; + + // TODO make sure this formula is good + float distance + = pos2.x- pos.x; + int loud = int(255.0/float(screen->w*2) * fabsf(distance)); + if(loud > 255) + return; + + int chan = Mix_PlayChannel(-1, sound, 0); + if(chan < 0) + return; + Mix_SetDistance(chan, loud); + + // very bad way to do this... + if(distance > 100) + Mix_SetPanning(chan, 230, 24); + else if(distance < -100) + Mix_SetPanning(chan, 24, 230); +} + +MusicRef +SoundManager::load_music(const std::string& file) +{ + if(!audio_device) + return MusicRef(0); + + if(!exists_music(file)) + st_abort("Couldn't load musicfile ", file.c_str()); + + std::map::iterator i = musics.find(file); + assert(i != musics.end()); + return MusicRef(& (i->second)); +} + +bool +SoundManager::exists_music(const std::string& file) +{ + if(!audio_device) + return true; + + // song already loaded? + std::map::iterator i = musics.find(file); + if(i != musics.end()) { + return true; + } + + Mix_Music* song = Mix_LoadMUS(file.c_str()); + if(song == 0) + return false; + + // insert into music list + std::pair::iterator, bool> result = + musics.insert( + std::make_pair (file, MusicResource())); + MusicResource& resource = result.first->second; + resource.manager = this; + resource.music = song; + + return true; +} + +void +SoundManager::free_music(MusicResource* ) +{ + // TODO free music, currently we can't do this since SDL_mixer seems to have + // some bugs if you load/free alot of mod files. +} + +void +SoundManager::play_music(const MusicRef& musicref, int loops) +{ + if(!audio_device) + return; + + if(musicref.music == 0 || current_music == musicref.music) + return; + + if(current_music) + current_music->refcount--; + + current_music = musicref.music; + current_music->refcount++; + + if(music_enabled) + Mix_PlayMusic(current_music->music, loops); +} + +void +SoundManager::halt_music() +{ + if(!audio_device) + return; + + Mix_HaltMusic(); + + if(current_music) { + current_music->refcount--; + if(current_music->refcount == 0) + free_music(current_music); + current_music = 0; + } +} + +void +SoundManager::enable_music(bool enable) +{ + if(!audio_device) + return; + + if(enable == music_enabled) + return; + + music_enabled = enable; + if(music_enabled == false) { + Mix_HaltMusic(); + } else { + Mix_PlayMusic(current_music->music, -1); + } +} + +SoundManager::MusicResource::~MusicResource() +{ + // don't free music buggy SDL_Mixer crashs for some mod files + // Mix_FreeMusic(music); +} + diff --git a/lib/audio/sound_manager.h b/lib/audio/sound_manager.h new file mode 100644 index 000000000..4a7424450 --- /dev/null +++ b/lib/audio/sound_manager.h @@ -0,0 +1,74 @@ +// $Id$ +// +// SuperTux - A Jump'n Run +// Copyright (C) 2004 Matthias Braun +#include + +#include "SDL_mixer.h" + +#include "math/vector.h" + +class MusicRef; +class MovingObject; + +/** This class handles all sounds that are played + */ +class SoundManager +{ +public: + SoundManager(); + ~SoundManager(); + + void play_sound(Mix_Chunk* sound); + void play_sound(Mix_Chunk* sound, const Vector& pos, const Vector& pos2); + void play_sound(Mix_Chunk* sound, const MovingObject* object, const Vector& pos); + + MusicRef load_music(const std::string& file); + bool exists_music(const std::string& filename); + + void play_music(const MusicRef& music, int loops = -1); + void halt_music(); + + void enable_music(bool enable); + +private: + // music part + friend class MusicRef; + class MusicResource + { + public: + ~MusicResource(); + + SoundManager* manager; + Mix_Music* music; + int refcount; + }; + + void free_music(MusicResource* music); + + std::map musics; + MusicResource* current_music; + bool music_enabled; +}; + +#endif /*SUPERTUX_SOUND_MANAGER_H*/ + diff --git a/lib/gui/button.cpp b/lib/gui/button.cpp new file mode 100644 index 000000000..66b096320 --- /dev/null +++ b/lib/gui/button.cpp @@ -0,0 +1,324 @@ +// $Id$ +// +// SuperTux +// Copyright (C) 2004 Tobias Glaesser +// +// This program is free software; you can redistribute it and/or +// modify it under the terms of the GNU General Public License +// as published by the Free Software Foundation; either version 2 +// of the License, or (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software +// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA +// 02111-1307, USA. + +#include +#include + +#include "app/setup.h" +#include "video/screen.h" +#include "video/drawing_context.h" +#include "app/globals.h" +#include "gui/button.h" + +Timer Button::popup_timer; + +Button::Button(Surface* button_image, const std::string& ninfo, + SDLKey nshortcut, int x, int y, int mw, int mh) +{ + popup_timer.init(false); + + if(button_image) + icon.push_back(button_image); + + info = ninfo; + + shortcut = nshortcut; + + rect.x = x; + rect.y = y; + rect.w = icon[0]->w; + rect.h = icon[0]->h; + tag = -1; + state = BUTTON_NONE; + show_info = false; +} + +Button::Button(const std::string& imagefilename, const std::string& ninfo, + SDLKey nshortcut, int x, int y, int mw, int mh) +{ + popup_timer.init(false); + + add_icon(imagefilename, mw, mh); + + info = ninfo; + + shortcut = nshortcut; + + rect.x = x; + rect.y = y; + rect.w = icon[0]->w; + rect.h = icon[0]->h; + tag = -1; + state = BUTTON_NONE; + show_info = false; +} + +void Button::add_icon(const std::string& icon_file, int mw, int mh) +{ + char filename[1024]; + + if(!icon_file.empty()) + { + snprintf(filename, 1024, "%s/%s", datadir.c_str(), icon_file.c_str()); + if(!faccessible(filename)) + snprintf(filename, 1024, "%s/images/icons/default-icon.png", datadir.c_str()); + } + else + { + snprintf(filename, 1024, "%s/images/icons/default-icon.png", datadir.c_str()); + } + + if(mw != -1 || mh != -1) + { + icon.push_back(new Surface(filename,true)); + icon.back()->resize(mw,mh); + } + else + icon.push_back(new Surface(filename,true)); + +} + +void Button::draw(DrawingContext& context) +{ + if(state == BUTTON_HOVER) + if(!popup_timer.check()) + show_info = true; + + fillrect(rect.x,rect.y,rect.w,rect.h,75,75,75,200); + fillrect(rect.x+1,rect.y+1,rect.w-2,rect.h-2,175,175,175,200); + + for(std::vector::iterator it = icon.begin(); it != icon.end(); ++it) + context.draw_surface(*it, Vector(rect.x,rect.y), LAYER_GUI); + +/* if(drawable) + { + Camera viewport; + viewport.set_translation(Vector(rect.x, rect.y)); + drawable->draw(viewport, 0); + }*/ + + if(show_info) + { + char str[80]; + int i = -32; + + if(0 > rect.x - white_small_text->get_text_width(info)) + i = rect.w + (int)white_small_text->get_text_width(info); + + if(!info.empty()) + context.draw_text(white_small_text, info, Vector(i + rect.x - white_small_text->get_text_width(info), rect.y), LAYER_GUI); + sprintf(str,"(%s)", SDL_GetKeyName(shortcut)); + context.draw_text(white_small_text, str, Vector(i + rect.x - white_small_text->get_text_width(str), rect.y + white_small_text->get_height()+2), LAYER_GUI); + } + if(state == BUTTON_PRESSED || state == BUTTON_DEACTIVE) + fillrect(rect.x,rect.y,rect.w,rect.h,75,75,75,200); + else if(state == BUTTON_HOVER) + fillrect(rect.x,rect.y,rect.w,rect.h,150,150,150,128); +} + +Button::~Button() +{ + for(std::vector::iterator it = icon.begin(); it != icon.end(); ++it) + delete (*it); + icon.clear(); + // FIXME TODO XXX: commenting this out fixes the leveleditor quit crash + // probably should be deleted somehow, though + //delete drawable; +} + +void Button::event(SDL_Event &event) +{ + if(state == BUTTON_DEACTIVE) + return; + + SDLKey key = event.key.keysym.sym; + + if(event.type == SDL_MOUSEBUTTONDOWN || event.type == SDL_MOUSEBUTTONUP) + { + if(event.button.x < rect.x || event.button.x >= rect.x + rect.w || + event.button.y < rect.y || event.button.y >= rect.y + rect.h) + return; + + if(event.button.button == SDL_BUTTON_RIGHT) + { + show_info = true; + return; + } + else if(event.type == SDL_MOUSEBUTTONUP && event.button.button == 4) /* Mouse wheel up. */ + { + state = BUTTON_WHEELUP; + return; + } + else if(event.type == SDL_MOUSEBUTTONUP && event.button.button == 5) /* Mouse wheel down. */ + { + state = BUTTON_WHEELDOWN; + return; + } + + if(event.button.button == SDL_BUTTON_LEFT) + if(event.type == SDL_MOUSEBUTTONDOWN) + state = BUTTON_PRESSED; + else + state = BUTTON_CLICKED; + } + else if(event.type == SDL_MOUSEMOTION) + { + if(event.motion.x < rect.x || event.motion.x >= rect.x + rect.w || + event.motion.y < rect.y || event.motion.y >= rect.y + rect.h) + { + state = BUTTON_NONE; + } + else + { + state = BUTTON_HOVER; + popup_timer.start(1500); + } + + if(show_info) + { + show_info = false; + } + } + else if(event.type == SDL_KEYDOWN) + { + if(key == shortcut) + state = BUTTON_PRESSED; + } + else if(event.type == SDL_KEYUP) + { + if(state == BUTTON_PRESSED && key == shortcut) + state = BUTTON_CLICKED; + } +} + +int Button::get_state() +{ + int rstate; + switch(state) + { + case BUTTON_CLICKED: + case BUTTON_WHEELUP: + case BUTTON_WHEELDOWN: + rstate = state; + state = BUTTON_NONE; + return rstate; + default: + return state; + } +} + +ButtonPanel::ButtonPanel(int x, int y, int w, int h) +{ + bw = 32; + bh = 32; + rect.x = x; + rect.y = y; + rect.w = w; + rect.h = h; + hidden = false; + hlast = false; +} + +Button* ButtonPanel::event(SDL_Event& event) +{ + if(!hidden) + { + Button* ret = NULL; + for(std::vector::iterator it = item.begin(); it != item.end(); ++it) + { + (*it)->event(event); + if((*it)->state != BUTTON_NONE) + { + if(hlast && (*it)->state == BUTTON_CLICKED) + last_clicked = it; + ret = (*it); + } + } + return ret; + } + else + { + return NULL; + } +} + +ButtonPanel::~ButtonPanel() +{ + for(std::vector::iterator it = item.begin(); it != item.end(); ++it) + { + delete (*it); + } + item.clear(); +} + +void ButtonPanel::draw(DrawingContext& context) +{ + + if(hidden == false) + { + fillrect(rect.x,rect.y,rect.w,rect.h,100,100,100,200); + for(std::vector::iterator it = item.begin(); it != item.end(); ++it) + { + (*it)->draw(context); + if(hlast && it == last_clicked) + { + fillrect((*it)->get_pos().x,(*it)->get_pos().y,(*it)->get_pos().w,(*it)->get_pos().h,100,100,100,128); + } + } + } +} + +void ButtonPanel::additem(Button* pbutton, int tag) +{ + int max_cols, row, col; + + item.push_back(pbutton); + + /* A button_panel takes control of the buttons it contains and arranges them */ + + max_cols = rect.w / bw; + + row = (item.size()-1) / max_cols; + col = (item.size()-1) % max_cols; + + item[item.size()-1]->rect.x = rect.x + col * bw; + item[item.size()-1]->rect.y = rect.y + row * bh; + item[item.size()-1]->tag = tag; + +} + +void ButtonPanel::set_button_size(int w, int h) +{ + bw = w; + bh = h; +} + +Button* ButtonPanel::manipulate_button(int i) +{ + if(int(item.size())-1 < i) + return item[item.size()-1]; + else + return item[i]; +} + +void ButtonPanel::highlight_last(bool b) +{ + hlast = b; +} diff --git a/lib/gui/button.h b/lib/gui/button.h new file mode 100644 index 000000000..a24cc9d39 --- /dev/null +++ b/lib/gui/button.h @@ -0,0 +1,98 @@ +// $Id$ +// +// SuperTux +// Copyright (C) 2004 Tobias Glaesser +// +// This program is free software; you can redistribute it and/or +// modify it under the terms of the GNU General Public License +// as published by the Free Software Foundation; either version 2 +// of the License, or (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software +// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA +// 02111-1307, USA. + +#ifndef SUPERTUX_BUTTON_H +#define SUPERTUX_BUTTON_H + +#include + +#include "video/surface.h" +#include "special/timer.h" + +enum ButtonState { + BUTTON_NONE = -1, + BUTTON_CLICKED, + BUTTON_PRESSED, + BUTTON_HOVER, + BUTTON_WHEELUP, + BUTTON_WHEELDOWN, + BUTTON_DEACTIVE +}; + +class ButtonPanel; + +class Button +{ + friend class ButtonPanel; + +public: + Button(Surface* icon_file, const std::string& info, SDLKey shortcut, + int x, int y, int mw = -1, int h = -1); + Button(const std::string& icon_name, const std::string& info, SDLKey shortcut, + int x, int y, int mw = -1, int h = -1); + + ~Button(); + void event(SDL_Event& event); + void draw(DrawingContext& context); + int get_state(); + void set_active(bool active) { active ? state = BUTTON_NONE : state = BUTTON_DEACTIVE; }; + void add_icon(const std::string& imagefile, int mw, int mh); + SDL_Rect get_pos() { return rect; } + int get_tag(){return tag; } +// void set_drawable(Drawable* newdrawable) +// { drawable = newdrawable; } + +private: + static Timer popup_timer; +// Drawable* drawable; + std::vector icon; + std::string info; + SDLKey shortcut; + SDL_Rect rect; + bool show_info; + ButtonState state; + int tag; +}; + +class ButtonPanel +{ +public: + ButtonPanel(int x, int y, int w, int h); + ~ButtonPanel(); + void draw(DrawingContext& context); + Button* event(SDL_Event &event); + void additem(Button* pbutton, int tag); + Button* button_panel_event(SDL_Event& event); + void set_button_size(int w, int h); + Button* manipulate_button(int i); + void highlight_last(bool b); + void set_last_clicked(unsigned int last) + { if(hlast) { if(item.size() >= last) { last_clicked = item.begin() + last; } } }; + +private: + int bw, bh; + bool hlast; + bool hidden; + SDL_Rect rect; + std::vector item; + std::vector::iterator last_clicked; +}; + +#endif /*SUPERTUX_BUTTON_H*/ diff --git a/lib/gui/menu.cpp b/lib/gui/menu.cpp new file mode 100644 index 000000000..4dc253d55 --- /dev/null +++ b/lib/gui/menu.cpp @@ -0,0 +1,914 @@ +// $Id$ +// +// SuperTux +// Copyright (C) 2004 Tobias Glaesser +// +// This program is free software; you can redistribute it and/or +// modify it under the terms of the GNU General Public License +// as published by the Free Software Foundation; either version 2 +// of the License, or (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software +// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + +#ifndef WIN32 +#include +#include +#endif + +#include +#include +#include +#include +#include +#include + +#include "app/globals.h" +#include "gui/menu.h" +#include "video/screen.h" +#include "video/drawing_context.h" +#include "app/setup.h" +#include "audio/sound.h" +#include "special/timer.h" +#include "app/gettext.h" + +#define FLICK_CURSOR_TIME 500 + +Surface* checkbox; +Surface* checkbox_checked; +Surface* back; +Surface* arrow_left; +Surface* arrow_right; + +Menu* main_menu = 0; +Menu* game_menu = 0; +Menu* options_menu = 0; +Menu* options_keys_menu = 0; +Menu* options_joystick_menu = 0; +Menu* highscore_menu = 0; +Menu* load_game_menu = 0; +Menu* save_game_menu = 0; +Menu* contrib_menu = 0; +Menu* contrib_subset_menu = 0; + +std::vector Menu::last_menus; +Menu* Menu::current_ = 0; + +/* just displays a Yes/No text that can be used to confirm stuff */ +bool confirm_dialog(Surface *background, std::string text) +{ + //Surface* cap_screen = Surface::CaptureScreen(); + + Menu* dialog = new Menu; + dialog->additem(MN_DEACTIVE, text,0,0); + dialog->additem(MN_HL,"",0,0); + dialog->additem(MN_ACTION,_("Yes"),0,0,true); + dialog->additem(MN_ACTION,_("No"),0,0,false); + dialog->additem(MN_HL,"",0,0); + + Menu::set_current(dialog); + + DrawingContext context; + + while(true) + { + SDL_Event event; + + while (SDL_PollEvent(&event)) + { + dialog->event(event); + } + + if(background == NULL) + context.draw_gradient(Color(200,240,220), Color(200,200,220), LAYER_BACKGROUND0); + else + context.draw_surface(background, Vector(0,0), LAYER_BACKGROUND0); + + dialog->draw(context); + dialog->action(); + + switch (dialog->check()) + { + case true: + //delete cap_screen; + Menu::set_current(0); + delete dialog; + return true; + break; + case false: + //delete cap_screen; + Menu::set_current(0); + delete dialog; + return false; + break; + default: + break; + } + + mouse_cursor->draw(context); + context.do_drawing(); + SDL_Delay(25); + } + + return false; +} + +void +Menu::push_current(Menu* pmenu) +{ + if (current_) + last_menus.push_back(current_); + + current_ = pmenu; + current_->effect.start(500); +} + +void +Menu::pop_current() +{ + if (!last_menus.empty()) + { + current_ = last_menus.back(); + current_->effect.start(500); + + last_menus.pop_back(); + } + else + { + current_ = 0; + } +} + +void +Menu::set_current(Menu* menu) +{ + last_menus.clear(); + + if (menu) + menu->effect.start(500); + + current_ = menu; +} + +/* Return a pointer to a new menu item */ +MenuItem* +MenuItem::create(MenuItemKind kind_, const char *text_, int init_toggle_, Menu* target_menu_, int id, int* int_p_) +{ + MenuItem *pnew_item = new MenuItem; + + pnew_item->kind = kind_; + pnew_item->text = (char*) malloc(sizeof(char) * (strlen(text_) + 1)); + strcpy(pnew_item->text, text_); + + if(kind_ == MN_TOGGLE) + pnew_item->toggled = init_toggle_; + else + pnew_item->toggled = false; + + pnew_item->target_menu = target_menu_; + pnew_item->input = (char*) malloc(sizeof(char)); + pnew_item->input[0] = '\0'; + + if(kind_ == MN_STRINGSELECT) + { + pnew_item->list = (string_list_type*) malloc(sizeof(string_list_type)); + string_list_init(pnew_item->list); + } + else + pnew_item->list = NULL; + + pnew_item->id = id; + pnew_item->int_p = int_p_; + + pnew_item->input_flickering = false; + pnew_item->input_flickering_timer.init(true); + pnew_item->input_flickering_timer.start(FLICK_CURSOR_TIME); + + return pnew_item; +} + +void +MenuItem::change_text(const char *text_) +{ + if (text_) + { + free(text); + text = (char*) malloc(sizeof(char )*(strlen(text_)+1)); + strcpy(text, text_); + } +} + +void +MenuItem::change_input(const char *text_) +{ + if(text) + { + free(input); + input = (char*) malloc(sizeof(char )*(strlen(text_)+1)); + strcpy(input, text_); + } +} + +std::string MenuItem::get_input_with_symbol(bool active_item) +{ + if(!active_item) + input_flickering = true; + else + { + if(input_flickering_timer.get_left() < 0) + { + if(input_flickering) + input_flickering = false; + else + input_flickering = true; + input_flickering_timer.start(FLICK_CURSOR_TIME); + } + } + + char str[1024]; + if(input_flickering) + sprintf(str,"%s ",input); + else + sprintf(str,"%s_",input); + + std::string string = str; + + return string; +} + +/* Set ControlField for keyboard key */ +void Menu::get_controlfield_key_into_input(MenuItem *item) +{ + switch(*item->int_p) + { + case SDLK_UP: + item->change_input(_("Up cursor")); + break; + case SDLK_DOWN: + item->change_input(_("Down cursor")); + break; + case SDLK_LEFT: + item->change_input(_("Left cursor")); + break; + case SDLK_RIGHT: + item->change_input(_("Right cursor")); + break; + case SDLK_RETURN: + item->change_input(_("Return")); + break; + case SDLK_SPACE: + item->change_input(_("Space")); + break; + case SDLK_RSHIFT: + item->change_input(_("Right Shift")); + break; + case SDLK_LSHIFT: + item->change_input(_("Left Shift")); + break; + case SDLK_RCTRL: + item->change_input(_("Right Control")); + break; + case SDLK_LCTRL: + item->change_input(_("Left Control")); + break; + case SDLK_RALT: + item->change_input(_("Right Alt")); + break; + case SDLK_LALT: + item->change_input(_("Left Alt")); + break; + default: + { + char tmp[64]; + snprintf(tmp, 64, "%d", *item->int_p); + item->change_input(tmp); + } + break; + } +} + +/* Set ControlField for joystick button */ +void Menu::get_controlfield_js_into_input(MenuItem *item) +{ + std::ostringstream oss; + oss << "Button " << *item->int_p; + item->change_input(oss.str().c_str()); +} + +/* Free a menu and all its items */ +Menu::~Menu() +{ + if(item.size() != 0) + { + for(unsigned int i = 0; i < item.size(); ++i) + { + free(item[i].text); + free(item[i].input); + string_list_free(item[i].list); + } + } +} + + +Menu::Menu() +{ + hit_item = -1; + menuaction = MENU_ACTION_NONE; + delete_character = 0; + mn_input_char = '\0'; + + pos_x = screen->w/2; + pos_y = screen->h/2; + arrange_left = 0; + active_item = 0; + effect.init(false); + + joystick_timer.init(true); +} + +void Menu::set_pos(int x, int y, float rw, float rh) +{ + pos_x = x + (int)((float)get_width() * rw); + pos_y = y + (int)((float)get_height() * rh); +} + +void +Menu::additem(MenuItemKind kind_, const std::string& text_, int toggle_, Menu* menu_, int id, int* int_p) +{ + additem(MenuItem::create(kind_, text_.c_str(), toggle_, menu_, id, int_p)); +} + +/* Add an item to a menu */ +void +Menu::additem(MenuItem* pmenu_item) +{ + item.push_back(*pmenu_item); + delete pmenu_item; +} + +void +Menu::clear() +{ + item.clear(); +} + +/* Process actions done on the menu */ +void +Menu::action() +{ + hit_item = -1; + if(item.size() != 0) + { + switch(menuaction) + { + case MENU_ACTION_UP: + if (active_item > 0) + --active_item; + else + active_item = int(item.size())-1; + break; + + case MENU_ACTION_DOWN: + if(active_item < int(item.size())-1) + ++active_item; + else + active_item = 0; + break; + + case MENU_ACTION_LEFT: + if(item[active_item].kind == MN_STRINGSELECT + && item[active_item].list->num_items != 0) + { + if(item[active_item].list->active_item > 0) + --item[active_item].list->active_item; + else + item[active_item].list->active_item = item[active_item].list->num_items-1; + } + break; + + case MENU_ACTION_RIGHT: + if(item[active_item].kind == MN_STRINGSELECT + && item[active_item].list->num_items != 0) + { + if(item[active_item].list->active_item < item[active_item].list->num_items-1) + ++item[active_item].list->active_item; + else + item[active_item].list->active_item = 0; + } + break; + + case MENU_ACTION_HIT: + { + hit_item = active_item; + switch (item[active_item].kind) + { + case MN_GOTO: + if (item[active_item].target_menu != NULL) + Menu::push_current(item[active_item].target_menu); + else + puts("NULLL"); + break; + + case MN_TOGGLE: + item[active_item].toggled = !item[active_item].toggled; + break; + + case MN_ACTION: + Menu::set_current(0); + item[active_item].toggled = true; + break; + case MN_TEXTFIELD: + case MN_NUMFIELD: + menuaction = MENU_ACTION_DOWN; + action(); + break; + + case MN_BACK: + Menu::pop_current(); + break; + default: + break; + } + } + break; + + case MENU_ACTION_REMOVE: + if(item[active_item].kind == MN_TEXTFIELD + || item[active_item].kind == MN_NUMFIELD) + { + if(item[active_item].input != NULL) + { + int i = strlen(item[active_item].input); + + while(delete_character > 0) /* remove charactes */ + { + item[active_item].input[i-1] = '\0'; + delete_character--; + } + } + } + break; + + case MENU_ACTION_INPUT: + if(item[active_item].kind == MN_TEXTFIELD + || (item[active_item].kind == MN_NUMFIELD && mn_input_char >= '0' && mn_input_char <= '9')) + { + if(item[active_item].input != NULL) + { + int i = strlen(item[active_item].input); + item[active_item].input = (char*) realloc(item[active_item].input,sizeof(char)*(i + 2)); + item[active_item].input[i] = mn_input_char; + item[active_item].input[i+1] = '\0'; + } + else + { + item[active_item].input = (char*) malloc(2*sizeof(char)); + item[active_item].input[0] = mn_input_char; + item[active_item].input[1] = '\0'; + } + } + + case MENU_ACTION_NONE: + break; + } + } + + MenuItem& new_item = item[active_item]; + if(new_item.kind == MN_DEACTIVE + || new_item.kind == MN_LABEL + || new_item.kind == MN_HL) + { + // Skip the horzontal line item + if (menuaction != MENU_ACTION_UP && menuaction != MENU_ACTION_DOWN) + menuaction = MENU_ACTION_DOWN; + + if (item.size() > 1) + action(); + } + + menuaction = MENU_ACTION_NONE; + + if (active_item >= int(item.size())) + active_item = int(item.size()) - 1; +} + +int +Menu::check() +{ + if (hit_item != -1) + return item[hit_item].id; + else + return -1; +} + +void +Menu::draw_item(DrawingContext& context, + int index, // Position of the current item in the menu + int menu_width, int menu_height) +{ + MenuItem& pitem = item[index]; + + int effect_offset = 0; + { + int effect_time = 0; + + if(effect.check()) + effect_time = effect.get_left() / 4; + + effect_offset = (index % 2) ? effect_time : -effect_time; + } + + Font* text_font = white_text; + int x_pos = pos_x; + int y_pos = pos_y + 24*index - menu_height/2 + 12 + effect_offset; + int shadow_size = 2; + int text_width = int(text_font->get_text_width(pitem.text)); + int input_width = int(text_font->get_text_width(pitem.input) + 10); + int list_width = + int(text_font->get_text_width(string_list_active(pitem.list))); + + if (arrange_left) + x_pos += 24 - menu_width/2 + (text_width + input_width + list_width)/2; + + if(index == active_item) + { + shadow_size = 3; + text_font = blue_text; + } + + switch (pitem.kind) + { + case MN_DEACTIVE: + { + context.draw_text_center(gray_text, pitem.text, + Vector(0, y_pos - int(blue_text->get_height()/2)), + LAYER_GUI); + break; + } + + case MN_HL: + { + // TODO + int x = pos_x - menu_width/2; + int y = y_pos - 12 - effect_offset; + /* Draw a horizontal line with a little 3d effect */ + context.draw_filled_rect(Vector(x, y + 6), + Vector(menu_width, 4), Color(150,200,255,225), LAYER_GUI); + context.draw_filled_rect(Vector(x, y + 6), + Vector(menu_width, 2), Color(255,255,255,255), LAYER_GUI); + break; + } + case MN_LABEL: + { + context.draw_text_center(white_big_text, + pitem.text, Vector(0, y_pos - int(white_big_text->get_height()/2)), + LAYER_GUI); + break; + } + case MN_TEXTFIELD: + case MN_NUMFIELD: + case MN_CONTROLFIELD_KB: + case MN_CONTROLFIELD_JS: + { + int width = text_width + input_width + 5; + int text_pos = screen->w/2 - width/2; + int input_pos = text_pos + text_width + 10; + + context.draw_filled_rect( + Vector(input_pos - 5, y_pos - 10), + Vector(input_width + 10, 20), + Color(255,255,255,255), LAYER_GUI-5); + context.draw_filled_rect( + Vector(input_pos - 4, y_pos - 9), + Vector(input_width + 8, 18), + Color(0,0,0,128), LAYER_GUI-4); + + if(pitem.kind == MN_CONTROLFIELD_KB) + get_controlfield_key_into_input(&pitem); + else if (pitem.kind == MN_CONTROLFIELD_JS) + get_controlfield_js_into_input(&pitem); + + if(pitem.kind == MN_TEXTFIELD || pitem.kind == MN_NUMFIELD) + { + if(active_item == index) + context.draw_text(gold_text, + pitem.get_input_with_symbol(true), + Vector(input_pos, y_pos - int(gold_text->get_height()/2)), + LAYER_GUI); + else + context.draw_text(gold_text, + pitem.get_input_with_symbol(false), + Vector(input_pos, y_pos - int(gold_text->get_height()/2)), + LAYER_GUI); + } + else + context.draw_text(gold_text, pitem.input, + Vector(input_pos, y_pos - int(gold_text->get_height()/2)), + LAYER_GUI); + + context.draw_text(text_font, pitem.text, + Vector(text_pos, y_pos - int(text_font->get_height()/2)), + LAYER_GUI); + break; + } + case MN_STRINGSELECT: + { + int list_pos_2 = list_width + 16; + int list_pos = list_width/2; + int text_pos = (text_width + 16)/2; + + /* Draw arrows */ + context.draw_surface(arrow_left, + Vector(x_pos - list_pos + text_pos - 17, y_pos - 8), + LAYER_GUI); + context.draw_surface(arrow_right, + Vector(x_pos - list_pos + text_pos - 1 + list_pos_2, y_pos - 8), + LAYER_GUI); + + /* Draw input background */ + context.draw_filled_rect( + Vector(x_pos - list_pos + text_pos - 1, y_pos - 10), + Vector(list_pos_2 + 2, 20), + Color(255,255,255,255), LAYER_GUI - 4); + context.draw_filled_rect( + Vector(x_pos - list_pos + text_pos, y_pos - 9), + Vector(list_pos_2, 18), + Color(0,0,0,128), LAYER_GUI - 5); + + context.draw_text_center(text_font, string_list_active(pitem.list), + Vector(text_pos, y_pos - int(text_font->get_height()/2)), + LAYER_GUI); + context.draw_text_center(text_font, pitem.text, + Vector(list_pos_2/2, y_pos - int(text_font->get_height()/2)), + LAYER_GUI); + break; + } + case MN_BACK: + { + context.draw_text_center(text_font, pitem.text, + Vector(0, y_pos - int(text_font->get_height()/2)), + LAYER_GUI); + context.draw_surface(back, + Vector(x_pos + text_width/2 + 16, y_pos - 8), + LAYER_GUI); + break; + } + + case MN_TOGGLE: + { + context.draw_text_center(text_font, pitem.text, + Vector(0, y_pos - (text_font->get_height()/2)), + LAYER_GUI); + + if(pitem.toggled) + context.draw_surface(checkbox_checked, + Vector(x_pos + (text_width+16)/2, y_pos - 8), + LAYER_GUI + 1); + else + context.draw_surface(checkbox, + Vector(x_pos + (text_width+16)/2, y_pos - 8), + LAYER_GUI + 1); + break; + } + case MN_ACTION: + context.draw_text_center(text_font, pitem.text, + Vector(0, y_pos - int(text_font->get_height()/2)), + LAYER_GUI); + break; + + case MN_GOTO: + context.draw_text_center(text_font, pitem.text, + Vector(0, y_pos - int(text_font->get_height()/2)), + LAYER_GUI); + break; + } +} + +int Menu::get_width() const +{ + /* The width of the menu has to be more than the width of the text + with the most characters */ + int menu_width = 0; + for(unsigned int i = 0; i < item.size(); ++i) + { + int w = strlen(item[i].text) + (item[i].input ? strlen(item[i].input) + 1 : 0) + strlen(string_list_active(item[i].list)); + if( w > menu_width ) + { + menu_width = w; + if( item[i].kind == MN_TOGGLE) + menu_width += 2; + } + } + + return (menu_width * 16 + 24); +} + +int Menu::get_height() const +{ + return item.size() * 24; +} + +/* Draw the current menu. */ +void +Menu::draw(DrawingContext& context) +{ + int menu_height = get_height(); + int menu_width = get_width(); + + /* Draw a transparent background */ + context.draw_filled_rect( + Vector(pos_x - menu_width/2, pos_y - 24*item.size()/2 - 10), + Vector(menu_width,menu_height + 20), + Color(150,180,200,125), LAYER_GUI-10); + + for(unsigned int i = 0; i < item.size(); ++i) + { + draw_item(context, i, menu_width, menu_height); + } +} + +MenuItem& +Menu::get_item_by_id(int id) +{ + for(std::vector::iterator i = item.begin(); i != item.end(); ++i) + { + if(i->id == id) + return *i; + } + + assert(false); + static MenuItem dummyitem; + return dummyitem; +} + +int Menu::get_active_item_id() +{ + return item[active_item].id; +} + +bool +Menu::isToggled(int id) +{ + return get_item_by_id(id).toggled; +} + +/* Check for menu event */ +void +Menu::event(SDL_Event& event) +{ + switch(event.type) + { + case SDL_KEYDOWN: + { + SDLKey key = key = event.key.keysym.sym; + SDLMod keymod; + char ch[2]; + keymod = SDL_GetModState(); + + /* If the current unicode character is an ASCII character, + assign it to ch. */ + if ( (event.key.keysym.unicode & 0xFF80) == 0 ) + { + ch[0] = event.key.keysym.unicode & 0x7F; + ch[1] = '\0'; + } + else + { + /* An International Character. */ + } + + if(item[active_item].kind == MN_CONTROLFIELD_KB) + { + if(key == SDLK_ESCAPE) + { + Menu::pop_current(); + return; + } + *item[active_item].int_p = key; + menuaction = MENU_ACTION_DOWN; + return; + } + + + switch(key) + { + case SDLK_UP: /* Menu Up */ + menuaction = MENU_ACTION_UP; + break; + case SDLK_DOWN: /* Menu Down */ + menuaction = MENU_ACTION_DOWN; + break; + case SDLK_LEFT: /* Menu Up */ + menuaction = MENU_ACTION_LEFT; + break; + case SDLK_RIGHT: /* Menu Down */ + menuaction = MENU_ACTION_RIGHT; + break; + case SDLK_SPACE: + if(item[active_item].kind == MN_TEXTFIELD) + { + menuaction = MENU_ACTION_INPUT; + mn_input_char = ' '; + break; + } + case SDLK_RETURN: /* Menu Hit */ + menuaction = MENU_ACTION_HIT; + break; + case SDLK_DELETE: + case SDLK_BACKSPACE: + menuaction = MENU_ACTION_REMOVE; + delete_character++; + break; + case SDLK_ESCAPE: + Menu::pop_current(); + break; + default: + if( (key >= SDLK_0 && key <= SDLK_9) || (key >= SDLK_a && key <= SDLK_z) || (key >= SDLK_SPACE && key <= SDLK_SLASH)) + { + menuaction = MENU_ACTION_INPUT; + mn_input_char = *ch; + } + else + { + mn_input_char = '\0'; + } + break; + } + } + break; + + case SDL_JOYAXISMOTION: + if(event.jaxis.axis == joystick_keymap.y_axis) + { + if (event.jaxis.value > joystick_keymap.dead_zone && !joystick_timer.started()) + { + menuaction = MENU_ACTION_DOWN; + joystick_timer.start(JOYSTICK_MENU_DELAY); + } + else if (event.jaxis.value < -joystick_keymap.dead_zone && !joystick_timer.started()) + { + menuaction = MENU_ACTION_UP; + joystick_timer.start(JOYSTICK_MENU_DELAY); + } + else + joystick_timer.stop(); + } + break; + case SDL_JOYBUTTONDOWN: + if (item[active_item].kind == MN_CONTROLFIELD_JS) + { + // FIXME: This next line does nothing useable, right? + // *item[active_item].int_p = key; + menuaction = MENU_ACTION_DOWN; + } + menuaction = MENU_ACTION_HIT; + break; + + case SDL_MOUSEBUTTONDOWN: + { + int x = event.motion.x; + int y = event.motion.y; + + if(x > pos_x - get_width()/2 && + x < pos_x + get_width()/2 && + y > pos_y - get_height()/2 && + y < pos_y + get_height()/2) + { + menuaction = MENU_ACTION_HIT; + } + } + break; + + case SDL_MOUSEMOTION: + { + int x = event.motion.x; + int y = event.motion.y; + + if(x > pos_x - get_width()/2 && + x < pos_x + get_width()/2 && + y > pos_y - get_height()/2 && + y < pos_y + get_height()/2) + { + active_item = (y - (pos_y - get_height()/2)) / 24; + mouse_cursor->set_state(MC_LINK); + } + else + { + mouse_cursor->set_state(MC_NORMAL); + } + } + break; + + default: + break; + } +} + + +// EOF // diff --git a/lib/gui/menu.h b/lib/gui/menu.h new file mode 100644 index 000000000..e2113d0db --- /dev/null +++ b/lib/gui/menu.h @@ -0,0 +1,194 @@ +// $Id$ +// +// SuperTux +// Copyright (C) 2004 Tobias Glaesser +// +// This program is free software; you can redistribute it and/or +// modify it under the terms of the GNU General Public License +// as published by the Free Software Foundation; either version 2 +// of the License, or (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software +// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + +#ifndef SUPERTUX_MENU_H +#define SUPERTUX_MENU_H + +#include + +#include "SDL.h" + +#include "video/surface.h" +#include "special/timer.h" +#include "special/base.h" +#include "special/stringlist.h" +#include "gui/mousecursor.h" + + +/* Joystick menu delay */ +#define JOYSTICK_MENU_DELAY 500 + +/* IDs for menus */ + +bool confirm_dialog(Surface* background, std::string text); + +/* Kinds of menu items */ +enum MenuItemKind { + MN_ACTION, + MN_GOTO, + MN_TOGGLE, + MN_BACK, + MN_DEACTIVE, + MN_TEXTFIELD, + MN_NUMFIELD, + MN_CONTROLFIELD_KB, + MN_CONTROLFIELD_JS, + MN_STRINGSELECT, + MN_LABEL, + MN_HL, /* horizontal line */ +}; + +class Menu; + +class MenuItem +{ +public: + MenuItemKind kind; + int toggled; + char *text; + char *input; + int *int_p; // used for setting keys (can be used for more stuff...) + int id; // item id + string_list_type* list; + Menu* target_menu; + + void change_text (const char *text); + void change_input(const char *text); + + static MenuItem* create(MenuItemKind kind, const char *text, int init_toggle, Menu* target_menu, int id, int* int_p); + + std::string get_input_with_symbol(bool active_item); // returns the text with an input symbol +private: + bool input_flickering; + Timer input_flickering_timer; +}; + +class Menu +{ +private: + static std::vector last_menus; + static Menu* current_; + + static void push_current(Menu* pmenu); + static void pop_current(); + +public: + /** Set the current menu, if pmenu is NULL, hide the current menu */ + static void set_current(Menu* pmenu); + + /** Return the current active menu or NULL if none is active */ + static Menu* current() { return current_; } + +private: + /* Action done on the menu */ + enum MenuAction { + MENU_ACTION_NONE = -1, + MENU_ACTION_UP, + MENU_ACTION_DOWN, + MENU_ACTION_LEFT, + MENU_ACTION_RIGHT, + MENU_ACTION_HIT, + MENU_ACTION_INPUT, + MENU_ACTION_REMOVE + }; + + /** Number of the item that got 'hit' (ie. pressed) in the last + event()/action() call, -1 if none */ + int hit_item; + + // position of the menu (ie. center of the menu, not top/left) + int pos_x; + int pos_y; + + /** input event for the menu (up, down, left, right, etc.) */ + MenuAction menuaction; + + /* input implementation variables */ + int delete_character; + char mn_input_char; + Timer joystick_timer; + +public: + Timer effect; + int arrange_left; + int active_item; + + std::vector item; + + Menu(); + ~Menu(); + + void additem(MenuItem* pmenu_item); + void additem(MenuItemKind kind, const std::string& text, int init_toggle, Menu* target_menu, int id = -1, int *int_p = NULL); + + void action (); + + /** Remove all entries from the menu */ + void clear(); + + /** Return the index of the menu item that was 'hit' (ie. the user + clicked on it) in the last event() call */ + int check (); + + MenuItem& get_item(int index) { return item[index]; } + MenuItem& get_item_by_id(int id); + + int get_active_item_id(); + + bool isToggled(int id); + + void Menu::get_controlfield_key_into_input(MenuItem *item); + void Menu::get_controlfield_js_into_input(MenuItem *item); + + void draw(DrawingContext& context); + void draw_item(DrawingContext& context, + int index, int menu_width, int menu_height); + void set_pos(int x, int y, float rw = 0, float rh = 0); + + /** translate a SDL_Event into a menu_action */ + void event(SDL_Event& event); + + int get_width() const; + int get_height() const; + + bool is_toggled(int id) const; +}; + +extern Surface* checkbox; +extern Surface* checkbox_checked; +extern Surface* back; +extern Surface* arrow_left; +extern Surface* arrow_right; + +extern Menu* contrib_menu; +extern Menu* contrib_subset_menu; +extern Menu* main_menu; +extern Menu* game_menu; +extern Menu* options_menu; +extern Menu* options_keys_menu; +extern Menu* options_joystick_menu; +extern Menu* highscore_menu; +extern Menu* load_game_menu; +extern Menu* save_game_menu; + +#endif /*SUPERTUX_MENU_H*/ + +/* Local Variables: */ +/* mode: c++ */ +/* End: */ diff --git a/lib/gui/mousecursor.cpp b/lib/gui/mousecursor.cpp new file mode 100644 index 000000000..3956c0fcf --- /dev/null +++ b/lib/gui/mousecursor.cpp @@ -0,0 +1,96 @@ +// $Id$ +// +// SuperTux - A Jump'n Run +// Copyright (C) 2004 Ricardo Cruz +// +// This program is free software; you can redistribute it and/or +// modify it under the terms of the GNU General Public License +// as published by the Free Software Foundation; either version 2 +// of the License, or (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software +// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + +#include "video/drawing_context.h" +#include "gui/mousecursor.h" + +MouseCursor* MouseCursor::current_ = 0; + +MouseCursor::MouseCursor(std::string cursor_file, int frames) : mid_x(0), mid_y(0) +{ + cursor = new Surface(cursor_file, true); + + cur_state = MC_NORMAL; + cur_frame = 0; + tot_frames = frames; + + timer.init(false); + timer.start(MC_FRAME_PERIOD); + + SDL_ShowCursor(SDL_DISABLE); +} + +MouseCursor::~MouseCursor() +{ + delete cursor; + + SDL_ShowCursor(SDL_ENABLE); +} + +int MouseCursor::state() +{ + return cur_state; +} + +void MouseCursor::set_state(int nstate) +{ + cur_state = nstate; +} + +void MouseCursor::set_mid(int x, int y) +{ + mid_x = x; + mid_y = y; +} + +void MouseCursor::draw(DrawingContext& context) +{ + if(cur_state == MC_HIDE) + return; + + int x,y,w,h; + Uint8 ispressed = SDL_GetMouseState(&x,&y); + w = cursor->w / tot_frames; + h = cursor->h / MC_STATES_NB; + if(ispressed &SDL_BUTTON(1) || ispressed &SDL_BUTTON(2)) + { + if(cur_state != MC_CLICK) + { + state_before_click = cur_state; + cur_state = MC_CLICK; + } + } + else + { + if(cur_state == MC_CLICK) + cur_state = state_before_click; + } + + if(timer.get_left() < 0 && tot_frames > 1) + { + cur_frame++; + if(cur_frame++ >= tot_frames) + cur_frame = 0; + + timer.start(MC_FRAME_PERIOD); + } + + context.draw_surface_part(cursor, Vector(w*cur_frame, h*cur_state), Vector(w, + h), Vector(x-mid_x, y-mid_y), LAYER_GUI+100); +} diff --git a/lib/gui/mousecursor.h b/lib/gui/mousecursor.h new file mode 100644 index 000000000..8ff45e640 --- /dev/null +++ b/lib/gui/mousecursor.h @@ -0,0 +1,61 @@ +// $Id$ +// +// SuperTux - A Jump'n Run +// Copyright (C) 2004 Ricardo Cruz +// +// This program is free software; you can redistribute it and/or +// modify it under the terms of the GNU General Public License +// as published by the Free Software Foundation; either version 2 +// of the License, or (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software +// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + +#ifndef SUPERTUX_MOUSECURSOR_H +#define SUPERTUX_MOUSECURSOR_H + +#include + +#include "special/timer.h" +#include "video/surface.h" + +#define MC_FRAME_PERIOD 800 // in ms + +#define MC_STATES_NB 3 +enum { + MC_NORMAL, + MC_CLICK, + MC_LINK, + MC_HIDE +}; + +class MouseCursor +{ +public: + MouseCursor(std::string cursor_file, int frames); + ~MouseCursor(); + int state(); + void set_state(int nstate); + void set_mid(int x, int y); + void draw(DrawingContext& context); + + static MouseCursor* current() { return current_; }; + static void set_current(MouseCursor* pcursor) { current_ = pcursor; }; + +private: + int mid_x, mid_y; + static MouseCursor* current_; + int state_before_click; + int cur_state; + int cur_frame, tot_frames; + Surface* cursor; + Timer timer; +}; + +#endif /*SUPERTUX_MOUSECURSOR_H*/ diff --git a/lib/math/physic.cpp b/lib/math/physic.cpp new file mode 100644 index 000000000..09568f852 --- /dev/null +++ b/lib/math/physic.cpp @@ -0,0 +1,140 @@ +// $Id$ +// +// SuperTux +// Copyright (C) 2004 Tobias Glaesser +// +// This program is free software; you can redistribute it and/or +// modify it under the terms of the GNU General Public License +// as published by the Free Software Foundation; either version 2 +// of the License, or (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software +// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA +// 02111-1307, USA. + +#include + +#include "math/physic.h" +#include "special/timer.h" + +Physic::Physic() + : ax(0), ay(0), vx(0), vy(0), gravity_enabled(true) +{ +} + +Physic::~Physic() +{ +} + +void +Physic::reset() +{ + ax = ay = vx = vy = 0; + gravity_enabled = true; +} + +void +Physic::set_velocity_x(float nvx) +{ + vx = nvx; +} + +void +Physic::set_velocity_y(float nvy) +{ + vy = -nvy; +} + +void +Physic::set_velocity(float nvx, float nvy) +{ + vx = nvx; + vy = -nvy; +} + +void Physic::inverse_velocity_x() +{ +vx = -vx; +} + +void Physic::inverse_velocity_y() +{ +vy = -vy; +} + +float +Physic::get_velocity_x() +{ + return vx; +} + +float +Physic::get_velocity_y() +{ + return -vy; +} + +void +Physic::set_acceleration_x(float nax) +{ + ax = nax; +} + +void +Physic::set_acceleration_y(float nay) +{ + ay = -nay; +} + +void +Physic::set_acceleration(float nax, float nay) +{ + ax = nax; + ay = -nay; +} + +float +Physic::get_acceleration_x() +{ + return ax; +} + +float +Physic::get_acceleration_y() +{ + return -ay; +} + +void +Physic::enable_gravity(bool enable_gravity) +{ + gravity_enabled = enable_gravity; +} + +void +Physic::apply(float elapsed_time, float &x, float &y, float& gravity) +{ + float grav; + if(gravity_enabled) + grav = gravity / 100.0; + else + grav = 0; + + x += vx * elapsed_time + ax * elapsed_time * elapsed_time; + y += vy * elapsed_time + (ay + grav) * elapsed_time * elapsed_time; + vx += ax * elapsed_time; + vy += (ay + grav) * elapsed_time; +} + +void +Physic::apply(Vector& vector, float elapsed_time, float& gravity) +{ + apply(elapsed_time, vector.x, vector.y, gravity); +} + diff --git a/lib/math/physic.h b/lib/math/physic.h new file mode 100644 index 000000000..28dfbb0fe --- /dev/null +++ b/lib/math/physic.h @@ -0,0 +1,80 @@ +// $Id$ +// +// SuperTux +// Copyright (C) 2004 Tobias Glaesser +// +// This program is free software; you can redistribute it and/or +// modify it under the terms of the GNU General Public License +// as published by the Free Software Foundation; either version 2 +// of the License, or (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software +// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA +// 02111-1307, USA. + +#ifndef SUPERTUX_PHYSIC_H +#define SUPERTUX_PHYSIC_H + +#include "math/vector.h" + +/** This is a very simplistic physics engine handling accelerated and constant + * movement along with gravity. + */ +class Physic +{ +public: + Physic(); + ~Physic(); + + /** resets all velocities and accelerations to 0 */ + void reset(); + + /** sets velocity to a fixed value */ + void set_velocity(float vx, float vy); + + void set_velocity_x(float vx); + void set_velocity_y(float vy); + + /** velocities invertion */ + void inverse_velocity_x(); + void inverse_velocity_y(); + + float get_velocity_x(); + float get_velocity_y(); + + /** sets acceleration applied to the object. (Note that gravity is + * eventually added to the vertical acceleration) + */ + void set_acceleration(float ax, float ay); + + void set_acceleration_x(float ax); + void set_acceleration_y(float ay); + + float get_acceleration_x(); + float get_acceleration_y(); + + /** enables or disables handling of gravity */ + void enable_gravity(bool gravity_enabled); + + /** applies the physical simulation to given x and y coordinates */ + void apply(float frame_ratio, float &x, float &y, float& gravity = 10); + + /** applies the physical simulation to given x and y coordinates */ + void apply(Vector& vector, float frame_ratio, float& gravity = 10); + +private: + /// horizontal and vertical acceleration + float ax, ay; + /// horizontal and vertical velocity + float vx, vy; + /// should we respect gravity in out calculations? + bool gravity_enabled; +}; + +#endif /*SUPERTUX_PHYSIC_H*/ diff --git a/lib/math/vector.cpp b/lib/math/vector.cpp new file mode 100644 index 000000000..2901e3569 --- /dev/null +++ b/lib/math/vector.cpp @@ -0,0 +1,32 @@ +// $Id$ +// +// SuperTux - A Jump'n Run +// Copyright (C) 2004 Matthias Braun + +#include "math/vector.h" + +Vector Vector::unit() const +{ + return *this / norm(); +} + +float Vector::norm() const +{ + return sqrt(x*x + y*y); +} diff --git a/lib/math/vector.h b/lib/math/vector.h new file mode 100644 index 000000000..b93345445 --- /dev/null +++ b/lib/math/vector.h @@ -0,0 +1,96 @@ +// $Id$ +// +// SuperTux - A Jump'n Run +// Copyright (C) 2004 Matthias Braun +// +// This program is free software; you can redistribute it and/or +// modify it under the terms of the GNU General Public License +// as published by the Free Software Foundation; either version 2 +// of the License, or (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software +// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA +// 02111-1307, USA. + +#ifndef SUPERTUX_TYPE_H +#define SUPERTUX_TYPE_H + +#include + +#include "SDL.h" + +/* 'Base' type for game objects */ + +struct base_type +{ + float x; + float y; + float width; + float height; +}; + + +#endif /*SUPERTUX_TYPE_H*/ + diff --git a/lib/special/game_object.cpp b/lib/special/game_object.cpp new file mode 100644 index 000000000..e1bf9b985 --- /dev/null +++ b/lib/special/game_object.cpp @@ -0,0 +1,30 @@ +// $Id$ +// +// SuperTux - A Jump'n Run +// Copyright (C) 2004 Matthias Braun + +class DrawingContext; + +/** + * Base class for all game objects. This contains functions for: + * -querying the actual type of the object + * -a flag that indicates if the object wants to be removed. Objects with this + * flag will be removed at the end of each frame. This is alot safer than + * having some uncontrollable "delete this" in the code. + * -an action function that is called once per frame and allows the object to + * update it's state. + * + * Most GameObjects will also implement the DrawableObject interface so that + * they can actually be drawn on screen. + */ +class GameObject // TODO rename this once the game has been converted +{ +public: + GameObject(); + virtual ~GameObject(); + + /** This function is called once per frame and allows the object to update + * it's state. The elapsed_time is the time since the last frame and should be + * the base for all timed things. + */ + virtual void action(float elapsed_time) = 0; + + /** The GameObject should draw itself onto the provided DrawingContext if this + * function is called. + */ + virtual void draw(DrawingContext& context) = 0; + + /** returns true if the object is not scheduled to be removed yet */ + bool is_valid() const + { return !wants_to_die; } + /** schedules this object to be removed at the end of the frame */ + void remove_me() + { wants_to_die = true; } + +private: + /** this flag indicates if the object should be removed at the end of the + * frame + */ + bool wants_to_die; +}; + +#endif /*SUPERTUX_GAMEOBJECT_H*/ + diff --git a/lib/special/moving_object.cpp b/lib/special/moving_object.cpp new file mode 100644 index 000000000..92c922935 --- /dev/null +++ b/lib/special/moving_object.cpp @@ -0,0 +1,31 @@ +// $Id$ +// +// SuperTux - A Jump'n Run +// Copyright (C) 2004 Matthias Braun +// +// This program is free software; you can redistribute it and/or +// modify it under the terms of the GNU General Public License +// as published by the Free Software Foundation; either version 2 +// of the License, or (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software +// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + +#include +#include + +#include "app/globals.h" +#include "app/setup.h" +#include "special/sprite.h" +#include "video/drawing_context.h" + +Sprite::Sprite(lisp_object_t* cur) +{ + init_defaults(); + + LispReader reader(cur); + + if(!reader.read_string("name", name)) + st_abort("Sprite wihtout name", ""); + reader.read_int("x-hotspot", x_hotspot); + reader.read_int("y-hotspot", y_hotspot); + reader.read_float("fps", fps); + + std::vector images; + if(!reader.read_string_vector("images", images)) + st_abort("Sprite contains no images: ", name.c_str()); + + for(std::vector::size_type i = 0; i < images.size(); ++i) + { + surfaces.push_back( + new Surface(datadir + "/images/" + images[i], true)); + } + + frame_delay = 1000.0f/fps; +} + +Sprite::~Sprite() +{ + for(std::vector::iterator i = surfaces.begin(); i != surfaces.end(); + ++i) + delete *i; +} + +void +Sprite::init_defaults() +{ + x_hotspot = 0; + y_hotspot = 0; + fps = 10; + time = 0; + frame_delay = 1000.0f/fps; +} + +void +Sprite::update(float /*delta*/) +{ + //time += 10*delta; + //std::cout << "Delta: " << delta << std::endl; +} + +void +Sprite::draw(DrawingContext& context, const Vector& pos, int layer, + Uint32 drawing_effect) +{ + time = SDL_GetTicks(); + unsigned int frame = get_current_frame(); + + if (frame < surfaces.size()) + { + Surface* surface = surfaces[frame]; + + context.draw_surface(surface, pos - Vector(x_hotspot, y_hotspot), layer, drawing_effect); + } +} + +#if 0 +void +Sprite::draw_part(float sx, float sy, float x, float y, float w, float h) +{ + time = SDL_GetTicks(); + unsigned int frame = get_current_frame(); + + if (frame < surfaces.size()) + surfaces[frame]->draw_part(sx, sy, x - x_hotspot, y - y_hotspot, w, h); +} +#endif + +void +Sprite::reset() +{ + time = 0; +} + +int +Sprite::get_current_frame() const +{ + unsigned int frame = static_cast(fmodf(time, surfaces.size()*frame_delay)/frame_delay); + return frame % surfaces.size(); +} + +int +Sprite::get_width() const +{ + return surfaces[get_current_frame()]->w; +} + +int +Sprite::get_height() const +{ + return surfaces[get_current_frame()]->h; +} + +/* EOF */ diff --git a/lib/special/sprite.h b/lib/special/sprite.h new file mode 100644 index 000000000..3e03ee2db --- /dev/null +++ b/lib/special/sprite.h @@ -0,0 +1,80 @@ +// $Id$ +// +// SuperTux +// Copyright (C) 2004 Ingo Ruhnke +// +// This program is free software; you can redistribute it and/or +// modify it under the terms of the GNU General Public License +// as published by the Free Software Foundation; either version 2 +// of the License, or (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software +// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + +#ifndef SUPERTUX_SPRITE_H +#define SUPERTUX_SPRITE_H + +#include +#include + +#include "utils/lispreader.h" +#include "video/surface.h" +#include "math/vector.h" + +class Sprite +{ + private: + std::string name; + + int x_hotspot; + int y_hotspot; + + /** Frames per second */ + float fps; + + /** Number of seconds that a frame is displayed until it is switched + to the next frame */ + float frame_delay; + + float time; + + std::vector surfaces; + + void init_defaults(); + public: + /** cur has to be a pointer to data in the form of ((x-hotspot 5) + (y-hotspot 10) ...) */ + Sprite(lisp_object_t* cur); + ~Sprite(); + + void reset(); + + /** Update the sprite and process to the next frame */ + void update(float delta); + void draw(DrawingContext& context, const Vector& pos, int layer, + Uint32 drawing_effect = NONE_EFFECT); + int get_current_frame() const; + + float get_fps() { return fps; } ; + int get_frames() { return surfaces.size(); } ; + + std::string get_name() const { return name; } + int get_width() const; + int get_height() const; + + Surface* get_frame(unsigned int frame) + { if(frame < surfaces.size()) return surfaces[frame]; + else return surfaces[0]; } +}; + +#endif /*SUPERTUX_SPRITE_H*/ + +/* Local Variables: */ +/* mode:c++ */ +/* End: */ diff --git a/lib/special/sprite_manager.cpp b/lib/special/sprite_manager.cpp new file mode 100644 index 000000000..1c4b72e2e --- /dev/null +++ b/lib/special/sprite_manager.cpp @@ -0,0 +1,100 @@ +// $Id$ +// +// SuperTux +// Copyright (C) 2004 Ingo Ruhnke +// +// This program is free software; you can redistribute it and/or +// modify it under the terms of the GNU General Public License +// as published by the Free Software Foundation; either version 2 +// of the License, or (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software +// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + +#include + +#include "utils/lispreader.h" +#include "special/sprite_manager.h" + +SpriteManager::SpriteManager(const std::string& filename) +{ + load_resfile(filename); +} + +SpriteManager::~SpriteManager() +{ + for(std::map::iterator i = sprites.begin(); + i != sprites.end(); ++i) { + delete i->second; + } +} + +void +SpriteManager::load_resfile(const std::string& filename) +{ + lisp_object_t* root_obj = lisp_read_from_file(filename); + if (!root_obj) + { + std::cout << "SpriteManager: Couldn't load: " << filename << std::endl; + return; + } + + lisp_object_t* cur = root_obj; + + if (strcmp(lisp_symbol(lisp_car(cur)), "supertux-resources") != 0) + return; + cur = lisp_cdr(cur); + + while(cur) + { + lisp_object_t* el = lisp_car(cur); + + if (strcmp(lisp_symbol(lisp_car(el)), "sprite") == 0) + { + Sprite* sprite = new Sprite(lisp_cdr(el)); + + Sprites::iterator i = sprites.find(sprite->get_name()); + if (i == sprites.end()) + { + sprites[sprite->get_name()] = sprite; + } + else + { + delete i->second; + i->second = sprite; + std::cout << "Warning: dulpicate entry: '" << sprite->get_name() << "'" << std::endl; + } + } + else + { + std::cout << "SpriteManager: Unknown tag" << std::endl; + } + + cur = lisp_cdr(cur); + } + + lisp_free(root_obj); +} + +Sprite* +SpriteManager::load(const std::string& name) +{ + Sprites::iterator i = sprites.find(name); + if (i != sprites.end()) + { + return i->second; + } + else + { + std::cout << "SpriteManager: Sprite '" << name << "' not found" << std::endl; + return 0; + } +} + +/* EOF */ diff --git a/lib/special/sprite_manager.h b/lib/special/sprite_manager.h new file mode 100644 index 000000000..9b240a04d --- /dev/null +++ b/lib/special/sprite_manager.h @@ -0,0 +1,47 @@ +// $Id$ +// +// SuperTux +// Copyright (C) 2004 Ingo Ruhnke +// +// This program is free software; you can redistribute it and/or +// modify it under the terms of the GNU General Public License +// as published by the Free Software Foundation; either version 2 +// of the License, or (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software +// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + +#ifndef SUPERTUX_SPRITE_MANAGER_H +#define SUPERTUX_SPRITE_MANAGER_H + +#include + +#include "special/sprite.h" + +class SpriteManager +{ + private: + typedef std::map Sprites; + Sprites sprites; + public: + SpriteManager(const std::string& filename); + ~SpriteManager(); + + void load_resfile(const std::string& filename); + /** loads a sprite. + * WARNING: You must not delete the returned object. + */ + Sprite* load(const std::string& name); +}; + +#endif /*SUPERTUX_SPRITE_MANAGER_H*/ + +/* Local Variables: */ +/* mode:c++ */ +/* End: */ diff --git a/lib/special/stringlist.cpp b/lib/special/stringlist.cpp new file mode 100644 index 000000000..1a3440c22 --- /dev/null +++ b/lib/special/stringlist.cpp @@ -0,0 +1,119 @@ +// $Id$ +// +// SuperTux +// Copyright (C) 2004 Tobias Glaesser +// +// This program is free software; you can redistribute it and/or +// modify it under the terms of the GNU General Public License +// as published by the Free Software Foundation; either version 2 +// of the License, or (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software +// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA +// 02111-1307, USA. + +#include "string.h" +#include "stdlib.h" +#include "special/stringlist.h" + +void string_list_init(string_list_type* pstring_list) +{ + pstring_list->num_items = 0; + pstring_list->active_item = -1; + pstring_list->item = NULL; +} + +char* string_list_active(string_list_type* pstring_list) +{ + if(pstring_list == NULL) + return ""; + + if(pstring_list->active_item != -1) + return pstring_list->item[pstring_list->active_item]; + else + return ""; +} + +void string_list_add_item(string_list_type* pstring_list,const char* str) +{ + char *pnew_string; + pnew_string = (char*) malloc(sizeof(char)*(strlen(str)+1)); + strcpy(pnew_string,str); + ++pstring_list->num_items; + pstring_list->item = (char**) realloc(pstring_list->item,sizeof(char**)*pstring_list->num_items); + pstring_list->item[pstring_list->num_items-1] = pnew_string; + if(pstring_list->active_item == -1) + pstring_list->active_item = 0; +} + +void string_list_copy(string_list_type* pstring_list, string_list_type pstring_list_orig) +{ + int i; + string_list_free(pstring_list); + for(i = 0; i < pstring_list_orig.num_items; ++i) + string_list_add_item(pstring_list,pstring_list_orig.item[i]); +} + +int string_list_find(string_list_type* pstring_list,const char* str) +{ + int i; + for(i = 0; i < pstring_list->num_items; ++i) + { + if(strcmp(pstring_list->item[i],str) == 0) + { + return i; + } + } + return -1; +} + +void string_list_sort(string_list_type* pstring_list) +{ + int i,j,y; + + for(j = 0; j < pstring_list->num_items; ++j) + for(i = 0; i < pstring_list->num_items-1; ++i) + { + + y = strcmp(pstring_list->item[i],pstring_list->item[i+1]); + if(y == 0) + { + continue; + } + else if(y < 0) + { + continue; + } + else if(y > 0) + { + char* char_pointer; + char_pointer = pstring_list->item[i]; + pstring_list->item[i] = pstring_list->item[i+1]; + pstring_list->item[i+1] = char_pointer; + continue; + } + + } + +} + +void string_list_free(string_list_type* pstring_list) +{ + if(pstring_list != NULL) + { + int i; + for(i=0; i < pstring_list->num_items; ++i) + free(pstring_list->item[i]); + free(pstring_list->item); + pstring_list->item = NULL; + pstring_list->num_items = 0; + pstring_list->active_item = -1; + } +} + diff --git a/lib/special/stringlist.h b/lib/special/stringlist.h new file mode 100644 index 000000000..6428813cd --- /dev/null +++ b/lib/special/stringlist.h @@ -0,0 +1,42 @@ +// $Id$ +// +// SuperTux +// Copyright (C) 2004 Tobias Glaesser +// +// This program is free software; you can redistribute it and/or +// modify it under the terms of the GNU General Public License +// as published by the Free Software Foundation; either version 2 +// of the License, or (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software +// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA +// 02111-1307, USA. + +#ifndef SUPERTUX_STRINGLIST_H +#define SUPERTUX_STRINGLIST_H + +struct string_list_type +{ + int num_items; + int active_item; + char **item; +}; + +void string_list_init(string_list_type* pstring_list); +char* string_list_active(string_list_type* pstring_list); +void string_list_copy(string_list_type* pstring_list, string_list_type pstring_list_orig); +int string_list_find(string_list_type* pstring_list, const char* str); +void string_list_sort(string_list_type* pstring_list); +void string_list_add_item(string_list_type* pstring_list, const char* str); +void string_list_free(string_list_type* pstring_list); + + + +#endif /*SUPERTUX_STRINGLIST_H*/ + diff --git a/lib/special/timer.cpp b/lib/special/timer.cpp new file mode 100644 index 000000000..b1a093bb1 --- /dev/null +++ b/lib/special/timer.cpp @@ -0,0 +1,166 @@ +// $Id$ +// +// SuperTux +// Copyright (C) 2004 Tobias Glaesser +// +// This program is free software; you can redistribute it and/or +// modify it under the terms of the GNU General Public License +// as published by the Free Software Foundation; either version 2 +// of the License, or (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software +// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA +// 02111-1307, USA. + +#include "SDL.h" +#include "special/timer.h" + +unsigned int st_pause_ticks, st_pause_count; + +unsigned int st_get_ticks(void) +{ + if(st_pause_count != 0) + return /*SDL_GetTicks()*/ - st_pause_ticks /*- SDL_GetTicks()*/ + st_pause_count; + else + return SDL_GetTicks() - st_pause_ticks; +} + +void st_pause_ticks_init(void) +{ + st_pause_ticks = 0; + st_pause_count = 0; +} + +void st_pause_ticks_start(void) +{ + if(st_pause_count == 0) + st_pause_count = SDL_GetTicks(); +} + +void st_pause_ticks_stop(void) +{ +if(st_pause_count == 0) +return; + + st_pause_ticks += SDL_GetTicks() - st_pause_count; + st_pause_count = 0; +} + +bool st_pause_ticks_started(void) +{ +if(st_pause_count == 0) +return false; +else +return true; +} + +Timer::Timer() +{ + init(true); +} + +void +Timer::init(bool st_ticks) +{ + period = 0; + time = 0; + get_ticks = st_ticks ? st_get_ticks : SDL_GetTicks; +} + +void +Timer::start(unsigned int period_) +{ + time = get_ticks(); + period = period_; +} + +void +Timer::stop() +{ + if(get_ticks == st_get_ticks) + init(true); + else + init(false); +} + +int +Timer::check() +{ + if((time != 0) && (time + period > get_ticks())) + return true; + else + { + time = 0; + return false; + } +} + +int +Timer::started() +{ + if(time != 0) + return true; + else + return false; +} + +int +Timer::get_left() +{ + return (period - (get_ticks() - time)); +} + +int +Timer::get_gone() +{ + return (get_ticks() - time); +} + +void +Timer::fwrite(FILE* fi) +{ + unsigned int diff_ticks; + int tick_mode; + if(time != 0) + diff_ticks = get_ticks() - time; + else + diff_ticks = 0; + + ::fwrite(&period,sizeof(unsigned int),1,fi); + ::fwrite(&diff_ticks,sizeof(unsigned int),1,fi); + if(get_ticks == st_get_ticks) + tick_mode = true; + else + tick_mode = false; + ::fwrite(&tick_mode,sizeof(unsigned int),1,fi); +} + +void +Timer::fread(FILE* fi) +{ + unsigned int diff_ticks; + int tick_mode; + + ::fread(&period,sizeof(unsigned int),1,fi); + ::fread(&diff_ticks,sizeof(unsigned int),1,fi); + ::fread(&tick_mode,sizeof(unsigned int),1,fi); + + if (tick_mode) + get_ticks = st_get_ticks; + else + get_ticks = SDL_GetTicks; + + if (diff_ticks != 0) + time = get_ticks() - diff_ticks; + else + time = 0; + +} + +/* EOF */ diff --git a/lib/special/timer.h b/lib/special/timer.h new file mode 100644 index 000000000..734c896a0 --- /dev/null +++ b/lib/special/timer.h @@ -0,0 +1,69 @@ +// $Id$ +// +// SuperTux +// Copyright (C) 2004 Tobias Glaesser +// +// This program is free software; you can redistribute it and/or +// modify it under the terms of the GNU General Public License +// as published by the Free Software Foundation; either version 2 +// of the License, or (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software +// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA +// 02111-1307, USA. + +#ifndef SUPERTUX_TIMER_H +#define SUPERTUX_TIMER_H + +extern unsigned int st_pause_ticks, st_pause_count; + +unsigned int st_get_ticks(void); +void st_pause_ticks_init(void); +void st_pause_ticks_start(void); +void st_pause_ticks_stop(void); +bool st_pause_ticks_started(void); + +class Timer +{ + public: + unsigned int period; + unsigned int time; + unsigned int (*get_ticks) (void); + + public: + Timer(); + + void init(bool st_ticks); + void start(unsigned int period); + void stop(); + + /*====================================================================== + return: NO = the timer is not started + or it is over + YES = otherwise + ======================================================================*/ + int check(); + int started(); + + /*====================================================================== + return: the time left (in millisecond) + note : the returned value can be negative + ======================================================================*/ + int get_left(); + + int get_gone(); + void fwrite(FILE* fi); + void fread(FILE* fi); +}; + +#endif /*SUPERTUX_TIMER_H*/ + +/* Local Variables: */ +/* mode:c++ */ +/* End: */ diff --git a/lib/utils/configfile.cpp b/lib/utils/configfile.cpp new file mode 100644 index 000000000..cbef85a84 --- /dev/null +++ b/lib/utils/configfile.cpp @@ -0,0 +1,143 @@ +// $Id$ +// +// SuperTux - A Jump'n Run +// Copyright (C) 2004 Michael George +// +// This program is free software; you can redistribute it and/or +// modify it under the terms of the GNU General Public License +// as published by the Free Software Foundation; either version 2 +// of the License, or (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software +// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + +#include +#include + +#include "utils/configfile.h" +#include "app/setup.h" +#include "app/globals.h" + +#ifdef WIN32 +const char * config_filename = "/st_config.dat"; +#else +const char * config_filename = "/config"; +#endif + +Config* config = 0; + +static void defaults () +{ + /* Set defaults: */ + debug_mode = false; + audio_device = true; + + use_fullscreen = false; + show_fps = false; + use_gl = false; + + use_sound = true; + use_music = true; +} + +void Config::load() +{ + FILE * file = NULL; + + defaults(); + + /* override defaults from config file */ + + file = opendata(config_filename, "r"); + + if (file == NULL) + return; + + /* read config file */ + + lisp_stream_t stream; + lisp_object_t * root_obj = NULL; + + lisp_stream_init_file (&stream, file); + root_obj = lisp_read (&stream); + + if (root_obj->type == LISP_TYPE_EOF || root_obj->type == LISP_TYPE_PARSE_ERROR) + return; + + if (strcmp(lisp_symbol(lisp_car(root_obj)), "supertux-config") != 0) + return; + + LispReader reader(lisp_cdr(root_obj)); + + reader.read_bool("fullscreen", use_fullscreen); + reader.read_bool("sound", use_sound); + reader.read_bool("music", use_music); + reader.read_bool("show_fps", show_fps); + + std::string video; + reader.read_string ("video", video); + if (video == "opengl") + use_gl = true; + else + use_gl = false; + + reader.read_int ("joystick", joystick_num); + + if (joystick_num >= 0) + { + reader.read_int ("joystick-x", joystick_keymap.x_axis); + reader.read_int ("joystick-y", joystick_keymap.y_axis); + reader.read_int ("joystick-a", joystick_keymap.a_button); + reader.read_int ("joystick-b", joystick_keymap.b_button); + reader.read_int ("joystick-start", joystick_keymap.start_button); + reader.read_int ("joystick-deadzone", joystick_keymap.dead_zone); + } + + customload(reader); + + lisp_free(root_obj); +} + +void Config::save () +{ + /* write settings to config file */ + FILE * config = opendata(config_filename, "w"); + + if(config) + { + fprintf(config, "(supertux-config\n"); + fprintf(config, "\t;; the following options can be set to #t or #f:\n"); + fprintf(config, "\t(fullscreen %s)\n", use_fullscreen ? "#t" : "#f"); + fprintf(config, "\t(sound %s)\n", use_sound ? "#t" : "#f"); + fprintf(config, "\t(music %s)\n", use_music ? "#t" : "#f"); + fprintf(config, "\t(show_fps %s)\n", show_fps ? "#t" : "#f"); + + fprintf(config, "\n\t;; either \"opengl\" or \"sdl\"\n"); + fprintf(config, "\t(video \"%s\")\n", use_gl ? "opengl" : "sdl"); + + if(use_joystick) + { + fprintf(config, "\n\t;; joystick number:\n"); + fprintf(config, "\t(joystick %d)\n", joystick_num); + + fprintf(config, "\t(joystick-x %d)\n", joystick_keymap.x_axis); + fprintf(config, "\t(joystick-y %d)\n", joystick_keymap.y_axis); + fprintf(config, "\t(joystick-a %d)\n", joystick_keymap.a_button); + fprintf(config, "\t(joystick-b %d)\n", joystick_keymap.b_button); + fprintf(config, "\t(joystick-start %d)\n", joystick_keymap.start_button); + fprintf(config, "\t(joystick-deadzone %d)\n", joystick_keymap.dead_zone); + } + + customsave(config); + + fprintf(config, ")\n"); + } +} + +/* EOF */ diff --git a/lib/utils/configfile.h b/lib/utils/configfile.h new file mode 100644 index 000000000..9ef9d319d --- /dev/null +++ b/lib/utils/configfile.h @@ -0,0 +1,40 @@ +// $Id$ +// +// SuperTux - A Jump'n Run +// Copyright (C) 2004 Michael George +// +// This program is free software; you can redistribute it and/or +// modify it under the terms of the GNU General Public License +// as published by the Free Software Foundation; either version 2 +// of the License, or (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software +// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + +#ifndef SUPERTUX_CONFIGFILE_H +#define SUPERTUX_CONFIGFILE_H + +#include "utils/lispreader.h" + +class Config { + public: + void load (); + void save (); + virtual void customload(LispReader& reader) {}; + virtual void customsave(FILE * config) {}; +}; + +extern Config* config; + + +#endif + +/* Local Variables: */ +/* mode:c++ */ +/* End: */ diff --git a/lib/utils/exceptions.h b/lib/utils/exceptions.h new file mode 100644 index 000000000..974bf0af5 --- /dev/null +++ b/lib/utils/exceptions.h @@ -0,0 +1,44 @@ +// $Id$ +// +// SuperTux - A Jump'n Run +// Copyright (C) 2003 Tobias Glaesser +// +// This program is free software; you can redistribute it and/or +// modify it under the terms of the GNU General Public License +// as published by the Free Software Foundation; either version 2 +// of the License, or (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software +// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + +#ifndef SUPERTUX_EXCEPTIONS_H +#define SUPERTUX_EXCEPTIONS_H + +// Exceptions +#include +#include + +class SuperTuxException : public std::exception +{ + public: + SuperTuxException(const char* _message, const char* _file = "", const unsigned int _line = 0) + : message(_message), file(_file), line(_line) { }; + virtual ~SuperTuxException() throw() { }; + + const char* what() const throw() { return message; }; + const char* what_file() const throw() { return file; }; + const unsigned int what_line() const throw() { return line; }; + + private: + const char* message; + const char* file; + const unsigned int line; +}; + +#endif /*SUPERTUX_EXCEPTIONS_H*/ diff --git a/lib/utils/lispreader.cpp b/lib/utils/lispreader.cpp new file mode 100644 index 000000000..c49d34fd9 --- /dev/null +++ b/lib/utils/lispreader.cpp @@ -0,0 +1,1293 @@ +/* $Id$ */ +/* + * lispreader.c + * + * Copyright (C) 1998-2000 Mark Probst + * Copyright (C) 2002 Ingo Ruhnke + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + */ +#include +#include +#include +#include +#include +#include + +#include "app/globals.h" +#include "app/setup.h" +#include "utils/lispreader.h" + +#define TOKEN_ERROR -1 +#define TOKEN_EOF 0 +#define TOKEN_OPEN_PAREN 1 +#define TOKEN_CLOSE_PAREN 2 +#define TOKEN_SYMBOL 3 +#define TOKEN_STRING 4 +#define TOKEN_INTEGER 5 +#define TOKEN_REAL 6 +#define TOKEN_PATTERN_OPEN_PAREN 7 +#define TOKEN_DOT 8 +#define TOKEN_TRUE 9 +#define TOKEN_FALSE 10 + + +#define MAX_TOKEN_LENGTH 4096 + +static char token_string[MAX_TOKEN_LENGTH + 1] = ""; +static int token_length = 0; + +static lisp_object_t end_marker = { LISP_TYPE_EOF, {{0, 0}} }; +static lisp_object_t error_object = { LISP_TYPE_PARSE_ERROR , {{0,0}} }; +static lisp_object_t close_paren_marker = { LISP_TYPE_PARSE_ERROR , {{0,0}} }; +static lisp_object_t dot_marker = { LISP_TYPE_PARSE_ERROR , {{0,0}} }; + +static void +_token_clear (void) +{ + token_string[0] = '\0'; + token_length = 0; +} + +static void +_token_append (char c) +{ + if (token_length >= MAX_TOKEN_LENGTH) + throw LispReaderException("_token_append()", __FILE__, __LINE__); + + token_string[token_length++] = c; + token_string[token_length] = '\0'; +} + +static int +_next_char (lisp_stream_t *stream) +{ + switch (stream->type) + { + case LISP_STREAM_FILE : + return getc(stream->v.file); + + case LISP_STREAM_STRING : + { + char c = stream->v.string.buf[stream->v.string.pos]; + + if (c == 0) + return EOF; + + ++stream->v.string.pos; + + return c; + } + + case LISP_STREAM_ANY: + return stream->v.any.next_char(stream->v.any.data); + } + + throw LispReaderException("_next_char()", __FILE__, __LINE__); + return EOF; +} + +static void +_unget_char (char c, lisp_stream_t *stream) +{ + switch (stream->type) + { + case LISP_STREAM_FILE : + ungetc(c, stream->v.file); + break; + + case LISP_STREAM_STRING : + --stream->v.string.pos; + break; + + case LISP_STREAM_ANY: + stream->v.any.unget_char(c, stream->v.any.data); + break; + + default : + throw LispReaderException("_unget_char()", __FILE__, __LINE__); + } +} + +static int +_scan (lisp_stream_t *stream) +{ + static char *delims = "\"();"; + + int c; + + _token_clear(); + + do + { + c = _next_char(stream); + if (c == EOF) + return TOKEN_EOF; + else if (c == ';') /* comment start */ + while (1) + { + c = _next_char(stream); + if (c == EOF) + return TOKEN_EOF; + else if (c == '\n') + break; + } + } + while (isspace(c)); + + switch (c) + { + case '(' : + return TOKEN_OPEN_PAREN; + + case ')' : + return TOKEN_CLOSE_PAREN; + + case '"' : + while (1) + { + c = _next_char(stream); + if (c == EOF) + return TOKEN_ERROR; + if (c == '"') + break; + if (c == '\\') + { + c = _next_char(stream); + + switch (c) + { + case EOF : + return TOKEN_ERROR; + + case 'n' : + c = '\n'; + break; + + case 't' : + c = '\t'; + break; + } + } + + _token_append(c); + } + return TOKEN_STRING; + + case '#' : + c = _next_char(stream); + if (c == EOF) + return TOKEN_ERROR; + + switch (c) + { + case 't' : + return TOKEN_TRUE; + + case 'f' : + return TOKEN_FALSE; + + case '?' : + c = _next_char(stream); + if (c == EOF) + return TOKEN_ERROR; + + if (c == '(') + return TOKEN_PATTERN_OPEN_PAREN; + else + return TOKEN_ERROR; + } + return TOKEN_ERROR; + + default : + if (isdigit(c) || c == '-') + { + int have_nondigits = 0; + int have_digits = 0; + int have_floating_point = 0; + + do + { + if (isdigit(c)) + have_digits = 1; + else if (c == '.') + have_floating_point++; + _token_append(c); + + c = _next_char(stream); + + if (c != EOF && !isdigit(c) && !isspace(c) && c != '.' && !strchr(delims, c)) + have_nondigits = 1; + } + while (c != EOF && !isspace(c) && !strchr(delims, c)); + + if (c != EOF) + _unget_char(c, stream); + + if (have_nondigits || !have_digits || have_floating_point > 1) + return TOKEN_SYMBOL; + else if (have_floating_point == 1) + return TOKEN_REAL; + else + return TOKEN_INTEGER; + } + else + { + if (c == '.') + { + c = _next_char(stream); + if (c != EOF && !isspace(c) && !strchr(delims, c)) + _token_append('.'); + else + { + _unget_char(c, stream); + return TOKEN_DOT; + } + } + do + { + _token_append(c); + c = _next_char(stream); + } + while (c != EOF && !isspace(c) && !strchr(delims, c)); + if (c != EOF) + _unget_char(c, stream); + + return TOKEN_SYMBOL; + } + } + + throw LispReaderException("_scan()", __FILE__, __LINE__); + return TOKEN_ERROR; +} + +static lisp_object_t* +lisp_object_alloc (int type) +{ + lisp_object_t *obj = (lisp_object_t*)malloc(sizeof(lisp_object_t)); + + obj->type = type; + + return obj; +} + +lisp_stream_t* +lisp_stream_init_file (lisp_stream_t *stream, FILE *file) +{ + stream->type = LISP_STREAM_FILE; + stream->v.file = file; + + return stream; +} + +lisp_stream_t* +lisp_stream_init_string (lisp_stream_t *stream, char *buf) +{ + stream->type = LISP_STREAM_STRING; + stream->v.string.buf = buf; + stream->v.string.pos = 0; + + return stream; +} + +lisp_stream_t* +lisp_stream_init_any (lisp_stream_t *stream, void *data, + int (*next_char) (void *data), + void (*unget_char) (char c, void *data)) +{ + if (next_char == 0 || unget_char == 0) + throw LispReaderException("lisp_stream_init_any()", __FILE__, __LINE__); + + stream->type = LISP_STREAM_ANY; + stream->v.any.data = data; + stream->v.any.next_char= next_char; + stream->v.any.unget_char = unget_char; + + return stream; +} + +lisp_object_t* +lisp_make_integer (int value) +{ + lisp_object_t *obj = lisp_object_alloc(LISP_TYPE_INTEGER); + + obj->v.integer = value; + + return obj; +} + +lisp_object_t* +lisp_make_real (float value) +{ + lisp_object_t *obj = lisp_object_alloc(LISP_TYPE_REAL); + + obj->v.real = value; + + return obj; +} + +lisp_object_t* +lisp_make_symbol (const char *value) +{ + lisp_object_t *obj = lisp_object_alloc(LISP_TYPE_SYMBOL); + + obj->v.string = strdup(value); + + return obj; +} + +lisp_object_t* +lisp_make_string (const char *value) +{ + lisp_object_t *obj = lisp_object_alloc(LISP_TYPE_STRING); + + obj->v.string = strdup(value); + + return obj; +} + +lisp_object_t* +lisp_make_cons (lisp_object_t *car, lisp_object_t *cdr) +{ + lisp_object_t *obj = lisp_object_alloc(LISP_TYPE_CONS); + + obj->v.cons.car = car; + obj->v.cons.cdr = cdr; + + return obj; +} + +lisp_object_t* +lisp_make_boolean (int value) +{ + lisp_object_t *obj = lisp_object_alloc(LISP_TYPE_BOOLEAN); + + obj->v.integer = value ? 1 : 0; + + return obj; +} + +static lisp_object_t* +lisp_make_pattern_cons (lisp_object_t *car, lisp_object_t *cdr) +{ + lisp_object_t *obj = lisp_object_alloc(LISP_TYPE_PATTERN_CONS); + + obj->v.cons.car = car; + obj->v.cons.cdr = cdr; + + return obj; +} + +static lisp_object_t* +lisp_make_pattern_var (int type, int index, lisp_object_t *sub) +{ + lisp_object_t *obj = lisp_object_alloc(LISP_TYPE_PATTERN_VAR); + + obj->v.pattern.type = type; + obj->v.pattern.index = index; + obj->v.pattern.sub = sub; + + return obj; +} + +lisp_object_t* +lisp_read (lisp_stream_t *in) +{ + int token = _scan(in); + lisp_object_t *obj = lisp_nil(); + + if (token == TOKEN_EOF) + return &end_marker; + + switch (token) + { + case TOKEN_ERROR : + return &error_object; + + case TOKEN_EOF : + return &end_marker; + + case TOKEN_OPEN_PAREN : + case TOKEN_PATTERN_OPEN_PAREN : + { + lisp_object_t *last = lisp_nil(), *car; + + do + { + car = lisp_read(in); + if (car == &error_object || car == &end_marker) + { + lisp_free(obj); + return &error_object; + } + else if (car == &dot_marker) + { + if (lisp_nil_p(last)) + { + lisp_free(obj); + return &error_object; + } + + car = lisp_read(in); + if (car == &error_object || car == &end_marker) + { + lisp_free(obj); + return car; + } + else + { + last->v.cons.cdr = car; + + if (_scan(in) != TOKEN_CLOSE_PAREN) + { + lisp_free(obj); + return &error_object; + } + + car = &close_paren_marker; + } + } + else if (car != &close_paren_marker) + { + if (lisp_nil_p(last)) + obj = last = (token == TOKEN_OPEN_PAREN ? lisp_make_cons(car, lisp_nil()) : lisp_make_pattern_cons(car, lisp_nil())); + else + last = last->v.cons.cdr = lisp_make_cons(car, lisp_nil()); + } + } + while (car != &close_paren_marker); + } + return obj; + + case TOKEN_CLOSE_PAREN : + return &close_paren_marker; + + case TOKEN_SYMBOL : + return lisp_make_symbol(token_string); + + case TOKEN_STRING : + return lisp_make_string(token_string); + + case TOKEN_INTEGER : + return lisp_make_integer(atoi(token_string)); + + case TOKEN_REAL : + return lisp_make_real((float)atof(token_string)); + + case TOKEN_DOT : + return &dot_marker; + + case TOKEN_TRUE : + return lisp_make_boolean(1); + + case TOKEN_FALSE : + return lisp_make_boolean(0); + } + + throw LispReaderException("lisp_read()", __FILE__, __LINE__); + return &error_object; +} + +void +lisp_free (lisp_object_t *obj) +{ + if (obj == 0) + return; + + /** We have to use this iterative code, because the recursive function + * produces a stack overflow and crashs on OSX 10.2 + */ + std::vector objs; + objs.push_back(obj); + + while(!objs.empty()) { + lisp_object_t* obj = objs.back(); + objs.pop_back(); + + switch (obj->type) { + case LISP_TYPE_INTERNAL : + case LISP_TYPE_PARSE_ERROR : + case LISP_TYPE_EOF : + return; + + case LISP_TYPE_SYMBOL : + case LISP_TYPE_STRING : + free(obj->v.string); + break; + + case LISP_TYPE_CONS : + case LISP_TYPE_PATTERN_CONS : + if(obj->v.cons.car) + objs.push_back(obj->v.cons.car); + if(obj->v.cons.cdr) + objs.push_back(obj->v.cons.cdr); + break; + + case LISP_TYPE_PATTERN_VAR : + if(obj->v.pattern.sub) + objs.push_back(obj->v.pattern.sub); + break; + } + + free(obj); + } +} + +lisp_object_t* +lisp_read_from_string (const char *buf) +{ + lisp_stream_t stream; + + lisp_stream_init_string(&stream, (char*)buf); + return lisp_read(&stream); +} + +static int +_compile_pattern (lisp_object_t **obj, int *index) +{ + if (*obj == 0) + return 1; + + switch (lisp_type(*obj)) + { + case LISP_TYPE_PATTERN_CONS : + { + struct + { + char *name; + int type; + } + types[] = + { + { "any", LISP_PATTERN_ANY }, + { "symbol", LISP_PATTERN_SYMBOL }, + { "string", LISP_PATTERN_STRING }, + { "integer", LISP_PATTERN_INTEGER }, + { "real", LISP_PATTERN_REAL }, + { "boolean", LISP_PATTERN_BOOLEAN }, + { "list", LISP_PATTERN_LIST }, + { "or", LISP_PATTERN_OR }, + { 0, 0 } + }; + char *type_name; + int type; + int i; + lisp_object_t *pattern; + type = -1; + + if (lisp_type(lisp_car(*obj)) != LISP_TYPE_SYMBOL) + return 0; + + type_name = lisp_symbol(lisp_car(*obj)); + for (i = 0; types[i].name != 0; ++i) + { + if (strcmp(types[i].name, type_name) == 0) + { + type = types[i].type; + break; + } + } + + if (types[i].name == 0) + return 0; + + if (type != LISP_PATTERN_OR && lisp_cdr(*obj) != 0) + return 0; + + pattern = lisp_make_pattern_var(type, (*index)++, lisp_nil()); + + if (type == LISP_PATTERN_OR) + { + lisp_object_t *cdr = lisp_cdr(*obj); + + if (!_compile_pattern(&cdr, index)) + { + lisp_free(pattern); + return 0; + } + + pattern->v.pattern.sub = cdr; + + (*obj)->v.cons.cdr = lisp_nil(); + } + + lisp_free(*obj); + + *obj = pattern; + } + break; + + case LISP_TYPE_CONS : + if (!_compile_pattern(&(*obj)->v.cons.car, index)) + return 0; + if (!_compile_pattern(&(*obj)->v.cons.cdr, index)) + return 0; + break; + } + + return 1; +} + +int +lisp_compile_pattern (lisp_object_t **obj, int *num_subs) +{ + int index = 0; + int result; + + result = _compile_pattern(obj, &index); + + if (result && num_subs != 0) + *num_subs = index; + + return result; +} + +static int _match_pattern (lisp_object_t *pattern, lisp_object_t *obj, lisp_object_t **vars); + +static int +_match_pattern_var (lisp_object_t *pattern, lisp_object_t *obj, lisp_object_t **vars) +{ + if (lisp_type(pattern) != LISP_TYPE_PATTERN_VAR) + throw LispReaderException("_match_pattern_var", __FILE__, __LINE__); + + switch (pattern->v.pattern.type) + { + case LISP_PATTERN_ANY : + break; + + case LISP_PATTERN_SYMBOL : + if (obj == 0 || lisp_type(obj) != LISP_TYPE_SYMBOL) + return 0; + break; + + case LISP_PATTERN_STRING : + if (obj == 0 || lisp_type(obj) != LISP_TYPE_STRING) + return 0; + break; + + case LISP_PATTERN_INTEGER : + if (obj == 0 || lisp_type(obj) != LISP_TYPE_INTEGER) + return 0; + break; + + case LISP_PATTERN_REAL : + if (obj == 0 || lisp_type(obj) != LISP_TYPE_REAL) + return 0; + break; + + case LISP_PATTERN_BOOLEAN : + if (obj == 0 || lisp_type(obj) != LISP_TYPE_BOOLEAN) + return 0; + break; + + case LISP_PATTERN_LIST : + if (obj == 0 || lisp_type(obj) != LISP_TYPE_CONS) + return 0; + break; + + case LISP_PATTERN_OR : + { + lisp_object_t *sub; + int matched = 0; + + for (sub = pattern->v.pattern.sub; sub != 0; sub = lisp_cdr(sub)) + { + if (lisp_type(sub) != LISP_TYPE_CONS) + throw LispReaderException("_match_pattern_var()", __FILE__, __LINE__); + + if (_match_pattern(lisp_car(sub), obj, vars)) + matched = 1; + } + + if (!matched) + return 0; + } + break; + + default : + throw LispReaderException("_match_pattern_var()", __FILE__, __LINE__); + } + + if (vars != 0) + vars[pattern->v.pattern.index] = obj; + + return 1; +} + +static int +_match_pattern (lisp_object_t *pattern, lisp_object_t *obj, lisp_object_t **vars) +{ + if (pattern == 0) + return obj == 0; + + if (obj == 0) + return 0; + + if (lisp_type(pattern) == LISP_TYPE_PATTERN_VAR) + return _match_pattern_var(pattern, obj, vars); + + if (lisp_type(pattern) != lisp_type(obj)) + return 0; + + switch (lisp_type(pattern)) + { + case LISP_TYPE_SYMBOL : + return strcmp(lisp_symbol(pattern), lisp_symbol(obj)) == 0; + + case LISP_TYPE_STRING : + return strcmp(lisp_string(pattern), lisp_string(obj)) == 0; + + case LISP_TYPE_INTEGER : + return lisp_integer(pattern) == lisp_integer(obj); + + case LISP_TYPE_REAL : + return lisp_real(pattern) == lisp_real(obj); + + case LISP_TYPE_CONS : + { + int result1, result2; + + result1 = _match_pattern(lisp_car(pattern), lisp_car(obj), vars); + result2 = _match_pattern(lisp_cdr(pattern), lisp_cdr(obj), vars); + + return result1 && result2; + } + break; + + default : + throw LispReaderException("_match_pattern()", __FILE__, __LINE__); + } + + return 0; +} + +int +lisp_match_pattern (lisp_object_t *pattern, lisp_object_t *obj, lisp_object_t **vars, int num_subs) +{ + int i; + + if (vars != 0) + for (i = 0; i < num_subs; ++i) + vars[i] = &error_object; + + return _match_pattern(pattern, obj, vars); +} + +int +lisp_match_string (const char *pattern_string, lisp_object_t *obj, lisp_object_t **vars) +{ + lisp_object_t *pattern; + int result; + int num_subs; + + pattern = lisp_read_from_string(pattern_string); + + if (pattern != 0 && (lisp_type(pattern) == LISP_TYPE_EOF + || lisp_type(pattern) == LISP_TYPE_PARSE_ERROR)) + return 0; + + if (!lisp_compile_pattern(&pattern, &num_subs)) + { + lisp_free(pattern); + return 0; + } + + result = lisp_match_pattern(pattern, obj, vars, num_subs); + + lisp_free(pattern); + + return result; +} + +int +lisp_type (lisp_object_t *obj) +{ + if (obj == 0) + return LISP_TYPE_NIL; + return obj->type; +} + +int +lisp_integer (lisp_object_t *obj) +{ + if (obj->type != LISP_TYPE_INTEGER) + throw LispReaderException("lisp_integer()", __FILE__, __LINE__); + + return obj->v.integer; +} + +char* +lisp_symbol (lisp_object_t *obj) +{ + if (obj->type != LISP_TYPE_SYMBOL) + throw LispReaderException("lisp_symbol()", __FILE__, __LINE__); + + return obj->v.string; +} + +char* +lisp_string (lisp_object_t *obj) +{ + if (obj->type != LISP_TYPE_STRING) + throw LispReaderException("lisp_string()", __FILE__, __LINE__); + + return obj->v.string; +} + +int +lisp_boolean (lisp_object_t *obj) +{ + if (obj->type != LISP_TYPE_BOOLEAN) + throw LispReaderException("lisp_boolean()", __FILE__, __LINE__); + + return obj->v.integer; +} + +float +lisp_real (lisp_object_t *obj) +{ + if (obj->type != LISP_TYPE_REAL && obj->type != LISP_TYPE_INTEGER) + throw LispReaderException("lisp_real()", __FILE__, __LINE__); + + if (obj->type == LISP_TYPE_INTEGER) + return obj->v.integer; + return obj->v.real; +} + +lisp_object_t* +lisp_car (lisp_object_t *obj) +{ + if (obj->type != LISP_TYPE_CONS && obj->type != LISP_TYPE_PATTERN_CONS) + throw LispReaderException("lisp_car()", __FILE__, __LINE__); + + return obj->v.cons.car; +} + +lisp_object_t* +lisp_cdr (lisp_object_t *obj) +{ + if (obj->type != LISP_TYPE_CONS && obj->type != LISP_TYPE_PATTERN_CONS) + throw LispReaderException("lisp_cdr()", __FILE__, __LINE__); + + return obj->v.cons.cdr; +} + +lisp_object_t* +lisp_cxr (lisp_object_t *obj, const char *x) +{ + int i; + + for (i = strlen(x) - 1; i >= 0; --i) + if (x[i] == 'a') + obj = lisp_car(obj); + else if (x[i] == 'd') + obj = lisp_cdr(obj); + else + throw LispReaderException("lisp_cxr()", __FILE__, __LINE__); + + return obj; +} + +int +lisp_list_length (lisp_object_t *obj) +{ + int length = 0; + + while (obj != 0) + { + if (obj->type != LISP_TYPE_CONS && obj->type != LISP_TYPE_PATTERN_CONS) + throw LispReaderException("lisp_list_length()", __FILE__, __LINE__); + + ++length; + obj = obj->v.cons.cdr; + } + + return length; +} + +lisp_object_t* +lisp_list_nth_cdr (lisp_object_t *obj, int index) +{ + while (index > 0) + { + if (obj == 0) + throw LispReaderException("lisp_list_nth_cdr()", __FILE__, __LINE__); + if (obj->type != LISP_TYPE_CONS && obj->type != LISP_TYPE_PATTERN_CONS) + throw LispReaderException("lisp_list_nth_cdr()", __FILE__, __LINE__); + + --index; + obj = obj->v.cons.cdr; + } + + return obj; +} + +lisp_object_t* +lisp_list_nth (lisp_object_t *obj, int index) +{ + obj = lisp_list_nth_cdr(obj, index); + + if (obj == 0) + throw LispReaderException("lisp_list_nth()", __FILE__, __LINE__); + + return obj->v.cons.car; +} + +void +lisp_dump (lisp_object_t *obj, FILE *out) +{ + if (obj == 0) + { + fprintf(out, "()"); + return; + } + + switch (lisp_type(obj)) + { + case LISP_TYPE_EOF : + fputs("#", out); + break; + + case LISP_TYPE_PARSE_ERROR : + fputs("#", out); + break; + + case LISP_TYPE_INTEGER : + fprintf(out, "%d", lisp_integer(obj)); + break; + + case LISP_TYPE_REAL : + fprintf(out, "%f", lisp_real(obj)); + break; + + case LISP_TYPE_SYMBOL : + fputs(lisp_symbol(obj), out); + break; + + case LISP_TYPE_STRING : + { + char *p; + + fputc('"', out); + for (p = lisp_string(obj); *p != 0; ++p) + { + if (*p == '"' || *p == '\\') + fputc('\\', out); + fputc(*p, out); + } + fputc('"', out); + } + break; + + case LISP_TYPE_CONS : + case LISP_TYPE_PATTERN_CONS : + fputs(lisp_type(obj) == LISP_TYPE_CONS ? "(" : "#?(", out); + while (obj != 0) + { + lisp_dump(lisp_car(obj), out); + obj = lisp_cdr(obj); + if (obj != 0) + { + if (lisp_type(obj) != LISP_TYPE_CONS + && lisp_type(obj) != LISP_TYPE_PATTERN_CONS) + { + fputs(" . ", out); + lisp_dump(obj, out); + break; + } + else + fputc(' ', out); + } + } + fputc(')', out); + break; + + case LISP_TYPE_BOOLEAN : + if (lisp_boolean(obj)) + fputs("#t", out); + else + fputs("#f", out); + break; + + default : + throw LispReaderException("lisp_dump()", __FILE__, __LINE__); + } +} + +using namespace std; + +LispReader::LispReader (lisp_object_t* l) + : owner(0), lst (l) +{ +} + +LispReader::~LispReader() +{ + if(owner) + lisp_free(owner); +} + +LispReader* +LispReader::load(const std::string& filename, const std::string& toplevellist) +{ + lisp_object_t* obj = lisp_read_from_file(filename); + + if(obj->type == LISP_TYPE_EOF || obj->type == LISP_TYPE_PARSE_ERROR) { + lisp_free(obj); + throw LispReaderException("LispReader::load", __FILE__, __LINE__); + } + + if(toplevellist != lisp_symbol(lisp_car(obj))) { + lisp_car(obj); + throw LispReaderException("LispReader::load wrong toplevel symbol", + __FILE__, __LINE__); + } + + LispReader* reader = new LispReader(lisp_cdr(obj)); + reader->owner = obj; + + return reader; +} + +lisp_object_t* +LispReader::search_for(const char* name) +{ + //std::cout << "LispReader::search_for(" << name << ")" << std::endl; + lisp_object_t* cursor = lst; + + while(!lisp_nil_p(cursor)) + { + lisp_object_t* cur = lisp_car(cursor); + + if (!lisp_cons_p(cur) || !lisp_symbol_p (lisp_car(cur))) + { + lisp_dump(cur, stdout); + //throw ConstruoError (std::string("LispReader: Read error in search_for ") + name); + printf("LispReader: Read error in search\n"); + } + else + { + if (strcmp(lisp_symbol(lisp_car(cur)), name) == 0) + { + return lisp_cdr(cur); + } + } + + cursor = lisp_cdr (cursor); + } + return 0; +} + +bool +LispReader::read_int (const char* name, int& i) +{ + lisp_object_t* obj = search_for (name); + if(!obj) + return false; + + if (!lisp_integer_p(lisp_car(obj))) + return false; + + i = lisp_integer(lisp_car(obj)); + return true; +} + +bool +LispReader::read_lisp(const char* name, lisp_object_t*& b) +{ + lisp_object_t* obj = search_for (name); + if (!obj) + return false; + + b = obj; + return true; +} + +lisp_object_t* +LispReader::read_lisp(const char* name) +{ + return search_for(name); +} + +bool +LispReader::read_float (const char* name, float& f) +{ + lisp_object_t* obj = search_for (name); + if (!obj) + return false; + + if (!lisp_real_p(lisp_car(obj)) && !lisp_integer_p(lisp_car(obj))) + st_abort("LispReader expected type real at token: ", name); + + f = lisp_real(lisp_car(obj)); + return true; +} + +bool +LispReader::read_string_vector (const char* name, std::vector& vec) +{ + lisp_object_t* obj = search_for (name); + if (!obj) + return false; + + vec.clear(); + while(!lisp_nil_p(obj)) + { + if (!lisp_string_p(lisp_car(obj))) + st_abort("LispReader expected type string at token: ", name); + vec.push_back(lisp_string(lisp_car(obj))); + obj = lisp_cdr(obj); + } + return true; +} + +bool +LispReader::read_int_vector (const char* name, std::vector& vec) +{ + lisp_object_t* obj = search_for (name); + if (!obj) + return false; + + vec.clear(); + while(!lisp_nil_p(obj)) + { + if (!lisp_integer_p(lisp_car(obj))) + st_abort("LispReader expected type integer at token: ", name); + vec.push_back(lisp_integer(lisp_car(obj))); + obj = lisp_cdr(obj); + } + return true; +} + +bool +LispReader::read_int_vector (const char* name, std::vector& vec) +{ + lisp_object_t* obj = search_for (name); + if (!obj) + return false; + + vec.clear(); + while(!lisp_nil_p(obj)) + { + if (!lisp_integer_p(lisp_car(obj))) + st_abort("LispReader expected type integer at token: ", name); + vec.push_back(lisp_integer(lisp_car(obj))); + obj = lisp_cdr(obj); + } + return true; +} + +bool +LispReader::read_char_vector (const char* name, std::vector& vec) +{ + lisp_object_t* obj = search_for (name); + if (!obj) + return false; + + vec.clear(); + while(!lisp_nil_p(obj)) + { + vec.push_back(*lisp_string(lisp_car(obj))); + obj = lisp_cdr(obj); + } + return true; +} + +bool +LispReader::read_string (const char* name, std::string& str, bool translatable) +{ + lisp_object_t* obj; + if(translatable) + { + /* Internationalization support: check for the suffix: str + "-" + $LANG variable. + If not found, use the regular string. + So, translating a string in a Lisp file would result in something like: + (text "Hello World!") + (text-fr "Bonjour Monde!") + being fr the value of LANG (echo $LANG) for the language we want to translate to */ + + char* lang = getenv("LANG"); + + char str_[1024]; // check, for instance, for (title-fr_FR "Bonjour") + sprintf(str_, "%s-%s", name, lang); + + obj = search_for (str_); + + if(!obj) // check, for instance, for (title-fr "Bonjour") + { + if(lang != NULL && strlen(lang) >= 2) + { + char lang_[3]; + strncpy(lang_, lang, 2); + lang_[2] = '\0'; + sprintf(str_, "%s-%s", name, lang_); + + obj = search_for (str_); + } + else + obj = 0; + } + + if(!obj) // check, for instance, for (title "Hello") + obj = search_for (name); + } + else + obj = search_for (name); + + if (!obj) + return false; + + if (!lisp_string_p(lisp_car(obj))) + st_abort("LispReader expected type string at token: ", name); + str = lisp_string(lisp_car(obj)); + return true; +} + +bool +LispReader::read_bool (const char* name, bool& b) +{ + lisp_object_t* obj = search_for (name); + if (!obj) + return false; + + if (!lisp_boolean_p(lisp_car(obj))) + st_abort("LispReader expected type bool at token: ", name); + b = lisp_boolean(lisp_car(obj)); + return true; +} + +lisp_object_t* +LispReader::get_lisp() +{ + return lst; +} + +lisp_object_t* lisp_read_from_file(const std::string& filename) +{ + FILE* in = fopen(filename.c_str(), "r"); + + if(!in) + return 0; + + lisp_stream_t stream; + lisp_stream_init_file(&stream, in); + lisp_object_t* obj = lisp_read(&stream); + fclose(in); + + return obj; +} + +// EOF // diff --git a/lib/utils/lispreader.h b/lib/utils/lispreader.h new file mode 100644 index 000000000..f0f480e52 --- /dev/null +++ b/lib/utils/lispreader.h @@ -0,0 +1,204 @@ +/* $Id$ */ +/* + * lispreader.h + * + * Copyright (C) 1998-2000 Mark Probst + * Copyright (C) 2002 Ingo Ruhnke + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + */ + +#ifndef SUPERTUX_LISPREADER_H +#define SUPERTUX_LISPREADER_H + +#include +#include +#include +#include + +#include + +#include "utils/exceptions.h" + +#define LISP_STREAM_FILE 1 +#define LISP_STREAM_STRING 2 +#define LISP_STREAM_ANY 3 + +#define LISP_TYPE_INTERNAL -3 +#define LISP_TYPE_PARSE_ERROR -2 +#define LISP_TYPE_EOF -1 +#define LISP_TYPE_NIL 0 +#define LISP_TYPE_SYMBOL 1 +#define LISP_TYPE_INTEGER 2 +#define LISP_TYPE_STRING 3 +#define LISP_TYPE_REAL 4 +#define LISP_TYPE_CONS 5 +#define LISP_TYPE_PATTERN_CONS 6 +#define LISP_TYPE_BOOLEAN 7 +#define LISP_TYPE_PATTERN_VAR 8 + +#define LISP_PATTERN_ANY 1 +#define LISP_PATTERN_SYMBOL 2 +#define LISP_PATTERN_STRING 3 +#define LISP_PATTERN_INTEGER 4 +#define LISP_PATTERN_REAL 5 +#define LISP_PATTERN_BOOLEAN 6 +#define LISP_PATTERN_LIST 7 +#define LISP_PATTERN_OR 8 + +// Exception +class LispReaderException : public SuperTuxException +{ + public: + LispReaderException(const char* _message = "lispreader error", const char* _file = "", const unsigned int _line = 0) + : SuperTuxException(_message, _file, _line) { }; +}; + +typedef struct + { + int type; + + union + { + FILE *file; + struct + { + char *buf; + int pos; + } + string; + struct + { + void *data; + int (*next_char) (void *data); + void (*unget_char) (char c, void *data); + } + any; + } v; + } +lisp_stream_t; + +typedef struct _lisp_object_t lisp_object_t; +struct _lisp_object_t + { + int type; + + union + { + struct + { + struct _lisp_object_t *car; + struct _lisp_object_t *cdr; + } + cons; + + char *string; + int integer; + float real; + + struct + { + int type; + int index; + struct _lisp_object_t *sub; + } + pattern; + } v; + }; + +lisp_stream_t* lisp_stream_init_file (lisp_stream_t *stream, FILE *file); +lisp_stream_t* lisp_stream_init_string (lisp_stream_t *stream, char *buf); +lisp_stream_t* lisp_stream_init_any (lisp_stream_t *stream, void *data, + int (*next_char) (void *data), + void (*unget_char) (char c, void *data)); + +lisp_object_t* lisp_read (lisp_stream_t *in); +lisp_object_t* lisp_read_from_file(const std::string& filename); +void lisp_free (lisp_object_t *obj); + +lisp_object_t* lisp_read_from_string (const char *buf); + +int lisp_compile_pattern (lisp_object_t **obj, int *num_subs); +int lisp_match_pattern (lisp_object_t *pattern, lisp_object_t *obj, lisp_object_t **vars, int num_subs); +int lisp_match_string (const char *pattern_string, lisp_object_t *obj, lisp_object_t **vars); + +int lisp_type (lisp_object_t *obj); +int lisp_integer (lisp_object_t *obj); +float lisp_real (lisp_object_t *obj); +char* lisp_symbol (lisp_object_t *obj); +char* lisp_string (lisp_object_t *obj); +int lisp_boolean (lisp_object_t *obj); +lisp_object_t* lisp_car (lisp_object_t *obj); +lisp_object_t* lisp_cdr (lisp_object_t *obj); + +lisp_object_t* lisp_cxr (lisp_object_t *obj, const char *x); + +lisp_object_t* lisp_make_integer (int value); +lisp_object_t* lisp_make_real (float value); +lisp_object_t* lisp_make_symbol (const char *value); +lisp_object_t* lisp_make_string (const char *value); +lisp_object_t* lisp_make_cons (lisp_object_t *car, lisp_object_t *cdr); +lisp_object_t* lisp_make_boolean (int value); + +int lisp_list_length (lisp_object_t *obj); +lisp_object_t* lisp_list_nth_cdr (lisp_object_t *obj, int index); +lisp_object_t* lisp_list_nth (lisp_object_t *obj, int index); + +void lisp_dump (lisp_object_t *obj, FILE *out); + +#define lisp_nil() ((lisp_object_t*)0) + +#define lisp_nil_p(obj) (obj == 0) +#define lisp_integer_p(obj) (lisp_type((obj)) == LISP_TYPE_INTEGER) +#define lisp_real_p(obj) (lisp_type((obj)) == LISP_TYPE_REAL) +#define lisp_symbol_p(obj) (lisp_type((obj)) == LISP_TYPE_SYMBOL) +#define lisp_string_p(obj) (lisp_type((obj)) == LISP_TYPE_STRING) +#define lisp_cons_p(obj) (lisp_type((obj)) == LISP_TYPE_CONS) +#define lisp_boolean_p(obj) (lisp_type((obj)) == LISP_TYPE_BOOLEAN) + +/** */ +class LispReader +{ +private: + lisp_object_t* owner; + lisp_object_t* lst; + + lisp_object_t* search_for(const char* name); + +public: + /** cur == ((pos 1 2 3) (id 12 3 4)...) */ + LispReader(lisp_object_t* l); + ~LispReader(); + + bool read_int_vector(const char* name, std::vector& vec); + bool read_int_vector(const char* name, std::vector& vec); + bool read_char_vector(const char* name, std::vector& vec); + bool read_string_vector(const char* name, std::vector& vec); + bool read_string(const char* name, std::string& str, bool translatable = false); + bool read_int(const char* name, int& i); + bool read_float(const char* name, float& f); + bool read_bool(const char* name, bool& b); + bool read_lisp(const char* name, lisp_object_t*& b); + lisp_object_t* read_lisp(const char* name); + + static LispReader* load(const std::string& filename, + const std::string& toplevellist); + + lisp_object_t* get_lisp(); +}; + +#endif /*SUPERTUX_LISPREADER_H*/ + diff --git a/lib/utils/lispwriter.cpp b/lib/utils/lispwriter.cpp new file mode 100644 index 000000000..6a08cab81 --- /dev/null +++ b/lib/utils/lispwriter.cpp @@ -0,0 +1,128 @@ +// $Id$ +// +// SuperTux - A Jump'n Run +// Copyright (C) 2004 Matthias Braun + +#include "utils/lispwriter.h" + +LispWriter::LispWriter(std::ostream& newout) + : out(newout), indent_depth(0) +{ +} + +LispWriter::~LispWriter() +{ + if(lists.size() > 0) { + std::cerr << "Warning: Not all sections closed in lispwriter!\n"; + } +} + +void +LispWriter::write_comment(const std::string& comment) +{ + out << "; " << comment << "\n"; +} + +void +LispWriter::start_list(const std::string& listname) +{ + indent(); + out << '(' << listname << '\n'; + indent_depth += 2; + + lists.push_back(listname); +} + +void +LispWriter::end_list(const std::string& listname) +{ + if(lists.size() == 0) { + std::cerr << "Trying to close list '" << listname + << "', which is not open.\n"; + return; + } + if(lists.back() != listname) { + std::cerr << "Warning: trying to close list '" << listname + << "' while list '" << lists.back() << "' is open.\n"; + return; + } + lists.pop_back(); + + indent_depth -= 2; + indent(); + out << ")\n"; +} + +void +LispWriter::write_int(const std::string& name, int value) +{ + indent(); + out << '(' << name << ' ' << value << ")\n"; +} + +void +LispWriter::write_float(const std::string& name, float value) +{ + indent(); + out << '(' << name << ' ' << value << ")\n"; +} + +void +LispWriter::write_string(const std::string& name, const std::string& value) +{ + indent(); + out << '(' << name << " \"" << value << "\")\n"; +} + +void +LispWriter::write_bool(const std::string& name, bool value) +{ + indent(); + out << '(' << name << ' ' << (value ? "#t" : "#f") << ")\n"; +} + +void +LispWriter::write_int_vector(const std::string& name, + const std::vector& value) +{ + indent(); + out << '(' << name; + for(std::vector::const_iterator i = value.begin(); i != value.end(); ++i) + out << " " << *i; + out << ")\n"; +} + +void +LispWriter::write_int_vector(const std::string& name, + const std::vector& value) +{ + indent(); + out << '(' << name; + for(std::vector::const_iterator i = value.begin(); i != value.end(); ++i) + out << " " << *i; + out << ")\n"; +} + +void +LispWriter::indent() +{ + for(int i = 0; i +#include +#include + +class LispWriter +{ +public: + LispWriter(std::ostream& out); + ~LispWriter(); + + void write_comment(const std::string& comment); + + void start_list(const std::string& listname); + + void write_int(const std::string& name, int value); + void write_float(const std::string& name, float value); + void write_string(const std::string& name, const std::string& value); + void write_bool(const std::string& name, bool value); + void write_int_vector(const std::string& name, const std::vector& value); + void write_int_vector(const std::string& name, const std::vector& value); + // add more write-functions when needed... + + void end_list(const std::string& listname); + +private: + void indent(); + + std::ostream& out; + int indent_depth; + std::vector lists; +}; + +#endif + diff --git a/lib/video/drawing_context.cpp b/lib/video/drawing_context.cpp new file mode 100644 index 000000000..846e38c55 --- /dev/null +++ b/lib/video/drawing_context.cpp @@ -0,0 +1,353 @@ +// $Id$ +// +// SuperTux - A Jump'n Run +// Copyright (C) 2004 Matthias Braun +#include +#include + +#include "video/drawing_context.h" +#include "video/surface.h" +#include "app/globals.h" +#include "video/font.h" + +DrawingContext::DrawingContext() +{ +transform.draw_effect = NONE_EFFECT; +} + +DrawingContext::~DrawingContext() +{ +} + +void +DrawingContext::draw_surface(const Surface* surface, const Vector& position, + int layer, Uint32 drawing_effect) +{ + assert(surface != 0); + + DrawingRequest request; + + request.type = SURFACE; + request.layer = layer; + request.request_data = const_cast (surface); + request.pos = transform.apply(position); + request.drawing_effect = drawing_effect; + request.drawing_effect = transform.draw_effect | drawing_effect; + + drawingrequests.push_back(request); +} + +void +DrawingContext::draw_surface_part(const Surface* surface, const Vector& source, + const Vector& size, const Vector& dest, int layer, Uint32 drawing_effect) +{ + assert(surface != 0); + + DrawingRequest request; + + request.type = SURFACE_PART; + request.layer = layer; + request.pos = transform.apply(dest); + request.drawing_effect = drawing_effect; + + SurfacePartRequest* surfacepartrequest = new SurfacePartRequest(); + surfacepartrequest->size = size; + surfacepartrequest->source = source; + surfacepartrequest->surface = surface; + request.request_data = surfacepartrequest; + + drawingrequests.push_back(request); +} + +void +DrawingContext::draw_text(Font* font, const std::string& text, + const Vector& position, int layer, Uint32 drawing_effect) +{ + DrawingRequest request; + + request.type = TEXT; + request.layer = layer; + request.pos = transform.apply(position); + request.drawing_effect = drawing_effect; + + TextRequest* textrequest = new TextRequest; + textrequest->font = font; + textrequest->text = text; + request.request_data = textrequest; + + drawingrequests.push_back(request); +} + +void +DrawingContext::draw_text_center(Font* font, const std::string& text, + const Vector& position, int layer, Uint32 drawing_effect) +{ + DrawingRequest request; + + request.type = TEXT; + request.layer = layer; + request.pos = transform.apply(position) + Vector(screen->w/2 - + font->get_text_width(text)/2, 0); + request.drawing_effect = drawing_effect; + + TextRequest* textrequest = new TextRequest; + textrequest->font = font; + textrequest->text = text; + request.request_data = textrequest; + + drawingrequests.push_back(request); +} + +void +DrawingContext::draw_gradient(Color top, Color bottom, int layer) +{ + DrawingRequest request; + + request.type = GRADIENT; + request.layer = layer; + request.pos = Vector(0,0); + + GradientRequest* gradientrequest = new GradientRequest; + gradientrequest->top = top; + gradientrequest->bottom = bottom; + request.request_data = gradientrequest; + + drawingrequests.push_back(request); +} + +void +DrawingContext::draw_filled_rect(const Vector& topleft, const Vector& size, + Color color, int layer) +{ + DrawingRequest request; + + request.type = FILLRECT; + request.layer = layer; + request.pos = transform.apply(topleft); + + FillRectRequest* fillrectrequest = new FillRectRequest; + fillrectrequest->size = size; + fillrectrequest->color = color; + request.request_data = fillrectrequest; + + drawingrequests.push_back(request); +} + +void +DrawingContext::draw_surface_part(DrawingRequest& request) +{ + SurfacePartRequest* surfacepartrequest + = (SurfacePartRequest*) request.request_data; + + surfacepartrequest->surface->impl->draw_part( + surfacepartrequest->source.x, surfacepartrequest->source.y, + request.pos.x, request.pos.y, + surfacepartrequest->size.x, surfacepartrequest->size.y, 255, + request.drawing_effect); + + delete surfacepartrequest; +} + +void +DrawingContext::draw_gradient(DrawingRequest& request) +{ + GradientRequest* gradientrequest = (GradientRequest*) request.request_data; + const Color& top = gradientrequest->top; + const Color& bottom = gradientrequest->bottom; + +#ifndef NOOPENGL + if(use_gl) + { + glBegin(GL_QUADS); + glColor3ub(top.red, top.green, top.blue); + glVertex2f(0, 0); + glVertex2f(screen->w, 0); + glColor3ub(bottom.red, bottom.green, bottom.blue); + glVertex2f(screen->w, screen->h); + glVertex2f(0, screen->h); + glEnd(); + } + else + { +#endif + if(&top == &bottom) + { + fillrect(0, 0, screen->w, screen->h, top.red, top.green, top.blue); + } + else + { + float redstep = (float(bottom.red)-float(top.red)) / float(screen->h); + float greenstep = (float(bottom.green)-float(top.green)) / float(screen->h); + float bluestep = (float(bottom.blue) - float(top.blue)) / float(screen->h); + + for(float y = 0; y < screen->h; y += 2) + fillrect(0, (int)y, screen->w, 2, + int(float(top.red) + redstep * y), + int(float(top.green) + greenstep * y), + int(float(top.blue) + bluestep * y), 255); + } +#ifndef NOOPENGL + + } +#endif + + delete gradientrequest; +} + +void +DrawingContext::draw_text(DrawingRequest& request) +{ + TextRequest* textrequest = (TextRequest*) request.request_data; + + textrequest->font->draw(textrequest->text, request.pos, request.drawing_effect); + + delete textrequest; +} + +void +DrawingContext::draw_filled_rect(DrawingRequest& request) +{ + FillRectRequest* fillrectrequest = (FillRectRequest*) request.request_data; + + float x = request.pos.x; + float y = request.pos.y; + float w = fillrectrequest->size.x; + float h = fillrectrequest->size.y; + +#ifndef NOOPENGL + if(use_gl) + { + glEnable(GL_BLEND); + glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); + glColor4ub(fillrectrequest->color.red, fillrectrequest->color.green, + fillrectrequest->color.blue, fillrectrequest->color.alpha); + + glBegin(GL_POLYGON); + glVertex2f(x, y); + glVertex2f(x+w, y); + glVertex2f(x+w, y+h); + glVertex2f(x, y+h); + glEnd(); + glDisable(GL_BLEND); + } + else + { +#endif + SDL_Rect src, rect; + SDL_Surface *temp = NULL; + + rect.x = (int)x; + rect.y = (int)y; + rect.w = (int)w; + rect.h = (int)h; + + if(fillrectrequest->color.alpha != 255) + { + temp = SDL_CreateRGBSurface(screen->flags, rect.w, rect.h, screen->format->BitsPerPixel, + screen->format->Rmask, + screen->format->Gmask, + screen->format->Bmask, + screen->format->Amask); + + + src.x = 0; + src.y = 0; + src.w = rect.w; + src.h = rect.h; + + SDL_FillRect(temp, &src, SDL_MapRGB(screen->format, + fillrectrequest->color.red, fillrectrequest->color.green, + fillrectrequest->color.blue)); + + SDL_SetAlpha(temp, SDL_SRCALPHA, fillrectrequest->color.alpha); + + SDL_BlitSurface(temp,0,screen,&rect); + + SDL_FreeSurface(temp); + } + else + SDL_FillRect(screen, &rect, SDL_MapRGB(screen->format, + fillrectrequest->color.red, fillrectrequest->color.green, + fillrectrequest->color.blue)); + +#ifndef NOOPENGL + + } +#endif + + delete fillrectrequest; +} + +void +DrawingContext::do_drawing() +{ + std::stable_sort(drawingrequests.begin(), drawingrequests.end()); + + for(DrawingRequests::iterator i = drawingrequests.begin(); + i != drawingrequests.end(); ++i) { + switch(i->type) { + case SURFACE: + { + const Surface* surface = (const Surface*) i->request_data; + surface->impl->draw(i->pos.x, i->pos.y, 255, i->drawing_effect); + break; + } + case SURFACE_PART: + draw_surface_part(*i); + break; + case GRADIENT: + draw_gradient(*i); + break; + case TEXT: + draw_text(*i); + break; + case FILLRECT: + draw_filled_rect(*i); + break; + } + } + + // update screen + if(use_gl) + SDL_GL_SwapBuffers(); + else + SDL_Flip(screen); + + drawingrequests.clear(); +} + +void +DrawingContext::push_transform() +{ + transformstack.push_back(transform); +} + +void +DrawingContext::pop_transform() +{ + assert(!transformstack.empty()); + + transform = transformstack.back(); + transformstack.pop_back(); +} + +void +DrawingContext::set_drawing_effect(int effect) +{ + transform.draw_effect = effect; +} diff --git a/lib/video/drawing_context.h b/lib/video/drawing_context.h new file mode 100644 index 000000000..b08bd6c97 --- /dev/null +++ b/lib/video/drawing_context.h @@ -0,0 +1,163 @@ +// $Id$ +// +// SuperTux - A Jump'n Run +// Copyright (C) 2004 Matthias Braun +#include + +#include "SDL.h" + +#include "math/vector.h" +#include "video/screen.h" + +class Surface; +class Font; + +// some constants for predefined layer values +enum { + LAYER_BACKGROUND0 = -300, + LAYER_BACKGROUND1 = -200, + LAYER_BACKGROUNDTILES = -100, + LAYER_TILES = 0, + LAYER_OBJECTS = 100, + LAYER_FOREGROUNDTILES = 200, + LAYER_FOREGROUND0 = 300, + LAYER_FOREGROUND1 = 400, + LAYER_GUI = 500 +}; + +/** + * This class provides functions for drawing things on screen. It also + * maintains a stack of transforms that are applied to graphics. + */ +class DrawingContext +{ +public: + DrawingContext(); + ~DrawingContext(); + + /** Adds a drawing request for a surface into the request list */ + void draw_surface(const Surface* surface, const Vector& position, int layer, + Uint32 drawing_effect = NONE_EFFECT); + /** Adds a drawing request for part of a surface */ + void draw_surface_part(const Surface* surface, const Vector& source, + const Vector& size, const Vector& dest, int layer, + Uint32 drawing_effect = NONE_EFFECT); + /** draws a text */ + void draw_text(Font* font, const std::string& text, const Vector& position, + int layer, Uint32 drawing_effect = NONE_EFFECT); + /** draws aligned text */ + void draw_text_center(Font* font, const std::string& text, + const Vector& position, int layer, Uint32 drawing_effect = NONE_EFFECT); + /** draws a color gradient onto the whole screen */ + void draw_gradient(Color from, Color to, int layer); + /** fills a rectangle */ + void draw_filled_rect(const Vector& topleft, const Vector& size, + Color color, int layer); + + /** Processes all pending drawing requests and flushes the list */ + void do_drawing(); + + const Vector& get_translation() const + { return transform.translation; } + void set_translation(const Vector& newtranslation) + { transform.translation = newtranslation; } + + void push_transform(); + void pop_transform(); + + /** apply that effect in the next draws (effects are listed on surface.h) */ + void set_drawing_effect(int effect); + +private: + class Transform + { + public: + Vector translation; // only translation for now... + + Vector apply(const Vector& v) const + { + return v - translation; + } + + int draw_effect; + }; + + /// the transform stack + std::vector transformstack; + /// the currently active transform + Transform transform; + + enum RequestType + { + SURFACE, SURFACE_PART, TEXT, GRADIENT, FILLRECT + }; + + struct SurfacePartRequest + { + const Surface* surface; + Vector source, size; + }; + + struct TextRequest + { + Font* font; + std::string text; + }; + + struct GradientRequest + { + Color top, bottom; + Vector size; + }; + + struct FillRectRequest + { + Color color; + Vector size; + }; + + struct DrawingRequest + { + int layer; + Uint32 drawing_effect; + + RequestType type; + Vector pos; + + void* request_data; + + bool operator<(const DrawingRequest& other) const + { + return layer < other.layer; + } + }; + + void draw_surface_part(DrawingRequest& request); + void draw_text(DrawingRequest& request); + void draw_gradient(DrawingRequest& request); + void draw_filled_rect(DrawingRequest& request); + + typedef std::vector DrawingRequests; + DrawingRequests drawingrequests; +}; + +#endif /*SUPERTUX_DRAWINGCONTEXT_H*/ + diff --git a/lib/video/font.cpp b/lib/video/font.cpp new file mode 100644 index 000000000..831f406eb --- /dev/null +++ b/lib/video/font.cpp @@ -0,0 +1,267 @@ +// $Id$ +// +// SuperTux +// Copyright (C) 2004 Tobias Glaesser +// +// This program is free software; you can redistribute it and/or +// modify it under the terms of the GNU General Public License +// as published by the Free Software Foundation; either version 2 +// of the License, or (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software +// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA +// 02111-1307, USA. + +#include +#include + +#include "app/globals.h" +#include "video/screen.h" +#include "video/font.h" +#include "video/drawing_context.h" +#include "utils/lispreader.h" + +Font::Font(const std::string& file, FontType ntype, int nw, int nh, + int nshadowsize) + : chars(0), shadow_chars(0), type(ntype), w(nw), h(nh), + shadowsize(nshadowsize) +{ + chars = new Surface(file, true); + + switch(type) { + case TEXT: + first_char = 32; + break; + case NUM: + first_char = 48; + break; + } + last_char = first_char + (chars->h / h) * 16; + if(last_char > 127) // we have left out some control chars at 128-159 + last_char += 32; + + // Load shadow font. + if(shadowsize > 0) { + SDL_Surface* conv = SDL_DisplayFormatAlpha(chars->impl->get_sdl_surface()); + int pixels = conv->w * conv->h; + SDL_LockSurface(conv); + for(int i = 0; i < pixels; ++i) { + Uint32 *p = (Uint32 *)conv->pixels + i; + *p = *p & conv->format->Amask; + } + SDL_UnlockSurface(conv); + SDL_SetAlpha(conv, SDL_SRCALPHA, 128); + shadow_chars = new Surface(conv, true); + SDL_FreeSurface(conv); + } +} + +Font::~Font() +{ + delete chars; + delete shadow_chars; +} + +float +Font::get_height() const +{ + return h; +} + +float +Font::get_text_width(const std::string& text) const +{ + return text.size() * w; +} + +void +Font::draw(const std::string& text, const Vector& pos, Uint32 drawing_effect) +{ + if(shadowsize > 0) + draw_chars(shadow_chars, text, pos + Vector(shadowsize, shadowsize), + drawing_effect); + + draw_chars(chars, text, pos, drawing_effect); +} + +void +Font::draw_chars(Surface* pchars, const std::string& text, const Vector& pos, + Uint32 drawing_effect) +{ + SurfaceImpl* impl = pchars->impl; + + Vector p = pos; + for(size_t i = 0; i < text.size(); ++i) + { + int c = (unsigned char) text[i]; + if(c > 127) // correct for the 32 controlchars at 128-159 + c -= 32; + // a non-printable character? + if(c == '\n') { + p.x = pos.x; + p.y += h + 2; + continue; + } + if(c == ' ' || c < first_char || c > last_char) { + p.x += w; + continue; + } + + int index = c - first_char; + int source_x = (index % 16) * w; + int source_y = (index / 16) * h; + + impl->draw_part(source_x, source_y, p.x, p.y, w, h, 255, drawing_effect); + p.x += w; + } +} + +/* --- SCROLL TEXT FUNCTION --- */ + +#define MAX_VEL 10 +#define SPEED_INC 0.01 +#define SCROLL 60 +#define ITEMS_SPACE 4 + +void display_text_file(const std::string& file, float scroll_speed) +{ + std::string text; + std::vector names; + + LispReader* reader = LispReader::load(datadir + "/" + file, "supertux-text"); + + if(!reader) + { + std::cerr << "Error: Could not open text. Ignoring...\n"; + return; + } + + reader->read_string("text", text, true); + std::string background_file; + reader->read_string("background", background_file, true); + delete reader; + + // Split text string lines into a vector + names.clear(); + unsigned int i, l; + i = 0; + while(true) + { + l = text.find("\n", i); + + if(l == std::string::npos) + { + char temp[1024]; + temp[text.copy(temp, text.size() - i, i)] = '\0'; + names.push_back(temp); + break; + } + + char temp[1024]; + temp[text.copy(temp, l-i, i)] = '\0'; + names.push_back(temp); + + i = l+1; + } + + // load background image + Surface* background = new Surface(datadir + "/images/background/" + background_file, false); + + int done = 0; + float scroll = 0; + float speed = scroll_speed / 50; + + DrawingContext context; + SDL_EnableKeyRepeat(SDL_DEFAULT_REPEAT_DELAY, SDL_DEFAULT_REPEAT_INTERVAL); + + Uint32 lastticks = SDL_GetTicks(); + while(!done) + { + /* in case of input, exit */ + SDL_Event event; + while(SDL_PollEvent(&event)) + switch(event.type) + { + case SDL_KEYDOWN: + switch(event.key.keysym.sym) + { + case SDLK_UP: + speed -= SPEED_INC; + break; + case SDLK_DOWN: + speed += SPEED_INC; + break; + case SDLK_SPACE: + case SDLK_RETURN: + if(speed >= 0) + scroll += SCROLL; + break; + case SDLK_ESCAPE: + done = 1; + break; + default: + break; + } + break; + case SDL_QUIT: + done = 1; + break; + default: + break; + } + + if(speed > MAX_VEL) + speed = MAX_VEL; + else if(speed < -MAX_VEL) + speed = -MAX_VEL; + + /* draw the credits */ + context.draw_surface(background, Vector(0,0), 0); + + float y = 0; + for(size_t i = 0; i < names.size(); i++) { + if(names[i].size() == 0) { + y += white_text->get_height() + ITEMS_SPACE; + continue; + } + + Font* font = 0; + switch(names[i][0]) + { + case ' ': font = white_small_text; break; + case '\t': font = white_text; break; + case '-': font = white_big_text; break; + case '*': font = blue_text; break; + default: font = blue_text; break; + } + + context.draw_text_center(font, + names[i].substr(1, names[i].size()-1), + Vector(0, screen->h + y - scroll), LAYER_FOREGROUND1); + y += font->get_height() + ITEMS_SPACE; + } + + context.do_drawing(); + + if(screen->h+y-scroll < 0 && 20+screen->h+y-scroll < 0) + done = 1; + + Uint32 ticks = SDL_GetTicks(); + scroll += speed * (ticks - lastticks); + lastticks = ticks; + if(scroll < 0) + scroll = 0; + + SDL_Delay(10); + } + + SDL_EnableKeyRepeat(0, 0); // disables key repeating + delete background; +} + diff --git a/lib/video/font.h b/lib/video/font.h new file mode 100644 index 000000000..4b2617f75 --- /dev/null +++ b/lib/video/font.h @@ -0,0 +1,76 @@ +// $Id$ +// +// SuperTux +// Copyright (C) 2004 Tobias Glaesser +// +// This program is free software; you can redistribute it and/or +// modify it under the terms of the GNU General Public License +// as published by the Free Software Foundation; either version 2 +// of the License, or (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software +// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA +// 02111-1307, USA. + +#ifndef SUPERTUX_FONT_H +#define SUPERTUX_FONT_H + +#include + +#include "video/surface.h" +#include "math/vector.h" + +/** Reads a text file (using LispReader, so it as to be in its formatting) + and displays it in a StarTrek fashion */ +void display_text_file(const std::string& file, float scroll_speed); + +/* Text type */ +class Font +{ +public: + /* Kinds of texts. */ + enum FontType { + TEXT, // images for all characters + NUM // only images for numbers + }; + + Font(const std::string& file, FontType type, int w, int h, int shadowsize=2); + ~Font(); + + /** returns the height of the font */ + float get_height() const; + /** returns the width of a given text. (Note that I won't add a normal + * get_width function here, as we might switch to variable width fonts in the + * future. + */ + float get_text_width(const std::string& text) const; + +private: + friend class DrawingContext; + + void draw(const std::string& text, const Vector& pos, + Uint32 drawing_effect = NONE_EFFECT); + void draw_chars(Surface* pchars, const std::string& text, + const Vector& position, Uint32 drawing_effect); + + Surface* chars; + Surface* shadow_chars; + FontType type; + int w; + int h; + int shadowsize; + + /// the number of the first character that is represented in the font + int first_char; + /// the number of the last character that is represented in the font + int last_char; +}; + +#endif /*SUPERTUX_FONT_H*/ + diff --git a/lib/video/screen.cpp b/lib/video/screen.cpp new file mode 100644 index 000000000..b123cecbc --- /dev/null +++ b/lib/video/screen.cpp @@ -0,0 +1,299 @@ +// $Id$ +// +// SuperTux - A Jump'n Run +// Copyright (C) 2000 Bill Kendrick +// +// This program is free software; you can redistribute it and/or +// modify it under the terms of the GNU General Public License +// as published by the Free Software Foundation; either version 2 +// of the License, or (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software +// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + +#include +#include +#include +#include +#include + +#include + +#include "SDL.h" +#include "SDL_image.h" + +#ifndef WIN32 +#include +#include +#endif + +#include "app/globals.h" +#include "video/screen.h" +#include "video/drawing_context.h" +#include "special/base.h" + +/* 'Stolen' from the SDL documentation. + * Set the pixel at (x, y) to the given value + * NOTE: The surface must be locked before calling this! + */ +void putpixel(SDL_Surface *surface, int x, int y, Uint32 pixel) +{ + int bpp = surface->format->BytesPerPixel; + /* Here p is the address to the pixel we want to set */ + Uint8 *p = (Uint8 *)surface->pixels + y * surface->pitch + x * bpp; + + switch(bpp) + { + case 1: + *p = pixel; + break; + + case 2: + *(Uint16 *)p = pixel; + break; + + case 3: + if(SDL_BYTEORDER == SDL_BIG_ENDIAN) + { + p[0] = (pixel >> 16) & 0xff; + p[1] = (pixel >> 8) & 0xff; + p[2] = pixel & 0xff; + } + else + { + p[0] = pixel & 0xff; + p[1] = (pixel >> 8) & 0xff; + p[2] = (pixel >> 16) & 0xff; + } + break; + + case 4: + *(Uint32 *)p = pixel; + break; + } +} + +/* Draw a single pixel on the screen. */ +void drawpixel(int x, int y, Uint32 pixel) +{ + /* Lock the screen for direct access to the pixels */ + if ( SDL_MUSTLOCK(screen) ) + { + if ( SDL_LockSurface(screen) < 0 ) + { + fprintf(stderr, "Can't lock screen: %s\n", SDL_GetError()); + return; + } + } + + if(!(x < 0 || y < 0 || x > screen->w || y > screen->h)) + putpixel(screen, x, y, pixel); + + if ( SDL_MUSTLOCK(screen) ) + { + SDL_UnlockSurface(screen); + } + /* Update just the part of the display that we've changed */ + SDL_UpdateRect(screen, x, y, 1, 1); +} + +/* --- FILL A RECT --- */ + +void fillrect(float x, float y, float w, float h, int r, int g, int b, int a) +{ +if(w < 0) + { + x += w; + w = -w; + } +if(h < 0) + { + y += h; + h = -h; + } + +#ifndef NOOPENGL + if(use_gl) + { + glEnable(GL_BLEND); + glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); + glColor4ub(r, g, b,a); + + glBegin(GL_POLYGON); + glVertex2f(x, y); + glVertex2f(x+w, y); + glVertex2f(x+w, y+h); + glVertex2f(x, y+h); + glEnd(); + glDisable(GL_BLEND); + } + else + { +#endif + SDL_Rect src, rect; + SDL_Surface *temp = NULL; + + rect.x = (int)x; + rect.y = (int)y; + rect.w = (int)w; + rect.h = (int)h; + + if(a != 255) + { + temp = SDL_CreateRGBSurface(screen->flags, rect.w, rect.h, screen->format->BitsPerPixel, + screen->format->Rmask, + screen->format->Gmask, + screen->format->Bmask, + screen->format->Amask); + + + src.x = 0; + src.y = 0; + src.w = rect.w; + src.h = rect.h; + + SDL_FillRect(temp, &src, SDL_MapRGB(screen->format, r, g, b)); + + SDL_SetAlpha(temp, SDL_SRCALPHA, a); + + SDL_BlitSurface(temp,0,screen,&rect); + + SDL_FreeSurface(temp); + } + else + SDL_FillRect(screen, &rect, SDL_MapRGB(screen->format, r, g, b)); + +#ifndef NOOPENGL + + } +#endif +} + +/* Needed for line calculations */ +#define SGN(x) ((x)>0 ? 1 : ((x)==0 ? 0:(-1))) +#define ABS(x) ((x)>0 ? (x) : (-x)) + +void +draw_line(float x1, float y1, float x2, float y2, int r, int g, int b, int a) +{ +#ifndef NOOPENGL + if(use_gl) + { + glEnable(GL_BLEND); + glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); + glColor4ub(r, g, b,a); + + glBegin(GL_LINES); + glVertex2f(x1, y1); + glVertex2f(x2, y2); + glEnd(); + glDisable(GL_BLEND); + } + else + { +#endif + /* Basic unantialiased Bresenham line algorithm */ + int lg_delta, sh_delta, cycle, lg_step, sh_step; + Uint32 color = SDL_MapRGBA(screen->format, r, g, b, a); + + lg_delta = (int)(x2 - x1); + sh_delta = (int)(y2 - y1); + lg_step = SGN(lg_delta); + lg_delta = ABS(lg_delta); + sh_step = SGN(sh_delta); + sh_delta = ABS(sh_delta); + if (sh_delta < lg_delta) + { + cycle = lg_delta >> 1; + while (x1 != x2) + { + drawpixel((int)x1, (int)y1, color); + cycle += sh_delta; + if (cycle > lg_delta) + { + cycle -= lg_delta; + y1 += sh_step; + } + x1 += lg_step; + } + drawpixel((int)x1, (int)y1, color); + } + cycle = sh_delta >> 1; + while (y1 != y2) + { + drawpixel((int)x1, (int)y1, color); + cycle += lg_delta; + if (cycle > sh_delta) + { + cycle -= sh_delta; + x1 += lg_step; + } + y1 += sh_step; + } + drawpixel((int)x1, (int)y1, color); +#ifndef NOOPENGL + + } +#endif +} + +#define LOOP_DELAY 20.0 + +void fadeout(int fade_time) +{ + float alpha_inc = 256 / (fade_time / LOOP_DELAY); + float alpha = 256; + + while(alpha > 0) + { + alpha -= alpha_inc; + fillrect(0, 0, screen->w, screen->h, 0,0,0, (int)alpha_inc); // left side + + DrawingContext context; // ugly... + context.do_drawing(); + + SDL_Delay(int(LOOP_DELAY)); + } + + fillrect(0, 0, screen->w, screen->h, 0, 0, 0, 255); + + DrawingContext context; + context.draw_text_center(white_text, "Loading...", + Vector(0, screen->h/2), LAYER_FOREGROUND1); + context.do_drawing(); +} + +void shrink_fade(const Vector& point, int fade_time) +{ + float left_inc = point.x / ((float)fade_time / LOOP_DELAY); + float right_inc = (screen->w - point.x) / ((float)fade_time / LOOP_DELAY); + float up_inc = point.y / ((float)fade_time / LOOP_DELAY); + float down_inc = (screen->h - point.y) / ((float)fade_time / LOOP_DELAY); + + float left_cor = 0, right_cor = 0, up_cor = 0, down_cor = 0; + + while(left_cor < point.x && right_cor < screen->w - point.x && + up_cor < point.y && down_cor < screen->h - point.y) + { + left_cor += left_inc; + right_cor += right_inc; + up_cor += up_inc; + down_cor += down_inc; + + fillrect(0, 0, left_cor, screen->h, 0,0,0); // left side + fillrect(screen->w - right_cor, 0, right_cor, screen->h, 0,0,0); // right side + fillrect(0, 0, screen->w, up_cor, 0,0,0); // up side + fillrect(0, screen->h - down_cor, screen->w, down_cor+1, 0,0,0); // down side + DrawingContext context; // ugly... + context.do_drawing(); + + SDL_Delay(int(LOOP_DELAY)); + } +} + diff --git a/lib/video/screen.h b/lib/video/screen.h new file mode 100644 index 000000000..263bdae42 --- /dev/null +++ b/lib/video/screen.h @@ -0,0 +1,64 @@ +// $Id$ +// +// SuperTux - A Jump'n Run +// Copyright (C) 2000 Bill Kendrick +// +// This program is free software; you can redistribute it and/or +// modify it under the terms of the GNU General Public License +// as published by the Free Software Foundation; either version 2 +// of the License, or (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software +// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + +#ifndef SUPERTUX_SCREEN_H +#define SUPERTUX_SCREEN_H + +#include +#ifndef NOOPENGL +#include +#endif +#include +class Color +{ +public: + Color() + : red(0), green(0), blue(0), alpha(255) + {} + + Color(Uint8 red_, Uint8 green_, Uint8 blue_, Uint8 alpha_ = 255) + : red(red_), green(green_), blue(blue_), alpha(alpha_) + {} + + Color(const Color& o) + : red(o.red), green(o.green), blue(o.blue), alpha(o.alpha) + { } + + bool operator==(const Color& o) + { if(red == o.red && green == o.green && + blue == o.blue && alpha == o.alpha) + return true; + return false; } + + Uint8 red, green, blue, alpha; +}; + +#include "video/surface.h" + +class Vector; + +void putpixel(SDL_Surface *surface, int x, int y, Uint32 pixel); +void drawpixel(int x, int y, Uint32 pixel); +void fillrect(float x, float y, float w, float h, int r, int g, int b, int a = 255); +void draw_line(float x1, float y1, float x2, int r, int g, int b, int a = 255); + +void fadeout(int fade_time); +void shrink_fade(const Vector& point, int fade_time); + +#endif /*SUPERTUX_SCREEN_H*/ diff --git a/lib/video/surface.cpp b/lib/video/surface.cpp new file mode 100644 index 000000000..e423aa335 --- /dev/null +++ b/lib/video/surface.cpp @@ -0,0 +1,877 @@ +// $Id$ +// +// SuperTux +// Copyright (C) 2004 Tobias Glaesser +// +// This program is free software; you can redistribute it and/or +// modify it under the terms of the GNU General Public License +// as published by the Free Software Foundation; either version 2 +// of the License, or (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software +// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA +// 02111-1307, USA. + +#include +#include +#include + +#include "SDL.h" +#include "SDL_image.h" + +#include "video/surface.h" +#include "app/globals.h" +#include "app/setup.h" + +Surface::Surfaces Surface::surfaces; + +SurfaceData::SurfaceData(SDL_Surface* temp, bool use_alpha_) + : type(SURFACE), surface(0), use_alpha(use_alpha_) +{ + // Copy the given surface and make sure that it is not stored in + // video memory + surface = SDL_CreateRGBSurface(temp->flags & (~SDL_HWSURFACE), + temp->w, temp->h, + temp->format->BitsPerPixel, + temp->format->Rmask, + temp->format->Gmask, + temp->format->Bmask, + temp->format->Amask); + if(!surface) + st_abort("No memory left.", ""); + SDL_SetAlpha(temp,0,0); + SDL_BlitSurface(temp, NULL, surface, NULL); +} + +SurfaceData::SurfaceData(const std::string& file_, bool use_alpha_) + : type(LOAD), surface(0), file(file_), use_alpha(use_alpha_) +{} + +SurfaceData::SurfaceData(const std::string& file_, int x_, int y_, int w_, int h_, bool use_alpha_) + : type(LOAD_PART), surface(0), file(file_), use_alpha(use_alpha_), + x(x_), y(y_), w(w_), h(h_) +{} + +SurfaceData::SurfaceData(Color top_gradient_, Color bottom_gradient_, int w_, int h_) + : type(GRADIENT), surface(0), use_alpha(false), w(w_), h(h_) +{ +top_gradient = top_gradient_; +bottom_gradient = bottom_gradient_; +} + + +SurfaceData::~SurfaceData() +{ + SDL_FreeSurface(surface); +} + +SurfaceImpl* +SurfaceData::create() +{ +#ifndef NOOPENGL + if (use_gl) + return create_SurfaceOpenGL(); + else + return create_SurfaceSDL(); +#else + return create_SurfaceSDL(); +#endif +} + +SurfaceSDL* +SurfaceData::create_SurfaceSDL() +{ + switch(type) + { + case LOAD: + return new SurfaceSDL(file, use_alpha); + case LOAD_PART: + return new SurfaceSDL(file, x, y, w, h, use_alpha); + case SURFACE: + return new SurfaceSDL(surface, use_alpha); + case GRADIENT: + return new SurfaceSDL(top_gradient, bottom_gradient, w, h); + } + assert(0); +} + +SurfaceOpenGL* +SurfaceData::create_SurfaceOpenGL() +{ +#ifndef NOOPENGL + switch(type) + { + case LOAD: + return new SurfaceOpenGL(file, use_alpha); + case LOAD_PART: + return new SurfaceOpenGL(file, x, y, w, h, use_alpha); + case SURFACE: + return new SurfaceOpenGL(surface, use_alpha); + case GRADIENT: + return new SurfaceOpenGL(top_gradient, bottom_gradient, w, h); + } +#endif + assert(0); +} + +#ifndef NOOPENGL +/* Quick utility function for texture creation */ +static int power_of_two(int input) +{ + int value = 1; + + while ( value < input ) + { + value <<= 1; + } + return value; +} +#endif + +Surface::Surface(SDL_Surface* surf, bool use_alpha) + : data(surf, use_alpha), w(0), h(0) +{ + impl = data.create(); + if (impl) + { + w = impl->w; + h = impl->h; + } + surfaces.push_back(this); +} + +Surface::Surface(const std::string& file, bool use_alpha) + : data(file, use_alpha), w(0), h(0) +{ + impl = data.create(); + if (impl) + { + w = impl->w; + h = impl->h; + } + surfaces.push_back(this); +} + +Surface::Surface(const std::string& file, int x, int y, int w, int h, bool use_alpha) + : data(file, x, y, w, h, use_alpha), w(0), h(0) +{ + impl = data.create(); + if (impl) + { + w = impl->w; + h = impl->h; + } + surfaces.push_back(this); +} + +Surface::Surface(Color top_background, Color bottom_background, int w_, int h_) + : data(top_background, bottom_background, 0, 0), w(0), h(0) +{ + // FIXME: Gradient surfaces currently don't accept width/height + // If nonzero values are passed to data.create(), supertux + // crashes. + impl = data.create(); + if (impl) + { + w = impl->w; + h = impl->h; + } + surfaces.push_back(this); +} + +void +Surface::reload() +{ + delete impl; + impl = data.create(); + if (impl) + { + w = impl->w; + h = impl->h; + } +} + +Surface::~Surface() +{ +#ifdef DEBUG + bool found = false; + for(std::list::iterator i = surfaces.begin(); i != surfaces.end(); + ++i) + { + if(*i == this) + { + found = true; break; + } + } + if(!found) + printf("Error: Surface freed twice!!!\n"); +#endif + surfaces.remove(this); + delete impl; +} + +void +Surface::reload_all() +{ + for(Surfaces::iterator i = surfaces.begin(); i != surfaces.end(); ++i) + { + (*i)->reload(); + } +} + +void +Surface::debug_check() +{ + for(Surfaces::iterator i = surfaces.begin(); i != surfaces.end(); ++i) + { + printf("Surface not freed: T:%d F:%s.\n", (*i)->data.type, + (*i)->data.file.c_str()); + } +} + +void +Surface::resize(int w_, int h_) +{ + if (impl) + { + w = w_; + h = h_; + if (impl->resize(w_,h_) == -2) + reload(); + } +} + +SDL_Surface* +sdl_surface_part_from_file(const std::string& file, int x, int y, int w, int h, bool use_alpha) +{ + SDL_Rect src; + SDL_Surface * sdl_surface; + SDL_Surface * temp; + SDL_Surface * conv; + + temp = IMG_Load(file.c_str()); + + if (temp == NULL) + st_abort("Can't load", file); + + /* Set source rectangle for conv: */ + + src.x = x; + src.y = y; + src.w = w; + src.h = h; + + conv = SDL_CreateRGBSurface(temp->flags, w, h, temp->format->BitsPerPixel, + temp->format->Rmask, + temp->format->Gmask, + temp->format->Bmask, + temp->format->Amask); + + /* #if SDL_BYTEORDER == SDL_BIG_ENDIAN + 0xff000000, 0x00ff0000, 0x0000ff00, 0x000000ff); + #else + + 0x000000ff, 0x0000ff00, 0x00ff0000, 0xff000000); + #endif*/ + + SDL_SetAlpha(temp,0,0); + + SDL_BlitSurface(temp, &src, conv, NULL); + if(use_alpha == false && !use_gl) + sdl_surface = SDL_DisplayFormat(conv); + else + sdl_surface = SDL_DisplayFormatAlpha(conv); + + if (sdl_surface == NULL) + st_abort("Can't covert to display format", file); + + if (use_alpha == false && !use_gl) + SDL_SetAlpha(sdl_surface, 0, 0); + + SDL_FreeSurface(temp); + SDL_FreeSurface(conv); + + return sdl_surface; +} + +SDL_Surface* +sdl_surface_from_file(const std::string& file, bool use_alpha) +{ + SDL_Surface* sdl_surface; + SDL_Surface* temp; + + temp = IMG_Load(file.c_str()); + + if (temp == NULL) + st_abort("Can't load", file); + + if(use_alpha == false && !use_gl) + sdl_surface = SDL_DisplayFormat(temp); + else + sdl_surface = SDL_DisplayFormatAlpha(temp); + + if (sdl_surface == NULL) + st_abort("Can't covert to display format", file); + + if (use_alpha == false && !use_gl) + SDL_SetAlpha(sdl_surface, 0, 0); + + SDL_FreeSurface(temp); + + return sdl_surface; +} + +SDL_Surface* +sdl_surface_from_sdl_surface(SDL_Surface* sdl_surf, bool use_alpha) +{ + SDL_Surface* sdl_surface; + Uint32 saved_flags; + Uint8 saved_alpha; + + /* Save the alpha blending attributes */ + saved_flags = sdl_surf->flags&(SDL_SRCALPHA|SDL_RLEACCELOK); + saved_alpha = sdl_surf->format->alpha; + if ( (saved_flags & SDL_SRCALPHA) + == SDL_SRCALPHA ) + { + SDL_SetAlpha(sdl_surf, 0, 0); + } + + if(use_alpha == false && !use_gl) + sdl_surface = SDL_DisplayFormat(sdl_surf); + else + sdl_surface = SDL_DisplayFormatAlpha(sdl_surf); + + /* Restore the alpha blending attributes */ + if ( (saved_flags & SDL_SRCALPHA) + == SDL_SRCALPHA ) + { + SDL_SetAlpha(sdl_surface, saved_flags, saved_alpha); + } + + if (sdl_surface == NULL) + st_abort("Can't covert to display format", "SURFACE"); + + if (use_alpha == false && !use_gl) + SDL_SetAlpha(sdl_surface, 0, 0); + + return sdl_surface; +} + +SDL_Surface* +sdl_surface_from_gradient(Color top, Color bottom, int w, int h) +{ + SDL_Surface* sdl_surface; + + sdl_surface = SDL_CreateRGBSurface(screen->flags, w, h, + screen->format->BitsPerPixel, screen->format->Rmask, + screen->format->Gmask, screen->format->Bmask, screen->format->Amask); + + if(sdl_surface == NULL) + st_abort("Cannot create surface for the gradient", "SURFACE"); + + if(top == bottom) + { + SDL_FillRect(sdl_surface, NULL, SDL_MapRGB(sdl_surface->format, + top.red, top.green, top.blue)); + } + else + { + float redstep = (float(bottom.red)-float(top.red)) / float(h); + float greenstep = (float(bottom.green)-float(top.green)) / float(h); + float bluestep = (float(bottom.blue) - float(top.blue)) / float(h); + + SDL_Rect rect; + rect.x = 0; + rect.w = w; + rect.h = 1; + for(float y = 0; y < h; y++) + { + rect.y = (int)y; + SDL_FillRect(sdl_surface, &rect, SDL_MapRGB(sdl_surface->format, + int(float(top.red) + redstep * y), + int(float(top.green) + greenstep * y), + int(float(top.blue) + bluestep * y))); + } + } + + return sdl_surface; +} + +//--------------------------------------------------------------------------- + +SurfaceImpl::SurfaceImpl() +{} + +SurfaceImpl::~SurfaceImpl() +{ + SDL_FreeSurface(sdl_surface); +} + +SDL_Surface* SurfaceImpl::get_sdl_surface() const +{ + return sdl_surface; +} + +int SurfaceImpl::resize(int w_, int h_) +{ + w = w_; + h = h_; + SDL_Rect dest; + dest.x = 0; + dest.y = 0; + dest.w = w; + dest.h = h; + int ret = SDL_SoftStretch(sdl_surface, NULL, + sdl_surface, &dest); + return ret; +} + +#ifndef NOOPENGL +SurfaceOpenGL::SurfaceOpenGL(SDL_Surface* surf, bool use_alpha) +{ + sdl_surface = sdl_surface_from_sdl_surface(surf, use_alpha); + create_gl(sdl_surface,&gl_texture); + + w = sdl_surface->w; + h = sdl_surface->h; +} + +SurfaceOpenGL::SurfaceOpenGL(const std::string& file, bool use_alpha) +{ + sdl_surface = sdl_surface_from_file(file, use_alpha); + create_gl(sdl_surface,&gl_texture); + + w = sdl_surface->w; + h = sdl_surface->h; +} + +SurfaceOpenGL::SurfaceOpenGL(const std::string& file_, int x_, int y_, int w_, int h_, bool use_alpha_) +{ + sdl_surface = sdl_surface_part_from_file(file_,x_,y_,w_,h_,use_alpha_); + + create_gl(sdl_surface, &gl_texture); + + w = sdl_surface->w; + h = sdl_surface->h; +} + +SurfaceOpenGL::SurfaceOpenGL(Color top_gradient, Color bottom_gradient, int w, int h) +{ + sdl_surface = sdl_surface_from_gradient(top_gradient, bottom_gradient, w, h); + create_gl(sdl_surface, &gl_texture); + + w = sdl_surface->w; + h = sdl_surface->h; +} + +SurfaceOpenGL::~SurfaceOpenGL() +{ + glDeleteTextures(1, &gl_texture); +} + +void +SurfaceOpenGL::create_gl(SDL_Surface * surf, GLuint * tex) +{ + Uint32 saved_flags; + Uint8 saved_alpha; + int w, h; + SDL_Surface *conv; + + w = power_of_two(surf->w); + h = power_of_two(surf->h), + +#if SDL_BYTEORDER == SDL_BIG_ENDIAN + conv = SDL_CreateRGBSurface(SDL_SWSURFACE, w, h, surf->format->BitsPerPixel, + 0xff000000, 0x00ff0000, 0x0000ff00, 0x000000ff); +#else + conv = SDL_CreateRGBSurface(SDL_SWSURFACE, w, h, surf->format->BitsPerPixel, + 0x000000ff, 0x0000ff00, 0x00ff0000, 0xff000000); +#endif + + /* Save the alpha blending attributes */ + saved_flags = surf->flags&(SDL_SRCALPHA|SDL_RLEACCELOK); + saved_alpha = surf->format->alpha; + if ( (saved_flags & SDL_SRCALPHA) + == SDL_SRCALPHA ) + { + SDL_SetAlpha(surf, 0, 0); + } + + SDL_BlitSurface(surf, 0, conv, 0); + + /* Restore the alpha blending attributes */ + if ( (saved_flags & SDL_SRCALPHA) + == SDL_SRCALPHA ) + { + SDL_SetAlpha(surf, saved_flags, saved_alpha); + } + + // We check all the pixels of the surface to figure out which + // internal format OpenGL should use for storing it, ie. if no alpha + // is present store in RGB instead of RGBA, this saves a few bytes + // of memory, but much more importantly it makes the game look + // *much* better in 16bit color mode + int internal_format = GL_RGB10_A2; + bool has_alpha = false; + + unsigned char* buf = static_cast(conv->pixels); + for (int y = 0; y < surf->h; ++y) + for (int x = 0; x < surf->w; ++x) + { + if (buf[(conv->pitch*y + x*4) + 3] != 255) + { + has_alpha = true; + break; + } + } + + if (!has_alpha) + { + internal_format = GL_RGB; + } + + glGenTextures(1, &*tex); + glBindTexture(GL_TEXTURE_2D , *tex); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); + glPixelStorei(GL_UNPACK_ROW_LENGTH, conv->pitch / conv->format->BytesPerPixel); + glTexImage2D(GL_TEXTURE_2D, 0, internal_format, w, h, 0, GL_RGBA, GL_UNSIGNED_BYTE, conv->pixels); + glPixelStorei(GL_UNPACK_ROW_LENGTH, 0); + + SDL_FreeSurface(conv); +} + +int +SurfaceOpenGL::draw(float x, float y, Uint8 alpha, Uint32 effect) +{ + float pw = power_of_two(w); + float ph = power_of_two(h); + + if(effect & SEMI_TRANSPARENT) + alpha = 128; + + glEnable(GL_TEXTURE_2D); + glEnable(GL_BLEND); + glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); + + glColor4ub(alpha, alpha, alpha, alpha); + + glBindTexture(GL_TEXTURE_2D, gl_texture); + + glBegin(GL_QUADS); + + if(effect & VERTICAL_FLIP) + { + glTexCoord2f(0, 0); + glVertex2f(x, (float)h+y); + + glTexCoord2f((float)w / pw, 0); + glVertex2f((float)w+x, (float)h+y); + + glTexCoord2f((float)w / pw, (float)h / ph); + glVertex2f((float)w+x, y); + + glTexCoord2f(0, (float)h / ph); + glVertex2f(x, y); + } + else + { + glTexCoord2f(0, 0); + glVertex2f(x, y); + + glTexCoord2f((float)w / pw, 0); + glVertex2f((float)w+x, y); + + glTexCoord2f((float)w / pw, (float)h / ph); + glVertex2f((float)w+x, (float)h+y); + + glTexCoord2f(0, (float)h / ph); + glVertex2f(x, (float)h+y); + } + glEnd(); + + glDisable(GL_TEXTURE_2D); + glDisable(GL_BLEND); + + return 0; +} + +int +SurfaceOpenGL::draw_part(float sx, float sy, float x, float y, float w, float h, Uint8 alpha, Uint32 effect) +{ + float pw = power_of_two(int(this->w)); + float ph = power_of_two(int(this->h)); + + if(effect & SEMI_TRANSPARENT) + alpha = 128; + + glBindTexture(GL_TEXTURE_2D, gl_texture); + + glEnable(GL_BLEND); + glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); + + glColor4ub(alpha, alpha, alpha, alpha); + + glEnable(GL_TEXTURE_2D); + + + glBegin(GL_QUADS); + + if(effect & VERTICAL_FLIP) + { + glTexCoord2f(sx / pw, sy / ph); + glVertex2f(x, y); + + glTexCoord2f((float)(sx + w) / pw, sy / ph); + glVertex2f(w+x, y); + + glTexCoord2f((sx+w) / pw, (sy+h) / ph); + glVertex2f(w +x, h+y); + + glTexCoord2f(sx / pw, (float)(sy+h) / ph); + glVertex2f(x, h+y); + } + else + { + glTexCoord2f(sx / pw, (float)(sy+h) / ph); + glVertex2f(x, h+y); + + glTexCoord2f((sx+w) / pw, (sy+h) / ph); + glVertex2f(w +x, h+y); + + glTexCoord2f((float)(sx + w) / pw, sy / ph); + glVertex2f(w+x, y); + + glTexCoord2f(sx / pw, sy / ph); + glVertex2f(x, y); + } + + glEnd(); + + glDisable(GL_TEXTURE_2D); + glDisable(GL_BLEND); + + return 0; +} + +#if 0 +int +SurfaceOpenGL::draw_stretched(float x, float y, int sw, int sh, Uint8 alpha) +{ + float pw = power_of_two(int(this->w)); + float ph = power_of_two(int(this->h)); + + glBindTexture(GL_TEXTURE_2D, gl_texture); + + glEnable(GL_BLEND); + glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); + + glColor4ub(alpha, alpha, alpha, alpha); + + glEnable(GL_TEXTURE_2D); + + + glBegin(GL_QUADS); + glTexCoord2f(0, 0); + glVertex2f(x, y); + glTexCoord2f((float)w / pw, 0); + glVertex2f(sw+x, y); + glTexCoord2f((float)w / pw, (float)h / ph); glVertex2f((float)sw+x, (float)sh+y); + glVertex2f(sw +x, sh+y); + glTexCoord2f(0, (float)h / ph); + glVertex2f(x, sh+y); + glEnd(); + + glDisable(GL_TEXTURE_2D); + glDisable(GL_BLEND); + + return 0; +} +#endif + +#endif + +SurfaceSDL::SurfaceSDL(SDL_Surface* surf, bool use_alpha) +{ + sdl_surface = sdl_surface_from_sdl_surface(surf, use_alpha); + w = sdl_surface->w; + h = sdl_surface->h; +} + +SurfaceSDL::SurfaceSDL(const std::string& file, bool use_alpha) +{ + sdl_surface = sdl_surface_from_file(file, use_alpha); + w = sdl_surface->w; + h = sdl_surface->h; +} + +SurfaceSDL::SurfaceSDL(const std::string& file, int x, int y, int w, int h, bool use_alpha) +{ + sdl_surface = sdl_surface_part_from_file(file, x, y, w, h, use_alpha); + w = sdl_surface->w; + h = sdl_surface->h; +} + +SurfaceSDL::SurfaceSDL(Color top_gradient, Color bottom_gradient, int w, int h) +{ + sdl_surface = sdl_surface_from_gradient(top_gradient, bottom_gradient, w, h); + w = sdl_surface->w; + h = sdl_surface->h; +} + +int +SurfaceSDL::draw(float x, float y, Uint8 alpha, Uint32 effect) +{ + SDL_Rect dest; + + dest.x = (int)x; + dest.y = (int)y; + dest.w = w; + dest.h = h; + + if(effect & SEMI_TRANSPARENT) + alpha = 128; + + if(effect & VERTICAL_FLIP) // FIXME: feel free to replace this hack + { + for(float sy = 0; sy < h; sy++) + if(draw_part(0, sy, x, y+(h-sy), w, 1, alpha, NONE_EFFECT) == -2) + return -2; + return 0; + } + + if(alpha != 255) + { + /* Create a Surface, make it using colorkey, blit surface into temp, apply alpha + to temp sur, blit the temp into the screen */ + /* Note: this has to be done, since SDL doesn't allow to set alpha to surfaces that + already have an alpha mask yet... */ + + SDL_Surface* sdl_surface_copy = SDL_CreateRGBSurface (sdl_surface->flags, + sdl_surface->w, sdl_surface->h, sdl_surface->format->BitsPerPixel, + sdl_surface->format->Rmask, sdl_surface->format->Gmask, + sdl_surface->format->Bmask, + 0); + int colorkey = SDL_MapRGB(sdl_surface_copy->format, 255, 0, 255); + SDL_FillRect(sdl_surface_copy, NULL, colorkey); + SDL_SetColorKey(sdl_surface_copy, SDL_SRCCOLORKEY, colorkey); + + + SDL_BlitSurface(sdl_surface, NULL, sdl_surface_copy, NULL); + SDL_SetAlpha(sdl_surface_copy ,SDL_SRCALPHA,alpha); + + int ret = SDL_BlitSurface(sdl_surface_copy, NULL, screen, &dest); + + SDL_FreeSurface (sdl_surface_copy); + return ret; + } + + int ret = SDL_BlitSurface(sdl_surface, NULL, screen, &dest); + + return ret; +} + +int +SurfaceSDL::draw_part(float sx, float sy, float x, float y, float w, float h, Uint8 alpha, Uint32 effect) +{ + SDL_Rect src, dest; + + src.x = (int)sx; + src.y = (int)sy; + src.w = (int)w; + src.h = (int)h; + + dest.x = (int)x; + dest.y = (int)y; + dest.w = (int)w; + dest.h = (int)h; + + if(effect & SEMI_TRANSPARENT) + alpha = 128; + + if(effect & VERTICAL_FLIP) // FIXME: feel free to replace this hack + { + for(float sy_ = sy; sy_ < h; sy_++) + if(draw_part(sx, sy_, x, y+(h-sy_), w, 1, alpha, NONE_EFFECT) == -2) + return -2; + return 0; + } + + if(alpha != 255) + { + /* Create a Surface, make it using colorkey, blit surface into temp, apply alpha + to temp sur, blit the temp into the screen */ + /* Note: this has to be done, since SDL doesn't allow to set alpha to surfaces that + already have an alpha mask yet... */ + + SDL_Surface* sdl_surface_copy = SDL_CreateRGBSurface (sdl_surface->flags, + sdl_surface->w, sdl_surface->h, sdl_surface->format->BitsPerPixel, + sdl_surface->format->Rmask, sdl_surface->format->Gmask, + sdl_surface->format->Bmask, + 0); + int colorkey = SDL_MapRGB(sdl_surface_copy->format, 255, 0, 255); + SDL_FillRect(sdl_surface_copy, NULL, colorkey); + SDL_SetColorKey(sdl_surface_copy, SDL_SRCCOLORKEY, colorkey); + + + SDL_BlitSurface(sdl_surface, NULL, sdl_surface_copy, NULL); + SDL_SetAlpha(sdl_surface_copy ,SDL_SRCALPHA,alpha); + + int ret = SDL_BlitSurface(sdl_surface_copy, NULL, screen, &dest); + + SDL_FreeSurface (sdl_surface_copy); + return ret; + } + + int ret = SDL_BlitSurface(sdl_surface, &src, screen, &dest); + + return ret; +} + +#if 0 +int +SurfaceSDL::draw_stretched(float x, float y, int sw, int sh, Uint8 alpha, bool update) +{ + SDL_Rect dest; + + dest.x = (int)x; + dest.y = (int)y; + dest.w = (int)sw; + dest.h = (int)sh; + + if(alpha != 255) + SDL_SetAlpha(sdl_surface ,SDL_SRCALPHA,alpha); + + + SDL_Surface* sdl_surface_copy = SDL_CreateRGBSurface (sdl_surface->flags, + sw, sh, sdl_surface->format->BitsPerPixel, + sdl_surface->format->Rmask, sdl_surface->format->Gmask, + sdl_surface->format->Bmask, + 0); + + SDL_BlitSurface(sdl_surface, NULL, sdl_surface_copy, NULL); + SDL_SoftStretch(sdl_surface_copy, NULL, sdl_surface_copy, &dest); + + int ret = SDL_BlitSurface(sdl_surface_copy,NULL,screen,&dest); + SDL_FreeSurface(sdl_surface_copy); + + if (update == UPDATE) + update_rect(screen, dest.x, dest.y, dest.w, dest.h); + + return ret; +} +#endif + +SurfaceSDL::~SurfaceSDL() +{} + +/* EOF */ diff --git a/lib/video/surface.h b/lib/video/surface.h new file mode 100644 index 000000000..aa38548b1 --- /dev/null +++ b/lib/video/surface.h @@ -0,0 +1,180 @@ +// $Id$ +// +// SuperTux +// Copyright (C) 2004 Tobias Glaesser +// +// This program is free software; you can redistribute it and/or +// modify it under the terms of the GNU General Public License +// as published by the Free Software Foundation; either version 2 +// of the License, or (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software +// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA +// 02111-1307, USA. + +#ifndef SUPERTUX_TEXTURE_H +#define SUPERTUX_TEXTURE_H + +#include +#include + +#ifndef NOOPENGL +#include "SDL_opengl.h" +#endif + +#include "SDL.h" + +#include "video/screen.h" +#include "math/vector.h" + +SDL_Surface* sdl_surface_from_sdl_surface(SDL_Surface* sdl_surf, bool use_alpha); +SDL_Surface* sdl_surface_from_nothing(); + +class SurfaceImpl; +class SurfaceSDL; +class SurfaceOpenGL; +class DrawingContext; + +/// bitset for drawing effects +enum { + /** Don't apply anything */ + NONE_EFFECT = 0x0000, + /** Draw the Surface upside down */ + VERTICAL_FLIP = 0x0001, + /** Draw the Surface with alpha equal to 128 */ + SEMI_TRANSPARENT = 0x0002 + }; + +/** This class holds all the data necessary to construct a surface */ +class SurfaceData +{ +public: + enum ConstructorType { LOAD, LOAD_PART, SURFACE, GRADIENT }; + ConstructorType type; + SDL_Surface* surface; + std::string file; + bool use_alpha; + int x; + int y; + int w; + int h; + Color top_gradient; + Color bottom_gradient; + + SurfaceData(SDL_Surface* surf, bool use_alpha_); + SurfaceData(const std::string& file_, bool use_alpha_); + SurfaceData(const std::string& file_, int x_, int y_, int w_, int h_, bool use_alpha_); + SurfaceData(Color top_gradient_, Color bottom_gradient_, int w_, int h_); + ~SurfaceData(); + + SurfaceSDL* create_SurfaceSDL(); + SurfaceOpenGL* create_SurfaceOpenGL(); + SurfaceImpl* create(); +}; + +/** Container class that holds a surface, necessary so that we can + switch Surface implementations (OpenGL, SDL) on the fly */ +class Surface +{ +public: + SurfaceData data; + SurfaceImpl* impl; + int w; + int h; + + typedef std::list Surfaces; + static Surfaces surfaces; +public: + static void reload_all(); + static void debug_check(); + + Surface(SDL_Surface* surf, bool use_alpha); + Surface(const std::string& file, bool use_alpha); + Surface(const std::string& file, int x, int y, int w, int h, bool use_alpha); + Surface(Color top_gradient, Color bottom_gradient, int w_, int h_); + ~Surface(); + + /** Reload the surface, which is necesarry in case of a mode swich */ + void reload(); + + void resize(int widht, int height); +}; + +/** Surface implementation, all implementation have to inherit from + this class */ +class SurfaceImpl +{ +protected: + SDL_Surface* sdl_surface; + +public: + int w; + int h; + +public: + SurfaceImpl(); + virtual ~SurfaceImpl(); + + /** Return 0 on success, -2 if surface needs to be reloaded */ + virtual int draw(float x, float y, Uint8 alpha, Uint32 effect = NONE_EFFECT) = 0; + virtual int draw_part(float sx, float sy, float x, float y, float w, float h, Uint8 alpha, Uint32 effect = NONE_EFFECT) = 0; +#if 0 + virtual int draw_stretched(float x, float y, int w, int h, Uint8 alpha, bool update) = 0; +#endif + int resize(int w_, int h_); + + SDL_Surface* get_sdl_surface() const; // @evil@ try to avoid this function +}; + +class SurfaceSDL : public SurfaceImpl +{ +public: + SurfaceSDL(SDL_Surface* surf, bool use_alpha); + SurfaceSDL(const std::string& file, bool use_alpha); + SurfaceSDL(const std::string& file, int x, int y, int w, int h, bool use_alpha); + SurfaceSDL(Color top_gradient, Color bottom_gradient, int w, int h); + virtual ~SurfaceSDL(); + + int draw(float x, float y, Uint8 alpha, Uint32 effect = NONE_EFFECT); + int draw_part(float sx, float sy, float x, float y, float w, float h, Uint8 alpha, Uint32 effect = NONE_EFFECT); +#if 0 + int draw_stretched(float x, float y, int w, int h, Uint8 alpha); +#endif +}; + +#ifndef NOOPENGL +class SurfaceOpenGL : public SurfaceImpl +{ +public: + GLuint gl_texture; + +public: + SurfaceOpenGL(SDL_Surface* surf, bool use_alpha); + SurfaceOpenGL(const std::string& file, bool use_alpha); + SurfaceOpenGL(const std::string& file, int x, int y, int w, int h, bool use_alpha); + SurfaceOpenGL(Color top_gradient, Color bottom_gradient, int w, int h); + + virtual ~SurfaceOpenGL(); + + int draw(float x, float y, Uint8 alpha, Uint32 effect = NONE_EFFECT); + int draw_part(float sx, float sy, float x, float y, float w, float h, Uint8 alpha, Uint32 effect = NONE_EFFECT); +#if 0 + int draw_stretched(float x, float y, int w, int h, Uint8 alpha); +#endif + +private: + void create_gl(SDL_Surface * surf, GLuint * tex); +}; +#endif + +#endif /*SUPERTUX_TEXTURE_H*/ + +/* Local Variables: */ +/* mode: c++ */ +/* End: */ -- 2.11.0