--- /dev/null
+SubDir TOP src ;
+
+SubInclude TOP src squirrel ;
+SubInclude TOP src scripting ;
+
+sources =
+ [ Wildcard *.cpp *.hpp ]
+ [ Wildcard audio : *.cpp *.hpp ]
+ [ Wildcard audio/newapi : *.cpp *.hpp ]
+ [ Wildcard badguy : *.cpp *.hpp ]
+ [ Wildcard binreloc : *.c *.h ]
+ [ Wildcard control : *.cpp *.hpp ]
+ [ Wildcard gui : *.cpp *.hpp ]
+ [ Wildcard lisp : *.cpp *.hpp ]
+ [ Wildcard math : *.cpp *.hpp ]
+ [ Wildcard object : *.cpp *.hpp ]
+ [ Wildcard physfs : *.cpp *.hpp ]
+ [ Wildcard sprite : *.cpp *.hpp ]
+ [ Wildcard tinygettext : *.cpp *.hpp ]
+ [ Wildcard trigger : *.cpp *.hpp ]
+ [ Wildcard video : *.cpp *.hpp ]
+ [ Wildcard worldmap : *.cpp *.hpp ]
+ [ Wildcard obstack : *.c *.h *.hpp ]
+;
+TRANSLATABLE_SOURCES += [ SearchSource $(sources) ] ;
+
+#Application supertux : $(sources) $(wrapper_objects) ;
+Application supertux2 : $(sources) $(wrapper_objects) : linkerfile ;
+C++Flags supertux2 : -DAPPDATADIR=\'\"$(appdatadir)\"\' ;
+LinkWith supertux2 : squirrel ;
+ExternalLibs supertux2 : SDL SDLIMAGE GL OPENAL VORBIS VORBISFILE OGG ICONV PHYSFS BINRELOC LIBCURL ;
+Help supertux2 : "Build the supertux2 executable" ;
+IncludeDir supertux2 : squirrel/include squirrel ;
+Package [ Wildcard scripting : *.cpp *.hpp ] ;
+
--- /dev/null
+// $Id$
+//
+// SuperTux - Add-on
+// Copyright (C) 2007 Christoph Sommer <christoph.sommer@2007.expires.deltadevelopment.de>
+//
+// 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 <sstream>
+#include <stdexcept>
+#include "addon.hpp"
+#include "addon_manager.hpp"
+
+void
+Addon::install()
+{
+ AddonManager& adm = AddonManager::get_instance();
+ adm.install(*this);
+}
+
+void
+Addon::remove()
+{
+ AddonManager& adm = AddonManager::get_instance();
+ adm.remove(*this);
+}
+
+void
+Addon::parse(const lisp::Lisp& lisp)
+{
+ try {
+ lisp.get("kind", kind);
+ lisp.get("title", title);
+ lisp.get("author", author);
+ lisp.get("license", license);
+ lisp.get("http-url", http_url);
+ lisp.get("file", file);
+ lisp.get("md5", md5);
+ } catch(std::exception& e) {
+ std::stringstream msg;
+ msg << "Problem when parsing addoninfo: " << e.what();
+ throw std::runtime_error(msg.str());
+ }
+}
+
+void
+Addon::parse(std::string fname)
+{
+ try {
+ lisp::Parser parser;
+ const lisp::Lisp* root = parser.parse(fname);
+ const lisp::Lisp* addon = root->get_lisp("supertux-addoninfo");
+ if(!addon) throw std::runtime_error("file is not a supertux-addoninfo file.");
+ parse(*addon);
+ } catch(std::exception& e) {
+ std::stringstream msg;
+ msg << "Problem when reading addoninfo '" << fname << "': " << e.what();
+ throw std::runtime_error(msg.str());
+ }
+}
+
+void
+Addon::write(lisp::Writer& writer) const
+{
+ writer.start_list("supertux-addoninfo");
+ if (kind != "") writer.write_string("kind", kind);
+ if (title != "") writer.write_string("title", title);
+ if (author != "") writer.write_string("author", author);
+ if (license != "") writer.write_string("license", license);
+ if (http_url != "") writer.write_string("http-url", http_url);
+ if (file != "") writer.write_string("file", file);
+ if (md5 != "") writer.write_string("md5", md5);
+ writer.end_list("supertux-addoninfo");
+}
+
+void
+Addon::write(std::string fname) const
+{
+ lisp::Writer writer(fname);
+ write(writer);
+}
+
+bool
+Addon::equals(const Addon& addon2) const
+{
+ if ((this->md5 == "") || (addon2.md5 == "")) return (this->title == addon2.title);
+ return (this->md5 == addon2.md5);
+}
+
--- /dev/null
+// $Id$
+//
+// SuperTux - Add-on
+// Copyright (C) 2007 Christoph Sommer <christoph.sommer@2007.expires.deltadevelopment.de>
+//
+// 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 ADDON_H
+#define ADDON_H
+
+#include <string>
+#include <vector>
+#include "lisp/parser.hpp"
+#include "lisp/lisp.hpp"
+#include "lisp/writer.hpp"
+
+/**
+ * Represents an (available or installed) Add-on, e.g. a level set
+ */
+class Addon
+{
+public:
+ std::string kind;
+ std::string title;
+ std::string author;
+ std::string license;
+ std::string http_url;
+ std::string file;
+ std::string md5;
+
+ bool isInstalled;
+
+ /**
+ * Download and install Add-on
+ */
+ void install();
+
+ /**
+ * Physically delete Add-on
+ */
+ void remove();
+
+ /**
+ * Read additional information from given contents of a (supertux-addoninfo ...) block
+ */
+ void parse(const lisp::Lisp& lisp);
+
+ /**
+ * Read additional information from given file
+ */
+ void parse(std::string fname);
+
+ /**
+ * Writes out Add-on metainformation to a Lisp Writer
+ */
+ void write(lisp::Writer& writer) const;
+
+ /**
+ * Writes out Add-on metainformation to a file
+ */
+ void write(std::string fname) const;
+
+ /**
+ * Checks if Add-on is the same as given one.
+ * If available, checks MD5 sum, else relies on title alone.
+ */
+ bool equals(const Addon& addon2) const;
+
+};
+
+#endif
--- /dev/null
+// $Id$
+//
+// SuperTux - Add-on Manager
+// Copyright (C) 2007 Christoph Sommer <christoph.sommer@2007.expires.deltadevelopment.de>
+//
+// 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 <sstream>
+#include <stdexcept>
+#include <list>
+#include <physfs.h>
+#include <sys/stat.h>
+#include <stdio.h>
+#include "addon_manager.hpp"
+#include "config.h"
+#include "log.hpp"
+#include "lisp/parser.hpp"
+#include "lisp/lisp.hpp"
+#include "lisp/list_iterator.hpp"
+
+#ifdef HAVE_LIBCURL
+#include <curl/curl.h>
+#include <curl/types.h>
+#include <curl/easy.h>
+#endif
+
+#ifdef HAVE_LIBCURL
+namespace {
+
+ size_t my_curl_string_append(void *ptr, size_t size, size_t nmemb, void *string_ptr)
+ {
+ std::string& s = *static_cast<std::string*>(string_ptr);
+ std::string buf(static_cast<char*>(ptr), size * nmemb);
+ s += buf;
+ log_debug << "read " << size * nmemb << " bytes of data..." << std::endl;
+ return size * nmemb;
+ }
+
+ size_t my_curl_physfs_write(void *ptr, size_t size, size_t nmemb, void *f_p)
+ {
+ PHYSFS_file* f = static_cast<PHYSFS_file*>(f_p);
+ PHYSFS_sint64 written = PHYSFS_write(f, ptr, size, nmemb);
+ log_debug << "read " << size * nmemb << " bytes of data..." << std::endl;
+ return size * written;
+ }
+
+}
+#endif
+
+AddonManager&
+AddonManager::get_instance()
+{
+ static AddonManager instance;
+ return instance;
+}
+
+AddonManager::AddonManager()
+{
+#ifdef HAVE_LIBCURL
+ curl_global_init(CURL_GLOBAL_ALL);
+#endif
+}
+
+AddonManager::~AddonManager()
+{
+#ifdef HAVE_LIBCURL
+ curl_global_cleanup();
+#endif
+}
+
+std::vector<Addon>
+AddonManager::get_installed_addons() const
+{
+ std::vector<Addon> addons;
+
+ // iterate over complete search path (i.e. directories and archives)
+ char **i = PHYSFS_getSearchPath();
+ if (!i) throw std::runtime_error("Could not query physfs search path");
+ for (; *i != NULL; i++) {
+
+ // get filename of potential archive
+ std::string fileName = *i;
+
+ // make sure it's in the writeDir
+ static const std::string writeDir = PHYSFS_getWriteDir();
+ if (fileName.compare(0, writeDir.length(), writeDir) != 0) continue;
+
+ // make sure it looks like an archive
+ static const std::string archiveExt = ".zip";
+ if (fileName.compare(fileName.length()-archiveExt.length(), archiveExt.length(), archiveExt) != 0) continue;
+
+ // make sure it exists
+ struct stat stats;
+ if (stat(fileName.c_str(), &stats) != 0) continue;
+
+ // make sure it's an actual file
+ if (!S_ISREG(stats.st_mode)) continue;
+
+ Addon addon;
+
+ // extract nice title as fallback for when the Add-on has no addoninfo file
+ static const char* dirSep = PHYSFS_getDirSeparator();
+ std::string::size_type n = fileName.rfind(dirSep) + 1;
+ if (n == std::string::npos) n = 0;
+ addon.title = fileName.substr(n, fileName.length() - n - archiveExt.length());
+ std::string shortFileName = fileName.substr(n, fileName.length() - n);
+ addon.file = shortFileName;
+
+ // read an accompaining .nfo file, if it exists
+ static const std::string infoExt = ".nfo";
+ std::string infoFileName = fileName.substr(n, fileName.length() - n - archiveExt.length()) + infoExt;
+ if (PHYSFS_exists(infoFileName.c_str())) {
+ addon.parse(infoFileName);
+ if (addon.file != shortFileName) {
+ log_warning << "Add-on \"" << addon.title << "\", contained in file \"" << shortFileName << "\" is accompained by an addoninfo file that specifies \"" << addon.file << "\" as the Add-on's file name. Skipping." << std::endl;
+ }
+ }
+
+ // make sure the Add-on's file name does not contain weird characters
+ if (addon.file.find_first_not_of("match.quiz-proxy_gwenblvdjfks0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ") != std::string::npos) {
+ log_warning << "Add-on \"" << addon.title << "\" contains unsafe file name. Skipping." << std::endl;
+ continue;
+ }
+
+ addon.isInstalled = true;
+ addons.push_back(addon);
+ }
+
+ return addons;
+}
+
+std::vector<Addon>
+AddonManager::get_available_addons() const
+{
+ std::vector<Addon> addons;
+
+#ifdef HAVE_LIBCURL
+
+ char error_buffer[CURL_ERROR_SIZE+1];
+
+ const char* baseUrl = "http://supertux.lethargik.org/addons/index.nfo";
+ std::string addoninfos = "";
+
+ CURL *curl_handle;
+ curl_handle = curl_easy_init();
+ curl_easy_setopt(curl_handle, CURLOPT_URL, baseUrl);
+ curl_easy_setopt(curl_handle, CURLOPT_USERAGENT, "SuperTux/" PACKAGE_VERSION " libcURL");
+ curl_easy_setopt(curl_handle, CURLOPT_WRITEFUNCTION, my_curl_string_append);
+ curl_easy_setopt(curl_handle, CURLOPT_WRITEDATA, &addoninfos);
+ curl_easy_setopt(curl_handle, CURLOPT_ERRORBUFFER, error_buffer);
+ curl_easy_setopt(curl_handle, CURLOPT_NOPROGRESS, 1);
+ curl_easy_setopt(curl_handle, CURLOPT_NOSIGNAL, 1);
+ curl_easy_setopt(curl_handle, CURLOPT_FAILONERROR, 1);
+ CURLcode result = curl_easy_perform(curl_handle);
+ curl_easy_cleanup(curl_handle);
+
+ if (result != CURLE_OK) {
+ std::string why = error_buffer[0] ? error_buffer : "unhandled error";
+ throw std::runtime_error("Downloading Add-on list failed: " + why);
+ }
+
+ try {
+ lisp::Parser parser;
+ std::stringstream addoninfos_stream(addoninfos);
+ const lisp::Lisp* root = parser.parse(addoninfos_stream, "supertux-addons");
+
+ const lisp::Lisp* addons_lisp = root->get_lisp("supertux-addons");
+ if(!addons_lisp) throw std::runtime_error("Downloaded file is not an Add-on list");
+
+ lisp::ListIterator iter(addons_lisp);
+ while(iter.next()) {
+ const std::string& token = iter.item();
+ if(token == "supertux-addoninfo") {
+ Addon addon;
+ addon.parse(*(iter.lisp()));
+
+ // make sure the Add-on's file name does not contain weird characters
+ if (addon.file.find_first_not_of("match.quiz-proxy_gwenblvdjfks0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ") != std::string::npos) {
+ log_warning << "Add-on \"" << addon.title << "\" contains unsafe file name. Skipping." << std::endl;
+ continue;
+ }
+
+ addon.isInstalled = false;
+ addons.push_back(addon);
+ } else {
+ log_warning << "Unknown token '" << token << "' in Add-on list" << std::endl;
+ }
+ }
+ } catch(std::exception& e) {
+ std::stringstream msg;
+ msg << "Problem when reading Add-on list: " << e.what();
+ throw std::runtime_error(msg.str());
+ }
+
+#endif
+
+ return addons;
+}
+
+
+void
+AddonManager::install(const Addon& addon)
+{
+ // make sure the Add-on's file name does not contain weird characters
+ if (addon.file.find_first_not_of("match.quiz-proxy_gwenblvdjfks0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ") != std::string::npos) {
+ throw std::runtime_error("Add-on has unsafe file name (\""+addon.file+"\")");
+ }
+
+#ifdef HAVE_LIBCURL
+
+ char error_buffer[CURL_ERROR_SIZE+1];
+
+ char* url = (char*)malloc(addon.http_url.length() + 1);
+ strncpy(url, addon.http_url.c_str(), addon.http_url.length() + 1);
+
+ PHYSFS_file* f = PHYSFS_openWrite(addon.file.c_str());
+
+ log_debug << "Downloading \"" << url << "\"" << std::endl;
+
+ CURL *curl_handle;
+ curl_handle = curl_easy_init();
+ curl_easy_setopt(curl_handle, CURLOPT_URL, url);
+ curl_easy_setopt(curl_handle, CURLOPT_USERAGENT, "SuperTux/" PACKAGE_VERSION " libcURL");
+ curl_easy_setopt(curl_handle, CURLOPT_WRITEFUNCTION, my_curl_physfs_write);
+ curl_easy_setopt(curl_handle, CURLOPT_WRITEDATA, f);
+ curl_easy_setopt(curl_handle, CURLOPT_ERRORBUFFER, error_buffer);
+ curl_easy_setopt(curl_handle, CURLOPT_NOPROGRESS, 1);
+ curl_easy_setopt(curl_handle, CURLOPT_NOSIGNAL, 1);
+ curl_easy_setopt(curl_handle, CURLOPT_FAILONERROR, 1);
+ CURLcode result = curl_easy_perform(curl_handle);
+ curl_easy_cleanup(curl_handle);
+
+ PHYSFS_close(f);
+
+ free(url);
+
+ if (result != CURLE_OK) {
+ PHYSFS_delete(addon.file.c_str());
+ std::string why = error_buffer[0] ? error_buffer : "unhandled error";
+ throw std::runtime_error("Downloading Add-on failed: " + why);
+ }
+
+ // write an accompaining .nfo file
+ static const std::string archiveExt = ".zip";
+ static const std::string infoExt = ".nfo";
+ std::string infoFileName = addon.file.substr(0, addon.file.length()-archiveExt.length()) + infoExt;
+ addon.write(infoFileName);
+
+ static const std::string writeDir = PHYSFS_getWriteDir();
+ static const std::string dirSep = PHYSFS_getDirSeparator();
+ std::string fullFilename = writeDir + dirSep + addon.file;
+ log_debug << "Finished downloading \"" << fullFilename << "\"" << std::endl;
+ PHYSFS_addToSearchPath(fullFilename.c_str(), 1);
+#else
+ (void) addon;
+#endif
+
+}
+
+void
+AddonManager::remove(const Addon& addon)
+{
+ // make sure the Add-on's file name does not contain weird characters
+ if (addon.file.find_first_not_of("match.quiz-proxy_gwenblvdjfks0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ") != std::string::npos) {
+ throw std::runtime_error("Add-on has unsafe file name (\""+addon.file+"\")");
+ }
+
+ log_debug << "deleting file \"" << addon.file << "\"" << std::endl;
+ PHYSFS_removeFromSearchPath(addon.file.c_str());
+ PHYSFS_delete(addon.file.c_str());
+
+ // remove an accompaining .nfo file
+ static const std::string archiveExt = ".zip";
+ static const std::string infoExt = ".nfo";
+ std::string infoFileName = addon.file.substr(0, addon.file.length()-archiveExt.length()) + infoExt;
+ if (PHYSFS_exists(infoFileName.c_str())) {
+ log_debug << "deleting file \"" << infoFileName << "\"" << std::endl;
+ PHYSFS_delete(infoFileName.c_str());
+ }
+}
+
--- /dev/null
+// $Id$
+//
+// SuperTux - Add-on Manager
+// Copyright (C) 2007 Christoph Sommer <christoph.sommer@2007.expires.deltadevelopment.de>
+//
+// 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 ADDON_MANAGER_H
+#define ADDON_MANAGER_H
+
+#include <string>
+#include <vector>
+#include "addon.hpp"
+
+/**
+ * Checks for, installs and removes Add-ons
+ */
+class AddonManager
+{
+public:
+ /**
+ * returns a list of installed Add-ons
+ */
+ std::vector<Addon> get_installed_addons() const;
+
+ /**
+ * returns a list of available Add-ons
+ */
+ std::vector<Addon> get_available_addons() const;
+
+ /**
+ * Download and install Add-on
+ */
+ void install(const Addon& addon);
+
+ /**
+ * Physically delete Add-on
+ */
+ void remove(const Addon& addon);
+
+ static AddonManager& get_instance();
+
+protected:
+ AddonManager();
+ ~AddonManager();
+};
+
+#endif
--- /dev/null
+// $Id$
+//
+// SuperTux
+// Copyright (C) 2006 Matthias Braun <matze@braunis.de>
+//
+// 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 <config.h>
+
+#include "dummy_sound_source.hpp"
+
+class DummySoundSource : public SoundSource
+{
+public:
+ DummySoundSource()
+ {}
+ virtual ~DummySoundSource()
+ {}
+
+ virtual void play()
+ {
+ is_playing = true;
+ }
+
+ virtual void stop()
+ {
+ is_playing = false;
+ }
+
+ virtual bool playing()
+ {
+ return is_playing;
+ }
+
+ virtual void set_looping(bool )
+ {
+ }
+
+ virtual void set_gain(float )
+ {
+ }
+
+ virtual void set_pitch(float )
+ {
+ }
+
+ virtual void set_position(const Vector& )
+ {
+ }
+
+ virtual void set_velocity(const Vector& )
+ {
+ }
+
+ virtual void set_reference_distance(float )
+ {
+ }
+
+ virtual void set_rollof_factor(float )
+ {
+ }
+
+private:
+ bool is_playing;
+};
+
+SoundSource* create_dummy_sound_source()
+{
+ return new DummySoundSource();
+}
--- /dev/null
+// $Id$
+//
+// SuperTux
+// Copyright (C) 2006 Matthias Braun <matze@braunis.de>
+//
+// 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 __DUMMY_SOUND_SOURCE_HPP__
+#define __DUMMY_SOUND_SOURCE_HPP__
+
+#include "sound_source.hpp"
+
+SoundSource* create_dummy_sound_source();
+
+#endif
--- /dev/null
+// $Id$
+//
+// SuperTux
+// Copyright (C) 2006 Matthias Braun <matze@braunis.de>
+//
+// 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 <config.h>
+
+#include "openal_sound_source.hpp"
+#include "sound_manager.hpp"
+
+OpenALSoundSource::OpenALSoundSource()
+{
+ alGenSources(1, &source);
+ SoundManager::check_al_error("Couldn't create audio source: ");
+ set_reference_distance(128);
+}
+
+OpenALSoundSource::~OpenALSoundSource()
+{
+ stop();
+ alDeleteSources(1, &source);
+}
+
+void
+OpenALSoundSource::stop()
+{
+ alSourceStop(source);
+ alSourcei(source, AL_BUFFER, AL_NONE);
+ SoundManager::check_al_error("Problem stopping audio source: ");
+}
+
+void
+OpenALSoundSource::play()
+{
+ alSourcePlay(source);
+ SoundManager::check_al_error("Couldn't start audio source: ");
+}
+
+bool
+OpenALSoundSource::playing()
+{
+ ALint state = AL_PLAYING;
+ alGetSourcei(source, AL_SOURCE_STATE, &state);
+ return state != AL_STOPPED;
+}
+
+void
+OpenALSoundSource::update()
+{
+}
+
+void
+OpenALSoundSource::set_looping(bool looping)
+{
+ alSourcei(source, AL_LOOPING, looping ? AL_TRUE : AL_FALSE);
+}
+
+void
+OpenALSoundSource::set_position(const Vector& position)
+{
+ alSource3f(source, AL_POSITION, position.x, position.y, 0);
+}
+
+void
+OpenALSoundSource::set_velocity(const Vector& velocity)
+{
+ alSource3f(source, AL_VELOCITY, velocity.x, velocity.y, 0);
+}
+
+void
+OpenALSoundSource::set_gain(float gain)
+{
+ alSourcef(source, AL_GAIN, gain);
+}
+
+void
+OpenALSoundSource::set_pitch(float pitch)
+{
+ alSourcef(source, AL_PITCH, pitch);
+}
+
+void
+OpenALSoundSource::set_reference_distance(float distance)
+{
+ alSourcef(source, AL_REFERENCE_DISTANCE, distance);
+}
+
+void
+OpenALSoundSource::set_rollof_factor(float factor)
+{
+ alSourcef(source, AL_ROLLOFF_FACTOR, factor);
+}
--- /dev/null
+// $Id$
+//
+// SuperTux
+// Copyright (C) 2006 Matthias Braun <matze@braunis.de>
+//
+// 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 __OPENAL_SOUND_SOURCE_H__
+#define __OPENAL_SOUND_SOURCE_H__
+
+#ifndef MACOSX
+#include <AL/al.h>
+#else
+#include <OpenAL/al.h>
+#endif
+
+#include "math/vector.hpp"
+#include "sound_source.hpp"
+
+class OpenALSoundSource : public SoundSource
+{
+public:
+ OpenALSoundSource();
+ virtual ~OpenALSoundSource();
+
+ virtual void play();
+ virtual void stop();
+ virtual bool playing();
+
+ virtual void update();
+
+ virtual void set_looping(bool looping);
+ virtual void set_gain(float gain);
+ virtual void set_pitch(float pitch);
+ virtual void set_position(const Vector& position);
+ virtual void set_velocity(const Vector& position);
+ virtual void set_reference_distance(float distance);
+ virtual void set_rollof_factor(float factor);
+
+protected:
+ friend class SoundManager;
+
+ ALuint source;
+};
+
+#endif
--- /dev/null
+// $Id$
+//
+// SuperTux
+// Copyright (C) 2006 Matthias Braun <matze@braunis.de>
+//
+// 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.
+
+/** Used SDL_mixer and glest source as reference */
+#include <config.h>
+
+#include "sound_file.hpp"
+
+#include <stdio.h>
+#include <stdint.h>
+#include <algorithm>
+#include <stdexcept>
+#include <sstream>
+#include <assert.h>
+#include <physfs.h>
+#include <vorbis/codec.h>
+#include <vorbis/vorbisfile.h>
+#include "log.hpp"
+#include "lisp/parser.hpp"
+#include "lisp/lisp.hpp"
+#include "file_system.hpp"
+
+class WavSoundFile : public SoundFile
+{
+public:
+ WavSoundFile(PHYSFS_file* file);
+ ~WavSoundFile();
+
+ size_t read(void* buffer, size_t buffer_size);
+ void reset();
+
+private:
+ PHYSFS_file* file;
+
+ PHYSFS_sint64 datastart;
+};
+
+static inline uint32_t read32LE(PHYSFS_file* file)
+{
+ uint32_t result;
+ if(PHYSFS_readULE32(file, &result) == 0)
+ throw std::runtime_error("file too short");
+
+ return result;
+}
+
+static inline uint16_t read16LE(PHYSFS_file* file)
+{
+ uint16_t result;
+ if(PHYSFS_readULE16(file, &result) == 0)
+ throw std::runtime_error("file too short");
+
+ return result;
+}
+
+WavSoundFile::WavSoundFile(PHYSFS_file* file)
+{
+ this->file = file;
+
+ char magic[4];
+ if(PHYSFS_read(file, magic, sizeof(magic), 1) != 1)
+ throw std::runtime_error("Couldn't read file magic (not a wave file)");
+ if(strncmp(magic, "RIFF", 4) != 0) {
+ log_debug << "MAGIC: " << magic << std::endl;
+ throw std::runtime_error("file is not a RIFF wav file");
+ }
+
+ uint32_t wavelen = read32LE(file);
+ (void) wavelen;
+
+ if(PHYSFS_read(file, magic, sizeof(magic), 1) != 1)
+ throw std::runtime_error("Couldn't read chunk header (not a wav file?)");
+ if(strncmp(magic, "WAVE", 4) != 0)
+ throw std::runtime_error("file is not a valid RIFF/WAVE file");
+
+ char chunkmagic[4];
+ uint32_t chunklen;
+
+ // search audio data format chunk
+ do {
+ if(PHYSFS_read(file, chunkmagic, sizeof(chunkmagic), 1) != 1)
+ throw std::runtime_error("EOF while searching format chunk");
+ chunklen = read32LE(file);
+
+ if(strncmp(chunkmagic, "fmt ", 4) == 0)
+ break;
+
+ if(strncmp(chunkmagic, "fact", 4) == 0
+ || strncmp(chunkmagic, "LIST", 4) == 0) {
+ // skip chunk
+ if(PHYSFS_seek(file, PHYSFS_tell(file) + chunklen) == 0)
+ throw std::runtime_error("EOF while searching fmt chunk");
+ } else {
+ throw std::runtime_error("complex WAVE files not supported");
+ }
+ } while(true);
+
+ if(chunklen < 16)
+ throw std::runtime_error("Format chunk too short");
+
+ // parse format
+ uint16_t encoding = read16LE(file);
+ if(encoding != 1)
+ throw std::runtime_error("only PCM encoding supported");
+ channels = read16LE(file);
+ rate = read32LE(file);
+ uint32_t byterate = read32LE(file);
+ (void) byterate;
+ uint16_t blockalign = read16LE(file);
+ (void) blockalign;
+ bits_per_sample = read16LE(file);
+
+ if(chunklen > 16) {
+ if(PHYSFS_seek(file, PHYSFS_tell(file) + (chunklen-16)) == 0)
+ throw std::runtime_error("EOF while reading reast of format chunk");
+ }
+
+ // set file offset to DATA chunk data
+ do {
+ if(PHYSFS_read(file, chunkmagic, sizeof(chunkmagic), 1) != 1)
+ throw std::runtime_error("EOF while searching data chunk");
+ chunklen = read32LE(file);
+
+ if(strncmp(chunkmagic, "data", 4) == 0)
+ break;
+
+ // skip chunk
+ if(PHYSFS_seek(file, PHYSFS_tell(file) + chunklen) == 0)
+ throw std::runtime_error("EOF while searching fmt chunk");
+ } while(true);
+
+ datastart = PHYSFS_tell(file);
+ size = static_cast<size_t> (chunklen);
+}
+
+WavSoundFile::~WavSoundFile()
+{
+ PHYSFS_close(file);
+}
+
+void
+WavSoundFile::reset()
+{
+ if(PHYSFS_seek(file, datastart) == 0)
+ throw std::runtime_error("Couldn't seek to data start");
+}
+
+size_t
+WavSoundFile::read(void* buffer, size_t buffer_size)
+{
+ PHYSFS_sint64 end = datastart + size;
+ PHYSFS_sint64 cur = PHYSFS_tell(file);
+ if(cur >= end)
+ return 0;
+
+ size_t readsize = std::min(static_cast<size_t> (end - cur), buffer_size);
+ if(PHYSFS_read(file, buffer, readsize, 1) != 1)
+ throw std::runtime_error("read error while reading samples");
+
+#ifdef WORDS_BIGENDIAN
+ if (bits_per_sample != 16)
+ return readsize;
+ char *tmp = (char*)buffer;
+
+ size_t i;
+ char c;
+ for (i = 0; i < readsize / 2; i++)
+ {
+ c = tmp[2*i];
+ tmp[2*i] = tmp[2*i+1];
+ tmp[2*i+1] = c;
+ }
+
+ buffer = tmp;
+#endif
+
+ return readsize;
+}
+
+//---------------------------------------------------------------------------
+
+class OggSoundFile : public SoundFile
+{
+public:
+ OggSoundFile(PHYSFS_file* file, double loop_begin, double loop_at);
+ ~OggSoundFile();
+
+ size_t read(void* buffer, size_t buffer_size);
+ void reset();
+
+private:
+ static size_t cb_read(void* ptr, size_t size, size_t nmemb, void* source);
+ static int cb_seek(void* source, ogg_int64_t offset, int whence);
+ static int cb_close(void* source);
+ static long cb_tell(void* source);
+
+ PHYSFS_file* file;
+ OggVorbis_File vorbis_file;
+ ogg_int64_t loop_begin;
+ ogg_int64_t loop_at;
+ size_t normal_buffer_loop;
+};
+
+OggSoundFile::OggSoundFile(PHYSFS_file* file, double loop_begin, double loop_at)
+{
+ this->file = file;
+
+ ov_callbacks callbacks = { cb_read, cb_seek, cb_close, cb_tell };
+ ov_open_callbacks(file, &vorbis_file, 0, 0, callbacks);
+
+ vorbis_info* vi = ov_info(&vorbis_file, -1);
+
+ channels = vi->channels;
+ rate = vi->rate;
+ bits_per_sample = 16;
+ size = static_cast<size_t> (ov_pcm_total(&vorbis_file, -1) * 2);
+
+ double sample_len = 1.0f / rate;
+ double samples_begin = loop_begin / sample_len;
+ double sample_loop = loop_at / sample_len;
+
+ this->loop_begin = (ogg_int64_t) samples_begin;
+ if(loop_begin < 0) {
+ this->loop_at = (ogg_int64_t) -1;
+ } else {
+ this->loop_at = (ogg_int64_t) sample_loop;
+ }
+}
+
+OggSoundFile::~OggSoundFile()
+{
+ ov_clear(&vorbis_file);
+}
+
+size_t
+OggSoundFile::read(void* _buffer, size_t buffer_size)
+{
+ char* buffer = reinterpret_cast<char*> (_buffer);
+ int section = 0;
+ size_t totalBytesRead = 0;
+
+ while(buffer_size>0) {
+#ifdef WORDS_BIGENDIAN
+ int bigendian = 1;
+#else
+ int bigendian = 0;
+#endif
+
+ size_t bytes_to_read = buffer_size;
+ if(loop_at > 0) {
+ size_t bytes_per_sample = 2;
+ ogg_int64_t time = ov_pcm_tell(&vorbis_file);
+ ogg_int64_t samples_left_till_loop = loop_at - time;
+ ogg_int64_t bytes_left_till_loop
+ = samples_left_till_loop * bytes_per_sample;
+ if(bytes_left_till_loop <= 4)
+ break;
+
+ if(bytes_left_till_loop < (ogg_int64_t) bytes_to_read) {
+ bytes_to_read = (size_t) bytes_left_till_loop;
+ }
+ }
+
+ long bytesRead
+ = ov_read(&vorbis_file, buffer, bytes_to_read, bigendian,
+ 2, 1, §ion);
+ if(bytesRead == 0) {
+ break;
+ }
+ buffer_size -= bytesRead;
+ buffer += bytesRead;
+ totalBytesRead += bytesRead;
+ }
+
+ return totalBytesRead;
+}
+
+void
+OggSoundFile::reset()
+{
+ ov_pcm_seek(&vorbis_file, loop_begin);
+}
+
+size_t
+OggSoundFile::cb_read(void* ptr, size_t size, size_t nmemb, void* source)
+{
+ PHYSFS_file* file = reinterpret_cast<PHYSFS_file*> (source);
+
+ PHYSFS_sint64 res
+ = PHYSFS_read(file, ptr, static_cast<PHYSFS_uint32> (size),
+ static_cast<PHYSFS_uint32> (nmemb));
+ if(res <= 0)
+ return 0;
+
+ return static_cast<size_t> (res);
+}
+
+int
+OggSoundFile::cb_seek(void* source, ogg_int64_t offset, int whence)
+{
+ PHYSFS_file* file = reinterpret_cast<PHYSFS_file*> (source);
+
+ switch(whence) {
+ case SEEK_SET:
+ if(PHYSFS_seek(file, static_cast<PHYSFS_uint64> (offset)) == 0)
+ return -1;
+ break;
+ case SEEK_CUR:
+ if(PHYSFS_seek(file, PHYSFS_tell(file) + offset) == 0)
+ return -1;
+ break;
+ case SEEK_END:
+ if(PHYSFS_seek(file, PHYSFS_fileLength(file) + offset) == 0)
+ return -1;
+ break;
+ default:
+#ifdef DEBUG
+ assert(false);
+#else
+ return -1;
+#endif
+ }
+ return 0;
+}
+
+int
+OggSoundFile::cb_close(void* source)
+{
+ PHYSFS_file* file = reinterpret_cast<PHYSFS_file*> (source);
+ PHYSFS_close(file);
+ return 0;
+}
+
+long
+OggSoundFile::cb_tell(void* source)
+{
+ PHYSFS_file* file = reinterpret_cast<PHYSFS_file*> (source);
+ return static_cast<long> (PHYSFS_tell(file));
+}
+
+//---------------------------------------------------------------------------
+
+SoundFile* load_music_file(const std::string& filename)
+{
+ lisp::Parser parser(false);
+ const lisp::Lisp* root = parser.parse(filename);
+ const lisp::Lisp* music = root->get_lisp("supertux-music");
+ if(music == NULL)
+ throw std::runtime_error("file is not a supertux-music file.");
+
+ std::string raw_music_file;
+ float loop_begin = 0;
+ float loop_at = -1;
+
+ music->get("file", raw_music_file);
+ music->get("loop-begin", loop_begin);
+ music->get("loop-at", loop_at);
+
+ if(loop_begin < 0) {
+ throw std::runtime_error("can't loop from negative value");
+ }
+
+ std::string basedir = FileSystem::dirname(filename);
+ raw_music_file = FileSystem::normalize(basedir + raw_music_file);
+
+ PHYSFS_file* file = PHYSFS_openRead(raw_music_file.c_str());
+ if(!file) {
+ std::stringstream msg;
+ msg << "Couldn't open '" << raw_music_file << "': " << PHYSFS_getLastError();
+ throw std::runtime_error(msg.str());
+ }
+
+ return new OggSoundFile(file, loop_begin, loop_at);
+}
+
+SoundFile* load_sound_file(const std::string& filename)
+{
+ if(filename.length() > 6
+ && filename.compare(filename.length()-6, 6, ".music") == 0) {
+ return load_music_file(filename);
+ }
+
+ PHYSFS_file* file = PHYSFS_openRead(filename.c_str());
+ if(!file) {
+ std::stringstream msg;
+ msg << "Couldn't open '" << filename << "': " << PHYSFS_getLastError();
+ throw std::runtime_error(msg.str());
+ }
+
+ try {
+ char magic[4];
+ if(PHYSFS_read(file, magic, sizeof(magic), 1) != 1)
+ throw std::runtime_error("Couldn't read magic, file too short");
+ PHYSFS_seek(file, 0);
+ if(strncmp(magic, "RIFF", 4) == 0)
+ return new WavSoundFile(file);
+ else if(strncmp(magic, "OggS", 4) == 0)
+ return new OggSoundFile(file, 0, -1);
+ else
+ throw std::runtime_error("Unknown file format");
+ } catch(std::exception& e) {
+ std::stringstream msg;
+ msg << "Couldn't read '" << filename << "': " << e.what();
+ throw std::runtime_error(msg.str());
+ }
+}
--- /dev/null
+// $Id$
+//
+// SuperTux
+// Copyright (C) 2006 Matthias Braun <matze@braunis.de>
+//
+// 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 __SOUND_FILE_H__
+#define __SOUND_FILE_H__
+
+#include <stdio.h>
+#include <iostream>
+
+class SoundFile
+{
+public:
+ virtual ~SoundFile()
+ { }
+
+ virtual size_t read(void* buffer, size_t buffer_size) = 0;
+ virtual void reset() = 0;
+
+ int channels;
+ int rate;
+ int bits_per_sample;
+ /// size in bytes
+ size_t size;
+};
+
+SoundFile* load_sound_file(const std::string& filename);
+
+#endif
--- /dev/null
+// $Id$
+//
+// SuperTux
+// Copyright (C) 2006 Matthias Braun <matze@braunis.de>
+//
+// 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 <config.h>
+
+#include "sound_manager.hpp"
+
+#include <stdexcept>
+#include <iostream>
+#include <sstream>
+#include <memory>
+#include <assert.h>
+#include <SDL.h>
+
+#include "sound_file.hpp"
+#include "sound_source.hpp"
+#include "openal_sound_source.hpp"
+#include "stream_sound_source.hpp"
+#include "dummy_sound_source.hpp"
+#include "log.hpp"
+#include "timer.hpp"
+
+#ifndef DEBUG
+ /** Older openal versions often miss this function and it isn't that vital for
+ * supertux...
+ */
+#ifdef alcGetString
+#undef alcGetString
+#endif
+#define alcGetString(x,y) ""
+#endif
+
+SoundManager* sound_manager = 0;
+
+SoundManager::SoundManager()
+ : device(0), context(0), sound_enabled(false), music_source(0),
+ music_enabled(false)
+{
+ try {
+ device = alcOpenDevice(0);
+ if (device == NULL) {
+ throw std::runtime_error("Couldn't open audio device.");
+ }
+
+ int attributes[] = { 0 };
+ context = alcCreateContext(device, attributes);
+ check_alc_error("Couldn't create audio context: ");
+ alcMakeContextCurrent(context);
+ check_alc_error("Couldn't select audio context: ");
+
+ check_al_error("Audio error after init: ");
+ sound_enabled = true;
+ music_enabled = true;
+ } catch(std::exception& e) {
+ if(context != NULL)
+ alcDestroyContext(context);
+ context = NULL;
+ if(device != NULL)
+ alcCloseDevice(device);
+ device = NULL;
+ log_warning << "Couldn't initialize audio device: " << e.what() << std::endl;
+ print_openal_version();
+ }
+}
+
+SoundManager::~SoundManager()
+{
+ delete music_source;
+
+ for(SoundSources::iterator i = sources.begin(); i != sources.end(); ++i) {
+ delete *i;
+ }
+
+ for(SoundBuffers::iterator i = buffers.begin(); i != buffers.end(); ++i) {
+ ALuint buffer = i->second;
+ alDeleteBuffers(1, &buffer);
+ }
+
+ if(context != NULL) {
+ alcDestroyContext(context);
+ }
+ if(device != NULL) {
+ alcCloseDevice(device);
+ }
+}
+
+ALuint
+SoundManager::load_file_into_buffer(SoundFile* file)
+{
+ ALenum format = get_sample_format(file);
+ ALuint buffer;
+ alGenBuffers(1, &buffer);
+ check_al_error("Couldn't create audio buffer: ");
+ char* samples = new char[file->size];
+ try {
+ file->read(samples, file->size);
+ alBufferData(buffer, format, samples,
+ static_cast<ALsizei> (file->size),
+ static_cast<ALsizei> (file->rate));
+ check_al_error("Couldn't fill audio buffer: ");
+ } catch(...) {
+ delete[] samples;
+ throw;
+ }
+ delete[] samples;
+
+ return buffer;
+}
+
+OpenALSoundSource*
+SoundManager::intern_create_sound_source(const std::string& filename)
+{
+ if(!sound_enabled)
+ throw std::runtime_error("sound disabled");
+
+ std::auto_ptr<OpenALSoundSource> source (new OpenALSoundSource());
+
+ ALuint buffer;
+
+ // reuse an existing static sound buffer
+ SoundBuffers::iterator i = buffers.find(filename);
+ if(i != buffers.end()) {
+ buffer = i->second;
+ } else {
+ // Load sound file
+ std::auto_ptr<SoundFile> file (load_sound_file(filename));
+
+ if(file->size < 100000) {
+ buffer = load_file_into_buffer(file.get());
+ buffers.insert(std::make_pair(filename, buffer));
+ } else {
+ StreamSoundSource* source = new StreamSoundSource();
+ source->set_sound_file(file.release());
+ return source;
+ }
+ }
+
+ alSourcei(source->source, AL_BUFFER, buffer);
+ return source.release();
+}
+
+SoundSource*
+SoundManager::create_sound_source(const std::string& filename)
+{
+ if(!sound_enabled)
+ return create_dummy_sound_source();
+
+ try {
+ return intern_create_sound_source(filename);
+ } catch(std::exception &e) {
+ log_warning << "Couldn't create audio source: " << e.what() << std::endl;
+ return create_dummy_sound_source();
+ }
+}
+
+void
+SoundManager::preload(const std::string& filename)
+{
+ if(!sound_enabled)
+ return;
+
+ SoundBuffers::iterator i = buffers.find(filename);
+ // already loaded?
+ if(i != buffers.end())
+ return;
+
+ std::auto_ptr<SoundFile> file (load_sound_file(filename));
+ // only keep small files
+ if(file->size >= 100000)
+ return;
+
+ ALuint buffer = load_file_into_buffer(file.get());
+ buffers.insert(std::make_pair(filename, buffer));
+}
+
+void
+SoundManager::play(const std::string& filename, const Vector& pos)
+{
+ if(!sound_enabled)
+ return;
+
+ try {
+ std::auto_ptr<OpenALSoundSource> source
+ (intern_create_sound_source(filename));
+
+ if(pos == Vector(-1, -1)) {
+ source->set_rollof_factor(0);
+ } else {
+ source->set_position(pos);
+ }
+ source->play();
+ sources.push_back(source.release());
+ } catch(std::exception& e) {
+ log_warning << "Couldn't play sound " << filename << ": " << e.what() << std::endl;
+ }
+}
+
+void
+SoundManager::manage_source(SoundSource* source)
+{
+ assert(source != NULL);
+
+ OpenALSoundSource* openal_source = dynamic_cast<OpenALSoundSource*> (source);
+ if(openal_source != NULL) {
+ sources.push_back(openal_source);
+ }
+}
+
+void
+SoundManager::register_for_update( StreamSoundSource* sss ){
+ if( sss != NULL ){
+ update_list.push_back( sss );
+ }
+}
+
+void
+SoundManager::remove_from_update( StreamSoundSource* sss ){
+ if( sss != NULL ){
+ StreamSoundSources::iterator i = update_list.begin();
+ while( i != update_list.end() ){
+ if( *i == sss ){
+ i = update_list.erase(i);
+ } else {
+ i++;
+ }
+ }
+ }
+}
+
+void
+SoundManager::enable_sound(bool enable)
+{
+ if(device == NULL)
+ return;
+
+ sound_enabled = enable;
+}
+
+void
+SoundManager::enable_music(bool enable)
+{
+ if(device == NULL)
+ return;
+
+ music_enabled = enable;
+ if(music_enabled) {
+ play_music(current_music);
+ } else {
+ if(music_source) {
+ delete music_source;
+ music_source = 0;
+ }
+ }
+}
+
+void
+SoundManager::stop_music(float fadetime)
+{
+ if(fadetime > 0) {
+ if(music_source
+ && music_source->get_fade_state() != StreamSoundSource::FadingOff)
+ music_source->set_fading(StreamSoundSource::FadingOff, fadetime);
+ } else {
+ delete music_source;
+ music_source = NULL;
+ }
+ current_music = "";
+}
+
+void
+SoundManager::play_music(const std::string& filename, bool fade)
+{
+ if(filename == current_music && music_source != NULL)
+ return;
+ current_music = filename;
+ if(!music_enabled)
+ return;
+
+ if(filename == "") {
+ delete music_source;
+ music_source = NULL;
+ return;
+ }
+
+ try {
+ std::auto_ptr<StreamSoundSource> newmusic (new StreamSoundSource());
+ alSourcef(newmusic->source, AL_ROLLOFF_FACTOR, 0);
+ newmusic->set_sound_file(load_sound_file(filename));
+ newmusic->set_looping(true);
+ if(fade)
+ newmusic->set_fading(StreamSoundSource::FadingOn, .5f);
+ newmusic->play();
+
+ delete music_source;
+ music_source = newmusic.release();
+ } catch(std::exception& e) {
+ log_warning << "Couldn't play music file '" << filename << "': " << e.what() << std::endl;
+ }
+}
+
+void
+SoundManager::set_listener_position(const Vector& pos)
+{
+ static Uint32 lastticks = SDL_GetTicks();
+
+ Uint32 current_ticks = SDL_GetTicks();
+ if(current_ticks - lastticks < 300)
+ return;
+ lastticks = current_ticks;
+
+ alListener3f(AL_POSITION, pos.x, pos.y, 0);
+}
+
+void
+SoundManager::set_listener_velocity(const Vector& vel)
+{
+ alListener3f(AL_VELOCITY, vel.x, vel.y, 0);
+}
+
+void
+SoundManager::update()
+{
+ static Uint32 lasttime = SDL_GetTicks();
+ Uint32 now = SDL_GetTicks();
+
+ if(now - lasttime < 300)
+ return;
+ lasttime = now;
+
+ // update and check for finished sound sources
+ for(SoundSources::iterator i = sources.begin(); i != sources.end(); ) {
+ OpenALSoundSource* source = *i;
+
+ source->update();
+
+ if(!source->playing()) {
+ delete source;
+ i = sources.erase(i);
+ } else {
+ ++i;
+ }
+ }
+ // check streaming sounds
+ if(music_source) {
+ music_source->update();
+ }
+
+ if (context)
+ {
+ alcProcessContext(context);
+ check_alc_error("Error while processing audio context: ");
+ }
+
+ //run update() for stream_sound_source
+ StreamSoundSources::iterator s = update_list.begin();
+ while( s != update_list.end() ){
+ (*s)->update();
+ s++;
+ }
+}
+
+ALenum
+SoundManager::get_sample_format(SoundFile* file)
+{
+ if(file->channels == 2) {
+ if(file->bits_per_sample == 16) {
+ return AL_FORMAT_STEREO16;
+ } else if(file->bits_per_sample == 8) {
+ return AL_FORMAT_STEREO8;
+ } else {
+ throw std::runtime_error("Only 16 and 8 bit samples supported");
+ }
+ } else if(file->channels == 1) {
+ if(file->bits_per_sample == 16) {
+ return AL_FORMAT_MONO16;
+ } else if(file->bits_per_sample == 8) {
+ return AL_FORMAT_MONO8;
+ } else {
+ throw std::runtime_error("Only 16 and 8 bit samples supported");
+ }
+ }
+
+ throw std::runtime_error("Only 1 and 2 channel samples supported");
+}
+
+void
+SoundManager::print_openal_version()
+{
+ log_info << "OpenAL Vendor: " << alGetString(AL_VENDOR) << std::endl;
+ log_info << "OpenAL Version: " << alGetString(AL_VERSION) << std::endl;
+ log_info << "OpenAL Renderer: " << alGetString(AL_RENDERER) << std::endl;
+ log_info << "OpenAl Extensions: " << alGetString(AL_EXTENSIONS) << std::endl;
+}
+
+void
+SoundManager::check_alc_error(const char* message)
+{
+ int err = alcGetError(device);
+ if(err != ALC_NO_ERROR) {
+ std::stringstream msg;
+ msg << message << alcGetString(device, err);
+ throw std::runtime_error(msg.str());
+ }
+}
+
+void
+SoundManager::check_al_error(const char* message)
+{
+ int err = alGetError();
+ if(err != AL_NO_ERROR) {
+ std::stringstream msg;
+ msg << message << alGetString(err);
+ throw std::runtime_error(msg.str());
+ }
+}
--- /dev/null
+// $Id$
+//
+// SuperTux
+// Copyright (C) 2006 Matthias Braun <matze@braunis.de>
+//
+// 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 __SOUND_MANAGER_H__
+#define __SOUND_MANAGER_H__
+
+#include <string>
+#include <vector>
+#include <map>
+
+#ifndef MACOSX
+#include <AL/alc.h>
+#include <AL/al.h>
+#else
+#include <OpenAL/alc.h>
+#include <OpenAL/al.h>
+#endif
+
+#include "math/vector.hpp"
+
+class SoundFile;
+class SoundSource;
+class StreamSoundSource;
+class OpenALSoundSource;
+
+class SoundManager
+{
+public:
+ SoundManager();
+ virtual ~SoundManager();
+
+ void enable_sound(bool sound_enabled);
+ /**
+ * Creates a new sound source object which plays the specified soundfile.
+ * You are responsible for deleting the sound source later (this will stop the
+ * sound).
+ * This function never throws exceptions, but might return a DummySoundSource
+ */
+ SoundSource* create_sound_source(const std::string& filename);
+ /**
+ * Convenience function to simply play a sound at a given position.
+ */
+ void play(const std::string& name, const Vector& pos = Vector(-1, -1));
+ /**
+ * Adds the source to the list of managed sources (= the source gets deleted
+ * when it finished playing)
+ */
+ void manage_source(SoundSource* source);
+ /// preloads a sound, so that you don't get a lag later when playing it
+ void preload(const std::string& name);
+
+ void set_listener_position(const Vector& position);
+ void set_listener_velocity(const Vector& velocity);
+
+ void enable_music(bool music_enabled);
+ void play_music(const std::string& filename, bool fade = false);
+ void stop_music(float fadetime = 0);
+
+ bool is_music_enabled() { return music_enabled; }
+ bool is_sound_enabled() { return sound_enabled; }
+
+ bool is_audio_enabled() {
+ return device != 0 && context != 0;
+ }
+
+ void update();
+
+ /*
+ * Tell soundmanager to call update() for stream_sound_source.
+ */
+ void register_for_update( StreamSoundSource* sss );
+ /*
+ * Unsubscribe from updates for stream_sound_source.
+ */
+ void remove_from_update( StreamSoundSource* sss );
+
+private:
+ friend class OpenALSoundSource;
+ friend class StreamSoundSource;
+
+ /** creates a new sound source, might throw exceptions, never returns NULL */
+ OpenALSoundSource* intern_create_sound_source(const std::string& filename);
+ static ALuint load_file_into_buffer(SoundFile* file);
+ static ALenum get_sample_format(SoundFile* file);
+
+ void print_openal_version();
+ void check_alc_error(const char* message);
+ static void check_al_error(const char* message);
+
+ ALCdevice* device;
+ ALCcontext* context;
+ bool sound_enabled;
+
+ typedef std::map<std::string, ALuint> SoundBuffers;
+ SoundBuffers buffers;
+ typedef std::vector<OpenALSoundSource*> SoundSources;
+ SoundSources sources;
+
+ typedef std::vector<StreamSoundSource*> StreamSoundSources;
+ StreamSoundSources update_list;
+
+ StreamSoundSource* music_source;
+
+ bool music_enabled;
+ std::string current_music;
+};
+
+extern SoundManager* sound_manager;
+
+#endif
--- /dev/null
+// $Id$
+//
+// SuperTux
+// Copyright (C) 2006 Matthias Braun <matze@braunis.de>
+//
+// 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 __SOUND_SOURCE_H__
+#define __SOUND_SOURCE_H__
+
+#include "math/vector.hpp"
+
+/**
+ * A sound source represents the source of audio output. You can place
+ * sources at certain points in your world or set their velocity to produce
+ * doppler effects
+ */
+class SoundSource
+{
+public:
+ virtual ~SoundSource()
+ { }
+
+ virtual void play() = 0;
+ virtual void stop() = 0;
+ virtual bool playing() = 0;
+
+ virtual void set_looping(bool looping) = 0;
+ /// Set volume (0.0 is silent, 1.0 is normal)
+ virtual void set_gain(float gain) = 0;
+ virtual void set_pitch(float pitch) = 0;
+ virtual void set_position(const Vector& position) = 0;
+ virtual void set_velocity(const Vector& position) = 0;
+ virtual void set_reference_distance(float distance) = 0;
+ virtual void set_rollof_factor(float factor) = 0;
+};
+
+#endif
--- /dev/null
+// $Id$
+//
+// SuperTux
+// Copyright (C) 2006 Matthias Braun <matze@braunis.de>
+//
+// 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 <config.h>
+#include <assert.h>
+
+#include <SDL.h>
+
+#include "stream_sound_source.hpp"
+#include "sound_manager.hpp"
+#include "sound_file.hpp"
+#include "timer.hpp"
+#include "log.hpp"
+
+StreamSoundSource::StreamSoundSource()
+ : file(0), fade_state(NoFading), looping(false)
+{
+ alGenBuffers(STREAMFRAGMENTS, buffers);
+ SoundManager::check_al_error("Couldn't allocate audio buffers: ");
+ //add me to update list
+ sound_manager->register_for_update( this );
+}
+
+StreamSoundSource::~StreamSoundSource()
+{
+ //don't update me any longer
+ sound_manager->remove_from_update( this );
+ delete file;
+ stop();
+ alDeleteBuffers(STREAMFRAGMENTS, buffers);
+ SoundManager::check_al_error("Couldn't delete audio buffers: ");
+}
+
+void
+StreamSoundSource::set_sound_file(SoundFile* newfile)
+{
+ delete file;
+ file = newfile;
+
+ ALint queued;
+ alGetSourcei(source, AL_BUFFERS_QUEUED, &queued);
+ for(size_t i = 0; i < STREAMFRAGMENTS - queued; ++i) {
+ if(fillBufferAndQueue(buffers[i]) == false)
+ break;
+ }
+}
+
+void
+StreamSoundSource::update()
+{
+ ALint processed = 0;
+ alGetSourcei(source, AL_BUFFERS_PROCESSED, &processed);
+ for(ALint i = 0; i < processed; ++i) {
+ ALuint buffer;
+ alSourceUnqueueBuffers(source, 1, &buffer);
+ SoundManager::check_al_error("Couldn't unqueu audio buffer: ");
+
+ if(fillBufferAndQueue(buffer) == false)
+ break;
+ }
+
+ if(!playing()) {
+ if(processed == 0 || !looping)
+ return;
+
+ // we might have to restart the source if we had a buffer underrun
+ log_info << "Restarting audio source because of buffer underrun" << std::endl;
+ play();
+ }
+
+ if(fade_state == FadingOn) {
+ float time = real_time - fade_start_time;
+ if(time >= fade_time) {
+ set_gain(1.0);
+ fade_state = NoFading;
+ } else {
+ set_gain(time / fade_time);
+ }
+ } else if(fade_state == FadingOff) {
+ float time = real_time - fade_start_time;
+ if(time >= fade_time) {
+ stop();
+ fade_state = NoFading;
+ } else {
+ set_gain( (fade_time-time) / fade_time);
+ }
+ }
+}
+
+void
+StreamSoundSource::set_fading(FadeState state, float fade_time)
+{
+ this->fade_state = state;
+ this->fade_time = fade_time;
+ this->fade_start_time = real_time;
+}
+
+bool
+StreamSoundSource::fillBufferAndQueue(ALuint buffer)
+{
+ // fill buffer
+ char* bufferdata = new char[STREAMFRAGMENTSIZE];
+ size_t bytesread = 0;
+ do {
+ bytesread += file->read(bufferdata + bytesread,
+ STREAMFRAGMENTSIZE - bytesread);
+ // end of sound file
+ if(bytesread < STREAMFRAGMENTSIZE) {
+ if(looping)
+ file->reset();
+ else
+ break;
+ }
+ } while(bytesread < STREAMFRAGMENTSIZE);
+
+ if(bytesread > 0) {
+ ALenum format = SoundManager::get_sample_format(file);
+ alBufferData(buffer, format, bufferdata, bytesread, file->rate);
+ SoundManager::check_al_error("Couldn't refill audio buffer: ");
+
+ alSourceQueueBuffers(source, 1, &buffer);
+ SoundManager::check_al_error("Couldn't queue audio buffer: ");
+ }
+ delete[] bufferdata;
+
+ // return false if there aren't more buffers to fill
+ return bytesread >= STREAMFRAGMENTSIZE;
+}
--- /dev/null
+// $Id$
+//
+// SuperTux
+// Copyright (C) 2006 Matthias Braun <matze@braunis.de>
+//
+// 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 __STREAM_SOUND_SOURCE_H__
+#define __STREAM_SOUND_SOURCE_H__
+
+#include <stdio.h>
+#include <SDL.h>
+#include "openal_sound_source.hpp"
+
+class SoundFile;
+
+class StreamSoundSource : public OpenALSoundSource
+{
+public:
+ StreamSoundSource();
+ virtual ~StreamSoundSource();
+
+ void set_sound_file(SoundFile* file);
+
+ enum FadeState { NoFading, FadingOn, FadingOff };
+
+ void set_fading(FadeState state, float fadetime);
+ FadeState get_fade_state() const
+ {
+ return fade_state;
+ }
+ void update();
+
+ void set_looping(bool looping)
+ {
+ this->looping = looping;
+ }
+ bool get_looping() const
+ {
+ return looping;
+ }
+
+private:
+ static const size_t STREAMBUFFERSIZE = 1024 * 500;
+ static const size_t STREAMFRAGMENTS = 5;
+ static const size_t STREAMFRAGMENTSIZE
+ = STREAMBUFFERSIZE / STREAMFRAGMENTS;
+
+ bool fillBufferAndQueue(ALuint buffer);
+ SoundFile* file;
+ ALuint buffers[STREAMFRAGMENTS];
+
+ FadeState fade_state;
+ float fade_start_time;
+ float fade_time;
+ bool looping;
+};
+
+#endif
--- /dev/null
+// $Id$
+//
+// AngryStone - A spiked block that charges towards the player
+// Copyright (C) 2006 Christoph Sommer <supertux@2006.expires.deltadevelopment.de>
+//
+// 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 <config.h>
+
+#include "angrystone.hpp"
+
+static const float SPEED = 240;
+
+static const float CHARGE_TIME = .5;
+static const float ATTACK_TIME = 1;
+static const float RECOVER_TIME = .5;
+
+AngryStone::AngryStone(const lisp::Lisp& reader)
+ : BadGuy(reader, "images/creatures/angrystone/angrystone.sprite"), state(IDLE)
+{
+}
+
+void
+AngryStone::write(lisp::Writer& writer)
+{
+ writer.start_list("angrystone");
+
+ writer.write_float("x", start_position.x);
+ writer.write_float("y", start_position.y);
+
+ writer.end_list("angrystone");
+}
+
+void
+AngryStone::activate()
+{
+ physic.set_velocity_x(0);
+ physic.set_velocity_y(0);
+ physic.enable_gravity(true);
+ sprite->set_action("idle");
+}
+
+void
+AngryStone::collision_solid(const CollisionHit& hit)
+{
+ // TODO
+ (void) hit;
+#if 0
+ if ((state == ATTACKING) &&
+ (hit.normal.x == -attackDirection.x) && (hit.normal.y == attackDirection.y)) {
+ state = IDLE;
+ sprite->set_action("idle");
+ physic.set_velocity_x(0);
+ physic.set_velocity_y(0);
+ physic.enable_gravity(true);
+ oldWallDirection.x = attackDirection.x;
+ oldWallDirection.y = attackDirection.y;
+ }
+#endif
+}
+
+void
+AngryStone::kill_fall()
+{
+ //prevents AngryStone from getting killed by other enemies or the player
+}
+
+HitResponse
+AngryStone::collision_badguy(BadGuy& badguy, const CollisionHit& )
+{
+ if (state == ATTACKING) {
+ badguy.kill_fall();
+ return FORCE_MOVE;
+ }
+
+ return FORCE_MOVE;
+}
+
+void
+AngryStone::active_update(float elapsed_time) {
+ BadGuy::active_update(elapsed_time);
+
+ if (state == IDLE) {
+ MovingObject* player = this->get_nearest_player();
+ MovingObject* badguy = this;
+ const Vector playerPos = player->get_pos();
+ const Vector badguyPos = badguy->get_pos();
+ float dx = (playerPos.x - badguyPos.x);
+ float dy = (playerPos.y - badguyPos.y);
+
+ float playerHeight = player->get_bbox().p2.y - player->get_bbox().p1.y;
+ float badguyHeight = badguy->get_bbox().p2.y - badguy->get_bbox().p1.y;
+
+ float playerWidth = player->get_bbox().p2.x - player->get_bbox().p1.x;
+ float badguyWidth = badguy->get_bbox().p2.x - badguy->get_bbox().p1.x;
+
+ if ((dx > -playerWidth) && (dx < badguyWidth)) {
+ if (dy > 0) {
+ attackDirection.x = 0;
+ attackDirection.y = 1;
+ } else {
+ attackDirection.x = 0;
+ attackDirection.y = -1;
+ }
+ if ((attackDirection.x != oldWallDirection.x) || (attackDirection.y != oldWallDirection.y)) {
+ sprite->set_action("charging");
+ timer.start(CHARGE_TIME);
+ state = CHARGING;
+ }
+ } else
+ if ((dy > -playerHeight) && (dy < badguyHeight)) {
+ if (dx > 0) {
+ attackDirection.x = 1;
+ attackDirection.y = 0;
+ } else {
+ attackDirection.x = -1;
+ attackDirection.y = 0;
+ }
+ if ((attackDirection.x != oldWallDirection.x) || (attackDirection.y != oldWallDirection.y)) {
+ sprite->set_action("charging");
+ timer.start(CHARGE_TIME);
+ state = CHARGING;
+ }
+ }
+
+ }
+
+ if (state == CHARGING) {
+ if (timer.check()) {
+ sprite->set_action("attacking");
+ timer.start(ATTACK_TIME);
+ state = ATTACKING;
+ physic.enable_gravity(false);
+ physic.set_velocity_x(SPEED * attackDirection.x);
+ physic.set_velocity_y(SPEED * attackDirection.y);
+ oldWallDirection.x = 0;
+ oldWallDirection.y = 0;
+ }
+ }
+
+ if (state == ATTACKING) {
+ if (timer.check()) {
+ timer.start(RECOVER_TIME);
+ state = RECOVERING;
+ sprite->set_action("idle");
+ physic.enable_gravity(true);
+ physic.set_velocity_x(0);
+ physic.set_velocity_y(0);
+ }
+ }
+
+ if (state == RECOVERING) {
+ if (timer.check()) {
+ state = IDLE;
+ sprite->set_action("idle");
+ physic.enable_gravity(true);
+ physic.set_velocity_x(0);
+ physic.set_velocity_y(0);
+ }
+ }
+
+}
+
+IMPLEMENT_FACTORY(AngryStone, "angrystone")
--- /dev/null
+// $Id$
+//
+// AngryStone - A spiked block that charges towards the player
+// Copyright (C) 2006 Christoph Sommer <supertux@2006.expires.deltadevelopment.de>
+//
+// 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 __ANGRYSTONE_H__
+#define __ANGRYSTONE_H__
+
+#include "badguy.hpp"
+
+class AngryStone : public BadGuy
+{
+public:
+ AngryStone(const lisp::Lisp& reader);
+
+ void activate();
+ void write(lisp::Writer& writer);
+ void collision_solid(const CollisionHit& hit);
+ HitResponse collision_badguy(BadGuy& badguy, const CollisionHit& hit);
+ void active_update(float elapsed_time);
+ void kill_fall();
+
+ virtual AngryStone* clone() const { return new AngryStone(*this); }
+
+protected:
+ Vector attackDirection; /**< 1-normalized vector of current attack direction */
+ Vector oldWallDirection; /**< if wall was hit during last attack: 1-normalized vector of last attack direction, (0,0) otherwise */
+
+ Timer timer;
+
+ enum AngryStoneState {
+ IDLE,
+ CHARGING,
+ ATTACKING,
+ RECOVERING
+ };
+ AngryStoneState state;
+
+};
+
+#endif
--- /dev/null
+// $Id$
+//
+// SuperTux
+// Copyright (C) 2006 Matthias Braun <matze@braunis.de>
+//
+// 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 <config.h>
+
+#include "badguy.hpp"
+#include "object/camera.hpp"
+#include "object/tilemap.hpp"
+#include "tile.hpp"
+#include "statistics.hpp"
+#include "game_session.hpp"
+#include "log.hpp"
+#include "level.hpp"
+#include "object/bullet.hpp"
+#include "main.hpp"
+#include "object/particles.hpp"
+#include "random_generator.hpp"
+
+static const float SQUISH_TIME = 2;
+static const float X_OFFSCREEN_DISTANCE = 1600;
+static const float Y_OFFSCREEN_DISTANCE = 1200;
+
+BadGuy::BadGuy(const Vector& pos, const std::string& sprite_name, int layer)
+ : MovingSprite(pos, sprite_name, layer, COLGROUP_DISABLED), countMe(true),
+ dir(LEFT), start_dir(AUTO), frozen(false), ignited(false),
+ state(STATE_INIT), on_ground_flag(false)
+{
+ start_position = bbox.p1;
+
+ sound_manager->preload("sounds/squish.wav");
+ sound_manager->preload("sounds/fall.wav");
+}
+
+BadGuy::BadGuy(const Vector& pos, Direction direction, const std::string& sprite_name, int layer)
+ : MovingSprite(pos, sprite_name, layer, COLGROUP_DISABLED), countMe(true),
+ dir(direction), start_dir(direction), frozen(false), ignited(false),
+ state(STATE_INIT), on_ground_flag(false)
+{
+ start_position = bbox.p1;
+
+ sound_manager->preload("sounds/squish.wav");
+ sound_manager->preload("sounds/fall.wav");
+}
+
+BadGuy::BadGuy(const lisp::Lisp& reader, const std::string& sprite_name, int layer)
+ : MovingSprite(reader, sprite_name, layer, COLGROUP_DISABLED), countMe(true), dir(LEFT), start_dir(AUTO), frozen(false), ignited(false), state(STATE_INIT), on_ground_flag(false)
+{
+ start_position = bbox.p1;
+
+ std::string dir_str = "auto";
+ reader.get("direction", dir_str);
+ start_dir = str2dir( dir_str );
+ dir = start_dir;
+
+ reader.get("dead-script", dead_script);
+
+ sound_manager->preload("sounds/squish.wav");
+ sound_manager->preload("sounds/fall.wav");
+}
+
+void
+BadGuy::draw(DrawingContext& context)
+{
+ if(!sprite)
+ return;
+ if(state == STATE_INIT || state == STATE_INACTIVE)
+ return;
+ if(state == STATE_FALLING) {
+ DrawingEffect old_effect = context.get_drawing_effect();
+ context.set_drawing_effect((DrawingEffect) (old_effect | VERTICAL_FLIP));
+ sprite->draw(context, get_pos(), layer);
+ context.set_drawing_effect(old_effect);
+ } else {
+ sprite->draw(context, get_pos(), layer);
+ }
+}
+
+void
+BadGuy::update(float elapsed_time)
+{
+ if(!Sector::current()->inside(bbox)) {
+ remove_me();
+ return;
+ }
+ if(is_offscreen()) {
+ if (state == STATE_ACTIVE) deactivate();
+ set_state(STATE_INACTIVE);
+ }
+
+ switch(state) {
+ case STATE_ACTIVE:
+ active_update(elapsed_time);
+ break;
+ case STATE_INIT:
+ case STATE_INACTIVE:
+ inactive_update(elapsed_time);
+ try_activate();
+ break;
+ case STATE_SQUISHED:
+ if(state_timer.check()) {
+ remove_me();
+ break;
+ }
+ movement = physic.get_movement(elapsed_time);
+ break;
+ case STATE_FALLING:
+ movement = physic.get_movement(elapsed_time);
+ break;
+ }
+
+ on_ground_flag = false;
+}
+
+Direction
+BadGuy::str2dir( std::string dir_str )
+{
+ if( dir_str == "auto" )
+ return AUTO;
+ if( dir_str == "left" )
+ return LEFT;
+ if( dir_str == "right" )
+ return RIGHT;
+
+ //default to "auto"
+ log_warning << "Badguy::str2dir: unknown direction \"" << dir_str << "\"" << std::endl;;
+ return AUTO;
+}
+
+void
+BadGuy::activate()
+{
+}
+
+void
+BadGuy::deactivate()
+{
+}
+
+void
+BadGuy::write(lisp::Writer& )
+{
+ log_warning << "tried to write out a generic badguy" << std::endl;
+}
+
+void
+BadGuy::active_update(float elapsed_time)
+{
+ movement = physic.get_movement(elapsed_time);
+}
+
+void
+BadGuy::inactive_update(float )
+{
+}
+
+void
+BadGuy::collision_tile(uint32_t tile_attributes)
+{
+ if(tile_attributes & Tile::HURTS) {
+ if (tile_attributes & Tile::FIRE) {
+ if (is_flammable()) ignite();
+ }
+ else if (tile_attributes & Tile::ICE) {
+ if (is_freezable()) freeze();
+ }
+ else {
+ kill_fall();
+ }
+ }
+}
+
+HitResponse
+BadGuy::collision(GameObject& other, const CollisionHit& hit)
+{
+ switch(state) {
+ case STATE_INIT:
+ case STATE_INACTIVE:
+ return ABORT_MOVE;
+ case STATE_ACTIVE: {
+ BadGuy* badguy = dynamic_cast<BadGuy*> (&other);
+ if(badguy && badguy->state == STATE_ACTIVE && badguy->get_group() == COLGROUP_MOVING) {
+
+ // hit from above?
+ if (badguy->get_bbox().p2.y < (bbox.p1.y + 16)) {
+ if(collision_squished(*badguy)) {
+ return ABORT_MOVE;
+ }
+ }
+
+ return collision_badguy(*badguy, hit);
+ }
+
+ Player* player = dynamic_cast<Player*> (&other);
+ if(player) {
+
+ // hit from above?
+ if (player->get_bbox().p2.y < (bbox.p1.y + 16)) {
+ if(collision_squished(*player)) {
+ return ABORT_MOVE;
+ }
+ }
+
+ return collision_player(*player, hit);
+ }
+
+ Bullet* bullet = dynamic_cast<Bullet*> (&other);
+ if(bullet)
+ return collision_bullet(*bullet, hit);
+
+ return FORCE_MOVE;
+ }
+ case STATE_SQUISHED:
+ return FORCE_MOVE;
+ case STATE_FALLING:
+ return FORCE_MOVE;
+ }
+
+ return ABORT_MOVE;
+}
+
+void
+BadGuy::collision_solid(const CollisionHit& hit)
+{
+ physic.set_velocity_x(0);
+ physic.set_velocity_y(0);
+ update_on_ground_flag(hit);
+}
+
+HitResponse
+BadGuy::collision_player(Player& player, const CollisionHit& )
+{
+ if(player.is_invincible()) {
+ kill_fall();
+ return ABORT_MOVE;
+ }
+
+ if(frozen)
+ unfreeze();
+ player.kill(false);
+ return FORCE_MOVE;
+}
+
+HitResponse
+BadGuy::collision_badguy(BadGuy& , const CollisionHit& )
+{
+ return FORCE_MOVE;
+}
+
+bool
+BadGuy::collision_squished(GameObject& )
+{
+ return false;
+}
+
+HitResponse
+BadGuy::collision_bullet(Bullet& bullet, const CollisionHit& hit)
+{
+ if (is_frozen()) {
+ if(bullet.get_type() == FIRE_BONUS) {
+ // fire bullet thaws frozen badguys
+ unfreeze();
+ bullet.remove_me();
+ return ABORT_MOVE;
+ } else {
+ // other bullets ricochet
+ bullet.ricochet(*this, hit);
+ return FORCE_MOVE;
+ }
+ }
+ else if (is_ignited()) {
+ if(bullet.get_type() == ICE_BONUS) {
+ // ice bullets extinguish ignited badguys
+ extinguish();
+ bullet.remove_me();
+ return ABORT_MOVE;
+ } else {
+ // other bullets are absorbed by ignited badguys
+ bullet.remove_me();
+ return FORCE_MOVE;
+ }
+ }
+ else if(bullet.get_type() == FIRE_BONUS && is_flammable()) {
+ // fire bullets ignite flammable badguys
+ ignite();
+ bullet.remove_me();
+ return ABORT_MOVE;
+ }
+ else if(bullet.get_type() == ICE_BONUS && is_freezable()) {
+ // ice bullets freeze freezable badguys
+ freeze();
+ bullet.remove_me();
+ return ABORT_MOVE;
+ }
+ else {
+ // in all other cases, bullets ricochet
+ bullet.ricochet(*this, hit);
+ return FORCE_MOVE;
+ }
+}
+
+void
+BadGuy::kill_squished(GameObject& object)
+{
+ sound_manager->play("sounds/squish.wav", get_pos());
+ physic.enable_gravity(true);
+ physic.set_velocity_x(0);
+ physic.set_velocity_y(0);
+ set_state(STATE_SQUISHED);
+ set_group(COLGROUP_MOVING_ONLY_STATIC);
+ Player* player = dynamic_cast<Player*>(&object);
+ if (player) {
+ if (countMe) Sector::current()->get_level()->stats.badguys++;
+ player->bounce(*this);
+ }
+
+ // start dead-script
+ if(dead_script != "") {
+ std::istringstream stream(dead_script);
+ Sector::current()->run_script(stream, "dead-script");
+ }
+}
+
+void
+BadGuy::kill_fall()
+{
+ sound_manager->play("sounds/fall.wav", get_pos());
+ if (countMe) Sector::current()->get_level()->stats.badguys++;
+ physic.set_velocity_y(0);
+ physic.enable_gravity(true);
+ set_state(STATE_FALLING);
+
+ // start dead-script
+ if(dead_script != "") {
+ std::istringstream stream(dead_script);
+ Sector::current()->run_script(stream, "dead-script");
+ }
+}
+
+void
+BadGuy::run_dead_script()
+{
+ if (countMe)
+ Sector::current()->get_level()->stats.badguys++;
+
+ // start dead-script
+ if(dead_script != "") {
+ std::istringstream stream(dead_script);
+ Sector::current()->run_script(stream, "dead-script");
+ }
+}
+
+void
+BadGuy::set_state(State state)
+{
+ if(this->state == state)
+ return;
+
+ State laststate = this->state;
+ this->state = state;
+ switch(state) {
+ case STATE_SQUISHED:
+ state_timer.start(SQUISH_TIME);
+ break;
+ case STATE_ACTIVE:
+ set_group(COLGROUP_MOVING);
+ bbox.set_pos(start_position);
+ break;
+ case STATE_INACTIVE:
+ // was the badguy dead anyway?
+ if(laststate == STATE_SQUISHED || laststate == STATE_FALLING) {
+ remove_me();
+ }
+ set_group(COLGROUP_DISABLED);
+ break;
+ case STATE_FALLING:
+ set_group(COLGROUP_DISABLED);
+ break;
+ default:
+ break;
+ }
+}
+
+bool
+BadGuy::is_offscreen()
+{
+ float scroll_x = Sector::current()->camera->get_translation().x;
+ float scroll_y = Sector::current()->camera->get_translation().y;
+
+ if(bbox.p2.x < scroll_x - X_OFFSCREEN_DISTANCE
+ || bbox.p1.x > scroll_x + X_OFFSCREEN_DISTANCE + SCREEN_WIDTH
+ || bbox.p2.y < scroll_y - Y_OFFSCREEN_DISTANCE
+ || bbox.p1.y > scroll_y + Y_OFFSCREEN_DISTANCE + SCREEN_HEIGHT)
+ return true;
+
+ return false;
+}
+
+void
+BadGuy::try_activate()
+{
+ float scroll_x = Sector::current()->camera->get_translation().x;
+ float scroll_y = Sector::current()->camera->get_translation().y;
+
+ /* Activate badguys if they're just around the screen to avoid
+ * the effect of having badguys suddenly popping up from nowhere.
+ */
+ //Badguy left of screen
+ if (start_position.x > scroll_x - X_OFFSCREEN_DISTANCE &&
+ start_position.x < scroll_x - bbox.get_width() &&
+ start_position.y > scroll_y - Y_OFFSCREEN_DISTANCE &&
+ start_position.y < scroll_y + SCREEN_HEIGHT + Y_OFFSCREEN_DISTANCE) {
+ if (start_dir != AUTO) dir = start_dir; else dir = RIGHT;
+ set_state(STATE_ACTIVE);
+ activate();
+ //Badguy right of screen
+ } else if (start_position.x > scroll_x + SCREEN_WIDTH &&
+ start_position.x < scroll_x + SCREEN_WIDTH + X_OFFSCREEN_DISTANCE &&
+ start_position.y > scroll_y - Y_OFFSCREEN_DISTANCE &&
+ start_position.y < scroll_y + SCREEN_HEIGHT + Y_OFFSCREEN_DISTANCE) {
+ if (start_dir != AUTO) dir = start_dir; else dir = LEFT;
+ set_state(STATE_ACTIVE);
+ activate();
+ //Badguy over or under screen
+ } else if (start_position.x > scroll_x - X_OFFSCREEN_DISTANCE &&
+ start_position.x < scroll_x + SCREEN_WIDTH + X_OFFSCREEN_DISTANCE &&
+ ((start_position.y > scroll_y + SCREEN_HEIGHT &&
+ start_position.y < scroll_y + SCREEN_HEIGHT + Y_OFFSCREEN_DISTANCE) ||
+ (start_position.y > scroll_y - Y_OFFSCREEN_DISTANCE &&
+ start_position.y < scroll_y - bbox.get_height() ))) {
+ if (start_dir != AUTO) dir = start_dir;
+ else{
+ // if nearest player is to our right, start facing right
+ Player* player = get_nearest_player();
+ if (player && (player->get_bbox().p1.x > get_bbox().p2.x)) {
+ dir = RIGHT;
+ } else {
+ dir = LEFT;
+ }
+ }
+ set_state(STATE_ACTIVE);
+ activate();
+ } else if(state == STATE_INIT
+ && start_position.x > scroll_x - X_OFFSCREEN_DISTANCE
+ && start_position.x < scroll_x + X_OFFSCREEN_DISTANCE + SCREEN_WIDTH
+ && start_position.y > scroll_y - Y_OFFSCREEN_DISTANCE
+ && start_position.y < scroll_y + Y_OFFSCREEN_DISTANCE + SCREEN_HEIGHT ) {
+ if (start_dir != AUTO) {
+ dir = start_dir;
+ } else {
+ // if nearest player is to our right, start facing right
+ Player* player = get_nearest_player();
+ if (player && (player->get_bbox().p1.x > get_bbox().p2.x)) {
+ dir = RIGHT;
+ } else {
+ dir = LEFT;
+ }
+ }
+ set_state(STATE_ACTIVE);
+ activate();
+ }
+}
+
+bool
+BadGuy::might_fall(int height)
+{
+ // make sure we check for at least a 1-pixel fall
+ assert(height > 0);
+
+ float x1;
+ float x2;
+ float y1 = bbox.p2.y + 1;
+ float y2 = bbox.p2.y + 1 + height;
+ if (dir == LEFT) {
+ x1 = bbox.p1.x - 1;
+ x2 = bbox.p1.x - 1;
+ } else {
+ x1 = bbox.p2.x + 1;
+ x2 = bbox.p2.x + 1;
+ }
+ return Sector::current()->is_free_of_statics(Rect(x1, y1, x2, y2));
+}
+
+Player*
+BadGuy::get_nearest_player()
+{
+ // FIXME: does not really return nearest player
+
+ std::vector<Player*> players = Sector::current()->get_players();
+ for (std::vector<Player*>::iterator playerIter = players.begin(); playerIter != players.end(); ++playerIter) {
+ Player* player = *playerIter;
+ return player;
+ }
+
+ return 0;
+}
+
+void
+BadGuy::update_on_ground_flag(const CollisionHit& hit)
+{
+ if (hit.bottom) {
+ on_ground_flag = true;
+ floor_normal = hit.slope_normal;
+ }
+}
+
+bool
+BadGuy::on_ground()
+{
+ return on_ground_flag;
+}
+
+Vector
+BadGuy::get_floor_normal()
+{
+ return floor_normal;
+}
+
+void
+BadGuy::freeze()
+{
+ set_group(COLGROUP_MOVING_STATIC);
+ frozen = true;
+}
+
+void
+BadGuy::unfreeze()
+{
+ set_group(COLGROUP_MOVING);
+ frozen = false;
+}
+
+bool
+BadGuy::is_freezable() const
+{
+ return false;
+}
+
+bool
+BadGuy::is_frozen() const
+{
+ return frozen;
+}
+
+void
+BadGuy::ignite()
+{
+ kill_fall();
+}
+
+void
+BadGuy::extinguish()
+{
+}
+
+bool
+BadGuy::is_flammable() const
+{
+ return true;
+}
+
+bool
+BadGuy::is_ignited() const
+{
+ return ignited;
+}
--- /dev/null
+// $Id$
+//
+// SuperTux
+// Copyright (C) 2006 Matthias Braun <matze@braunis.de>
+//
+// 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 __BADGUY_H__
+#define __BADGUY_H__
+
+// moved them here to make it less typing when implementing new badguys
+#include <math.h>
+#include "timer.hpp"
+#include "object/moving_sprite.hpp"
+#include "physic.hpp"
+#include "object/player.hpp"
+#include "serializable.hpp"
+#include "resources.hpp"
+#include "sector.hpp"
+#include "direction.hpp"
+#include "object_factory.hpp"
+#include "lisp/parser.hpp"
+#include "lisp/lisp.hpp"
+#include "lisp/writer.hpp"
+#include "video/drawing_context.hpp"
+#include "audio/sound_manager.hpp"
+#include "audio/sound_source.hpp"
+
+class BadGuy : public MovingSprite, protected UsesPhysic, public Serializable
+{
+public:
+ BadGuy(const Vector& pos, const std::string& sprite_name, int layer = LAYER_OBJECTS);
+ BadGuy(const Vector& pos, Direction direction, const std::string& sprite_name, int layer = LAYER_OBJECTS);
+ BadGuy(const lisp::Lisp& reader, const std::string& sprite_name, int layer = LAYER_OBJECTS);
+
+ /** Called when the badguy is drawn. The default implementation simply draws
+ * the badguy sprite on screen
+ */
+ virtual void draw(DrawingContext& context);
+ /** Called each frame. The default implementation checks badguy state and
+ * calls active_update and inactive_update
+ */
+ virtual void update(float elapsed_time);
+ /** Called when a collision with another object occured. The default
+ * implemetnation calls collision_player, collision_solid, collision_badguy
+ * and collision_squished
+ */
+ virtual HitResponse collision(GameObject& other, const CollisionHit& hit);
+
+ /** Called when a collision with tile with special attributes occured */
+ virtual void collision_tile(uint32_t tile_attributes);
+
+ /** Set the badguy to kill/falling state, which makes him falling of the
+ * screen (his sprite is turned upside-down)
+ */
+ virtual void kill_fall();
+
+ /** Call this, if you use custom kill_fall() or kill_squashed(GameObject& object) */
+ virtual void run_dead_script();
+
+ /** Writes out the badguy into the included lisp::Writer. Useful e.g. when
+ * converting an old-format level to the new format.
+ */
+ virtual void write(lisp::Writer& writer);
+
+ /**
+ * True if this badguy can break bricks or open bonusblocks in his current form.
+ */
+ virtual bool can_break()
+ {
+ return false;
+ }
+
+ Vector get_start_position() const
+ {
+ return start_position;
+ }
+ void set_start_position(const Vector& vec)
+ {
+ start_position = vec;
+ }
+
+ /** Count this badguy to the statistics? This value should not be changed
+ * during runtime. */
+ bool countMe;
+
+ /**
+ * Called when hit by a fire bullet, and is_flammable() returns true
+ */
+ virtual void ignite();
+
+ /**
+ * Called to revert a badguy when is_ignited() returns true
+ */
+ virtual void extinguish();
+
+ /**
+ * Returns whether to call ignite() when a badguy gets hit by a fire bullet
+ */
+ virtual bool is_flammable() const;
+
+ /**
+ * Returns whether this badguys is currently on fire
+ */
+ bool is_ignited() const;
+
+ /**
+ * Called when hit by an ice bullet, and is_freezable() returns true.
+ */
+ virtual void freeze();
+
+ /**
+ * Called to unfreeze the badguy.
+ */
+ virtual void unfreeze();
+
+ virtual bool is_freezable() const;
+
+ bool is_frozen() const;
+
+protected:
+ enum State {
+ STATE_INIT,
+ STATE_INACTIVE,
+ STATE_ACTIVE,
+ STATE_SQUISHED,
+ STATE_FALLING
+ };
+
+ /** Called when the badguy collided with a player */
+ virtual HitResponse collision_player(Player& player, const CollisionHit& hit);
+ /** Called when the badguy collided with solid ground */
+ virtual void collision_solid(const CollisionHit& hit);
+ /** Called when the badguy collided with another badguy */
+ virtual HitResponse collision_badguy(BadGuy& other, const CollisionHit& hit);
+
+ /** Called when the player hit the badguy from above. You should return true
+ * if the badguy was squished, false if squishing wasn't possible
+ */
+ virtual bool collision_squished(GameObject& object);
+
+ /** Called when the badguy collided with a bullet */
+ virtual HitResponse collision_bullet(Bullet& bullet, const CollisionHit& hit);
+
+ /** called each frame when the badguy is activated. */
+ virtual void active_update(float elapsed_time);
+ /** called each frame when the badguy is not activated. */
+ virtual void inactive_update(float elapsed_time);
+
+ /**
+ * called when the badguy has been activated. (As a side effect the dir
+ * variable might have been changed so that it faces towards the player.
+ */
+ virtual void activate();
+ /** called when the badguy has been deactivated */
+ virtual void deactivate();
+
+ void kill_squished(GameObject& object);
+
+ void set_state(State state);
+ State get_state() const
+ { return state; }
+
+ /**
+ * returns a pointer to the nearest player or 0 if no player is available
+ */
+ Player* get_nearest_player();
+
+ /// is the enemy activated
+ bool activated;
+ /**
+ * initial position of the enemy. Also the position where enemy respawns when
+ * after being deactivated.
+ */
+ bool is_offscreen();
+ /**
+ * Returns true if we might soon fall at least @c height pixels. Minimum
+ * value for height is 1 pixel
+ */
+ bool might_fall(int height = 1);
+
+ Vector start_position;
+
+ /**
+ * The direction we currently face in
+ */
+ Direction dir;
+
+ /**
+ * The direction we initially faced in
+ */
+ Direction start_dir;
+
+ /**
+ * Get Direction from String.
+ */
+ Direction str2dir( std::string dir_str );
+
+ /**
+ * Update on_ground_flag judging by solid collision @c hit.
+ * This gets called from the base implementation of collision_solid, so call this when overriding collision_solid's default behaviour.
+ */
+ void update_on_ground_flag(const CollisionHit& hit);
+
+ /**
+ * Returns true if we touched ground in the past frame
+ * This only works if update_on_ground_flag() gets called in collision_solid.
+ */
+ bool on_ground();
+
+ /**
+ * Returns floor normal stored the last time when update_on_ground_flag was called and we touched something solid from above.
+ */
+ Vector get_floor_normal();
+
+ bool frozen;
+ bool ignited; /**< true if this badguy is currently on fire */
+
+ std::string dead_script; /**< script to execute when badguy is killed */
+
+private:
+ void try_activate();
+
+ State state;
+ Timer state_timer;
+ bool on_ground_flag; /**< true if we touched something solid from above and update_on_ground_flag was called last frame */
+ Vector floor_normal; /**< floor normal stored the last time when update_on_ground_flag was called and we touched something solid from above */
+
+};
+
+#endif
--- /dev/null
+// $Id$
+//
+// SuperTux
+// Copyright (C) 2006 Matthias Braun <matze@braunis.de>
+//
+// 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 <config.h>
+
+#include "bomb.hpp"
+#include "random_generator.hpp"
+#include "object/explosion.hpp"
+
+Bomb::Bomb(const Vector& pos, Direction dir, std::string custom_sprite /*= "images/creatures/mr_bomb/mr_bomb.sprite"*/ )
+ : BadGuy( pos, dir, custom_sprite )
+{
+ state = STATE_TICKING;
+ set_action(dir == LEFT ? "ticking-left" : "ticking-right", 1);
+ countMe = false;
+
+ ticking.reset(sound_manager->create_sound_source("sounds/fizz.wav"));
+ ticking->set_position(get_pos());
+ ticking->set_looping(true);
+ ticking->set_gain(2.0);
+ ticking->set_reference_distance(32);
+ ticking->play();
+}
+
+Bomb::Bomb(const Bomb& other)
+ : BadGuy(other), state(other.state)
+{
+ if (state == STATE_TICKING) {
+ ticking.reset(sound_manager->create_sound_source("sounds/fizz.wav"));
+ ticking->set_position(get_pos());
+ ticking->set_looping(true);
+ ticking->set_gain(2.0);
+ ticking->set_reference_distance(32);
+ ticking->play();
+ }
+}
+
+void
+Bomb::write(lisp::Writer& )
+{
+ // bombs are only temporarily so don't write them out...
+}
+
+void
+Bomb::collision_solid(const CollisionHit& hit)
+{
+ if(hit.bottom)
+ physic.set_velocity_y(0);
+}
+
+HitResponse
+Bomb::collision_player(Player& , const CollisionHit& )
+{
+ return ABORT_MOVE;
+}
+
+HitResponse
+Bomb::collision_badguy(BadGuy& , const CollisionHit& )
+{
+ return ABORT_MOVE;
+}
+
+void
+Bomb::active_update(float )
+{
+ ticking->set_position(get_pos());
+ if(sprite->animation_done()) {
+ explode();
+ }
+}
+
+void
+Bomb::explode()
+{
+ ticking->stop();
+
+ remove_me();
+ Explosion* explosion = new Explosion(get_bbox().get_middle());
+ Sector::current()->add_object(explosion);
+
+ run_dead_script();
+}
+
+void
+Bomb::kill_fall()
+{
+ explode();
+}
--- /dev/null
+// $Id$
+//
+// SuperTux
+// Copyright (C) 2006 Matthias Braun <matze@braunis.de>
+//
+// 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 __BOMB_H__
+#define __BOMB_H__
+
+#include "badguy.hpp"
+
+class Bomb : public BadGuy
+{
+public:
+ Bomb(const Vector& pos, Direction dir, std::string custom_sprite = "images/creatures/mr_bomb/bomb.sprite" );
+ Bomb(const Bomb& bomb);
+
+ void write(lisp::Writer& writer);
+ void collision_solid(const CollisionHit& hit);
+ HitResponse collision_player(Player& player, const CollisionHit& hit);
+ HitResponse collision_badguy(BadGuy& badguy, const CollisionHit& hit);
+ void active_update(float elapsed_time);
+ void kill_fall();
+ void explode();
+
+private:
+ enum State {
+ STATE_TICKING
+ };
+
+ State state;
+
+ std::auto_ptr<SoundSource> ticking;
+};
+
+#endif
--- /dev/null
+// $Id$
+//
+// SuperTux
+// Copyright (C) 2006 Matthias Braun <matze@braunis.de>
+//
+// 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 <config.h>
+
+#include "bouncing_snowball.hpp"
+
+static const float JUMPSPEED = -450;
+static const float WALKSPEED = 80;
+
+BouncingSnowball::BouncingSnowball(const lisp::Lisp& reader)
+ : BadGuy(reader, "images/creatures/bouncing_snowball/bouncing_snowball.sprite")
+{
+}
+
+BouncingSnowball::BouncingSnowball(const Vector& pos, Direction d)
+ : BadGuy(pos, d, "images/creatures/bouncing_snowball/bouncing_snowball.sprite")
+{
+}
+
+void
+BouncingSnowball::write(lisp::Writer& writer)
+{
+ writer.start_list("bouncingsnowball");
+
+ writer.write_float("x", start_position.x);
+ writer.write_float("y", start_position.y);
+
+ writer.end_list("bouncingsnowball");
+}
+
+void
+BouncingSnowball::activate()
+{
+ physic.set_velocity_x(dir == LEFT ? -WALKSPEED : WALKSPEED);
+ sprite->set_action(dir == LEFT ? "left" : "right");
+}
+
+bool
+BouncingSnowball::collision_squished(GameObject& object)
+{
+ sprite->set_action("squished");
+ kill_squished(object);
+ return true;
+}
+
+void
+BouncingSnowball::collision_solid(const CollisionHit& hit)
+{
+ if(hit.bottom) {
+ if(get_state() == STATE_ACTIVE) {
+ physic.set_velocity_y(JUMPSPEED);
+ } else {
+ physic.set_velocity_y(0);
+ }
+ } else if(hit.top) {
+ physic.set_velocity_y(0);
+ }
+
+ if(hit.left || hit.right) { // left or right collision
+ dir = dir == LEFT ? RIGHT : LEFT;
+ sprite->set_action(dir == LEFT ? "left" : "right");
+ physic.set_velocity_x(-physic.get_velocity_x());
+ }
+}
+
+HitResponse
+BouncingSnowball::collision_badguy(BadGuy& , const CollisionHit& hit)
+{
+ collision_solid(hit);
+ return CONTINUE;
+}
+
+IMPLEMENT_FACTORY(BouncingSnowball, "bouncingsnowball")
--- /dev/null
+// $Id$
+//
+// SuperTux
+// Copyright (C) 2006 Matthias Braun <matze@braunis.de>
+//
+// 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 __BOUNCING_SNOWBALL_H__
+#define __BOUNCING_SNOWBALL_H__
+
+#include "badguy.hpp"
+
+class BouncingSnowball : public BadGuy
+{
+public:
+ BouncingSnowball(const lisp::Lisp& reader);
+ BouncingSnowball(const Vector& pos, Direction d);
+
+ void activate();
+ void write(lisp::Writer& writer);
+ void collision_solid(const CollisionHit& hit);
+ HitResponse collision_badguy(BadGuy& badguy, const CollisionHit& hit);
+
+ virtual BouncingSnowball* clone() const { return new BouncingSnowball(*this); }
+
+protected:
+ bool collision_squished(GameObject& object);
+};
+
+#endif
--- /dev/null
+// $Id$
+//
+// Dart - Your average poison dart
+// Copyright (C) 2006 Christoph Sommer <supertux@2006.expires.deltadevelopment.de>
+//
+// 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 <config.h>
+
+#include "dart.hpp"
+
+namespace {
+ const float SPEED = 200;
+}
+
+static const std::string SOUNDFILE = "sounds/flame.wav";
+
+Dart::Dart(const lisp::Lisp& reader)
+ : BadGuy(reader, "images/creatures/dart/dart.sprite"), parent(0)
+{
+ physic.enable_gravity(false);
+ countMe = false;
+ sound_manager->preload("sounds/darthit.wav");
+ sound_manager->preload("sounds/stomp.wav");
+}
+
+Dart::Dart(const Vector& pos, Direction d, const BadGuy* parent = 0)
+ : BadGuy(pos, d, "images/creatures/dart/dart.sprite"), parent(parent)
+{
+ physic.enable_gravity(false);
+ countMe = false;
+ sound_manager->preload("sounds/darthit.wav");
+ sound_manager->preload("sounds/stomp.wav");
+}
+
+Dart::Dart(const Dart& other)
+ : BadGuy(other), parent(other.parent)
+{
+ sound_source.reset(sound_manager->create_sound_source(SOUNDFILE));
+ sound_manager->preload("sounds/darthit.wav");
+ sound_manager->preload("sounds/stomp.wav");
+}
+
+Dart::~Dart()
+{
+}
+
+bool
+Dart::updatePointers(const GameObject* from_object, GameObject* to_object)
+{
+ if (from_object == parent) {
+ parent = dynamic_cast<Dart*>(to_object);
+ return true;
+ }
+ return false;
+}
+
+void
+Dart::write(lisp::Writer& writer)
+{
+ writer.start_list("dart");
+ writer.write_float("x", start_position.x);
+ writer.write_float("y", start_position.y);
+ writer.end_list("dart");
+}
+
+void
+Dart::activate()
+{
+ physic.set_velocity_x(dir == LEFT ? -::SPEED : ::SPEED);
+ sprite->set_action(dir == LEFT ? "flying-left" : "flying-right");
+
+ sound_source.reset(sound_manager->create_sound_source(SOUNDFILE));
+ sound_source->set_position(get_pos());
+ sound_source->set_looping(true);
+ sound_source->set_gain(1.0);
+ sound_source->set_reference_distance(32);
+ sound_source->play();
+}
+
+void
+Dart::deactivate()
+{
+ sound_source.reset();
+ remove_me();
+}
+
+void
+Dart::active_update(float elapsed_time)
+{
+ BadGuy::active_update(elapsed_time);
+ sound_source->set_position(get_pos());
+}
+
+void
+Dart::collision_solid(const CollisionHit& )
+{
+ sound_manager->play("sounds/darthit.wav", get_pos());
+ remove_me();
+}
+
+HitResponse
+Dart::collision_badguy(BadGuy& badguy, const CollisionHit& )
+{
+ // ignore collisions with parent
+ if (&badguy == parent) {
+ return FORCE_MOVE;
+ }
+ sound_manager->play("sounds/stomp.wav", get_pos());
+ remove_me();
+ badguy.kill_fall();
+ return ABORT_MOVE;
+}
+
+HitResponse
+Dart::collision_player(Player& player, const CollisionHit& hit)
+{
+ sound_manager->play("sounds/stomp.wav", get_pos());
+ remove_me();
+ return BadGuy::collision_player(player, hit);
+}
+
+IMPLEMENT_FACTORY(Dart, "dart")
--- /dev/null
+// $Id$
+//
+// Dart - Your average poison dart
+// Copyright (C) 2006 Christoph Sommer <supertux@2006.expires.deltadevelopment.de>
+//
+// 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 __DART_H__
+#define __DART_H__
+
+#include "badguy.hpp"
+
+/**
+ * Badguy "Dart" - Your average poison dart
+ */
+class Dart : public BadGuy
+{
+public:
+ Dart(const lisp::Lisp& reader);
+ Dart(const Vector& pos, Direction d, const BadGuy* parent);
+ Dart(const Dart& dart);
+ ~Dart();
+
+ void activate();
+ void deactivate();
+ void write(lisp::Writer& writer);
+
+ void active_update(float elapsed_time);
+
+ void collision_solid(const CollisionHit& hit);
+ HitResponse collision_badguy(BadGuy& badguy, const CollisionHit& hit);
+ HitResponse collision_player(Player& player, const CollisionHit& hit);
+
+ virtual Dart* clone() const { return new Dart(*this); }
+
+ virtual bool updatePointers(const GameObject* from_object, GameObject* to_object);
+
+protected:
+ const BadGuy* parent; /**< collisions with this BadGuy will be ignored */
+ std::auto_ptr<SoundSource> sound_source; /**< SoundSource for ambient sound */
+};
+
+#endif
--- /dev/null
+// $Id$
+//
+// DartTrap - Shoots a Dart at regular intervals
+// Copyright (C) 2006 Christoph Sommer <supertux@2006.expires.deltadevelopment.de>
+//
+// 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 <config.h>
+
+#include "darttrap.hpp"
+#include "dart.hpp"
+
+namespace {
+ const float MUZZLE_Y = 25; /**< [px] muzzle y-offset from top */
+}
+
+DartTrap::DartTrap(const lisp::Lisp& reader)
+ : BadGuy(reader, "images/creatures/darttrap/darttrap.sprite", LAYER_TILES-1), initial_delay(0), fire_delay(2), ammo(-1), state(IDLE)
+{
+ reader.get("initial-delay", initial_delay);
+ reader.get("fire-delay", fire_delay);
+ reader.get("ammo", ammo);
+ countMe = false;
+ sound_manager->preload("sounds/dartfire.wav");
+ if (start_dir == AUTO) log_warning << "Setting a DartTrap's direction to AUTO is no good idea" << std::endl;
+}
+
+void
+DartTrap::write(lisp::Writer& writer)
+{
+ writer.start_list("darttrap");
+ writer.write_float("x", start_position.x);
+ writer.write_float("y", start_position.y);
+ writer.write_float("initial-delay", initial_delay);
+ writer.write_float("fire-delay", fire_delay);
+ writer.write_int("ammo", ammo);
+ writer.end_list("darttrap");
+}
+
+void
+DartTrap::activate()
+{
+ state = IDLE;
+ sprite->set_action(dir == LEFT ? "idle-left" : "idle-right");
+ set_group(COLGROUP_DISABLED);
+
+ if (initial_delay == 0) initial_delay = 0.1f;
+ fire_timer.start(initial_delay);
+}
+
+HitResponse
+DartTrap::collision_player(Player& , const CollisionHit& )
+{
+ return ABORT_MOVE;
+}
+
+void
+DartTrap::active_update(float )
+{
+ if (state == IDLE) {
+ if ((ammo != 0) && (fire_timer.check())) {
+ if (ammo > 0) ammo--;
+ load();
+ fire_timer.start(fire_delay);
+ }
+ }
+ if (state == LOADING) {
+ if (sprite->animation_done()) {
+ fire();
+ }
+ }
+}
+
+void
+DartTrap::load()
+{
+ state = LOADING;
+ sprite->set_action(dir == LEFT ? "loading-left" : "loading-right", 1);
+}
+
+void
+DartTrap::fire()
+{
+ float px = get_pos().x;
+ if (dir == RIGHT) px += 5;
+ float py = get_pos().y;
+ py += MUZZLE_Y;
+
+ sound_manager->play("sounds/dartfire.wav", get_pos());
+ Sector::current()->add_object(new Dart(Vector(px, py), dir, this));
+ state = IDLE;
+ sprite->set_action(dir == LEFT ? "idle-left" : "idle-right");
+}
+
+IMPLEMENT_FACTORY(DartTrap, "darttrap")
--- /dev/null
+// $Id$
+//
+// DartTrap - Shoots a Dart at regular intervals
+// Copyright (C) 2006 Christoph Sommer <supertux@2006.expires.deltadevelopment.de>
+//
+// 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 __DARTTRAP_H__
+#define __DARTTRAP_H__
+
+#include "badguy.hpp"
+#include "timer.hpp"
+
+/**
+ * Badguy "DartTrap" - Shoots a Dart at regular intervals
+ */
+class DartTrap : public BadGuy
+{
+public:
+ DartTrap(const lisp::Lisp& reader);
+
+ void activate();
+ void write(lisp::Writer& writer);
+ void active_update(float elapsed_time);
+ HitResponse collision_player(Player& player, const CollisionHit& hit);
+
+ virtual DartTrap* clone() const { return new DartTrap(*this); }
+
+protected:
+ enum State {
+ IDLE, LOADING
+ };
+
+ void load(); /**< load a shot */
+ void fire(); /**< fire a shot */
+
+ float initial_delay; /**< time to wait before firing first shot */
+ float fire_delay; /**< reload time */
+ int ammo; /**< ammo left (-1 means unlimited) */
+
+ State state; /**< current state */
+ Timer fire_timer; /**< time until new shot is fired */
+};
+
+#endif
--- /dev/null
+// $Id$
+//
+// SuperTux
+// Copyright (C) 2006 Matthias Braun <matze@braunis.de>
+//
+// 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 <config.h>
+
+#include "dispenser.hpp"
+#include "badguy/bouncing_snowball.hpp"
+#include "badguy/snowball.hpp"
+#include "badguy/mrbomb.hpp"
+#include "badguy/mriceblock.hpp"
+#include "badguy/mrrocket.hpp"
+#include "badguy/poisonivy.hpp"
+#include "badguy/snail.hpp"
+#include "badguy/skullyhop.hpp"
+#include "random_generator.hpp"
+
+Dispenser::Dispenser(const lisp::Lisp& reader)
+ : BadGuy(reader, "images/creatures/dispenser/dispenser.sprite")
+{
+ reader.get("cycle", cycle);
+ reader.get("badguy", badguy);
+ if (badguy == "mrrocket") {
+ if (start_dir == AUTO) log_warning << "Setting a Dispenser's direction to AUTO is no good idea" << std::endl;
+ sprite->set_action(dir == LEFT ? "working-left" : "working-right");
+ }
+ else {sprite->set_action("dropper");}
+ bbox.set_size(sprite->get_current_hitbox_width(), sprite->get_current_hitbox_height());
+ countMe = false;
+}
+
+void
+Dispenser::write(lisp::Writer& writer)
+{
+ writer.start_list("dispenser");
+
+ writer.write_float("x", start_position.x);
+ writer.write_float("y", start_position.y);
+ writer.write_float("cycle", cycle);
+ writer.write_string("badguy", badguy);
+
+ writer.end_list("dispenser");
+}
+
+void
+Dispenser::activate()
+{
+ if(frozen)
+ return;
+ dispense_timer.start(cycle, true);
+ launch_badguy();
+}
+
+void
+Dispenser::deactivate()
+{
+ dispense_timer.stop();
+}
+
+//TODO: Add launching velocity to certain badguys
+bool
+Dispenser::collision_squished(GameObject& object)
+{
+ //TODO: Should it act like a normal tile when killed?
+ sprite->set_action(dir == LEFT ? "broken-left" : "broken-right");
+ dispense_timer.start(0);
+ Player* player = dynamic_cast<Player*>(&object);
+ if (player) player->bounce(*this);
+ kill_squished(object);
+ return true;
+}
+
+void
+Dispenser::active_update(float )
+{
+ if (dispense_timer.check()) {
+ launch_badguy();
+ }
+}
+
+// Add themed randomizer
+void
+Dispenser::launch_badguy()
+{
+ //FIXME: Does is_offscreen() work right here?
+ if (!is_offscreen()) {
+ if (badguy == "snowball")
+ Sector::current()->add_object(new SnowBall(Vector(get_pos().x, get_pos().y+32), dir));
+ else if (badguy == "bouncingsnowball")
+ Sector::current()->add_object(new BouncingSnowball(Vector(get_pos().x, get_pos().y+32), dir));
+ else if (badguy == "mrbomb")
+ Sector::current()->add_object(new MrBomb(Vector(get_pos().x, get_pos().y+32), dir));
+ else if (badguy == "mriceblock")
+ Sector::current()->add_object(new MrIceBlock(Vector(get_pos().x, get_pos().y+32), dir));
+ else if (badguy == "snail")
+ Sector::current()->add_object(new Snail(Vector(get_pos().x, get_pos().y+32), dir));
+ else if (badguy == "mrrocket") {
+ Sector::current()->add_object(new MrRocket(Vector(get_pos().x+(dir == LEFT ? -32 : 32), get_pos().y), dir));}
+ else if (badguy == "poisonivy")
+ Sector::current()->add_object(new PoisonIvy(Vector(get_pos().x, get_pos().y+32), dir));
+ else if (badguy == "skullyhop")
+ Sector::current()->add_object(new SkullyHop(Vector(get_pos().x, get_pos().y+44), dir));
+ else if (badguy == "random")
+ {
+ switch (systemRandom.rand(7))
+ {
+ case 0: Sector::current()->add_object(new SnowBall(Vector(get_pos().x, get_pos().y+32), dir)); break;
+ case 1: Sector::current()->add_object(new BouncingSnowball(Vector(get_pos().x, get_pos().y+32), dir)); break;
+ case 2: Sector::current()->add_object(new MrBomb(Vector(get_pos().x, get_pos().y+32), dir)); break;
+ case 3: Sector::current()->add_object(new MrIceBlock(Vector(get_pos().x, get_pos().y+32), dir)); break;
+ case 4: Sector::current()->add_object(new PoisonIvy(Vector(get_pos().x, get_pos().y+32), dir)); break;
+ case 5: Sector::current()->add_object(new Snail(Vector(get_pos().x, get_pos().y+32), dir)); break;
+ case 6: Sector::current()->add_object(new SkullyHop(Vector(get_pos().x, get_pos().y+44), dir)); break;
+ }
+ }
+ }
+}
+
+void
+Dispenser::freeze()
+{
+ BadGuy::freeze();
+ dispense_timer.stop();
+}
+
+void
+Dispenser::unfreeze()
+{
+ BadGuy::unfreeze();
+ activate();
+}
+
+bool
+Dispenser::is_freezable() const
+{
+ return true;
+}
+IMPLEMENT_FACTORY(Dispenser, "dispenser")
--- /dev/null
+// $Id$
+//
+// SuperTux
+// Copyright (C) 2006 Matthias Braun <matze@braunis.de>
+//
+// 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 __DISPENSER_H__
+#define __DISPENSER_H__
+
+#include "badguy.hpp"
+#include "timer.hpp"
+
+class Dispenser : public BadGuy
+{
+public:
+ Dispenser(const lisp::Lisp& reader);
+
+ void activate();
+ void deactivate();
+ void write(lisp::Writer& writer);
+ void active_update(float elapsed_time);
+
+ void freeze();
+ void unfreeze();
+ bool is_freezable() const;
+
+ virtual Dispenser* clone() const { return new Dispenser(*this); }
+
+protected:
+ bool collision_squished(GameObject& object);
+ void launch_badguy();
+ float cycle;
+ std::string badguy;
+ Timer dispense_timer;
+};
+
+#endif
--- /dev/null
+// $Id$
+//
+// SuperTux
+// Copyright (C) 2006 Matthias Braun <matze@braunis.de>
+//
+// 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 <config.h>
+
+#include "fish.hpp"
+#include "tile.hpp"
+#include "object/tilemap.hpp"
+#include "log.hpp"
+
+static const float FISH_JUMP_POWER = -600;
+static const float FISH_WAIT_TIME = 1;
+
+Fish::Fish(const lisp::Lisp& reader)
+ : BadGuy(reader, "images/creatures/fish/fish.sprite", LAYER_TILES-1), stop_y(0)
+{
+ physic.enable_gravity(true);
+}
+
+Fish::Fish(const Vector& pos)
+ : BadGuy(pos, "images/creatures/fish/fish.sprite", LAYER_TILES-1), stop_y(0)
+{
+ physic.enable_gravity(true);
+}
+
+void
+Fish::write(lisp::Writer& writer)
+{
+ writer.start_list("fish");
+
+ writer.write_float("x", start_position.x);
+ writer.write_float("y", start_position.y);
+
+ writer.end_list("fish");
+}
+
+void
+Fish::collision_solid(const CollisionHit& chit)
+{
+ hit(chit);
+}
+
+HitResponse
+Fish::collision_badguy(BadGuy& , const CollisionHit& chit)
+{
+ return hit(chit);
+}
+
+void
+Fish::draw(DrawingContext& context)
+{
+ if(waiting.started())
+ return;
+
+ BadGuy::draw(context);
+}
+
+HitResponse
+Fish::hit(const CollisionHit& hit)
+{
+ if(hit.top) {
+ physic.set_velocity_y(0);
+ }
+
+ return CONTINUE;
+}
+
+void
+Fish::collision_tile(uint32_t tile_attributes)
+{
+ if ((tile_attributes & Tile::WATER) && (physic.get_velocity_y() >= 0)) {
+
+ // initialize stop position if uninitialized
+ if (stop_y == 0) stop_y = get_pos().y + get_bbox().get_height();
+
+ // stop when we have reached the stop position
+ if (get_pos().y >= stop_y) {
+ if(!frozen)
+ start_waiting();
+ movement = Vector(0, 0);
+ }
+
+ }
+}
+
+void
+Fish::active_update(float elapsed_time)
+{
+ BadGuy::active_update(elapsed_time);
+
+ // waited long enough?
+ if(waiting.check()) {
+ jump();
+ }
+
+ // set sprite
+ if(!frozen)
+ sprite->set_action(physic.get_velocity_y() < 0 ? "normal" : "down");
+
+ // we can't afford flying out of the tilemap, 'cause the engine would remove us.
+ if ((get_pos().y - 31.8) < 0) // too high, let us fall
+ {
+ physic.set_velocity_y(0);
+ physic.enable_gravity(true);
+ }
+}
+
+void
+Fish::start_waiting()
+{
+ waiting.start(FISH_WAIT_TIME);
+ set_group(COLGROUP_DISABLED);
+ physic.enable_gravity(false);
+ physic.set_velocity_y(0);
+}
+
+void
+Fish::jump()
+{
+ physic.set_velocity_y(FISH_JUMP_POWER);
+ physic.enable_gravity(true);
+ set_group(COLGROUP_MOVING);
+}
+
+void
+Fish::freeze()
+{
+ BadGuy::freeze();
+ sprite->set_action(physic.get_velocity_y() < 0 ? "iced" : "iced-down");
+ waiting.stop();
+}
+
+void
+Fish::unfreeze()
+{ // does this happen at all? (or do fishes die when they fall frozen?)
+ BadGuy::unfreeze();
+ start_waiting();
+}
+
+bool
+Fish::is_freezable() const
+{
+ return true;
+}
+
+IMPLEMENT_FACTORY(Fish, "fish")
--- /dev/null
+// $Id$
+//
+// SuperTux
+// Copyright (C) 2006 Matthias Braun <matze@braunis.de>
+//
+// 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 __FISH_H__
+#define __FISH_H__
+
+#include "badguy.hpp"
+
+class Fish : public BadGuy
+{
+public:
+ Fish(const lisp::Lisp& );
+ Fish(const Vector& pos);
+
+ void draw(DrawingContext& context);
+
+ void collision_solid(const CollisionHit& hit);
+ HitResponse collision_badguy(BadGuy& , const CollisionHit& );
+ void collision_tile(uint32_t tile_attributes);
+
+ void write(lisp::Writer& );
+ void active_update(float);
+
+ void freeze();
+ void unfreeze();
+ bool is_freezable() const;
+
+ virtual Fish* clone() const { return new Fish(*this); }
+
+private:
+ HitResponse hit(const CollisionHit& );
+ void start_waiting();
+ void jump();
+
+ Timer waiting;
+ float stop_y; /**< y-coordinate to stop at */
+};
+
+#endif
--- /dev/null
+// $Id$
+//
+// SuperTux
+// Copyright (C) 2006 Matthias Braun <matze@braunis.de>
+//
+// 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 <config.h>
+
+#include "flame.hpp"
+#include "log.hpp"
+
+static const std::string SOUNDFILE = "sounds/flame.wav";
+
+Flame::Flame(const lisp::Lisp& reader)
+ : BadGuy(reader, "images/creatures/flame/flame.sprite", LAYER_FLOATINGOBJECTS), angle(0), radius(100), speed(2)
+{
+ reader.get("radius", radius);
+ reader.get("speed", speed);
+ bbox.set_pos(Vector(start_position.x + cos(angle) * radius,
+ start_position.y + sin(angle) * radius));
+ countMe = false;
+ sound_manager->preload(SOUNDFILE);
+}
+
+void
+Flame::write(lisp::Writer& writer)
+{
+ writer.start_list("flame");
+
+ writer.write_float("x", start_position.x);
+ writer.write_float("y", start_position.y);
+ writer.write_float("radius", radius);
+ writer.write_float("speed", speed);
+
+ writer.end_list("flame");
+}
+
+void
+Flame::active_update(float elapsed_time)
+{
+ angle = fmodf(angle + elapsed_time * speed, (float) (2*M_PI));
+ Vector newpos(start_position.x + cos(angle) * radius,
+ start_position.y + sin(angle) * radius);
+ movement = newpos - get_pos();
+
+ sound_source->set_position(get_pos());
+}
+
+void
+Flame::activate()
+{
+ set_group(COLGROUP_TOUCHABLE);
+
+ sound_source.reset(sound_manager->create_sound_source(SOUNDFILE));
+ sound_source->set_position(get_pos());
+ sound_source->set_looping(true);
+ sound_source->set_gain(2.0);
+ sound_source->set_reference_distance(32);
+ sound_source->play();
+}
+
+void
+Flame::deactivate()
+{
+ sound_source.reset();
+}
+
+void
+Flame::kill_fall()
+{
+}
+
+IMPLEMENT_FACTORY(Flame, "flame")
--- /dev/null
+// $Id$
+//
+// SuperTux
+// Copyright (C) 2006 Matthias Braun <matze@braunis.de>
+//
+// 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 __FLAME_H__
+#define __FLAME_H__
+
+#include "badguy.hpp"
+
+class Flame : public BadGuy
+{
+public:
+ Flame(const lisp::Lisp& reader);
+ Flame(const Flame& flame);
+
+ void activate();
+ void deactivate();
+
+ void write(lisp::Writer& write);
+ void active_update(float elapsed_time);
+ void kill_fall();
+
+private:
+ float angle;
+ float radius;
+ float speed;
+
+ std::auto_ptr<SoundSource> sound_source;
+};
+
+#endif
--- /dev/null
+// $Id$
+//
+// SuperTux
+// Copyright (C) 2006 Matthias Braun <matze@braunis.de>
+//
+// 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 <config.h>
+
+#include <stdio.h>
+
+#include "flyingsnowball.hpp"
+#include "random_generator.hpp"
+#include "object/sprite_particle.hpp"
+
+static const float FLYTIME = 1.0f;
+static const float FLYSPEED = -100.0f;
+
+namespace {
+ const float PUFF_PROBABILITY = 0.1f; /**< chanche of puffs being spawned in the current cycle */
+ const float PUFF_INTERVAL_MIN = 0.1f; /**< spawn new puff of smoke at most that often */
+ const float PUFF_INTERVAL_MAX = 1.1f; /**< spawn new puff of smoke at least that often */
+}
+
+FlyingSnowBall::FlyingSnowBall(const lisp::Lisp& reader)
+ : BadGuy(reader, "images/creatures/flying_snowball/flying_snowball.sprite")
+{
+ physic.enable_gravity(false);
+}
+
+FlyingSnowBall::FlyingSnowBall(const Vector& pos)
+ : BadGuy(pos, "images/creatures/flying_snowball/flying_snowball.sprite")
+{
+ physic.enable_gravity(false);
+}
+
+void
+FlyingSnowBall::write(lisp::Writer& writer)
+{
+ writer.start_list("flyingsnowball");
+
+ writer.write_float("x", start_position.x);
+ writer.write_float("y", start_position.y);
+
+ writer.end_list("flyingsnowball");
+}
+
+void
+FlyingSnowBall::activate()
+{
+ sprite->set_action(dir == LEFT ? "left" : "right");
+ mode = FLY_UP;
+ physic.set_velocity_y(FLYSPEED);
+ timer.start(FLYTIME/2);
+ puff_timer.start(systemRandom.randf(PUFF_INTERVAL_MIN, PUFF_INTERVAL_MAX));
+}
+
+bool
+FlyingSnowBall::collision_squished(GameObject& object)
+{
+ sprite->set_action(dir == LEFT ? "squished-left" : "squished-right");
+ kill_squished(object);
+ return true;
+}
+
+void
+FlyingSnowBall::collision_solid(const CollisionHit& hit)
+{
+ if(hit.top || hit.bottom) {
+ physic.set_velocity_y(0);
+ }
+}
+
+void
+FlyingSnowBall::active_update(float elapsed_time)
+{
+ if(timer.check()) {
+ if(mode == FLY_UP) {
+ mode = FLY_DOWN;
+ physic.set_velocity_y(-FLYSPEED);
+
+ // stop puffing
+ puff_timer.stop();
+
+ } else if(mode == FLY_DOWN) {
+ mode = FLY_UP;
+ physic.set_velocity_y(FLYSPEED);
+
+ // roll a dice whether to start puffing
+ if (systemRandom.randf(0, 1) < PUFF_PROBABILITY) {
+ puff_timer.start(systemRandom.randf(PUFF_INTERVAL_MIN, PUFF_INTERVAL_MAX));
+ }
+
+ }
+ timer.start(FLYTIME);
+ }
+ movement=physic.get_movement(elapsed_time);
+
+ Player* player = this->get_nearest_player();
+ if (player) {
+ dir = (player->get_pos().x > get_pos().x) ? RIGHT : LEFT;
+ sprite->set_action(dir == LEFT ? "left" : "right");
+ }
+
+ // spawn smoke puffs
+ if (puff_timer.check()) {
+ Vector ppos = bbox.get_middle();
+ Vector pspeed = Vector(systemRandom.randf(-10, 10), 150);
+ Vector paccel = Vector(0,0);
+ Sector::current()->add_object(new SpriteParticle("images/objects/particles/smoke.sprite", "default", ppos, ANCHOR_MIDDLE, pspeed, paccel, LAYER_OBJECTS-1));
+ puff_timer.start(systemRandom.randf(PUFF_INTERVAL_MIN, PUFF_INTERVAL_MAX));
+ }
+}
+
+IMPLEMENT_FACTORY(FlyingSnowBall, "flyingsnowball")
--- /dev/null
+// $Id$
+//
+// SuperTux
+// Copyright (C) 2006 Matthias Braun <matze@braunis.de>
+//
+// 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 __FLYINGSNOWBALL_H__
+#define __FLYINGSNOWBALL_H__
+
+#include "badguy.hpp"
+
+class FlyingSnowBall : public BadGuy
+{
+public:
+ FlyingSnowBall(const lisp::Lisp& reader);
+ FlyingSnowBall(const Vector& pos);
+
+ void activate();
+ void write(lisp::Writer& writer);
+ void active_update(float elapsed_time);
+ void collision_solid(const CollisionHit& hit);
+
+ virtual FlyingSnowBall* clone() const { return new FlyingSnowBall(*this); }
+
+protected:
+ enum FlyingSnowballMode {
+ FLY_UP,
+ FLY_DOWN
+ };
+ FlyingSnowballMode mode;
+ bool collision_squished(GameObject& object);
+private:
+ Timer timer;
+ Timer puff_timer; /**< time until the next smoke puff is spawned */
+};
+
+#endif
--- /dev/null
+// $Id$
+//
+// SuperTux - Boss "GhostTree"
+// Copyright (C) 2007 Matthias Braun <matze@braunis.de>
+//
+// 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 <config.h>
+
+#include "ghosttree.hpp"
+#include "treewillowisp.hpp"
+#include "sprite/sprite_manager.hpp"
+#include "root.hpp"
+#include "random_generator.hpp"
+#include "object/lantern.hpp"
+
+static const size_t WILLOWISP_COUNT = 10;
+static const float ROOT_TOP_OFFSET = 64;
+static const float WILLOWISP_TOP_OFFSET = -64;
+static const Vector SUCK_TARGET_OFFSET = Vector(-16,-16);
+static const float SUCK_TARGET_SPREAD = 8;
+
+GhostTree::GhostTree(const lisp::Lisp& lisp)
+ : BadGuy(lisp, "images/creatures/ghosttree/ghosttree.sprite",
+ LAYER_OBJECTS - 10), mystate(STATE_IDLE),
+ willo_spawn_y(0), willo_radius(200), willo_speed(1.8f), willo_color(0),
+ treecolor(0), suck_lantern(0)
+{
+ glow_sprite.reset(sprite_manager->create("images/creatures/ghosttree/ghosttree-glow.sprite"));
+}
+
+GhostTree::~GhostTree()
+{
+}
+
+void
+GhostTree::die()
+{
+ mystate = STATE_DYING;
+ sprite->set_action("dying", 1);
+ glow_sprite->set_action("dying", 1);
+
+ std::vector<TreeWillOWisp*>::iterator iter;
+ for(iter = willowisps.begin(); iter != willowisps.end(); ++iter) {
+ TreeWillOWisp *willo = *iter;
+ willo->vanish();
+ }
+}
+
+void
+GhostTree::activate()
+{
+ willowisp_timer.start(1.0f, true);
+ colorchange_timer.start(13, true);
+ root_timer.start(5, true);
+ set_group(COLGROUP_TOUCHABLE);
+}
+
+void
+GhostTree::active_update(float elapsed_time)
+{
+ (void) elapsed_time;
+
+ if (mystate == STATE_IDLE) {
+ if(colorchange_timer.check()) {
+ sound_manager->play("sounds/tree_howling.ogg", get_pos());
+ suck_timer.start(3);
+ treecolor = (treecolor + 1) % 3;
+
+ Color col;
+ switch(treecolor) {
+ case 0: col = Color(1, 0, 0); break;
+ case 1: col = Color(0, 1, 0); break;
+ case 2: col = Color(0, 0, 1); break;
+ case 3: col = Color(1, 1, 0); break;
+ case 4: col = Color(1, 0, 1); break;
+ case 5: col = Color(0, 1, 1); break;
+ default: assert(false);
+ }
+ glow_sprite->set_color(col);
+ }
+
+ if(suck_timer.check()) {
+ Color col = glow_sprite->get_color();
+ sound_manager->play("sounds/tree_suck.ogg", get_pos());
+ std::vector<TreeWillOWisp*>::iterator iter;
+ for(iter = willowisps.begin(); iter != willowisps.end(); ++iter) {
+ TreeWillOWisp *willo = *iter;
+ if(willo->get_color() == col) {
+ willo->start_sucking(get_bbox().get_middle() + SUCK_TARGET_OFFSET + Vector(systemRandom.randf(-SUCK_TARGET_SPREAD, SUCK_TARGET_SPREAD), systemRandom.randf(-SUCK_TARGET_SPREAD, SUCK_TARGET_SPREAD)));
+ }
+ }
+ mystate = STATE_SUCKING;
+ }
+
+ if(willowisp_timer.check()) {
+ if(willowisps.size() < WILLOWISP_COUNT) {
+ Vector pos = Vector(bbox.get_width() / 2, bbox.get_height() / 2 + willo_spawn_y + WILLOWISP_TOP_OFFSET);
+ TreeWillOWisp *willowisp
+ = new TreeWillOWisp(this, pos, 200 + willo_radius, willo_speed);
+
+ Sector::current()->add_object(willowisp);
+ willowisps.push_back(willowisp);
+
+ willo_spawn_y -= 40;
+ if(willo_spawn_y < -160)
+ willo_spawn_y = 0;
+
+ willo_radius += 20;
+ if(willo_radius > 120)
+ willo_radius = 0;
+
+ if(willo_speed == 1.8f) {
+ willo_speed = 1.5f;
+ } else {
+ willo_speed = 1.8f;
+ }
+
+ do {
+ willo_color = (willo_color + 1) % 3;
+ } while(willo_color == treecolor);
+
+ switch(willo_color) {
+ case 0: willowisp->set_color(Color(1, 0, 0)); break;
+ case 1: willowisp->set_color(Color(0, 1, 0)); break;
+ case 2: willowisp->set_color(Color(0, 0, 1)); break;
+ case 3: willowisp->set_color(Color(1, 1, 0)); break;
+ case 4: willowisp->set_color(Color(1, 0, 1)); break;
+ case 5: willowisp->set_color(Color(0, 1, 1)); break;
+ default: assert(false);
+ }
+ }
+ }
+
+ if(root_timer.check()) {
+ /* TODO indicate root with an animation */
+ Player* player = get_nearest_player();
+ Root* root = new Root(Vector(player->get_bbox().get_left(), get_bbox().get_bottom()+ROOT_TOP_OFFSET));
+ Sector::current()->add_object(root);
+ }
+ } else if (mystate == STATE_SWALLOWING) {
+ if (suck_lantern) {
+ // suck in lantern
+ assert (suck_lantern);
+ Vector pos = suck_lantern->get_pos();
+ Vector delta = get_bbox().get_middle() + SUCK_TARGET_OFFSET - pos;
+ Vector dir = delta.unit();
+ if (delta.norm() < 1) {
+ dir = delta;
+ suck_lantern->ungrab(*this, RIGHT);
+ suck_lantern->remove_me();
+ suck_lantern = 0;
+ sprite->set_action("swallow", 1);
+ } else {
+ pos += dir;
+ suck_lantern->grab(*this, pos, RIGHT);
+ }
+ } else {
+ // wait until lantern is swallowed
+ if (sprite->animation_done()) {
+ if (is_color_deadly(suck_lantern_color)) {
+ die();
+ } else {
+ sprite->set_action("default");
+ mystate = STATE_IDLE;
+ spawn_lantern();
+ }
+ }
+ }
+ }
+}
+
+bool
+GhostTree::is_color_deadly(Color color) const {
+ if (color == Color(0,0,0)) return false;
+ Color my_color = glow_sprite->get_color();
+ return ((my_color.red != color.red) || (my_color.green != color.green) || (my_color.blue != color.blue));
+}
+
+void
+GhostTree::willowisp_died(TreeWillOWisp *willowisp)
+{
+ if ((mystate == STATE_SUCKING) && (willowisp->was_sucked)) {
+ mystate = STATE_IDLE;
+ }
+ willowisps.erase(std::find(willowisps.begin(), willowisps.end(), willowisp));
+}
+
+void
+GhostTree::draw(DrawingContext& context)
+{
+ BadGuy::draw(context);
+
+ context.push_target();
+ context.push_transform();
+ context.set_target(DrawingContext::LIGHTMAP);
+ if (mystate == STATE_SUCKING) {
+ context.set_alpha(0.5 + fmodf(game_time, 0.5));
+ } else {
+ context.set_alpha(0.5);
+ }
+ glow_sprite->draw(context, get_pos(), layer);
+ context.pop_transform();
+ context.pop_target();
+}
+
+bool
+GhostTree::collides(GameObject& other, const CollisionHit& ) {
+ if (mystate != STATE_SUCKING) return false;
+ if (dynamic_cast<Lantern*>(&other)) return true;
+ if (dynamic_cast<Player*>(&other)) return true;
+ return false;
+}
+
+HitResponse
+GhostTree::collision(GameObject& other, const CollisionHit& ) {
+ if(mystate != STATE_SUCKING) return ABORT_MOVE;
+
+ Player* player = dynamic_cast<Player*>(&other);
+ if (player) {
+ player->kill(false);
+ }
+
+ Lantern* lantern = dynamic_cast<Lantern*>(&other);
+ if (lantern) {
+ suck_lantern = lantern;
+ suck_lantern->grab(*this, suck_lantern->get_pos(), RIGHT);
+ suck_lantern_color = lantern->get_color();
+ mystate = STATE_SWALLOWING;
+ }
+
+ return ABORT_MOVE;
+}
+
+void
+GhostTree::spawn_lantern() {
+ Lantern* lantern = new Lantern(get_bbox().get_middle() + SUCK_TARGET_OFFSET);
+ Sector::current()->add_object(lantern);
+}
+
+IMPLEMENT_FACTORY(GhostTree, "ghosttree");
+
--- /dev/null
+// $Id$
+//
+// SuperTux - Boss "GhostTree"
+// Copyright (C) 2007 Matthias Braun <matze@braunis.de>
+//
+// 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 __GHOSTTREE_H__
+#define __GHOSTTREE_H__
+
+#include <vector>
+#include "badguy.hpp"
+
+class TreeWillOWisp;
+class Lantern;
+
+class GhostTree : public BadGuy
+{
+public:
+ GhostTree(const lisp::Lisp& lisp);
+ ~GhostTree();
+
+ virtual bool is_flammable() const { return false; }
+ virtual bool is_freezable() const { return false; }
+ virtual void kill_fall() { }
+
+ void activate();
+ void active_update(float elapsed_time);
+ void willowisp_died(TreeWillOWisp* willowisp);
+ virtual void draw(DrawingContext& context);
+
+ virtual bool collides(GameObject& other, const CollisionHit& hit);
+ virtual HitResponse collision(GameObject& other, const CollisionHit& hit);
+
+ void die();
+
+private:
+ enum MyState {
+ STATE_IDLE, STATE_SUCKING, STATE_SWALLOWING, STATE_DYING
+ };
+ MyState mystate;
+ Timer willowisp_timer;
+ float willo_spawn_y;
+ float willo_radius;
+ float willo_speed;
+ int willo_color;
+
+ std::auto_ptr<Sprite> glow_sprite;
+ Timer colorchange_timer;
+ Timer suck_timer;
+ Timer root_timer;
+ int treecolor;
+ Color suck_lantern_color;
+
+ Lantern* suck_lantern; /**< Lantern that is currently being sucked in */
+
+ std::vector<TreeWillOWisp*> willowisps;
+
+ bool is_color_deadly(Color color) const;
+ void spawn_lantern();
+};
+
+#endif
+
--- /dev/null
+// $Id$
+//
+// SuperTux - Badguy "Igel"
+// Copyright (C) 2006 Christoph Sommer <christoph.sommer@2006.expires.deltadevelopment.de>
+//
+// 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 <config.h>
+
+#include "igel.hpp"
+#include "object/block.hpp"
+#include "sector.hpp"
+#include "object/bullet.hpp"
+
+namespace {
+ const float WALKSPEED = 80; /**< speed at which we walk around */
+ const float TURN_RECOVER_TIME = 0.5; /**< seconds before we will again turn around when shot at */
+ const float RANGE_OF_VISION = 256; /**< range in px at which we can see bullets */
+}
+
+Igel::Igel(const lisp::Lisp& reader)
+ : WalkingBadguy(reader, "images/creatures/igel/igel.sprite", "walking-left", "walking-right")
+{
+ walk_speed = WALKSPEED;
+ max_drop_height = 16;
+}
+
+Igel::Igel(const Vector& pos, Direction d)
+ : WalkingBadguy(pos, d, "images/creatures/igel/igel.sprite", "walking-left", "walking-right")
+{
+ walk_speed = WALKSPEED;
+ max_drop_height = 16;
+}
+
+void
+Igel::write(lisp::Writer& writer)
+{
+ writer.start_list("igel");
+ WalkingBadguy::write(writer);
+ writer.end_list("igel");
+}
+
+void
+Igel::be_normal()
+{
+ activate();
+}
+
+void
+Igel::turn_around()
+{
+ WalkingBadguy::turn_around();
+ turn_recover_timer.start(TURN_RECOVER_TIME);
+}
+
+bool
+Igel::can_see(const MovingObject& o)
+{
+ Rect mb = get_bbox();
+ Rect ob = o.get_bbox();
+
+ bool inReach_left = ((ob.p2.x < mb.p1.x) && (ob.p2.x >= mb.p1.x-((dir == LEFT) ? RANGE_OF_VISION : 0)));
+ bool inReach_right = ((ob.p1.x > mb.p2.x) && (ob.p1.x <= mb.p2.x+((dir == RIGHT) ? RANGE_OF_VISION : 0)));
+ bool inReach_top = (ob.p2.y >= mb.p1.y);
+ bool inReach_bottom = (ob.p1.y <= mb.p2.y);
+
+ return ((inReach_left || inReach_right) && inReach_top && inReach_bottom);
+}
+
+void
+Igel::active_update(float elapsed_time)
+{
+ bool wants_to_flee = false;
+
+ // check if we see a fire bullet
+ Sector* sector = Sector::current();
+ for (Sector::GameObjects::iterator i = sector->gameobjects.begin(); i != sector->gameobjects.end(); ++i) {
+ Bullet* bullet = dynamic_cast<Bullet*>(*i);
+ if (!bullet) continue;
+ if (bullet->get_type() != FIRE_BONUS) continue;
+ if (can_see(*bullet)) wants_to_flee = true;
+ }
+
+ // if we flee, handle this ourselves
+ if (wants_to_flee && (!turn_recover_timer.started())) {
+ turn_around();
+ BadGuy::active_update(elapsed_time);
+ return;
+ }
+
+ // else adhere to default behaviour
+ WalkingBadguy::active_update(elapsed_time);
+}
+
+HitResponse
+Igel::collision_bullet(Bullet& bullet, const CollisionHit& hit)
+{
+ // default reaction if hit on front side
+ if (((dir == LEFT) && hit.left) || ((dir == RIGHT) && hit.right)) {
+ return BadGuy::collision_bullet(bullet, hit);
+ }
+
+ // else make bullet ricochet and ignore the hit
+ bullet.ricochet(*this, hit);
+ return FORCE_MOVE;
+}
+
+bool
+Igel::collision_squished(GameObject& )
+{
+ // this will hurt
+ return false;
+}
+
+IMPLEMENT_FACTORY(Igel, "igel")
--- /dev/null
+// $Id$
+//
+// SuperTux - Badguy "Igel"
+// Copyright (C) 2006 Christoph Sommer <christoph.sommer@2006.expires.deltadevelopment.de>
+//
+// 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 __IGEL_H__
+#define __IGEL_H__
+
+#include "walking_badguy.hpp"
+#include "moving_object.hpp"
+
+/**
+ * Badguy "Igel" - a hedgehog that can absorb bullets
+ */
+class Igel : public WalkingBadguy
+{
+public:
+ Igel(const lisp::Lisp& reader);
+ Igel(const Vector& pos, Direction d);
+
+ void write(lisp::Writer& writer);
+ HitResponse collision_bullet(Bullet& bullet, const CollisionHit& hit);
+
+ void active_update(float elapsed_time);
+
+ virtual Igel* clone() const { return new Igel(*this); }
+
+protected:
+ bool collision_squished(GameObject& object);
+ void be_normal(); /**< switch to state STATE_NORMAL */
+ void turn_around(); /**< reverse direction, assumes we are in STATE_NORMAL */
+ bool can_see(const MovingObject& o); /**< check if we can see o */
+
+private:
+ Timer turn_recover_timer; /**< wait time until we will turn around again when shot at */
+
+};
+
+#endif
--- /dev/null
+// $Id$
+//
+// SuperTux
+// Copyright (C) 2006 Matthias Braun <matze@braunis.de>
+//
+// 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 <config.h>
+
+#include "jumpy.hpp"
+
+static const float JUMPSPEED=-600;
+static const float JUMPY_MID_TOLERANCE=4;
+static const float JUMPY_LOW_TOLERANCE=2;
+
+Jumpy::Jumpy(const lisp::Lisp& reader)
+ : BadGuy(reader, "images/creatures/jumpy/jumpy.sprite"), groundhit_pos_set(false)
+{
+}
+
+void
+Jumpy::write(lisp::Writer& writer)
+{
+ writer.start_list("jumpy");
+
+ writer.write_float("x", start_position.x);
+ writer.write_float("y", start_position.y);
+
+ writer.end_list("jumpy");
+}
+
+void
+Jumpy::collision_solid(const CollisionHit& chit)
+{
+ hit(chit);
+}
+
+HitResponse
+Jumpy::collision_badguy(BadGuy& , const CollisionHit& chit)
+{
+ return hit(chit);
+}
+
+HitResponse
+Jumpy::hit(const CollisionHit& chit)
+{
+ if(chit.bottom) {
+ if (!groundhit_pos_set)
+ {
+ pos_groundhit = get_pos();
+ groundhit_pos_set = true;
+ }
+
+ physic.set_velocity_y(frozen ? 0 : JUMPSPEED);
+ // TODO create a nice sound for this...
+ //sound_manager->play("sounds/skid.wav");
+ } else if(chit.top) {
+ physic.set_velocity_y(0);
+ }
+
+ return CONTINUE;
+}
+
+void
+Jumpy::active_update(float elapsed_time)
+{
+ BadGuy::active_update(elapsed_time);
+
+ if(frozen)
+ return;
+
+ Player* player = this->get_nearest_player();
+ if (player)
+ {
+ dir = (player->get_pos().x > get_pos().x) ? RIGHT : LEFT;
+ }
+
+ if (!groundhit_pos_set)
+ {
+ sprite->set_action(dir == LEFT ? "left-middle" : "right-middle");
+ return;
+ }
+
+ if ( get_pos().y < (pos_groundhit.y - JUMPY_MID_TOLERANCE ) )
+ sprite->set_action(dir == LEFT ? "left-up" : "right-up");
+ else if ( get_pos().y >= (pos_groundhit.y - JUMPY_MID_TOLERANCE) &&
+ get_pos().y < (pos_groundhit.y - JUMPY_LOW_TOLERANCE) )
+ sprite->set_action(dir == LEFT ? "left-middle" : "right-middle");
+ else
+ sprite->set_action(dir == LEFT ? "left-down" : "right-down");
+}
+
+void
+Jumpy::freeze()
+{
+ BadGuy::freeze();
+ physic.set_velocity_y(std::max(0.0f, physic.get_velocity_y()));
+ sprite->set_action(dir == LEFT ? "left-iced" : "right-iced");
+}
+
+bool
+Jumpy::is_freezable() const
+{
+ return true;
+}
+
+IMPLEMENT_FACTORY(Jumpy, "jumpy")
--- /dev/null
+// $Id$
+//
+// SuperTux
+// Copyright (C) 2006 Matthias Braun <matze@braunis.de>
+//
+// 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 __JUMPY_H__
+#define __JUMPY_H__
+
+#include "badguy.hpp"
+
+class Jumpy : public BadGuy
+{
+public:
+ Jumpy(const lisp::Lisp& reader);
+
+ void collision_solid(const CollisionHit& hit);
+ HitResponse collision_badguy(BadGuy& other, const CollisionHit& hit);
+
+ void write(lisp::Writer& writer);
+ void active_update(float);
+
+ void freeze();
+ bool is_freezable() const;
+
+ virtual Jumpy* clone() const { return new Jumpy(*this); }
+
+private:
+ HitResponse hit(const CollisionHit& hit);
+ Vector pos_groundhit;
+ bool groundhit_pos_set;
+};
+
+#endif
--- /dev/null
+// $Id$
+//
+// SuperTux
+// Copyright (C) 2006 Matthias Braun <matze@braunis.de>
+//
+// 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 <config.h>
+
+#include "kugelblitz.hpp"
+#include "object/tilemap.hpp"
+#include "object/camera.hpp"
+#include "tile.hpp"
+#include "random_generator.hpp"
+
+#define LIFETIME 5
+#define MOVETIME 0.75
+#define BASE_SPEED 200
+#define RAND_SPEED 150
+
+static const float X_OFFSCREEN_DISTANCE = 1600;
+static const float Y_OFFSCREEN_DISTANCE = 1200;
+
+Kugelblitz::Kugelblitz(const lisp::Lisp& reader)
+ : BadGuy(Vector(0,0), "images/creatures/kugelblitz/kugelblitz.sprite"), groundhit_pos_set(false)
+{
+ reader.get("x", start_position.x);
+ sprite->set_action("falling");
+ physic.enable_gravity(false);
+}
+
+void
+Kugelblitz::write(lisp::Writer& writer)
+{
+ writer.start_list("kugelblitz");
+
+ writer.write_float("x", start_position.x);
+
+ writer.end_list("kugelblitz");
+}
+
+void
+Kugelblitz::activate()
+{
+ physic.set_velocity_y(300);
+ physic.set_velocity_x(-20); //fall a little to the left
+ direction = 1;
+ dying = false;
+}
+
+void
+Kugelblitz::collision_solid(const CollisionHit& chit)
+{
+ hit(chit);
+}
+
+HitResponse
+Kugelblitz::collision_player(Player& player, const CollisionHit& )
+{
+ if(player.is_invincible()) {
+ explode();
+ return ABORT_MOVE;
+ }
+ // hit from above?
+ if(player.get_movement().y - get_movement().y > 0 && player.get_bbox().p2.y <
+ (get_bbox().p1.y + get_bbox().p2.y) / 2) {
+ // if it's not is it possible to squish us, then this will hurt
+ if(!collision_squished(player))
+ player.kill(false);
+ explode();
+ return FORCE_MOVE;
+ }
+ player.kill(false);
+ explode();
+ return FORCE_MOVE;
+}
+
+HitResponse
+Kugelblitz::collision_badguy(BadGuy& other , const CollisionHit& chit)
+{
+ //Let the Kugelblitz explode, too? The problem with that is that
+ //two Kugelblitzes would cancel each other out on contact...
+ other.kill_fall();
+ return hit(chit);
+}
+
+HitResponse
+Kugelblitz::hit(const CollisionHit& hit)
+{
+ // hit floor?
+ if(hit.bottom) {
+ if (!groundhit_pos_set)
+ {
+ pos_groundhit = get_pos();
+ groundhit_pos_set = true;
+ }
+ sprite->set_action("flying");
+ physic.set_velocity_y(0);
+ //Set random initial speed and direction
+ direction = systemRandom.rand(2)? 1: -1;
+ int speed = (BASE_SPEED + (systemRandom.rand(RAND_SPEED))) * direction;
+ physic.set_velocity_x(speed);
+ movement_timer.start(MOVETIME);
+ lifetime.start(LIFETIME);
+
+ } else if(hit.top) { // bumped on roof
+ physic.set_velocity_y(0);
+ }
+
+ return CONTINUE;
+}
+
+void
+Kugelblitz::active_update(float elapsed_time)
+{
+ if (lifetime.check()) {
+ explode();
+ }
+ else {
+ if (groundhit_pos_set) {
+ if (movement_timer.check()) {
+ if (direction == 1) direction = -1; else direction = 1;
+ int speed = (BASE_SPEED + (systemRandom.rand(RAND_SPEED))) * direction;
+ physic.set_velocity_x(speed);
+ movement_timer.start(MOVETIME);
+ }
+ }
+ /*
+ if (Sector::current()->solids->get_tile_at(get_pos())->getAttributes() == 16) {
+ //HIT WATER
+ Sector::current()->add_object(new Electrifier(75,1421,1.5));
+ Sector::current()->add_object(new Electrifier(76,1422,1.5));
+ explode();
+ }
+ if (Sector::current()->solids->get_tile_at(get_pos())->getAttributes() == 48) {
+ //HIT ELECTRIFIED WATER
+ explode();
+ }
+ */
+ }
+ BadGuy::active_update(elapsed_time);
+}
+
+void
+Kugelblitz::kill_fall()
+{
+}
+
+void
+Kugelblitz::explode()
+{
+ if (!dying) {
+ sprite->set_action("pop");
+ lifetime.start(0.2f);
+ dying = true;
+ }
+ else remove_me();
+}
+
+void
+Kugelblitz::try_activate()
+{
+ //FIXME: Don't activate Kugelblitz before it's on-screen
+ float scroll_x = Sector::current()->camera->get_translation().x;
+ float scroll_y = Sector::current()->camera->get_translation().y;
+
+ /* Activate badguys if they're just around the screen to avoid
+ * the effect of having badguys suddenly popping up from nowhere.
+ */
+ if (start_position.x > scroll_x - X_OFFSCREEN_DISTANCE &&
+ start_position.x < scroll_x - bbox.get_width() &&
+ start_position.y > scroll_y - Y_OFFSCREEN_DISTANCE &&
+ start_position.y < scroll_y + Y_OFFSCREEN_DISTANCE) {
+ dir = RIGHT;
+ set_state(STATE_ACTIVE);
+ activate();
+ } else if (start_position.x > scroll_x &&
+ start_position.x < scroll_x + X_OFFSCREEN_DISTANCE &&
+ start_position.y > scroll_y - Y_OFFSCREEN_DISTANCE &&
+ start_position.y < scroll_y + Y_OFFSCREEN_DISTANCE) {
+ dir = LEFT;
+ set_state(STATE_ACTIVE);
+ activate();
+ } else if (start_position.x > scroll_x - X_OFFSCREEN_DISTANCE &&
+ start_position.x < scroll_x + X_OFFSCREEN_DISTANCE &&
+ ((start_position.y > scroll_y &&
+ start_position.y < scroll_y + Y_OFFSCREEN_DISTANCE) ||
+ (start_position.y > scroll_y - Y_OFFSCREEN_DISTANCE &&
+ start_position.y < scroll_y))) {
+ dir = start_position.x < scroll_x ? RIGHT : LEFT;
+ set_state(STATE_ACTIVE);
+ activate();
+ } else if(state == STATE_INIT
+ && start_position.x > scroll_x - X_OFFSCREEN_DISTANCE
+ && start_position.x < scroll_x + X_OFFSCREEN_DISTANCE
+ && start_position.y > scroll_y - Y_OFFSCREEN_DISTANCE
+ && start_position.y < scroll_y + Y_OFFSCREEN_DISTANCE) {
+ dir = LEFT;
+ set_state(STATE_ACTIVE);
+ activate();
+ }
+}
+
+IMPLEMENT_FACTORY(Kugelblitz, "kugelblitz")
--- /dev/null
+// $Id$
+//
+// SuperTux
+// Copyright (C) 2006 Matthias Braun <matze@braunis.de>
+//
+// 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 __KUGELBLITZ_H__
+#define __KUGELBLITZ_H__
+
+#include "badguy.hpp"
+#include "timer.hpp"
+#include "object/electrifier.hpp"
+
+class Kugelblitz : public BadGuy
+{
+public:
+ Kugelblitz(const lisp::Lisp& reader);
+
+ void activate();
+ HitResponse collision_badguy(BadGuy& other, const CollisionHit& hit);
+ void collision_solid(const CollisionHit& hit);
+ HitResponse collision_player(Player& player, const CollisionHit& hit);
+
+ void write(lisp::Writer& writer);
+ void active_update(float);
+ void kill_fall();
+ void explode();
+
+ virtual Kugelblitz* clone() const { return new Kugelblitz(*this); }
+
+private:
+ void try_activate();
+ HitResponse hit(const CollisionHit& hit);
+ Vector pos_groundhit;
+ bool groundhit_pos_set;
+ bool dying;
+ Timer movement_timer;
+ Timer lifetime;
+ int direction;
+ State state;
+};
+
+#endif
--- /dev/null
+// $Id$
+//
+// SuperTux - Mole Badguy
+// Copyright (C) 2006 Christoph Sommer <christoph.sommer@2006.expires.deltadevelopment.de>
+//
+// 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 <config.h>
+
+#include "mole.hpp"
+#include "mole_rock.hpp"
+#include "tile.hpp"
+#include "object/tilemap.hpp"
+#include "random_generator.hpp"
+#include "log.hpp"
+#include "level.hpp"
+
+static const float IDLE_TIME = 0.2f; /**< time to wait before and after throwing */
+static const float THROW_TIME = 4.6f; /**< time to spend throwing */
+static const float THROW_INTERVAL = 1; /**< time between two thrown rocks */
+static const float THROW_VELOCITY = 400; /**< initial velocity of thrown rocks */
+
+Mole::Mole(const lisp::Lisp& reader)
+ : BadGuy(reader, "images/creatures/mole/mole.sprite", LAYER_TILES-1), state(PRE_THROWING)
+{
+ physic.enable_gravity(false);
+}
+
+Mole::Mole(const Vector& pos)
+ : BadGuy(pos, "images/creatures/mole/mole.sprite", LAYER_TILES-1), state(PRE_THROWING)
+{
+ physic.enable_gravity(false);
+}
+
+void
+Mole::write(lisp::Writer& writer)
+{
+ writer.start_list("mole");
+ writer.write_float("x", start_position.x);
+ writer.write_float("y", start_position.y);
+ writer.end_list("mole");
+}
+
+void
+Mole::activate()
+{
+ if (state != DEAD) set_state(PRE_THROWING);
+}
+
+void
+Mole::kill_fall()
+{
+ set_state(DEAD);
+ sound_manager->play("sounds/fall.wav", get_pos());
+ if (countMe) Sector::current()->get_level()->stats.badguys++;
+}
+
+HitResponse
+Mole::collision_badguy(BadGuy& , const CollisionHit& )
+{
+ return FORCE_MOVE;
+}
+
+bool
+Mole::collision_squished(GameObject& )
+{
+ set_state(DEAD);
+ sound_manager->play("sounds/squish.wav", get_pos());
+ if (countMe) Sector::current()->get_level()->stats.badguys++;
+ return true;
+}
+
+void
+Mole::throw_rock()
+{
+ float px = get_bbox().get_middle().x;
+ float py = get_bbox().get_middle().y;
+
+ float angle = systemRandom.rand(90 - 15, 90 + 15) * (M_PI / 180);
+ float vx = cos(angle) * THROW_VELOCITY;
+ float vy = -sin(angle) * THROW_VELOCITY;
+
+ sound_manager->play("sounds/dartfire.wav", get_pos());
+ Sector::current()->add_object(new MoleRock(Vector(px, py), Vector(vx, vy), this));
+}
+
+void
+Mole::active_update(float elapsed_time)
+{
+ BadGuy::active_update(elapsed_time);
+
+ switch (state) {
+ case PRE_THROWING:
+ if (timer.check()) {
+ set_state(THROWING);
+ }
+ break;
+ case THROWING:
+ if (throw_timer.check()) {
+ throw_rock();
+ throw_timer.start(THROW_INTERVAL);
+ }
+ if (timer.check()) {
+ set_state(POST_THROWING);
+ }
+ break;
+ case POST_THROWING:
+ if (timer.check()) {
+ set_state(PEEKING);
+ }
+ break;
+ case PEEKING:
+ if (sprite->animation_done()) {
+ set_state(PRE_THROWING);
+ }
+ break;
+ case DEAD:
+ break;
+ }
+
+}
+
+void
+Mole::set_state(MoleState new_state)
+{
+ switch (new_state) {
+ case PRE_THROWING:
+ sprite->set_action("idle");
+ set_group(COLGROUP_DISABLED);
+ timer.start(IDLE_TIME);
+ break;
+ case THROWING:
+ sprite->set_action("idle");
+ set_group(COLGROUP_DISABLED);
+ timer.start(THROW_TIME);
+ throw_timer.start(THROW_INTERVAL);
+ break;
+ case POST_THROWING:
+ sprite->set_action("idle");
+ set_group(COLGROUP_DISABLED);
+ timer.start(IDLE_TIME);
+ break;
+ case PEEKING:
+ sprite->set_action("peeking", 1);
+ set_group(COLGROUP_STATIC);
+ break;
+ case DEAD:
+ sprite->set_action("idle");
+ set_group(COLGROUP_DISABLED);
+ break;
+ }
+
+ state = new_state;
+}
+
+IMPLEMENT_FACTORY(Mole, "mole")
--- /dev/null
+// $Id$
+//
+// SuperTux - Mole Badguy
+// Copyright (C) 2006 Christoph Sommer <christoph.sommer@2006.expires.deltadevelopment.de>
+//
+// 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 __MOLE_H__
+#define __MOLE_H__
+
+#include "badguy.hpp"
+
+class Mole : public BadGuy
+{
+public:
+ Mole(const lisp::Lisp& );
+ Mole(const Vector& pos);
+
+ void kill_fall();
+ HitResponse collision_badguy(BadGuy& , const CollisionHit& );
+ bool collision_squished(GameObject& object);
+
+ void activate();
+ void write(lisp::Writer& );
+ void active_update(float);
+
+ virtual Mole* clone() const { return new Mole(*this); }
+
+private:
+ enum MoleState {
+ PRE_THROWING,
+ THROWING,
+ POST_THROWING,
+ PEEKING,
+ DEAD
+ };
+
+ MoleState state;
+ Timer timer;
+ Timer throw_timer;
+
+ void set_state(MoleState new_state);
+ void throw_rock();
+
+};
+
+#endif
--- /dev/null
+// $Id$
+//
+// MoleRock - Rock thrown by "Mole" Badguy
+// Copyright (C) 2006 Christoph Sommer <christoph.sommer@2006.expires.deltadevelopment.de>
+//
+// 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 <config.h>
+
+#include "mole_rock.hpp"
+
+MoleRock::MoleRock(const lisp::Lisp& reader)
+ : BadGuy(reader, "images/creatures/mole/mole_rock.sprite", LAYER_TILES - 2), parent(0), initial_velocity(Vector(0, -400))
+{
+ physic.enable_gravity(true);
+ countMe = false;
+}
+
+MoleRock::MoleRock(const Vector& pos, const Vector& velocity, const BadGuy* parent = 0)
+ : BadGuy(pos, LEFT, "images/creatures/mole/mole_rock.sprite", LAYER_TILES - 2), parent(parent), initial_velocity(velocity)
+{
+ physic.enable_gravity(true);
+ countMe = false;
+}
+
+MoleRock::MoleRock(const MoleRock& other)
+ : BadGuy(other), parent(other.parent), initial_velocity(Vector(0, -400))
+{
+}
+
+MoleRock::~MoleRock()
+{
+}
+
+bool
+MoleRock::updatePointers(const GameObject* from_object, GameObject* to_object)
+{
+ if (from_object == parent) {
+ parent = dynamic_cast<MoleRock*>(to_object);
+ return true;
+ }
+ return false;
+}
+
+void
+MoleRock::write(lisp::Writer& writer)
+{
+ writer.start_list("mole_rock");
+ writer.write_float("x", start_position.x);
+ writer.write_float("y", start_position.y);
+ writer.end_list("mole_rock");
+}
+
+void
+MoleRock::activate()
+{
+ physic.set_velocity(initial_velocity);
+ sprite->set_action("default");
+}
+
+void
+MoleRock::deactivate()
+{
+ remove_me();
+}
+
+void
+MoleRock::active_update(float elapsed_time)
+{
+ BadGuy::active_update(elapsed_time);
+}
+
+void
+MoleRock::collision_solid(const CollisionHit& )
+{
+ sound_manager->play("sounds/darthit.wav", get_pos());
+ remove_me();
+}
+
+HitResponse
+MoleRock::collision_badguy(BadGuy& badguy, const CollisionHit& )
+{
+ // ignore collisions with parent
+ if (&badguy == parent) {
+ return FORCE_MOVE;
+ }
+ sound_manager->play("sounds/stomp.wav", get_pos());
+ remove_me();
+ badguy.kill_fall();
+ return ABORT_MOVE;
+}
+
+HitResponse
+MoleRock::collision_player(Player& player, const CollisionHit& hit)
+{
+ sound_manager->play("sounds/stomp.wav", get_pos());
+ remove_me();
+ return BadGuy::collision_player(player, hit);
+}
+
+IMPLEMENT_FACTORY(MoleRock, "mole_rock")
--- /dev/null
+// $Id$
+//
+// MoleRock - Rock thrown by "Mole" Badguy
+// Copyright (C) 2006 Christoph Sommer <christoph.sommer@2006.expires.deltadevelopment.de>
+//
+// 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 __MOLE_ROCK_H__
+#define __MOLE_ROCK_H__
+
+#include "badguy.hpp"
+
+/**
+ * Badguy "MoleRock" - Rock thrown by "Mole" Badguy
+ */
+class MoleRock : public BadGuy
+{
+public:
+ MoleRock(const lisp::Lisp& reader);
+ MoleRock(const Vector& pos, const Vector& velocity, const BadGuy* parent);
+ MoleRock(const MoleRock& mole_rock);
+ ~MoleRock();
+
+ void activate();
+ void deactivate();
+ void write(lisp::Writer& writer);
+
+ void active_update(float elapsed_time);
+
+ void collision_solid(const CollisionHit& hit);
+ HitResponse collision_badguy(BadGuy& badguy, const CollisionHit& hit);
+ HitResponse collision_player(Player& player, const CollisionHit& hit);
+
+ virtual MoleRock* clone() const { return new MoleRock(*this); }
+
+ virtual bool updatePointers(const GameObject* from_object, GameObject* to_object);
+
+protected:
+ const BadGuy* parent; /**< collisions with this BadGuy will be ignored */
+ const Vector initial_velocity; /**< velocity at time of creation */
+};
+
+#endif
--- /dev/null
+// $Id$
+//
+// SuperTux
+// Copyright (C) 2006 Matthias Braun <matze@braunis.de>
+//
+// 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 <config.h>
+
+#include "mrbomb.hpp"
+#include "bomb.hpp"
+#include "object/explosion.hpp"
+#include "sprite/sprite_manager.hpp"
+#include "audio/sound_manager.hpp"
+
+MrBomb::MrBomb(const lisp::Lisp& reader)
+ : WalkingBadguy(reader, "images/creatures/mr_bomb/mr_bomb.sprite", "left", "right")
+{
+ walk_speed = 80;
+ max_drop_height = 16;
+ grabbed = false;
+
+ //Prevent stutter when Tux jumps on Mr Bomb
+ sound_manager->preload("sounds/explosion.wav");
+
+ //Check if we need another sprite
+ if( !reader.get( "sprite", sprite_name ) ){
+ return;
+ }
+ if( sprite_name == "" ){
+ sprite_name = "images/creatures/mr_bomb/mr_bomb.sprite";
+ return;
+ }
+ //Replace sprite
+ sprite = sprite_manager->create( sprite_name );
+}
+
+/* MrBomb created by a despencer always gets default sprite atm.*/
+MrBomb::MrBomb(const Vector& pos, Direction d)
+ : WalkingBadguy(pos, d, "images/creatures/mr_bomb/mr_bomb.sprite", "left", "right")
+{
+ walk_speed = 80;
+ max_drop_height = 16;
+ grabbed = false;
+ sound_manager->preload("sounds/explosion.wav");
+}
+
+void
+MrBomb::write(lisp::Writer& writer)
+{
+ writer.start_list("mrbomb");
+ WalkingBadguy::write(writer);
+ writer.end_list("mrbomb");
+}
+
+HitResponse
+MrBomb::collision(GameObject& object, const CollisionHit& hit)
+{
+ if(grabbed)
+ return FORCE_MOVE;
+ return WalkingBadguy::collision(object, hit);
+}
+
+HitResponse
+MrBomb::collision_player(Player& player, const CollisionHit& hit)
+{
+ if(grabbed)
+ return FORCE_MOVE;
+ return WalkingBadguy::collision_player(player, hit);
+}
+
+bool
+MrBomb::collision_squished(GameObject& object)
+{
+ remove_me();
+ Sector::current()->add_object(new Bomb(get_pos(), dir, sprite_name ));
+ kill_squished(object);
+ return true;
+}
+
+void
+MrBomb::active_update(float elapsed_time)
+{
+ if(grabbed)
+ return;
+ WalkingBadguy::active_update(elapsed_time);
+}
+
+void
+MrBomb::kill_fall()
+{
+ remove_me();
+ Explosion* explosion = new Explosion(get_bbox().get_middle());
+ Sector::current()->add_object(explosion);
+
+ run_dead_script();
+}
+
+void
+MrBomb::grab(MovingObject&, const Vector& pos, Direction dir)
+{
+ assert(frozen);
+ movement = pos - get_pos();
+ this->dir = dir;
+ sprite->set_action(dir == LEFT ? "iced-left" : "iced-right");
+ set_group(COLGROUP_DISABLED);
+ grabbed = true;
+}
+
+void
+MrBomb::ungrab(MovingObject& , Direction dir)
+{
+ this->dir = dir;
+ set_group(COLGROUP_MOVING);
+ grabbed = false;
+}
+
+void
+MrBomb::freeze()
+{
+ WalkingBadguy::freeze();
+ sprite->set_action(dir == LEFT ? "iced-left" : "iced-right");
+}
+
+bool
+MrBomb::is_freezable() const
+{
+ return true;
+}
+
+bool
+MrBomb::is_portable() const
+{
+ return frozen;
+}
+
+IMPLEMENT_FACTORY(MrBomb, "mrbomb")
--- /dev/null
+// $Id$
+//
+// SuperTux
+// Copyright (C) 2006 Matthias Braun <matze@braunis.de>
+//
+// 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 __MRBOMB_H__
+#define __MRBOMB_H__
+
+#include "walking_badguy.hpp"
+#include "object/portable.hpp"
+
+class MrBomb : public WalkingBadguy, public Portable
+{
+public:
+ MrBomb(const lisp::Lisp& reader);
+ MrBomb(const Vector& pos, Direction d);
+
+ void write(lisp::Writer& writer);
+ void kill_fall();
+ HitResponse collision(GameObject& object, const CollisionHit& hit);
+ HitResponse collision_player(Player& player, const CollisionHit& hit);
+
+ void active_update(float elapsed_time);
+
+ void grab(MovingObject& object, const Vector& pos, Direction dir);
+ void ungrab(MovingObject& object, Direction dir);
+ bool is_portable() const;
+
+ void freeze();
+ bool is_freezable() const;
+
+ virtual MrBomb* clone() const { return new MrBomb(*this); }
+
+protected:
+ bool collision_squished(GameObject& object);
+
+private:
+ bool grabbed;
+};
+
+#endif
--- /dev/null
+// $Id$
+//
+// SuperTux
+// Copyright (C) 2006 Matthias Braun <matze@braunis.de>
+//
+// 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 <config.h>
+
+#include "mriceblock.hpp"
+#include "object/block.hpp"
+
+namespace {
+ const float KICKSPEED = 500;
+ const int MAXSQUISHES = 10;
+}
+
+MrIceBlock::MrIceBlock(const lisp::Lisp& reader)
+ : WalkingBadguy(reader, "images/creatures/mr_iceblock/mr_iceblock.sprite", "left", "right"), ice_state(ICESTATE_NORMAL), squishcount(0)
+{
+ walk_speed = 80;
+ max_drop_height = 600;
+ sound_manager->preload("sounds/iceblock_bump.wav");
+ sound_manager->preload("sounds/stomp.wav");
+ sound_manager->preload("sounds/kick.wav");
+}
+
+MrIceBlock::MrIceBlock(const Vector& pos, Direction d)
+ : WalkingBadguy(pos, d, "images/creatures/mr_iceblock/mr_iceblock.sprite", "left", "right"), ice_state(ICESTATE_NORMAL), squishcount(0)
+{
+ walk_speed = 80;
+ max_drop_height = 600;
+ sound_manager->preload("sounds/iceblock_bump.wav");
+ sound_manager->preload("sounds/stomp.wav");
+ sound_manager->preload("sounds/kick.wav");
+}
+
+void
+MrIceBlock::write(lisp::Writer& writer)
+{
+ writer.start_list("mriceblock");
+ WalkingBadguy::write(writer);
+ writer.end_list("mriceblock");
+}
+
+void
+MrIceBlock::activate()
+{
+ WalkingBadguy::activate();
+ set_state(ICESTATE_NORMAL);
+}
+
+void
+MrIceBlock::active_update(float elapsed_time)
+{
+ if(ice_state == ICESTATE_GRABBED)
+ return;
+
+ if(ice_state == ICESTATE_FLAT && flat_timer.check()) {
+ set_state(ICESTATE_NORMAL);
+ }
+
+ if (ice_state == ICESTATE_NORMAL)
+ {
+ WalkingBadguy::active_update(elapsed_time);
+ return;
+ }
+
+ BadGuy::active_update(elapsed_time);
+}
+
+bool
+MrIceBlock::can_break(){
+ return ice_state == ICESTATE_KICKED;
+}
+
+void
+MrIceBlock::collision_solid(const CollisionHit& hit)
+{
+ update_on_ground_flag(hit);
+
+ if(hit.top || hit.bottom) { // floor or roof
+ physic.set_velocity_y(0);
+ return;
+ }
+
+ // hit left or right
+ switch(ice_state) {
+ case ICESTATE_NORMAL:
+ WalkingBadguy::collision_solid(hit);
+ break;
+ case ICESTATE_KICKED: {
+ if(hit.right && dir == RIGHT) {
+ dir = LEFT;
+ sound_manager->play("sounds/iceblock_bump.wav", get_pos());
+ physic.set_velocity_x(-KICKSPEED);
+ } else if(hit.left && dir == LEFT) {
+ dir = RIGHT;
+ sound_manager->play("sounds/iceblock_bump.wav", get_pos());
+ physic.set_velocity_x(KICKSPEED);
+ }
+ sprite->set_action(dir == LEFT ? "flat-left" : "flat-right");
+ break;
+ }
+ case ICESTATE_FLAT:
+ physic.set_velocity_x(0);
+ break;
+ case ICESTATE_GRABBED:
+ break;
+ }
+}
+
+HitResponse
+MrIceBlock::collision(GameObject& object, const CollisionHit& hit)
+{
+ if(ice_state == ICESTATE_GRABBED)
+ return FORCE_MOVE;
+
+ return BadGuy::collision(object, hit);
+}
+
+HitResponse
+MrIceBlock::collision_player(Player& player, const CollisionHit& hit)
+{
+ if(ice_state == ICESTATE_GRABBED)
+ return FORCE_MOVE;
+
+ if(dir == UP) {
+ return FORCE_MOVE;
+ }
+
+ // handle kicks from left or right side
+ if(ice_state == ICESTATE_FLAT && get_state() == STATE_ACTIVE) {
+ if(hit.left) {
+ dir = RIGHT;
+ player.kick();
+ set_state(ICESTATE_KICKED);
+ return FORCE_MOVE;
+ } else if(hit.right) {
+ dir = LEFT;
+ player.kick();
+ set_state(ICESTATE_KICKED);
+ return FORCE_MOVE;
+ }
+ }
+
+ return BadGuy::collision_player(player, hit);
+}
+
+HitResponse
+MrIceBlock::collision_badguy(BadGuy& badguy, const CollisionHit& hit)
+{
+ switch(ice_state) {
+ case ICESTATE_NORMAL:
+ return WalkingBadguy::collision_badguy(badguy, hit);
+ case ICESTATE_FLAT:
+ return FORCE_MOVE;
+ case ICESTATE_KICKED:
+ badguy.kill_fall();
+ return FORCE_MOVE;
+ default:
+ assert(false);
+ }
+
+ return ABORT_MOVE;
+}
+
+bool
+MrIceBlock::collision_squished(GameObject& object)
+{
+ switch(ice_state) {
+ case ICESTATE_KICKED:
+ case ICESTATE_NORMAL:
+ squishcount++;
+ if(squishcount >= MAXSQUISHES) {
+ kill_fall();
+ return true;
+ }
+
+ set_state(ICESTATE_FLAT);
+ break;
+ case ICESTATE_FLAT:
+ {
+ MovingObject* movingobject = dynamic_cast<MovingObject*>(&object);
+ if (movingobject && (movingobject->get_pos().x < get_pos().x)) {
+ dir = RIGHT;
+ } else {
+ dir = LEFT;
+ }
+ }
+ set_state(ICESTATE_KICKED);
+ break;
+ case ICESTATE_GRABBED:
+ assert(false);
+ break;
+ }
+
+ Player* player = dynamic_cast<Player*>(&object);
+ if (player) player->bounce(*this);
+ return true;
+}
+
+void
+MrIceBlock::set_state(IceState state)
+{
+ if(ice_state == state)
+ return;
+
+ switch(state) {
+ case ICESTATE_NORMAL:
+ WalkingBadguy::activate();
+ break;
+ case ICESTATE_FLAT:
+ if(dir == UP) {
+ physic.set_velocity_y(-KICKSPEED);
+ bbox.set_size(34, 31.8f);
+ } else {
+ sound_manager->play("sounds/stomp.wav", get_pos());
+ physic.set_velocity_x(0);
+ physic.set_velocity_y(0);
+
+ sprite->set_action(dir == LEFT ? "flat-left" : "flat-right");
+ }
+ flat_timer.start(4);
+ break;
+ case ICESTATE_KICKED:
+ sound_manager->play("sounds/kick.wav", get_pos());
+
+ physic.set_velocity_x(dir == LEFT ? -KICKSPEED : KICKSPEED);
+ sprite->set_action(dir == LEFT ? "flat-left" : "flat-right");
+ // we should slide above 1 block holes now...
+ bbox.set_size(34, 31.8f);
+ break;
+ case ICESTATE_GRABBED:
+ flat_timer.stop();
+ break;
+ default:
+ assert(false);
+ }
+ ice_state = state;
+}
+
+void
+MrIceBlock::grab(MovingObject&, const Vector& pos, Direction dir)
+{
+ movement = pos - get_pos();
+ this->dir = dir;
+ sprite->set_action(dir == LEFT ? "flat-left" : "flat-right");
+ set_state(ICESTATE_GRABBED);
+ set_group(COLGROUP_DISABLED);
+}
+
+void
+MrIceBlock::ungrab(MovingObject& , Direction dir)
+{
+ this->dir = dir;
+ set_state(dir == UP ? ICESTATE_FLAT : ICESTATE_KICKED);
+ set_group(COLGROUP_MOVING);
+}
+
+bool
+MrIceBlock::is_portable() const
+{
+ return ice_state == ICESTATE_FLAT;
+}
+
+IMPLEMENT_FACTORY(MrIceBlock, "mriceblock")
--- /dev/null
+// $Id$
+//
+// SuperTux
+// Copyright (C) 2006 Matthias Braun <matze@braunis.de>
+//
+// 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 __MRICEBLOCK_H__
+#define __MRICEBLOCK_H__
+
+#include "walking_badguy.hpp"
+#include "object/portable.hpp"
+
+class MrIceBlock : public WalkingBadguy, public Portable
+{
+public:
+ MrIceBlock(const lisp::Lisp& reader);
+ MrIceBlock(const Vector& pos, Direction d);
+
+ void activate();
+ void write(lisp::Writer& writer);
+ HitResponse collision(GameObject& object, const CollisionHit& hit);
+ void collision_solid(const CollisionHit& hit);
+ HitResponse collision_badguy(BadGuy& badguy, const CollisionHit& hit);
+ HitResponse collision_player(Player& player, const CollisionHit& hit);
+
+ void active_update(float elapsed_time);
+
+ void grab(MovingObject& object, const Vector& pos, Direction dir);
+ void ungrab(MovingObject& object, Direction dir);
+ bool is_portable() const;
+
+ bool can_break();
+
+ virtual MrIceBlock* clone() const { return new MrIceBlock(*this); }
+
+protected:
+ bool collision_squished(GameObject& object);
+
+private:
+ enum IceState {
+ ICESTATE_NORMAL,
+ ICESTATE_FLAT,
+ ICESTATE_GRABBED,
+ ICESTATE_KICKED
+ };
+
+ void set_state(IceState state);
+
+ IceState ice_state;
+ Timer flat_timer;
+ int squishcount;
+};
+
+#endif
--- /dev/null
+// $Id$
+//
+// SuperTux
+// Copyright (C) 2006 Matthias Braun <matze@braunis.de>
+//
+// 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 <config.h>
+
+#include "mrrocket.hpp"
+#include "object/explosion.hpp"
+
+static const float SPEED = 200;
+
+MrRocket::MrRocket(const lisp::Lisp& reader)
+ : BadGuy(reader, "images/creatures/mr_rocket/mr_rocket.sprite")
+{
+}
+
+MrRocket::MrRocket(const Vector& pos, Direction d)
+ : BadGuy(pos, d, "images/creatures/mr_rocket/mr_rocket.sprite")
+{
+}
+
+void
+MrRocket::write(lisp::Writer& writer)
+{
+ writer.start_list("mrrocket");
+
+ writer.write_float("x", start_position.x);
+ writer.write_float("y", start_position.y);
+
+ writer.end_list("mrrocket");
+}
+
+void
+MrRocket::activate()
+{
+ physic.set_velocity_x(dir == LEFT ? -SPEED : SPEED);
+ physic.enable_gravity(false);
+ sprite->set_action(dir == LEFT ? "left" : "right");
+}
+
+void
+MrRocket::active_update(float elapsed_time)
+{
+ if (collision_timer.check()) {
+ Sector::current()->add_object(new Explosion(get_bbox().get_middle()));
+ remove_me();
+ }
+ else if (!collision_timer.started()) {
+ movement=physic.get_movement(elapsed_time);
+ sprite->set_action(dir == LEFT ? "left" : "right");
+ }
+}
+
+bool
+MrRocket::collision_squished(GameObject& object)
+{
+ sprite->set_action(dir == LEFT ? "squished-left" : "squished-right");
+ kill_squished(object);
+ kill_fall();
+ return true;
+}
+
+void
+MrRocket::collision_solid(const CollisionHit& hit)
+{
+ if(hit.top || hit.bottom) {
+ physic.set_velocity_y(0);
+ } else if(hit.left || hit.right) {
+ sprite->set_action(dir == LEFT ? "collision-left" : "collision-right");
+ physic.set_velocity_x(0);
+ collision_timer.start(0.2f, true);
+ }
+}
+
+IMPLEMENT_FACTORY(MrRocket, "mrrocket")
--- /dev/null
+// $Id$
+//
+// SuperTux
+// Copyright (C) 2006 Matthias Braun <matze@braunis.de>
+//
+// 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 __MRROCKET_H__
+#define __MRROCKET_H__
+
+#include "badguy.hpp"
+#include "timer.hpp"
+
+class MrRocket : public BadGuy
+{
+public:
+ MrRocket(const lisp::Lisp& reader);
+ MrRocket(const Vector& pos, Direction d);
+
+ void activate();
+ void active_update(float elapsed_time);
+ void write(lisp::Writer& writer);
+ void collision_solid(const CollisionHit& hit);
+
+ virtual MrRocket* clone() const { return new MrRocket(*this); }
+
+protected:
+ bool collision_squished(GameObject& object);
+ Timer collision_timer;
+};
+
+#endif
--- /dev/null
+// $Id$
+//
+// SuperTux
+// Copyright (C) 2006 Matthias Braun <matze@braunis.de>
+//
+// 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 <config.h>
+
+#include "mrtree.hpp"
+#include "stumpy.hpp"
+#include "poisonivy.hpp"
+#include "random_generator.hpp"
+#include "object/sprite_particle.hpp"
+#include "sector.hpp"
+
+static const float WALKSPEED = 100;
+
+static const float POISONIVY_WIDTH = 32;
+static const float POISONIVY_HEIGHT = 32;
+static const float POISONIVY_Y_OFFSET = 24;
+
+
+MrTree::MrTree(const lisp::Lisp& reader)
+ : WalkingBadguy(reader, "images/creatures/mr_tree/mr_tree.sprite","left","right")
+{
+ walk_speed = WALKSPEED;
+ max_drop_height = 16;
+ sound_manager->preload("sounds/mr_tree.ogg");
+}
+
+void
+MrTree::write(lisp::Writer& writer)
+{
+ writer.start_list("mrtree");
+ WalkingBadguy::write(writer);
+ writer.end_list("mrtree");
+}
+
+bool
+MrTree::collision_squished(GameObject& object)
+{
+ // replace with Stumpy
+ Vector stumpy_pos = get_pos();
+ stumpy_pos.x += 20;
+ stumpy_pos.y += 25;
+ Stumpy* stumpy = new Stumpy(stumpy_pos, dir);
+ remove_me();
+ Sector::current()->add_object(stumpy);
+
+ // give Feedback
+ sound_manager->play("sounds/mr_tree.ogg", get_pos());
+ Player* player = dynamic_cast<Player*>(&object);
+ if (player) player->bounce(*this);
+
+ // spawn some particles
+ // TODO: provide convenience function in MovingSprite or MovingObject?
+ for (int px = (int)stumpy->get_bbox().p1.x; px < (int)stumpy->get_bbox().p2.x; px+=10) {
+ Vector ppos = Vector(px, stumpy->get_bbox().p1.y-5);
+ float angle = systemRandom.randf(-M_PI_2, M_PI_2);
+ float velocity = systemRandom.randf(45, 90);
+ float vx = sin(angle)*velocity;
+ float vy = -cos(angle)*velocity;
+ Vector pspeed = Vector(vx, vy);
+ Vector paccel = Vector(0, 100);
+ Sector::current()->add_object(new SpriteParticle("images/objects/particles/leaf.sprite", "default", ppos, ANCHOR_MIDDLE, pspeed, paccel, LAYER_OBJECTS-1));
+ }
+
+ // spawn PoisonIvy
+ Vector leaf1_pos = Vector(stumpy_pos.x - POISONIVY_WIDTH - 1, stumpy_pos.y - POISONIVY_Y_OFFSET);
+ Rect leaf1_bbox = Rect(leaf1_pos.x, leaf1_pos.y, leaf1_pos.x + POISONIVY_WIDTH, leaf1_pos.y + POISONIVY_HEIGHT);
+ if (Sector::current()->is_free_of_movingstatics(leaf1_bbox, this)) {
+ PoisonIvy* leaf1 = new PoisonIvy(leaf1_bbox.p1, LEFT);
+ leaf1 = leaf1;
+ Sector::current()->add_object(leaf1);
+ }
+
+ // spawn PoisonIvy
+ Vector leaf2_pos = Vector(stumpy_pos.x + sprite->get_current_hitbox_width() + 1, stumpy_pos.y - POISONIVY_Y_OFFSET);
+ Rect leaf2_bbox = Rect(leaf2_pos.x, leaf2_pos.y, leaf2_pos.x + POISONIVY_WIDTH, leaf2_pos.y + POISONIVY_HEIGHT);
+ if (Sector::current()->is_free_of_movingstatics(leaf2_bbox, this)) {
+ PoisonIvy* leaf2 = new PoisonIvy(leaf2_bbox.p1, RIGHT);
+ leaf2 = leaf2;
+ Sector::current()->add_object(leaf2);
+ }
+
+ return true;
+}
+
+IMPLEMENT_FACTORY(MrTree, "mrtree")
--- /dev/null
+// $Id$
+//
+// SuperTux
+// Copyright (C) 2006 Matthias Braun <matze@braunis.de>
+//
+// 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 __MRTREE_H__
+#define __MRTREE_H__
+
+#include "walking_badguy.hpp"
+
+class MrTree : public WalkingBadguy
+{
+public:
+ MrTree(const lisp::Lisp& reader);
+ void write(lisp::Writer& writer);
+ virtual MrTree* clone() const { return new MrTree(*this); }
+
+protected:
+ bool collision_squished(GameObject& object);
+
+};
+
+#endif
--- /dev/null
+// $Id$
+//
+// SuperTux
+// Copyright (C) 2006 Matthias Braun <matze@braunis.de>
+//
+// 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 <config.h>
+
+#include "plant.hpp"
+
+static const float WALKSPEED = 80;
+static const float WAKE_TIME = .5;
+
+Plant::Plant(const lisp::Lisp& reader)
+ : BadGuy(reader, "images/creatures/plant/plant.sprite")
+{
+ state = PLANT_SLEEPING;
+}
+
+void
+Plant::write(lisp::Writer& writer)
+{
+ writer.start_list("plant");
+
+ writer.write_float("x", start_position.x);
+ writer.write_float("y", start_position.y);
+
+ writer.end_list("plant");
+}
+
+void
+Plant::activate()
+{
+ //FIXME: turns sspiky around for debugging
+ dir = dir == LEFT ? RIGHT : LEFT;
+
+ state = PLANT_SLEEPING;
+ physic.set_velocity_x(0);
+ sprite->set_action(dir == LEFT ? "sleeping-left" : "sleeping-right");
+}
+
+void
+Plant::collision_solid(const CollisionHit& hit)
+{
+ if(hit.top || hit.bottom) {
+ physic.set_velocity_y(0);
+ } else if(hit.left || hit.right) {
+ dir = dir == LEFT ? RIGHT : LEFT;
+ sprite->set_action(dir == LEFT ? "left" : "right");
+ physic.set_velocity_x(-physic.get_velocity_x());
+ }
+}
+
+HitResponse
+Plant::collision_badguy(BadGuy& , const CollisionHit& hit)
+{
+ if(state != PLANT_WALKING) return CONTINUE;
+
+ if(hit.left || hit.right) {
+ dir = dir == LEFT ? RIGHT : LEFT;
+ sprite->set_action(dir == LEFT ? "left" : "right");
+ physic.set_velocity_x(-physic.get_velocity_x());
+ }
+
+ return CONTINUE;
+}
+
+void
+Plant::active_update(float elapsed_time) {
+ BadGuy::active_update(elapsed_time);
+
+ if(state == PLANT_SLEEPING) {
+
+ Player* player = this->get_nearest_player();
+ if (player) {
+ Rect mb = this->get_bbox();
+ Rect pb = player->get_bbox();
+
+ bool inReach_left = (pb.p2.x >= mb.p2.x-((dir == LEFT) ? 256 : 0));
+ bool inReach_right = (pb.p1.x <= mb.p1.x+((dir == RIGHT) ? 256 : 0));
+ bool inReach_top = (pb.p2.y >= mb.p2.y);
+ bool inReach_bottom = (pb.p1.y <= mb.p1.y);
+
+ if (inReach_left && inReach_right && inReach_top && inReach_bottom) {
+ // wake up
+ sprite->set_action(dir == LEFT ? "waking-left" : "waking-right");
+ if(!timer.started()) timer.start(WAKE_TIME);
+ state = PLANT_WAKING;
+ }
+ }
+ }
+
+ if(state == PLANT_WAKING) {
+ if(timer.check()) {
+ // start walking
+ sprite->set_action(dir == LEFT ? "left" : "right");
+ physic.set_velocity_x(dir == LEFT ? -WALKSPEED : WALKSPEED);
+ state = PLANT_WALKING;
+ }
+ }
+
+}
+
+IMPLEMENT_FACTORY(Plant, "plant")
--- /dev/null
+// $Id$
+//
+// SuperTux
+// Copyright (C) 2006 Matthias Braun <matze@braunis.de>
+//
+// 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 __PLANT_H__
+#define __PLANT_H__
+
+#include "badguy.hpp"
+
+class Plant : public BadGuy
+{
+public:
+ Plant(const lisp::Lisp& reader);
+
+ void activate();
+ void write(lisp::Writer& writer);
+ void collision_solid(const CollisionHit& hit);
+ HitResponse collision_badguy(BadGuy& badguy, const CollisionHit& hit);
+ void active_update(float elapsed_time);
+
+ virtual Plant* clone() const { return new Plant(*this); }
+
+protected:
+ Timer timer;
+
+ enum PlantState {
+ PLANT_SLEEPING,
+ PLANT_WAKING,
+ PLANT_WALKING
+ };
+ PlantState state;
+
+};
+
+#endif
--- /dev/null
+// $Id$
+//
+// SuperTux
+// Copyright (C) 2006 Matthias Braun <matze@braunis.de>
+//
+// 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 <config.h>
+
+#include "poisonivy.hpp"
+#include "random_generator.hpp"
+#include "object/sprite_particle.hpp"
+
+PoisonIvy::PoisonIvy(const lisp::Lisp& reader)
+ : WalkingBadguy(reader, "images/creatures/poison_ivy/poison_ivy.sprite", "left", "right")
+{
+ walk_speed = 80;
+}
+
+PoisonIvy::PoisonIvy(const Vector& pos, Direction d)
+ : WalkingBadguy(pos, d, "images/creatures/poison_ivy/poison_ivy.sprite", "left", "right")
+{
+ walk_speed = 80;
+}
+
+void
+PoisonIvy::write(lisp::Writer& writer)
+{
+ writer.start_list("poisonivy");
+ WalkingBadguy::write(writer);
+ writer.end_list("poisonivy");
+}
+
+bool
+PoisonIvy::collision_squished(GameObject& object)
+{
+ sprite->set_action(dir == LEFT ? "squished-left" : "squished-right");
+ // spawn some particles
+ // TODO: provide convenience function in MovingSprite or MovingObject?
+ for (int i = 0; i < 3; i++) {
+ Vector ppos = bbox.get_middle();
+ float angle = systemRandom.randf(-M_PI_2, M_PI_2);
+ float velocity = systemRandom.randf(350, 400);
+ float vx = sin(angle)*velocity;
+ float vy = -cos(angle)*velocity;
+ Vector pspeed = Vector(vx, vy);
+ Vector paccel = Vector(0, 100);
+ Sector::current()->add_object(new SpriteParticle("images/objects/particles/poisonivy.sprite", "default", ppos, ANCHOR_MIDDLE, pspeed, paccel, LAYER_OBJECTS-1));
+ }
+ kill_squished(object);
+ return true;
+}
+
+IMPLEMENT_FACTORY(PoisonIvy, "poisonivy")
--- /dev/null
+// $Id$
+//
+// SuperTux
+// Copyright (C) 2006 Matthias Braun <matze@braunis.de>
+//
+// 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 __POISONIVY_H__
+#define __POISONIVY_H__
+
+#include "walking_badguy.hpp"
+
+class PoisonIvy : public WalkingBadguy
+{
+public:
+ PoisonIvy(const lisp::Lisp& reader);
+ PoisonIvy(const Vector& pos, Direction d);
+
+ void write(lisp::Writer& writer);
+ virtual PoisonIvy* clone() const { return new PoisonIvy(*this); }
+
+protected:
+ bool collision_squished(GameObject& object);
+
+};
+
+#endif
--- /dev/null
+// $Id$
+//
+// SuperTux - "Will-O-Wisp" Badguy
+// Copyright (C) 2006 Christoph Sommer <christoph.sommer@2006.expires.deltadevelopment.de>
+//
+// 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 <config.h>
+
+#include "root.hpp"
+#include "sprite/sprite_manager.hpp"
+#include "timer.hpp"
+
+static const float SPEED_GROW = 256;
+static const float SPEED_SHRINK = 128;
+static const float HATCH_TIME = 0.75;
+
+Root::Root(const Vector& pos)
+ : BadGuy(pos, "images/creatures/ghosttree/root.sprite", LAYER_TILES-1),
+ mystate(STATE_APPEARING), offset_y(0)
+{
+ base_sprite.reset(sprite_manager->create("images/creatures/ghosttree/root-base.sprite"));
+ base_sprite->set_action("appearing", 1);
+ base_sprite->set_animation_loops(1); // TODO: necessary because set_action ignores loops for default action
+ physic.enable_gravity(false);
+}
+
+Root::~Root()
+{
+}
+
+void
+Root::activate()
+{
+ set_group(COLGROUP_TOUCHABLE);
+}
+
+void
+Root::deactivate()
+{
+ remove_me();
+}
+
+void
+Root::active_update(float elapsed_time)
+{
+ if (mystate == STATE_APPEARING) {
+ if (base_sprite->animation_done()) {
+ hatch_timer.start(HATCH_TIME);
+ mystate = STATE_HATCHING;
+ }
+ }
+ if (mystate == STATE_HATCHING) {
+ if (!hatch_timer.started()) mystate = STATE_GROWING;
+ }
+ else if (mystate == STATE_GROWING) {
+ offset_y -= elapsed_time * SPEED_GROW;
+ if (offset_y < -sprite->get_height()) {
+ offset_y = -sprite->get_height();
+ mystate = STATE_SHRINKING;
+ }
+ set_pos(start_position + Vector(0, offset_y));
+ }
+ else if (mystate == STATE_SHRINKING) {
+ offset_y += elapsed_time * SPEED_SHRINK;
+ if (offset_y > 0) {
+ offset_y = 0;
+ mystate = STATE_VANISHING;
+ base_sprite->set_action("vanishing", 2);
+ base_sprite->set_animation_loops(2); // TODO: doesn't seem to work for loops=1
+ }
+ set_pos(start_position + Vector(0, offset_y));
+ }
+ else if (mystate == STATE_VANISHING) {
+ if (base_sprite->animation_done()) remove_me();
+ }
+ BadGuy::active_update(elapsed_time);
+}
+
+void
+Root::draw(DrawingContext& context)
+{
+ base_sprite->draw(context, start_position, LAYER_TILES+1);
+ if ((mystate != STATE_APPEARING) && (mystate != STATE_VANISHING)) BadGuy::draw(context);
+}
+
--- /dev/null
+// $Id$
+//
+// SuperTux - Boss "GhostTree"
+// Copyright (C) 2007 Matthias Braun <matze@braunis.de>
+//
+// 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 __ROOT_H__
+#define __ROOT_H__
+
+#include <memory>
+#include "badguy.hpp"
+
+class Timer;
+
+class Root : public BadGuy
+{
+public:
+ Root(const Vector& pos);
+ ~Root();
+
+ void activate();
+ void deactivate();
+ void active_update(float elapsed_time);
+ virtual void draw(DrawingContext& context);
+ virtual bool is_flammable() const { return false; }
+ virtual bool is_freezable() const { return false; }
+ virtual void kill_fall() { }
+
+protected:
+ enum MyState {
+ STATE_APPEARING, STATE_HATCHING, STATE_GROWING, STATE_SHRINKING, STATE_VANISHING
+ };
+ MyState mystate;
+ std::auto_ptr<Sprite> base_sprite;
+ float offset_y;
+ Timer hatch_timer;
+};
+
+#endif
--- /dev/null
+// $Id$
+//
+// SkullyHop - A Hopping Skull
+// Copyright (C) 2006 Christoph Sommer <supertux@2006.expires.deltadevelopment.de>
+//
+// 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 <config.h>
+
+#include "skullyhop.hpp"
+#include "random_generator.hpp"
+
+namespace {
+ const float VERTICAL_SPEED = -450; /**< y-speed when jumping */
+ const float HORIZONTAL_SPEED = 220; /**< x-speed when jumping */
+ const float MIN_RECOVER_TIME = 0.1f; /**< minimum time to stand still before starting a (new) jump */
+ const float MAX_RECOVER_TIME = 1.0f; /**< maximum time to stand still before starting a (new) jump */
+ static const std::string HOP_SOUND = "sounds/hop.ogg";
+}
+
+SkullyHop::SkullyHop(const lisp::Lisp& reader)
+ : BadGuy(reader, "images/creatures/skullyhop/skullyhop.sprite")
+{
+ sound_manager->preload( HOP_SOUND );
+}
+
+SkullyHop::SkullyHop(const Vector& pos, Direction d)
+ : BadGuy(pos, d, "images/creatures/skullyhop/skullyhop.sprite")
+{
+ sound_manager->preload( HOP_SOUND );
+}
+
+void
+SkullyHop::write(lisp::Writer& writer)
+{
+ writer.start_list("skullyhop");
+ writer.write_float("x", start_position.x);
+ writer.write_float("y", start_position.y);
+ writer.end_list("skullyhop");
+}
+
+void
+SkullyHop::activate()
+{
+ // initial state is JUMPING, because we might start airborne
+ state = JUMPING;
+ sprite->set_action(dir == LEFT ? "jumping-left" : "jumping-right");
+}
+
+void
+SkullyHop::set_state(SkullyHopState newState)
+{
+ if (newState == STANDING) {
+ physic.set_velocity_x(0);
+ physic.set_velocity_y(0);
+ sprite->set_action(dir == LEFT ? "standing-left" : "standing-right");
+
+ float recover_time = systemRandom.randf(MIN_RECOVER_TIME,MAX_RECOVER_TIME);
+ recover_timer.start(recover_time);
+ } else
+ if (newState == CHARGING) {
+ sprite->set_action(dir == LEFT ? "charging-left" : "charging-right", 1);
+ } else
+ if (newState == JUMPING) {
+ sprite->set_action(dir == LEFT ? "jumping-left" : "jumping-right");
+ physic.set_velocity_x(dir == LEFT ? -HORIZONTAL_SPEED : HORIZONTAL_SPEED);
+ physic.set_velocity_y(VERTICAL_SPEED);
+ sound_manager->play( HOP_SOUND, get_pos());
+ }
+
+ state = newState;
+}
+
+bool
+SkullyHop::collision_squished(GameObject& object)
+{
+ sprite->set_action(dir == LEFT ? "squished-left" : "squished-right");
+ kill_squished(object);
+ return true;
+}
+
+void
+SkullyHop::collision_solid(const CollisionHit& hit)
+{
+ // just default behaviour (i.e. stop at floor/walls) when squished
+ if (BadGuy::get_state() == STATE_SQUISHED) {
+ BadGuy::collision_solid(hit);
+ }
+
+ // ignore collisions while standing still
+ if(state != JUMPING)
+ return;
+
+ // check if we hit the floor while falling
+ if(hit.bottom && physic.get_velocity_y() > 0 ) {
+ set_state(STANDING);
+ }
+ // check if we hit the roof while climbing
+ if(hit.top) {
+ physic.set_velocity_y(0);
+ }
+
+ // check if we hit left or right while moving in either direction
+ if(hit.left || hit.right) {
+ dir = dir == LEFT ? RIGHT : LEFT;
+ sprite->set_action(dir == LEFT ? "jumping-left" : "jumping-right");
+ physic.set_velocity_x(-0.25*physic.get_velocity_x());
+ }
+}
+
+HitResponse
+SkullyHop::collision_badguy(BadGuy& , const CollisionHit& hit)
+{
+ // behaviour for badguy collisions is the same as for collisions with solids
+ collision_solid(hit);
+
+ return CONTINUE;
+}
+
+void
+SkullyHop::active_update(float elapsed_time)
+{
+ BadGuy::active_update(elapsed_time);
+
+ // charge when fully recovered
+ if ((state == STANDING) && (recover_timer.check())) {
+ set_state(CHARGING);
+ return;
+ }
+
+ // jump as soon as charging animation completed
+ if ((state == CHARGING) && (sprite->animation_done())) {
+ set_state(JUMPING);
+ return;
+ }
+}
+
+IMPLEMENT_FACTORY(SkullyHop, "skullyhop")
--- /dev/null
+// $Id$
+//
+// SkullyHop - A Hopping Skull
+// Copyright (C) 2006 Christoph Sommer <supertux@2006.expires.deltadevelopment.de>
+//
+// 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 __SKULLYHOP_H__
+#define __SKULLYHOP_H__
+
+#include "badguy.hpp"
+
+/**
+ * Badguy "SkullyHop" - A Hopping Skull
+ */
+class SkullyHop : public BadGuy
+{
+public:
+ SkullyHop(const lisp::Lisp& reader);
+ SkullyHop(const Vector& pos, Direction d);
+
+ void activate();
+ void write(lisp::Writer& writer);
+ void collision_solid(const CollisionHit& hit);
+ HitResponse collision_badguy(BadGuy& badguy, const CollisionHit& hit);
+ bool collision_squished(GameObject& object);
+ void active_update(float elapsed_time);
+
+ virtual SkullyHop* clone() const { return new SkullyHop(*this); }
+
+protected:
+ enum SkullyHopState {
+ STANDING,
+ CHARGING,
+ JUMPING
+ };
+
+ Timer recover_timer;
+ SkullyHopState state;
+
+ void set_state(SkullyHopState newState);
+};
+
+#endif
--- /dev/null
+// $Id$
+//
+// SuperTux - Badguy "Snail"
+// Copyright (C) 2006 Christoph Sommer <christoph.sommer@2006.expires.deltadevelopment.de>
+//
+// 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 <config.h>
+
+#include "snail.hpp"
+#include "object/block.hpp"
+
+namespace {
+ const float KICKSPEED = 500;
+ const int MAXSQUISHES = 10;
+ const float KICKSPEED_Y = -500; /**< y-velocity gained when kicked */
+}
+
+Snail::Snail(const lisp::Lisp& reader)
+ : WalkingBadguy(reader, "images/creatures/snail/snail.sprite", "left", "right"), state(STATE_NORMAL), squishcount(0)
+{
+ walk_speed = 80;
+ max_drop_height = 600;
+ sound_manager->preload("sounds/iceblock_bump.wav");
+ sound_manager->preload("sounds/stomp.wav");
+ sound_manager->preload("sounds/kick.wav");
+}
+
+Snail::Snail(const Vector& pos, Direction d)
+ : WalkingBadguy(pos, d, "images/creatures/snail/snail.sprite", "left", "right"), state(STATE_NORMAL), squishcount(0)
+{
+ walk_speed = 80;
+ max_drop_height = 600;
+ sound_manager->preload("sounds/iceblock_bump.wav");
+ sound_manager->preload("sounds/stomp.wav");
+ sound_manager->preload("sounds/kick.wav");
+}
+
+void
+Snail::write(lisp::Writer& writer)
+{
+ writer.start_list("snail");
+ WalkingBadguy::write(writer);
+ writer.end_list("snail");
+}
+
+void
+Snail::activate()
+{
+ WalkingBadguy::activate();
+ be_normal();
+}
+
+void
+Snail::be_normal()
+{
+ if (state == STATE_NORMAL) return;
+
+ state = STATE_NORMAL;
+ WalkingBadguy::activate();
+}
+
+void
+Snail::be_flat()
+{
+ state = STATE_FLAT;
+ sprite->set_action(dir == LEFT ? "flat-left" : "flat-right");
+ sprite->set_fps(64);
+
+ physic.set_velocity_x(0);
+ physic.set_velocity_y(0);
+
+ flat_timer.start(4);
+}
+
+void
+Snail::be_kicked()
+{
+ state = STATE_KICKED_DELAY;
+ sprite->set_action(dir == LEFT ? "flat-left" : "flat-right");
+ sprite->set_fps(64);
+
+ physic.set_velocity_x(0);
+ physic.set_velocity_y(0);
+
+ // start a timer to delay addition of upward movement until we are (hopefully) out from under the player
+ kicked_delay_timer.start(0.05f);
+}
+
+bool
+Snail::can_break(){
+ return state == STATE_KICKED;
+}
+
+void
+Snail::active_update(float elapsed_time)
+{
+ switch (state) {
+
+ case STATE_NORMAL:
+ WalkingBadguy::active_update(elapsed_time);
+ break;
+
+ case STATE_FLAT:
+ if (flat_timer.started()) {
+ sprite->set_fps(64 - 15 * flat_timer.get_timegone());
+ }
+ if (flat_timer.check()) {
+ be_normal();
+ }
+ BadGuy::active_update(elapsed_time);
+ break;
+
+ case STATE_KICKED_DELAY:
+ if (kicked_delay_timer.check()) {
+ physic.set_velocity_x(dir == LEFT ? -KICKSPEED : KICKSPEED);
+ physic.set_velocity_y(KICKSPEED_Y);
+ state = STATE_KICKED;
+ }
+ BadGuy::active_update(elapsed_time);
+ break;
+
+ case STATE_KICKED:
+ physic.set_velocity_x(physic.get_velocity_x() * pow(0.99, elapsed_time/0.02));
+ if (fabsf(physic.get_velocity_x()) < walk_speed) be_normal();
+ BadGuy::active_update(elapsed_time);
+ break;
+
+ }
+}
+
+void
+Snail::collision_solid(const CollisionHit& hit)
+{
+ update_on_ground_flag(hit);
+
+ switch (state) {
+ case STATE_NORMAL:
+ WalkingBadguy::collision_solid(hit);
+ break;
+ case STATE_FLAT:
+ if(hit.top || hit.bottom) {
+ physic.set_velocity_y(0);
+ }
+ if(hit.left || hit.right) {
+ }
+ break;
+ case STATE_KICKED_DELAY:
+ if(hit.top || hit.bottom) {
+ physic.set_velocity_y(0);
+ }
+ if(hit.left || hit.right) {
+ physic.set_velocity_x(0);
+ }
+ break;
+ case STATE_KICKED:
+ if(hit.top || hit.bottom) {
+ physic.set_velocity_y(0);
+ }
+ if(hit.left || hit.right) {
+ sound_manager->play("sounds/iceblock_bump.wav", get_pos());
+
+ if( ( dir == LEFT && hit.left ) || ( dir == RIGHT && hit.right) ){
+ dir = (dir == LEFT) ? RIGHT : LEFT;
+ sprite->set_action(dir == LEFT ? "flat-left" : "flat-right");
+
+ physic.set_velocity_x(-physic.get_velocity_x()*0.75);
+ if (fabsf(physic.get_velocity_x()) < walk_speed) be_normal();
+ }
+
+ }
+ break;
+ }
+
+}
+
+HitResponse
+Snail::collision_badguy(BadGuy& badguy, const CollisionHit& hit)
+{
+ switch(state) {
+ case STATE_NORMAL:
+ return WalkingBadguy::collision_badguy(badguy, hit);
+ case STATE_FLAT:
+ case STATE_KICKED_DELAY:
+ return FORCE_MOVE;
+ case STATE_KICKED:
+ badguy.kill_fall();
+ return FORCE_MOVE;
+ default:
+ assert(false);
+ }
+
+ return ABORT_MOVE;
+}
+
+bool
+Snail::collision_squished(GameObject& object)
+{
+ switch(state) {
+
+ case STATE_KICKED:
+ case STATE_NORMAL:
+ squishcount++;
+ if(squishcount >= MAXSQUISHES) {
+ kill_fall();
+ return true;
+ }
+
+ sound_manager->play("sounds/stomp.wav", get_pos());
+ be_flat();
+ break;
+
+ case STATE_FLAT:
+ sound_manager->play("sounds/kick.wav", get_pos());
+ {
+ MovingObject* movingobject = dynamic_cast<MovingObject*>(&object);
+ if (movingobject && (movingobject->get_pos().x < get_pos().x)) {
+ dir = RIGHT;
+ } else {
+ dir = LEFT;
+ }
+ }
+ be_kicked();
+ break;
+
+ case STATE_KICKED_DELAY:
+ break;
+
+ }
+
+ Player* player = dynamic_cast<Player*>(&object);
+ if (player) player->bounce(*this);
+ return true;
+}
+
+IMPLEMENT_FACTORY(Snail, "snail")
--- /dev/null
+// $Id$
+//
+// SuperTux - Badguy "Snail"
+// Copyright (C) 2006 Christoph Sommer <christoph.sommer@2006.expires.deltadevelopment.de>
+//
+// 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 __SNAIL_H__
+#define __SNAIL_H__
+
+#include "walking_badguy.hpp"
+
+/**
+ * Badguy "Snail" - a snail-like creature that can be flipped and tossed around at an angle
+ */
+class Snail : public WalkingBadguy
+{
+public:
+ Snail(const lisp::Lisp& reader);
+ Snail(const Vector& pos, Direction d);
+
+ void activate();
+ void write(lisp::Writer& writer);
+ void collision_solid(const CollisionHit& hit);
+ HitResponse collision_badguy(BadGuy& badguy, const CollisionHit& hit);
+ bool can_break();
+
+ void active_update(float elapsed_time);
+
+ virtual Snail* clone() const { return new Snail(*this); }
+
+protected:
+ bool collision_squished(GameObject& object);
+ void be_normal(); /**< switch to state STATE_NORMAL */
+ void be_flat(); /**< switch to state STATE_FLAT */
+ void be_kicked(); /**< switch to state STATE_KICKED_DELAY */
+
+private:
+ enum State {
+ STATE_NORMAL, /**< walking around */
+ STATE_FLAT, /**< flipped upside-down */
+ STATE_KICKED_DELAY, /**< short delay before being launched */
+ STATE_KICKED /**< launched */
+ };
+ State state;
+ Timer flat_timer; /**< wait time until flipping right-side-up again */
+ Timer kicked_delay_timer; /**< wait time until switching from STATE_KICKED_DELAY to STATE_KICKED */
+ int squishcount;
+};
+
+#endif
--- /dev/null
+// $Id$
+//
+// SuperTux
+// Copyright (C) 2006 Matthias Braun <matze@braunis.de>
+//
+// 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 <config.h>
+
+#include "snowball.hpp"
+
+SnowBall::SnowBall(const lisp::Lisp& reader)
+ : WalkingBadguy(reader, "images/creatures/snowball/snowball.sprite", "left", "right")
+{
+ walk_speed = 80;
+}
+
+SnowBall::SnowBall(const Vector& pos, Direction d)
+ : WalkingBadguy(pos, d, "images/creatures/snowball/snowball.sprite", "left", "right")
+{
+ walk_speed = 80;
+}
+
+void
+SnowBall::write(lisp::Writer& writer)
+{
+ writer.start_list("snowball");
+ WalkingBadguy::write(writer);
+ writer.end_list("snowball");
+}
+
+bool
+SnowBall::collision_squished(GameObject& object)
+{
+ sprite->set_action(dir == LEFT ? "squished-left" : "squished-right");
+ kill_squished(object);
+ return true;
+}
+
+IMPLEMENT_FACTORY(SnowBall, "snowball")
--- /dev/null
+// $Id$
+//
+// SuperTux
+// Copyright (C) 2006 Matthias Braun <matze@braunis.de>
+//
+// 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 __SNOWBALL_H__
+#define __SNOWBALL_H__
+
+#include "walking_badguy.hpp"
+
+class SnowBall : public WalkingBadguy
+{
+public:
+ SnowBall(const lisp::Lisp& reader);
+ SnowBall(const Vector& pos, Direction d);
+
+ void write(lisp::Writer& writer);
+ virtual SnowBall* clone() const { return new SnowBall(*this); }
+
+protected:
+ bool collision_squished(GameObject& object);
+
+};
+
+#endif
--- /dev/null
+// $Id$
+//
+// SuperTux
+// Copyright (C) 2006 Matthias Braun <matze@braunis.de>
+//
+// 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 <config.h>
+#include <stdio.h>
+
+#include "spidermite.hpp"
+
+static const float FLYTIME = 1.2f;
+static const float FLYSPEED = -100.0f;
+
+SpiderMite::SpiderMite(const lisp::Lisp& reader)
+ : BadGuy(reader, "images/creatures/spidermite/spidermite.sprite")
+{
+ physic.enable_gravity(false);
+}
+
+SpiderMite::SpiderMite(const Vector& pos)
+ : BadGuy(pos, "images/creatures/spidermite/spidermite.sprite")
+{
+ physic.enable_gravity(false);
+}
+
+void
+SpiderMite::write(lisp::Writer& writer)
+{
+ writer.start_list("spidermite");
+
+ writer.write_float("x", start_position.x);
+ writer.write_float("y", start_position.y);
+
+ writer.end_list("spidermite");
+}
+
+void
+SpiderMite::activate()
+{
+ sprite->set_action(dir == LEFT ? "left" : "right");
+ mode = FLY_UP;
+ physic.set_velocity_y(FLYSPEED);
+ timer.start(FLYTIME/2);
+}
+
+bool
+SpiderMite::collision_squished(GameObject& object)
+{
+ sprite->set_action(dir == LEFT ? "squished-left" : "squished-right");
+ kill_squished(object);
+ return true;
+}
+
+void
+SpiderMite::collision_solid(const CollisionHit& hit)
+{
+ if(hit.top || hit.bottom) { // hit floor or roof?
+ physic.set_velocity_y(0);
+ }
+}
+
+void
+SpiderMite::active_update(float elapsed_time)
+{
+ if(timer.check()) {
+ if(mode == FLY_UP) {
+ mode = FLY_DOWN;
+ physic.set_velocity_y(-FLYSPEED);
+ } else if(mode == FLY_DOWN) {
+ mode = FLY_UP;
+ physic.set_velocity_y(FLYSPEED);
+ }
+ timer.start(FLYTIME);
+ }
+ movement=physic.get_movement(elapsed_time);
+
+ Player* player = this->get_nearest_player();
+ if (player) {
+ dir = (player->get_pos().x > get_pos().x) ? RIGHT : LEFT;
+ sprite->set_action(dir == LEFT ? "left" : "right");
+ }
+}
+
+IMPLEMENT_FACTORY(SpiderMite, "spidermite")
--- /dev/null
+// $Id$
+//
+// SuperTux
+// Copyright (C) 2006 Matthias Braun <matze@braunis.de>
+//
+// 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 __SPIDERMITE_H__
+#define __SPIDERMITE_H__
+
+#include "badguy.hpp"
+
+class SpiderMite : public BadGuy
+{
+public:
+ SpiderMite(const lisp::Lisp& reader);
+ SpiderMite(const Vector& pos);
+
+ void activate();
+ void write(lisp::Writer& writer);
+ void active_update(float elapsed_time);
+ void collision_solid(const CollisionHit& hit);
+
+ virtual SpiderMite* clone() const { return new SpiderMite(*this); }
+
+protected:
+ enum SpiderMiteMode {
+ FLY_UP,
+ FLY_DOWN
+ };
+ SpiderMiteMode mode;
+ bool collision_squished(GameObject& object);
+private:
+ Timer timer;
+};
+
+#endif
--- /dev/null
+// $Id$
+//
+// SuperTux
+// Copyright (C) 2006 Matthias Braun <matze@braunis.de>
+//
+// 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 <config.h>
+
+#include "spiky.hpp"
+
+Spiky::Spiky(const lisp::Lisp& reader)
+ : WalkingBadguy(reader, "images/creatures/spiky/spiky.sprite", "left", "right")
+{
+ walk_speed = 80;
+ max_drop_height = 600;
+}
+
+void
+Spiky::write(lisp::Writer& writer)
+{
+ writer.start_list("spiky");
+ WalkingBadguy::write(writer);
+ writer.end_list("spiky");
+}
+
+void
+Spiky::freeze()
+{
+ WalkingBadguy::freeze();
+ sprite->set_action(dir == LEFT ? "iced-left" : "iced-right");
+}
+
+bool
+Spiky::is_freezable() const
+{
+ return true;
+}
+
+IMPLEMENT_FACTORY(Spiky, "spiky")
--- /dev/null
+// $Id$
+//
+// SuperTux
+// Copyright (C) 2006 Matthias Braun <matze@braunis.de>
+//
+// 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 __SPIKY_H__
+#define __SPIKY_H__
+
+#include "walking_badguy.hpp"
+
+class Spiky : public WalkingBadguy
+{
+public:
+ Spiky(const lisp::Lisp& reader);
+
+ void write(lisp::Writer& writer);
+ virtual Spiky* clone() const { return new Spiky(*this); }
+
+ void freeze();
+ bool is_freezable() const;
+
+private:
+};
+
+#endif
--- /dev/null
+// $Id$
+//
+// SuperTux
+// Copyright (C) 2006 Matthias Braun <matze@braunis.de>
+//
+// 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 <config.h>
+
+#include "sspiky.hpp"
+
+static const float WALKSPEED = 80;
+
+SSpiky::SSpiky(const lisp::Lisp& reader)
+ : WalkingBadguy(reader, "images/creatures/spiky/sleepingspiky.sprite", "left", "right"), state(SSPIKY_SLEEPING)
+{
+ walk_speed = WALKSPEED;
+ max_drop_height = -1;
+}
+
+void
+SSpiky::write(lisp::Writer& writer)
+{
+ writer.start_list("sspiky");
+ WalkingBadguy::write(writer);
+ writer.end_list("sspiky");
+}
+
+void
+SSpiky::activate()
+{
+ state = SSPIKY_SLEEPING;
+ physic.set_velocity_x(0);
+ sprite->set_action(dir == LEFT ? "sleeping-left" : "sleeping-right");
+}
+
+void
+SSpiky::collision_solid(const CollisionHit& hit)
+{
+ if(state != SSPIKY_WALKING) {
+ BadGuy::collision_solid(hit);
+ return;
+ }
+ WalkingBadguy::collision_solid(hit);
+}
+
+HitResponse
+SSpiky::collision_badguy(BadGuy& badguy, const CollisionHit& hit)
+{
+ if(state != SSPIKY_WALKING) {
+ return BadGuy::collision_badguy(badguy, hit);
+ }
+ return WalkingBadguy::collision_badguy(badguy, hit);
+}
+
+void
+SSpiky::active_update(float elapsed_time) {
+
+ if(state == SSPIKY_WALKING) {
+ WalkingBadguy::active_update(elapsed_time);
+ return;
+ }
+
+ if(state == SSPIKY_SLEEPING) {
+
+ Player* player = this->get_nearest_player();
+ if (player) {
+ Rect mb = this->get_bbox();
+ Rect pb = player->get_bbox();
+
+ bool inReach_left = (pb.p2.x >= mb.p2.x-((dir == LEFT) ? 256 : 0));
+ bool inReach_right = (pb.p1.x <= mb.p1.x+((dir == RIGHT) ? 256 : 0));
+ bool inReach_top = (pb.p2.y >= mb.p1.y);
+ bool inReach_bottom = (pb.p1.y <= mb.p2.y);
+
+ if (inReach_left && inReach_right && inReach_top && inReach_bottom) {
+ // wake up
+ sprite->set_action(dir == LEFT ? "waking-left" : "waking-right", 1);
+ state = SSPIKY_WAKING;
+ }
+ }
+
+ BadGuy::active_update(elapsed_time);
+ }
+
+ if(state == SSPIKY_WAKING) {
+ if(sprite->animation_done()) {
+ // start walking
+ state = SSPIKY_WALKING;
+ WalkingBadguy::activate();
+ }
+
+ BadGuy::active_update(elapsed_time);
+ }
+}
+
+void
+SSpiky::freeze()
+{
+ WalkingBadguy::freeze();
+ sprite->set_action(dir == LEFT ? "iced-left" : "iced-right");
+ state = SSPIKY_WALKING; // if we get hit while sleeping, wake up :)
+}
+
+bool
+SSpiky::is_freezable() const
+{
+ return true;
+}
+
+IMPLEMENT_FACTORY(SSpiky, "sspiky")
--- /dev/null
+// $Id$
+//
+// SuperTux
+// Copyright (C) 2006 Matthias Braun <matze@braunis.de>
+//
+// 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 __SSPIKY_H__
+#define __SSPIKY_H__
+
+#include "walking_badguy.hpp"
+
+class SSpiky : public WalkingBadguy
+{
+public:
+ SSpiky(const lisp::Lisp& reader);
+
+ void activate();
+ void write(lisp::Writer& writer);
+ void collision_solid(const CollisionHit& hit);
+ HitResponse collision_badguy(BadGuy& badguy, const CollisionHit& hit);
+ void active_update(float elapsed_time);
+
+ void freeze();
+ bool is_freezable() const;
+
+ virtual SSpiky* clone() const { return new SSpiky(*this); }
+
+protected:
+ enum SSpikyState {
+ SSPIKY_SLEEPING,
+ SSPIKY_WAKING,
+ SSPIKY_WALKING
+ };
+ SSpikyState state;
+};
+
+#endif
--- /dev/null
+// $Id$
+//
+// SuperTux
+// Copyright (C) 2006 Matthias Braun <matze@braunis.de>
+//
+// 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 <config.h>
+
+#include "stalactite.hpp"
+#include "random_generator.hpp"
+
+static const int SHAKE_RANGE_X = 40;
+static const float SHAKE_TIME = .8f;
+static const float SQUISH_TIME = 2;
+static const float SHAKE_RANGE_Y = 400;
+
+Stalactite::Stalactite(const lisp::Lisp& lisp)
+ : BadGuy(lisp, "images/creatures/stalactite/stalactite.sprite", LAYER_TILES - 1), state(STALACTITE_HANGING)
+{
+ countMe = false;
+}
+
+void
+Stalactite::write(lisp::Writer& writer)
+{
+ writer.start_list("stalactite");
+ writer.write_float("x", start_position.x);
+ writer.write_float("y", start_position.y);
+ writer.end_list("stalactite");
+}
+
+void
+Stalactite::active_update(float elapsed_time)
+{
+ if(state == STALACTITE_HANGING) {
+ Player* player = this->get_nearest_player();
+ if (player) {
+ if(player->get_bbox().p2.x > bbox.p1.x - SHAKE_RANGE_X
+ && player->get_bbox().p1.x < bbox.p2.x + SHAKE_RANGE_X
+ && player->get_bbox().p2.y > bbox.p1.y
+ && player->get_bbox().p1.y < bbox.p2.y + SHAKE_RANGE_Y) {
+ timer.start(SHAKE_TIME);
+ state = STALACTITE_SHAKING;
+ }
+ }
+ } else if(state == STALACTITE_SHAKING) {
+ if(timer.check()) {
+ state = STALACTITE_FALLING;
+ physic.enable_gravity(true);
+ }
+ } else if(state == STALACTITE_FALLING || state == STALACTITE_SQUISHED) {
+ movement = physic.get_movement(elapsed_time);
+ if(state == STALACTITE_SQUISHED && timer.check())
+ remove_me();
+ }
+}
+
+void
+Stalactite::squish()
+{
+ state = STALACTITE_SQUISHED;
+ set_group(COLGROUP_MOVING_ONLY_STATIC);
+ sprite->set_action("squished");
+ if(!timer.started())
+ timer.start(SQUISH_TIME);
+}
+
+void
+Stalactite::collision_solid(const CollisionHit& hit)
+{
+ if(state == STALACTITE_FALLING) {
+ if (hit.bottom) squish();
+ }
+ if(state == STALACTITE_SQUISHED) {
+ physic.set_velocity_y(0);
+ }
+}
+
+HitResponse
+Stalactite::collision_player(Player& player)
+{
+ if(state != STALACTITE_SQUISHED) {
+ player.kill(false);
+ }
+
+ return FORCE_MOVE;
+}
+
+HitResponse
+Stalactite::collision_badguy(BadGuy& other, const CollisionHit& hit)
+{
+ if (state == STALACTITE_SQUISHED) return FORCE_MOVE;
+ if (state != STALACTITE_FALLING) return BadGuy::collision_badguy(other, hit);
+
+ // ignore other Stalactites
+ if (dynamic_cast<Stalactite*>(&other)) return FORCE_MOVE;
+
+ if (other.is_freezable()) {
+ other.freeze();
+ } else {
+ other.kill_fall();
+ }
+
+ remove_me();
+
+ return FORCE_MOVE;
+}
+
+void
+Stalactite::kill_fall()
+{
+}
+
+void
+Stalactite::draw(DrawingContext& context)
+{
+ if(get_state() != STATE_ACTIVE)
+ return;
+
+
+ if(state == STALACTITE_SQUISHED) {
+ sprite->draw(context, get_pos(), LAYER_OBJECTS);
+ return;
+ }
+
+ if(state == STALACTITE_SHAKING) {
+ sprite->draw(context, get_pos() + Vector(systemRandom.rand(-3,3), 0), layer);
+ } else {
+ sprite->draw(context, get_pos(), layer);
+ }
+}
+
+void
+Stalactite::deactivate()
+{
+ if(state != STALACTITE_HANGING)
+ remove_me();
+}
+
+IMPLEMENT_FACTORY(Stalactite, "stalactite")
--- /dev/null
+// $Id$
+//
+// SuperTux
+// Copyright (C) 2006 Matthias Braun <matze@braunis.de>
+//
+// 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 __STALACTITE_H__
+#define __STALACTITE_H__
+
+#include "badguy.hpp"
+
+class Stalactite : public BadGuy
+{
+public:
+ Stalactite(const lisp::Lisp& reader);
+
+ void active_update(float elapsed_time);
+ void write(lisp::Writer& writer);
+ void collision_solid(const CollisionHit& hit);
+ HitResponse collision_player(Player& player);
+ HitResponse collision_badguy(BadGuy& other, const CollisionHit& hit);
+
+ void kill_fall();
+ void draw(DrawingContext& context);
+ void deactivate();
+
+ virtual Stalactite* clone() const { return new Stalactite(*this); }
+
+ void squish();
+
+protected:
+ Timer timer;
+
+ enum StalactiteState {
+ STALACTITE_HANGING,
+ STALACTITE_SHAKING,
+ STALACTITE_FALLING,
+ STALACTITE_SQUISHED
+ };
+ StalactiteState state;
+};
+
+#endif
--- /dev/null
+// $Id$
+//
+// SuperTux
+// Copyright (C) 2006 Matthias Braun <matze@braunis.de>
+//
+// 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 <config.h>
+
+#include "stumpy.hpp"
+#include "poisonivy.hpp"
+#include "random_generator.hpp"
+#include "object/sprite_particle.hpp"
+
+static const float WALKSPEED = 120;
+static const float INVINCIBLE_TIME = 1;
+
+Stumpy::Stumpy(const lisp::Lisp& reader)
+ : WalkingBadguy(reader, "images/creatures/mr_tree/stumpy.sprite","left","right"), mystate(STATE_NORMAL)
+{
+ walk_speed = WALKSPEED;
+ max_drop_height = 16;
+ sound_manager->preload("sounds/mr_tree.ogg");
+ sound_manager->preload("sounds/mr_treehit.ogg");
+}
+
+Stumpy::Stumpy(const Vector& pos, Direction d)
+ : WalkingBadguy(pos, d, "images/creatures/mr_tree/stumpy.sprite","left","right"), mystate(STATE_INVINCIBLE)
+{
+ walk_speed = WALKSPEED;
+ max_drop_height = 16;
+ sound_manager->preload("sounds/mr_treehit.ogg");
+ invincible_timer.start(INVINCIBLE_TIME);
+}
+
+
+void
+Stumpy::write(lisp::Writer& writer)
+{
+ writer.start_list("stumpy");
+ WalkingBadguy::write(writer);
+ writer.end_list("stumpy");
+}
+
+void
+Stumpy::activate()
+{
+ switch (mystate) {
+ case STATE_INVINCIBLE:
+ sprite->set_action(dir == LEFT ? "dizzy-left" : "dizzy-right");
+ bbox.set_size(sprite->get_current_hitbox_width(), sprite->get_current_hitbox_height());
+ physic.set_velocity_x(0);
+ break;
+ case STATE_NORMAL:
+ WalkingBadguy::activate();
+ break;
+ }
+}
+
+void
+Stumpy::active_update(float elapsed_time)
+{
+ switch (mystate) {
+ case STATE_INVINCIBLE:
+ if (invincible_timer.check()) {
+ mystate = STATE_NORMAL;
+ WalkingBadguy::activate();
+ }
+ BadGuy::active_update(elapsed_time);
+ break;
+ case STATE_NORMAL:
+ WalkingBadguy::active_update(elapsed_time);
+ break;
+ }
+}
+
+bool
+Stumpy::collision_squished(GameObject& object)
+{
+
+ // if we're still invincible, we ignore the hit
+ if (mystate == STATE_INVINCIBLE) {
+ sound_manager->play("sounds/mr_treehit.ogg", get_pos());
+ Player* player = dynamic_cast<Player*>(&object);
+ if (player) player->bounce(*this);
+ return true;
+ }
+
+ // if we can die, we do
+ if (mystate == STATE_NORMAL) {
+ sprite->set_action(dir == LEFT ? "squished-left" : "squished-right");
+ set_size(sprite->get_current_hitbox_width(), sprite->get_current_hitbox_height());
+ kill_squished(object);
+ // spawn some particles
+ // TODO: provide convenience function in MovingSprite or MovingObject?
+ for (int i = 0; i < 25; i++) {
+ Vector ppos = bbox.get_middle();
+ float angle = systemRandom.randf(-M_PI_2, M_PI_2);
+ float velocity = systemRandom.randf(45, 90);
+ float vx = sin(angle)*velocity;
+ float vy = -cos(angle)*velocity;
+ Vector pspeed = Vector(vx, vy);
+ Vector paccel = Vector(0, 100);
+ Sector::current()->add_object(new SpriteParticle("images/objects/particles/bark.sprite", "default", ppos, ANCHOR_MIDDLE, pspeed, paccel, LAYER_OBJECTS-1));
+ }
+
+ return true;
+
+ }
+
+ //TODO: exception?
+ return true;
+}
+
+void
+Stumpy::collision_solid(const CollisionHit& hit)
+{
+ update_on_ground_flag(hit);
+
+ switch (mystate) {
+ case STATE_INVINCIBLE:
+ if(hit.top || hit.bottom) {
+ physic.set_velocity_y(0);
+ }
+ if(hit.left || hit.right) {
+ physic.set_velocity_x(0);
+ }
+ break;
+ case STATE_NORMAL:
+ WalkingBadguy::collision_solid(hit);
+ break;
+ }
+}
+
+HitResponse
+Stumpy::collision_badguy(BadGuy& badguy, const CollisionHit& hit)
+{
+ switch (mystate) {
+ case STATE_INVINCIBLE:
+ if(hit.top || hit.bottom) {
+ physic.set_velocity_y(0);
+ }
+ if(hit.left || hit.right) {
+ physic.set_velocity_x(0);
+ }
+ return CONTINUE;
+ break;
+ case STATE_NORMAL:
+ return WalkingBadguy::collision_badguy(badguy, hit);
+ break;
+ }
+ return CONTINUE;
+}
+
+IMPLEMENT_FACTORY(Stumpy, "stumpy")
--- /dev/null
+// $Id$
+//
+// SuperTux
+// Copyright (C) 2006 Matthias Braun <matze@braunis.de>
+//
+// 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 __STUMPY_H__
+#define __STUMPY_H__
+
+#include "walking_badguy.hpp"
+
+class Stumpy : public WalkingBadguy
+{
+public:
+ Stumpy(const lisp::Lisp& reader);
+ Stumpy(const Vector& pos, Direction d);
+
+ void activate();
+ void active_update(float elapsed_time);
+ void write(lisp::Writer& writer);
+ void collision_solid(const CollisionHit& hit);
+ HitResponse collision_badguy(BadGuy& badguy, const CollisionHit& hit);
+
+ virtual Stumpy* clone() const { return new Stumpy(*this); }
+
+protected:
+ enum MyState {
+ STATE_INVINCIBLE, STATE_NORMAL
+ };
+ MyState mystate;
+
+ Timer invincible_timer;
+
+ bool collision_squished(GameObject& object);
+};
+
+#endif
--- /dev/null
+// $Id$
+//
+// Toad - A jumping toad
+// Copyright (C) 2006 Christoph Sommer <christoph.sommer@2006.expires.deltadevelopment.de>
+//
+// 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 <config.h>
+
+#include "toad.hpp"
+#include "random_generator.hpp"
+
+namespace {
+ const float VERTICAL_SPEED = -450; /**< y-speed when jumping */
+ const float HORIZONTAL_SPEED = 320; /**< x-speed when jumping */
+ const float RECOVER_TIME = 0.5; /**< time to stand still before starting a (new) jump */
+ static const std::string HOP_SOUND = "sounds/hop.ogg";
+}
+
+Toad::Toad(const lisp::Lisp& reader)
+ : BadGuy(reader, "images/creatures/toad/toad.sprite")
+{
+ sound_manager->preload(HOP_SOUND);
+}
+
+Toad::Toad(const Vector& pos, Direction d)
+ : BadGuy(pos, d, "images/creatures/toad/toad.sprite")
+{
+ sound_manager->preload(HOP_SOUND);
+}
+
+void
+Toad::write(lisp::Writer& writer)
+{
+ writer.start_list("toad");
+ writer.write_float("x", start_position.x);
+ writer.write_float("y", start_position.y);
+ writer.end_list("toad");
+}
+
+void
+Toad::activate()
+{
+ // initial state is JUMPING, because we might start airborne
+ state = JUMPING;
+ sprite->set_action(dir == LEFT ? "jumping-left" : "jumping-right");
+}
+
+void
+Toad::set_state(ToadState newState)
+{
+ if (newState == IDLE) {
+ physic.set_velocity_x(0);
+ physic.set_velocity_y(0);
+ sprite->set_action(dir == LEFT ? "idle-left" : "idle-right");
+
+ recover_timer.start(RECOVER_TIME);
+ } else
+ if (newState == JUMPING) {
+ sprite->set_action(dir == LEFT ? "jumping-left" : "jumping-right");
+ physic.set_velocity_x(dir == LEFT ? -HORIZONTAL_SPEED : HORIZONTAL_SPEED);
+ physic.set_velocity_y(VERTICAL_SPEED);
+ sound_manager->play( HOP_SOUND, get_pos());
+ } else
+ if (newState == FALLING) {
+ Player* player = get_nearest_player();
+ // face player
+ if (player && (player->get_bbox().p2.x < get_bbox().p1.x) && (dir == RIGHT)) dir = LEFT;
+ if (player && (player->get_bbox().p1.x > get_bbox().p2.x) && (dir == LEFT)) dir = RIGHT;
+ sprite->set_action(dir == LEFT ? "idle-left" : "idle-right");
+ }
+
+ state = newState;
+}
+
+bool
+Toad::collision_squished(GameObject& object)
+{
+ sprite->set_action(dir == LEFT ? "squished-left" : "squished-right");
+ kill_squished(object);
+ return true;
+}
+
+void
+Toad::collision_solid(const CollisionHit& hit)
+{
+ // just default behaviour (i.e. stop at floor/walls) when squished
+ if (BadGuy::get_state() == STATE_SQUISHED) {
+ BadGuy::collision_solid(hit);
+ return;
+ }
+
+ // ignore collisions while standing still
+ if(state == IDLE) {
+ return;
+ }
+
+ // check if we hit left or right while moving in either direction
+ if(((physic.get_velocity_x() < 0) && hit.left) || ((physic.get_velocity_x() > 0) && hit.right)) {
+ /*
+ dir = dir == LEFT ? RIGHT : LEFT;
+ if (state == JUMPING) {
+ sprite->set_action(dir == LEFT ? "jumping-left" : "jumping-right");
+ } else {
+ sprite->set_action(dir == LEFT ? "idle-left" : "idle-right");
+ }
+ */
+ physic.set_velocity_x(-0.25*physic.get_velocity_x());
+ }
+
+ // check if we hit the floor while falling
+ if ((state == FALLING) && hit.bottom) {
+ set_state(IDLE);
+ return;
+ }
+
+ // check if we hit the roof while climbing
+ if ((state == JUMPING) && hit.top) {
+ physic.set_velocity_y(0);
+ }
+
+}
+
+HitResponse
+Toad::collision_badguy(BadGuy& , const CollisionHit& hit)
+{
+ // behaviour for badguy collisions is the same as for collisions with solids
+ collision_solid(hit);
+
+ return CONTINUE;
+}
+
+void
+Toad::active_update(float elapsed_time)
+{
+ BadGuy::active_update(elapsed_time);
+
+ // change sprite when we are falling
+ if ((state == JUMPING) && (physic.get_velocity_y() > 0)) {
+ set_state(FALLING);
+ return;
+ }
+
+ // jump when fully recovered
+ if ((state == IDLE) && (recover_timer.check())) {
+ set_state(JUMPING);
+ return;
+ }
+
+}
+
+IMPLEMENT_FACTORY(Toad, "toad")
--- /dev/null
+// $Id$
+//
+// Toad - A jumping toad
+// Copyright (C) 2006 Christoph Sommer <christoph.sommer@2006.expires.deltadevelopment.de>
+//
+// 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 __TOAD_H__
+#define __TOAD_H__
+
+#include "badguy.hpp"
+
+/**
+ * Badguy "Toad" - A jumping toad
+ */
+class Toad : public BadGuy
+{
+public:
+ Toad(const lisp::Lisp& reader);
+ Toad(const Vector& pos, Direction d);
+
+ void activate();
+ void write(lisp::Writer& writer);
+ void collision_solid(const CollisionHit& hit);
+ HitResponse collision_badguy(BadGuy& badguy, const CollisionHit& hit);
+ bool collision_squished(GameObject& object);
+ void active_update(float elapsed_time);
+
+ virtual Toad* clone() const { return new Toad(*this); }
+
+protected:
+ enum ToadState {
+ IDLE,
+ JUMPING,
+ FALLING
+ };
+
+ Timer recover_timer;
+ ToadState state;
+
+ void set_state(ToadState newState);
+};
+
+#endif
--- /dev/null
+// $Id$
+//
+// SuperTux - "Totem" Badguy
+// Copyright (C) 2006 Christoph Sommer <christoph.sommer@2006.expires.deltadevelopment.de>
+//
+// 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 <config.h>
+
+#include "totem.hpp"
+#include "log.hpp"
+
+static const float WALKSPEED = 100;
+static const float JUMP_ON_SPEED_Y = -400;
+static const float JUMP_OFF_SPEED_Y = -500;
+static const std::string LAND_ON_TOTEM_SOUND = "sounds/totem.ogg";
+
+Totem::Totem(const lisp::Lisp& reader)
+ : BadGuy(reader, "images/creatures/totem/totem.sprite")
+{
+ carrying = 0;
+ carried_by = 0;
+ sound_manager->preload( LAND_ON_TOTEM_SOUND );
+}
+
+Totem::Totem(const Totem& other)
+ : BadGuy(other), carrying(other.carrying), carried_by(other.carried_by)
+{
+ sound_manager->preload( LAND_ON_TOTEM_SOUND );
+}
+
+Totem::~Totem()
+{
+ if (carrying) carrying->jump_off();
+ if (carried_by) jump_off();
+}
+
+bool
+Totem::updatePointers(const GameObject* from_object, GameObject* to_object)
+{
+ if (from_object == carrying) {
+ carrying = dynamic_cast<Totem*>(to_object);
+ return true;
+ }
+ if (from_object == carried_by) {
+ carried_by = dynamic_cast<Totem*>(to_object);
+ return true;
+ }
+ return false;
+}
+
+void
+Totem::write(lisp::Writer& writer)
+{
+ writer.start_list("totem");
+
+ writer.write_float("x", start_position.x);
+ writer.write_float("y", start_position.y);
+
+ writer.end_list("totem");
+}
+
+void
+Totem::activate()
+{
+ if (!carried_by) {
+ physic.set_velocity_x(dir == LEFT ? -WALKSPEED : WALKSPEED);
+ sprite->set_action(dir == LEFT ? "walking-left" : "walking-right");
+ return;
+ } else {
+ synchronize_with(carried_by);
+ sprite->set_action(dir == LEFT ? "stacked-left" : "stacked-right");
+ return;
+ }
+}
+
+void
+Totem::active_update(float elapsed_time)
+{
+ BadGuy::active_update(elapsed_time);
+
+ if (!carried_by) {
+ if (on_ground() && might_fall())
+ {
+ dir = (dir == LEFT ? RIGHT : LEFT);
+ activate();
+ }
+
+ Sector* s = Sector::current();
+ if (s) {
+ // jump a bit if we find a suitable totem
+ for (std::vector<MovingObject*>::iterator i = s->moving_objects.begin(); i != s->moving_objects.end(); i++) {
+ Totem* t = dynamic_cast<Totem*>(*i);
+ if (!t) continue;
+
+ // skip if we are not approaching each other
+ if (!((this->dir == LEFT) && (t->dir == RIGHT))) continue;
+
+ Vector p1 = this->get_pos();
+ Vector p2 = t->get_pos();
+
+ // skip if not on same height
+ float dy = (p1.y - p2.y);
+ if (fabsf(dy - 0) > 2) continue;
+
+ // skip if too far away
+ float dx = (p1.x - p2.x);
+ if (fabsf(dx - 128) > 2) continue;
+
+ physic.set_velocity_y(JUMP_ON_SPEED_Y);
+ p1.y -= 1;
+ this->set_pos(p1);
+ break;
+ }
+ }
+ }
+
+ if (carried_by) {
+ this->synchronize_with(carried_by);
+ }
+
+ if (carrying) {
+ carrying->synchronize_with(this);
+ }
+
+}
+
+bool
+Totem::collision_squished(GameObject& object)
+{
+ if (carrying) carrying->jump_off();
+ if (carried_by) {
+ Player* player = dynamic_cast<Player*>(&object);
+ if (player) player->bounce(*this);
+ jump_off();
+ }
+
+ sprite->set_action(dir == LEFT ? "squished-left" : "squished-right");
+ bbox.set_size(sprite->get_current_hitbox_width(), sprite->get_current_hitbox_height());
+
+ kill_squished(object);
+ return true;
+}
+
+void
+Totem::collision_solid(const CollisionHit& hit)
+{
+ update_on_ground_flag(hit);
+
+ // if we are being carried around, pass event to bottom of stack and ignore it
+ if (carried_by) {
+ carried_by->collision_solid(hit);
+ return;
+ }
+
+ // If we hit something from above or below: stop moving in this direction
+ if (hit.top || hit.bottom) {
+ physic.set_velocity_y(0);
+ }
+
+ // If we are hit from the direction we are facing: turn around
+ if (hit.left && (dir == LEFT)) {
+ dir = RIGHT;
+ activate();
+ }
+ if (hit.right && (dir == RIGHT)) {
+ dir = LEFT;
+ activate();
+ }
+}
+
+HitResponse
+Totem::collision_badguy(BadGuy& badguy, const CollisionHit& hit)
+{
+ // if we are being carried around, pass event to bottom of stack and ignore it
+ if (carried_by) {
+ carried_by->collision_badguy(badguy, hit);
+ return CONTINUE;
+ }
+
+ // if we hit a Totem that is not from our stack: have our base jump on its top
+ Totem* totem = dynamic_cast<Totem*>(&badguy);
+ if (totem) {
+ Totem* thisBase = this; while (thisBase->carried_by) thisBase=thisBase->carried_by;
+ Totem* srcBase = totem; while (srcBase->carried_by) srcBase=srcBase->carried_by;
+ Totem* thisTop = this; while (thisTop->carrying) thisTop=thisTop->carrying;
+ if (srcBase != thisBase) {
+ srcBase->jump_on(thisTop);
+ }
+ }
+
+ // If we are hit from the direction we are facing: turn around
+ if(hit.left && (dir == LEFT)) {
+ dir = RIGHT;
+ activate();
+ }
+ if(hit.right && (dir == RIGHT)) {
+ dir = LEFT;
+ activate();
+ }
+
+ return CONTINUE;
+}
+
+void
+Totem::kill_fall()
+{
+ if (carrying) carrying->jump_off();
+ if (carried_by) jump_off();
+
+ BadGuy::kill_fall();
+}
+
+void
+Totem::jump_on(Totem* target)
+{
+ if (target->carrying) {
+ log_warning << "target is already carrying someone" << std::endl;
+ return;
+ }
+
+ target->carrying = this;
+
+ this->carried_by = target;
+ this->activate();
+ bbox.set_size(sprite->get_current_hitbox_width(), sprite->get_current_hitbox_height());
+
+ sound_manager->play( LAND_ON_TOTEM_SOUND , get_pos());
+
+
+ this->synchronize_with(target);
+}
+
+void
+Totem::jump_off() {
+ if (!carried_by) {
+ log_warning << "not carried by anyone" << std::endl;
+ return;
+ }
+
+ carried_by->carrying = 0;
+
+ this->carried_by = 0;
+
+ this->activate();
+ bbox.set_size(sprite->get_current_hitbox_width(), sprite->get_current_hitbox_height());
+
+
+ physic.set_velocity_y(JUMP_OFF_SPEED_Y);
+}
+
+void
+Totem::synchronize_with(Totem* base)
+{
+
+ if (dir != base->dir) {
+ dir = base->dir;
+ sprite->set_action(dir == LEFT ? "stacked-left" : "stacked-right");
+ }
+
+ Vector pos = base->get_pos();
+ pos.y -= sprite->get_current_hitbox_height();
+ set_pos(pos);
+
+ physic.set_velocity_x(base->physic.get_velocity_x());
+ physic.set_velocity_y(base->physic.get_velocity_y());
+}
+
+
+IMPLEMENT_FACTORY(Totem, "totem")
--- /dev/null
+// $Id$
+//
+// SuperTux - "Totem" Badguy
+// Copyright (C) 2006 Christoph Sommer <christoph.sommer@2006.expires.deltadevelopment.de>
+//
+// 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 __TOTEM_H__
+#define __TOTEM_H__
+
+#include "badguy.hpp"
+
+/**
+ * "Totem" Badguy - A variable-height stack of wooden blocks
+ */
+class Totem : public BadGuy
+{
+public:
+ Totem(const lisp::Lisp& reader);
+ Totem(const Totem& totem);
+ ~Totem();
+
+ void activate();
+ void active_update(float elapsed_time);
+ void write(lisp::Writer& writer);
+ void collision_solid(const CollisionHit& hit);
+ HitResponse collision_badguy(BadGuy& badguy, const CollisionHit& hit);
+
+ virtual Totem* clone() const { return new Totem(*this); }
+ virtual bool updatePointers(const GameObject* from_object, GameObject* to_object);
+
+protected:
+ Totem* carrying; /**< Totem we are currently carrying (or 0) */
+ Totem* carried_by; /**< Totem by which we are currently carried (or 0) */
+
+ bool collision_squished(GameObject& object);
+ void kill_fall();
+
+ void jump_on(Totem* target); /**< jump on target */
+ void jump_off(); /**< jump off current base */
+
+ void synchronize_with(Totem* baseTotem); /**< synchronize position and movement with baseTotem */
+};
+
+#endif
--- /dev/null
+// $Id$
+//
+// SuperTux - "Will-O-Wisp" Badguy
+// Copyright (C) 2007 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 <config.h>
+
+#include "treewillowisp.hpp"
+#include "ghosttree.hpp"
+#include "object/lantern.hpp"
+
+static const std::string SOUNDFILE = "sounds/willowisp.wav";
+static const float SUCKSPEED = 25;
+
+TreeWillOWisp::TreeWillOWisp(GhostTree* tree, const Vector& pos,
+ float radius, float speed)
+ : BadGuy(Vector(0, 0), "images/creatures/willowisp/willowisp.sprite",
+ LAYER_OBJECTS - 20), was_sucked(false), mystate(STATE_DEFAULT), tree(tree)
+{
+ treepos_delta = pos;
+ sound_manager->preload(SOUNDFILE);
+
+ this->radius = radius;
+ this->angle = 0;
+ this->speed = speed;
+ start_position = tree->get_pos() + treepos_delta;
+}
+
+TreeWillOWisp::~TreeWillOWisp()
+{
+}
+
+void
+TreeWillOWisp::activate()
+{
+ sound_source.reset(sound_manager->create_sound_source(SOUNDFILE));
+ sound_source->set_position(get_pos());
+ sound_source->set_looping(true);
+ sound_source->set_gain(2.0);
+ sound_source->set_reference_distance(32);
+ sound_source->play();
+
+ set_group(COLGROUP_MOVING);
+}
+
+void
+TreeWillOWisp::vanish()
+{
+ mystate = STATE_VANISHING;
+ sprite->set_action("vanishing", 1);
+ set_group(COLGROUP_DISABLED);
+}
+
+void
+TreeWillOWisp::start_sucking(Vector suck_target)
+{
+ mystate = STATE_SUCKED;
+ this->suck_target = suck_target;
+ was_sucked = true;
+}
+
+HitResponse
+TreeWillOWisp::collision_player(Player& player, const CollisionHit& hit)
+{
+ //TODO: basically a no-op. Remove if this doesn't change.
+ return BadGuy::collision_player(player, hit);
+}
+
+bool
+TreeWillOWisp::collides(GameObject& other, const CollisionHit& ) {
+ Lantern* lantern = dynamic_cast<Lantern*>(&other);
+ if (lantern && lantern->is_open())
+ return true;
+ if (dynamic_cast<Player*>(&other))
+ return true;
+
+ return false;
+}
+
+void
+TreeWillOWisp::draw(DrawingContext& context)
+{
+ sprite->draw(context, get_pos(), layer);
+
+ context.push_target();
+ context.set_target(DrawingContext::LIGHTMAP);
+
+ sprite->draw(context, get_pos(), layer);
+
+ context.pop_target();
+}
+
+void
+TreeWillOWisp::active_update(float elapsed_time)
+{
+ // remove TreeWillOWisp if it has completely vanished
+ if (mystate == STATE_VANISHING) {
+ if(sprite->animation_done()) {
+ remove_me();
+ tree->willowisp_died(this);
+ }
+ return;
+ }
+
+ if (mystate == STATE_SUCKED) {
+ Vector dir = suck_target - get_pos();
+ if(dir.norm() < 5) {
+ vanish();
+ return;
+ }
+ Vector newpos = get_pos() + dir * elapsed_time;
+ movement = newpos - get_pos();
+ return;
+ }
+
+ angle = fmodf(angle + elapsed_time * speed, (float) (2*M_PI));
+ Vector newpos(tree->get_pos() + treepos_delta + Vector(sin(angle) * radius, 0));
+ movement = newpos - get_pos();
+ float sizemod = cos(angle) * 0.8f;
+ /* TODO: modify sprite size */
+
+ sound_source->set_position(get_pos());
+
+ if(sizemod < 0) {
+ layer = LAYER_OBJECTS + 5;
+ } else {
+ layer = LAYER_OBJECTS - 20;
+ }
+}
+
+void
+TreeWillOWisp::set_color(const Color& color)
+{
+ this->color = color;
+ sprite->set_color(color);
+}
+
+Color
+TreeWillOWisp::get_color() const
+{
+ return color;
+}
+
--- /dev/null
+// $Id$
+//
+// SuperTux - "Will-O-Wisp" Badguy
+// Copyright (C) 2007 Matthias Braun <matze@braunis.de>
+//
+// 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 __TREEWILLOWISP_H__
+#define __TREEWILLOWISP_H__
+
+#include "badguy.hpp"
+
+class GhostTree;
+
+class TreeWillOWisp : public BadGuy
+{
+public:
+ TreeWillOWisp(GhostTree* tree, const Vector& pos, float radius, float speed);
+ virtual ~TreeWillOWisp();
+
+ void activate();
+
+ /**
+ * make TreeWillOWisp vanish
+ */
+ void vanish();
+ void start_sucking(Vector suck_target);
+ bool was_sucked;
+
+ void active_update(float elapsed_time);
+ void set_color(const Color& color);
+ Color get_color() const;
+
+ virtual bool is_flammable() const { return false; }
+ virtual bool is_freezable() const { return false; }
+ virtual void kill_fall() { vanish(); }
+
+ virtual void draw(DrawingContext& context);
+
+protected:
+ virtual bool collides(GameObject& other, const CollisionHit& hit);
+ HitResponse collision_player(Player& player, const CollisionHit& hit);
+
+private:
+ enum MyState {
+ STATE_DEFAULT, STATE_VANISHING, STATE_SUCKED
+ };
+ MyState mystate;
+
+ Color color;
+ float angle;
+ float radius;
+ float speed;
+
+ std::auto_ptr<SoundSource> sound_source;
+ Vector treepos_delta;
+ GhostTree* tree;
+
+ Vector suck_target;
+};
+
+#endif
+
--- /dev/null
+// $Id$
+//
+// SuperTux - WalkingBadguy
+// Copyright (C) 2006 Christoph Sommer <christoph.sommer@2006.expires.deltadevelopment.de>
+//
+// 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 <config.h>
+
+#include "walking_badguy.hpp"
+#include "log.hpp"
+#include "timer.hpp"
+
+WalkingBadguy::WalkingBadguy(const Vector& pos, const std::string& sprite_name, const std::string& walk_left_action, const std::string& walk_right_action, int layer)
+ : BadGuy(pos, sprite_name, layer), walk_left_action(walk_left_action), walk_right_action(walk_right_action), walk_speed(80), max_drop_height(-1)
+{
+}
+
+WalkingBadguy::WalkingBadguy(const Vector& pos, Direction direction, const std::string& sprite_name, const std::string& walk_left_action, const std::string& walk_right_action, int layer)
+ : BadGuy(pos, direction, sprite_name, layer), walk_left_action(walk_left_action), walk_right_action(walk_right_action), walk_speed(80), max_drop_height(-1)
+{
+}
+
+WalkingBadguy::WalkingBadguy(const lisp::Lisp& reader, const std::string& sprite_name, const std::string& walk_left_action, const std::string& walk_right_action, int layer)
+ : BadGuy(reader, sprite_name, layer), walk_left_action(walk_left_action), walk_right_action(walk_right_action), walk_speed(80), max_drop_height(-1)
+{
+}
+
+void
+WalkingBadguy::write(lisp::Writer& writer)
+{
+ writer.write_float("x", start_position.x);
+ writer.write_float("y", start_position.y);
+}
+
+void
+WalkingBadguy::activate()
+{
+ if(frozen)
+ return;
+ sprite->set_action(dir == LEFT ? walk_left_action : walk_right_action);
+ bbox.set_size(sprite->get_current_hitbox_width(), sprite->get_current_hitbox_height());
+ physic.set_velocity_x(dir == LEFT ? -walk_speed : walk_speed);
+}
+
+void
+WalkingBadguy::active_update(float elapsed_time)
+{
+ BadGuy::active_update(elapsed_time);
+
+ if (max_drop_height > -1) {
+ if (on_ground() && might_fall(max_drop_height+1))
+ {
+ turn_around();
+ }
+ }
+
+}
+
+void
+WalkingBadguy::collision_solid(const CollisionHit& hit)
+{
+
+ update_on_ground_flag(hit);
+
+ if (hit.top) {
+ if (physic.get_velocity_y() < 0) physic.set_velocity_y(0);
+ }
+ if (hit.bottom) {
+ if (physic.get_velocity_y() > 0) physic.set_velocity_y(0);
+ }
+
+ if ((hit.left && (hit.slope_normal.y == 0) && (dir == LEFT)) || (hit.right && (hit.slope_normal.y == 0) && (dir == RIGHT))) {
+ turn_around();
+ }
+
+}
+
+HitResponse
+WalkingBadguy::collision_badguy(BadGuy& , const CollisionHit& hit)
+{
+
+ if ((hit.left && (dir == LEFT)) || (hit.right && (dir == RIGHT))) {
+ turn_around();
+ }
+
+ return CONTINUE;
+}
+
+void
+WalkingBadguy::turn_around()
+{
+ if(frozen)
+ return;
+ dir = dir == LEFT ? RIGHT : LEFT;
+ sprite->set_action(dir == LEFT ? walk_left_action : walk_right_action);
+ physic.set_velocity_x(-physic.get_velocity_x());
+
+ // if we get dizzy, we fall off the screen
+ if (turn_around_timer.started()) {
+ if (turn_around_counter++ > 10) kill_fall();
+ } else {
+ turn_around_timer.start(1);
+ turn_around_counter = 0;
+ }
+
+}
+
+void
+WalkingBadguy::freeze()
+{
+ BadGuy::freeze();
+ physic.set_velocity_x(0);
+}
+
+void
+WalkingBadguy::unfreeze()
+{
+ BadGuy::unfreeze();
+ WalkingBadguy::activate();
+}
+
+
+float
+WalkingBadguy::get_velocity_y() const
+{
+ return physic.get_velocity_y();
+}
+
+void
+WalkingBadguy::set_velocity_y(float vy)
+{
+ physic.set_velocity_y(vy);
+}
--- /dev/null
+// $Id$
+//
+// SuperTux - WalkingBadguy
+// Copyright (C) 2006 Christoph Sommer <christoph.sommer@2006.expires.deltadevelopment.de>
+//
+// 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 __WALKING_BADGUY_H__
+#define __WALKING_BADGUY_H__
+
+#include "badguy.hpp"
+
+class Timer;
+
+/**
+ * Baseclass for a Badguy that just walks around.
+ */
+class WalkingBadguy : public BadGuy
+{
+public:
+ WalkingBadguy(const Vector& pos, const std::string& sprite_name, const std::string& walk_left_action, const std::string& walk_right_action, int layer = LAYER_OBJECTS);
+ WalkingBadguy(const Vector& pos, Direction direction, const std::string& sprite_name, const std::string& walk_left_action, const std::string& walk_right_action, int layer = LAYER_OBJECTS);
+ WalkingBadguy(const lisp::Lisp& reader, const std::string& sprite_name, const std::string& walk_left_action, const std::string& walk_right_action, int layer = LAYER_OBJECTS);
+
+ void activate();
+ void write(lisp::Writer& writer);
+ void active_update(float elapsed_time);
+ void collision_solid(const CollisionHit& hit);
+ HitResponse collision_badguy(BadGuy& badguy, const CollisionHit& hit);
+ void freeze();
+ void unfreeze();
+
+ float get_velocity_y() const;
+ void set_velocity_y(float vy);
+
+protected:
+ void turn_around();
+
+ std::string walk_left_action;
+ std::string walk_right_action;
+ float walk_speed;
+ int max_drop_height; /**< Maximum height of drop before we will turn around, or -1 to just drop from any ledge */
+ Timer turn_around_timer;
+ int turn_around_counter; /**< counts number of turns since turn_around_timer was started */
+};
+
+#endif
--- /dev/null
+// $Id$
+//
+// SuperTux - Walking Leaf
+// Copyright (C) 2006 Wolfgang Becker <uafr@gmx.de>
+//
+// 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 <config.h>
+
+#include "walkingleaf.hpp"
+#include "random_generator.hpp"
+#include "object/sprite_particle.hpp"
+
+WalkingLeaf::WalkingLeaf(const lisp::Lisp& reader)
+ : WalkingBadguy(reader, "images/creatures/walkingleaf/walkingleaf.sprite", "left", "right")
+{
+ walk_speed = 60;
+ max_drop_height = 16;
+}
+
+WalkingLeaf::WalkingLeaf(const Vector& pos, Direction d)
+ : WalkingBadguy(pos, d, "images/creatures/walkingleaf/walkingleaf.sprite", "left", "right")
+{
+ walk_speed = 60;
+ max_drop_height = 16;
+}
+
+bool
+WalkingLeaf::collision_squished(GameObject& object)
+{
+ sprite->set_action(dir == LEFT ? "squished-left" : "squished-right");
+ kill_squished(object);
+ return true;
+}
+
+IMPLEMENT_FACTORY(WalkingLeaf, "walkingleaf")
--- /dev/null
+// $Id$
+//
+// SuperTux - Walking Leaf
+// Copyright (C) 2006 Wolfgang Becker <uafr@gmx.de>
+//
+// 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 __WALKINGLEAF_H__
+#define __WALKINGLEAF_H__
+
+#include "walking_badguy.hpp"
+
+/*
+ * Easy to kill badguy that does not jump down from it's ledge.
+ */
+class WalkingLeaf : public WalkingBadguy
+{
+public:
+ WalkingLeaf(const lisp::Lisp& reader);
+ WalkingLeaf(const Vector& pos, Direction d);
+
+ virtual WalkingLeaf* clone() const { return new WalkingLeaf(*this); }
+
+protected:
+ bool collision_squished(GameObject& object);
+
+};
+
+#endif
--- /dev/null
+// $Id$
+//
+// SuperTux - "Will-O-Wisp" Badguy
+// Copyright (C) 2006 Christoph Sommer <christoph.sommer@2006.expires.deltadevelopment.de>
+//
+// 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 <config.h>
+
+#include "willowisp.hpp"
+#include "log.hpp"
+#include "game_session.hpp"
+#include "object/lantern.hpp"
+#include "object/player.hpp"
+#include "scripting/squirrel_util.hpp"
+
+static const float FLYSPEED = 64; /**< speed in px per second */
+static const float TRACK_RANGE = 384; /**< at what distance to start tracking the player */
+static const float VANISH_RANGE = 512; /**< at what distance to stop tracking and vanish */
+static const std::string SOUNDFILE = "sounds/willowisp.wav";
+
+WillOWisp::WillOWisp(const lisp::Lisp& reader)
+ : BadGuy(reader, "images/creatures/willowisp/willowisp.sprite", LAYER_FLOATINGOBJECTS), mystate(STATE_IDLE), target_sector("main"), target_spawnpoint("main")
+{
+ bool running = false;
+ flyspeed = FLYSPEED;
+ track_range = TRACK_RANGE;
+ vanish_range = VANISH_RANGE;
+
+ reader.get("sector", target_sector);
+ reader.get("spawnpoint", target_spawnpoint);
+ reader.get("name", name);
+ reader.get("flyspeed", flyspeed);
+ reader.get("track-range", track_range);
+ reader.get("vanish-range", vanish_range);
+ reader.get("hit-script", hit_script);
+ reader.get("running", running);
+
+ const lisp::Lisp* pathLisp = reader.get_lisp("path");
+ if(pathLisp != NULL) {
+ path.reset(new Path());
+ path->read(*pathLisp);
+ walker.reset(new PathWalker(path.get(), running));
+ if(running)
+ mystate = STATE_PATHMOVING_TRACK;
+ }
+
+ countMe = false;
+ sound_manager->preload(SOUNDFILE);
+}
+
+void
+WillOWisp::draw(DrawingContext& context)
+{
+ sprite->draw(context, get_pos(), layer);
+
+ context.push_target();
+ context.set_target(DrawingContext::LIGHTMAP);
+
+ sprite->draw(context, get_pos(), layer);
+
+ context.pop_target();
+}
+
+void
+WillOWisp::active_update(float elapsed_time)
+{
+ Player* player = get_nearest_player();
+ if (!player) return;
+ Vector p1 = this->get_pos() + (this->get_bbox().p2 - this->get_bbox().p1) / 2;
+ Vector p2 = player->get_pos() + (player->get_bbox().p2 - player->get_bbox().p1) / 2;
+ Vector dist = (p2 - p1);
+
+ switch(mystate) {
+ case STATE_STOPPED:
+ break;
+
+ case STATE_IDLE:
+ if (dist.norm() <= track_range) {
+ mystate = STATE_TRACKING;
+ }
+ break;
+
+ case STATE_TRACKING:
+ if (dist.norm() <= vanish_range) {
+ Vector dir = dist.unit();
+ movement = dir * elapsed_time * flyspeed;
+ } else {
+ vanish();
+ }
+ sound_source->set_position(get_pos());
+ break;
+
+ case STATE_WARPING:
+ if(sprite->animation_done()) {
+ remove_me();
+ }
+
+ case STATE_VANISHING: {
+ Vector dir = dist.unit();
+ movement = dir * elapsed_time * flyspeed;
+ if(sprite->animation_done()) {
+ remove_me();
+ }
+ break;
+ }
+
+ case STATE_PATHMOVING:
+ case STATE_PATHMOVING_TRACK:
+ if(walker.get() == NULL)
+ return;
+ movement = walker->advance(elapsed_time) - get_pos();
+ if(mystate == STATE_PATHMOVING_TRACK && dist.norm() <= track_range) {
+ mystate = STATE_TRACKING;
+ }
+ break;
+
+ default:
+ assert(false);
+ }
+}
+
+void
+WillOWisp::activate()
+{
+ sprite->set_action("idle");
+
+ sound_source.reset(sound_manager->create_sound_source(SOUNDFILE));
+ sound_source->set_position(get_pos());
+ sound_source->set_looping(true);
+ sound_source->set_gain(2.0);
+ sound_source->set_reference_distance(32);
+ sound_source->play();
+}
+
+void
+WillOWisp::deactivate()
+{
+ sound_source.reset(NULL);
+
+ switch (mystate) {
+ case STATE_STOPPED:
+ case STATE_IDLE:
+ case STATE_PATHMOVING:
+ case STATE_PATHMOVING_TRACK:
+ break;
+ case STATE_TRACKING:
+ mystate = STATE_IDLE;
+ break;
+ case STATE_WARPING:
+ case STATE_VANISHING:
+ remove_me();
+ break;
+ }
+}
+
+void
+WillOWisp::vanish()
+{
+ mystate = STATE_VANISHING;
+ sprite->set_action("vanishing", 1);
+ set_group(COLGROUP_DISABLED);
+}
+
+bool
+WillOWisp::collides(GameObject& other, const CollisionHit& ) {
+ Lantern* lantern = dynamic_cast<Lantern*>(&other);
+
+ if (lantern && lantern->is_open())
+ return true;
+
+ if (dynamic_cast<Player*>(&other))
+ return true;
+
+ return false;
+}
+
+HitResponse
+WillOWisp::collision_player(Player& player, const CollisionHit& ) {
+ if(player.is_invincible())
+ return ABORT_MOVE;
+
+ if (mystate != STATE_TRACKING)
+ return ABORT_MOVE;
+
+ mystate = STATE_WARPING;
+ sprite->set_action("warping", 1);
+
+ if(hit_script != "") {
+ std::istringstream stream(hit_script);
+ Sector::current()->run_script(stream, "hit-script");
+ } else {
+ GameSession::current()->respawn(target_sector, target_spawnpoint);
+ }
+ sound_manager->play("sounds/warp.wav");
+
+ return CONTINUE;
+}
+
+void
+WillOWisp::goto_node(int node_no)
+{
+ walker->goto_node(node_no);
+ if(mystate != STATE_PATHMOVING && mystate != STATE_PATHMOVING_TRACK) {
+ mystate = STATE_PATHMOVING;
+ }
+}
+
+void
+WillOWisp::start_moving()
+{
+ walker->start_moving();
+}
+
+void
+WillOWisp::stop_moving()
+{
+ walker->stop_moving();
+}
+
+void
+WillOWisp::set_state(const std::string& new_state)
+{
+ if(new_state == "stopped") {
+ mystate = STATE_STOPPED;
+ } else if(new_state == "idle") {
+ mystate = STATE_IDLE;
+ } else if(new_state == "move_path") {
+ mystate = STATE_PATHMOVING;
+ walker->start_moving();
+ } else if(new_state == "move_path_track") {
+ mystate = STATE_PATHMOVING_TRACK;
+ walker->start_moving();
+ } else if(new_state == "normal") {
+ mystate = STATE_IDLE;
+ } else if(new_state == "vanish") {
+ vanish();
+ } else {
+ std::ostringstream msg;
+ msg << "Can't set unknown willowisp state '" << new_state << "', should "
+ "be stopped, move_path, move_path_track or normal";
+ throw new std::runtime_error(msg.str());
+ }
+}
+
+void
+WillOWisp::expose(HSQUIRRELVM vm, SQInteger table_idx)
+{
+ if (name.empty())
+ return;
+
+ std::cout << "Expose me '" << name << "'\n";
+ Scripting::WillOWisp* interface = static_cast<Scripting::WillOWisp*> (this);
+ expose_object(vm, table_idx, interface, name);
+}
+
+void
+WillOWisp::unexpose(HSQUIRRELVM vm, SQInteger table_idx)
+{
+ if (name.empty())
+ return;
+
+ std::cout << "UnExpose me '" << name << "'\n";
+ Scripting::unexpose_object(vm, table_idx, name);
+}
+
+IMPLEMENT_FACTORY(WillOWisp, "willowisp")
--- /dev/null
+// $Id$
+//
+// SuperTux - "Will-O-Wisp" Badguy
+// Copyright (C) 2006 Christoph Sommer <christoph.sommer@2006.expires.deltadevelopment.de>
+//
+// 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 __WILLOWISP_H__
+#define __WILLOWISP_H__
+
+#include "badguy.hpp"
+#include "object/path.hpp"
+#include "object/path_walker.hpp"
+#include "script_interface.hpp"
+#include "scripting/willowisp.hpp"
+
+class WillOWisp : public BadGuy, public Scripting::WillOWisp,
+ public ScriptInterface
+{
+public:
+ WillOWisp(const lisp::Lisp& reader);
+
+ void activate();
+ void deactivate();
+
+ void active_update(float elapsed_time);
+ virtual bool is_flammable() const { return false; }
+ virtual bool is_freezable() const { return false; }
+ virtual void kill_fall() { vanish(); }
+
+ /**
+ * make WillOWisp vanish
+ */
+ void vanish();
+
+ virtual void draw(DrawingContext& context);
+
+ virtual void goto_node(int node_no);
+ virtual void set_state(const std::string& state);
+ virtual void start_moving();
+ virtual void stop_moving();
+
+ virtual void expose(HSQUIRRELVM vm, SQInteger table_idx);
+ virtual void unexpose(HSQUIRRELVM vm, SQInteger table_idx);
+
+protected:
+ virtual bool collides(GameObject& other, const CollisionHit& hit);
+ HitResponse collision_player(Player& player, const CollisionHit& hit);
+
+private:
+ enum MyState {
+ STATE_STOPPED, STATE_IDLE, STATE_TRACKING, STATE_VANISHING, STATE_WARPING,
+ STATE_PATHMOVING, STATE_PATHMOVING_TRACK
+ };
+ MyState mystate;
+
+ std::string target_sector;
+ std::string target_spawnpoint;
+ std::string hit_script;
+
+ std::auto_ptr<SoundSource> sound_source;
+
+ std::auto_ptr<Path> path;
+ std::auto_ptr<PathWalker> walker;
+
+ float flyspeed;
+ float track_range;
+ float vanish_range;
+};
+
+#endif
--- /dev/null
+// $Id$
+//
+// SuperTux - Boss "Yeti"
+// Copyright (C) 2005 Matthias Braun <matze@braunis.de>
+// Copyright (C) 2006 Christoph Sommer <christoph.sommer@2006.expires.deltadevelopment.de>
+//
+// 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 <config.h>
+
+#include <float.h>
+#include <sstream>
+#include <memory>
+#include "yeti.hpp"
+#include "object/camera.hpp"
+#include "yeti_stalactite.hpp"
+#include "bouncing_snowball.hpp"
+#include "game_session.hpp"
+#include "level.hpp"
+
+namespace {
+ const float JUMP_DOWN_VX = 250; /**< horizontal speed while jumping off the dais */
+ const float JUMP_DOWN_VY = -250; /**< vertical speed while jumping off the dais */
+
+ const float RUN_VX = 350; /**< horizontal speed while running */
+
+ const float JUMP_UP_VX = 350; /**< horizontal speed while jumping on the dais */
+ const float JUMP_UP_VY = -800; /**< vertical speed while jumping on the dais */
+
+ const float STOMP_VY = -250; /** vertical speed while stomping on the dais */
+
+ const float LEFT_STAND_X = 16; /**< x-coordinate of left dais' end position */
+ const float RIGHT_STAND_X = 800-60-16; /**< x-coordinate of right dais' end position */
+ const float LEFT_JUMP_X = LEFT_STAND_X+224; /**< x-coordinate of from where to jump on the left dais */
+ const float RIGHT_JUMP_X = RIGHT_STAND_X-224; /**< x-coordinate of from where to jump on the right dais */
+ const float STOMP_WAIT = .5; /**< time we stay on the dais before jumping again */
+ const float SAFE_TIME = .5; /**< the time we are safe when tux just hit us */
+ const int INITIAL_HITPOINTS = 3; /**< number of hits we can take */
+
+ const float SQUISH_TIME = 5;
+}
+
+Yeti::Yeti(const lisp::Lisp& reader)
+ : BadGuy(reader, "images/creatures/yeti/yeti.sprite")
+{
+ hit_points = INITIAL_HITPOINTS;
+ countMe = false;
+ sound_manager->preload("sounds/yeti_gna.wav");
+ sound_manager->preload("sounds/yeti_roar.wav");
+ hud_head.reset(new Surface("images/creatures/yeti/hudlife.png"));
+}
+
+Yeti::~Yeti()
+{
+}
+
+void
+Yeti::activate()
+{
+ dir = RIGHT;
+ jump_down();
+}
+
+void
+Yeti::draw(DrawingContext& context)
+{
+ // we blink when we are safe
+ if(safe_timer.started() && size_t(game_time*40)%2)
+ return;
+
+ draw_hit_points(context);
+
+ BadGuy::draw(context);
+}
+
+void
+Yeti::draw_hit_points(DrawingContext& context)
+{
+ int i;
+
+ Surface *hh = hud_head.get();
+ if (!hh)
+ return;
+
+ context.push_transform();
+ context.set_translation(Vector(0, 0));
+
+ for (i = 0; i < hit_points; ++i)
+ {
+ context.draw_surface(hh, Vector(BORDER_X + (i * hh->get_width()), BORDER_Y + 1), LAYER_FOREGROUND1);
+ }
+
+ context.pop_transform();
+}
+
+void
+Yeti::active_update(float elapsed_time)
+{
+ switch(state) {
+ case JUMP_DOWN:
+ physic.set_velocity_x((dir==RIGHT)?+JUMP_DOWN_VX:-JUMP_DOWN_VX);
+ break;
+ case RUN:
+ physic.set_velocity_x((dir==RIGHT)?+RUN_VX:-RUN_VX);
+ if (((dir == RIGHT) && (get_pos().x >= RIGHT_JUMP_X)) || ((dir == LEFT) && (get_pos().x <= LEFT_JUMP_X))) jump_up();
+ break;
+ case JUMP_UP:
+ physic.set_velocity_x((dir==RIGHT)?+JUMP_UP_VX:-JUMP_UP_VX);
+ if (((dir == RIGHT) && (get_pos().x >= RIGHT_STAND_X)) || ((dir == LEFT) && (get_pos().x <= LEFT_STAND_X))) be_angry();
+ break;
+ case BE_ANGRY:
+ if(state_timer.check()) {
+ sound_manager->play("sounds/yeti_gna.wav");
+ physic.set_velocity_y(STOMP_VY);
+ sprite->set_action((dir==RIGHT)?"stomp-right":"stomp-left");
+ }
+ break;
+ case SQUISHED:
+ if (state_timer.check()) {
+ remove_me();
+ }
+ break;
+ }
+
+ movement = physic.get_movement(elapsed_time);
+}
+
+void
+Yeti::jump_down()
+{
+ sprite->set_action((dir==RIGHT)?"jump-right":"jump-left");
+ physic.set_velocity_x((dir==RIGHT)?(+JUMP_DOWN_VX):(-JUMP_DOWN_VX));
+ physic.set_velocity_y(JUMP_DOWN_VY);
+ state = JUMP_DOWN;
+}
+
+void
+Yeti::run()
+{
+ sprite->set_action((dir==RIGHT)?"run-right":"run-left");
+ physic.set_velocity_x((dir==RIGHT)?(+RUN_VX):(-RUN_VX));
+ physic.set_velocity_y(0);
+ state = RUN;
+}
+
+void
+Yeti::jump_up()
+{
+ sprite->set_action((dir==RIGHT)?"jump-right":"jump-left");
+ physic.set_velocity_x((dir==RIGHT)?(+JUMP_UP_VX):(-JUMP_UP_VX));
+ physic.set_velocity_y(JUMP_UP_VY);
+ state = JUMP_UP;
+}
+
+void
+Yeti::be_angry()
+{
+ //turn around
+ dir = (dir==RIGHT)?LEFT:RIGHT;
+
+ sprite->set_action((dir==RIGHT)?"stand-right":"stand-left");
+ physic.set_velocity_x(0);
+ physic.set_velocity_y(0);
+ if (hit_points < INITIAL_HITPOINTS) summon_snowball();
+ stomp_count = 0;
+ state = BE_ANGRY;
+ state_timer.start(STOMP_WAIT);
+}
+
+void
+Yeti::summon_snowball()
+{
+ Sector::current()->add_object(new BouncingSnowball(Vector(get_pos().x+(dir == RIGHT ? 64 : -64), get_pos().y), dir));
+}
+
+bool
+Yeti::collision_squished(GameObject& object)
+{
+ kill_squished(object);
+
+ return true;
+}
+
+void
+Yeti::kill_squished(GameObject& object)
+{
+ Player* player = dynamic_cast<Player*>(&object);
+ if (player) {
+ player->bounce(*this);
+ take_hit(*player);
+ }
+}
+
+void Yeti::take_hit(Player& )
+{
+ if(safe_timer.started())
+ return;
+
+ sound_manager->play("sounds/yeti_roar.wav");
+ hit_points--;
+
+ if(hit_points <= 0) {
+ // We're dead
+ physic.enable_gravity(true);
+ physic.set_velocity_x(0);
+ physic.set_velocity_y(0);
+
+ state = SQUISHED;
+ state_timer.start(SQUISH_TIME);
+ set_group(COLGROUP_MOVING_ONLY_STATIC);
+ sprite->set_action("dead");
+
+ if (countMe) Sector::current()->get_level()->stats.badguys++;
+
+ if(dead_script != "") {
+ std::istringstream stream(dead_script);
+ Sector::current()->run_script(stream, "Yeti - dead-script");
+ }
+ }
+ else {
+ safe_timer.start(SAFE_TIME);
+ }
+}
+
+void
+Yeti::kill_fall()
+{
+ // shooting bullets or being invincible won't work :)
+ take_hit(*get_nearest_player()); // FIXME: debug only(?)
+}
+
+void
+Yeti::write(lisp::Writer& writer)
+{
+ writer.start_list("yeti");
+
+ writer.write_float("x", start_position.x);
+ writer.write_float("y", start_position.y);
+
+ writer.end_list("yeti");
+}
+
+void
+Yeti::drop_stalactite()
+{
+ // make a stalactite falling down and shake camera a bit
+ Sector::current()->camera->shake(.1f, 0, 10);
+
+ YetiStalactite* nearest = 0;
+ float dist = FLT_MAX;
+
+ Player* player = this->get_nearest_player();
+ if (!player) return;
+
+ Sector* sector = Sector::current();
+ for(Sector::GameObjects::iterator i = sector->gameobjects.begin();
+ i != sector->gameobjects.end(); ++i) {
+ YetiStalactite* stalactite = dynamic_cast<YetiStalactite*> (*i);
+ if(stalactite && stalactite->is_hanging()) {
+ float sdist
+ = fabsf(stalactite->get_pos().x - player->get_pos().x);
+ if(sdist < dist) {
+ nearest = stalactite;
+ dist = sdist;
+ }
+ }
+ }
+
+ if(nearest)
+ nearest->start_shaking();
+}
+
+void
+Yeti::collision_solid(const CollisionHit& hit)
+{
+ if(hit.top || hit.bottom) {
+ // hit floor or roof
+ physic.set_velocity_y(0);
+ switch (state) {
+ case JUMP_DOWN:
+ run();
+ break;
+ case RUN:
+ break;
+ case JUMP_UP:
+ break;
+ case BE_ANGRY:
+ // we just landed
+ if(!state_timer.started()) {
+ sprite->set_action((dir==RIGHT)?"stand-right":"stand-left");
+ stomp_count++;
+ drop_stalactite();
+
+ // go to other side after 3 jumps
+ if(stomp_count == 3) {
+ jump_down();
+ } else {
+ // jump again
+ state_timer.start(STOMP_WAIT);
+ }
+ }
+ break;
+ case SQUISHED:
+ break;
+ }
+ } else if(hit.left || hit.right) {
+ // hit wall
+ jump_up();
+ }
+}
+
+IMPLEMENT_FACTORY(Yeti, "yeti")
--- /dev/null
+// $Id$
+//
+// SuperTux - Boss "Yeti"
+// Copyright (C) 2005 Matthias Braun <matze@braunis.de>
+// Copyright (C) 2006 Christoph Sommer <christoph.sommer@2006.expires.deltadevelopment.de>
+//
+// 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 __YETI_H__
+#define __YETI_H__
+
+#include <memory>
+
+#include "badguy.hpp"
+
+class Yeti : public BadGuy
+{
+public:
+ Yeti(const lisp::Lisp& lisp);
+ ~Yeti();
+
+ void draw(DrawingContext& context);
+ void write(lisp::Writer& writer);
+ void activate();
+ void active_update(float elapsed_time);
+ void collision_solid(const CollisionHit& hit);
+ bool collision_squished(GameObject& object);
+ void kill_squished(GameObject& object);
+ void kill_fall();
+
+ virtual Yeti* clone() const { return new Yeti((Yeti&)*this); }
+
+private:
+ void run();
+ void jump_up();
+ void be_angry();
+ void drop_stalactite();
+ void summon_snowball();
+ void jump_down();
+
+ void draw_hit_points(DrawingContext& context);
+
+ void take_hit(Player& player);
+
+ enum YetiState {
+ JUMP_DOWN,
+ RUN,
+ JUMP_UP,
+ BE_ANGRY,
+ SQUISHED
+ };
+ YetiState state;
+ Timer state_timer;
+ Timer safe_timer;
+ int stomp_count;
+ int hit_points;
+ std::auto_ptr<Surface> hud_head;
+};
+
+#endif
--- /dev/null
+// $Id$
+//
+// SuperTux
+// Copyright (C) 2006 Matthias Braun <matze@braunis.de>
+//
+// 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 "yeti_stalactite.hpp"
+
+static const float SHAKE_TIME = .8f;
+
+YetiStalactite::YetiStalactite(const lisp::Lisp& lisp)
+ : Stalactite(lisp)
+{
+}
+
+YetiStalactite::~YetiStalactite()
+{
+}
+
+void
+YetiStalactite::write(lisp::Writer& writer)
+{
+ writer.start_list("yeti_stalactite");
+ writer.write_float("x", start_position.x);
+ writer.write_float("y", start_position.y);
+ writer.end_list("yeti_stalactite");
+}
+
+void
+YetiStalactite::start_shaking()
+{
+ timer.start(SHAKE_TIME);
+ state = STALACTITE_SHAKING;
+}
+
+bool
+YetiStalactite::is_hanging()
+{
+ return state == STALACTITE_HANGING;
+}
+
+void
+YetiStalactite::active_update(float elapsed_time)
+{
+ if(state == STALACTITE_HANGING)
+ return;
+
+ Stalactite::active_update(elapsed_time);
+}
+
+IMPLEMENT_FACTORY(YetiStalactite, "yeti_stalactite")
--- /dev/null
+// $Id$
+//
+// SuperTux
+// Copyright (C) 2006 Matthias Braun <matze@braunis.de>
+//
+// 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 __YETI_STALACTITE_H__
+#define __YETI_STALACTITE_H__
+
+#include "stalactite.hpp"
+
+class YetiStalactite : public Stalactite
+{
+public:
+ YetiStalactite(const lisp::Lisp& lisp);
+ virtual ~YetiStalactite();
+
+ void write(lisp::Writer& );
+ void active_update(float elapsed_time);
+ void start_shaking();
+ bool is_hanging();
+
+ virtual YetiStalactite* clone() const { return new YetiStalactite(*this); }
+};
+
+#endif
--- /dev/null
+// $Id$
+//
+// Zeekling - flyer that swoops down when she spots the player
+// Copyright (C) 2005 Matthias Braun <matze@braunis.de>
+// Copyright (C) 2006 Christoph Sommer <supertux@2006.expires.deltadevelopment.de>
+//
+// 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 <config.h>
+#include <math.h>
+
+#include "zeekling.hpp"
+#include "random_generator.hpp"
+
+Zeekling::Zeekling(const lisp::Lisp& reader)
+ : BadGuy(reader, "images/creatures/zeekling/zeekling.sprite"), last_player(0)
+{
+ state = FLYING;
+}
+
+Zeekling::Zeekling(const Vector& pos, Direction d)
+ : BadGuy(pos, d, "images/creatures/zeekling/zeekling.sprite"), last_player(0)
+{
+ state = FLYING;
+}
+
+void
+Zeekling::write(lisp::Writer& writer)
+{
+ writer.start_list("zeekling");
+
+ writer.write_float("x", start_position.x);
+ writer.write_float("y", start_position.y);
+
+ writer.end_list("zeekling");
+}
+
+void
+Zeekling::activate()
+{
+ speed = systemRandom.rand(130, 171);
+ physic.set_velocity_x(dir == LEFT ? -speed : speed);
+ physic.enable_gravity(false);
+ sprite->set_action(dir == LEFT ? "left" : "right");
+}
+
+bool
+Zeekling::collision_squished(GameObject& object)
+{
+ sprite->set_action(dir == LEFT ? "squished-left" : "squished-right");
+ kill_squished(object);
+ kill_fall();
+ return true;
+}
+
+void
+Zeekling::onBumpHorizontal() {
+ if (state == FLYING) {
+ dir = (dir == LEFT ? RIGHT : LEFT);
+ sprite->set_action(dir == LEFT ? "left" : "right");
+ physic.set_velocity_x(dir == LEFT ? -speed : speed);
+ } else
+ if (state == DIVING) {
+ dir = (dir == LEFT ? RIGHT : LEFT);
+ state = FLYING;
+ sprite->set_action(dir == LEFT ? "left" : "right");
+ physic.set_velocity_x(dir == LEFT ? -speed : speed);
+ physic.set_velocity_y(0);
+ } else
+ if (state == CLIMBING) {
+ dir = (dir == LEFT ? RIGHT : LEFT);
+ sprite->set_action(dir == LEFT ? "left" : "right");
+ physic.set_velocity_x(dir == LEFT ? -speed : speed);
+ } else {
+ assert(false);
+ }
+}
+
+void
+Zeekling::onBumpVertical() {
+ if (state == FLYING) {
+ physic.set_velocity_y(0);
+ } else
+ if (state == DIVING) {
+ state = CLIMBING;
+ physic.set_velocity_y(-speed);
+ sprite->set_action(dir == LEFT ? "left" : "right");
+ } else
+ if (state == CLIMBING) {
+ state = FLYING;
+ physic.set_velocity_y(0);
+ }
+}
+
+void
+Zeekling::collision_solid(const CollisionHit& hit)
+{
+ if(hit.top || hit.bottom) {
+ onBumpVertical();
+ } else if(hit.left || hit.right) {
+ onBumpHorizontal();
+ }
+}
+
+/**
+ * linear prediction of player and badguy positions to decide if we should enter the DIVING state
+ */
+bool
+Zeekling::should_we_dive() {
+
+ const MovingObject* player = this->get_nearest_player();
+ if (player && last_player && (player == last_player)) {
+
+ // get positions, calculate movement
+ const Vector player_pos = player->get_pos();
+ const Vector player_mov = (player_pos - last_player_pos);
+ const Vector self_pos = this->get_pos();
+ const Vector self_mov = (self_pos - last_self_pos);
+
+ // new vertical speed to test with
+ float vy = 2*fabsf(self_mov.x);
+
+ // do not dive if we are not above the player
+ float height = player_pos.y - self_pos.y;
+ if (height <= 0) return false;
+
+ // do not dive if we are too far above the player
+ if (height > 512) return false;
+
+ // do not dive if we would not descend faster than the player
+ float relSpeed = vy - player_mov.y;
+ if (relSpeed <= 0) return false;
+
+ // guess number of frames to descend to same height as player
+ float estFrames = height / relSpeed;
+
+ // guess where the player would be at this time
+ float estPx = (player_pos.x + (estFrames * player_mov.x));
+
+ // guess where we would be at this time
+ float estBx = (self_pos.x + (estFrames * self_mov.x));
+
+ // near misses are OK, too
+ if (fabsf(estPx - estBx) < 8) return true;
+ }
+
+ // update last player tracked, as well as our positions
+ last_player = player;
+ if (player) {
+ last_player_pos = player->get_pos();
+ last_self_pos = this->get_pos();
+ }
+
+ return false;
+}
+
+void
+Zeekling::active_update(float elapsed_time) {
+ if (state == FLYING) {
+ if (should_we_dive()) {
+ state = DIVING;
+ physic.set_velocity_y(2*fabsf(physic.get_velocity_x()));
+ sprite->set_action(dir == LEFT ? "diving-left" : "diving-right");
+ }
+ BadGuy::active_update(elapsed_time);
+ return;
+ } else if (state == DIVING) {
+ BadGuy::active_update(elapsed_time);
+ return;
+ } else if (state == CLIMBING) {
+ // stop climbing when we're back at initial height
+ if (get_pos().y <= start_position.y) {
+ state = FLYING;
+ physic.set_velocity_y(0);
+ }
+ BadGuy::active_update(elapsed_time);
+ return;
+ } else {
+ assert(false);
+ }
+}
+
+IMPLEMENT_FACTORY(Zeekling, "zeekling")
--- /dev/null
+// $Id$
+//
+// Zeekling - flyer that swoops down when she spots the player
+// Copyright (C) 2005 Matthias Braun <matze@braunis.de>
+// Copyright (C) 2006 Christoph Sommer <supertux@2006.expires.deltadevelopment.de>
+//
+// 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 __ZEEKLING_H__
+#define __ZEEKLING_H__
+
+#include "badguy.hpp"
+
+class Zeekling : public BadGuy
+{
+public:
+ Zeekling(const lisp::Lisp& reader);
+ Zeekling(const Vector& pos, Direction d);
+
+ void activate();
+ void write(lisp::Writer& writer);
+ void collision_solid(const CollisionHit& hit);
+ void active_update(float elapsed_time);
+
+ virtual Zeekling* clone() const { return new Zeekling(*this); }
+
+protected:
+ bool collision_squished(GameObject& object);
+ float speed;
+
+ Timer diveRecoverTimer;
+
+ enum ZeeklingState {
+ FLYING,
+ DIVING,
+ CLIMBING
+ };
+ ZeeklingState state;
+
+private:
+ const MovingObject* last_player; /**< last player we tracked */
+ Vector last_player_pos; /**< position we last spotted the player at */
+ Vector last_self_pos; /**< position we last were at */
+
+ bool should_we_dive();
+ void onBumpHorizontal();
+ void onBumpVertical();
+};
+
+#endif
--- /dev/null
+/*
+ * BinReloc - a library for creating relocatable executables
+ * Written by: Hongli Lai <h.lai@chello.nl>
+ * http://autopackage.org/
+ *
+ * This source code is public domain. You can relicense this code
+ * under whatever license you want.
+ *
+ * See http://autopackage.org/docs/binreloc/ for
+ * more information and how to use this.
+ */
+
+#ifndef __BINRELOC_C__
+#define __BINRELOC_C__
+
+// [Christoph] use config.h, which defines ENABLE_BINRELOC
+#include <config.h>
+
+#ifdef ENABLE_BINRELOC
+ #include <sys/types.h>
+ #include <sys/stat.h>
+ #include <unistd.h>
+#endif /* ENABLE_BINRELOC */
+#include <stdio.h>
+#include <stdlib.h>
+#include <limits.h>
+#include <string.h>
+#include "binreloc.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif /* __cplusplus */
+
+
+
+/** @internal
+ * Find the canonical filename of the executable. Returns the filename
+ * (which must be freed) or NULL on error. If the parameter 'error' is
+ * not NULL, the error code will be stored there, if an error occured.
+ */
+static char *
+_br_find_exe (BrInitError *error)
+{
+#ifndef ENABLE_BINRELOC
+ if (error)
+ *error = BR_INIT_ERROR_DISABLED;
+ return NULL;
+#else
+ char *path, *path2, *line, *result;
+ size_t buf_size;
+ ssize_t size;
+ struct stat stat_buf;
+ FILE *f;
+
+ /* Read from /proc/self/exe (symlink) */
+ if (sizeof (path) > SSIZE_MAX)
+ buf_size = SSIZE_MAX - 1;
+ else
+ buf_size = PATH_MAX - 1;
+ path = (char *) malloc (buf_size);
+ if (path == NULL) {
+ /* Cannot allocate memory. */
+ if (error)
+ *error = BR_INIT_ERROR_NOMEM;
+ return NULL;
+ }
+ path2 = (char *) malloc (buf_size);
+ if (path2 == NULL) {
+ /* Cannot allocate memory. */
+ if (error)
+ *error = BR_INIT_ERROR_NOMEM;
+ free (path);
+ return NULL;
+ }
+
+ strncpy (path2, "/proc/self/exe", buf_size - 1);
+
+ while (1) {
+ int i;
+
+ size = readlink (path2, path, buf_size - 1);
+ if (size == -1) {
+ /* Error. */
+ free (path2);
+ break;
+ }
+
+ /* readlink() success. */
+ path[size] = '\0';
+
+ /* Check whether the symlink's target is also a symlink.
+ * We want to get the final target. */
+ i = stat (path, &stat_buf);
+ if (i == -1) {
+ /* Error. */
+ free (path2);
+ break;
+ }
+
+ /* stat() success. */
+ if (!S_ISLNK (stat_buf.st_mode)) {
+ /* path is not a symlink. Done. */
+ free (path2);
+ return path;
+ }
+
+ /* path is a symlink. Continue loop and resolve this. */
+ strncpy (path, path2, buf_size - 1);
+ }
+
+
+ /* readlink() or stat() failed; this can happen when the program is
+ * running in Valgrind 2.2. Read from /proc/self/maps as fallback. */
+
+ buf_size = PATH_MAX + 128;
+ line = (char *) realloc (path, buf_size);
+ if (line == NULL) {
+ /* Cannot allocate memory. */
+ free (path);
+ if (error)
+ *error = BR_INIT_ERROR_NOMEM;
+ return NULL;
+ }
+
+ f = fopen ("/proc/self/maps", "r");
+ if (f == NULL) {
+ free (line);
+ if (error)
+ *error = BR_INIT_ERROR_OPEN_MAPS;
+ return NULL;
+ }
+
+ /* The first entry should be the executable name. */
+ result = fgets (line, (int) buf_size, f);
+ if (result == NULL) {
+ fclose (f);
+ free (line);
+ if (error)
+ *error = BR_INIT_ERROR_READ_MAPS;
+ return NULL;
+ }
+
+ /* Get rid of newline character. */
+ buf_size = strlen (line);
+ if (buf_size <= 0) {
+ /* Huh? An empty string? */
+ fclose (f);
+ free (line);
+ if (error)
+ *error = BR_INIT_ERROR_INVALID_MAPS;
+ return NULL;
+ }
+ if (line[buf_size - 1] == 10)
+ line[buf_size - 1] = 0;
+
+ /* Extract the filename; it is always an absolute path. */
+ path = strchr (line, '/');
+
+ /* Sanity check. */
+ if (strstr (line, " r-xp ") == NULL || path == NULL) {
+ fclose (f);
+ free (line);
+ if (error)
+ *error = BR_INIT_ERROR_INVALID_MAPS;
+ return NULL;
+ }
+
+ path = strdup (path);
+ free (line);
+ fclose (f);
+ return path;
+#endif /* ENABLE_BINRELOC */
+}
+
+
+/** @internal
+ * Find the canonical filename of the executable which owns symbol.
+ * Returns a filename which must be freed, or NULL on error.
+ */
+static char *
+_br_find_exe_for_symbol (const void *symbol, BrInitError *error)
+{
+ symbol = symbol; // [Christoph] mark it as used
+#ifndef ENABLE_BINRELOC
+ if (error)
+ *error = BR_INIT_ERROR_DISABLED;
+ return (char *) NULL;
+#else
+ #define SIZE PATH_MAX + 100
+ FILE *f;
+ size_t address_string_len;
+ char *address_string, line[SIZE], *found;
+
+ if (symbol == NULL)
+ return (char *) NULL;
+
+ f = fopen ("/proc/self/maps", "r");
+ if (f == NULL)
+ return (char *) NULL;
+
+ address_string_len = 4;
+ address_string = (char *) malloc (address_string_len);
+ found = (char *) NULL;
+
+ while (!feof (f)) {
+ char *start_addr, *end_addr, *end_addr_end, *file;
+ void *start_addr_p, *end_addr_p;
+ size_t len;
+
+ if (fgets (line, SIZE, f) == NULL)
+ break;
+
+ /* Sanity check. */
+ if (strstr (line, " r-xp ") == NULL || strchr (line, '/') == NULL)
+ continue;
+
+ /* Parse line. */
+ start_addr = line;
+ end_addr = strchr (line, '-');
+ file = strchr (line, '/');
+
+ /* More sanity check. */
+ if (!(file > end_addr && end_addr != NULL && end_addr[0] == '-'))
+ continue;
+
+ end_addr[0] = '\0';
+ end_addr++;
+ end_addr_end = strchr (end_addr, ' ');
+ if (end_addr_end == NULL)
+ continue;
+
+ end_addr_end[0] = '\0';
+ len = strlen (file);
+ if (len == 0)
+ continue;
+ if (file[len - 1] == '\n')
+ file[len - 1] = '\0';
+
+ /* Get rid of "(deleted)" from the filename. */
+ len = strlen (file);
+ if (len > 10 && strcmp (file + len - 10, " (deleted)") == 0)
+ file[len - 10] = '\0';
+
+ /* I don't know whether this can happen but better safe than sorry. */
+ len = strlen (start_addr);
+ if (len != strlen (end_addr))
+ continue;
+
+
+ /* Transform the addresses into a string in the form of 0xdeadbeef,
+ * then transform that into a pointer. */
+ if (address_string_len < len + 3) {
+ address_string_len = len + 3;
+ address_string = (char *) realloc (address_string, address_string_len);
+ }
+
+ memcpy (address_string, "0x", 2);
+ memcpy (address_string + 2, start_addr, len);
+ address_string[2 + len] = '\0';
+ sscanf (address_string, "%p", &start_addr_p);
+
+ memcpy (address_string, "0x", 2);
+ memcpy (address_string + 2, end_addr, len);
+ address_string[2 + len] = '\0';
+ sscanf (address_string, "%p", &end_addr_p);
+
+
+ if (symbol >= start_addr_p && symbol < end_addr_p) {
+ found = file;
+ break;
+ }
+ }
+
+ free (address_string);
+ fclose (f);
+
+ if (found == NULL)
+ return (char *) NULL;
+ else
+ return strdup (found);
+#endif /* ENABLE_BINRELOC */
+}
+
+
+#ifndef BINRELOC_RUNNING_DOXYGEN
+ #undef NULL
+ #define NULL ((void *) 0) /* typecasted as char* for C++ type safeness */
+#endif
+
+static char *exe = (char *) NULL;
+
+
+/** Initialize the BinReloc library (for applications).
+ *
+ * This function must be called before using any other BinReloc functions.
+ * It attempts to locate the application's canonical filename.
+ *
+ * @note If you want to use BinReloc for a library, then you should call
+ * br_init_lib() instead.
+ *
+ * @param error If BinReloc failed to initialize, then the error code will
+ * be stored in this variable. Set to NULL if you want to
+ * ignore this. See #BrInitError for a list of error codes.
+ *
+ * @returns 1 on success, 0 if BinReloc failed to initialize.
+ */
+int
+br_init (BrInitError *error)
+{
+ exe = _br_find_exe (error);
+ return exe != NULL;
+}
+
+
+/** Initialize the BinReloc library (for libraries).
+ *
+ * This function must be called before using any other BinReloc functions.
+ * It attempts to locate the calling library's canonical filename.
+ *
+ * @note The BinReloc source code MUST be included in your library, or this
+ * function won't work correctly.
+ *
+ * @param error If BinReloc failed to initialize, then the error code will
+ * be stored in this variable. Set to NULL if you want to
+ * ignore this. See #BrInitError for a list of error codes.
+ *
+ * @returns 1 on success, 0 if a filename cannot be found.
+ */
+int
+br_init_lib (BrInitError *error)
+{
+ exe = _br_find_exe_for_symbol ((const void *) "", error);
+ return exe != NULL;
+}
+
+
+/** Find the canonical filename of the current application.
+ *
+ * @param default_exe A default filename which will be used as fallback.
+ * @returns A string containing the application's canonical filename,
+ * which must be freed when no longer necessary. If BinReloc is
+ * not initialized, or if br_init() failed, then a copy of
+ * default_exe will be returned. If default_exe is NULL, then
+ * NULL will be returned.
+ */
+char *
+br_find_exe (const char *default_exe)
+{
+ if (exe == (char *) NULL) {
+ /* BinReloc is not initialized. */
+ if (default_exe != (const char *) NULL)
+ return strdup (default_exe);
+ else
+ return (char *) NULL;
+ }
+ return strdup (exe);
+}
+
+
+/** Locate the directory in which the current application is installed.
+ *
+ * The prefix is generated by the following pseudo-code evaluation:
+ * \code
+ * dirname(exename)
+ * \endcode
+ *
+ * @param default_dir A default directory which will used as fallback.
+ * @return A string containing the directory, which must be freed when no
+ * longer necessary. If BinReloc is not initialized, or if the
+ * initialization function failed, then a copy of default_dir
+ * will be returned. If default_dir is NULL, then NULL will be
+ * returned.
+ */
+char *
+br_find_exe_dir (const char *default_dir)
+{
+ if (exe == NULL) {
+ /* BinReloc not initialized. */
+ if (default_dir != NULL)
+ return strdup (default_dir);
+ else
+ return NULL;
+ }
+
+ return br_dirname (exe);
+}
+
+
+/** Locate the prefix in which the current application is installed.
+ *
+ * The prefix is generated by the following pseudo-code evaluation:
+ * \code
+ * dirname(dirname(exename))
+ * \endcode
+ *
+ * @param default_prefix A default prefix which will used as fallback.
+ * @return A string containing the prefix, which must be freed when no
+ * longer necessary. If BinReloc is not initialized, or if
+ * the initialization function failed, then a copy of default_prefix
+ * will be returned. If default_prefix is NULL, then NULL will be returned.
+ */
+char *
+br_find_prefix (const char *default_prefix)
+{
+ char *dir1, *dir2;
+
+ if (exe == (char *) NULL) {
+ /* BinReloc not initialized. */
+ if (default_prefix != (const char *) NULL)
+ return strdup (default_prefix);
+ else
+ return (char *) NULL;
+ }
+
+ dir1 = br_dirname (exe);
+ dir2 = br_dirname (dir1);
+ free (dir1);
+ return dir2;
+}
+
+
+/** Locate the application's binary folder.
+ *
+ * The path is generated by the following pseudo-code evaluation:
+ * \code
+ * prefix + "/bin"
+ * \endcode
+ *
+ * @param default_bin_dir A default path which will used as fallback.
+ * @return A string containing the bin folder's path, which must be freed when
+ * no longer necessary. If BinReloc is not initialized, or if
+ * the initialization function failed, then a copy of default_bin_dir will
+ * be returned. If default_bin_dir is NULL, then NULL will be returned.
+ */
+char *
+br_find_bin_dir (const char *default_bin_dir)
+{
+ char *prefix, *dir;
+
+ prefix = br_find_prefix ((const char *) NULL);
+ if (prefix == (char *) NULL) {
+ /* BinReloc not initialized. */
+ if (default_bin_dir != (const char *) NULL)
+ return strdup (default_bin_dir);
+ else
+ return (char *) NULL;
+ }
+
+ dir = br_build_path (prefix, "bin");
+ free (prefix);
+ return dir;
+}
+
+
+/** Locate the application's superuser binary folder.
+ *
+ * The path is generated by the following pseudo-code evaluation:
+ * \code
+ * prefix + "/sbin"
+ * \endcode
+ *
+ * @param default_sbin_dir A default path which will used as fallback.
+ * @return A string containing the sbin folder's path, which must be freed when
+ * no longer necessary. If BinReloc is not initialized, or if the
+ * initialization function failed, then a copy of default_sbin_dir will
+ * be returned. If default_bin_dir is NULL, then NULL will be returned.
+ */
+char *
+br_find_sbin_dir (const char *default_sbin_dir)
+{
+ char *prefix, *dir;
+
+ prefix = br_find_prefix ((const char *) NULL);
+ if (prefix == (char *) NULL) {
+ /* BinReloc not initialized. */
+ if (default_sbin_dir != (const char *) NULL)
+ return strdup (default_sbin_dir);
+ else
+ return (char *) NULL;
+ }
+
+ dir = br_build_path (prefix, "sbin");
+ free (prefix);
+ return dir;
+}
+
+
+/** Locate the application's data folder.
+ *
+ * The path is generated by the following pseudo-code evaluation:
+ * \code
+ * prefix + "/share"
+ * \endcode
+ *
+ * @param default_data_dir A default path which will used as fallback.
+ * @return A string containing the data folder's path, which must be freed when
+ * no longer necessary. If BinReloc is not initialized, or if the
+ * initialization function failed, then a copy of default_data_dir
+ * will be returned. If default_data_dir is NULL, then NULL will be
+ * returned.
+ */
+char *
+br_find_data_dir (const char *default_data_dir)
+{
+ char *prefix, *dir;
+
+ prefix = br_find_prefix ((const char *) NULL);
+ if (prefix == (char *) NULL) {
+ /* BinReloc not initialized. */
+ if (default_data_dir != (const char *) NULL)
+ return strdup (default_data_dir);
+ else
+ return (char *) NULL;
+ }
+
+ dir = br_build_path (prefix, "share");
+ free (prefix);
+ return dir;
+}
+
+
+/** Locate the application's localization folder.
+ *
+ * The path is generated by the following pseudo-code evaluation:
+ * \code
+ * prefix + "/share/locale"
+ * \endcode
+ *
+ * @param default_locale_dir A default path which will used as fallback.
+ * @return A string containing the localization folder's path, which must be freed when
+ * no longer necessary. If BinReloc is not initialized, or if the
+ * initialization function failed, then a copy of default_locale_dir will be returned.
+ * If default_locale_dir is NULL, then NULL will be returned.
+ */
+char *
+br_find_locale_dir (const char *default_locale_dir)
+{
+ char *data_dir, *dir;
+
+ data_dir = br_find_data_dir ((const char *) NULL);
+ if (data_dir == (char *) NULL) {
+ /* BinReloc not initialized. */
+ if (default_locale_dir != (const char *) NULL)
+ return strdup (default_locale_dir);
+ else
+ return (char *) NULL;
+ }
+
+ dir = br_build_path (data_dir, "locale");
+ free (data_dir);
+ return dir;
+}
+
+
+/** Locate the application's library folder.
+ *
+ * The path is generated by the following pseudo-code evaluation:
+ * \code
+ * prefix + "/lib"
+ * \endcode
+ *
+ * @param default_lib_dir A default path which will used as fallback.
+ * @return A string containing the library folder's path, which must be freed when
+ * no longer necessary. If BinReloc is not initialized, or if the initialization
+ * function failed, then a copy of default_lib_dir will be returned.
+ * If default_lib_dir is NULL, then NULL will be returned.
+ */
+char *
+br_find_lib_dir (const char *default_lib_dir)
+{
+ char *prefix, *dir;
+
+ prefix = br_find_prefix ((const char *) NULL);
+ if (prefix == (char *) NULL) {
+ /* BinReloc not initialized. */
+ if (default_lib_dir != (const char *) NULL)
+ return strdup (default_lib_dir);
+ else
+ return (char *) NULL;
+ }
+
+ dir = br_build_path (prefix, "lib");
+ free (prefix);
+ return dir;
+}
+
+
+/** Locate the application's libexec folder.
+ *
+ * The path is generated by the following pseudo-code evaluation:
+ * \code
+ * prefix + "/libexec"
+ * \endcode
+ *
+ * @param default_libexec_dir A default path which will used as fallback.
+ * @return A string containing the libexec folder's path, which must be freed when
+ * no longer necessary. If BinReloc is not initialized, or if the initialization
+ * function failed, then a copy of default_libexec_dir will be returned.
+ * If default_libexec_dir is NULL, then NULL will be returned.
+ */
+char *
+br_find_libexec_dir (const char *default_libexec_dir)
+{
+ char *prefix, *dir;
+
+ prefix = br_find_prefix ((const char *) NULL);
+ if (prefix == (char *) NULL) {
+ /* BinReloc not initialized. */
+ if (default_libexec_dir != (const char *) NULL)
+ return strdup (default_libexec_dir);
+ else
+ return (char *) NULL;
+ }
+
+ dir = br_build_path (prefix, "libexec");
+ free (prefix);
+ return dir;
+}
+
+
+/** Locate the application's configuration files folder.
+ *
+ * The path is generated by the following pseudo-code evaluation:
+ * \code
+ * prefix + "/etc"
+ * \endcode
+ *
+ * @param default_etc_dir A default path which will used as fallback.
+ * @return A string containing the etc folder's path, which must be freed when
+ * no longer necessary. If BinReloc is not initialized, or if the initialization
+ * function failed, then a copy of default_etc_dir will be returned.
+ * If default_etc_dir is NULL, then NULL will be returned.
+ */
+char *
+br_find_etc_dir (const char *default_etc_dir)
+{
+ char *prefix, *dir;
+
+ prefix = br_find_prefix ((const char *) NULL);
+ if (prefix == (char *) NULL) {
+ /* BinReloc not initialized. */
+ if (default_etc_dir != (const char *) NULL)
+ return strdup (default_etc_dir);
+ else
+ return (char *) NULL;
+ }
+
+ dir = br_build_path (prefix, "etc");
+ free (prefix);
+ return dir;
+}
+
+
+/***********************
+ * Utility functions
+ ***********************/
+
+/** Concatenate str1 and str2 to a newly allocated string.
+ *
+ * @param str1 A string.
+ * @param str2 Another string.
+ * @returns A newly-allocated string. This string should be freed when no longer needed.
+ */
+char *
+br_strcat (const char *str1, const char *str2)
+{
+ char *result;
+ size_t len1, len2;
+
+ if (str1 == NULL)
+ str1 = "";
+ if (str2 == NULL)
+ str2 = "";
+
+ len1 = strlen (str1);
+ len2 = strlen (str2);
+
+ result = (char *) malloc (len1 + len2 + 1);
+ memcpy (result, str1, len1);
+ memcpy (result + len1, str2, len2);
+ result[len1 + len2] = '\0';
+
+ return result;
+}
+
+
+char *
+br_build_path (const char *dir, const char *file)
+{
+ char *dir2, *result;
+ size_t len;
+ int must_free = 0;
+
+ len = strlen (dir);
+ if (len > 0 && dir[len - 1] != '/') {
+ dir2 = br_strcat (dir, "/");
+ must_free = 1;
+ } else
+ dir2 = (char *) dir;
+
+ result = br_strcat (dir2, file);
+ if (must_free)
+ free (dir2);
+ return result;
+}
+
+
+/* Emulates glibc's strndup() */
+static char *
+br_strndup (const char *str, size_t size)
+{
+ char *result = (char *) NULL;
+ size_t len;
+
+ if (str == (const char *) NULL)
+ return (char *) NULL;
+
+ len = strlen (str);
+ if (len == 0)
+ return strdup ("");
+ if (size > len)
+ size = len;
+
+ result = (char *) malloc (len + 1);
+ memcpy (result, str, size);
+ result[size] = '\0';
+ return result;
+}
+
+
+/** Extracts the directory component of a path.
+ *
+ * Similar to g_dirname() or the dirname commandline application.
+ *
+ * Example:
+ * \code
+ * br_dirname ("/usr/local/foobar"); --> Returns: "/usr/local"
+ * \endcode
+ *
+ * @param path A path.
+ * @returns A directory name. This string should be freed when no longer needed.
+ */
+char *
+br_dirname (const char *path)
+{
+ char *end, *result;
+
+ if (path == (const char *) NULL)
+ return (char *) NULL;
+
+ end = strrchr (path, '/');
+ if (end == (const char *) NULL)
+ return strdup (".");
+
+ while (end > path && *end == '/')
+ end--;
+ result = br_strndup (path, end - path + 1);
+ if (result[0] == 0) {
+ free (result);
+ return strdup ("/");
+ } else
+ return result;
+}
+
+
+#ifdef __cplusplus
+}
+#endif /* __cplusplus */
+
+#endif /* __BINRELOC_C__ */
--- /dev/null
+/*
+ * BinReloc - a library for creating relocatable executables
+ * Written by: Hongli Lai <h.lai@chello.nl>
+ * http://autopackage.org/
+ *
+ * This source code is public domain. You can relicense this code
+ * under whatever license you want.
+ *
+ * See http://autopackage.org/docs/binreloc/ for
+ * more information and how to use this.
+ */
+
+#ifndef __BINRELOC_H__
+#define __BINRELOC_H__
+
+#ifdef __cplusplus
+extern "C" {
+#endif /* __cplusplus */
+
+
+/** These error codes can be returned by br_init(), br_init_lib(), gbr_init() or gbr_init_lib(). */
+typedef enum {
+ /** Cannot allocate memory. */
+ BR_INIT_ERROR_NOMEM,
+ /** Unable to open /proc/self/maps; see errno for details. */
+ BR_INIT_ERROR_OPEN_MAPS,
+ /** Unable to read from /proc/self/maps; see errno for details. */
+ BR_INIT_ERROR_READ_MAPS,
+ /** The file format of /proc/self/maps is invalid; kernel bug? */
+ BR_INIT_ERROR_INVALID_MAPS,
+ /** BinReloc is disabled (the ENABLE_BINRELOC macro is not defined). */
+ BR_INIT_ERROR_DISABLED
+} BrInitError;
+
+
+#ifndef BINRELOC_RUNNING_DOXYGEN
+/* Mangle symbol names to avoid symbol collisions with other ELF objects. */
+ #define br_init PTeH3518859728963_br_init
+ #define br_init_lib PTeH3518859728963_br_init_lib
+ #define br_find_exe PTeH3518859728963_br_find_exe
+ #define br_find_exe_dir PTeH3518859728963_br_find_exe_dir
+ #define br_find_prefix PTeH3518859728963_br_find_prefix
+ #define br_find_bin_dir PTeH3518859728963_br_find_bin_dir
+ #define br_find_sbin_dir PTeH3518859728963_br_find_sbin_dir
+ #define br_find_data_dir PTeH3518859728963_br_find_data_dir
+ #define br_find_locale_dir PTeH3518859728963_br_find_locale_dir
+ #define br_find_lib_dir PTeH3518859728963_br_find_lib_dir
+ #define br_find_libexec_dir PTeH3518859728963_br_find_libexec_dir
+ #define br_find_etc_dir PTeH3518859728963_br_find_etc_dir
+ #define br_strcat PTeH3518859728963_br_strcat
+ #define br_build_path PTeH3518859728963_br_build_path
+ #define br_dirname PTeH3518859728963_br_dirname
+
+
+#endif
+int br_init (BrInitError *error);
+int br_init_lib (BrInitError *error);
+
+char *br_find_exe (const char *default_exe);
+char *br_find_exe_dir (const char *default_dir);
+char *br_find_prefix (const char *default_prefix);
+char *br_find_bin_dir (const char *default_bin_dir);
+char *br_find_sbin_dir (const char *default_sbin_dir);
+char *br_find_data_dir (const char *default_data_dir);
+char *br_find_locale_dir (const char *default_locale_dir);
+char *br_find_lib_dir (const char *default_lib_dir);
+char *br_find_libexec_dir (const char *default_libexec_dir);
+char *br_find_etc_dir (const char *default_etc_dir);
+
+/* Utility functions */
+char *br_strcat (const char *str1, const char *str2);
+char *br_build_path (const char *dir, const char *file);
+char *br_dirname (const char *path);
+
+
+#ifdef __cplusplus
+}
+#endif /* __cplusplus */
+
+#endif /* __BINRELOC_H__ */
--- /dev/null
+// $Id$
+//
+// SuperTux
+// Copyright (C) 2006 Matthias Braun <matze@braunis.de>
+//
+// 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 <config.h>
+
+#include "collision.hpp"
+
+#include <algorithm>
+#include <iostream>
+#include <stdio.h>
+#include <float.h>
+#include <math.h>
+#include "math/vector.hpp"
+#include "math/aatriangle.hpp"
+#include "math/rect.hpp"
+#include "collision_hit.hpp"
+#include "log.hpp"
+
+namespace collision
+{
+
+bool intersects(const Rect& r1, const Rect& r2)
+{
+ if(r1.p2.x < r2.p1.x || r1.p1.x > r2.p2.x)
+ return false;
+ if(r1.p2.y < r2.p1.y || r1.p1.y > r2.p2.y)
+ return false;
+
+ return true;
+}
+
+//---------------------------------------------------------------------------
+
+namespace {
+ inline void makePlane(const Vector& p1, const Vector& p2, Vector& n, float& c)
+ {
+ n = Vector(p2.y-p1.y, p1.x-p2.x);
+ c = -(p2 * n);
+ float nval = n.norm();
+ n /= nval;
+ c /= nval;
+ }
+
+ static const float DELTA = .0001f;
+}
+
+bool rectangle_aatriangle(Constraints* constraints, const Rect& rect,
+ const AATriangle& triangle)
+{
+ if(!intersects(rect, (const Rect&) triangle))
+ return false;
+
+ Vector normal;
+ float c;
+ Vector p1;
+ Rect area;
+ switch(triangle.dir & AATriangle::DEFORM_MASK) {
+ case 0:
+ area.p1 = triangle.p1;
+ area.p2 = triangle.p2;
+ break;
+ case AATriangle::DEFORM1:
+ area.p1 = Vector(triangle.p1.x, triangle.p1.y + triangle.get_height()/2);
+ area.p2 = triangle.p2;
+ break;
+ case AATriangle::DEFORM2:
+ area.p1 = triangle.p1;
+ area.p2 = Vector(triangle.p2.x, triangle.p1.y + triangle.get_height()/2);
+ break;
+ case AATriangle::DEFORM3:
+ area.p1 = triangle.p1;
+ area.p2 = Vector(triangle.p1.x + triangle.get_width()/2, triangle.p2.y);
+ break;
+ case AATriangle::DEFORM4:
+ area.p1 = Vector(triangle.p1.x + triangle.get_width()/2, triangle.p1.y);
+ area.p2 = triangle.p2;
+ break;
+ default:
+ assert(false);
+ }
+
+ switch(triangle.dir & AATriangle::DIRECTION_MASK) {
+ case AATriangle::SOUTHWEST:
+ p1 = Vector(rect.p1.x, rect.p2.y);
+ makePlane(area.p1, area.p2, normal, c);
+ break;
+ case AATriangle::NORTHEAST:
+ p1 = Vector(rect.p2.x, rect.p1.y);
+ makePlane(area.p2, area.p1, normal, c);
+ break;
+ case AATriangle::SOUTHEAST:
+ p1 = rect.p2;
+ makePlane(Vector(area.p1.x, area.p2.y),
+ Vector(area.p2.x, area.p1.y), normal, c);
+ break;
+ case AATriangle::NORTHWEST:
+ p1 = rect.p1;
+ makePlane(Vector(area.p2.x, area.p1.y),
+ Vector(area.p1.x, area.p2.y), normal, c);
+ break;
+ default:
+ assert(false);
+ }
+
+ float n_p1 = -(normal * p1);
+ float depth = n_p1 - c;
+ if(depth < 0)
+ return false;
+
+#if 0
+ std::cout << "R: " << rect << " Tri: " << triangle << "\n";
+ std::cout << "Norm: " << normal << " Depth: " << depth << "\n";
+#endif
+
+ Vector outvec = normal * (depth + 0.2f);
+
+ const float RDELTA = 3;
+ if(p1.x < area.p1.x - RDELTA || p1.x > area.p2.x + RDELTA
+ || p1.y < area.p1.y - RDELTA || p1.y > area.p2.y + RDELTA) {
+ set_rectangle_rectangle_constraints(constraints, rect, area);
+ constraints->hit.left = false;
+ constraints->hit.right = false;
+ } else {
+ if(outvec.x < 0) {
+ constraints->right = rect.get_right() + outvec.x;
+ } else {
+ constraints->left = rect.get_left() + outvec.x;
+ }
+
+ if(outvec.y < 0) {
+ constraints->bottom = rect.get_bottom() + outvec.y;
+ constraints->hit.bottom = true;
+ } else {
+ constraints->top = rect.get_top() + outvec.y;
+ constraints->hit.top = true;
+ }
+ constraints->hit.slope_normal = normal;
+ }
+
+ return true;
+}
+
+void set_rectangle_rectangle_constraints(Constraints* constraints,
+ const Rect& r1, const Rect& r2)
+{
+ float itop = r1.get_bottom() - r2.get_top();
+ float ibottom = r2.get_bottom() - r1.get_top();
+ float ileft = r1.get_right() - r2.get_left();
+ float iright = r2.get_right() - r1.get_left();
+
+ float vert_penetration = std::min(itop, ibottom);
+ float horiz_penetration = std::min(ileft, iright);
+ if(vert_penetration < horiz_penetration) {
+ if(itop < ibottom) {
+ constraints->bottom = std::min(constraints->bottom, r2.get_top());
+ constraints->hit.bottom = true;
+ } else {
+ constraints->top = std::max(constraints->top, r2.get_bottom());
+ constraints->hit.top = true;
+ }
+ } else {
+ if(ileft < iright) {
+ constraints->right = std::min(constraints->right, r2.get_left());
+ constraints->hit.right = true;
+ } else {
+ constraints->left = std::max(constraints->left, r2.get_right());
+ constraints->hit.left = true;
+ }
+ }
+}
+
+}
--- /dev/null
+// $Id$
+//
+// SuperTux
+// Copyright (C) 2006 Matthias Braun <matze@braunis.de>
+//
+// 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 __COLLISION_H__
+#define __COLLISION_H__
+
+#include <float.h>
+#include "collision_hit.hpp"
+#include <limits>
+
+class Vector;
+class Rect;
+class AATriangle;
+
+namespace collision
+{
+
+class Constraints
+{
+public:
+ Constraints() {
+ float infinity = (std::numeric_limits<float>::has_infinity ? std::numeric_limits<float>::infinity() : std::numeric_limits<float>::max());
+ left = -infinity;
+ right = infinity;
+ top = -infinity;
+ bottom = infinity;
+ }
+
+ bool has_constraints() const {
+ float infinity = (std::numeric_limits<float>::has_infinity ? std::numeric_limits<float>::infinity() : std::numeric_limits<float>::max());
+ return left > -infinity || right < infinity
+ || top > -infinity || bottom < infinity;
+ }
+
+ float left;
+ float right;
+ float top;
+ float bottom;
+ Vector ground_movement;
+ CollisionHit hit;
+};
+
+/** checks if 2 rectangle intersect each other */
+bool intersects(const Rect& r1, const Rect& r2);
+
+/** does collision detection between a rectangle and an axis aligned triangle
+ * Returns true in case of a collision and fills in the hit structure then.
+ */
+bool rectangle_aatriangle(Constraints* constraints, const Rect& rect,
+ const AATriangle& triangle);
+
+void set_rectangle_rectangle_constraints(Constraints* constraints,
+ const Rect& r1, const Rect& r2);
+
+}
+
+#endif
--- /dev/null
+// $Id$
+//
+// SuperTux
+// Copyright (C) 2006 Matthias Braun <matze@braunis.de>
+//
+// 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_COLLISION_HIT_H
+#define SUPERTUX_COLLISION_HIT_H
+
+#include <float.h>
+#include <math.h>
+#include "math/vector.hpp"
+
+/**
+ * Used as return value for the collision functions, to indicate how the
+ * collision should be handled
+ */
+enum HitResponse
+{
+ /// don't move the object
+ ABORT_MOVE = 0,
+ /// move object out of collision and check for collisions again
+ /// if this happens to often then the move will just be aborted
+ CONTINUE,
+ /// do the move ignoring the collision
+ FORCE_MOVE,
+ /// passes movement to collided object
+ PASS_MOVEMENT,
+
+ /// the object should not appear solid
+ PASSTHROUGH,
+ /// the object should appear solid
+ SOLID,
+};
+
+/**
+ * This class collects data about a collision
+ */
+class CollisionHit
+{
+public:
+ CollisionHit() {
+ left = false;
+ right = false;
+ top = false;
+ bottom = false;
+ crush = false;
+ }
+
+ bool left, right;
+ bool top, bottom;
+ bool crush;
+
+ Vector slope_normal;
+};
+
+#endif
--- /dev/null
+// $Id$
+//
+// SuperTux - Console
+// Copyright (C) 2006 Christoph Sommer <christoph.sommer@2006.expires.deltadevelopment.de>
+//
+// 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 <config.h>
+
+#include <iostream>
+#include <math.h>
+#include <SDL_timer.h>
+#include <SDL_keyboard.h>
+#include "console.hpp"
+#include "video/drawing_context.hpp"
+#include "video/surface.hpp"
+#include "scripting/squirrel_error.hpp"
+#include "scripting/squirrel_util.hpp"
+#include "physfs/physfs_stream.hpp"
+#include "player_status.hpp"
+#include "main.hpp"
+#include "log.hpp"
+#include "resources.hpp"
+#include "gameconfig.hpp"
+
+/// speed (pixels/s) the console closes
+static const float FADE_SPEED = 1;
+
+Console::Console()
+ : history_position(history.end()), vm(NULL), backgroundOffset(0),
+ height(0), alpha(1.0), offset(0), focused(false), stayOpen(0) {
+ fontheight = 8;
+}
+
+Console::~Console()
+{
+ if(vm != NULL) {
+ sq_release(Scripting::global_vm, &vm_object);
+ }
+}
+
+void
+Console::init_graphics()
+{
+ font.reset(new Font(Font::FIXED,
+ "images/engine/fonts/andale12.png",
+ "images/engine/fonts/andale12-shadow.png", 7, 14, 1));
+ fontheight = font->get_height();
+ background.reset(new Surface("images/engine/console.png"));
+ background2.reset(new Surface("images/engine/console2.png"));
+}
+
+void
+Console::flush(ConsoleStreamBuffer* buffer)
+{
+ if (buffer == &outputBuffer) {
+ std::string s = outputBuffer.str();
+ if ((s.length() > 0) && ((s[s.length()-1] == '\n') || (s[s.length()-1] == '\r'))) {
+ while ((s[s.length()-1] == '\n') || (s[s.length()-1] == '\r')) s.erase(s.length()-1);
+ addLines(s);
+ outputBuffer.str(std::string());
+ }
+ }
+}
+
+void
+Console::ready_vm()
+{
+ if(vm == NULL) {
+ vm = Scripting::global_vm;
+ HSQUIRRELVM new_vm = sq_newthread(vm, 16);
+ if(new_vm == NULL)
+ throw Scripting::SquirrelError(vm, "Couldn't create new VM thread for console");
+
+ // store reference to thread
+ sq_resetobject(&vm_object);
+ if(SQ_FAILED(sq_getstackobj(vm, -1, &vm_object)))
+ throw Scripting::SquirrelError(vm, "Couldn't get vm object for console");
+ sq_addref(vm, &vm_object);
+ sq_pop(vm, 1);
+
+ // create new roottable for thread
+ sq_newtable(new_vm);
+ sq_pushroottable(new_vm);
+ if(SQ_FAILED(sq_setdelegate(new_vm, -2)))
+ throw Scripting::SquirrelError(new_vm, "Couldn't set console_table delegate");
+
+ sq_setroottable(new_vm);
+
+ vm = new_vm;
+
+ try {
+ std::string filename = "scripts/console.nut";
+ IFileStream stream(filename);
+ Scripting::compile_and_run(vm, stream, filename);
+ } catch(std::exception& e) {
+ log_warning << "Couldn't load console.nut: " << e.what() << std::endl;
+ }
+ }
+}
+
+void
+Console::execute_script(const std::string& command)
+{
+ using namespace Scripting;
+
+ ready_vm();
+
+ SQInteger oldtop = sq_gettop(vm);
+ try {
+ if(SQ_FAILED(sq_compilebuffer(vm, command.c_str(), command.length(),
+ "", SQTrue)))
+ throw SquirrelError(vm, "Couldn't compile command");
+
+ sq_pushroottable(vm);
+ if(SQ_FAILED(sq_call(vm, 1, SQTrue, SQTrue)))
+ throw SquirrelError(vm, "Problem while executing command");
+
+ if(sq_gettype(vm, -1) != OT_NULL)
+ addLines(squirrel2string(vm, -1));
+ } catch(std::exception& e) {
+ addLines(e.what());
+ }
+ SQInteger newtop = sq_gettop(vm);
+ if(newtop < oldtop) {
+ log_fatal << "Script destroyed squirrel stack..." << std::endl;
+ } else {
+ sq_settop(vm, oldtop);
+ }
+}
+
+void
+Console::input(char c)
+{
+ inputBuffer.insert(inputBufferPosition, 1, c);
+ inputBufferPosition++;
+}
+
+void
+Console::backspace()
+{
+ if ((inputBufferPosition > 0) && (inputBuffer.length() > 0)) {
+ inputBuffer.erase(inputBufferPosition-1, 1);
+ inputBufferPosition--;
+ }
+}
+
+void
+Console::eraseChar()
+{
+ if (inputBufferPosition < (int)inputBuffer.length()) {
+ inputBuffer.erase(inputBufferPosition, 1);
+ }
+}
+
+void
+Console::enter()
+{
+ addLines("> "+inputBuffer);
+ parse(inputBuffer);
+ inputBuffer = "";
+ inputBufferPosition = 0;
+}
+
+void
+Console::scroll(int numLines)
+{
+ offset += numLines;
+ if (offset > 0) offset = 0;
+}
+
+void
+Console::show_history(int offset)
+{
+ while ((offset > 0) && (history_position != history.end())) {
+ history_position++;
+ offset--;
+ }
+ while ((offset < 0) && (history_position != history.begin())) {
+ history_position--;
+ offset++;
+ }
+ if (history_position == history.end()) {
+ inputBuffer = "";
+ inputBufferPosition = 0;
+ } else {
+ inputBuffer = *history_position;
+ inputBufferPosition = inputBuffer.length();
+ }
+}
+
+void
+Console::move_cursor(int offset)
+{
+ if (offset == -65535) inputBufferPosition = 0;
+ if (offset == +65535) inputBufferPosition = inputBuffer.length();
+ inputBufferPosition+=offset;
+ if (inputBufferPosition < 0) inputBufferPosition = 0;
+ if (inputBufferPosition > (int)inputBuffer.length()) inputBufferPosition = inputBuffer.length();
+}
+
+// Helper functions for Console::autocomplete
+// TODO: Fix rough documentation
+namespace {
+
+void sq_insert_commands(std::list<std::string>& cmds, HSQUIRRELVM vm, std::string table_prefix, std::string search_prefix);
+
+/**
+ * Acts upon key,value on top of stack:
+ * Appends key (plus type-dependent suffix) to cmds if table_prefix+key starts with search_prefix;
+ * Calls sq_insert_commands if search_prefix starts with table_prefix+key (and value is a table/class/instance);
+ */
+void
+sq_insert_command(std::list<std::string>& cmds, HSQUIRRELVM vm, std::string table_prefix, std::string search_prefix)
+{
+ const SQChar* key_chars;
+ if (SQ_FAILED(sq_getstring(vm, -2, &key_chars))) return;
+ std::string key_string = table_prefix + key_chars;
+
+ switch (sq_gettype(vm, -1)) {
+ case OT_INSTANCE:
+ key_string+=".";
+ if (search_prefix.substr(0, key_string.length()) == key_string) {
+ sq_getclass(vm, -1);
+ sq_insert_commands(cmds, vm, key_string, search_prefix);
+ sq_pop(vm, 1);
+ }
+ break;
+ case OT_TABLE:
+ case OT_CLASS:
+ key_string+=".";
+ if (search_prefix.substr(0, key_string.length()) == key_string) {
+ sq_insert_commands(cmds, vm, key_string, search_prefix);
+ }
+ break;
+ case OT_CLOSURE:
+ case OT_NATIVECLOSURE:
+ key_string+="()";
+ break;
+ default:
+ break;
+ }
+
+ if (key_string.substr(0, search_prefix.length()) == search_prefix) {
+ cmds.push_back(key_string);
+ }
+
+}
+
+/**
+ * calls sq_insert_command for all entries of table/class on top of stack
+ */
+void
+sq_insert_commands(std::list<std::string>& cmds, HSQUIRRELVM vm, std::string table_prefix, std::string search_prefix)
+{
+ sq_pushnull(vm); // push iterator
+ while (SQ_SUCCEEDED(sq_next(vm,-2))) {
+ sq_insert_command(cmds, vm, table_prefix, search_prefix);
+ sq_pop(vm, 2); // pop key, val
+ }
+ sq_pop(vm, 1); // pop iterator
+}
+
+
+}
+// End of Console::autocomplete helper functions
+
+void
+Console::autocomplete()
+{
+ //int autocompleteFrom = inputBuffer.find_last_of(" ();+", inputBufferPosition);
+ int autocompleteFrom = inputBuffer.find_last_not_of("ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789_->.", inputBufferPosition);
+ if (autocompleteFrom != (int)std::string::npos) {
+ autocompleteFrom += 1;
+ } else {
+ autocompleteFrom = 0;
+ }
+ std::string prefix = inputBuffer.substr(autocompleteFrom, inputBufferPosition - autocompleteFrom);
+ addLines("> "+prefix);
+
+ std::list<std::string> cmds;
+
+ ready_vm();
+
+ // append all keys of the current root table to list
+ sq_pushroottable(vm); // push root table
+ while(true) {
+ // check all keys (and their children) for matches
+ sq_insert_commands(cmds, vm, "", prefix);
+
+ // cycle through parent(delegate) table
+ SQInteger oldtop = sq_gettop(vm);
+ if(SQ_FAILED(sq_getdelegate(vm, -1)) || oldtop == sq_gettop(vm)) {
+ break;
+ }
+ sq_remove(vm, -2); // remove old table
+ }
+ sq_pop(vm, 1); // remove table
+
+ // depending on number of hits, show matches or autocomplete
+ if (cmds.size() == 0) addLines("No known command starts with \""+prefix+"\"");
+ if (cmds.size() == 1) {
+ // one match: just replace input buffer with full command
+ std::string replaceWith = cmds.front();
+ inputBuffer.replace(autocompleteFrom, prefix.length(), replaceWith);
+ inputBufferPosition += (replaceWith.length() - prefix.length());
+ }
+ if (cmds.size() > 1) {
+ // multiple matches: show all matches and set input buffer to longest common prefix
+ std::string commonPrefix = cmds.front();
+ while (cmds.begin() != cmds.end()) {
+ std::string cmd = cmds.front();
+ cmds.pop_front();
+ addLines(cmd);
+ for (int n = commonPrefix.length(); n >= 1; n--) {
+ if (cmd.compare(0, n, commonPrefix) != 0) commonPrefix.resize(n-1); else break;
+ }
+ }
+ std::string replaceWith = commonPrefix;
+ inputBuffer.replace(autocompleteFrom, prefix.length(), replaceWith);
+ inputBufferPosition += (replaceWith.length() - prefix.length());
+ }
+}
+
+void
+Console::addLines(std::string s)
+{
+ std::istringstream iss(s);
+ std::string line;
+ while (std::getline(iss, line, '\n')) addLine(line);
+}
+
+void
+Console::addLine(std::string s)
+{
+ // output line to stderr
+ std::cerr << s << std::endl;
+
+ // wrap long lines
+ std::string overflow;
+ unsigned int line_count = 0;
+ do {
+ lines.push_front(Font::wrap_to_chars(s, 99, &overflow));
+ line_count++;
+ s = overflow;
+ } while (s.length() > 0);
+
+ // trim scrollback buffer
+ while (lines.size() >= 1000)
+ lines.pop_back();
+
+ // increase console height if necessary
+ if (height < 64) {
+ if(height < 4)
+ height = 4;
+ height += fontheight * line_count;
+ }
+
+ // reset console to full opacity
+ alpha = 1.0;
+
+ // increase time that console stays open
+ if(stayOpen < 6)
+ stayOpen += 1.5;
+}
+
+void
+Console::parse(std::string s)
+{
+ // make sure we actually have something to parse
+ if (s.length() == 0) return;
+
+ // add line to history
+ history.push_back(s);
+ history_position = history.end();
+
+ // split line into list of args
+ std::vector<std::string> args;
+ size_t start = 0;
+ size_t end = 0;
+ while (1) {
+ start = s.find_first_not_of(" ,", end);
+ end = s.find_first_of(" ,", start);
+ if (start == s.npos) break;
+ args.push_back(s.substr(start, end-start));
+ }
+
+ // command is args[0]
+ if (args.size() == 0) return;
+ std::string command = args.front();
+ args.erase(args.begin());
+
+ // ignore if it's an internal command
+ if (consoleCommand(command,args)) return;
+
+ try {
+ execute_script(s);
+ } catch(std::exception& e) {
+ addLines(e.what());
+ }
+
+}
+
+bool
+Console::consoleCommand(std::string /*command*/, std::vector<std::string> /*arguments*/)
+{
+ return false;
+}
+
+bool
+Console::hasFocus()
+{
+ return focused;
+}
+
+void
+Console::show()
+{
+ if(!config->console_enabled)
+ return;
+
+ focused = true;
+ height = 256;
+ alpha = 1.0;
+ SDL_EnableKeyRepeat(SDL_DEFAULT_REPEAT_DELAY, SDL_DEFAULT_REPEAT_INTERVAL);
+}
+
+void
+Console::hide()
+{
+ focused = false;
+ height = 0;
+ stayOpen = 0;
+
+ // clear input buffer
+ inputBuffer = "";
+ inputBufferPosition = 0;
+ SDL_EnableKeyRepeat(0, SDL_DEFAULT_REPEAT_INTERVAL);
+}
+
+void
+Console::toggle()
+{
+ if (Console::hasFocus()) {
+ Console::hide();
+ }
+ else {
+ Console::show();
+ }
+}
+
+void
+Console::update(float elapsed_time)
+{
+ if(stayOpen > 0) {
+ stayOpen -= elapsed_time;
+ if(stayOpen < 0)
+ stayOpen = 0;
+ } else if(!focused && height > 0) {
+ alpha -= elapsed_time * FADE_SPEED;
+ if(alpha < 0) {
+ alpha = 0;
+ height = 0;
+ }
+ }
+}
+
+void
+Console::draw(DrawingContext& context)
+{
+ if (height == 0)
+ return;
+
+ int layer = LAYER_GUI + 1;
+
+ context.push_transform();
+ context.set_alpha(alpha);
+ context.draw_surface(background2.get(), Vector(SCREEN_WIDTH/2 - background->get_width()/2 - background->get_width() + backgroundOffset, height - background->get_height()), layer);
+ context.draw_surface(background2.get(), Vector(SCREEN_WIDTH/2 - background->get_width()/2 + backgroundOffset, height - background->get_height()), layer);
+ for (int x = (SCREEN_WIDTH/2 - background->get_width()/2 - (static_cast<int>(ceilf((float)SCREEN_WIDTH / (float)background->get_width()) - 1) * background->get_width())); x < SCREEN_WIDTH; x+=background->get_width()) {
+ context.draw_surface(background.get(), Vector(x, height - background->get_height()), layer);
+ }
+ backgroundOffset+=10;
+ if (backgroundOffset > (int)background->get_width()) backgroundOffset -= (int)background->get_width();
+
+ int lineNo = 0;
+
+ if (focused) {
+ lineNo++;
+ float py = height-4-1 * font->get_height();
+ context.draw_text(font.get(), "> "+inputBuffer, Vector(4, py), ALIGN_LEFT, layer);
+ if (SDL_GetTicks() % 1000 < 750) {
+ int cursor_px = 2 + inputBufferPosition;
+ context.draw_text(font.get(), "_", Vector(4 + (cursor_px * font->get_text_width("X")), py), ALIGN_LEFT, layer);
+ }
+ }
+
+ int skipLines = -offset;
+ for (std::list<std::string>::iterator i = lines.begin(); i != lines.end(); i++) {
+ if (skipLines-- > 0) continue;
+ lineNo++;
+ float py = height - 4 - lineNo*font->get_height();
+ if (py < -font->get_height()) break;
+ context.draw_text(font.get(), *i, Vector(4, py), ALIGN_LEFT, layer);
+ }
+ context.pop_transform();
+}
+
+Console* Console::instance = NULL;
+int Console::inputBufferPosition = 0;
+std::string Console::inputBuffer;
+ConsoleStreamBuffer Console::outputBuffer;
+std::ostream Console::output(&Console::outputBuffer);
+
--- /dev/null
+// $Id$
+//
+// SuperTux - Console
+// Copyright (C) 2006 Christoph Sommer <christoph.sommer@2006.expires.deltadevelopment.de>
+//
+// 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_CONSOLE_H
+#define SUPERTUX_CONSOLE_H
+
+#include <list>
+#include <map>
+#include <vector>
+#include <string>
+#include <sstream>
+#include <iostream>
+#include <squirrel.h>
+
+class Console;
+class ConsoleStreamBuffer;
+class ConsoleCommandReceiver;
+class DrawingContext;
+class Surface;
+class Font;
+
+class Console
+{
+public:
+ Console();
+ ~Console();
+
+ static Console* instance;
+
+ static std::ostream output; /**< stream of characters to output to the console. Do not forget to send std::endl or to flush the stream. */
+
+ void init_graphics();
+
+ void input(char c); /**< add character to inputBuffer */
+ void backspace(); /**< delete character left of inputBufferPosition */
+ void eraseChar(); /**< delete character at inputBufferPosition */
+ void enter(); /**< process and clear input stream */
+ void scroll(int offset); /**< scroll console text up or down by @c offset lines */
+ void autocomplete(); /**< autocomplete current command */
+ void show_history(int offset); /**< move @c offset lines forward through history; Negative offset moves backward */
+ void move_cursor(int offset); /**< move the cursor @c offset chars to the right; Negative offset moves backward; 0xFFFF moves to the end */
+
+ void draw(DrawingContext& context); /**< draw the console in a DrawingContext */
+ void update(float elapsed_time);
+
+ void show(); /**< display the console */
+ void hide(); /**< hide the console */
+ void toggle(); /**< display the console if hidden, hide otherwise */
+
+ bool hasFocus(); /**< true if characters should be sent to the console instead of their normal target */
+
+ template<typename T> static bool string_is(std::string s) {
+ std::istringstream iss(s);
+ T i;
+ if ((iss >> i) && iss.eof()) {
+ return true;
+ } else {
+ return false;
+ }
+ }
+
+ template<typename T> static T string_to(std::string s) {
+ std::istringstream iss(s);
+ T i;
+ if ((iss >> i) && iss.eof()) {
+ return i;
+ } else {
+ return T();
+ }
+ }
+
+private:
+ std::list<std::string> history; /**< command history. New lines get added to back. */
+ std::list<std::string>::iterator history_position; /**< item of command history that is currently displayed */
+ std::list<std::string> lines; /**< backbuffer of lines sent to the console. New lines get added to front. */
+
+ std::auto_ptr<Surface> background; /**< console background image */
+ std::auto_ptr<Surface> background2; /**< second, moving console background image */
+
+ HSQUIRRELVM vm; /**< squirrel thread for the console (with custom roottable) */
+ HSQOBJECT vm_object;
+
+ int backgroundOffset; /**< current offset of scrolling background image */
+ float height; /**< height of the console in px */
+ float alpha;
+ int offset; /**< decrease to scroll text up */
+ bool focused; /**< true if console has input focus */
+ std::auto_ptr<Font> font;
+ float fontheight; /**< height of the font (this is a separate var, because the font could not be initialized yet but is needed in the addLine message */
+
+ float stayOpen;
+
+ static int inputBufferPosition; /**< position in inputBuffer before which to append new characters */
+ static std::string inputBuffer; /**< string used for keyboard input */
+ static ConsoleStreamBuffer outputBuffer; /**< stream buffer used by output stream */
+
+ void addLines(std::string s); /**< display a string of (potentially) multiple lines in the console */
+ void addLine(std::string s); /**< display a line in the console */
+ void parse(std::string s); /**< react to a given command */
+
+ /** ready a virtual machine instance, creating a new thread and loading default .nut files if needed */
+ void ready_vm();
+
+ /** execute squirrel script and output result */
+ void execute_script(const std::string& s);
+
+ bool consoleCommand(std::string command, std::vector<std::string> arguments); /**< process internal command; return false if command was unknown, true otherwise */
+
+ friend class ConsoleStreamBuffer;
+ void flush(ConsoleStreamBuffer* buffer); /**< act upon changes in a ConsoleStreamBuffer */
+};
+
+class ConsoleStreamBuffer : public std::stringbuf
+{
+ public:
+ int sync()
+ {
+ int result = std::stringbuf::sync();
+ if(Console::instance != NULL)
+ Console::instance->flush(this);
+ return result;
+ }
+};
+
+#endif
--- /dev/null
+// $Id$
+//
+// SuperTux
+// Copyright (C) 2006 Matthias Braun <matze@braunis.de>
+//
+// 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 <config.h>
+
+#include "codecontroller.hpp"
+
+CodeController::CodeController()
+{}
+
+CodeController::~CodeController()
+{}
+
+void
+CodeController::press(Control c, bool pressed)
+{
+ controls[c] = pressed;
+}
+
+void
+CodeController::update()
+{
+ Controller::update();
+
+ for(int i = 0; i < CONTROLCOUNT; ++i)
+ controls[i] = false;
+}
--- /dev/null
+// $Id$
+//
+// SuperTux
+// Copyright (C) 2006 Matthias Braun <matze@braunis.de>
+//
+// 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 __CODECONTROLLER_H__
+#define __CODECONTROLLER_H__
+
+#include "controller.hpp"
+
+/**
+ * This is a dummy controler that doesn't react to any user input but should
+ * be controlled by code
+ */
+class CodeController : public Controller
+{
+public:
+ CodeController();
+ virtual ~CodeController();
+
+ void press(Control c, bool pressed = true);
+ void update();
+};
+
+#endif
--- /dev/null
+// $Id$
+//
+// SuperTux
+// Copyright (C) 2006 Matthias Braun <matze@braunis.de>
+//
+// 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 <config.h>
+
+#include "controller.hpp"
+
+const char* Controller::controlNames[] = {
+ "left",
+ "right",
+ "up",
+ "down",
+ "jump",
+ "action",
+ "pause-menu",
+ "menu-select",
+ "console",
+ "peek-left",
+ "peek-right",
+ 0
+};
+
+Controller::Controller()
+{
+ reset();
+}
+
+Controller::~Controller()
+{}
+
+void
+Controller::reset()
+{
+ for(int i = 0; i < CONTROLCOUNT; ++i) {
+ controls[i] = false;
+ oldControls[i] = false;
+ }
+}
+
+bool
+Controller::hold(Control control)
+{
+ return controls[control];
+}
+
+bool
+Controller::pressed(Control control)
+{
+ return !oldControls[control] && controls[control];
+}
+
+bool
+Controller::released(Control control)
+{
+ return oldControls[control] && !controls[control];
+}
+
+void
+Controller::update()
+{
+ for(int i = 0; i < CONTROLCOUNT; ++i)
+ oldControls[i] = controls[i];
+}
--- /dev/null
+// $Id$
+//
+// SuperTux
+// Copyright (C) 2006 Matthias Braun <matze@braunis.de>
+//
+// 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 __CONTROLLER_H__
+#define __CONTROLLER_H__
+
+class Controller
+{
+public:
+ static const char* controlNames[];
+
+ enum Control {
+ LEFT = 0,
+ RIGHT,
+ UP,
+ DOWN,
+
+ JUMP,
+ ACTION,
+
+ PAUSE_MENU,
+ MENU_SELECT,
+
+ CONSOLE,
+
+ PEEK_LEFT,
+ PEEK_RIGHT,
+
+ CONTROLCOUNT
+ };
+
+ Controller();
+ virtual ~Controller();
+
+ /** returns true if the control is pressed down */
+ bool hold(Control control);
+ /** returns true if the control has just been pressed down this frame */
+ bool pressed(Control control);
+ /** returns true if the control has just been released this frame */
+ bool released(Control control);
+
+ virtual void reset();
+ virtual void update();
+
+protected:
+ /** current control status */
+ bool controls[CONTROLCOUNT];
+ /** control status at last frame */
+ bool oldControls[CONTROLCOUNT];
+};
+
+#endif
--- /dev/null
+// $Id$
+//
+// SuperTux
+// Copyright (C) 2006 Matthias Braun <matze@braunis.de>,
+// 2007 Ingo Ruhnke <grumbel@gmx.de>
+//
+// 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 <config.h>
+
+#include <sstream>
+#include "joystickkeyboardcontroller.hpp"
+#include "log.hpp"
+#include "gui/menu.hpp"
+#include "gettext.hpp"
+#include "lisp/lisp.hpp"
+#include "lisp/list_iterator.hpp"
+#include "game_session.hpp"
+#include "console.hpp"
+#include "gameconfig.hpp"
+
+class JoystickKeyboardController::JoystickMenu : public Menu
+{
+public:
+ JoystickMenu(JoystickKeyboardController* controller);
+ virtual ~JoystickMenu();
+
+ void update();
+ std::string get_button_name(int button);
+ void update_menu_item(Control id);
+ virtual void menu_action(MenuItem* item);
+ JoystickKeyboardController* controller;
+};
+
+class JoystickKeyboardController::KeyboardMenu : public Menu
+{
+public:
+ KeyboardMenu(JoystickKeyboardController* controller);
+ ~KeyboardMenu();
+
+ void update();
+ std::string get_key_name(SDLKey key);
+ virtual void menu_action(MenuItem* item);
+ JoystickKeyboardController* controller;
+};
+
+JoystickKeyboardController::JoystickKeyboardController()
+ : hat_state(0),
+ wait_for_key(-1), wait_for_joystick(-1),
+ key_options_menu(0), joystick_options_menu(0)
+{
+ // initialize default keyboard map
+ keymap[SDLK_LEFT] = LEFT;
+ keymap[SDLK_RIGHT] = RIGHT;
+ keymap[SDLK_UP] = UP;
+ keymap[SDLK_DOWN] = DOWN;
+ keymap[SDLK_SPACE] = JUMP;
+ keymap[SDLK_LCTRL] = ACTION;
+ keymap[SDLK_LALT] = ACTION;
+ keymap[SDLK_ESCAPE] = PAUSE_MENU;
+ keymap[SDLK_p] = PAUSE_MENU;
+ keymap[SDLK_PAUSE] = PAUSE_MENU;
+ keymap[SDLK_RETURN] = MENU_SELECT;
+ keymap[SDLK_KP_ENTER] = MENU_SELECT;
+ keymap[SDLK_CARET] = CONSOLE;
+ keymap[SDLK_DELETE] = PEEK_LEFT;
+ keymap[SDLK_END] = PEEK_RIGHT;
+
+ jump_with_up = false;
+
+ int joystick_count = SDL_NumJoysticks();
+ min_joybuttons = -1;
+ max_joybuttons = -1;
+ max_joyaxis = -1;
+ max_joyhats = -1;
+
+ for(int i = 0; i < joystick_count; ++i) {
+ SDL_Joystick* joystick = SDL_JoystickOpen(i);
+ bool good = true;
+ if(SDL_JoystickNumButtons(joystick) < 2) {
+ log_info << "Joystick " << i << " has less than 2 buttons" << std::endl;
+ good = false;
+ }
+ if(SDL_JoystickNumAxes(joystick) < 2
+ && SDL_JoystickNumHats(joystick) == 0) {
+ log_info << "Joystick " << i << " has less than 2 axes and no hat" << std::endl;
+ good = false;
+ }
+ if(!good) {
+ SDL_JoystickClose(joystick);
+ continue;
+ }
+
+ if(min_joybuttons < 0 || SDL_JoystickNumButtons(joystick) < min_joybuttons)
+ min_joybuttons = SDL_JoystickNumButtons(joystick);
+
+ if(SDL_JoystickNumButtons(joystick) > max_joybuttons)
+ max_joybuttons = SDL_JoystickNumButtons(joystick);
+
+ if(SDL_JoystickNumAxes(joystick) > max_joyaxis)
+ max_joyaxis = SDL_JoystickNumAxes(joystick);
+
+ if(SDL_JoystickNumHats(joystick) > max_joyhats)
+ max_joyhats = SDL_JoystickNumHats(joystick);
+
+ joysticks.push_back(joystick);
+ }
+
+ dead_zone = 1000;
+
+ // Default joystick button configuration
+ joy_button_map[0] = JUMP;
+ joy_button_map[1] = ACTION;
+ // 6 or more Buttons
+ if( min_joybuttons > 5 ){
+ joy_button_map[4] = PEEK_LEFT;
+ joy_button_map[5] = PEEK_RIGHT;
+ // 8 or more
+ if(min_joybuttons > 7)
+ joy_button_map[min_joybuttons-1] = PAUSE_MENU;
+ } else {
+ // map the last 2 buttons to menu and pause
+ if(min_joybuttons > 2)
+ joy_button_map[min_joybuttons-1] = PAUSE_MENU;
+ // map all remaining joystick buttons to MENU_SELECT
+ for(int i = 2; i < max_joybuttons; ++i) {
+ if(i != min_joybuttons-1)
+ joy_button_map[i] = MENU_SELECT;
+ }
+ }
+
+ // Default joystick axis configuration
+ joy_axis_map[-1] = LEFT;
+ joy_axis_map[ 1] = RIGHT;
+ joy_axis_map[-2] = UP;
+ joy_axis_map[ 2] = DOWN;
+
+ // some joysticks or SDL seem to produce some bogus events after being opened
+ Uint32 ticks = SDL_GetTicks();
+ while(SDL_GetTicks() - ticks < 200) {
+ SDL_Event event;
+ SDL_PollEvent(&event);
+ }
+}
+
+JoystickKeyboardController::~JoystickKeyboardController()
+{
+ for(std::vector<SDL_Joystick*>::iterator i = joysticks.begin();
+ i != joysticks.end(); ++i) {
+ if(*i != 0)
+ SDL_JoystickClose(*i);
+ }
+
+ delete key_options_menu;
+ delete joystick_options_menu;
+}
+
+void
+JoystickKeyboardController::read(const lisp::Lisp& lisp)
+{
+ const lisp::Lisp* keymap_lisp = lisp.get_lisp("keymap");
+ if(keymap_lisp) {
+ keymap.clear();
+ lisp::ListIterator iter(keymap_lisp);
+ while(iter.next()) {
+ if(iter.item() == "map") {
+ int key = -1;
+ std::string control;
+ const lisp::Lisp* map = iter.lisp();
+ map->get("key", key);
+ map->get("control", control);
+ if(key < SDLK_FIRST || key >= SDLK_LAST) {
+ log_info << "Invalid key '" << key << "' in keymap" << std::endl;
+ continue;
+ }
+
+ int i = 0;
+ for(i = 0; controlNames[i] != 0; ++i) {
+ if(control == controlNames[i])
+ break;
+ }
+ if(controlNames[i] == 0) {
+ log_info << "Invalid control '" << control << "' in keymap" << std::endl;
+ continue;
+ }
+ keymap[(SDLKey) key] = (Control)i;
+ } else {
+ log_info << "Invalid lisp element '" << iter.item() << "' in keymap" << std::endl;
+ }
+ }
+ }
+
+ const lisp::Lisp* joystick_lisp = lisp.get_lisp("joystick");
+ if(joystick_lisp) {
+ joystick_lisp->get("dead-zone", dead_zone);
+ joystick_lisp->get("jump-with-up", jump_with_up);
+ lisp::ListIterator iter(joystick_lisp);
+ while(iter.next()) {
+ if(iter.item() == "map") {
+ int button = -1;
+ int axis = 0;
+ int hat = -1;
+ std::string control;
+ const lisp::Lisp* map = iter.lisp();
+
+ map->get("control", control);
+ int i = 0;
+ for(i = 0; controlNames[i] != 0; ++i) {
+ if(control == controlNames[i])
+ break;
+ }
+ if(controlNames[i] == 0) {
+ log_info << "Invalid control '" << control << "' in buttonmap" << std::endl;
+ continue;
+ }
+
+ if (map->get("button", button)) {
+ if(button < 0 || button >= max_joybuttons) {
+ log_info << "Invalid button '" << button << "' in buttonmap" << std::endl;
+ continue;
+ }
+ bind_joybutton(button, (Control) i);
+ }
+
+ if (map->get("axis", axis)) {
+ if (axis == 0 || abs(axis) > max_joyaxis) {
+ log_info << "Invalid axis '" << axis << "' in axismap" << std::endl;
+ continue;
+ }
+ bind_joyaxis(axis, (Control) i);
+ }
+
+ if (map->get("hat", hat)) {
+ if (hat != SDL_HAT_UP &&
+ hat != SDL_HAT_DOWN &&
+ hat != SDL_HAT_LEFT &&
+ hat != SDL_HAT_RIGHT) {
+ log_info << "Invalid axis '" << axis << "' in axismap" << std::endl;
+ continue;
+ } else {
+ bind_joyhat(hat, (Control) i);
+ }
+ }
+ }
+ }
+ }
+}
+
+void
+JoystickKeyboardController::write(lisp::Writer& writer)
+{
+ writer.start_list("keymap");
+ for(KeyMap::iterator i = keymap.begin(); i != keymap.end(); ++i) {
+ writer.start_list("map");
+ writer.write_int("key", (int) i->first);
+ writer.write_string("control", controlNames[i->second]);
+ writer.end_list("map");
+ }
+ writer.end_list("keymap");
+
+ writer.start_list("joystick");
+ writer.write_int("dead-zone", dead_zone);
+ writer.write_bool("jump-with-up", jump_with_up);
+
+ for(ButtonMap::iterator i = joy_button_map.begin(); i != joy_button_map.end();
+ ++i) {
+ writer.start_list("map");
+ writer.write_int("button", i->first);
+ writer.write_string("control", controlNames[i->second]);
+ writer.end_list("map");
+ }
+
+ for(HatMap::iterator i = joy_hat_map.begin(); i != joy_hat_map.end(); ++i) {
+ writer.start_list("map");
+ writer.write_int("hat", i->first);
+ writer.write_string("control", controlNames[i->second]);
+ writer.end_list("map");
+ }
+
+ for(AxisMap::iterator i = joy_axis_map.begin(); i != joy_axis_map.end(); ++i) {
+ writer.start_list("map");
+ writer.write_int("axis", i->first);
+ writer.write_string("control", controlNames[i->second]);
+ writer.end_list("map");
+ }
+
+ writer.end_list("joystick");
+}
+
+void
+JoystickKeyboardController::reset()
+{
+ Controller::reset();
+}
+
+void
+JoystickKeyboardController::set_joy_controls(Control id, bool value)
+{
+ if (jump_with_up && id == Controller::UP)
+ controls[Controller::JUMP] = value;
+
+ controls[(Control)id] = value;
+}
+
+void
+JoystickKeyboardController::process_event(const SDL_Event& event)
+{
+ switch(event.type) {
+ case SDL_KEYUP:
+ case SDL_KEYDOWN:
+ process_key_event(event);
+ break;
+
+ case SDL_JOYAXISMOTION:
+ process_axis_event(event.jaxis);
+ break;
+
+ case SDL_JOYHATMOTION:
+ process_hat_event(event.jhat);
+ break;
+
+ case SDL_JOYBUTTONDOWN:
+ case SDL_JOYBUTTONUP:
+ process_button_event(event.jbutton);
+ break;
+
+ default:
+ break;
+ }
+}
+
+void
+JoystickKeyboardController::process_button_event(const SDL_JoyButtonEvent& jbutton)
+{
+ if(wait_for_joystick >= 0)
+ {
+ if(jbutton.state == SDL_PRESSED)
+ {
+ bind_joybutton(jbutton.button, (Control)wait_for_joystick);
+ joystick_options_menu->update();
+ reset();
+ wait_for_joystick = -1;
+ }
+ }
+ else
+ {
+ ButtonMap::iterator i = joy_button_map.find(jbutton.button);
+ if(i == joy_button_map.end()) {
+ log_debug << "Unmapped joybutton " << (int)jbutton.button << " pressed" << std::endl;
+ } else {
+ set_joy_controls(i->second, (jbutton.state == SDL_PRESSED));
+ }
+ }
+}
+
+void
+JoystickKeyboardController::process_axis_event(const SDL_JoyAxisEvent& jaxis)
+{
+ if (wait_for_joystick >= 0)
+ {
+ if (abs(jaxis.value) > dead_zone) {
+ if (jaxis.value < 0)
+ bind_joyaxis(-(jaxis.axis + 1), Control(wait_for_joystick));
+ else
+ bind_joyaxis(jaxis.axis + 1, Control(wait_for_joystick));
+
+ joystick_options_menu->update();
+ wait_for_joystick = -1;
+ }
+ }
+ else
+ {
+ // Split the axis into left and right, so that both can be
+ // mapped seperatly (needed for jump/down vs up/down)
+ int axis = jaxis.axis + 1;
+
+ AxisMap::iterator left = joy_axis_map.find(-axis);
+ AxisMap::iterator right = joy_axis_map.find(axis);
+
+ if(left == joy_axis_map.end()) {
+ std::cout << "Unmapped joyaxis " << (int)jaxis.axis << " moved" << std::endl;
+ } else {
+ if (jaxis.value < -dead_zone)
+ set_joy_controls(left->second, true);
+ else if (jaxis.value > dead_zone)
+ set_joy_controls(left->second, false);
+ else
+ set_joy_controls(left->second, false);
+ }
+
+ if(right == joy_axis_map.end()) {
+ std::cout << "Unmapped joyaxis " << (int)jaxis.axis << " moved" << std::endl;
+ } else {
+ if (jaxis.value < -dead_zone)
+ set_joy_controls(right->second, false);
+ else if (jaxis.value > dead_zone)
+ set_joy_controls(right->second, true);
+ else
+ set_joy_controls(right->second, false);
+ }
+ }
+}
+
+void
+JoystickKeyboardController::process_hat_event(const SDL_JoyHatEvent& jhat)
+{
+ Uint8 changed = hat_state ^ jhat.value;
+
+ if (wait_for_joystick >= 0)
+ {
+ if (changed & SDL_HAT_UP && jhat.value & SDL_HAT_UP)
+ bind_joyhat(SDL_HAT_UP, (Control)wait_for_joystick);
+
+ if (changed & SDL_HAT_DOWN && jhat.value & SDL_HAT_DOWN)
+ bind_joyhat(SDL_HAT_DOWN, (Control)wait_for_joystick);
+
+ if (changed & SDL_HAT_LEFT && jhat.value & SDL_HAT_LEFT)
+ bind_joyhat(SDL_HAT_LEFT, (Control)wait_for_joystick);
+
+ if (changed & SDL_HAT_RIGHT && jhat.value & SDL_HAT_RIGHT)
+ bind_joyhat(SDL_HAT_RIGHT, (Control)wait_for_joystick);
+
+ joystick_options_menu->update();
+ wait_for_joystick = -1;
+ }
+ else
+ {
+ if (changed & SDL_HAT_UP)
+ {
+ HatMap::iterator it = joy_hat_map.find(SDL_HAT_UP);
+ if (it != joy_hat_map.end())
+ set_joy_controls(it->second, jhat.value & SDL_HAT_UP);
+ }
+
+ if (changed & SDL_HAT_DOWN)
+ {
+ HatMap::iterator it = joy_hat_map.find(SDL_HAT_DOWN);
+ if (it != joy_hat_map.end())
+ set_joy_controls(it->second, jhat.value & SDL_HAT_DOWN);
+ }
+
+ if (changed & SDL_HAT_LEFT)
+ {
+ HatMap::iterator it = joy_hat_map.find(SDL_HAT_LEFT);
+ if (it != joy_hat_map.end())
+ set_joy_controls(it->second, jhat.value & SDL_HAT_LEFT);
+ }
+
+ if (changed & SDL_HAT_RIGHT)
+ {
+ HatMap::iterator it = joy_hat_map.find(SDL_HAT_RIGHT);
+ if (it != joy_hat_map.end())
+ set_joy_controls(it->second, jhat.value & SDL_HAT_RIGHT);
+ }
+ }
+
+ hat_state = jhat.value;
+}
+
+void
+JoystickKeyboardController::process_key_event(const SDL_Event& event)
+{
+ KeyMap::iterator key_mapping = keymap.find(event.key.keysym.sym);
+
+ // if console key was pressed: toggle console
+ if ((key_mapping != keymap.end()) && (key_mapping->second == CONSOLE)) {
+ if (event.type == SDL_KEYDOWN)
+ Console::instance->toggle();
+ } else {
+ if (Console::instance->hasFocus()) {
+ // if console is open: send key there
+ process_console_key_event(event);
+ } else if (Menu::current()) {
+ // if menu mode: send key there
+ process_menu_key_event(event);
+ } else if(key_mapping == keymap.end()) {
+ // default action: update controls
+ log_debug << "Key " << event.key.keysym.sym << " is unbound" << std::endl;
+ } else {
+ Control control = key_mapping->second;
+ controls[control] = (event.type == SDL_KEYDOWN);
+ }
+ }
+}
+
+void
+JoystickKeyboardController::process_console_key_event(const SDL_Event& event)
+{
+ if (event.type != SDL_KEYDOWN) return;
+
+ switch (event.key.keysym.sym) {
+ case SDLK_RETURN:
+ Console::instance->enter();
+ break;
+ case SDLK_BACKSPACE:
+ Console::instance->backspace();
+ break;
+ case SDLK_TAB:
+ Console::instance->autocomplete();
+ break;
+ case SDLK_PAGEUP:
+ Console::instance->scroll(-1);
+ break;
+ case SDLK_PAGEDOWN:
+ Console::instance->scroll(+1);
+ break;
+ case SDLK_HOME:
+ Console::instance->move_cursor(-65535);
+ break;
+ case SDLK_END:
+ Console::instance->move_cursor(+65535);
+ break;
+ case SDLK_UP:
+ Console::instance->show_history(-1);
+ break;
+ case SDLK_DOWN:
+ Console::instance->show_history(+1);
+ break;
+ case SDLK_LEFT:
+ Console::instance->move_cursor(-1);
+ break;
+ case SDLK_RIGHT:
+ Console::instance->move_cursor(+1);
+ break;
+ default:
+ int c = event.key.keysym.unicode;
+ if ((c >= 32) && (c <= 126)) {
+ Console::instance->input((char)c);
+ }
+ break;
+ }
+}
+
+void
+JoystickKeyboardController::process_menu_key_event(const SDL_Event& event)
+{
+ // wait for key mode?
+ if(wait_for_key >= 0) {
+ if(event.type == SDL_KEYUP)
+ return;
+
+ if(event.key.keysym.sym != SDLK_ESCAPE
+ && event.key.keysym.sym != SDLK_PAUSE) {
+ bind_key(event.key.keysym.sym, (Control) wait_for_key);
+ }
+ reset();
+ key_options_menu->update();
+ wait_for_key = -1;
+ return;
+ }
+ if(wait_for_joystick >= 0) {
+ if(event.key.keysym.sym == SDLK_ESCAPE) {
+ reset();
+ joystick_options_menu->update();
+ wait_for_joystick = -1;
+ }
+ return;
+ }
+
+ Control control;
+ /* we use default keys when the menu is open (to avoid problems when
+ * redefining keys to invalid settings
+ */
+ switch(event.key.keysym.sym) {
+ case SDLK_UP:
+ control = UP;
+ break;
+ case SDLK_DOWN:
+ control = DOWN;
+ break;
+ case SDLK_LEFT:
+ control = LEFT;
+ break;
+ case SDLK_RIGHT:
+ control = RIGHT;
+ break;
+ case SDLK_SPACE:
+ case SDLK_RETURN:
+ case SDLK_KP_ENTER:
+ control = MENU_SELECT;
+ break;
+ case SDLK_ESCAPE:
+ case SDLK_PAUSE:
+ control = PAUSE_MENU;
+ break;
+ default:
+ return;
+ break;
+ }
+
+ controls[control] = (event.type == SDL_KEYDOWN);
+}
+
+void
+JoystickKeyboardController::unbind_joystick_control(Control control)
+{
+ // remove all previous mappings for that control
+ for(AxisMap::iterator i = joy_axis_map.begin(); i != joy_axis_map.end(); /* no ++i */) {
+ if(i->second == control)
+ joy_axis_map.erase(i++);
+ else
+ ++i;
+ }
+
+ for(ButtonMap::iterator i = joy_button_map.begin(); i != joy_button_map.end(); /* no ++i */) {
+ if(i->second == control)
+ joy_button_map.erase(i++);
+ else
+ ++i;
+ }
+
+ for(HatMap::iterator i = joy_hat_map.begin(); i != joy_hat_map.end(); /* no ++i */) {
+ if(i->second == control)
+ joy_hat_map.erase(i++);
+ else
+ ++i;
+ }
+}
+
+void
+JoystickKeyboardController::bind_joyaxis(int axis, Control control)
+{
+ // axis isn't the SDL axis number, but axisnumber + 1 with sign
+ // changed depending on if the positive or negative end is to be
+ // used (negative axis 0 becomes -1, positive axis 2 becomes +3,
+ // etc.)
+
+ unbind_joystick_control(control);
+
+ // add new mapping
+ joy_axis_map[axis] = control;
+}
+
+void
+JoystickKeyboardController::bind_joyhat(int dir, Control c)
+{
+ unbind_joystick_control(c);
+
+ // add new mapping
+ joy_hat_map[dir] = c;
+}
+
+void
+JoystickKeyboardController::bind_joybutton(int button, Control control)
+{
+ unbind_joystick_control(control);
+
+ // add new mapping
+ joy_button_map[button] = control;
+}
+
+void
+JoystickKeyboardController::bind_key(SDLKey key, Control control)
+{
+ // remove all previous mappings for that control and for that key
+ for(KeyMap::iterator i = keymap.begin();
+ i != keymap.end(); /* no ++i */) {
+ if(i->second == control) {
+ KeyMap::iterator e = i;
+ ++i;
+ keymap.erase(e);
+ } else {
+ ++i;
+ }
+ }
+
+ KeyMap::iterator i = keymap.find(key);
+ if(i != keymap.end())
+ keymap.erase(i);
+
+ // add new mapping
+ keymap[key]= control;
+}
+
+void
+JoystickKeyboardController::print_joystick_mappings()
+{
+ std::cout << "Joystick Mappings" << std::endl;
+ std::cout << "-----------------" << std::endl;
+ for(AxisMap::iterator i = joy_axis_map.begin(); i != joy_axis_map.end(); ++i) {
+ std::cout << "Axis: " << i->first << " -> " << i->second << std::endl;
+ }
+
+ for(ButtonMap::iterator i = joy_button_map.begin(); i != joy_button_map.end(); ++i) {
+ std::cout << "Button: " << i->first << " -> " << i->second << std::endl;
+ }
+
+ for(HatMap::iterator i = joy_hat_map.begin(); i != joy_hat_map.end(); ++i) {
+ std::cout << "Hat: " << i->first << " -> " << i->second << std::endl;
+ }
+ std::cout << std::endl;
+}
+
+SDLKey
+JoystickKeyboardController::reversemap_key(Control c)
+{
+ for(KeyMap::iterator i = keymap.begin(); i != keymap.end(); ++i) {
+ if(i->second == c)
+ return i->first;
+ }
+
+ return SDLK_UNKNOWN;
+}
+
+int
+JoystickKeyboardController::reversemap_joyaxis(Control c)
+{
+ for(AxisMap::iterator i = joy_axis_map.begin(); i != joy_axis_map.end(); ++i) {
+ if(i->second == c)
+ return i->first;
+ }
+
+ return 0;
+}
+
+int
+JoystickKeyboardController::reversemap_joybutton(Control c)
+{
+ for(ButtonMap::iterator i = joy_button_map.begin(); i != joy_button_map.end(); ++i) {
+ if(i->second == c)
+ return i->first;
+ }
+
+ return -1;
+}
+
+int
+JoystickKeyboardController::reversemap_joyhat(Control c)
+{
+ for(HatMap::iterator i = joy_hat_map.begin(); i != joy_hat_map.end(); ++i) {
+ if(i->second == c)
+ return i->first;
+ }
+
+ return -1;
+}
+
+Menu*
+JoystickKeyboardController::get_key_options_menu()
+{
+ if(key_options_menu == 0) {
+ key_options_menu = new KeyboardMenu(this);
+ }
+
+ return key_options_menu;
+}
+
+Menu*
+JoystickKeyboardController::get_joystick_options_menu()
+{
+ if(joystick_options_menu == 0) {
+ joystick_options_menu = new JoystickMenu(this);
+ }
+
+ return joystick_options_menu;
+}
+
+//----------------------------------------------------------------------------
+
+JoystickKeyboardController::KeyboardMenu::KeyboardMenu(
+ JoystickKeyboardController* _controller)
+ : controller(_controller)
+{
+ add_label(_("Setup Keyboard"));
+ add_hl();
+ add_controlfield(Controller::UP, _("Up"));
+ add_controlfield(Controller::DOWN, _("Down"));
+ add_controlfield(Controller::LEFT, _("Left"));
+ add_controlfield(Controller::RIGHT, _("Right"));
+ add_controlfield(Controller::JUMP, _("Jump"));
+ add_controlfield(Controller::ACTION, _("Action"));
+ add_controlfield(Controller::PEEK_LEFT, _("Peek Left"));
+ add_controlfield(Controller::PEEK_RIGHT, _("Peek Right"));
+ if (config->console_enabled) {
+ add_controlfield(Controller::CONSOLE, _("Console"));
+ }
+ add_hl();
+ add_back(_("Back"));
+ update();
+}
+
+JoystickKeyboardController::KeyboardMenu::~KeyboardMenu()
+{}
+
+std::string
+JoystickKeyboardController::KeyboardMenu::get_key_name(SDLKey key)
+{
+ switch(key) {
+ case SDLK_UNKNOWN:
+ return _("None");
+ case SDLK_UP:
+ return _("Up cursor");
+ case SDLK_DOWN:
+ return _("Down cursor");
+ case SDLK_LEFT:
+ return _("Left cursor");
+ case SDLK_RIGHT:
+ return _("Right cursor");
+ case SDLK_RETURN:
+ return _("Return");
+ case SDLK_SPACE:
+ return _("Space");
+ case SDLK_RSHIFT:
+ return _("Right Shift");
+ case SDLK_LSHIFT:
+ return _("Left Shift");
+ case SDLK_RCTRL:
+ return _("Right Control");
+ case SDLK_LCTRL:
+ return _("Left Control");
+ case SDLK_RALT:
+ return _("Right Alt");
+ case SDLK_LALT:
+ return _("Left Alt");
+ default:
+ return SDL_GetKeyName((SDLKey) key);
+ }
+}
+
+void
+JoystickKeyboardController::KeyboardMenu::menu_action(MenuItem* item)
+{
+ assert(item->id >= 0 && item->id < Controller::CONTROLCOUNT);
+ item->change_input(_("Press Key"));
+ controller->wait_for_key = item->id;
+}
+
+void
+JoystickKeyboardController::KeyboardMenu::update()
+{
+ // update menu
+ get_item_by_id((int) Controller::UP).change_input(get_key_name(
+ controller->reversemap_key(Controller::UP)));
+ get_item_by_id((int) Controller::DOWN).change_input(get_key_name(
+ controller->reversemap_key(Controller::DOWN)));
+ get_item_by_id((int) Controller::LEFT).change_input(get_key_name(
+ controller->reversemap_key(Controller::LEFT)));
+ get_item_by_id((int) Controller::RIGHT).change_input(get_key_name(
+ controller->reversemap_key(Controller::RIGHT)));
+ get_item_by_id((int) Controller::JUMP).change_input(get_key_name(
+ controller->reversemap_key(Controller::JUMP)));
+ get_item_by_id((int) Controller::ACTION).change_input(get_key_name(
+ controller->reversemap_key(Controller::ACTION)));
+ get_item_by_id((int) Controller::PEEK_LEFT).change_input(get_key_name(
+ controller->reversemap_key(Controller::PEEK_LEFT)));
+ get_item_by_id((int) Controller::PEEK_RIGHT).change_input(get_key_name(
+ controller->reversemap_key(Controller::PEEK_RIGHT)));
+ if (config->console_enabled) {
+ get_item_by_id((int) Controller::CONSOLE).change_input(get_key_name(
+ controller->reversemap_key(Controller::CONSOLE)));
+ }
+}
+
+//---------------------------------------------------------------------------
+
+JoystickKeyboardController::JoystickMenu::JoystickMenu(
+ JoystickKeyboardController* _controller)
+ : controller(_controller)
+{
+ add_label(_("Setup Joystick"));
+ add_hl();
+ if(controller->joysticks.size() > 0) {
+ add_controlfield(Controller::UP, _("Up"));
+ add_controlfield(Controller::DOWN, _("Down"));
+ add_controlfield(Controller::LEFT, _("Left"));
+ add_controlfield(Controller::RIGHT, _("Right"));
+ add_controlfield(Controller::JUMP, _("Jump"));
+ add_controlfield(Controller::ACTION, _("Action"));
+ add_controlfield(Controller::PAUSE_MENU, _("Pause/Menu"));
+ add_controlfield(Controller::PEEK_LEFT, _("Peek Left"));
+ add_controlfield(Controller::PEEK_RIGHT, _("Peek Right"));
+
+ add_toggle(Controller::CONTROLCOUNT, _("Jump with Up"), controller->jump_with_up);
+ } else {
+ add_deactive(-1, _("No Joysticks found"));
+ }
+ add_hl();
+ add_back(_("Back"));
+ update();
+}
+
+JoystickKeyboardController::JoystickMenu::~JoystickMenu()
+{}
+
+std::string
+JoystickKeyboardController::JoystickMenu::get_button_name(int button)
+{
+ if(button < 0)
+ return _("None");
+
+ std::ostringstream name;
+ name << "Button " << button;
+ return name.str();
+}
+
+void
+JoystickKeyboardController::JoystickMenu::menu_action(MenuItem* item)
+{
+ if (item->id >= 0 && item->id < Controller::CONTROLCOUNT) {
+ item->change_input(_("Press Button"));
+ controller->wait_for_joystick = item->id;
+ } else if (item->id == Controller::CONTROLCOUNT) {
+ controller->jump_with_up = item->toggled;
+ }
+}
+
+void
+JoystickKeyboardController::JoystickMenu::update_menu_item(Control id)
+{
+ int button = controller->reversemap_joybutton(id);
+ int axis = controller->reversemap_joyaxis(id);
+ int hat_dir = controller->reversemap_joyhat(id);
+
+ if (button != -1) {
+ get_item_by_id((int)id).change_input(get_button_name(button));
+ } else if (axis != 0) {
+ std::ostringstream name;
+
+ name << "Axis ";
+
+ if (axis < 0)
+ name << "-";
+ else
+ name << "+";
+
+ if (abs(axis) == 1)
+ name << "X";
+ else if (abs(axis) == 2)
+ name << "Y";
+ else if (abs(axis) == 2)
+ name << "X2";
+ else if (abs(axis) == 3)
+ name << "Y2";
+ else
+ name << abs(axis);
+
+ get_item_by_id((int)id).change_input(name.str());
+ } else if (hat_dir != -1) {
+ std::string name;
+
+ switch (hat_dir)
+ {
+ case SDL_HAT_UP:
+ name = "Hat Up";
+ break;
+
+ case SDL_HAT_DOWN:
+ name = "Hat Down";
+ break;
+
+ case SDL_HAT_LEFT:
+ name = "Hat Left";
+ break;
+
+ case SDL_HAT_RIGHT:
+ name = "Hat Right";
+ break;
+
+ default:
+ name = "Unknown hat_dir";
+ break;
+ }
+
+ get_item_by_id((int)id).change_input(name);
+ } else {
+ get_item_by_id((int)id).change_input("None");
+ }
+}
+
+void
+JoystickKeyboardController::JoystickMenu::update()
+{
+ if(controller->joysticks.size() == 0)
+ return;
+
+ update_menu_item(Controller::UP);
+ update_menu_item(Controller::DOWN);
+ update_menu_item(Controller::LEFT);
+ update_menu_item(Controller::RIGHT);
+
+ update_menu_item(Controller::JUMP);
+ update_menu_item(Controller::ACTION);
+ update_menu_item(Controller::PAUSE_MENU);
+ update_menu_item(Controller::PEEK_LEFT);
+ update_menu_item(Controller::PEEK_RIGHT);
+
+ get_item_by_id(Controller::CONTROLCOUNT).toggled = controller->jump_with_up;
+}
--- /dev/null
+// $Id$
+//
+// SuperTux
+// Copyright (C) 2006 Matthias Braun <matze@braunis.de>
+//
+// 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 __JOYSTICKKEYBOARDCONTROLLER_H__
+#define __JOYSTICKKEYBOARDCONTROLLER_H__
+
+#include "controller.hpp"
+#include "lisp/lisp.hpp"
+#include "lisp/writer.hpp"
+#include <SDL.h>
+#include <string>
+#include <map>
+
+class Menu;
+
+class JoystickKeyboardController : public Controller
+{
+public:
+ JoystickKeyboardController();
+ virtual ~JoystickKeyboardController();
+
+ /** Process an SDL Event and return true if the event has been used
+ */
+ void process_event(const SDL_Event& event);
+
+ void write(lisp::Writer& writer);
+ void read(const lisp::Lisp& lisp);
+ void reset();
+
+ Menu* get_key_options_menu();
+ Menu* get_joystick_options_menu();
+
+private:
+ void process_key_event(const SDL_Event& event);
+ void process_hat_event(const SDL_JoyHatEvent& jhat);
+ void process_axis_event(const SDL_JoyAxisEvent& jaxis);
+ void process_button_event(const SDL_JoyButtonEvent& jbutton);
+ void process_console_key_event(const SDL_Event& event);
+ void process_menu_key_event(const SDL_Event& event);
+
+ void print_joystick_mappings();
+
+ typedef std::map<SDLKey, Control> KeyMap;
+ KeyMap keymap;
+
+ typedef std::map<int, Control> ButtonMap;
+ ButtonMap joy_button_map;
+
+ typedef std::map<int, Control> AxisMap;
+ AxisMap joy_axis_map;
+
+ typedef std::map<int, Control> HatMap;
+ HatMap joy_hat_map;
+
+ std::vector<SDL_Joystick*> joysticks;
+
+ std::string name;
+
+ int dead_zone;
+ /// the number of buttons all joysticks have
+ int min_joybuttons;
+ /// the max number of buttons a joystick has
+ int max_joybuttons;
+
+ int max_joyaxis;
+
+ int max_joyhats;
+
+ Uint8 hat_state;
+
+ bool jump_with_up;
+
+ SDLKey reversemap_key(Control c);
+ int reversemap_joybutton(Control c);
+ int reversemap_joyaxis(Control c);
+ int reversemap_joyhat(Control c);
+
+ void unbind_joystick_control(Control c);
+
+ void bind_joybutton(int button, Control c);
+ void bind_joyaxis(int axis, Control c);
+ void bind_joyhat(int dir, Control c);
+ void bind_key(SDLKey key, Control c);
+
+ void set_joy_controls(Control id, bool value);
+
+ int wait_for_key;
+ int wait_for_joystick;
+
+ class KeyboardMenu;
+ class JoystickMenu;
+
+ KeyboardMenu* key_options_menu;
+ JoystickMenu* joystick_options_menu;
+ friend class KeyboardMenu;
+ friend class JoystickMenu;
+};
+
+#endif
--- /dev/null
+// $Id$
+//
+// SuperTux
+// Copyright (C) 2006 Matthias Braun <matze@braunis.de>
+//
+// 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_DIRECTION_H
+#define SUPERTUX_DIRECTION_H
+
+enum Direction { AUTO, LEFT, RIGHT, UP, DOWN };
+
+#endif
--- /dev/null
+// $Id$
+//
+// SuperTux
+// Copyright (C) 2006 Matthias Braun <matze@braunis.de>
+//
+// 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 <config.h>
+
+#include "fadeout.hpp"
+#include "main.hpp"
+#include "video/drawing_context.hpp"
+
+FadeOut::FadeOut(float fade_time, Color color)
+ : color(color), fade_time(fade_time), accum_time(0)
+{
+}
+
+FadeOut::~FadeOut()
+{
+}
+
+void
+FadeOut::update(float elapsed_time)
+{
+ accum_time += elapsed_time;
+ if(accum_time > fade_time)
+ accum_time = fade_time;
+}
+
+void
+FadeOut::draw(DrawingContext& context)
+{
+ Color col = color;
+ col.alpha = accum_time / fade_time;
+ context.draw_filled_rect(Vector(0, 0),
+ Vector(SCREEN_WIDTH, SCREEN_HEIGHT),
+ col, LAYER_GUI+1);
+}
+
+bool
+FadeOut::done()
+{
+ return accum_time >= fade_time;
+}
--- /dev/null
+// $Id$
+//
+// SuperTux
+// Copyright (C) 2006 Matthias Braun <matze@braunis.de>
+//
+// 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 __FADEOUT_HPP__
+#define __FADEOUT_HPP__
+
+#include "video/color.hpp"
+#include "screen_fade.hpp"
+
+/**
+ * Fades a screen towards a specific color
+ */
+class FadeOut : public ScreenFade
+{
+public:
+ FadeOut(float fade_time, Color dest_color = Color(0, 0, 0));
+ virtual ~FadeOut();
+
+ virtual void update(float elapsed_time);
+ virtual void draw(DrawingContext& context);
+
+ /// returns true if the effect is completed
+ virtual bool done();
+
+private:
+ Color color;
+ float fade_time;
+ float accum_time;
+};
+
+#endif
--- /dev/null
+// $Id$
+//
+// SuperTux
+// Copyright (C) 2006 Matthias Braun <matze@braunis.de>
+//
+// 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 <config.h>
+
+#include "log.hpp"
+#include "file_system.hpp"
+
+#include <string>
+#include <vector>
+#include <sstream>
+
+namespace FileSystem
+{
+
+std::string dirname(const std::string& filename)
+{
+ std::string::size_type p = filename.find_last_of('/');
+ if(p == std::string::npos)
+ return "";
+
+ return filename.substr(0, p+1);
+}
+
+std::string basename(const std::string& filename)
+{
+ std::string::size_type p = filename.find_last_of('/');
+ if(p == std::string::npos)
+ return filename;
+
+ return filename.substr(p+1, filename.size()-p-1);
+}
+
+std::string strip_extension(const std::string& filename)
+{
+ std::string::size_type p = filename.find_last_of('.');
+ if(p == std::string::npos)
+ return filename;
+
+ return filename.substr(0, p);
+}
+
+std::string normalize(const std::string& filename)
+{
+ std::vector<std::string> path_stack;
+
+ const char* p = filename.c_str();
+
+ while(true) {
+ while(*p == '/') {
+ p++;
+ continue;
+ }
+
+ const char* pstart = p;
+ while(*p != '/' && *p != 0) {
+ ++p;
+ }
+
+ size_t len = p - pstart;
+ if(len == 0)
+ break;
+
+ std::string pathelem(pstart, p-pstart);
+ if(pathelem == ".")
+ continue;
+
+ if(pathelem == "..") {
+ if(path_stack.empty()) {
+
+ log_warning << "Invalid '..' in path '" << filename << "'" << std::endl;
+ // push it into the result path so that the users sees his error...
+ path_stack.push_back(pathelem);
+ } else {
+ path_stack.pop_back();
+ }
+ } else {
+ path_stack.push_back(pathelem);
+ }
+ }
+
+ // construct path
+ std::ostringstream result;
+ for(std::vector<std::string>::iterator i = path_stack.begin();
+ i != path_stack.end(); ++i) {
+ result << '/' << *i;
+ }
+ if(path_stack.empty())
+ result << '/';
+
+ return result.str();
+}
+
+}
--- /dev/null
+// $Id$
+//
+// SuperTux
+// Copyright (C) 2006 Matthias Braun <matze@braunis.de>
+//
+// 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 __FILESYSTEM_H__
+#define __FILESYSTEM_H__
+
+#include <set>
+#include <string>
+
+namespace FileSystem
+{
+ std::string dirname(const std::string& filename);
+ std::string basename(const std::string& filename);
+
+ /**
+ * remove everything starting from and including the last dot
+ */
+ std::string strip_extension(const std::string& filename);
+
+ /**
+ * normalize filename so that "blup/bla/blo/../../bar" will become
+ * "blup/bar"
+ */
+ std::string normalize(const std::string& filename);
+}
+
+#endif
--- /dev/null
+// $Id$
+//
+// SuperTux
+// Copyright (C) 2006 Matthias Braun <matze@braunis.de>
+//
+// 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 <config.h>
+
+#include "flip_level_transformer.hpp"
+#include "object/tilemap.hpp"
+#include "object/camera.hpp"
+#include "badguy/badguy.hpp"
+#include "sector.hpp"
+#include "tile_manager.hpp"
+#include "spawn_point.hpp"
+#include "object/platform.hpp"
+#include "object/block.hpp"
+
+void
+FlipLevelTransformer::transform_sector(Sector* sector)
+{
+ float height = sector->get_height();
+
+ for(Sector::GameObjects::iterator i = sector->gameobjects.begin();
+ i != sector->gameobjects.end(); ++i) {
+ GameObject* object = *i;
+
+ TileMap* tilemap = dynamic_cast<TileMap*> (object);
+ if(tilemap) {
+ transform_tilemap(tilemap);
+ }
+ Player* player = dynamic_cast<Player*> (object);
+ if(player) {
+ Vector pos = player->get_pos();
+ pos.y = height - pos.y - player->get_bbox().get_height();
+ player->move(pos);
+ continue;
+ }
+ BadGuy* badguy = dynamic_cast<BadGuy*> (object);
+ if(badguy) {
+ transform_badguy(height, badguy);
+ }
+ Platform* platform = dynamic_cast<Platform*> (object);
+ if(platform) {
+ transform_platform(height, *platform);
+ }
+ Block* block = dynamic_cast<Block*> (object);
+ if(block) {
+ transform_block(height, *block);
+ }
+ MovingObject* mobject = dynamic_cast<MovingObject*> (object);
+ if(mobject) {
+ transform_moving_object(height, mobject);
+ }
+ }
+ for(Sector::SpawnPoints::iterator i = sector->spawnpoints.begin();
+ i != sector->spawnpoints.end(); ++i) {
+ transform_spawnpoint(height, *i);
+ }
+
+ if(sector->camera != 0 && sector->player != 0)
+ sector->camera->reset(sector->player->get_pos());
+}
+
+void
+FlipLevelTransformer::transform_tilemap(TileMap* tilemap)
+{
+ for(size_t x = 0; x < tilemap->get_width(); ++x) {
+ for(size_t y = 0; y < tilemap->get_height()/2; ++y) {
+ // swap tiles
+ int y2 = tilemap->get_height()-1-y;
+ const Tile* t1 = tilemap->get_tile(x, y);
+ const Tile* t2 = tilemap->get_tile(x, y2);
+ tilemap->change(x, y, t2->getID());
+ tilemap->change(x, y2, t1->getID());
+ }
+ }
+ if(tilemap->get_drawing_effect() != 0) {
+ tilemap->set_drawing_effect(NO_EFFECT);
+ } else {
+ tilemap->set_drawing_effect(VERTICAL_FLIP);
+ }
+}
+
+void
+FlipLevelTransformer::transform_badguy(float height, BadGuy* badguy)
+{
+ Vector pos = badguy->get_start_position();
+ pos.y = height - pos.y;
+ badguy->set_start_position(pos);
+}
+
+void
+FlipLevelTransformer::transform_spawnpoint(float height, SpawnPoint* spawn)
+{
+ Vector pos = spawn->pos;
+ pos.y = height - pos.y;
+ spawn->pos = pos;
+}
+
+void
+FlipLevelTransformer::transform_moving_object(float height, MovingObject*object)
+{
+ Vector pos = object->get_pos();
+ pos.y = height - pos.y - object->get_bbox().get_height();
+ object->set_pos(pos);
+}
+
+void
+FlipLevelTransformer::transform_platform(float height, Platform& platform)
+{
+ Path& path = platform.get_path();
+ for (std::vector<Path::Node>::iterator i = path.nodes.begin(); i != path.nodes.end(); i++) {
+ Vector& pos = i->position;
+ pos.y = height - pos.y - platform.get_bbox().get_height();
+ }
+}
+
+void
+FlipLevelTransformer::transform_block(float height, Block& block)
+{
+ block.original_y = height - block.original_y - block.get_bbox().get_height();
+}
--- /dev/null
+// $Id$
+//
+// SuperTux
+// Copyright (C) 2006 Matthias Braun <matze@braunis.de>
+//
+// 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 __FLIP_LEVEL_TRANSFORMER_H__
+#define __FLIP_LEVEL_TRANSFORMER_H__
+
+#include "level_transformer.hpp"
+
+class TileMap;
+class BadGuy;
+class SpawnPoint;
+class MovingObject;
+class Platform;
+class Block;
+
+/** Vertically or horizontally flip a level */
+class FlipLevelTransformer : public LevelTransformer
+{
+public:
+ virtual void transform_sector(Sector* sector);
+
+private:
+ void transform_tilemap(TileMap* tilemap);
+ void transform_moving_object(float height, MovingObject* object);
+ void transform_badguy(float height, BadGuy* badguy);
+ void transform_spawnpoint(float height, SpawnPoint* spawnpoint);
+ void transform_platform(float height, Platform& platform);
+ void transform_block(float height, Block& block);
+};
+
+#endif
--- /dev/null
+// $Id$
+//
+// SuperTux
+// Copyright (C) 2006 Matthias Braun <matze@braunis.de>
+//
+// 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 <config.h>
+#include "log.hpp"
+#include "game_object.hpp"
+#include "object_remove_listener.hpp"
+
+GameObject::GameObject()
+ : wants_to_die(false), remove_listeners(NULL)
+{
+}
+
+GameObject::~GameObject()
+{
+ // call remove listeners (and remove them from the list)
+ RemoveListenerListEntry* entry = remove_listeners;
+ while(entry != NULL) {
+ RemoveListenerListEntry* next = entry->next;
+ entry->listener->object_removed(this);
+ delete entry;
+ entry = next;
+ }
+}
+
+
+void
+GameObject::add_remove_listener(ObjectRemoveListener* listener)
+{
+ RemoveListenerListEntry* entry = new RemoveListenerListEntry();
+ entry->next = remove_listeners;
+ entry->listener = listener;
+ remove_listeners = entry;
+}
+
+void
+GameObject::del_remove_listener(ObjectRemoveListener* listener)
+{
+ RemoveListenerListEntry* entry = remove_listeners;
+ if (entry->listener == listener) {
+ remove_listeners = entry->next;
+ delete entry;
+ return;
+ }
+ RemoveListenerListEntry* next = entry->next;
+ while(next != NULL) {
+ if (next->listener == listener) {
+ entry->next = next->next;
+ delete next;
+ break;
+ }
+ entry = next;
+ next = next->next;
+ }
+}
+
--- /dev/null
+// $Id$
+//
+// SuperTux
+// Copyright (C) 2006 Matthias Braun <matze@braunis.de>
+//
+// 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_GAMEOBJECT_H
+#define SUPERTUX_GAMEOBJECT_H
+
+#include <string>
+#include "refcounter.hpp"
+
+class DrawingContext;
+class ObjectRemoveListener;
+
+/**
+ * This is a base class for all game objects. Each sector of a level will hold a
+ * list of active GameObject while the game is played.
+ *
+ * This class is responsible for:
+ * - Updating and Drawing the object. This should happen in the update() and
+ * draw() functions. Both are called once per frame.
+ * - Providing a safe way to remove the object by calling the remove_me
+ * functions.
+ */
+class GameObject : public RefCounter
+{
+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 in
+ * seconds and should be the base for all timed calculations (don't use
+ * SDL_GetTicks directly as this will fail in pause mode)
+ */
+ virtual void update(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;
+ }
+
+ /** registers a remove listener which will be called if the object
+ * gets removed/destroyed
+ */
+ void add_remove_listener(ObjectRemoveListener* listener);
+
+ /**
+ * unregisters a remove listener, so it will no longer be called if the object
+ * gets removed/destroyed
+ */
+ void del_remove_listener(ObjectRemoveListener* listener);
+
+ const std::string& get_name() const
+ {
+ return name;
+ }
+
+private:
+ /** this flag indicates if the object should be removed at the end of the
+ * frame
+ */
+ bool wants_to_die;
+
+ struct RemoveListenerListEntry
+ {
+ RemoveListenerListEntry* next;
+ ObjectRemoveListener* listener;
+ };
+ RemoveListenerListEntry* remove_listeners;
+
+protected:
+ /**
+ * a name for the gameobject, this is mostly a hint for scripts and for
+ * debugging, don't rely on names being set or being unique
+ */
+ std::string name;
+};
+
+#endif /*SUPERTUX_GAMEOBJECT_H*/
--- /dev/null
+// $Id$
+//
+// SuperTux
+// Copyright (C) 2006 Matthias Braun <matze@braunis.de>
+//
+// 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 <config.h>
+
+#include <fstream>
+#include <sstream>
+#include <assert.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <math.h>
+#include <string.h>
+#include <errno.h>
+#include <unistd.h>
+#include <time.h>
+#include <stdexcept>
+
+#include <SDL.h>
+
+#include "game_session.hpp"
+#include "log.hpp"
+#include "console.hpp"
+#include "worldmap/worldmap.hpp"
+#include "mainloop.hpp"
+#include "audio/sound_manager.hpp"
+#include "gui/menu.hpp"
+#include "sector.hpp"
+#include "level.hpp"
+#include "tile.hpp"
+#include "player_status.hpp"
+#include "object/particlesystem.hpp"
+#include "object/background.hpp"
+#include "object/gradient.hpp"
+#include "object/tilemap.hpp"
+#include "object/camera.hpp"
+#include "object/player.hpp"
+#include "object/level_time.hpp"
+#include "lisp/lisp.hpp"
+#include "lisp/parser.hpp"
+#include "resources.hpp"
+#include "statistics.hpp"
+#include "timer.hpp"
+#include "options_menu.hpp"
+#include "textscroller.hpp"
+#include "control/codecontroller.hpp"
+#include "control/joystickkeyboardcontroller.hpp"
+#include "main.hpp"
+#include "file_system.hpp"
+#include "gameconfig.hpp"
+#include "gettext.hpp"
+#include "console.hpp"
+#include "flip_level_transformer.hpp"
+#include "trigger/secretarea_trigger.hpp"
+#include "trigger/sequence_trigger.hpp"
+#include "random_generator.hpp"
+#include "scripting/squirrel_util.hpp"
+#include "object/endsequence_walkright.hpp"
+#include "object/endsequence_walkleft.hpp"
+#include "object/endsequence_fireworks.hpp"
+#include "direction.hpp"
+#include "scripting/time_scheduler.hpp"
+
+// the engine will be run with a logical framerate of 64fps.
+// We chose 64fps here because it is a power of 2, so 1/64 gives an "even"
+// binary fraction...
+static const float LOGICAL_FPS = 64.0;
+
+enum GameMenuIDs {
+ MNID_CONTINUE,
+ MNID_ABORTLEVEL
+};
+
+GameSession* GameSession::current_ = NULL;
+
+GameSession::GameSession(const std::string& levelfile_, Statistics* statistics)
+ : level(0), currentsector(0),
+ end_sequence(0),
+ levelfile(levelfile_), best_level_statistics(statistics),
+ capture_demo_stream(0), playback_demo_stream(0), demo_controller(0),
+ play_time(0)
+{
+ current_ = this;
+ currentsector = NULL;
+
+ game_pause = false;
+ speed_before_pause = main_loop->get_speed();
+
+ statistics_backdrop.reset(new Surface("images/engine/menu/score-backdrop.png"));
+
+ restart_level();
+
+ game_menu.reset(new Menu());
+ game_menu->add_label(_("Pause"));
+ game_menu->add_hl();
+ game_menu->add_entry(MNID_CONTINUE, _("Continue"));
+ game_menu->add_submenu(_("Options"), get_options_menu());
+ game_menu->add_hl();
+ game_menu->add_entry(MNID_ABORTLEVEL, _("Abort Level"));
+}
+
+void
+GameSession::restart_level()
+{
+ game_pause = false;
+ end_sequence = 0;
+
+ main_controller->reset();
+
+ currentsector = 0;
+
+ level.reset(new Level);
+ level->load(levelfile);
+ level->stats.total_coins = level->get_total_coins();
+ level->stats.total_badguys = level->get_total_badguys();
+ level->stats.total_secrets = level->get_total_count<SecretAreaTrigger>();
+ level->stats.reset();
+ if(reset_sector != "")level->stats.declare_invalid();
+
+ if(reset_sector != "") {
+ currentsector = level->get_sector(reset_sector);
+ if(!currentsector) {
+ std::stringstream msg;
+ msg << "Couldn't find sector '" << reset_sector << "' for resetting tux.";
+ throw std::runtime_error(msg.str());
+ }
+ currentsector->activate(reset_pos);
+ } else {
+ currentsector = level->get_sector("main");
+ if(!currentsector)
+ throw std::runtime_error("Couldn't find main sector");
+ currentsector->activate("main");
+ }
+
+ //levelintro();
+
+ sound_manager->stop_music();
+ currentsector->play_music(LEVEL_MUSIC);
+
+ if(capture_file != "") {
+ int newSeed=0; // next run uses a new seed
+ while (newSeed == 0) // which is the next non-zero random num.
+ newSeed = systemRandom.rand();
+ config->random_seed = systemRandom.srand(newSeed);
+ log_info << "Next run uses random seed " <<config->random_seed <<std::endl;
+ record_demo(capture_file);
+ }
+}
+
+GameSession::~GameSession()
+{
+ delete capture_demo_stream;
+ delete playback_demo_stream;
+ delete demo_controller;
+ free_options_menu();
+
+ current_ = NULL;
+}
+
+void
+GameSession::record_demo(const std::string& filename)
+{
+ delete capture_demo_stream;
+
+ capture_demo_stream = new std::ofstream(filename.c_str());
+ if(!capture_demo_stream->good()) {
+ std::stringstream msg;
+ msg << "Couldn't open demo file '" << filename << "' for writing.";
+ throw std::runtime_error(msg.str());
+ }
+ capture_file = filename;
+
+ char buf[30]; // save the seed in the demo file
+ snprintf(buf, sizeof(buf), "random_seed=%10d", config->random_seed);
+ for (int i=0; i==0 || buf[i-1]; i++)
+ capture_demo_stream->put(buf[i]);
+}
+
+int
+GameSession::get_demo_random_seed(const std::string& filename)
+{
+ std::istream* test_stream = new std::ifstream(filename.c_str());
+ if(test_stream->good()) {
+ char buf[30]; // recall the seed from the demo file
+ int seed;
+ for (int i=0; i<30 && (i==0 || buf[i-1]); i++)
+ test_stream->get(buf[i]);
+ if (sscanf(buf, "random_seed=%10d", &seed) == 1) {
+ log_info << "Random seed " << seed << " from demo file" << std::endl;
+ return seed;
+ }
+ else
+ log_info << "Demo file contains no random number" << std::endl;
+ }
+ return 0;
+}
+
+void
+GameSession::play_demo(const std::string& filename)
+{
+ delete playback_demo_stream;
+ delete demo_controller;
+
+ playback_demo_stream = new std::ifstream(filename.c_str());
+ if(!playback_demo_stream->good()) {
+ std::stringstream msg;
+ msg << "Couldn't open demo file '" << filename << "' for reading.";
+ throw std::runtime_error(msg.str());
+ }
+
+ Player& tux = *currentsector->player;
+ demo_controller = new CodeController();
+ tux.set_controller(demo_controller);
+
+ // skip over random seed, if it exists in the file
+ char buf[30]; // ascii decimal seed
+ int seed;
+ for (int i=0; i<30 && (i==0 || buf[i-1]); i++)
+ playback_demo_stream->get(buf[i]);
+ if (sscanf(buf, "random_seed=%010d", &seed) != 1)
+ playback_demo_stream->seekg(0); // old style w/o seed, restart at beg
+}
+
+void
+GameSession::levelintro()
+{
+ sound_manager->stop_music();
+
+ DrawingContext context;
+ for(Sector::GameObjects::iterator i = currentsector->gameobjects.begin();
+ i != currentsector->gameobjects.end(); ++i) {
+ Background* background = dynamic_cast<Background*> (*i);
+ if(background) {
+ background->draw(context);
+ }
+ Gradient* gradient = dynamic_cast<Gradient*> (*i);
+ if(gradient) {
+ gradient->draw(context);
+ }
+ }
+
+// context.draw_text(gold_text, level->get_name(), Vector(SCREEN_WIDTH/2, 160),
+// ALIGN_CENTER, LAYER_FOREGROUND1);
+ context.draw_center_text(gold_text, level->get_name(), Vector(0, 160),
+ LAYER_FOREGROUND1);
+
+ std::stringstream ss_coins;
+ ss_coins << _("Coins") << ": " << player_status->coins;
+ context.draw_text(white_text, ss_coins.str(), Vector(SCREEN_WIDTH/2, 210),
+ ALIGN_CENTER, LAYER_FOREGROUND1);
+
+ if((level->get_author().size()) && (level->get_author() != "SuperTux Team"))
+ context.draw_text(white_small_text,
+ std::string(_("contributed by ")) + level->get_author(),
+ Vector(SCREEN_WIDTH/2, 350), ALIGN_CENTER, LAYER_FOREGROUND1);
+
+ if(best_level_statistics != NULL)
+ best_level_statistics->draw_message_info(context, _("Best Level Statistics"));
+
+ wait_for_event(1.0, 3.0);
+}
+
+void
+GameSession::on_escape_press()
+{
+ if(currentsector->player->is_dying() || end_sequence)
+ {
+ // Let the timers run out, we fast-forward them to force past a sequence
+ if (end_sequence)
+ end_sequence->stop();
+
+ currentsector->player->dying_timer.start(FLT_EPSILON);
+ return; // don't let the player open the menu, when he is dying
+ }
+
+ if(level->on_menukey_script != "") {
+ std::istringstream in(level->on_menukey_script);
+ run_script(in, "OnMenuKeyScript");
+ } else {
+ toggle_pause();
+ }
+}
+
+void
+GameSession::toggle_pause()
+{
+ if(!game_pause) {
+ speed_before_pause = main_loop->get_speed();
+ main_loop->set_speed(0);
+ Menu::set_current(game_menu.get());
+ game_menu->set_active_item(MNID_CONTINUE);
+ game_pause = true;
+ } else {
+ main_loop->set_speed(speed_before_pause);
+ Menu::set_current(NULL);
+ game_pause = false;
+ }
+}
+
+HSQUIRRELVM
+GameSession::run_script(std::istream& in, const std::string& sourcename)
+{
+ using namespace Scripting;
+
+ // garbage collect thread list
+ for(ScriptList::iterator i = scripts.begin();
+ i != scripts.end(); ) {
+ HSQOBJECT& object = *i;
+ HSQUIRRELVM vm = object_to_vm(object);
+
+ if(sq_getvmstate(vm) != SQ_VMSTATE_SUSPENDED) {
+ sq_release(global_vm, &object);
+ i = scripts.erase(i);
+ continue;
+ }
+
+ ++i;
+ }
+
+ HSQOBJECT object = create_thread(global_vm);
+ scripts.push_back(object);
+
+ HSQUIRRELVM vm = object_to_vm(object);
+
+ compile_and_run(vm, in, sourcename);
+
+ return vm;
+}
+
+void
+GameSession::process_events()
+{
+ // end of pause mode?
+ if(!Menu::current() && game_pause) {
+ game_pause = false;
+ }
+
+ // playback a demo?
+ if(playback_demo_stream != 0) {
+ demo_controller->update();
+ char left = false;
+ char right = false;
+ char up = false;
+ char down = false;
+ char jump = false;
+ char action = false;
+ playback_demo_stream->get(left);
+ playback_demo_stream->get(right);
+ playback_demo_stream->get(up);
+ playback_demo_stream->get(down);
+ playback_demo_stream->get(jump);
+ playback_demo_stream->get(action);
+ demo_controller->press(Controller::LEFT, left);
+ demo_controller->press(Controller::RIGHT, right);
+ demo_controller->press(Controller::UP, up);
+ demo_controller->press(Controller::DOWN, down);
+ demo_controller->press(Controller::JUMP, jump);
+ demo_controller->press(Controller::ACTION, action);
+ }
+
+ // save input for demo?
+ if(capture_demo_stream != 0) {
+ capture_demo_stream ->put(main_controller->hold(Controller::LEFT));
+ capture_demo_stream ->put(main_controller->hold(Controller::RIGHT));
+ capture_demo_stream ->put(main_controller->hold(Controller::UP));
+ capture_demo_stream ->put(main_controller->hold(Controller::DOWN));
+ capture_demo_stream ->put(main_controller->hold(Controller::JUMP));
+ capture_demo_stream ->put(main_controller->hold(Controller::ACTION));
+ }
+}
+
+void
+GameSession::check_end_conditions()
+{
+ Player* tux = currentsector->player;
+
+ /* End of level? */
+ if(end_sequence && end_sequence->is_done()) {
+ finish(true);
+ } else if (!end_sequence && tux->is_dead()) {
+ restart_level();
+ }
+}
+
+void
+GameSession::draw(DrawingContext& context)
+{
+ currentsector->draw(context);
+ drawstatus(context);
+
+ if(game_pause)
+ draw_pause(context);
+}
+
+void
+GameSession::draw_pause(DrawingContext& context)
+{
+ context.draw_filled_rect(
+ Vector(0,0), Vector(SCREEN_WIDTH, SCREEN_HEIGHT),
+ Color(.2f, .2f, .2f, .5f), LAYER_FOREGROUND1);
+}
+
+void
+GameSession::process_menu()
+{
+ Menu* menu = Menu::current();
+ if(menu) {
+ menu->update();
+
+ if(menu == game_menu.get()) {
+ switch (game_menu->check()) {
+ case MNID_CONTINUE:
+ Menu::set_current(0);
+ toggle_pause();
+ break;
+ case MNID_ABORTLEVEL:
+ Menu::set_current(0);
+ main_loop->exit_screen();
+ break;
+ }
+ }
+ }
+}
+
+void
+GameSession::setup()
+{
+ Menu::set_current(NULL);
+ current_ = this;
+
+ if(currentsector != Sector::current()) {
+ currentsector->activate(currentsector->player->get_pos());
+ }
+ currentsector->play_music(LEVEL_MUSIC);
+
+ // Eat unneeded events
+ SDL_Event event;
+ while(SDL_PollEvent(&event))
+ {}
+}
+
+void
+GameSession::update(float elapsed_time)
+{
+ // handle controller
+ if(main_controller->pressed(Controller::PAUSE_MENU))
+ on_escape_press();
+
+ process_events();
+ process_menu();
+
+ check_end_conditions();
+
+ // respawning in new sector?
+ if(newsector != "" && newspawnpoint != "") {
+ Sector* sector = level->get_sector(newsector);
+ if(sector == 0) {
+ log_warning << "Sector '" << newsector << "' not found" << std::endl;
+ }
+ sector->activate(newspawnpoint);
+ sector->play_music(LEVEL_MUSIC);
+ currentsector = sector;
+ newsector = "";
+ newspawnpoint = "";
+ }
+
+ // Update the world state and all objects in the world
+ if(!game_pause) {
+ // Update the world
+ if (!end_sequence) {
+ play_time += elapsed_time; //TODO: make sure we don't count cutscene time
+ level->stats.time = play_time;
+ currentsector->update(elapsed_time);
+ } else {
+ if (!end_sequence->is_tux_stopped()) {
+ currentsector->update(elapsed_time);
+ } else {
+ end_sequence->update(elapsed_time);
+ }
+ }
+ }
+
+ // update sounds
+ sound_manager->set_listener_position(currentsector->player->get_pos());
+
+ /* Handle music: */
+ if (end_sequence)
+ return;
+
+ if(currentsector->player->invincible_timer.started()) {
+ if(currentsector->player->invincible_timer.get_timeleft() <=
+ TUX_INVINCIBLE_TIME_WARNING) {
+ currentsector->play_music(HERRING_WARNING_MUSIC);
+ } else {
+ currentsector->play_music(HERRING_MUSIC);
+ }
+ } else if(currentsector->get_music_type() != LEVEL_MUSIC) {
+ currentsector->play_music(LEVEL_MUSIC);
+ }
+}
+
+void
+GameSession::finish(bool win)
+{
+ using namespace WorldMapNS;
+
+ if(win) {
+ if(WorldMap::current())
+ WorldMap::current()->finished_level(level.get());
+ }
+
+ main_loop->exit_screen();
+}
+
+void
+GameSession::respawn(const std::string& sector, const std::string& spawnpoint)
+{
+ newsector = sector;
+ newspawnpoint = spawnpoint;
+}
+
+void
+GameSession::set_reset_point(const std::string& sector, const Vector& pos)
+{
+ reset_sector = sector;
+ reset_pos = pos;
+}
+
+std::string
+GameSession::get_working_directory()
+{
+ return FileSystem::dirname(levelfile);
+}
+
+void
+GameSession::display_info_box(const std::string& text)
+{
+ InfoBox* box = new InfoBox(text);
+
+ bool running = true;
+ DrawingContext context;
+
+ while(running) {
+
+ // TODO make a screen out of this, another mainloop is ugly
+ main_controller->update();
+ SDL_Event event;
+ while (SDL_PollEvent(&event)) {
+ main_controller->process_event(event);
+ if(event.type == SDL_QUIT)
+ main_loop->quit();
+ }
+
+ if(main_controller->pressed(Controller::JUMP)
+ || main_controller->pressed(Controller::ACTION)
+ || main_controller->pressed(Controller::PAUSE_MENU)
+ || main_controller->pressed(Controller::MENU_SELECT))
+ running = false;
+ else if(main_controller->pressed(Controller::DOWN))
+ box->scrolldown();
+ else if(main_controller->pressed(Controller::UP))
+ box->scrollup();
+ box->draw(context);
+ draw(context);
+ context.do_drawing();
+ sound_manager->update();
+ }
+
+ delete box;
+}
+
+void
+GameSession::start_sequence(const std::string& sequencename)
+{
+ // handle special "stoptux" sequence
+ if (sequencename == "stoptux") {
+ if (!end_sequence) {
+ log_warning << "Final target reached without an active end sequence" << std::endl;
+ this->start_sequence("endsequence");
+ }
+ if (end_sequence) end_sequence->stop_tux();
+ return;
+ }
+
+ // abort if a sequence is already playing
+ if (end_sequence)
+ return;
+
+ if (sequencename == "endsequence") {
+ if (currentsector->get_players()[0]->physic.get_velocity_x() < 0) {
+ end_sequence = new EndSequenceWalkLeft();
+ } else {
+ end_sequence = new EndSequenceWalkRight();
+ }
+ } else if (sequencename == "fireworks") {
+ end_sequence = new EndSequenceFireworks();
+ } else {
+ log_warning << "Unknown sequence '" << sequencename << "'. Ignoring." << std::endl;
+ return;
+ }
+
+ /* slow down the game for end-sequence */
+ main_loop->set_speed(0.5f);
+
+ currentsector->add_object(end_sequence);
+ end_sequence->start();
+
+ sound_manager->play_music("music/leveldone.ogg", false);
+ currentsector->player->invincible_timer.start(10000.0f);
+
+ // Stop all clocks.
+ for(std::vector<GameObject*>::iterator i = currentsector->gameobjects.begin();
+ i != currentsector->gameobjects.end(); ++i)
+ {
+ GameObject* obj = *i;
+
+ LevelTime* lt = dynamic_cast<LevelTime*> (obj);
+ if(lt)
+ lt->stop();
+ }
+}
+
+/* (Status): */
+void
+GameSession::drawstatus(DrawingContext& context)
+{
+ player_status->draw(context);
+
+ // draw level stats while end_sequence is running
+ if (end_sequence) {
+ level->stats.draw_endseq_panel(context, best_level_statistics, statistics_backdrop.get());
+ }
+}
--- /dev/null
+// $Id$
+//
+// SuperTux
+// Copyright (C) 2006 Matthias Braun <matze@braunis.de>
+//
+// 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_GAMELOOP_H
+#define SUPERTUX_GAMELOOP_H
+
+#include <string>
+#include <SDL.h>
+#include <squirrel.h>
+#include "screen.hpp"
+#include "math/vector.hpp"
+#include "video/surface.hpp"
+#include "object/endsequence.hpp"
+
+class Level;
+class Sector;
+class Statistics;
+class DrawingContext;
+class CodeController;
+class Menu;
+
+/**
+ * The GameSession class controlls the controll flow of the Game (the part
+ * where you actually play a level)
+ */
+class GameSession : public Screen
+{
+public:
+ GameSession(const std::string& levelfile, Statistics* statistics = NULL);
+ ~GameSession();
+
+ void record_demo(const std::string& filename);
+ int get_demo_random_seed(const std::string& filename);
+ void play_demo(const std::string& filename);
+
+ void draw(DrawingContext& context);
+ void update(float frame_ratio);
+ void setup();
+
+ void set_current()
+ { current_ = this; }
+ static GameSession* current()
+ { return current_; }
+
+ /// ends the current level
+ void finish(bool win = true);
+ void respawn(const std::string& sectorname, const std::string& spawnpointname);
+ void set_reset_point(const std::string& sectorname, const Vector& pos);
+ std::string get_reset_point_sectorname()
+ { return reset_sector; }
+
+ Vector get_reset_point_pos()
+ { return reset_pos; }
+
+ void display_info_box(const std::string& text);
+
+ Sector* get_current_sector()
+ { return currentsector; }
+
+ Level* get_current_level()
+ { return level.get(); }
+
+ void start_sequence(const std::string& sequencename);
+
+ /**
+ * returns the "working directory" usually this is the directory where the
+ * currently played level resides. This is used when locating additional
+ * resources for the current level/world
+ */
+ std::string get_working_directory();
+ void restart_level();
+
+ void toggle_pause();
+
+private:
+ void check_end_conditions();
+ void process_events();
+ void capture_demo_step();
+
+ void levelintro();
+ void drawstatus(DrawingContext& context);
+ void draw_pause(DrawingContext& context);
+
+ HSQUIRRELVM run_script(std::istream& in, const std::string& sourcename);
+ void on_escape_press();
+ void process_menu();
+
+ std::auto_ptr<Level> level;
+ std::auto_ptr<Surface> statistics_backdrop;
+
+ // scripts
+ typedef std::vector<HSQOBJECT> ScriptList;
+ ScriptList scripts;
+
+ Sector* currentsector;
+
+ int levelnb;
+ int pause_menu_frame;
+
+ EndSequence* end_sequence;
+
+ bool game_pause;
+ float speed_before_pause;
+
+ std::string levelfile;
+
+ // reset point (the point where tux respawns if he dies)
+ std::string reset_sector;
+ Vector reset_pos;
+
+ // the sector and spawnpoint we should spawn after this frame
+ std::string newsector;
+ std::string newspawnpoint;
+
+ static GameSession* current_;
+
+ Statistics* best_level_statistics;
+
+ std::ostream* capture_demo_stream;
+ std::string capture_file;
+ std::istream* playback_demo_stream;
+ CodeController* demo_controller;
+
+ std::auto_ptr<Menu> game_menu;
+
+ float play_time; /**< total time in seconds that this session ran interactively */
+};
+
+#endif /*SUPERTUX_GAMELOOP_H*/
--- /dev/null
+// $Id$
+//
+// SuperTux - A Jump'n Run
+// Copyright (C) 2006 Matthias Braun <matze@braunis.de>
+//
+// 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 <config.h>
+
+#include "gameconfig.hpp"
+
+#include <stdlib.h>
+#include <string>
+#include <stdexcept>
+
+#include "lisp/parser.hpp"
+#include "lisp/lisp.hpp"
+#include "lisp/writer.hpp"
+#include "control/joystickkeyboardcontroller.hpp"
+#include "resources.hpp"
+#include "main.hpp"
+
+Config* config = 0;
+
+Config::Config()
+{
+ use_fullscreen = true;
+ video = AUTO_VIDEO;
+ try_vsync = true;
+ show_fps = false;
+ sound_enabled = true;
+ music_enabled = true;
+ console_enabled = false;
+ random_seed = 0; // set by time(), by default (unless in config)
+
+ screenwidth = 800;
+ screenheight = 600;
+ aspect_ratio = -1; // autodetect
+
+ enable_script_debugger = false;
+
+ locale = ""; // autodetect
+}
+
+Config::~Config()
+{}
+
+void
+Config::load()
+{
+ lisp::Parser parser;
+ const lisp::Lisp* root = parser.parse("config");
+
+ const lisp::Lisp* config_lisp = root->get_lisp("supertux-config");
+ if(!config_lisp)
+ throw std::runtime_error("File is not a supertux-config file");
+
+ config_lisp->get("show_fps", show_fps);
+ config_lisp->get("console", console_enabled);
+ config_lisp->get("locale", locale);
+ config_lisp->get("random_seed", random_seed);
+
+ const lisp::Lisp* config_video_lisp = config_lisp->get_lisp("video");
+ if(config_video_lisp) {
+ config_video_lisp->get("fullscreen", use_fullscreen);
+ std::string video_string;
+ config_video_lisp->get("video", video_string);
+ video = get_video_system(video_string);
+ config_video_lisp->get("vsync", try_vsync);
+ config_video_lisp->get("width", screenwidth);
+ config_video_lisp->get("height", screenheight);
+ config_video_lisp->get("aspect_ratio", aspect_ratio);
+ }
+
+ const lisp::Lisp* config_audio_lisp = config_lisp->get_lisp("audio");
+ if(config_audio_lisp) {
+ config_audio_lisp->get("sound_enabled", sound_enabled);
+ config_audio_lisp->get("music_enabled", music_enabled);
+ }
+
+ const lisp::Lisp* config_control_lisp = config_lisp->get_lisp("control");
+ if(config_control_lisp && main_controller) {
+ main_controller->read(*config_control_lisp);
+ }
+}
+
+void
+Config::save()
+{
+ lisp::Writer writer("config");
+
+ writer.start_list("supertux-config");
+
+ writer.write_bool("show_fps", show_fps);
+ writer.write_bool("console", console_enabled);
+ writer.write_string("locale", locale);
+
+ writer.start_list("video");
+ writer.write_bool("fullscreen", use_fullscreen);
+ writer.write_string("video", get_video_string(video));
+ writer.write_bool("vsync", try_vsync);
+ writer.write_int("width", screenwidth);
+ writer.write_int("height", screenheight);
+ writer.write_float("aspect_ratio", aspect_ratio);
+ writer.end_list("video");
+
+ writer.start_list("audio");
+ writer.write_bool("sound_enabled", sound_enabled);
+ writer.write_bool("music_enabled", music_enabled);
+ writer.end_list("audio");
+
+ if(main_controller) {
+ writer.start_list("control");
+ main_controller->write(writer);
+ writer.end_list("control");
+ }
+
+ writer.end_list("supertux-config");
+}
--- /dev/null
+// $Id$
+//
+// SuperTux=
+// Copyright (C) 2006 Matthias Braun <matze@braunis.de>
+//
+// 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_CONFIG_H
+#define SUPERTUX_CONFIG_H
+
+#include <config.h>
+
+#include <string>
+
+#include "video/video_systems.hpp"
+
+class Config
+{
+public:
+ Config();
+ ~Config();
+
+ void load();
+ void save();
+
+ /** screen width in pixel (warning: this is the real screen width+height,
+ * you should use the logical SCREEN_WIDTH and SCREEN_HEIGHT for your
+ * rendering code.)
+ */
+ int screenwidth;
+ int screenheight;
+ float aspect_ratio;
+
+ bool use_fullscreen;
+ VideoSystem video;
+ bool try_vsync;
+ bool show_fps;
+ bool sound_enabled;
+ bool music_enabled;
+ bool console_enabled;
+
+ int random_seed; // initial random seed. 0 ==> set from time()
+
+ /** this variable is set if supertux should start in a specific level */
+ std::string start_level;
+ bool enable_script_debugger;
+ std::string start_demo;
+ std::string record_demo;
+
+ std::string locale; /**< force SuperTux language to this locale, e.g. "de". A file "data/locale/xx.po" must exist for this to work. An empty string means autodetect. */
+};
+
+extern Config* config;
+
+#endif
--- /dev/null
+/*
+ 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
+
+#include "tinygettext/tinygettext.hpp"
+
+extern TinyGetText::DictionaryManager dictionary_manager;
+
+static inline const char* _(const char* message)
+{
+ return dictionary_manager.get_dictionary().translate(message);
+}
+
+static inline std::string _(const std::string& message)
+{
+ return dictionary_manager.get_dictionary().translate(message);
+}
+
+static inline const char* N_(const char* id, const char* id2, int num)
+{
+ return dictionary_manager.get_dictionary().translate(id, id2, num).c_str();
+}
+
+#endif /* _LIBGETTEXT_H */
--- /dev/null
+// $Id$
+//
+// SuperTux
+// Copyright (C) 2006 Matthias Braun <matze@braunis.de>
+//
+// 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 <config.h>
+
+#include <SDL.h>
+#include <iostream>
+
+#include "main.hpp"
+#include "button.hpp"
+#include "mousecursor.hpp"
+#include "video/font.hpp"
+#include "video/surface.hpp"
+
+Font* Button::info_font = 0;
+extern SDL_Surface* screen;
+
+/* Buttons */
+
+Button::Button(Surface* image_, std::string info_, SDLKey binding_)
+ : binding(binding_)
+{
+ image = image_;
+ size = Vector(image->get_width(), image->get_height());
+ id = 0;
+ info = info_;
+}
+
+Button::~Button()
+{
+}
+
+void Button::draw(DrawingContext &context, bool selected)
+{
+if(selected)
+ context.draw_filled_rect(pos, size, Color (200,240,220), LAYER_GUI);
+else
+ context.draw_filled_rect(pos, size, Color (200,200,220), LAYER_GUI);
+
+Vector tanslation = -context.get_translation();
+if(state == BT_SHOW_INFO)
+ {
+ Vector offset;
+ if(pos.x + tanslation.x < 100 && pos.y + tanslation.y > SCREEN_HEIGHT - 20)
+ offset = Vector(size.x, - 10);
+ else if(pos.x + tanslation.x < 100)
+ offset = Vector(size.x, 0);
+ else
+ offset = Vector(-30, -size.y/2);
+ context.draw_text(info_font, info, pos + offset, ALIGN_LEFT, LAYER_GUI+2);
+ if(binding != 0)
+ context.draw_text(info_font, "(" + std::string(SDL_GetKeyName(binding)) +
+ ")", pos + offset + Vector(0,12),
+ ALIGN_LEFT, LAYER_GUI+2);
+ }
+
+context.draw_surface_part(image, Vector(0,0), size, pos, LAYER_GUI+1);
+}
+
+int Button::event(SDL_Event &event, int x_offset, int y_offset)
+{
+state = BT_NONE;
+switch(event.type)
+ {
+ case SDL_MOUSEBUTTONDOWN:
+ if(event.button.x > pos.x + x_offset && event.button.x < pos.x + x_offset + size.x &&
+ event.button.y > pos.y + y_offset && event.button.y < pos.y + y_offset + size.y)
+ {
+ if(event.button.button == SDL_BUTTON_RIGHT)
+ state = BT_SHOW_INFO;
+ }
+ break;
+ case SDL_MOUSEBUTTONUP:
+ if(event.button.x > pos.x + x_offset && event.button.x < pos.x + x_offset + size.x &&
+ event.button.y > pos.y + y_offset && event.button.y < pos.y + y_offset + size.y)
+ {
+ if(event.button.button == SDL_BUTTON_LEFT)
+ state = BT_SELECTED;
+ }
+ break;
+ case SDL_KEYDOWN: // key pressed
+ if(event.key.keysym.sym == binding)
+ state = BT_SELECTED;
+ break;
+ default:
+ break;
+ }
+return state;
+}
+
+/* Group of buttons */
+
+ButtonGroup::ButtonGroup(Vector pos_, Vector buttons_size_, Vector buttons_box_)
+ : pos(pos_), buttons_size(buttons_size_), buttons_box(buttons_box_)
+{
+buttons.clear();
+row = 0;
+button_selected = -1;
+mouse_hover = false;
+mouse_left_button = false;
+buttons_pair_nb = 0;
+}
+
+ButtonGroup::~ButtonGroup()
+{
+}
+
+void ButtonGroup::add_button(Button button, int id, bool select)
+{
+button.pos.x = ((buttons.size()-buttons_pair_nb) % (int)buttons_box.x) * buttons_size.x;
+button.pos.y = ((int)((buttons.size()-buttons_pair_nb) / buttons_box.x)) * buttons_size.y;
+button.size = buttons_size;
+button.id = id;
+if(select)
+ button_selected = id;
+
+buttons.push_back(button);
+}
+
+void ButtonGroup::add_pair_of_buttons(Button button1, int id1, Button button2, int id2)
+{
+button1.pos.x = button2.pos.x = ((buttons.size()-buttons_pair_nb) % (int)buttons_box.x) * buttons_size.x;
+button1.pos.y = button2.pos.y = ((int)((buttons.size()-buttons_pair_nb) / buttons_box.x)) * buttons_size.y;
+button1.size.x = button2.size.x = buttons_size.x;
+button1.size.y = button2.size.y = buttons_size.y / 2;
+button2.pos.y += buttons_size.y / 2;
+button1.id = id1;
+button2.id = id2;
+
+buttons_pair_nb++;
+buttons.push_back(button1);
+buttons.push_back(button2);
+}
+
+void ButtonGroup::draw(DrawingContext &context)
+{
+context.draw_filled_rect(pos - Vector(12,4),
+ Vector(buttons_size.x*buttons_box.x + 16, buttons_size.y*buttons_box.y + 8),
+ Color (0,0,0, 128), LAYER_GUI-1);
+
+context.push_transform();
+context.set_translation(Vector(-pos.x, -pos.y + buttons_size.y*row));
+for(Buttons::iterator i = buttons.begin(); i != buttons.end(); ++i)
+ {
+ if(i->pos.y < row*buttons_size.y ||
+ i->pos.y + i->size.y > (row + buttons_box.y) * buttons_size.y)
+ continue;
+
+ i->draw(context, i->id == button_selected);
+ }
+context.pop_transform();
+}
+
+bool ButtonGroup::event(SDL_Event &event)
+{
+bool caught_event = false;
+
+switch(event.type)
+ {
+ case SDL_MOUSEMOTION:
+ mouse_hover = false;
+
+ if(mouse_left_button)
+ {
+ pos.x += int(event.motion.xrel * float(SCREEN_WIDTH)/screen->w);
+ pos.y += int(event.motion.yrel * float(SCREEN_HEIGHT)/screen->h);
+ caught_event = true;
+ }
+ if(event.button.x > pos.x-12 && event.button.x < pos.x+16 + buttons_box.x*buttons_size.x &&
+ event.button.y > pos.y-4 && event.button.y < pos.y+8 + buttons_box.y*buttons_size.y)
+ mouse_hover = true;
+ break;
+ case SDL_MOUSEBUTTONDOWN:
+ if(event.button.x < pos.x-12 || event.button.x > pos.x+16 +
+ buttons_box.x*buttons_size.x || event.button.y < pos.y-4 ||
+ event.button.y > pos.y+8 + buttons_box.y*buttons_size.y)
+ break;
+
+ caught_event = true;
+
+ if(event.button.button == SDL_BUTTON_WHEELUP)
+ {
+ row--;
+ if(row < 0)
+ row = 0;
+ }
+ else if(event.button.button == SDL_BUTTON_WHEELDOWN)
+ {
+ row++;
+ if(row > (int)((buttons.size()-buttons_pair_nb)/buttons_box.x) - (int)buttons_box.y +
+ ((int)(buttons.size()-buttons_pair_nb)%(int)buttons_box.x != 0 ? 1 : 0))
+ row = (int)((buttons.size()-buttons_pair_nb)/buttons_box.x) - (int)buttons_box.y +
+ ((int)(buttons.size()-buttons_pair_nb)%(int)buttons_box.x != 0 ? 1 : 0);
+ }
+ else if(event.button.button == SDL_BUTTON_LEFT)
+ mouse_left_button = true;
+ else
+ caught_event = false;
+ break;
+ case SDL_MOUSEBUTTONUP:
+ mouse_left_button = false;
+ break;
+ default:
+ break;
+ }
+
+if(caught_event)
+ return true;
+
+for(Buttons::iterator i = buttons.begin(); i != buttons.end(); ++i)
+ {
+ if(i->pos.y < row*buttons_size.y ||
+ i->pos.y + i->size.y > (row + buttons_box.y) * buttons_size.y)
+ continue;
+
+ if(i->event(event, (int)pos.x,
+ (int)pos.y - row*(int)buttons_size.y) == BT_SELECTED)
+ {
+ button_selected = i->id;
+ caught_event = true;
+ break;
+ }
+ }
+
+return caught_event;
+}
+
+int ButtonGroup::selected_id()
+{
+return button_selected;
+}
+
+void ButtonGroup::set_unselected()
+{
+button_selected = -1;
+}
+
+bool ButtonGroup::is_hover()
+{
+return mouse_hover;
+}
--- /dev/null
+// $Id$
+//
+// SuperTux
+// Copyright (C) 2006 Matthias Braun <matze@braunis.de>
+//
+// 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 <vector>
+#include <string>
+
+#include "math/vector.hpp"
+#include "video/drawing_context.hpp"
+
+class Surface;
+
+class ButtonGroup;
+
+enum {
+ BT_NONE,
+ BT_HOVER,
+ BT_SELECTED,
+ BT_SHOW_INFO
+ };
+
+class Button
+{
+public:
+ Button(Surface* image_, std::string info_, SDLKey binding_);
+ ~Button();
+
+ void draw(DrawingContext& context, bool selected);
+ int event(SDL_Event& event, int x_offset = 0, int y_offset = 0);
+
+ static Font* info_font;
+
+private:
+ friend class ButtonGroup;
+
+ Vector pos, size;
+
+ Surface* image;
+ SDLKey binding;
+
+ int id;
+ int state;
+ std::string info;
+};
+
+class ButtonGroup
+{
+public:
+ ButtonGroup(Vector pos_, Vector size_, Vector button_box_);
+ ~ButtonGroup();
+
+ void draw(DrawingContext& context);
+ bool event(SDL_Event& event);
+
+ void add_button(Button button, int id, bool select = false);
+ void add_pair_of_buttons(Button button1, int id1, Button button2, int id2);
+
+ int selected_id();
+ void set_unselected();
+ bool is_hover();
+
+private:
+ Vector pos, buttons_size, buttons_box;
+ typedef std::vector <Button> Buttons;
+ Buttons buttons;
+
+ int button_selected, row;
+ bool mouse_hover, mouse_left_button;
+
+ int buttons_pair_nb;
+};
+
+#endif
--- /dev/null
+// $Id$
+//
+// SuperTux
+// Copyright (C) 2006 Matthias Braun <matze@braunis.de>
+//
+// 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 <config.h>
+
+#include <sys/types.h>
+#include <ctype.h>
+
+#include <iostream>
+#include <sstream>
+#include <cstdlib>
+#include <cstdio>
+#include <string>
+#include <cassert>
+#include <stdexcept>
+
+#include "menu.hpp"
+#include "mainloop.hpp"
+#include "video/drawing_context.hpp"
+#include "gettext.hpp"
+#include "math/vector.hpp"
+#include "main.hpp"
+#include "resources.hpp"
+#include "timer.hpp"
+#include "control/joystickkeyboardcontroller.hpp"
+
+static const float MENU_REPEAT_INITIAL = 0.4f;
+static const float MENU_REPEAT_RATE = 0.2f;
+static const float FLICK_CURSOR_TIME = 0.5f;
+
+extern SDL_Surface* screen;
+
+std::vector<Menu*> Menu::last_menus;
+Menu* Menu::current_ = 0;
+Font* Menu::default_font;
+Font* Menu::active_font;
+Font* Menu::deactive_font;
+Font* Menu::label_font;
+Font* Menu::field_font;
+
+/* 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->add_deactive(-1, text);
+ dialog->add_hl();
+ dialog->add_entry(true, _("Yes"));
+ dialog->add_entry(false, _("No"));
+ dialog->add_hl();
+
+ Menu::set_current(dialog);
+
+ DrawingContext context;
+
+ // TODO make this a screen and not another mainloop...
+ while(true)
+ {
+ SDL_Event event;
+ while (SDL_PollEvent(&event)) {
+ if(event.type == SDL_QUIT)
+ main_loop->quit();
+ main_controller->process_event(event);
+ dialog->event(event);
+ }
+
+ if(background == NULL)
+ context.draw_gradient(Color(0.8f, 0.95f, 0.85f), Color(0.8f, 0.8f, 0.8f),
+ LAYER_BACKGROUND0);
+ else
+ context.draw_surface(background, Vector(0,0), LAYER_BACKGROUND0);
+
+ dialog->draw(context);
+ dialog->update();
+
+ 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_time = real_time;
+}
+
+void
+Menu::pop_current()
+{
+ if (last_menus.size() >= 1) {
+ current_ = last_menus.back();
+ current_->effect_time = real_time;
+ last_menus.pop_back();
+ } else {
+ current_ = 0;
+ }
+}
+
+void
+Menu::set_current(Menu* menu)
+{
+ last_menus.clear();
+
+ if (menu)
+ menu->effect_time = real_time;
+
+ current_ = menu;
+ // just to be sure...
+ main_controller->reset();
+}
+
+MenuItem::MenuItem(MenuItemKind _kind, int _id)
+ : kind(_kind) , id(_id)
+{
+ toggled = false;
+ selected = false;
+ target_menu = 0;
+}
+
+void
+MenuItem::change_text(const std::string& text_)
+{
+ text = text_;
+}
+
+void
+MenuItem::change_input(const std::string& text_)
+{
+ input = text_;
+}
+
+std::string MenuItem::get_input_with_symbol(bool active_item)
+{
+ if(!active_item) {
+ input_flickering = true;
+ } else {
+ input_flickering = ((int) (real_time / FLICK_CURSOR_TIME)) % 2;
+ }
+
+ char str[1024];
+ if(input_flickering)
+ snprintf(str, sizeof(str), "%s ",input.c_str());
+ else
+ snprintf(str, sizeof(str), "%s_",input.c_str());
+
+ std::string string = str;
+
+ return string;
+}
+
+Menu::~Menu()
+{
+ for(std::vector<MenuItem*>::iterator i = items.begin();
+ i != items.end(); ++i)
+ delete *i;
+ if(current_ == this)
+ current_ = NULL;
+}
+
+Menu::Menu()
+{
+ hit_item = -1;
+ menuaction = MENU_ACTION_NONE;
+ delete_character = 0;
+ mn_input_char = '\0';
+
+ pos_x = SCREEN_WIDTH/2;
+ pos_y = SCREEN_HEIGHT/2;
+ arrange_left = 0;
+ active_item = -1;
+
+ checkbox.reset(new Surface("images/engine/menu/checkbox-unchecked.png"));
+ checkbox_checked.reset(new Surface("images/engine/menu/checkbox-checked.png"));
+ back.reset(new Surface("images/engine/menu/arrow-back.png"));
+ arrow_left.reset(new Surface("images/engine/menu/arrow-left.png"));
+ arrow_right.reset(new Surface("images/engine/menu/arrow-right.png"));
+}
+
+void Menu::set_pos(float x, float y, float rw, float rh)
+{
+ pos_x = x + get_width() * rw;
+ pos_y = y + get_height() * rh;
+}
+
+/* Add an item to a menu */
+void
+Menu::additem(MenuItem* item)
+{
+ items.push_back(item);
+
+ /* If a new menu is being built, the active item shouldn't be set to
+ * something that isnt selectable. Set the active_item to the first
+ * selectable item added
+ */
+ if (active_item == -1
+ && item->kind != MN_HL
+ && item->kind != MN_LABEL
+ && item->kind != MN_DEACTIVE) {
+ active_item = items.size() - 1;
+ }
+}
+
+void
+Menu::add_hl()
+{
+ additem(new MenuItem(MN_HL));
+}
+
+void
+Menu::add_label(const std::string& text)
+{
+ MenuItem* item = new MenuItem(MN_LABEL);
+ item->text = text;
+ additem(item);
+}
+
+void
+Menu::add_controlfield(int id, const std::string& text,
+ const std::string& mapping)
+{
+ MenuItem* item = new MenuItem(MN_CONTROLFIELD, id);
+ item->change_text(text);
+ item->change_input(mapping);
+ additem(item);
+}
+
+void
+Menu::add_entry(int id, const std::string& text)
+{
+ MenuItem* item = new MenuItem(MN_ACTION, id);
+ item->text = text;
+ additem(item);
+}
+
+void
+Menu::add_deactive(int id, const std::string& text)
+{
+ MenuItem* item = new MenuItem(MN_DEACTIVE, id);
+ item->text = text;
+ additem(item);
+}
+
+void
+Menu::add_toggle(int id, const std::string& text, bool toogled)
+{
+ MenuItem* item = new MenuItem(MN_TOGGLE, id);
+ item->text = text;
+ item->toggled = toogled;
+ additem(item);
+}
+
+void
+Menu::add_back(const std::string& text)
+{
+ MenuItem* item = new MenuItem(MN_BACK);
+ item->text = text;
+ additem(item);
+}
+
+void
+Menu::add_submenu(const std::string& text, Menu* submenu, int id)
+{
+ MenuItem* item = new MenuItem(MN_GOTO, id);
+ item->text = text;
+ item->target_menu = submenu;
+ additem(item);
+}
+
+void
+Menu::clear()
+{
+ for(std::vector<MenuItem*>::iterator i = items.begin();
+ i != items.end(); ++i) {
+ delete *i;
+ }
+ items.clear();
+ active_item = -1;
+}
+
+/* Process actions done on the menu */
+void
+Menu::update()
+{
+ /** check main input controller... */
+ if(main_controller->pressed(Controller::UP)) {
+ menuaction = MENU_ACTION_UP;
+ menu_repeat_time = real_time + MENU_REPEAT_INITIAL;
+ }
+ if(main_controller->hold(Controller::UP) &&
+ menu_repeat_time != 0 && real_time > menu_repeat_time) {
+ menuaction = MENU_ACTION_UP;
+ menu_repeat_time = real_time + MENU_REPEAT_RATE;
+ }
+ if(main_controller->pressed(Controller::DOWN)) {
+ menuaction = MENU_ACTION_DOWN;
+ menu_repeat_time = real_time + MENU_REPEAT_INITIAL;
+ }
+ if(main_controller->hold(Controller::DOWN) &&
+ menu_repeat_time != 0 && real_time > menu_repeat_time) {
+ menuaction = MENU_ACTION_DOWN;
+ menu_repeat_time = real_time + MENU_REPEAT_RATE;
+ }
+ if(main_controller->pressed(Controller::ACTION)
+ || main_controller->pressed(Controller::MENU_SELECT)) {
+ menuaction = MENU_ACTION_HIT;
+ }
+ if(main_controller->pressed(Controller::PAUSE_MENU)) {
+ menuaction = MENU_ACTION_BACK;
+ }
+
+ hit_item = -1;
+ if(items.size() == 0)
+ return;
+
+ int last_active_item = active_item;
+ switch(menuaction) {
+ case MENU_ACTION_UP:
+ do {
+ if (active_item > 0)
+ --active_item;
+ else
+ active_item = int(items.size())-1;
+ } while ((items[active_item]->kind == MN_HL
+ || items[active_item]->kind == MN_LABEL
+ || items[active_item]->kind == MN_DEACTIVE)
+ && (active_item != last_active_item));
+
+ break;
+
+ case MENU_ACTION_DOWN:
+ do {
+ if(active_item < int(items.size())-1 )
+ ++active_item;
+ else
+ active_item = 0;
+ } while ((items[active_item]->kind == MN_HL
+ || items[active_item]->kind == MN_LABEL
+ || items[active_item]->kind == MN_DEACTIVE)
+ && (active_item != last_active_item));
+
+ break;
+
+ case MENU_ACTION_LEFT:
+ if(items[active_item]->kind == MN_STRINGSELECT) {
+ if(items[active_item]->selected > 0)
+ items[active_item]->selected--;
+ else
+ items[active_item]->selected = items[active_item]->list.size()-1;
+ }
+ break;
+
+ case MENU_ACTION_RIGHT:
+ if(items[active_item]->kind == MN_STRINGSELECT) {
+ if(items[active_item]->selected+1 < items[active_item]->list.size())
+ items[active_item]->selected++;
+ else
+ items[active_item]->selected = 0;
+ }
+ break;
+
+ case MENU_ACTION_HIT: {
+ hit_item = active_item;
+ switch (items[active_item]->kind) {
+ case MN_GOTO:
+ assert(items[active_item]->target_menu != 0);
+ Menu::push_current(items[active_item]->target_menu);
+ break;
+
+ case MN_TOGGLE:
+ items[active_item]->toggled = !items[active_item]->toggled;
+ menu_action(items[active_item]);
+ break;
+
+ case MN_CONTROLFIELD:
+ menu_action(items[active_item]);
+ break;
+
+ case MN_ACTION:
+ menu_action(items[active_item]);
+ break;
+
+ case MN_TEXTFIELD:
+ case MN_NUMFIELD:
+ menuaction = MENU_ACTION_DOWN;
+ update();
+ break;
+
+ case MN_BACK:
+ Menu::pop_current();
+ break;
+ default:
+ break;
+ }
+ break;
+ }
+
+ case MENU_ACTION_REMOVE:
+ if(items[active_item]->kind == MN_TEXTFIELD
+ || items[active_item]->kind == MN_NUMFIELD)
+ {
+ if(!items[active_item]->input.empty())
+ {
+ int i = items[active_item]->input.size();
+
+ while(delete_character > 0) /* remove charactes */
+ {
+ items[active_item]->input.resize(i-1);
+ delete_character--;
+ }
+ }
+ }
+ break;
+
+ case MENU_ACTION_INPUT:
+ if(items[active_item]->kind == MN_TEXTFIELD
+ || (items[active_item]->kind == MN_NUMFIELD
+ && mn_input_char >= '0' && mn_input_char <= '9'))
+ {
+ items[active_item]->input.push_back(mn_input_char);
+ }
+ break;
+
+ case MENU_ACTION_BACK:
+ Menu::pop_current();
+ break;
+
+ case MENU_ACTION_NONE:
+ break;
+ }
+ menuaction = MENU_ACTION_NONE;
+
+ assert(active_item < int(items.size()));
+}
+
+int
+Menu::check()
+{
+ if (hit_item != -1)
+ return items[hit_item]->id;
+ else
+ return -1;
+}
+
+void
+Menu::menu_action(MenuItem* )
+{}
+
+void
+Menu::draw_item(DrawingContext& context, int index)
+{
+ float menu_height = get_height();
+ float menu_width = get_width();
+
+ MenuItem& pitem = *(items[index]);
+
+ int effect_offset = 0;
+ if(effect_time != 0) {
+ if(real_time - effect_time > 0.5) {
+ effect_time = 0;
+ } else {
+ float effect_delta = (0.5 - (real_time - effect_time)) * 250;
+ effect_offset = (int) ((index % 2) ? effect_delta : -effect_delta);
+ }
+ }
+
+ Font* text_font = default_font;
+ float x_pos = pos_x;
+ float 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 = 0;
+ if(pitem.list.size() > 0) {
+ list_width = (int) text_font->get_text_width(pitem.list[pitem.selected]);
+ }
+
+ 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 = active_font;
+ }
+
+ switch (pitem.kind)
+ {
+ case MN_DEACTIVE:
+ {
+ context.draw_text(deactive_font, pitem.text,
+ Vector(SCREEN_WIDTH/2, y_pos - int(deactive_font->get_height()/2)),
+ ALIGN_CENTER, LAYER_GUI);
+ break;
+ }
+
+ case MN_HL:
+ {
+ // TODO
+ float x = pos_x - menu_width/2;
+ float 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(0.6f, 0.7f, 1.0f, 1.0f), LAYER_GUI);
+ context.draw_filled_rect(Vector(x, y + 6),
+ Vector(menu_width, 2),
+ Color(1.0f, 1.0f, 1.0f, 1.0f), LAYER_GUI);
+ break;
+ }
+ case MN_LABEL:
+ {
+ context.draw_text(label_font, pitem.text,
+ Vector(SCREEN_WIDTH/2, y_pos - int(label_font->get_height()/2)),
+ ALIGN_CENTER, LAYER_GUI);
+ break;
+ }
+ case MN_TEXTFIELD:
+ case MN_NUMFIELD:
+ case MN_CONTROLFIELD:
+ {
+ float width = text_width + input_width + 5;
+ float text_pos = SCREEN_WIDTH/2 - width/2;
+ float input_pos = text_pos + text_width + 10;
+
+ context.draw_filled_rect(
+ Vector(input_pos - 5, y_pos - 10),
+ Vector(input_width + 10, 20),
+ Color(1.0f, 1.0f, 1.0f, 1.0f), LAYER_GUI-5);
+ context.draw_filled_rect(
+ Vector(input_pos - 4, y_pos - 9),
+ Vector(input_width + 8, 18),
+ Color(0, 0, 0, 0.5f), LAYER_GUI-4);
+
+ if(pitem.kind == MN_TEXTFIELD || pitem.kind == MN_NUMFIELD)
+ {
+ if(active_item == index)
+ context.draw_text(field_font,
+ pitem.get_input_with_symbol(true),
+ Vector(input_pos, y_pos - int(field_font->get_height()/2)),
+ ALIGN_LEFT, LAYER_GUI);
+ else
+ context.draw_text(field_font,
+ pitem.get_input_with_symbol(false),
+ Vector(input_pos, y_pos - int(field_font->get_height()/2)),
+ ALIGN_LEFT, LAYER_GUI);
+ }
+ else
+ context.draw_text(field_font, pitem.input,
+ Vector(input_pos, y_pos - int(field_font->get_height()/2)),
+ ALIGN_LEFT, LAYER_GUI);
+
+ context.draw_text(text_font, pitem.text,
+ Vector(text_pos, y_pos - int(text_font->get_height()/2)),
+ ALIGN_LEFT, 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.get(),
+ Vector(x_pos - list_pos + text_pos - 17, y_pos - 8),
+ LAYER_GUI);
+ context.draw_surface(arrow_right.get(),
+ 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(1.0f, 1.0f, 1.0f, 1.0f), 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, 0.5f), LAYER_GUI - 5);
+
+ context.draw_text(text_font, pitem.list[pitem.selected],
+ Vector(SCREEN_WIDTH/2 + text_pos, y_pos - int(text_font->get_height()/2)),
+ ALIGN_CENTER, LAYER_GUI);
+ context.draw_text(text_font, pitem.text,
+ Vector(SCREEN_WIDTH/2 + list_pos_2/2, y_pos - int(text_font->get_height()/2)),
+ ALIGN_CENTER, LAYER_GUI);
+ break;
+ }
+ case MN_BACK:
+ {
+ context.draw_text(text_font, pitem.text,
+ Vector(SCREEN_WIDTH/2, y_pos - int(text_font->get_height()/2)),
+ ALIGN_CENTER, LAYER_GUI);
+ context.draw_surface(back.get(),
+ Vector(x_pos + text_width/2 + 16, y_pos - 8),
+ LAYER_GUI);
+ break;
+ }
+
+ case MN_TOGGLE:
+ {
+ context.draw_text(text_font, pitem.text,
+ Vector(SCREEN_WIDTH/2, y_pos - (text_font->get_height()/2)),
+ ALIGN_CENTER, LAYER_GUI);
+
+ if(pitem.toggled)
+ context.draw_surface(checkbox_checked.get(),
+ Vector(x_pos + (text_width+16)/2, y_pos - 8),
+ LAYER_GUI + 1);
+ else
+ context.draw_surface(checkbox.get(),
+ Vector(x_pos + (text_width+16)/2, y_pos - 8),
+ LAYER_GUI + 1);
+ break;
+ }
+ case MN_ACTION:
+ context.draw_text(text_font, pitem.text,
+ Vector(SCREEN_WIDTH/2, y_pos - int(text_font->get_height()/2)),
+ ALIGN_CENTER, LAYER_GUI);
+ break;
+
+ case MN_GOTO:
+ context.draw_text(text_font, pitem.text,
+ Vector(SCREEN_WIDTH/2, y_pos - int(text_font->get_height()/2)),
+ ALIGN_CENTER, LAYER_GUI);
+ break;
+ }
+}
+
+float Menu::get_width() const
+{
+ /* The width of the menu has to be more than the width of the text
+ with the most characters */
+ float menu_width = 0;
+ for(unsigned int i = 0; i < items.size(); ++i)
+ {
+ Font* font = default_font;
+ if(items[i]->kind == MN_LABEL)
+ font = label_font;
+
+ float w = font->get_text_width(items[i]->text) +
+ label_font->get_text_width(items[i]->input) + 16;
+ if(items[i]->kind == MN_TOGGLE)
+ w += 32;
+
+ if(w > menu_width)
+ menu_width = w;
+ }
+
+ return menu_width + 24;
+}
+
+float Menu::get_height() const
+{
+ return items.size() * 24;
+}
+
+/* Draw the current menu. */
+void
+Menu::draw(DrawingContext& context)
+{
+ if(MouseCursor::current()) {
+ MouseCursor::current()->draw(context);
+ }
+
+ float menu_height = get_height();
+ float menu_width = get_width();
+
+ /* Draw a transparent background */
+ context.draw_filled_rect(
+ Vector(pos_x - menu_width/2, pos_y - 24*items.size()/2 - 10),
+ Vector(menu_width,menu_height + 20),
+ Color(0.6f, 0.7f, 0.8f, 0.5f), LAYER_GUI-10);
+
+ for(unsigned int i = 0; i < items.size(); ++i)
+ {
+ draw_item(context, i);
+ }
+}
+
+MenuItem&
+Menu::get_item_by_id(int id)
+{
+ for(std::vector<MenuItem*>::iterator i = items.begin();
+ i != items.end(); ++i) {
+ MenuItem& item = **i;
+
+ if(item.id == id)
+ return item;
+ }
+
+ throw std::runtime_error("MenuItem not found");
+}
+
+const MenuItem&
+Menu::get_item_by_id(int id) const
+{
+ for(std::vector<MenuItem*>::const_iterator i = items.begin();
+ i != items.end(); ++i) {
+ const MenuItem& item = **i;
+
+ if(item.id == id)
+ return item;
+ }
+
+ throw std::runtime_error("MenuItem not found");
+}
+
+int Menu::get_active_item_id()
+{
+ return items[active_item]->id;
+}
+
+bool
+Menu::is_toggled(int id) const
+{
+ return get_item_by_id(id).toggled;
+}
+
+/* Check for menu event */
+void
+Menu::event(const SDL_Event& event)
+{
+ if(effect_time != 0)
+ return;
+
+ switch(event.type) {
+ case SDL_MOUSEBUTTONDOWN:
+ {
+ int x = int(event.motion.x * float(SCREEN_WIDTH)/screen->w);
+ int y = int(event.motion.y * float(SCREEN_HEIGHT)/screen->h);
+
+ 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:
+ {
+ float x = event.motion.x * SCREEN_WIDTH/screen->w;
+ float y = event.motion.y * SCREEN_HEIGHT/screen->h;
+
+ 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)
+ {
+ int new_active_item
+ = static_cast<int> ((y - (pos_y - get_height()/2)) / 24);
+
+ /* only change the mouse focus to a selectable item */
+ if ((items[new_active_item]->kind != MN_HL)
+ && (items[new_active_item]->kind != MN_LABEL)
+ && (items[new_active_item]->kind != MN_DEACTIVE))
+ active_item = new_active_item;
+
+ if(MouseCursor::current())
+ MouseCursor::current()->set_state(MC_LINK);
+ }
+ else
+ {
+ if(MouseCursor::current())
+ MouseCursor::current()->set_state(MC_NORMAL);
+ }
+ }
+ break;
+
+ default:
+ break;
+ }
+}
+
+void
+Menu::set_active_item(int id)
+{
+ for(size_t i = 0; i < items.size(); ++i) {
+ MenuItem* item = items[i];
+ if(item->id == id) {
+ active_item = i;
+ break;
+ }
+ }
+}
--- /dev/null
+// $Id$
+//
+// SuperTux
+// Copyright (C) 2006 Matthias Braun <matze@braunis.de>
+//
+// 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 <vector>
+#include <memory>
+#include <set>
+#include <string>
+#include <utility>
+#include <assert.h>
+
+#include <SDL.h>
+
+#include "video/surface.hpp"
+#include "video/font.hpp"
+#include "mousecursor.hpp"
+
+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,
+ MN_STRINGSELECT,
+ MN_LABEL,
+ MN_HL, /* horizontal line */
+};
+
+class Menu;
+
+class MenuItem
+{
+public:
+ MenuItem(MenuItemKind kind, int id = -1);
+ MenuItemKind kind;
+ int id; // item id
+ bool toggled;
+ std::string text;
+ std::string input;
+
+ std::vector<std::string> list; // list of values for a STRINGSELECT item
+ size_t selected; // currently selected item
+
+ Menu* target_menu;
+
+ void change_text (const std::string& text);
+ void change_input(const std::string& text);
+
+ static MenuItem* create(MenuItemKind kind, const std::string& text,
+ int init_toggle, Menu* target_menu, int id, int key);
+
+ std::string get_input_with_symbol(bool active_item); // returns the text with an input symbol
+
+private:
+ /// copy-construction not allowed
+ MenuItem(const MenuItem& ) { assert(false); }
+ /// assignment not allowed
+ void operator= (const MenuItem& ) { assert(false); }
+
+ /// keyboard key or joystick button
+ bool input_flickering;
+};
+
+class Menu
+{
+private:
+ static std::vector<Menu*> last_menus;
+ static Menu* current_;
+
+ static void pop_current();
+
+public:
+ /** Set the current menu, if pmenu is NULL, hide the current menu */
+ static void set_current(Menu* pmenu);
+
+ static void push_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,
+ MENU_ACTION_BACK
+ };
+
+ /** Number of the item that got 'hit' (ie. pressed) in the last
+ event()/update() call, -1 if none */
+ int hit_item;
+
+ // position of the menu (ie. center of the menu, not top/left)
+ float pos_x;
+ float pos_y;
+
+ /** input event for the menu (up, down, left, right, etc.) */
+ MenuAction menuaction;
+
+ /* input implementation variables */
+ int delete_character;
+ char mn_input_char;
+ float menu_repeat_time;
+
+public:
+ static Font* default_font;
+ static Font* active_font;
+ static Font* deactive_font;
+ static Font* label_font;
+ static Font* field_font;
+
+ std::vector<MenuItem*> items;
+
+ Menu();
+ virtual ~Menu();
+
+ void add_hl();
+ void add_label(const std::string& text);
+ void add_entry(int id, const std::string& text);
+ void add_toggle(int id, const std::string& text, bool toggled = false);
+ void add_deactive(int id, const std::string& text);
+ void add_back(const std::string& text);
+ void add_submenu(const std::string& text, Menu* submenu, int id = -1);
+ void add_controlfield(int id, const std::string& text,
+ const std::string& mapping = "");
+
+ virtual void menu_action(MenuItem* item);
+
+ void update();
+
+ /** 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 *(items[index]);
+ }
+ MenuItem& get_item_by_id(int id);
+ const MenuItem& get_item_by_id(int id) const;
+
+ int get_active_item_id();
+ void set_active_item(int id);
+
+ void draw(DrawingContext& context);
+ void set_pos(float x, float y, float rw = 0, float rh = 0);
+
+ void event(const SDL_Event& event);
+
+ bool is_toggled(int id) const;
+
+protected:
+ void additem(MenuItem* pmenu_item);
+ float get_width() const;
+ float get_height() const;
+
+private:
+ void check_controlfield_change_event(const SDL_Event& event);
+ void draw_item(DrawingContext& context, int index);
+ float effect_time;
+ int arrange_left;
+ int active_item;
+
+ std::auto_ptr<Surface> checkbox;
+ std::auto_ptr<Surface> checkbox_checked;
+ std::auto_ptr<Surface> back;
+ std::auto_ptr<Surface> arrow_left;
+ std::auto_ptr<Surface> arrow_right;
+};
+
+#endif
--- /dev/null
+// $Id$
+//
+// SuperTux
+// Copyright (C) 2006 Matthias Braun <matze@braunis.de>
+//
+// 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 <config.h>
+
+#include <SDL_events.h>
+#include <SDL_mouse.h>
+
+#include "video/drawing_context.hpp"
+#include "gui/mousecursor.hpp"
+#include "main.hpp"
+
+MouseCursor* MouseCursor::current_ = 0;
+extern SDL_Surface* screen;
+
+MouseCursor::MouseCursor(std::string cursor_file) : mid_x(0), mid_y(0)
+{
+ cursor = new Surface(cursor_file);
+
+ cur_state = MC_NORMAL;
+}
+
+MouseCursor::~MouseCursor()
+{
+ delete cursor;
+}
+
+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);
+
+ x = int(x * float(SCREEN_WIDTH)/screen->w);
+ y = int(y * float(SCREEN_HEIGHT)/screen->h);
+
+ w = (int) cursor->get_width();
+ h = (int) (cursor->get_height() / 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;
+ }
+
+ context.draw_surface_part(cursor, Vector(0, h*cur_state),
+ Vector(w, h), Vector(x-mid_x, y-mid_y), LAYER_GUI+100);
+}
--- /dev/null
+// $Id$
+//
+// SuperTux
+// Copyright (C) 2006 Matthias Braun <matze@braunis.de>
+//
+// 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 <string>
+
+#include "video/surface.hpp"
+
+#define MC_STATES_NB 3
+
+enum {
+ MC_NORMAL = 0,
+ MC_CLICK,
+ MC_LINK,
+ MC_HIDE
+};
+
+class DrawingContext;
+
+/// Mouse cursor.
+/** Used to create mouse cursors.
+ The mouse cursors can be animated
+ and can be used in four different states.
+ (MC_NORMAL, MC_CLICK, MC_LINK or MC_HIDE) */
+class MouseCursor
+{
+public:
+ /// Constructor of MouseCursor.
+ /** Expects an imagefile for the cursor and the number of animation frames it contains. */
+ MouseCursor(std::string cursor_file);
+ ~MouseCursor();
+ /// Get MouseCursor state.
+ /** (MC_NORMAL, MC_CLICK, MC_LINK or MC_HIDE) */
+ int state();
+ /// Set MouseCursor state.
+ /** (MC_NORMAL, MC_CLICK, MC_LINK or MC_HIDE) */
+ void set_state(int nstate);
+ /// Define the middle of a MouseCursor.
+ /** Useful for cross mouse cursor images in example. */
+ void set_mid(int x, int y);
+
+ /// Draw MouseCursor on screen.
+ void draw(DrawingContext& context);
+
+ /// Return the current cursor.
+ static MouseCursor* current()
+ { return current_; };
+ /// Set current cursor.
+ static void set_current(MouseCursor* pcursor)
+ { current_ = pcursor; };
+
+private:
+ int mid_x, mid_y;
+ static MouseCursor* current_;
+ int state_before_click;
+ int cur_state;
+ Surface* cursor;
+};
+
+#endif /*SUPERTUX_MOUSECURSOR_H*/
--- /dev/null
+// $Id$
+//
+// SuperTux
+// Copyright (C) 2006 Matthias Braun <matze@braunis.de>
+//
+// 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 <config.h>
+
+#include <map>
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include <iostream>
+#include <fstream>
+#include <sstream>
+#include <memory>
+#include <stdexcept>
+
+#include "log.hpp"
+#include "lisp/parser.hpp"
+#include "lisp/lisp.hpp"
+#include "lisp/list_iterator.hpp"
+#include "lisp/writer.hpp"
+#include "level.hpp"
+#include "physic.hpp"
+#include "sector.hpp"
+#include "tile.hpp"
+#include "resources.hpp"
+#include "file_system.hpp"
+#include "object/gameobjs.hpp"
+#include "object/camera.hpp"
+#include "object/tilemap.hpp"
+#include "object/coin.hpp"
+#include "object/block.hpp"
+
+using namespace std;
+
+Level::Level()
+ : name("noname"), author("Mr. X")
+{
+}
+
+void
+Level::load(const std::string& filepath)
+{
+ try {
+ lisp::Parser parser;
+ const lisp::Lisp* root = parser.parse(filepath);
+
+ const lisp::Lisp* level = root->get_lisp("supertux-level");
+ if(!level)
+ throw std::runtime_error("file is not a supertux-level file.");
+
+ int version = 1;
+ level->get("version", version);
+ if(version == 1) {
+ load_old_format(*level);
+ return;
+ }
+
+ contact = "";
+ license = "";
+
+ lisp::ListIterator iter(level);
+ while(iter.next()) {
+ const std::string& token = iter.item();
+ if(token == "version") {
+ iter.value()->get(version);
+ if(version > 2) {
+ log_warning << "level format newer than application" << std::endl;
+ }
+ } else if(token == "name") {
+ iter.value()->get(name);
+ } else if(token == "author") {
+ iter.value()->get(author);
+ } else if(token == "contact") {
+ iter.value()->get(contact);
+ } else if(token == "license") {
+ iter.value()->get(license);
+ } else if(token == "on-menukey-script") {
+ iter.value()->get(on_menukey_script);
+ } else if(token == "sector") {
+ Sector* sector = new Sector(this);
+ sector->parse(*(iter.lisp()));
+ add_sector(sector);
+ } else {
+ log_warning << "Unknown token '" << token << "' in level file" << std::endl;
+ }
+ }
+
+ if (license == "") log_warning << "The level author did not specify a license for this level. You might not be allowed to share it." << std::endl;
+
+ } catch(std::exception& e) {
+ std::stringstream msg;
+ msg << "Problem when reading level '" << filepath << "': " << e.what();
+ throw std::runtime_error(msg.str());
+ }
+}
+
+void
+Level::load_old_format(const lisp::Lisp& reader)
+{
+ reader.get("name", name);
+ reader.get("author", author);
+
+ Sector* sector = new Sector(this);
+ sector->parse_old_format(reader);
+ add_sector(sector);
+}
+
+void
+Level::save(const std::string& filename)
+{
+ lisp::Writer* writer = new lisp::Writer(filename);
+
+ writer->write_comment("Level made using SuperTux's built-in Level Editor");
+
+ writer->start_list("supertux-level");
+
+ int version = 2;
+ writer->write_int("version", version);
+
+ writer->write_string("name", name, true);
+ writer->write_string("author", author);
+ if(on_menukey_script != "")
+ writer->write_string("on-menukey-script", on_menukey_script);
+
+ for(Sectors::iterator i = sectors.begin(); i != sectors.end(); ++i) {
+ Sector* sector = *i;
+ writer->start_list("sector");
+ sector->write(*writer);
+ writer->end_list("sector");
+ }
+
+ writer->end_list("supertux-level");
+
+ delete writer;
+}
+
+Level::~Level()
+{
+ for(Sectors::iterator i = sectors.begin(); i != sectors.end(); ++i)
+ delete *i;
+}
+
+void
+Level::add_sector(Sector* sector)
+{
+ Sector* test = get_sector(sector->get_name());
+ if(test != 0) {
+ throw std::runtime_error("Trying to add 2 sectors with same name");
+ }
+ sectors.push_back(sector);
+}
+
+Sector*
+Level::get_sector(const std::string& name)
+{
+ for(Sectors::iterator i = sectors.begin(); i != sectors.end(); ++i) {
+ Sector* sector = *i;
+ if(sector->get_name() == name)
+ return sector;
+ }
+
+ return 0;
+}
+
+size_t
+Level::get_sector_count()
+{
+ return sectors.size();
+}
+
+Sector*
+Level::get_sector(size_t num)
+{
+ return sectors.at(num);
+}
+
+int
+Level::get_total_coins()
+{
+ int total_coins = 0;
+ for(Sectors::iterator i = sectors.begin(); i != sectors.end(); ++i) {
+ Sector* sector = *i;
+ for(Sector::GameObjects::iterator o = sector->gameobjects.begin();
+ o != sector->gameobjects.end(); ++o) {
+ Coin* coin = dynamic_cast<Coin*> (*o);
+ if(coin)
+ {
+ total_coins++;
+ continue;
+ }
+ BonusBlock *block = dynamic_cast<BonusBlock*> (*o);
+ if(block)
+ {
+ if (block->contents == BonusBlock::CONTENT_COIN)
+ {
+ total_coins++;
+ continue;
+ }
+#if 0
+ // FIXME: do we want this? q.v. src/object/oneup.cpp
+ else if (block->contents == BonusBlock::CONTENT_1UP)
+ {
+ total_coins += 100;
+ continue;
+ }
+#endif
+ }
+ }
+ }
+ return total_coins;
+}
+
+int
+Level::get_total_badguys()
+{
+ int total_badguys = 0;
+ for(Sectors::iterator i = sectors.begin(); i != sectors.end(); ++i)
+ total_badguys += (*i)->get_total_badguys();
+ return total_badguys;
+}
--- /dev/null
+// $Id$
+//
+// SuperTux
+// Copyright (C) 2006 Matthias Braun <matze@braunis.de>
+//
+// 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_LEVEL_H
+#define SUPERTUX_LEVEL_H
+
+#include <vector>
+#include <string>
+#include "statistics.hpp"
+#include "sector.hpp"
+
+namespace lisp {
+class Lisp;
+}
+
+class Level
+{
+public:
+ std::string name;
+ std::string author;
+ std::string contact;
+ std::string license;
+ std::string on_menukey_script;
+ typedef std::vector<Sector*> Sectors;
+ Sectors sectors;
+ Statistics stats;
+
+public:
+ Level();
+ ~Level();
+
+ // loads a levelfile
+ void load(const std::string& filename);
+ void save(const std::string& filename);
+
+ const std::string& get_name() const
+ { return name; }
+
+ const std::string& get_author() const
+ { return author; }
+
+ void add_sector(Sector* sector);
+
+ Sector* get_sector(const std::string& name);
+
+ size_t get_sector_count();
+ Sector* get_sector(size_t num);
+
+ int get_total_coins();
+ int get_total_badguys();
+
+ /** Get total number of GameObjects of given type */
+ template<class T> int get_total_count()
+ {
+ int total = 0;
+ for(Sectors::iterator i = sectors.begin(); i != sectors.end(); ++i) {
+ total += (*i)->get_total_count<T>();
+ }
+ return total;
+ }
+
+private:
+ void load_old_format(const lisp::Lisp& reader);
+};
+
+#endif /*SUPERTUX_LEVEL_H*/
--- /dev/null
+// $Id$
+//
+// SuperTux
+// Copyright (C) 2006 Matthias Braun <matze@braunis.de>
+//
+// 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 <config.h>
+
+#include "level_transformer.hpp"
+#include "level.hpp"
+
+LevelTransformer::~LevelTransformer()
+{
+}
+
+void
+LevelTransformer::transform(Level* level)
+{
+ for(size_t i = 0; i < level->get_sector_count(); ++i) {
+ transform_sector(level->get_sector(i));
+ }
+}
--- /dev/null
+// $Id$
+//
+// SuperTux
+// Copyright (C) 2006 Matthias Braun <matze@braunis.de>
+//
+// 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 __LEVEL_TRANSFORMER_H__
+#define __LEVEL_TRANSFORMER_H__
+
+class Level;
+class Sector;
+
+/**
+ * This class is an abstract interface for algorithms that transform levels in
+ * some way before they are played.
+ */
+class LevelTransformer
+{
+public:
+ virtual ~LevelTransformer();
+
+ /** transform a complete Level, the standard implementation just calls
+ * transformSector on each sector in the level.
+ */
+ virtual void transform(Level* level);
+
+ virtual void transform_sector(Sector* sector) = 0;
+};
+
+#endif
--- /dev/null
+// $Id$
+//
+// SuperTux
+// Copyright (C) 2006 Matthias Braun <matze@braunis.de>
+//
+// 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 <config.h>
+
+#include <sstream>
+#include <stdexcept>
+#include <iostream>
+
+#include "lexer.hpp"
+
+namespace lisp
+{
+
+class EOFException
+{
+};
+
+Lexer::Lexer(std::istream& newstream)
+ : stream(newstream), eof(false), linenumber(0)
+{
+ try {
+ // trigger a refill of the buffer
+ c = 0;
+ bufend = 0;
+ nextChar();
+ } catch(EOFException& ) {
+ }
+}
+
+Lexer::~Lexer()
+{
+}
+
+void
+Lexer::nextChar()
+{
+ ++c;
+ if(c >= bufend) {
+ if(eof)
+ throw EOFException();
+ stream.read(buffer, BUFFER_SIZE);
+ size_t bytes_read = stream.gcount();
+
+ c = buffer;
+ bufend = buffer + bytes_read;
+
+ // the following is a hack that appends an additional ' ' at the end of
+ // the file to avoid problems when parsing symbols/elements and a sudden
+ // EOF. This is faster than relying on unget and IMO also nicer.
+ if(bytes_read == 0 || stream.eof()) {
+ eof = true;
+ *bufend = ' ';
+ ++bufend;
+ }
+ }
+}
+
+Lexer::TokenType
+Lexer::getNextToken()
+{
+ static const char* delims = "\"();";
+
+ try {
+ while(isspace(*c)) {
+ if(*c == '\n')
+ ++linenumber;
+ nextChar();
+ };
+
+ token_length = 0;
+
+ switch(*c) {
+ case ';': // comment
+ while(true) {
+ nextChar();
+ if(*c == '\n') {
+ ++linenumber;
+ break;
+ }
+ }
+ return getNextToken(); // and again
+ case '(':
+ nextChar();
+ return TOKEN_OPEN_PAREN;
+ case ')':
+ nextChar();
+ return TOKEN_CLOSE_PAREN;
+ case '"': { // string
+ int startline = linenumber;
+ try {
+ while(1) {
+ nextChar();
+ if(*c == '"')
+ break;
+ else if (*c == '\r') // XXX this breaks with pure \r EOL
+ continue;
+ else if(*c == '\n')
+ linenumber++;
+ else if(*c == '\\') {
+ nextChar();
+ switch(*c) {
+ case 'n':
+ *c = '\n';
+ break;
+ case 't':
+ *c = '\t';
+ break;
+ }
+ }
+ if(token_length < MAX_TOKEN_LENGTH)
+ token_string[token_length++] = *c;
+ }
+ token_string[token_length] = 0;
+ } catch(EOFException& ) {
+ std::stringstream msg;
+ msg << "Parse error in line " << startline << ": "
+ << "EOF while parsing string.";
+ throw std::runtime_error(msg.str());
+ }
+ nextChar();
+ return TOKEN_STRING;
+ }
+ case '#': // constant
+ try {
+ nextChar();
+
+ while(isalnum(*c) || *c == '_') {
+ if(token_length < MAX_TOKEN_LENGTH)
+ token_string[token_length++] = *c;
+ nextChar();
+ }
+ token_string[token_length] = 0;
+ } catch(EOFException& ) {
+ std::stringstream msg;
+ msg << "Parse Error in line " << linenumber << ": "
+ << "EOF while parsing constant.";
+ throw std::runtime_error(msg.str());
+ }
+
+ if(strcmp(token_string, "t") == 0)
+ return TOKEN_TRUE;
+ if(strcmp(token_string, "f") == 0)
+ return TOKEN_FALSE;
+
+ // we only handle #t and #f constants at the moment...
+
+ {
+ std::stringstream msg;
+ msg << "Parse Error in line " << linenumber << ": "
+ << "Unknown constant '" << token_string << "'.";
+ throw std::runtime_error(msg.str());
+ }
+
+ default:
+ if(isdigit(*c) || *c == '-') {
+ bool have_nondigits = false;
+ bool have_digits = false;
+ int have_floating_point = 0;
+
+ do {
+ if(isdigit(*c))
+ have_digits = true;
+ else if(*c == '.')
+ ++have_floating_point;
+ else if(isalnum(*c) || *c == '_')
+ have_nondigits = true;
+
+ if(token_length < MAX_TOKEN_LENGTH)
+ token_string[token_length++] = *c;
+
+ nextChar();
+ } while(!isspace(*c) && !strchr(delims, *c));
+
+ token_string[token_length] = 0;
+
+ // no nextChar
+
+ 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 {
+ do {
+ if(token_length < MAX_TOKEN_LENGTH)
+ token_string[token_length++] = *c;
+ nextChar();
+ } while(!isspace(*c) && !strchr(delims, *c));
+ token_string[token_length] = 0;
+
+ // no nextChar
+
+ return TOKEN_SYMBOL;
+ }
+ }
+ } catch(EOFException& ) {
+ return TOKEN_EOF;
+ }
+}
+
+} // end of namespace lisp
--- /dev/null
+// $Id$
+//
+// SuperTux
+// Copyright (C) 2006 Matthias Braun <matze@braunis.de>
+//
+// 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 __LISPLEXER_H__
+#define __LISPLEXER_H__
+
+namespace lisp
+{
+
+class Lexer
+{
+public:
+ enum TokenType {
+ TOKEN_EOF,
+ TOKEN_OPEN_PAREN,
+ TOKEN_CLOSE_PAREN,
+ TOKEN_SYMBOL,
+ TOKEN_STRING,
+ TOKEN_INTEGER,
+ TOKEN_REAL,
+ TOKEN_TRUE,
+ TOKEN_FALSE
+ };
+
+ Lexer(std::istream& stream);
+ ~Lexer();
+
+ TokenType getNextToken();
+ const char* getString() const
+ { return token_string; }
+ int getLineNumber() const
+ { return linenumber; }
+
+private:
+ enum {
+ MAX_TOKEN_LENGTH = 16384,
+ BUFFER_SIZE = 1024
+ };
+
+ inline void nextChar();
+
+ std::istream& stream;
+ bool eof;
+ int linenumber;
+ char buffer[BUFFER_SIZE+1];
+ char* bufend;
+ char* c;
+ char token_string[MAX_TOKEN_LENGTH + 1];
+ int token_length;
+};
+
+} // end of namespace lisp
+
+#endif
--- /dev/null
+// $Id$
+//
+// SuperTux
+// Copyright (C) 2006 Matthias Braun <matze@braunis.de>
+//
+// 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 <config.h>
+
+#include "lisp.hpp"
+
+namespace lisp
+{
+
+Lisp::Lisp(LispType newtype)
+ : type(newtype)
+{
+}
+
+Lisp::~Lisp()
+{
+ // resources should be on parser obstack, so no need to delete anything
+}
+
+const Lisp*
+Lisp::get_lisp(const char* name) const
+{
+ for(const Lisp* p = this; p != 0; p = p->get_cdr()) {
+ const Lisp* child = p->get_car();
+ if(!child || child->get_type() != TYPE_CONS)
+ continue;
+ const Lisp* childname = child->get_car();
+ if(!childname)
+ continue;
+ std::string childName;
+ if(!childname->get(childName))
+ continue;
+ if(childName == name) {
+ return child->get_cdr();
+ }
+ }
+
+ return 0;
+}
+
+void
+Lisp::print(int indent) const
+{
+ for(int i = 0; i < indent; ++i)
+ printf(" ");
+
+ if(type == TYPE_CONS) {
+ printf("(\n");
+ const Lisp* lisp = this;
+ while(lisp) {
+ if(lisp->v.cons.car)
+ lisp->v.cons.car->print(indent + 1);
+ lisp = lisp->v.cons.cdr;
+ }
+ for(int i = 0; i < indent; ++i)
+ printf(" ");
+ printf(")");
+ }
+ if(type == TYPE_STRING) {
+ printf("'%s' ", v.string);
+ }
+ if(type == TYPE_INTEGER) {
+ printf("%d", v.integer);
+ }
+ if(type == TYPE_REAL) {
+ printf("%f", v.real);
+ }
+ if(type == TYPE_SYMBOL) {
+ printf("%s ", v.string);
+ }
+ if(type == TYPE_BOOLEAN) {
+ printf("%s ", v.boolean ? "true" : "false");
+ }
+ printf("\n");
+}
+
+} // end of namespace lisp
--- /dev/null
+// $Id$
+//
+// SuperTux
+// Copyright (C) 2006 Matthias Braun <matze@braunis.de>
+//
+// 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 __LISPREADER_H__
+#define __LISPREADER_H__
+
+#include <string>
+#include <vector>
+#include <assert.h>
+
+namespace lisp
+{
+
+class Lisp
+{
+public:
+ ~Lisp();
+
+ enum LispType {
+ TYPE_CONS,
+ TYPE_SYMBOL,
+ TYPE_INTEGER,
+ TYPE_STRING,
+ TYPE_REAL,
+ TYPE_BOOLEAN
+ };
+
+ LispType get_type() const
+ { return type; }
+
+ const Lisp* get_car() const
+ { return v.cons.car; }
+ const Lisp* get_cdr() const
+ { return v.cons.cdr; }
+
+ bool get(std::string& val) const
+ {
+ if(type != TYPE_STRING && type != TYPE_SYMBOL)
+ return false;
+ val = v.string;
+ return true;
+ }
+
+ std::string get_symbol() const
+ {
+ assert(type == TYPE_SYMBOL);
+ return v.string;
+ }
+
+ std::string get_string() const
+ {
+ assert(type == TYPE_STRING);
+ return v.string;
+ }
+
+ bool get(unsigned int& val) const
+ {
+ if(type != TYPE_INTEGER)
+ return false;
+ val = v.integer;
+ return true;
+ }
+
+ bool get(int& val) const
+ {
+ if(type != TYPE_INTEGER)
+ return false;
+ val = v.integer;
+ return true;
+ }
+
+ int get_int() const
+ {
+ assert(type == TYPE_INTEGER);
+ return v.integer;
+ }
+
+ bool get(float& val) const
+ {
+ if(type != TYPE_REAL) {
+ if(type == TYPE_INTEGER) {
+ val = (float) v.integer;
+ return true;
+ }
+ return false;
+ }
+ val = v.real;
+ return true;
+ }
+
+ float get_float() const
+ {
+ assert(type == TYPE_REAL);
+ return v.real;
+ }
+
+ bool get(bool& val) const
+ {
+ if(type != TYPE_BOOLEAN)
+ return false;
+ val = v.boolean;
+ return true;
+ }
+
+ bool get_bool() const
+ {
+ assert(type == TYPE_BOOLEAN);
+ return v.boolean;
+ }
+
+ /** conveniance functions which traverse the list until a child with a
+ * specified name is found. The value part is then interpreted in a specific
+ * way. The functions return true, if a child was found and could be
+ * interpreted correctly, otherwise false is returned and the variable value
+ * is not changed.
+ * (Please note that searching the lisp structure is O(n) so these functions
+ * are no good idea for performance critical areas)
+ */
+ template<class T>
+ bool get(const char* name, T& val) const
+ {
+ const Lisp* lisp = get_lisp(name);
+ if(!lisp)
+ return false;
+
+ if(lisp->get_type() != TYPE_CONS)
+ return false;
+ lisp = lisp->get_car();
+ if(!lisp)
+ return false;
+ return lisp->get(val);
+ }
+
+ template<class T>
+ bool get_vector(const char* name, std::vector<T>& vec) const
+ {
+ vec.clear();
+
+ const Lisp* child = get_lisp(name);
+ if(!child)
+ return false;
+
+ for( ; child != 0; child = child->get_cdr()) {
+ T val;
+ if(!child->get_car())
+ continue;
+ if(child->get_car()->get(val)) {
+ vec.push_back(val);
+ }
+ }
+
+ return true;
+ }
+
+ const Lisp* get_lisp(const char* name) const;
+ const Lisp* get_lisp(const std::string& name) const
+ { return get_lisp(name.c_str()); }
+
+ // for debugging
+ void print(int indent = 0) const;
+
+private:
+ friend class Parser;
+ Lisp(LispType newtype);
+
+ LispType type;
+ union
+ {
+ struct
+ {
+ const Lisp* car;
+ const Lisp* cdr;
+ } cons;
+
+ char* string;
+ int integer;
+ bool boolean;
+ float real;
+ } v;
+};
+
+} // end of namespace lisp
+
+#endif
--- /dev/null
+// $Id$
+//
+// SuperTux
+// Copyright (C) 2006 Matthias Braun <matze@braunis.de>
+//
+// 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 <config.h>
+
+#include "list_iterator.hpp"
+#include <stdexcept>
+
+namespace lisp
+{
+
+ListIterator::ListIterator(const lisp::Lisp* newlisp)
+ : current_lisp(0), cur(newlisp)
+{
+}
+
+bool
+ListIterator::next()
+{
+ if(cur == 0)
+ return false;
+
+ const lisp::Lisp* child = cur->get_car();
+ if(!child)
+ throw std::runtime_error("child is 0 in list entry");
+ if(child->get_type() != lisp::Lisp::TYPE_CONS)
+ throw std::runtime_error("Expected CONS");
+ const lisp::Lisp* name = child->get_car();
+ if(!name || (
+ name->get_type() != lisp::Lisp::TYPE_SYMBOL
+ && name->get_type() != lisp::Lisp::TYPE_STRING))
+ throw std::runtime_error("Expected symbol");
+ name->get(current_item);
+ current_lisp = child->get_cdr();
+
+ cur = cur->get_cdr();
+ return true;
+}
+
+}
--- /dev/null
+// $Id$
+//
+// SuperTux
+// Copyright (C) 2006 Matthias Braun <matze@braunis.de>
+//
+// 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 __LISP_ITERATOR_H__
+#define __LISP_ITERATOR_H__
+
+#include "lisp/lisp.hpp"
+
+namespace lisp
+{
+
+/**
+ * Small and a bit hacky helper class that helps parsing lisp lists where all
+ * entries are lists again themselves
+ */
+class ListIterator
+{
+public:
+ ListIterator(const lisp::Lisp* cur);
+
+ const std::string& item() const
+ { return current_item; }
+ const lisp::Lisp* lisp() const
+ { return current_lisp; }
+ const lisp::Lisp* value() const
+ { return current_lisp->get_car(); }
+ bool next();
+
+private:
+ std::string current_item;
+ const lisp::Lisp* current_lisp;
+ const lisp::Lisp* cur;
+};
+
+}
+
+#endif
--- /dev/null
+// $Id$
+//
+// SuperTux
+// Copyright (C) 2006 Matthias Braun <matze@braunis.de>
+//
+// 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 <config.h>
+
+#include <sstream>
+#include <stdexcept>
+#include <fstream>
+#include <cassert>
+#include <iostream>
+
+#include "tinygettext/tinygettext.hpp"
+#include "physfs/physfs_stream.hpp"
+#include "parser.hpp"
+#include "lisp.hpp"
+#include "obstack/obstackpp.hpp"
+
+#include "gameconfig.hpp"
+
+namespace lisp
+{
+
+Parser::Parser(bool translate)
+ : lexer(0), dictionary_manager(0), dictionary(0)
+{
+ if(translate) {
+ dictionary_manager = new TinyGetText::DictionaryManager();
+ dictionary_manager->set_charset("UTF-8");
+ if (config && (config->locale != "")) dictionary_manager->set_language(config->locale);
+ }
+
+ obstack_init(&obst);
+}
+
+Parser::~Parser()
+{
+ obstack_free(&obst, NULL);
+ delete lexer;
+ delete dictionary_manager;
+}
+
+static std::string dirname(const std::string& filename)
+{
+ std::string::size_type p = filename.find_last_of('/');
+ if(p == std::string::npos)
+ return "";
+
+ return filename.substr(0, p+1);
+}
+
+const Lisp*
+Parser::parse(const std::string& filename)
+{
+ IFileStreambuf ins(filename);
+ std::istream in(&ins);
+
+ if(!in.good()) {
+ std::stringstream msg;
+ msg << "Parser problem: Couldn't open file '" << filename << "'.";
+ throw std::runtime_error(msg.str());
+ }
+
+ if(dictionary_manager) {
+ dictionary_manager->add_directory(dirname(filename));
+ dictionary = & (dictionary_manager->get_dictionary());
+ }
+
+ return parse(in, filename);
+}
+
+const Lisp*
+Parser::parse(std::istream& stream, const std::string& sourcename)
+{
+ delete lexer;
+ lexer = new Lexer(stream);
+
+ this->filename = sourcename;
+ token = lexer->getNextToken();
+
+ Lisp* result = new(obst) Lisp(Lisp::TYPE_CONS);
+ result->v.cons.car = read();
+ result->v.cons.cdr = 0;
+
+ delete lexer;
+ lexer = 0;
+
+ return result;
+}
+
+void
+Parser::parse_error(const char* msg) const
+{
+ std::stringstream emsg;
+ emsg << "Parse Error at '" << filename << "' line " << lexer->getLineNumber()
+ << ": " << msg;
+ throw std::runtime_error(emsg.str());
+}
+
+const Lisp*
+Parser::read()
+{
+ Lisp* result;
+ switch(token) {
+ case Lexer::TOKEN_EOF: {
+ parse_error("Unexpected EOF.");
+ }
+ case Lexer::TOKEN_CLOSE_PAREN: {
+ parse_error("Unexpected ')'.");
+ }
+ case Lexer::TOKEN_OPEN_PAREN: {
+ result = new(obst) Lisp(Lisp::TYPE_CONS);
+
+ token = lexer->getNextToken();
+ if(token == Lexer::TOKEN_CLOSE_PAREN) {
+ result->v.cons.car = 0;
+ result->v.cons.cdr = 0;
+ break;
+ }
+
+ if(token == Lexer::TOKEN_SYMBOL &&
+ strcmp(lexer->getString(), "_") == 0) {
+ // evaluate translation function (_ str) in place here
+ token = lexer->getNextToken();
+ if(token != Lexer::TOKEN_STRING)
+ parse_error("Expected string after '(_'");
+
+ result = new(obst) Lisp(Lisp::TYPE_STRING);
+ if(dictionary) {
+ std::string translation = dictionary->translate(lexer->getString());
+ result->v.string = new(obst) char[translation.size()+1];
+ memcpy(result->v.string, translation.c_str(), translation.size()+1);
+ } else {
+ size_t len = strlen(lexer->getString()) + 1;
+ result->v.string = new(obst) char[len];
+ memcpy(result->v.string, lexer->getString(), len);
+ }
+ token = lexer->getNextToken();
+ if(token != Lexer::TOKEN_CLOSE_PAREN)
+ parse_error("Expected ')' after '(_ string'");
+ break;
+ }
+
+ Lisp* cur = result;
+ do {
+ cur->v.cons.car = read();
+ if(token == Lexer::TOKEN_CLOSE_PAREN) {
+ cur->v.cons.cdr = 0;
+ break;
+ }
+ Lisp *newcur = new(obst) Lisp(Lisp::TYPE_CONS);
+ cur->v.cons.cdr = newcur;
+ cur = newcur;
+ } while(1);
+
+ break;
+ }
+ case Lexer::TOKEN_SYMBOL: {
+ result = new(obst) Lisp(Lisp::TYPE_SYMBOL);
+ size_t len = strlen(lexer->getString()) + 1;
+ result->v.string = new(obst) char[len];
+ memcpy(result->v.string, lexer->getString(), len);
+ break;
+ }
+ case Lexer::TOKEN_STRING: {
+ result = new(obst) Lisp(Lisp::TYPE_STRING);
+ size_t len = strlen(lexer->getString()) + 1;
+ result->v.string = new(obst) char[len];
+ memcpy(result->v.string, lexer->getString(), len);
+ break;
+ }
+ case Lexer::TOKEN_INTEGER:
+ result = new(obst) Lisp(Lisp::TYPE_INTEGER);
+ sscanf(lexer->getString(), "%d", &result->v.integer);
+ break;
+ case Lexer::TOKEN_REAL:
+ result = new(obst) Lisp(Lisp::TYPE_REAL);
+ sscanf(lexer->getString(), "%f", &result->v.real);
+ break;
+ case Lexer::TOKEN_TRUE:
+ result = new(obst) Lisp(Lisp::TYPE_BOOLEAN);
+ result->v.boolean = true;
+ break;
+ case Lexer::TOKEN_FALSE:
+ result = new(obst) Lisp(Lisp::TYPE_BOOLEAN);
+ result->v.boolean = false;
+ break;
+
+ default:
+ // this should never happen
+ assert(false);
+ }
+
+ token = lexer->getNextToken();
+ return result;
+}
+
+} // end of namespace lisp
--- /dev/null
+// $Id$
+//
+// SuperTux
+// Copyright (C) 2006 Matthias Braun <matze@braunis.de>
+//
+// 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 __LISPPARSER_H__
+#define __LISPPARSER_H__
+
+#include <string>
+#include "lexer.hpp"
+#include "obstack/obstack.h"
+
+namespace TinyGetText {
+class Dictionary;
+class DictionaryManager;
+}
+
+namespace lisp
+{
+
+class Lisp;
+class LispFile;
+
+class Parser
+{
+public:
+ Parser(bool translate = true);
+ ~Parser();
+
+ /**
+ * Parses a lispfile and returns the s-expression structure.
+ * Note that all memory is held by the parser so don't destroy the parser
+ * before you are finished with the lisp tree
+ */
+ const Lisp* parse(const std::string& filename);
+ /**
+ * Same as parse but reads from a generic std::istream. The sourcename is
+ * used for errormessages to indicate the source of the data.
+ */
+ const Lisp* parse(std::istream& stream, const std::string& sourcename);
+
+private:
+ void parse_error(const char* msg) const;
+ const Lisp* read();
+
+ Lexer* lexer;
+ std::string filename;
+ TinyGetText::DictionaryManager* dictionary_manager;
+ TinyGetText::Dictionary* dictionary;
+ Lexer::TokenType token;
+
+ struct obstack obst;
+};
+
+} // end of namespace lisp
+
+#endif
--- /dev/null
+// $Id$
+//
+// SuperTux
+// Copyright (C) 2006 Matthias Braun <matze@braunis.de>
+//
+// 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 <config.h>
+
+#include <iostream>
+
+#include "writer.hpp"
+#include "physfs/physfs_stream.hpp"
+#include "log.hpp"
+
+namespace lisp
+{
+
+Writer::Writer(const std::string& filename)
+{
+ out = new OFileStream(filename);
+ out_owned = true;
+ indent_depth = 0;
+ out->precision(10);
+}
+
+Writer::Writer(std::ostream* newout)
+{
+ out = newout;
+ out_owned = false;
+ indent_depth = 0;
+ out->precision(10);
+}
+
+Writer::~Writer()
+{
+ if(lists.size() > 0) {
+ log_warning << "Not all sections closed in lispwriter" << std::endl;
+ }
+ if(out_owned)
+ delete out;
+}
+
+void
+Writer::write_comment(const std::string& comment)
+{
+ *out << "; " << comment << "\n";
+}
+
+void
+Writer::start_list(const std::string& listname, bool string)
+{
+ indent();
+ *out << '(';
+ if(string)
+ write_escaped_string(listname);
+ else
+ *out << listname;
+ *out << '\n';
+ indent_depth += 2;
+
+ lists.push_back(listname);
+}
+
+void
+Writer::end_list(const std::string& listname)
+{
+ if(lists.size() == 0) {
+ log_warning << "Trying to close list '" << listname << "', which is not open" << std::endl;
+ return;
+ }
+ if(lists.back() != listname) {
+ log_warning << "trying to close list '" << listname << "' while list '" << lists.back() << "' is open" << std::endl;
+ return;
+ }
+ lists.pop_back();
+
+ indent_depth -= 2;
+ indent();
+ *out << ")\n";
+}
+
+void
+Writer::write_int(const std::string& name, int value)
+{
+ indent();
+ *out << '(' << name << ' ' << value << ")\n";
+}
+
+void
+Writer::write_float(const std::string& name, float value)
+{
+ indent();
+ *out << '(' << name << ' ' << value << ")\n";
+}
+
+void
+Writer::write_string(const std::string& name, const std::string& value,
+ bool translatable)
+{
+ indent();
+ *out << '(' << name;
+ if(translatable) {
+ *out << " (_ ";
+ write_escaped_string(value);
+ *out << "))\n";
+ } else {
+ *out << " ";
+ write_escaped_string(value);
+ *out << ")\n";
+ }
+}
+
+void
+Writer::write_bool(const std::string& name, bool value)
+{
+ indent();
+ *out << '(' << name << ' ' << (value ? "#t" : "#f") << ")\n";
+}
+
+void
+Writer::write_int_vector(const std::string& name,
+ const std::vector<int>& value)
+{
+ indent();
+ *out << '(' << name;
+ for(std::vector<int>::const_iterator i = value.begin(); i != value.end(); ++i)
+ *out << " " << *i;
+ *out << ")\n";
+}
+
+void
+Writer::write_int_vector(const std::string& name,
+ const std::vector<unsigned int>& value)
+{
+ indent();
+ *out << '(' << name;
+ for(std::vector<unsigned int>::const_iterator i = value.begin(); i != value.end(); ++i)
+ *out << " " << *i;
+ *out << ")\n";
+}
+
+void
+Writer::write_float_vector(const std::string& name,
+ const std::vector<float>& value)
+{
+ indent();
+ *out << '(' << name;
+ for(std::vector<float>::const_iterator i = value.begin(); i != value.end(); ++i)
+ *out << " " << *i;
+ *out << ")\n";
+}
+
+void
+Writer::write_escaped_string(const std::string& str)
+{
+ *out << '"';
+ for(const char* c = str.c_str(); *c != 0; ++c) {
+ if(*c == '\"')
+ *out << "\\\"";
+ else if(*c == '\\')
+ *out << "\\\\";
+ else
+ *out << *c;
+ }
+ *out << '"';
+}
+
+void
+Writer::indent()
+{
+ for(int i = 0; i<indent_depth; ++i)
+ *out << ' ';
+}
+
+} // end of namespace lisp
--- /dev/null
+// $Id$
+//
+// SuperTux
+// Copyright (C) 2006 Matthias Braun <matze@braunis.de>
+//
+// 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_LISPWRITER_H
+#define SUPERTUX_LISPWRITER_H
+
+#include <iostream>
+#include <string>
+#include <vector>
+
+namespace lisp
+{
+
+ class Writer
+ {
+ public:
+ Writer(const std::string& filename);
+ Writer(std::ostream* out);
+ ~Writer();
+
+ void write_comment(const std::string& comment);
+
+ void start_list(const std::string& listname, bool string = false);
+
+ 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,
+ bool translatable = false);
+ void write_bool(const std::string& name, bool value);
+ void write_int_vector(const std::string& name, const std::vector<int>& value);
+ void write_int_vector(const std::string& name, const std::vector<unsigned int>& value);
+ void write_float_vector(const std::string& name, const std::vector<float>& value);
+ // add more write-functions when needed...
+
+ void end_list(const std::string& listname);
+
+ private:
+ void write_escaped_string(const std::string& str);
+ void indent();
+
+ std::ostream* out;
+ bool out_owned;
+ int indent_depth;
+ std::vector<std::string> lists;
+ };
+
+} //namespace lisp
+
+#endif //SUPERTUX_LISPWRITER_H
--- /dev/null
+// $Id$
+//
+// SuperTux Debug Helper Functions
+// Copyright (C) 2006 Christoph Sommer <christoph.sommer@2006.expires.deltadevelopment.de>
+//
+// 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 <config.h>
+
+#include "log.hpp"
+#include "math/vector.hpp"
+#include "math/rect.hpp"
+
+std::ostream& operator<<(std::ostream& out, const Vector& vector)
+{
+ out << '[' << vector.x << ',' << vector.y << ']';
+ return out;
+}
+
+std::ostream& operator<<(std::ostream& out, const Rect& rect)
+{
+ out << "[" << rect.get_left() << "," << rect.get_top() << " "
+ << rect.get_right() << "," << rect.get_bottom() << "]";
+ return out;
+}
--- /dev/null
+// $Id$
+//
+// SuperTux Debug Helper Functions
+// Copyright (C) 2006 Christoph Sommer <christoph.sommer@2006.expires.deltadevelopment.de>
+//
+// 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_MSG_H__
+#define __SUPERTUX_MSG_H__
+
+#include <iostream>
+#include <stdio.h>
+
+#include "console.hpp"
+
+#ifdef DEBUG
+
+namespace {
+
+inline std::ostream& log_debug_f(const char* file, int line) {
+ Console::output << "[DEBUG] " << file << ":" << line << " ";
+ return Console::output;
+}
+
+inline std::ostream& log_info_f(const char* file, int line) {
+ Console::output << "[INFO] " << file << ":" << line << " ";
+ return Console::output;
+}
+
+inline std::ostream& log_warning_f(const char* file, int line) {
+ Console::output << "[WARNING] " << file << ":" << line << " ";
+ return Console::output;
+}
+
+inline std::ostream& log_fatal_f(const char* file, int line) {
+ Console::output << "[FATAL] " << file << ":" << line << " ";
+ return Console::output;
+}
+
+}
+
+#define log_debug log_debug_f(__FILE__, __LINE__)
+#define log_info log_info_f(__FILE__, __LINE__)
+#define log_warning log_warning_f(__FILE__, __LINE__)
+#define log_fatal log_fatal_f(__FILE__, __LINE__)
+
+#else
+
+namespace {
+
+inline std::ostream& log_warning_f() {
+ Console::output << "Warning: ";
+ return Console::output;
+}
+
+inline std::ostream& log_fatal_f() {
+ Console::output << "Fatal: ";
+ return Console::output;
+}
+
+}
+
+#define log_debug if (0) std::cerr
+#define log_info std::cout
+#define log_warning log_warning_f()
+#define log_fatal log_fatal_f()
+
+#endif
+
+class Vector;
+std::ostream& operator<< (std::ostream& str, const Vector& vector);
+class Rect;
+std::ostream& operator<< (std::ostream& str, const Rect& rect);
+
+#endif
--- /dev/null
+// $Id$
+//
+// SuperTux
+// Copyright (C) 2006 Matthias Braun <matze@braunis.de>
+//
+// 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 <config.h>
+#include <assert.h>
+
+#include "log.hpp"
+#include "main.hpp"
+
+#include <stdexcept>
+#include <sstream>
+#include <ctime>
+#include <cstdlib>
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <unistd.h>
+#include <physfs.h>
+#include <SDL.h>
+#include <SDL_image.h>
+
+#ifdef MACOSX
+namespace supertux_apple {
+#include <CoreFoundation/CoreFoundation.h>
+}
+#endif
+
+#include "gameconfig.hpp"
+#include "resources.hpp"
+#include "gettext.hpp"
+#include "audio/sound_manager.hpp"
+#include "video/surface.hpp"
+#include "video/texture_manager.hpp"
+#include "video/drawing_context.hpp"
+#include "video/glutil.hpp"
+#include "control/joystickkeyboardcontroller.hpp"
+#include "options_menu.hpp"
+#include "mainloop.hpp"
+#include "title.hpp"
+#include "game_session.hpp"
+#include "scripting/level.hpp"
+#include "scripting/squirrel_util.hpp"
+#include "file_system.hpp"
+#include "physfs/physfs_sdl.hpp"
+#include "random_generator.hpp"
+#include "worldmap/worldmap.hpp"
+#include "binreloc/binreloc.h"
+
+namespace { DrawingContext *context_pointer; }
+SDL_Surface *screen;
+JoystickKeyboardController* main_controller = 0;
+TinyGetText::DictionaryManager dictionary_manager;
+
+int SCREEN_WIDTH;
+int SCREEN_HEIGHT;
+
+static void init_config()
+{
+ config = new Config();
+ try {
+ config->load();
+ } catch(std::exception& e) {
+ log_info << "Couldn't load config file: " << e.what() << ", using default settings" << std::endl;
+ }
+}
+
+static void init_tinygettext()
+{
+ dictionary_manager.add_directory("locale");
+ dictionary_manager.set_charset("UTF-8");
+
+ // Config setting "locale" overrides language detection
+ if (config->locale != "") {
+ dictionary_manager.set_language( config->locale );
+ }
+}
+
+static void init_physfs(const char* argv0)
+{
+ if(!PHYSFS_init(argv0)) {
+ std::stringstream msg;
+ msg << "Couldn't initialize physfs: " << PHYSFS_getLastError();
+ throw std::runtime_error(msg.str());
+ }
+
+ // Initialize physfs (this is a slightly modified version of
+ // PHYSFS_setSaneConfig
+ const char* application = "supertux2"; //instead of PACKAGE_NAME so we can coexist with MS1
+ const char* userdir = PHYSFS_getUserDir();
+ const char* dirsep = PHYSFS_getDirSeparator();
+ char* writedir = new char[strlen(userdir) + strlen(application) + 2];
+
+ // Set configuration directory
+ sprintf(writedir, "%s.%s", userdir, application);
+ if(!PHYSFS_setWriteDir(writedir)) {
+ // try to create the directory
+ char* mkdir = new char[strlen(application) + 2];
+ sprintf(mkdir, ".%s", application);
+ if(!PHYSFS_setWriteDir(userdir) || !PHYSFS_mkdir(mkdir)) {
+ std::ostringstream msg;
+ msg << "Failed creating configuration directory '"
+ << writedir << "': " << PHYSFS_getLastError();
+ delete[] writedir;
+ delete[] mkdir;
+ throw std::runtime_error(msg.str());
+ }
+ delete[] mkdir;
+
+ if(!PHYSFS_setWriteDir(writedir)) {
+ std::ostringstream msg;
+ msg << "Failed to use configuration directory '"
+ << writedir << "': " << PHYSFS_getLastError();
+ delete[] writedir;
+ throw std::runtime_error(msg.str());
+ }
+ }
+ PHYSFS_addToSearchPath(writedir, 0);
+ delete[] writedir;
+
+ // Search for archives and add them to the search path
+ const char* archiveExt = "zip";
+ char** rc = PHYSFS_enumerateFiles("/");
+ size_t extlen = strlen(archiveExt);
+
+ for(char** i = rc; *i != 0; ++i) {
+ size_t l = strlen(*i);
+ if((l > extlen) && ((*i)[l - extlen - 1] == '.')) {
+ const char* ext = (*i) + (l - extlen);
+ if(strcasecmp(ext, archiveExt) == 0) {
+ const char* d = PHYSFS_getRealDir(*i);
+ char* str = new char[strlen(d) + strlen(dirsep) + l + 1];
+ sprintf(str, "%s%s%s", d, dirsep, *i);
+ PHYSFS_addToSearchPath(str, 1);
+ delete[] str;
+ }
+ }
+ }
+
+ PHYSFS_freeList(rc);
+
+ // when started from source dir...
+ std::string dir = PHYSFS_getBaseDir();
+ dir += "/data";
+ std::string testfname = dir;
+ testfname += "/credits.txt";
+ bool sourcedir = false;
+ FILE* f = fopen(testfname.c_str(), "r");
+ if(f) {
+ fclose(f);
+ if(!PHYSFS_addToSearchPath(dir.c_str(), 1)) {
+ log_warning << "Couldn't add '" << dir << "' to physfs searchpath: " << PHYSFS_getLastError() << std::endl;
+ } else {
+ sourcedir = true;
+ }
+ }
+
+#ifdef MACOSX
+{
+ using namespace supertux_apple;
+
+ // when started from Application file on Mac OS X...
+ char path[PATH_MAX];
+ CFBundleRef mainBundle = CFBundleGetMainBundle();
+ assert(mainBundle != 0);
+ CFURLRef mainBundleURL = CFBundleCopyBundleURL(mainBundle);
+ assert(mainBundleURL != 0);
+ CFStringRef pathStr = CFURLCopyFileSystemPath(mainBundleURL, kCFURLPOSIXPathStyle);
+ assert(pathStr != 0);
+ CFStringGetCString(pathStr, path, PATH_MAX, kCFStringEncodingUTF8);
+ CFRelease(mainBundleURL);
+ CFRelease(pathStr);
+
+ dir = std::string(path) + "/Contents/Resources/data";
+ testfname = dir + "/credits.txt";
+ sourcedir = false;
+ f = fopen(testfname.c_str(), "r");
+ if(f) {
+ fclose(f);
+ if(!PHYSFS_addToSearchPath(dir.c_str(), 1)) {
+ log_warning << "Couldn't add '" << dir << "' to physfs searchpath: " << PHYSFS_getLastError() << std::endl;
+ } else {
+ sourcedir = true;
+ }
+ }
+}
+#endif
+
+#ifdef _WIN32
+ PHYSFS_addToSearchPath(".\\data", 1);
+#endif
+
+ if(!sourcedir) {
+#if defined(APPDATADIR) || defined(ENABLE_BINRELOC)
+ std::string datadir;
+#ifdef ENABLE_BINRELOC
+
+ char* dir;
+ br_init (NULL);
+ dir = br_find_data_dir(APPDATADIR);
+ datadir = dir;
+ free(dir);
+
+#else
+ datadir = APPDATADIR;
+#endif
+ datadir += "/";
+ datadir += application;
+ if(!PHYSFS_addToSearchPath(datadir.c_str(), 1)) {
+ log_warning << "Couldn't add '" << datadir << "' to physfs searchpath: " << PHYSFS_getLastError() << std::endl;
+ }
+#endif
+ }
+
+ // allow symbolic links
+ PHYSFS_permitSymbolicLinks(1);
+
+ //show search Path
+ char** searchpath = PHYSFS_getSearchPath();
+ for(char** i = searchpath; *i != NULL; i++)
+ log_info << "[" << *i << "] is in the search path" << std::endl;
+ PHYSFS_freeList(searchpath);
+}
+
+static void print_usage(const char* argv0)
+{
+ fprintf(stderr, _("Usage: %s [OPTIONS] [LEVELFILE]\n\n"), argv0);
+ fprintf(stderr,
+ _("Options:\n"
+ " -f, --fullscreen Run in fullscreen mode\n"
+ " -w, --window Run in window mode\n"
+ " -g, --geometry WIDTHxHEIGHT Run SuperTux in given resolution\n"
+ " -a, --aspect WIDTH:HEIGHT Run SuperTux with given aspect ratio\n"
+ " --disable-sfx Disable sound effects\n"
+ " --disable-music Disable music\n"
+ " --help Show this help message\n"
+ " --version Display SuperTux version and quit\n"
+ " --console Enable ingame scripting console\n"
+ " --noconsole Disable ingame scripting console\n"
+ " --show-fps Display framerate in levels\n"
+ " --no-show-fps Do not display framerate in levels\n"
+ " --record-demo FILE LEVEL Record a demo to FILE\n"
+ " --play-demo FILE LEVEL Play a recorded demo\n"
+ "\n"));
+}
+
+/**
+ * Options that should be evaluated prior to any initializations at all go here
+ */
+static bool pre_parse_commandline(int argc, char** argv)
+{
+ for(int i = 1; i < argc; ++i) {
+ std::string arg = argv[i];
+
+ if(arg == "--version") {
+ std::cout << PACKAGE_NAME << " " << PACKAGE_VERSION << std::endl;
+ return true;
+ }
+ }
+
+ return false;
+}
+
+/**
+ * Options that should be evaluated after config is read go here
+ */
+static bool parse_commandline(int argc, char** argv)
+{
+ for(int i = 1; i < argc; ++i) {
+ std::string arg = argv[i];
+
+ if(arg == "--help") {
+ print_usage(argv[0]);
+ return true;
+ } else if(arg == "--fullscreen" || arg == "-f") {
+ config->use_fullscreen = true;
+ } else if(arg == "--window" || arg == "-w") {
+ config->use_fullscreen = false;
+ } else if(arg == "--geometry" || arg == "-g") {
+ if(i+1 >= argc) {
+ print_usage(argv[0]);
+ throw std::runtime_error("Need to specify a parameter for geometry switch");
+ }
+ if(sscanf(argv[++i], "%dx%d", &config->screenwidth, &config->screenheight)
+ != 2) {
+ print_usage(argv[0]);
+ throw std::runtime_error("Invalid geometry spec, should be WIDTHxHEIGHT");
+ }
+ } else if(arg == "--aspect" || arg == "-a") {
+ if(i+1 >= argc) {
+ print_usage(argv[0]);
+ throw std::runtime_error("Need to specify a parameter for aspect switch");
+ }
+ if(strcasecmp(argv[i+1], "auto") == 0) {
+ i++;
+ config->aspect_ratio = -1;
+ } else {
+ int aspect_width, aspect_height;
+ if(sscanf(argv[++i], "%d:%d", &aspect_width, &aspect_height) != 2) {
+ print_usage(argv[0]);
+ throw std::runtime_error("Invalid aspect spec, should be WIDTH:HEIGHT");
+ }
+ config->aspect_ratio = static_cast<double>(aspect_width) /
+ static_cast<double>(aspect_height);
+ }
+ } else if(arg == "--show-fps") {
+ config->show_fps = true;
+ } else if(arg == "--no-show-fps") {
+ config->show_fps = false;
+ } else if(arg == "--console") {
+ config->console_enabled = true;
+ } else if(arg == "--noconsole") {
+ config->console_enabled = false;
+ } else if(arg == "--disable-sfx") {
+ config->sound_enabled = false;
+ } else if(arg == "--disable-music") {
+ config->music_enabled = false;
+ } else if(arg == "--play-demo") {
+ if(i+1 >= argc) {
+ print_usage(argv[0]);
+ throw std::runtime_error("Need to specify a demo filename");
+ }
+ config->start_demo = argv[++i];
+ } else if(arg == "--record-demo") {
+ if(i+1 >= argc) {
+ print_usage(argv[0]);
+ throw std::runtime_error("Need to specify a demo filename");
+ }
+ config->record_demo = argv[++i];
+ } else if(arg == "-d") {
+ config->enable_script_debugger = true;
+ } else if(arg[0] != '-') {
+ config->start_level = arg;
+ } else {
+ log_warning << "Unknown option '" << arg << "'. Use --help to see a list of options" << std::endl;
+ return true;
+ }
+ }
+
+ return false;
+}
+
+static void init_sdl()
+{
+ if(SDL_Init(SDL_INIT_TIMER | SDL_INIT_VIDEO | SDL_INIT_JOYSTICK) < 0) {
+ std::stringstream msg;
+ msg << "Couldn't initialize SDL: " << SDL_GetError();
+ throw std::runtime_error(msg.str());
+ }
+ // just to be sure
+ atexit(SDL_Quit);
+
+ SDL_EnableUNICODE(1);
+
+ // wait 100ms and clear SDL event queue because sometimes we have random
+ // joystick events in the queue on startup...
+ SDL_Delay(100);
+ SDL_Event dummy;
+ while(SDL_PollEvent(&dummy))
+ ;
+}
+
+static void init_rand()
+{
+ config->random_seed = systemRandom.srand(config->random_seed);
+
+ //const char *how = config->random_seed? ", user fixed.": ", from time().";
+ //log_info << "Using random seed " << config->random_seed << how << std::endl;
+}
+
+void init_video()
+{
+ static int desktop_width = 0;
+ static int desktop_height = 0;
+
+/* unfortunately only newer SDLs have these infos */
+#if SDL_MAJOR_VERSION > 1 || SDL_MINOR_VERSION > 2 || (SDL_MINOR_VERSION == 2 && SDL_PATCHLEVEL >= 10)
+ /* find which resolution the user normally uses */
+ if(desktop_width == 0) {
+ const SDL_VideoInfo *info = SDL_GetVideoInfo();
+ desktop_width = info->current_w;
+ desktop_height = info->current_h;
+ }
+#endif
+
+ double aspect_ratio = config->aspect_ratio;
+
+ // try to guess aspect ratio of monitor if needed
+ if (aspect_ratio <= 0) {
+// TODO: commented out because
+// 1) it tends to guess wrong if widescreen-monitors don't stretch 800x600 to fit, but just display black borders
+// 2) aspect ratios other than 4:3 are largely untested
+/*
+ if(config->use_fullscreen && desktop_width > 0) {
+ aspect_ratio = static_cast<double>(desktop_width) / static_cast<double>(desktop_height);
+ } else {
+*/
+ aspect_ratio = 4.0 / 3.0;
+/*
+ }
+*/
+ }
+
+ // use aspect ratio to calculate logical resolution
+ if (aspect_ratio > 1) {
+ SCREEN_WIDTH = static_cast<int> (600 * aspect_ratio + 0.5);
+ SCREEN_HEIGHT = 600;
+ } else {
+ SCREEN_WIDTH = 600;
+ SCREEN_HEIGHT = static_cast<int> (600 * 1/aspect_ratio + 0.5);
+ }
+
+ context_pointer->init_renderer();
+ screen = SDL_GetVideoSurface();
+
+ SDL_WM_SetCaption(PACKAGE_NAME " " PACKAGE_VERSION, 0);
+
+ // set icon
+ SDL_Surface* icon = IMG_Load_RW(
+ get_physfs_SDLRWops("images/engine/icons/supertux.xpm"), true);
+ if(icon != 0) {
+ SDL_WM_SetIcon(icon, 0);
+ SDL_FreeSurface(icon);
+ }
+#ifdef DEBUG
+ else {
+ log_warning << "Couldn't find icon 'images/engine/icons/supertux.xpm'" << std::endl;
+ }
+#endif
+
+ SDL_ShowCursor(0);
+
+ log_info << (config->use_fullscreen?"fullscreen ":"window ") << SCREEN_WIDTH << "x" << SCREEN_HEIGHT << " Ratio: " << aspect_ratio << "\n";
+}
+
+static void init_audio()
+{
+ sound_manager = new SoundManager();
+
+ sound_manager->enable_sound(config->sound_enabled);
+ sound_manager->enable_music(config->music_enabled);
+}
+
+static void quit_audio()
+{
+ if(sound_manager != NULL) {
+ delete sound_manager;
+ sound_manager = NULL;
+ }
+}
+
+void wait_for_event(float min_delay, float max_delay)
+{
+ assert(min_delay <= max_delay);
+
+ Uint32 min = (Uint32) (min_delay * 1000);
+ Uint32 max = (Uint32) (max_delay * 1000);
+
+ Uint32 ticks = SDL_GetTicks();
+ while(SDL_GetTicks() - ticks < min) {
+ SDL_Delay(10);
+ sound_manager->update();
+ }
+
+ // clear event queue
+ SDL_Event event;
+ while (SDL_PollEvent(&event))
+ {}
+
+ /* Handle events: */
+ bool running = false;
+ ticks = SDL_GetTicks();
+ while(running) {
+ while(SDL_PollEvent(&event)) {
+ switch(event.type) {
+ case SDL_QUIT:
+ main_loop->quit();
+ break;
+ case SDL_KEYDOWN:
+ case SDL_JOYBUTTONDOWN:
+ case SDL_MOUSEBUTTONDOWN:
+ running = false;
+ }
+ }
+ if(SDL_GetTicks() - ticks >= (max - min))
+ running = false;
+ sound_manager->update();
+ SDL_Delay(10);
+ }
+}
+
+#ifdef DEBUG
+static Uint32 last_timelog_ticks = 0;
+static const char* last_timelog_component = 0;
+
+static inline void timelog(const char* component)
+{
+ Uint32 current_ticks = SDL_GetTicks();
+
+ if(last_timelog_component != 0) {
+ log_info << "Component '" << last_timelog_component << "' finished after " << (current_ticks - last_timelog_ticks) / 1000.0 << " seconds" << std::endl;
+ }
+
+ last_timelog_ticks = current_ticks;
+ last_timelog_component = component;
+}
+#else
+static inline void timelog(const char* )
+{
+}
+#endif
+
+int main(int argc, char** argv)
+{
+ int result = 0;
+
+#ifndef NO_CATCH
+ try {
+#endif
+
+ if(pre_parse_commandline(argc, argv))
+ return 0;
+
+ Console::instance = new Console();
+ init_physfs(argv[0]);
+ init_sdl();
+
+ timelog("controller");
+ main_controller = new JoystickKeyboardController();
+ timelog("config");
+ init_config();
+ timelog("tinygettext");
+ init_tinygettext();
+ timelog("commandline");
+ if(parse_commandline(argc, argv))
+ return 0;
+ timelog("audio");
+ init_audio();
+ timelog("video");
+ DrawingContext context;
+ context_pointer = &context;
+ init_video();
+ Console::instance->init_graphics();
+ timelog("scripting");
+ Scripting::init_squirrel(config->enable_script_debugger);
+ timelog("resources");
+ load_shared();
+ timelog(0);
+
+ main_loop = new MainLoop();
+ if(config->start_level != "") {
+ // we have a normal path specified at commandline not physfs paths.
+ // So we simply mount that path here...
+ std::string dir = FileSystem::dirname(config->start_level);
+ PHYSFS_addToSearchPath(dir.c_str(), true);
+
+ if(config->start_level.size() > 4 &&
+ config->start_level.compare(config->start_level.size() - 5, 5, ".stwm") == 0) {
+ init_rand();
+ main_loop->push_screen(new WorldMapNS::WorldMap(
+ FileSystem::basename(config->start_level)));
+ } else {
+ init_rand();//If level uses random eg. for
+ // rain particles before we do this:
+ std::auto_ptr<GameSession> session (
+ new GameSession(FileSystem::basename(config->start_level)));
+
+ config->random_seed =session->get_demo_random_seed(config->start_demo);
+ init_rand();//initialise generator with seed from session
+
+ if(config->start_demo != "")
+ session->play_demo(config->start_demo);
+
+ if(config->record_demo != "")
+ session->record_demo(config->record_demo);
+ main_loop->push_screen(session.release());
+ }
+ } else {
+ init_rand();
+ main_loop->push_screen(new TitleScreen());
+ }
+
+ //init_rand(); PAK: this call might subsume the above 3, but I'm chicken!
+ main_loop->run(context);
+#ifndef NO_CATCH
+ } catch(std::exception& e) {
+ log_fatal << "Unexpected exception: " << e.what() << std::endl;
+ result = 1;
+ } catch(...) {
+ log_fatal << "Unexpected exception" << std::endl;
+ result = 1;
+ }
+#endif
+
+ delete main_loop;
+ main_loop = NULL;
+
+ unload_shared();
+ quit_audio();
+
+ if(config)
+ config->save();
+ delete config;
+ config = NULL;
+ delete main_controller;
+ main_controller = NULL;
+ delete Console::instance;
+ Console::instance = NULL;
+ Scripting::exit_squirrel();
+ delete texture_manager;
+ texture_manager = NULL;
+ SDL_Quit();
+ PHYSFS_deinit();
+
+ return result;
+}
--- /dev/null
+// $Id$
+//
+// SuperTux
+// Copyright (C) 2006 Matthias Braun <matze@braunis.de>
+//
+// 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 __MAIN_H__
+#define __MAIN_H__
+
+void init_video();
+void wait_for_event(float min_delay, float max_delay);
+
+/// The width of the display (this is a logical value, not the physical value)
+extern int SCREEN_WIDTH;
+/// The height of the display (this is a logical value, not the physical value)
+extern int SCREEN_HEIGHT;
+
+// global variables
+class JoystickKeyboardController;
+extern JoystickKeyboardController* main_controller;
+
+#endif
--- /dev/null
+// $Id$
+//
+// SuperTux
+// Copyright (C) 2006 Matthias Braun <matze@braunis.de>
+//
+// 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 <config.h>
+
+#include "mainloop.hpp"
+
+#include <stdlib.h>
+#include <SDL.h>
+#include "video/drawing_context.hpp"
+#include "control/joystickkeyboardcontroller.hpp"
+#include "gui/menu.hpp"
+#include "audio/sound_manager.hpp"
+#include "scripting/time_scheduler.hpp"
+#include "scripting/squirrel_util.hpp"
+#include "gameconfig.hpp"
+#include "main.hpp"
+#include "resources.hpp"
+#include "screen.hpp"
+#include "screen_fade.hpp"
+#include "timer.hpp"
+#include "player_status.hpp"
+#include "random_generator.hpp"
+
+// the engine will be run with a logical framerate of 64fps.
+// We chose 64fps here because it is a power of 2, so 1/64 gives an "even"
+// binary fraction...
+static const float LOGICAL_FPS = 64.0;
+/** ticks (as returned from SDL_GetTicks) per frame */
+static const Uint32 TICKS_PER_FRAME = (Uint32) (1000.0 / LOGICAL_FPS);
+/** don't skip more than every 2nd frame */
+static const int MAX_FRAME_SKIP = 2;
+
+MainLoop* main_loop = NULL;
+
+MainLoop::MainLoop()
+ : speed(1.0), nextpop(false), nextpush(false), fps(0), screenshot_requested(false)
+{
+ using namespace Scripting;
+ TimeScheduler::instance = new TimeScheduler();
+}
+
+MainLoop::~MainLoop()
+{
+ using namespace Scripting;
+ delete TimeScheduler::instance;
+ TimeScheduler::instance = NULL;
+
+ for(std::vector<Screen*>::iterator i = screen_stack.begin();
+ i != screen_stack.end(); ++i) {
+ delete *i;
+ }
+}
+
+void
+MainLoop::push_screen(Screen* screen, ScreenFade* screen_fade)
+{
+ this->next_screen.reset(screen);
+ this->screen_fade.reset(screen_fade);
+ nextpush = !nextpop;
+ nextpop = false;
+ speed = 1.0;
+}
+
+void
+MainLoop::exit_screen(ScreenFade* screen_fade)
+{
+ next_screen.reset(NULL);
+ this->screen_fade.reset(screen_fade);
+ nextpop = true;
+ nextpush = false;
+}
+
+void
+MainLoop::set_screen_fade(ScreenFade* screen_fade)
+{
+ this->screen_fade.reset(screen_fade);
+}
+
+void
+MainLoop::quit(ScreenFade* screen_fade)
+{
+ for(std::vector<Screen*>::iterator i = screen_stack.begin();
+ i != screen_stack.end(); ++i)
+ delete *i;
+ screen_stack.clear();
+
+ exit_screen(screen_fade);
+}
+
+void
+MainLoop::set_speed(float speed)
+{
+ this->speed = speed;
+}
+
+float
+MainLoop::get_speed() const
+{
+ return speed;
+}
+
+void
+MainLoop::draw_fps(DrawingContext& context, float fps_fps)
+{
+ char str[60];
+ snprintf(str, sizeof(str), "%3.1f", fps_fps);
+ const char* fpstext = "FPS";
+ context.draw_text(white_text, fpstext, Vector(SCREEN_WIDTH - white_text->get_text_width(fpstext) - gold_text->get_text_width(" 99999") - BORDER_X, BORDER_Y + 20), ALIGN_LEFT, LAYER_HUD);
+ context.draw_text(gold_text, str, Vector(SCREEN_WIDTH - BORDER_X, BORDER_Y + 20), ALIGN_RIGHT, LAYER_HUD);
+}
+
+void
+MainLoop::draw(DrawingContext& context)
+{
+ static Uint32 fps_ticks = SDL_GetTicks();
+ static int frame_count = 0;
+
+ current_screen->draw(context);
+ if(Menu::current() != NULL)
+ Menu::current()->draw(context);
+ if(screen_fade.get() != NULL)
+ screen_fade->draw(context);
+ Console::instance->draw(context);
+
+ if(config->show_fps)
+ draw_fps(context, fps);
+
+ // if a screenshot was requested, pass request on to drawing_context
+ if (screenshot_requested) {
+ context.take_screenshot();
+ screenshot_requested = false;
+ }
+ context.do_drawing();
+
+ /* Calculate frames per second */
+ if(config->show_fps)
+ {
+ ++frame_count;
+
+ if(SDL_GetTicks() - fps_ticks >= 500)
+ {
+ fps = (float) frame_count / .5;
+ frame_count = 0;
+ fps_ticks = SDL_GetTicks();
+ }
+ }
+}
+
+void
+MainLoop::update_gamelogic(float elapsed_time)
+{
+ Scripting::update_debugger();
+ Scripting::TimeScheduler::instance->update(game_time);
+ current_screen->update(elapsed_time);
+ if(screen_fade.get() != NULL)
+ screen_fade->update(elapsed_time);
+ Console::instance->update(elapsed_time);
+}
+
+void
+MainLoop::process_events()
+{
+ main_controller->update();
+ SDL_Event event;
+ while(SDL_PollEvent(&event)) {
+ main_controller->process_event(event);
+ if(Menu::current() != NULL)
+ Menu::current()->event(event);
+ if(event.type == SDL_QUIT)
+ quit();
+ else if (event.type == SDL_KEYDOWN && event.key.keysym.sym == SDLK_F11) {
+ config->use_fullscreen = !config->use_fullscreen;
+ init_video();
+ }
+ else if (event.type == SDL_KEYDOWN && event.key.keysym.sym == SDLK_PRINT) {
+ take_screenshot();
+ }
+ }
+}
+
+void
+MainLoop::handle_screen_switch()
+{
+ while( (next_screen.get() != NULL || nextpop) &&
+ (screen_fade.get() == NULL || screen_fade->done())) {
+ if(current_screen.get() != NULL) {
+ current_screen->leave();
+ }
+
+ if(nextpop) {
+ if(screen_stack.empty()) {
+ running = false;
+ break;
+ }
+ next_screen.reset(screen_stack.back());
+ screen_stack.pop_back();
+ }
+ if(nextpush && current_screen.get() != NULL) {
+ screen_stack.push_back(current_screen.release());
+ }
+
+ nextpush = false;
+ nextpop = false;
+ speed = 1.0;
+ if(next_screen.get() != NULL)
+ next_screen->setup();
+ current_screen.reset(next_screen.release());
+ screen_fade.reset(NULL);
+
+ waiting_threads.wakeup();
+ }
+}
+
+void
+MainLoop::run(DrawingContext &context)
+{
+ Uint32 last_ticks = 0;
+ Uint32 elapsed_ticks = 0;
+
+ running = true;
+ while(running) {
+
+ handle_screen_switch();
+ if(!running || current_screen.get() == NULL)
+ break;
+
+ Uint32 ticks = SDL_GetTicks();
+ elapsed_ticks += ticks - last_ticks;
+ last_ticks = ticks;
+
+ if (elapsed_ticks > TICKS_PER_FRAME*4) {
+ // when the game loads up or levels are switched the
+ // elapsed_ticks grows extremly large, so we just ignore those
+ // large time jumps
+ elapsed_ticks = 0;
+ }
+
+ int frames = 0;
+
+ if (elapsed_ticks > TICKS_PER_FRAME) {
+ while(elapsed_ticks > TICKS_PER_FRAME && frames < MAX_FRAME_SKIP) {
+ elapsed_ticks -= TICKS_PER_FRAME;
+ float timestep = 1.0 / LOGICAL_FPS;
+ real_time += timestep;
+ timestep *= speed;
+ game_time += timestep;
+
+ process_events();
+ update_gamelogic(timestep);
+ frames += 1;
+ }
+
+ draw(context);
+ }
+
+ sound_manager->update();
+
+ SDL_Delay(0);
+ }
+}
+
+void
+MainLoop::take_screenshot()
+{
+ screenshot_requested = true;
+}
+
--- /dev/null
+// $Id$
+//
+// SuperTux
+// Copyright (C) 2006 Matthias Braun <matze@braunis.de>
+//
+// 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 __MAINLOOP_HPP__
+#define __MAINLOOP_HPP__
+
+#include <memory>
+#include <vector>
+#include "scripting/thread_queue.hpp"
+
+class Screen;
+class Console;
+class ScreenFade;
+class DrawingContext;
+
+class MainLoop
+{
+public:
+ MainLoop();
+ ~MainLoop();
+
+ void run(DrawingContext &context);
+ void exit_screen(ScreenFade* fade = NULL);
+ void quit(ScreenFade* fade = NULL);
+ void set_speed(float speed);
+ float get_speed() const;
+
+ /**
+ * requests that a screenshot be taken after the next frame has been rendered
+ */
+ void take_screenshot();
+
+ // push new screen on screen_stack
+ void push_screen(Screen* screen, ScreenFade* fade = NULL);
+ void set_screen_fade(ScreenFade* fade);
+
+ /// threads that wait for a screenswitch
+ Scripting::ThreadQueue waiting_threads;
+
+private:
+ void draw_fps(DrawingContext& context, float fps);
+ void draw(DrawingContext& context);
+ void update_gamelogic(float elapsed_time);
+ void process_events();
+ void handle_screen_switch();
+
+ bool running;
+ float speed;
+ bool nextpop;
+ bool nextpush;
+ /// measured fps
+ float fps;
+ std::auto_ptr<Screen> next_screen;
+ std::auto_ptr<Screen> current_screen;
+ std::auto_ptr<Console> console;
+ std::auto_ptr<ScreenFade> screen_fade;
+ std::vector<Screen*> screen_stack;
+ bool screenshot_requested; /**< true if a screenshot should be taken after the next frame has been rendered */
+};
+
+extern MainLoop* main_loop;
+
+#endif
--- /dev/null
+// $Id$
+//
+// SuperTux
+// Copyright (C) 2006 Matthias Braun <matze@braunis.de>
+//
+// 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 __AATRIANGLE_H__
+#define __AATRIANGLE_H__
+
+#include "rect.hpp"
+
+/**
+ * An axis aligned triangle (ie. a triangle where 2 sides are parallel to the x-
+ * and y-axis.
+ */
+class AATriangle : public Rect
+{
+public:
+ /** Directions:
+ *
+ * SOUTHEWEST NORTHEAST SOUTHEAST NORTHWEST
+ * * or *---* or * or *---*
+ * | \ \ | / | | /
+ * | \ \ | / | | /
+ * *---* * *---* *
+ *
+ * Deform flags: (see docs/aatriangletypes.png for details)
+ */
+ enum Direction {
+ SOUTHWEST = 0,
+ NORTHEAST,
+ SOUTHEAST,
+ NORTHWEST,
+ DIRECTION_MASK = 0x0003,
+ DEFORM1 = 0x0010,
+ DEFORM2 = 0x0020,
+ DEFORM3 = 0x0030,
+ DEFORM4 = 0x0040,
+ DEFORM_MASK = 0x0070
+ };
+
+ AATriangle()
+ : dir(SOUTHWEST)
+ {
+ }
+ AATriangle(const Vector& v1, const Vector& v2, int newdir)
+ : Rect(v1, v2), dir(newdir)
+ {
+ }
+
+ int dir;
+};
+
+#endif
--- /dev/null
+// $Id$
+//
+// SuperTux
+// Copyright (C) 2006 Matthias Braun <matze@braunis.de>
+//
+// 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 __RECT_H__
+#define __RECT_H__
+
+#include <assert.h>
+#include "vector.hpp"
+
+/** This class represents a rectangle.
+ * (Implementation Note) We're using upper left and lower right point instead of
+ * upper left and width/height here, because that makes the collision dectection
+ * a little bit efficienter.
+ */
+class Rect
+{
+public:
+ Rect()
+ { }
+
+ Rect(const Vector& np1, const Vector& np2)
+ : p1(np1), p2(np2)
+ {
+ }
+
+ Rect(float x1, float y1, float x2, float y2)
+ : p1(x1, y1), p2(x2, y2)
+ {
+ assert(p1.x <= p2.x && p1.y <= p2.y);
+ }
+
+ float get_left() const
+ { return p1.x; }
+
+ float get_right() const
+ { return p2.x; }
+
+ float get_top() const
+ { return p1.y; }
+
+ float get_bottom() const
+ { return p2.y; }
+
+ float get_width() const
+ { return p2.x - p1.x; }
+
+ float get_height() const
+ { return p2.y - p1.y; }
+
+ Vector get_middle() const
+ { return Vector((p1.x+p2.x)/2, (p1.y+p2.y)/2); }
+
+ void set_pos(const Vector& v)
+ {
+ move(v-p1);
+ }
+
+ void set_height(float height)
+ {
+ p2.y = p1.y + height;
+ }
+ void set_width(float width)
+ {
+ p2.x = p1.x + width;
+ }
+ void set_size(float width, float height)
+ {
+ set_width(width);
+ set_height(height);
+ }
+ Vector get_size()
+ {
+ return Vector(get_width(), get_height());
+ }
+
+ void move(const Vector& v)
+ {
+ p1 += v;
+ p2 += v;
+ }
+
+ bool contains(const Vector& v) const
+ {
+ return v.x >= p1.x && v.y >= p1.y && v.x < p2.x && v.y < p2.y;
+ }
+ bool contains(const Rect& other) const
+ {
+ if(p1.x >= other.p2.x || other.p1.x >= p2.x)
+ return false;
+ if(p1.y >= other.p2.y || other.p1.y >= p2.y)
+ return false;
+
+ return true;
+ }
+
+ // leave these 2 public to safe the headaches of set/get functions for such
+ // simple things :)
+
+ /// upper left edge
+ Vector p1;
+ /// lower right edge
+ Vector p2;
+};
+
+#endif
--- /dev/null
+// $Id$
+//
+// SuperTux
+// Copyright (C) 2006 Matthias Braun <matze@braunis.de>
+//
+// 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 <config.h>
+
+#include <cmath>
+
+#include "math/vector.hpp"
+
+Vector Vector::unit() const
+{
+ return *this / norm();
+}
+
+float Vector::norm() const
+{
+ return sqrt(x*x + y*y);
+}
--- /dev/null
+// $Id$
+//
+// SuperTux
+// Copyright (C) 2006 Matthias Braun <matze@braunis.de>
+//
+// 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_VECTOR_H
+#define SUPERTUX_VECTOR_H
+
+/** Simple two dimensional vector. */
+class Vector
+{
+public:
+ Vector(float nx, float ny)
+ : x(nx), y(ny)
+ { }
+ Vector(const Vector& other)
+ : x(other.x), y(other.y)
+ { }
+ Vector()
+ : x(0), y(0)
+ { }
+
+ bool operator ==(const Vector& other) const
+ {
+ return x == other.x && y == other.y;
+ }
+
+ bool operator !=(const Vector& other) const
+ {
+ return !(x == other.x && y == other.y);
+ }
+
+ const Vector& operator=(const Vector& other)
+ {
+ x = other.x;
+ y = other.y;
+ return *this;
+ }
+
+ Vector operator+(const Vector& other) const
+ {
+ return Vector(x + other.x, y + other.y);
+ }
+
+ Vector operator-(const Vector& other) const
+ {
+ return Vector(x - other.x, y - other.y);
+ }
+
+ Vector operator*(float s) const
+ {
+ return Vector(x * s, y * s);
+ }
+
+ Vector operator/(float s) const
+ {
+ return Vector(x / s, y / s);
+ }
+
+ Vector operator-() const
+ {
+ return Vector(-x, -y);
+ }
+
+ const Vector& operator +=(const Vector& other)
+ {
+ x += other.x;
+ y += other.y;
+ return *this;
+ }
+
+ const Vector& operator -=(const Vector& other)
+ {
+ x -= other.x;
+ y -= other.y;
+ return *this;
+ }
+
+ const Vector& operator *=(float val)
+ {
+ x *= val;
+ y *= val;
+ return *this;
+ }
+
+ const Vector& operator /=(float val)
+ {
+ x /= val;
+ y /= val;
+ return *this;
+ }
+
+ /// Scalar product of 2 vectors
+ float operator*(const Vector& other) const
+ {
+ return x*other.x + y*other.y;
+ }
+
+ float norm() const;
+ Vector unit() const;
+
+ // ... add the other operators as needed, I'm too lazy now ...
+
+ float x, y; // leave this public, get/set methods just give me headaches
+ // for such simple stuff :)
+};
+
+#endif
--- /dev/null
+// $Id$
+//
+// SuperTux
+// Copyright (C) 2006 Matthias Braun <matze@braunis.de>
+//
+// 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 <config.h>
+
+#include "moving_object.hpp"
+
+MovingObject::MovingObject()
+{
+ group = COLGROUP_MOVING;
+}
+
+MovingObject::~MovingObject()
+{
+}
--- /dev/null
+// $Id$
+//
+// SuperTux
+// Copyright (C) 2006 Matthias Braun <matze@braunis.de>
+//
+// 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_MOVING_OBJECT_H
+#define SUPERTUX_MOVING_OBJECT_H
+
+#include <stdint.h>
+
+#include "game_object.hpp"
+#include "collision_hit.hpp"
+#include "math/vector.hpp"
+#include "math/rect.hpp"
+
+class Sector;
+class CollisionGrid;
+
+enum CollisionGroup {
+ /** Objects in DISABLED group are not tested for collisions */
+ COLGROUP_DISABLED = 0,
+ /**
+ * "default" is moving object. MovingObjects get tested against all other
+ * objects and against other movingobjects
+ */
+ COLGROUP_MOVING,
+ /**
+ * a Moving object, that is not tested against other MovingObjects (or other
+ * MovingOnlyStatic objects), but is tested against all other objects.
+ */
+ COLGROUP_MOVING_ONLY_STATIC,
+ /** TODO write docu :-/ */
+ COLGROUP_MOVING_STATIC,
+ /**
+ * Doesn't move and isn't explicitely checked for collisions with other
+ * objects (but other objects might check with this)
+ * The difference to COLGROUP_TOUCHABLE is that we can do multiple
+ * collision response tests in a row which is needed for static object
+ * that tux walks on. The results for collisions with STATIC objects
+ * are also sorted by time (so that the first hit gets handled first).
+ *
+ * Use this for static obstacles
+ */
+ COLGROUP_STATIC,
+ /**
+ * Isn't explicitely checked for collisions with other objects. But other
+ * objects might check with this object.
+ * Difference to COLGROUP_STATIC is that collisions with this object are
+ * only tested once and collision response is typically not handled
+ *
+ * Use this for touchable things like spikes/areas or collectibles like
+ * coins
+ */
+ COLGROUP_TOUCHABLE,
+
+ /**
+ * Should be used for tilemaps
+ */
+ COLGROUP_TILEMAP
+};
+
+/**
+ * Base class for all dynamic/moving game objects. This class contains things
+ * for handling the bounding boxes and collision feedback.
+ */
+class MovingObject : public GameObject
+{
+public:
+ MovingObject();
+ virtual ~MovingObject();
+
+ /** this function is called when the object collided with something solid */
+ virtual void collision_solid(const CollisionHit& hit)
+ {
+ (void) hit;
+ }
+ /**
+ * when 2 objects collided, we will first call the pre_collision_check
+ * functions of both objects that can decide on how to react to the collision.
+ */
+ virtual bool collides(GameObject& other, const CollisionHit& hit)
+ {
+ (void) other;
+ (void) hit;
+ return true;
+ }
+ /** this function is called when the object collided with any other object */
+ virtual HitResponse collision(GameObject& other, const CollisionHit& hit) = 0;
+ /** called when tiles with special attributes have been touched */
+ virtual void collision_tile(uint32_t tile_attributes)
+ {
+ (void) tile_attributes;
+ }
+
+ const Vector& get_pos() const
+ {
+ return bbox.p1;
+ }
+
+ /** returns the bounding box of the Object */
+ const Rect& get_bbox() const
+ {
+ return bbox;
+ }
+
+ const Vector& get_movement() const
+ {
+ return movement;
+ }
+
+ /** places the moving object at a specific position. Be carefull when
+ * using this function. There are no collision detection checks performed
+ * here so bad things could happen.
+ */
+ virtual void set_pos(const Vector& pos)
+ {
+ dest.move(pos-get_pos());
+ bbox.set_pos(pos);
+ }
+
+ /**
+ * sets the moving object's bbox to a specific width. Be careful when
+ * using this function. There are no collision detection checks performed
+ * here so bad things could happen.
+ */
+ virtual void set_width(float w)
+ {
+ dest.set_width(w);
+ bbox.set_width(w);
+ }
+
+ /**
+ * sets the moving object's bbox to a specific size. Be careful when
+ * using this function. There are no collision detection checks performed
+ * here so bad things could happen.
+ */
+ virtual void set_size(float w, float h)
+ {
+ dest.set_size(w, h);
+ bbox.set_size(w, h);
+ }
+
+ CollisionGroup get_group() const
+ {
+ return group;
+ }
+
+protected:
+ friend class Sector;
+ friend class CollisionGrid;
+ friend class Platform;
+
+ void set_group(CollisionGroup group)
+ {
+ this->group = group;
+ }
+
+ /** The bounding box of the object (as used for collision detection, this
+ * isn't necessarily the bounding box for graphics)
+ */
+ Rect bbox;
+ /** The movement that will happen till next frame
+ */
+ Vector movement;
+ /** The collision group */
+ CollisionGroup group;
+
+private:
+ /**
+ * this is only here for internal collision detection use (don't touch this
+ * from outside collision detection code)
+ *
+ * This field holds the currently anticipated destination of the object
+ * during collision detection
+ */
+ Rect dest;
+};
+
+#endif
--- /dev/null
+// $Id$
+//
+// SuperTux
+// Copyright (C) 2006 Matthias Braun <matze@braunis.de>
+//
+// 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 <config.h>
+
+#include <math.h>
+#include <stdexcept>
+#include <iostream>
+#include <limits>
+
+#include "ambient_sound.hpp"
+#include "object_factory.hpp"
+#include "lisp/lisp.hpp"
+#include "sector.hpp"
+#include "audio/sound_manager.hpp"
+#include "audio/sound_source.hpp"
+#include "log.hpp"
+#include "scripting/squirrel_util.hpp"
+
+AmbientSound::AmbientSound(const lisp::Lisp& lisp)
+{
+ name="";
+ position.x = 0;
+ position.y = 0;
+
+ dimension.x = 0;
+ dimension.y = 0;
+
+ distance_factor = 0;
+ distance_bias = 0;
+ maximumvolume = 1;
+ sample = "";
+ currentvolume = 0;
+
+ if (!(lisp.get("x", position.x)&&lisp.get("y", position.y))) {
+ log_warning << "No Position in ambient_sound" << std::endl;
+ }
+
+ lisp.get("name" , name);
+ lisp.get("width" , dimension.x);
+ lisp.get("height", dimension.y);
+
+ lisp.get("distance_factor",distance_factor);
+ lisp.get("distance_bias" ,distance_bias );
+ lisp.get("sample" ,sample );
+ lisp.get("volume" ,maximumvolume );
+
+ // set dimension to zero if smaller than 64, which is default size in flexlay
+
+ if ((dimension.x <= 64) || (dimension.y <= 64)) {
+ dimension.x = 0;
+ dimension.y = 0;
+ }
+
+ // square all distances (saves us a sqrt later)
+
+ distance_bias*=distance_bias;
+ distance_factor*=distance_factor;
+
+ // set default silence_distance
+
+ if (distance_factor == 0)
+ silence_distance = std::numeric_limits<float>::max();
+ else
+ silence_distance = 1/distance_factor;
+
+ lisp.get("silence_distance",silence_distance);
+
+ sound_source = 0; // not playing at the beginning
+ latency=0;
+}
+
+AmbientSound::AmbientSound(Vector pos, float factor, float bias, float vol, std::string file)
+{
+ position.x=pos.x;
+ position.y=pos.y;
+
+ dimension.x=0;
+ dimension.y=0;
+
+ distance_factor=factor*factor;
+ distance_bias=bias*bias;
+ maximumvolume=vol;
+ sample=file;
+
+ // set default silence_distance
+
+ if (distance_factor == 0)
+ silence_distance = std::numeric_limits<float>::max();
+ else
+ silence_distance = 1/distance_factor;
+
+ sound_source = 0; // not playing at the beginning
+ latency=0;
+}
+
+AmbientSound::~AmbientSound() {
+ stop_playing();
+}
+
+void
+AmbientSound::hit(Player& )
+{
+}
+
+void
+AmbientSound::stop_playing() {
+ delete sound_source;
+ sound_source = 0;
+}
+
+void
+AmbientSound::start_playing()
+{
+ try {
+ sound_source = sound_manager->create_sound_source(sample);
+ if(!sound_source)
+ throw std::runtime_error("file not found");
+
+ sound_source->set_gain(0);
+ sound_source->set_looping(true);
+ currentvolume=targetvolume=1e-20f;
+ sound_source->play();
+ } catch(std::exception& e) {
+ log_warning << "Couldn't play '" << sample << "': " << e.what() << "" << std::endl;
+ delete sound_source;
+ sound_source = 0;
+ remove_me();
+ }
+}
+
+void
+AmbientSound::update(float deltat)
+{
+ if (latency-- <= 0) {
+ float px,py;
+ float rx,ry;
+
+ // Player position
+ px=Sector::current()->player->get_pos().x;
+ py=Sector::current()->player->get_pos().y;
+
+ // Relate to which point in the area
+ rx=px<position.x?position.x:
+ (px<position.x+dimension.x?px:position.x+dimension.x);
+ ry=py<position.y?position.y:
+ (py<position.y+dimension.y?py:position.y+dimension.y);
+
+ // calculate square of distance
+ float sqrdistance=(px-rx)*(px-rx)+(py-ry)*(py-ry);
+ sqrdistance-=distance_bias;
+
+ // inside the bias: full volume (distance 0)
+ if (sqrdistance<0)
+ sqrdistance=0;
+
+ // calculate target volume - will never become 0
+ targetvolume=1/(1+sqrdistance*distance_factor);
+ float rise=targetvolume/currentvolume;
+
+ // rise/fall half life?
+ currentvolume*=pow(rise,deltat*10);
+ currentvolume += 1e-6f; // volume is at least 1e-6 (0 would never rise)
+
+ if (sound_source != 0) {
+
+ // set the volume
+ sound_source->set_gain(currentvolume*maximumvolume);
+
+ if (sqrdistance>=silence_distance && currentvolume<1e-3)
+ stop_playing();
+ latency=0;
+ } else {
+ if (sqrdistance<silence_distance) {
+ start_playing();
+ latency=0;
+ }
+ else // set a reasonable latency
+ latency=(int)(0.001/distance_factor);
+ //(int)(10*((sqrdistance-silence_distance)/silence_distance));
+ }
+ }
+
+ // heuristically measured "good" latency maximum
+
+ // if (latency>0.001/distance_factor)
+ // latency=
+}
+
+void
+AmbientSound::draw(DrawingContext &)
+{
+}
+
+void
+AmbientSound::expose(HSQUIRRELVM vm, SQInteger table_idx)
+{
+ Scripting::AmbientSound* interface = static_cast<Scripting::AmbientSound*> (this);
+ expose_object(vm, table_idx, interface, name, false);
+}
+
+void
+AmbientSound::unexpose(HSQUIRRELVM vm, SQInteger table_idx)
+{
+ Scripting::unexpose_object(vm, table_idx, name);
+}
+
+void
+AmbientSound::set_pos(float x, float y)
+{
+ position.x = x;
+ position.y = y;
+}
+
+float
+AmbientSound::get_pos_x() const
+{
+ return position.x;
+}
+
+float
+AmbientSound::get_pos_y() const
+{
+ return position.y;
+}
+
+IMPLEMENT_FACTORY(AmbientSound, "ambient_sound");
--- /dev/null
+// $Id$
+//
+// SuperTux
+// Copyright (C) 2006 Matthias Braun <matze@braunis.de>
+//
+// 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.
+
+
+/**
+ * Ambient Sound Source, gamma version. Features:
+ *
+ * - "rounded rectancle" geometry with position, dimension and
+ * "rounding radius" (extending in all directions) of a 100%
+ * volume area, adjustable maximum volume, inverse square
+ * falloff outside area.
+ *
+ * - degenerates gracefully to a disc for dimension=0
+ *
+ * - parameters:
+ *
+ * x, y position
+ * width, height dimension
+ * distance_factor high = steep fallofff
+ * distance_bias high = big "100% disc"
+ * silence_distance defaults reasonably.
+ * sample sample to be played back in loop mode
+ *
+ * basti_
+ */
+
+#ifndef __AMBIENT_SOUND_H__
+#define __AMBIENT_SOUND_H__
+
+#include "game_object.hpp"
+#include "resources.hpp"
+#include "player.hpp"
+#include "script_interface.hpp"
+#include "scripting/ambient_sound.hpp"
+
+class SoundSource;
+
+class AmbientSound : public GameObject, public ScriptInterface, public Scripting::AmbientSound
+{
+public:
+ AmbientSound(const lisp::Lisp& lisp);
+ AmbientSound(Vector pos, float factor, float bias, float vol, std::string file);
+ ~AmbientSound();
+
+ void set_pos(Vector newpos)
+ {
+ position=newpos;
+ }
+ const Vector get_pos() const
+ {
+ return position;
+ }
+
+ /**
+ * @name Scriptable Methods
+ * @{
+ */
+ void set_pos(float x, float y);
+ float get_pos_x() const;
+ float get_pos_y() const;
+ /**
+ * @}
+ */
+
+protected:
+ virtual void hit(Player& player);
+ virtual void update(float time);
+ virtual void draw(DrawingContext&);
+ virtual void start_playing();
+ virtual void stop_playing();
+ virtual void expose(HSQUIRRELVM vm, SQInteger table_idx);
+ virtual void unexpose(HSQUIRRELVM vm, SQInteger table_idx);
+private:
+ std::string name; /**< user-defined name for use in scripts or empty string if not scriptable */
+ Vector position;
+ Vector dimension;
+
+ std::string sample;
+ SoundSource* sound_source;
+ int latency;
+
+ float distance_factor; /// distance scaling
+ float distance_bias; /// 100% volume disc radius
+ float silence_distance; /// not implemented yet
+
+ float maximumvolume; /// maximum volume
+ float targetvolume; /// how loud we want to be
+ float currentvolume; /// how loud we are
+
+ float * volume_ptr; /// this will be used by the volume adjustment effect.
+};
+
+#endif
--- /dev/null
+// $Id$
+//
+// SuperTux
+// Copyright (C) 2006 Matthias Braun <matze@braunis.de>
+//
+// 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 <config.h>
+
+#include <stdexcept>
+#include <sstream>
+#include "anchor_point.hpp"
+#include "math/rect.hpp"
+#include "log.hpp"
+
+std::string anchor_point_to_string(AnchorPoint point)
+{
+ switch(point) {
+ case ANCHOR_TOP_LEFT:
+ return "topleft";
+ case ANCHOR_TOP:
+ return "top";
+ case ANCHOR_TOP_RIGHT:
+ return "topright";
+ case ANCHOR_LEFT:
+ return "left";
+ case ANCHOR_MIDDLE:
+ return "middle";
+ case ANCHOR_RIGHT:
+ return "right";
+ case ANCHOR_BOTTOM_LEFT:
+ return "bottomleft";
+ case ANCHOR_BOTTOM:
+ return "bottom";
+ case ANCHOR_BOTTOM_RIGHT:
+ return "bottomright";
+ default:
+ throw std::runtime_error("Invalid anchor point");
+ }
+}
+
+AnchorPoint string_to_anchor_point(const std::string& str)
+{
+ if(str == "topleft")
+ return ANCHOR_TOP_LEFT;
+ else if(str == "top")
+ return ANCHOR_TOP;
+ else if(str == "topright")
+ return ANCHOR_TOP_RIGHT;
+ else if(str == "left")
+ return ANCHOR_LEFT;
+ else if(str == "middle")
+ return ANCHOR_MIDDLE;
+ else if(str == "right")
+ return ANCHOR_RIGHT;
+ else if(str == "bottomleft")
+ return ANCHOR_BOTTOM_LEFT;
+ else if(str == "bottom")
+ return ANCHOR_BOTTOM;
+ else if(str == "bottomright")
+ return ANCHOR_BOTTOM_RIGHT;
+
+ std::ostringstream msg;
+ msg << "Unknown anchor '" << str << "'";
+ throw std::runtime_error(msg.str());
+}
+
+Vector get_anchor_pos(const Rect& rect, AnchorPoint point)
+{
+ Vector result;
+
+ switch(point & ANCHOR_V_MASK) {
+ case ANCHOR_LEFT:
+ result.x = rect.get_left();
+ break;
+ case ANCHOR_MIDDLE:
+ result.x = rect.get_left() + (rect.get_right() - rect.get_left()) / 2.0;
+ break;
+ case ANCHOR_RIGHT:
+ result.x = rect.get_right();
+ break;
+ default:
+#ifdef DEBUG
+ throw std::runtime_error("Invalid anchor point found");
+#endif
+ log_warning << "Invalid anchor point found" << std::endl;
+ result.x = rect.get_left();
+ break;
+ }
+
+ switch(point & ANCHOR_H_MASK) {
+ case ANCHOR_TOP:
+ result.y = rect.get_top();
+ break;
+ case ANCHOR_MIDDLE:
+ result.y = rect.get_top() + (rect.get_bottom() - rect.get_top()) / 2.0;
+ break;
+ case ANCHOR_BOTTOM:
+ result.y = rect.get_bottom();
+ break;
+ default:
+#ifdef DEBUG
+ throw std::runtime_error("Invalid anchor point found");
+#endif
+ log_warning << "Invalid anchor point found" << std::endl;
+ result.y = rect.get_top();
+ break;
+ }
+
+ return result;
+}
+
+Vector get_anchor_pos(const Rect& destrect, float width, float height,
+ AnchorPoint point)
+{
+ Vector result;
+
+ switch(point & ANCHOR_V_MASK) {
+ case ANCHOR_LEFT:
+ result.x = destrect.get_left();
+ break;
+ case ANCHOR_MIDDLE:
+ result.x = destrect.get_middle().x - width/2.0;
+ break;
+ case ANCHOR_RIGHT:
+ result.x = destrect.get_right() - width;
+ break;
+ default:
+#ifdef DEBUG
+ throw std::runtime_error("Invalid anchor point found");
+#endif
+ log_warning << "Invalid anchor point found" << std::endl;
+ result.x = destrect.get_left();
+ break;
+ }
+
+ switch(point & ANCHOR_H_MASK) {
+ case ANCHOR_TOP:
+ result.y = destrect.get_top();
+ break;
+ case ANCHOR_MIDDLE:
+ result.y = destrect.get_middle().y - height/2.0;
+ break;
+ case ANCHOR_BOTTOM:
+ result.y = destrect.get_bottom() - height;
+ break;
+ default:
+#ifdef DEBUG
+ throw std::runtime_error("Invalid anchor point found");
+#endif
+ log_warning << "Invalid anchor point found" << std::endl;
+ result.y = destrect.get_top();
+ break;
+ }
+
+ return result;
+}
--- /dev/null
+// $Id$
+//
+// SuperTux
+// Copyright (C) 2006 Matthias Braun <matze@braunis.de>
+//
+// 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 __ANCHOR_POINT_HPP__
+#define __ANCHOR_POINT_HPP__
+
+#include <string>
+#include "math/vector.hpp"
+
+class Rect;
+
+enum AnchorPoint {
+ ANCHOR_H_MASK = 0x00f0,
+ ANCHOR_TOP = 0x0010,
+ ANCHOR_BOTTOM = 0x0020,
+ ANCHOR_V_MASK = 0x000f,
+ ANCHOR_LEFT = 0x0001,
+ ANCHOR_RIGHT = 0x0002,
+ ANCHOR_MIDDLE = 0x0000,
+
+ ANCHOR_TOP_LEFT = ANCHOR_TOP | ANCHOR_LEFT,
+ ANCHOR_TOP_RIGHT = ANCHOR_TOP | ANCHOR_RIGHT,
+ ANCHOR_BOTTOM_LEFT = ANCHOR_BOTTOM | ANCHOR_LEFT,
+ ANCHOR_BOTTOM_RIGHT = ANCHOR_BOTTOM | ANCHOR_RIGHT,
+};
+
+std::string anchor_point_to_string(AnchorPoint point);
+AnchorPoint string_to_anchor_point(const std::string& str);
+Vector get_anchor_pos(const Rect& rect, AnchorPoint point);
+Vector get_anchor_pos(const Rect& destrect, float width, float height,
+ AnchorPoint point);
+
+#endif
--- /dev/null
+// $Id$
+//
+// SuperTux
+// Copyright (C) 2006 Matthias Braun <matze@braunis.de>
+//
+// 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 <config.h>
+
+#include <stdexcept>
+#include "background.hpp"
+#include "camera.hpp"
+#include "video/drawing_context.hpp"
+#include "lisp/lisp.hpp"
+#include "lisp/writer.hpp"
+#include "object_factory.hpp"
+#include "resources.hpp"
+#include "main.hpp"
+#include "log.hpp"
+
+Background::Background()
+ : layer(LAYER_BACKGROUND0)
+{
+}
+
+Background::Background(const lisp::Lisp& reader)
+ : layer(LAYER_BACKGROUND0)
+{
+ // read position, defaults to (0,0)
+ float px = 0;
+ float py = 0;
+ reader.get("x", px);
+ reader.get("y", py);
+ this->pos = Vector(px,py);
+
+ speed = 1.0;
+ speed_y = 1.0;
+
+ reader.get("layer", layer);
+ if(!reader.get("image", imagefile) || !reader.get("speed", speed))
+ throw std::runtime_error("Must specify image and speed for background");
+
+ set_image(imagefile, speed);
+ reader.get("speed-y", speed_y);
+ if (reader.get("image-top", imagefile_top)) {
+ image_top.reset(new Surface(imagefile_top));
+ }
+ if (reader.get("image-bottom", imagefile_bottom)) {
+ image_bottom.reset(new Surface(imagefile_bottom));
+ }
+}
+
+Background::~Background()
+{
+}
+
+void
+Background::write(lisp::Writer& writer)
+{
+ writer.start_list("background");
+
+ if (image_top.get() != NULL)
+ writer.write_string("image-top", imagefile_top);
+
+ writer.write_string("image", imagefile);
+ if (image_bottom.get() != NULL)
+ writer.write_string("image-bottom", imagefile_bottom);
+
+ writer.write_float("speed", speed);
+ writer.write_float("speed-y", speed_y);
+ writer.write_int("layer", layer);
+
+ writer.end_list("background");
+}
+
+void
+Background::update(float)
+{
+}
+
+void
+Background::set_image(const std::string& name, float speed)
+{
+ this->imagefile = name;
+ this->speed = speed;
+
+ image.reset(new Surface(name));
+}
+
+void
+Background::draw(DrawingContext& context)
+{
+ if(image.get() == NULL)
+ return;
+
+ int w = (int) image->get_width();
+ int h = (int) image->get_height();
+ int sx = int(pos.x-context.get_translation().x * speed) % w - w;
+ int sy = int(pos.y-context.get_translation().y * speed_y) % h - h;
+ int center_image_py = int(pos.y-context.get_translation().y * speed_y);
+ int bottom_image_py = int(pos.y-context.get_translation().y * speed_y) + h;
+ context.push_transform();
+ context.set_translation(Vector(0, 0));
+ for(int x = sx; x < SCREEN_WIDTH; x += w) {
+ for(int y = sy; y < SCREEN_HEIGHT; y += h) {
+ if (image_top.get() != NULL && (y < center_image_py)) {
+ context.draw_surface(image_top.get(), Vector(x, y), layer);
+ continue;
+ }
+ if (image_bottom.get() != NULL && (y >= bottom_image_py)) {
+ context.draw_surface(image_bottom.get(), Vector(x, y), layer);
+ continue;
+ }
+ context.draw_surface(image.get(), Vector(x, y), layer);
+ }
+ }
+ context.pop_transform();
+}
+
+IMPLEMENT_FACTORY(Background, "background");
--- /dev/null
+// $Id$
+//
+// SuperTux
+// Copyright (C) 2006 Matthias Braun <matze@braunis.de>
+//
+// 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_BACKGROUND_H
+#define SUPERTUX_BACKGROUND_H
+
+#include <memory>
+#include "video/surface.hpp"
+#include "video/drawing_context.hpp"
+#include "game_object.hpp"
+#include "serializable.hpp"
+
+class DisplayManager;
+
+namespace lisp {
+class Lisp;
+}
+
+class Background : public GameObject, public Serializable
+{
+public:
+ Background();
+ Background(const lisp::Lisp& reader);
+ virtual ~Background();
+
+ virtual void write(lisp::Writer& writer);
+
+ void set_image(const std::string& name, float bkgd_speed);
+
+ std::string get_image() const
+ { return imagefile; }
+ float get_speed() const
+ { return speed; }
+
+ virtual void update(float elapsed_time);
+
+ virtual void draw(DrawingContext& context);
+
+private:
+ int layer;
+ std::string imagefile_top;
+ std::string imagefile;
+ std::string imagefile_bottom;
+ Vector pos; /**< coordinates of upper-left corner of image */
+ float speed; /**< scroll-speed in horizontal direction */
+ float speed_y; /**< scroll-speed in vertical direction */
+ std::auto_ptr<Surface> image_top; /**< image to draw above pos */
+ std::auto_ptr<Surface> image; /**< image to draw, anchored at pos */
+ std::auto_ptr<Surface> image_bottom; /**< image to draw below pos+screenheight */
+};
+
+#endif /*SUPERTUX_BACKGROUND_H*/
--- /dev/null
+// $Id$
+//
+// SuperTux - BicyclePlatform
+// Copyright (C) 2007 Christoph Sommer <christoph.sommer@2007.expires.deltadevelopment.de>
+//
+// 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 <config.h>
+
+#include "bicycle_platform.hpp"
+
+#include <math.h>
+#include <stdexcept>
+#include "log.hpp"
+#include "video/drawing_context.hpp"
+#include "resources.hpp"
+#include "player.hpp"
+#include "path.hpp"
+#include "path_walker.hpp"
+#include "sprite/sprite.hpp"
+#include "lisp/lisp.hpp"
+#include "object_factory.hpp"
+#include "sector.hpp"
+#include "object/portable.hpp"
+
+BicyclePlatform::BicyclePlatform(const lisp::Lisp& reader)
+ : MovingSprite(reader, LAYER_OBJECTS, COLGROUP_STATIC),
+ master(0), slave(0), radius(128), angle(0), angular_speed(0), momentum(0)
+{
+ center = get_pos();
+}
+
+BicyclePlatform::BicyclePlatform(BicyclePlatform* master)
+ : MovingSprite(*master),
+ master(master), slave(this), center(master->center), radius(master->radius), angle(master->angle + M_PI), angular_speed(0), momentum(0)
+{
+ set_pos(get_pos() + Vector(master->get_bbox().get_width(), 0));
+ master->master = master;
+ master->slave = this;
+}
+
+BicyclePlatform::~BicyclePlatform() {
+ if ((this == master) && (master)) {
+ slave->master = 0;
+ slave->slave = 0;
+ }
+ if ((master) && (this == slave)) {
+ master->master = 0;
+ master->slave = 0;
+ }
+ master = 0;
+ slave = 0;
+}
+
+HitResponse
+BicyclePlatform::collision(GameObject& other, const CollisionHit& )
+{
+
+ // somehow the hit parameter does not get filled in, so to determine (hit.top == true) we do this:
+ MovingObject* mo = dynamic_cast<MovingObject*>(&other);
+ if (!mo) return FORCE_MOVE;
+ if ((mo->get_bbox().p2.y) > (get_bbox().p1.y + 2)) return FORCE_MOVE;
+
+ Player* pl = dynamic_cast<Player*>(mo);
+ if (pl) {
+ if (pl->is_big()) momentum += 1;
+ Portable* po = pl->get_grabbed_object();
+ MovingObject* pomo = dynamic_cast<MovingObject*>(po);
+ if (contacts.insert(pomo).second) momentum += 1;
+ }
+
+ if (contacts.insert(&other).second) momentum += 1;
+ return FORCE_MOVE;
+}
+
+void
+BicyclePlatform::update(float elapsed_time)
+{
+ if (!slave) {
+ Sector::current()->add_object(new BicyclePlatform(this));
+ return;
+ }
+ if (!master) {
+ return;
+ }
+ if (this == slave) {
+ angle = master->angle + M_PI;
+ while (angle < 0) { angle += 2*M_PI; }
+ while (angle > 2*M_PI) { angle -= 2*M_PI; }
+ Vector dest = center + Vector(cosf(angle), sinf(angle)) * radius - (bbox.get_size() * 0.5);
+ movement = dest - get_pos();
+ }
+ if (this == master) {
+ float momentum_diff = momentum - slave->momentum;
+ contacts.clear(); momentum = 0;
+ slave->contacts.clear(); slave->momentum = 0;
+
+ float angular_momentum = cosf(angle) * momentum_diff;
+
+ angular_speed += (angular_momentum * elapsed_time) * M_PI;
+ angular_speed *= 1 - elapsed_time * 0.2;
+ angle += angular_speed * elapsed_time;
+ while (angle < 0) { angle += 2*M_PI; }
+ while (angle > 2*M_PI) { angle -= 2*M_PI; }
+ angular_speed = std::min(std::max(angular_speed, static_cast<float>(-128*M_PI*elapsed_time)), static_cast<float>(128*M_PI*elapsed_time));
+ Vector dest = center + Vector(cosf(angle), sinf(angle)) * radius - (bbox.get_size() * 0.5);
+ movement = dest - get_pos();
+
+ center += Vector(angular_speed, 0) * elapsed_time * 32;
+ slave->center += Vector(angular_speed, 0) * elapsed_time * 32;
+
+ }
+}
+
+IMPLEMENT_FACTORY(BicyclePlatform, "bicycle-platform");
+
--- /dev/null
+// $Id$
+//
+// SuperTux - BicyclePlatform
+// Copyright (C) 2007 Christoph Sommer <christoph.sommer@2007.expires.deltadevelopment.de>
+//
+// 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 __BICYCLE_PLATFORM_H__
+#define __BICYCLE_PLATFORM_H__
+
+#include <memory>
+#include <string>
+#include <set>
+#include "object/moving_sprite.hpp"
+#include "object/path.hpp"
+#include "object/path_walker.hpp"
+
+/**
+ * Used to construct a pair of bicycle platforms: If one is pushed down, the other one rises
+ */
+class BicyclePlatform : public MovingSprite
+{
+public:
+ BicyclePlatform(const lisp::Lisp& reader);
+ BicyclePlatform(BicyclePlatform* master);
+ virtual ~BicyclePlatform();
+
+ virtual HitResponse collision(GameObject& other, const CollisionHit& hit);
+ virtual void update(float elapsed_time);
+
+protected:
+ BicyclePlatform* master; /**< pointer to BicyclePlatform that does movement calculation */
+ BicyclePlatform* slave; /**< pointer to BicyclePlatform that reacts to master platform's movement calculation */
+ Vector center; /**< pivot point */
+ float radius; /**< radius of circle */
+ float angle; /**< current angle */
+ float angular_speed; /**< angular speed in rad per second */
+ std::set<GameObject*> contacts; /**< objects that are currently pushing on the platform */
+ float momentum; /** angular momentum in rad per second per second*/
+
+};
+
+#endif
--- /dev/null
+// $Id$
+//
+// SuperTux
+// Copyright (C) 2006 Matthias Braun <matze@braunis.de>
+//
+// 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 <config.h>
+
+#include "block.hpp"
+#include "log.hpp"
+
+#include <stdexcept>
+
+#include "resources.hpp"
+#include "player.hpp"
+#include "sector.hpp"
+#include "sprite/sprite.hpp"
+#include "sprite/sprite_manager.hpp"
+#include "video/drawing_context.hpp"
+#include "lisp/lisp.hpp"
+#include "gameobjs.hpp"
+#include "portable.hpp"
+#include "specialriser.hpp"
+#include "growup.hpp"
+#include "flower.hpp"
+#include "oneup.hpp"
+#include "star.hpp"
+#include "player_status.hpp"
+#include "badguy/badguy.hpp"
+#include "coin.hpp"
+#include "object_factory.hpp"
+#include "lisp/list_iterator.hpp"
+#include "object_factory.hpp"
+#include "level.hpp"
+
+static const float BOUNCY_BRICK_MAX_OFFSET = 8;
+static const float BOUNCY_BRICK_SPEED = 90;
+static const float EPSILON = .0001f;
+
+Block::Block(Sprite* newsprite)
+ : sprite(newsprite), bouncing(false), breaking(false), bounce_dir(0), bounce_offset(0)
+{
+ bbox.set_size(32, 32.1f);
+ set_group(COLGROUP_STATIC);
+ sound_manager->preload("sounds/upgrade.wav");
+ sound_manager->preload("sounds/brick.wav");
+}
+
+Block::~Block()
+{
+ delete sprite;
+}
+
+HitResponse
+Block::collision(GameObject& other, const CollisionHit& )
+{
+ Player* player = dynamic_cast<Player*> (&other);
+ if(player) {
+ if(player->get_bbox().get_top() > get_bbox().get_bottom() - 7.0) {
+ hit(*player);
+ }
+ }
+
+ // only interact with other objects if...
+ // 1) we are bouncing
+ // and
+ // 2) the object is not portable (either never or not currently)
+ Portable* portable = dynamic_cast<Portable*> (&other);
+ if(bouncing && (portable == 0 || (!portable->is_portable()))) {
+
+ // Badguys get killed
+ BadGuy* badguy = dynamic_cast<BadGuy*> (&other);
+ if(badguy) {
+ badguy->kill_fall();
+ }
+
+ // Coins get collected
+ Coin* coin = dynamic_cast<Coin*> (&other);
+ if(coin) {
+ coin->collect();
+ }
+
+ }
+
+ return SOLID;
+}
+
+void
+Block::update(float elapsed_time)
+{
+ if(!bouncing)
+ return;
+
+ float offset = original_y - get_pos().y;
+ if(offset > BOUNCY_BRICK_MAX_OFFSET) {
+ bounce_dir = BOUNCY_BRICK_SPEED;
+ movement = Vector(0, bounce_dir * elapsed_time);
+ if(breaking){
+ break_me();
+ }
+ } else if(offset < BOUNCY_BRICK_SPEED * elapsed_time && bounce_dir > 0) {
+ movement = Vector(0, offset);
+ bounce_dir = 0;
+ bouncing = false;
+ } else {
+ movement = Vector(0, bounce_dir * elapsed_time);
+ }
+}
+
+void
+Block::draw(DrawingContext& context)
+{
+ sprite->draw(context, get_pos(), LAYER_OBJECTS+1);
+}
+
+void
+Block::start_bounce()
+{
+ original_y = bbox.p1.y;
+ bouncing = true;
+ bounce_dir = -BOUNCY_BRICK_SPEED;
+ bounce_offset = 0;
+}
+
+void
+Block::start_break()
+{
+ start_bounce();
+ breaking = true;
+}
+
+//---------------------------------------------------------------------------
+
+BonusBlock::BonusBlock(const Vector& pos, int data)
+ : Block(sprite_manager->create("images/objects/bonus_block/bonusblock.sprite")), object(0)
+{
+ bbox.set_pos(pos);
+ sprite->set_action("normal");
+ switch(data) {
+ case 1: contents = CONTENT_COIN; break;
+ case 2: contents = CONTENT_FIREGROW; break;
+ case 3: contents = CONTENT_STAR; break;
+ case 4: contents = CONTENT_1UP; break;
+ case 5: contents = CONTENT_ICEGROW; break;
+ default:
+ log_warning << "Invalid box contents" << std::endl;
+ contents = CONTENT_COIN;
+ break;
+ }
+}
+
+BonusBlock::BonusBlock(const lisp::Lisp& lisp)
+ : Block(sprite_manager->create("images/objects/bonus_block/bonusblock.sprite"))
+{
+ Vector pos;
+
+ contents = CONTENT_COIN;
+ lisp::ListIterator iter(&lisp);
+ while(iter.next()) {
+ const std::string& token = iter.item();
+ if(token == "x") {
+ iter.value()->get(pos.x);
+ } else if(token == "y") {
+ iter.value()->get(pos.y);
+ } else if(token == "contents") {
+ std::string contentstring;
+ iter.value()->get(contentstring);
+ if(contentstring == "coin") {
+ contents = CONTENT_COIN;
+ } else if(contentstring == "firegrow") {
+ contents = CONTENT_FIREGROW;
+ } else if(contentstring == "icegrow") {
+ contents = CONTENT_ICEGROW;
+ } else if(contentstring == "star") {
+ contents = CONTENT_STAR;
+ } else if(contentstring == "1up") {
+ contents = CONTENT_1UP;
+ } else if(contentstring == "custom") {
+ contents = CONTENT_CUSTOM;
+ } else {
+ log_warning << "Invalid box contents '" << contentstring << "'" << std::endl;
+ }
+ } else {
+ if(contents == CONTENT_CUSTOM) {
+ GameObject* game_object = create_object(token, *(iter.lisp()));
+ object = dynamic_cast<MovingObject*> (game_object);
+ if(object == 0)
+ throw std::runtime_error(
+ "Only MovingObjects are allowed inside BonusBlocks");
+ } else {
+ log_warning << "Invalid element '" << token << "' in bonusblock" << std::endl;
+ }
+ }
+ }
+
+ if(contents == CONTENT_CUSTOM && object == 0)
+ throw std::runtime_error("Need to specify content object for custom block");
+
+ bbox.set_pos(pos);
+}
+
+BonusBlock::~BonusBlock()
+{
+ delete object;
+}
+
+void
+BonusBlock::hit(Player& )
+{
+ try_open();
+}
+
+HitResponse
+BonusBlock::collision(GameObject& other, const CollisionHit& hit){
+ BadGuy* badguy = dynamic_cast<BadGuy*> (&other);
+ if(badguy) {
+ // hit contains no information for collisions with blocks.
+ // Badguy's bottom has to be below the top of the bonusblock
+ // +7 is required to slide over one tile gaps.
+ if( badguy->can_break() && ( badguy->get_bbox().get_bottom() > get_bbox().get_top() + 7.0) ){
+ try_open();
+ }
+ }
+ Portable* portable = dynamic_cast<Portable*> (&other);
+ if(portable) {
+ MovingObject* moving = dynamic_cast<MovingObject*> (&other);
+ if(moving->get_bbox().get_top() > get_bbox().get_bottom() - 7.0) {
+ try_open();
+ }
+ }
+ return Block::collision(other, hit);
+}
+
+void
+BonusBlock::try_open()
+{
+ if(sprite->get_action() == "empty") {
+ sound_manager->play("sounds/brick.wav");
+ return;
+ }
+
+ Sector* sector = Sector::current();
+ assert(sector);
+ assert(sector->player);
+ Player& player = *(sector->player);
+ Direction direction = (player.get_bbox().get_middle().x > get_bbox().get_middle().x) ? LEFT : RIGHT;
+
+ switch(contents) {
+ case CONTENT_COIN:
+ Sector::current()->add_object(new BouncyCoin(get_pos()));
+ player.get_status()->add_coins(1);
+ Sector::current()->get_level()->stats.coins++;
+ break;
+
+ case CONTENT_FIREGROW:
+ if(player.get_status()->bonus == NO_BONUS) {
+ SpecialRiser* riser = new SpecialRiser(get_pos(), new GrowUp(direction));
+ sector->add_object(riser);
+ } else {
+ SpecialRiser* riser = new SpecialRiser(
+ get_pos(), new Flower(FIRE_BONUS));
+ sector->add_object(riser);
+ }
+ sound_manager->play("sounds/upgrade.wav");
+ break;
+
+ case CONTENT_ICEGROW:
+ if(player.get_status()->bonus == NO_BONUS) {
+ SpecialRiser* riser = new SpecialRiser(get_pos(), new GrowUp(direction));
+ sector->add_object(riser);
+ } else {
+ SpecialRiser* riser = new SpecialRiser(
+ get_pos(), new Flower(ICE_BONUS));
+ sector->add_object(riser);
+ }
+ sound_manager->play("sounds/upgrade.wav");
+ break;
+
+ case CONTENT_STAR:
+ sector->add_object(new Star(get_pos() + Vector(0, -32), direction));
+ break;
+
+ case CONTENT_1UP:
+ sector->add_object(new OneUp(get_pos(), direction));
+ break;
+
+ case CONTENT_CUSTOM:
+ SpecialRiser* riser = new SpecialRiser(get_pos(), object);
+ object = 0;
+ sector->add_object(riser);
+ sound_manager->play("sounds/upgrade.wav");
+ break;
+ }
+
+ start_bounce();
+ sprite->set_action("empty");
+}
+
+void
+Block::break_me()
+{
+ Sector* sector = Sector::current();
+ sector->add_object(
+ new BrokenBrick(new Sprite(*sprite), get_pos(), Vector(-100, -400)));
+ sector->add_object(
+ new BrokenBrick(new Sprite(*sprite), get_pos() + Vector(0, 16),
+ Vector(-150, -300)));
+ sector->add_object(
+ new BrokenBrick(new Sprite(*sprite), get_pos() + Vector(16, 0),
+ Vector(100, -400)));
+ sector->add_object(
+ new BrokenBrick(new Sprite(*sprite), get_pos() + Vector(16, 16),
+ Vector(150, -300)));
+ remove_me();
+}
+
+IMPLEMENT_FACTORY(BonusBlock, "bonusblock");
+
+//---------------------------------------------------------------------------
+
+Brick::Brick(const Vector& pos, int data)
+ : Block(sprite_manager->create("images/objects/bonus_block/brick.sprite")), breakable(false),
+ coin_counter(0)
+{
+ bbox.set_pos(pos);
+ if(data == 1)
+ coin_counter = 5;
+ else
+ breakable = true;
+}
+
+void
+Brick::hit(Player& )
+{
+ if(sprite->get_action() == "empty")
+ return;
+
+ try_break(true);
+}
+
+HitResponse
+Brick::collision(GameObject& other, const CollisionHit& hit){
+ BadGuy* badguy = dynamic_cast<BadGuy*> (&other);
+ if(badguy) {
+ // hit contains no information for collisions with blocks.
+ // Badguy's bottom has to be below the top of the brick
+ // +7 is required to slide over one tile gaps.
+ if( badguy->can_break() && ( badguy->get_bbox().get_bottom() > get_bbox().get_top() + 7.0 ) ){
+ try_break(false);
+ }
+ }
+ Portable* portable = dynamic_cast<Portable*> (&other);
+ if(portable) {
+ MovingObject* moving = dynamic_cast<MovingObject*> (&other);
+ if(moving->get_bbox().get_top() > get_bbox().get_bottom() - 7.0) {
+ try_break();
+ }
+ }
+ return Block::collision(other, hit);
+}
+
+void
+Brick::try_break(bool playerhit)
+{
+ if(sprite->get_action() == "empty")
+ return;
+
+ sound_manager->play("sounds/brick.wav");
+ Sector* sector = Sector::current();
+ Player& player = *(sector->player);
+ if(coin_counter > 0) {
+ sector->add_object(new BouncyCoin(get_pos()));
+ coin_counter--;
+ player.get_status()->add_coins(1);
+ if(coin_counter == 0)
+ sprite->set_action("empty");
+ start_bounce();
+ } else if(breakable) {
+ if(playerhit){
+ if(player.is_big()){
+ start_break();
+ return;
+ } else {
+ start_bounce();
+ return;
+ }
+ }
+ break_me();
+ }
+}
+
+//IMPLEMENT_FACTORY(Brick, "brick");
--- /dev/null
+// $Id$
+//
+// SuperTux
+// Copyright (C) 2006 Matthias Braun <matze@braunis.de>
+//
+// 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 __BLOCK_H__
+#define __BLOCK_H__
+
+#include "moving_object.hpp"
+#include "lisp/lisp.hpp"
+
+class Sprite;
+class Player;
+
+class Block : public MovingObject
+{
+public:
+ Block(Sprite* sprite = 0);
+ ~Block();
+
+ virtual HitResponse collision(GameObject& other, const CollisionHit& hit);
+ virtual void update(float elapsed_time);
+ virtual void draw(DrawingContext& context);
+
+protected:
+ friend class FlipLevelTransformer;
+
+ virtual void hit(Player& player) = 0;
+ void start_bounce();
+ void start_break();
+ void break_me();
+
+ Sprite* sprite;
+ bool bouncing;
+ bool breaking;
+ float bounce_dir;
+ float bounce_offset;
+ float original_y;
+
+};
+
+class BonusBlock : public Block
+{
+public:
+ BonusBlock(const Vector& pos, int data);
+ BonusBlock(const lisp::Lisp& lisp);
+ virtual ~BonusBlock();
+ HitResponse collision(GameObject& other, const CollisionHit& hit);
+
+ void try_open();
+
+ enum Contents {
+ CONTENT_COIN,
+ CONTENT_FIREGROW,
+ CONTENT_ICEGROW,
+ CONTENT_STAR,
+ CONTENT_1UP,
+ CONTENT_CUSTOM
+ };
+
+ Contents contents;
+protected:
+ virtual void hit(Player& player);
+
+private:
+ MovingObject* object;
+};
+
+class Brick : public Block
+{
+public:
+ Brick(const Vector& pos, int data);
+
+ void try_break(bool playerhit = false);
+ HitResponse collision(GameObject& other, const CollisionHit& hit);
+
+protected:
+ virtual void hit(Player& player);
+
+private:
+ bool breakable;
+ int coin_counter;
+};
+
+#endif
--- /dev/null
+// $Id$
+//
+// SuperTux
+// Copyright (C) 2006 Matthias Braun <matze@braunis.de>
+//
+// 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 <config.h>
+
+#include <math.h>
+#include "bullet.hpp"
+#include "resources.hpp"
+#include "camera.hpp"
+#include "sector.hpp"
+#include "sprite/sprite_manager.hpp"
+#include "badguy/badguy.hpp"
+#include "main.hpp"
+
+namespace {
+ const float BULLET_XM = 600;
+ const float BULLET_STARTING_YM = 0;
+}
+
+Bullet::Bullet(const Vector& pos, float xm, int dir, BonusType type)
+ : life_count(3), type(type)
+{
+ float speed = dir == RIGHT ? BULLET_XM : -BULLET_XM;
+ physic.set_velocity_x(speed + xm);
+
+ if(type == FIRE_BONUS) {
+ sprite.reset(sprite_manager->create("images/objects/bullets/firebullet.sprite"));
+ } else if(type == ICE_BONUS) {
+ life_count = 10;
+ sprite.reset(sprite_manager->create("images/objects/bullets/icebullet.sprite"));
+ }
+
+ bbox.set_pos(pos);
+ bbox.set_size(sprite->get_current_hitbox_width(), sprite->get_current_hitbox_height());
+}
+
+Bullet::~Bullet()
+{
+}
+
+void
+Bullet::update(float elapsed_time)
+{
+ // remove bullet when it's offscreen
+ float scroll_x =
+ Sector::current()->camera->get_translation().x;
+ float scroll_y =
+ Sector::current()->camera->get_translation().y;
+ if (get_pos().x < scroll_x ||
+ get_pos().x > scroll_x + SCREEN_WIDTH ||
+// get_pos().y < scroll_y ||
+ get_pos().y > scroll_y + SCREEN_HEIGHT ||
+ life_count <= 0) {
+ remove_me();
+ return;
+ }
+
+ movement = physic.get_movement(elapsed_time);
+}
+
+void
+Bullet::draw(DrawingContext& context)
+{
+ sprite->draw(context, get_pos(), LAYER_OBJECTS);
+}
+
+void
+Bullet::collision_solid(const CollisionHit& hit)
+{
+ if(hit.top || hit.bottom) {
+ physic.set_velocity_y(-physic.get_velocity_y());
+ life_count--;
+ } else if(hit.left || hit.right) {
+ if(type == ICE_BONUS) {
+ physic.set_velocity_x(-physic.get_velocity_x());
+ life_count--;
+ } else
+ remove_me();
+ }
+}
+
+void
+Bullet::ricochet(GameObject& , const CollisionHit& hit)
+{
+ collision_solid(hit);
+}
+
+HitResponse
+Bullet::collision(GameObject& , const CollisionHit& )
+{
+ return FORCE_MOVE;
+}
--- /dev/null
+// $Id$
+//
+// SuperTux
+// Copyright (C) 2006 Matthias Braun <matze@braunis.de>
+//
+// 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 __BULLET_H__
+#define __BULLET_H__
+
+#include "moving_object.hpp"
+#include "physic.hpp"
+#include "sprite/sprite.hpp"
+#include "player_status.hpp"
+
+class Bullet : public MovingObject, private UsesPhysic
+{
+public:
+ Bullet(const Vector& pos, float xm, int dir, BonusType type);
+ ~Bullet();
+
+ void update(float elapsed_time);
+ void draw(DrawingContext& context);
+ void collision_solid(const CollisionHit& hit);
+ HitResponse collision(GameObject& other, const CollisionHit& hit);
+
+ /**
+ * Makes bullet bounce off an object (that got hit).
+ * To be called by the collision handler of that object.
+ * Note that the @c hit parameter is filled in as perceived by the object, not by the bullet.
+ */
+ void ricochet(GameObject& other, const CollisionHit& hit);
+
+ BonusType get_type()
+ {
+ return type;
+ }
+
+private:
+ int life_count;
+ std::auto_ptr<Sprite> sprite;
+ BonusType type;
+};
+
+#endif
--- /dev/null
+// $Id$
+//
+// SuperTux
+// Copyright (C) 2006 Matthias Braun <matze@braunis.de>
+//
+// 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 <config.h>
+
+#include <stdexcept>
+#include <sstream>
+#include <cmath>
+
+#include "lisp/lisp.hpp"
+#include "lisp/writer.hpp"
+#include "lisp/list_iterator.hpp"
+#include "lisp/parser.hpp"
+#include "scripting/camera.hpp"
+#include "scripting/squirrel_util.hpp"
+#include "camera.hpp"
+#include "player.hpp"
+#include "tilemap.hpp"
+#include "game_session.hpp"
+#include "sector.hpp"
+#include "main.hpp"
+#include "object_factory.hpp"
+#include "log.hpp"
+#include "path.hpp"
+#include "path_walker.hpp"
+
+class CameraConfig
+{
+public:
+ // 0 = No, 1 = Fix, 2 = Mario/Yoshi, 3 = Kirby
+ int ymode;
+ // as above, 4 = super metroid like
+ int xmode;
+ float kirby_rectsize_x;
+ float kirby_rectsize_y;
+ // where to fix the player (used for Yoshi and Fix camera)
+ float target_y;
+ float target_x;
+ // maximum scrolling speed in Y direction
+ float max_speed_y;
+ float max_speed_x;
+ // factor to dynamically increase max_speed_x based on player speed
+ float dynamic_max_speed_x;
+
+ // time the player has to face into the other direction before we assume a
+ // changed direction
+ float dirchange_time;
+ // edge_x
+ float edge_x;
+ // when too change from noscroll mode back to lookahead left/right mode
+ // set to <= 0 to disable noscroll mode
+ float sensitive_x;
+
+ float clamp_y;
+ float clamp_x;
+
+ float dynamic_speed_sm;
+
+ CameraConfig() {
+ xmode = 1;
+ ymode = 1;
+ target_x = .5f;
+ target_y = 2.f/3.f;
+ max_speed_y = 140;
+ max_speed_x = 130;
+ clamp_x = 1.f/6.f;
+ clamp_y = 1.f/6.f;
+ kirby_rectsize_x = 0.2f;
+ kirby_rectsize_y = 0.34f;
+ edge_x = 1.f/3.f;
+ sensitive_x = 1.f/4.f;
+ dynamic_max_speed_x = 1.0;
+ dirchange_time = 0.2f;
+ dynamic_speed_sm = 1.0f;
+ }
+
+ void load(const std::string& filename)
+ {
+ lisp::Parser parser;
+ const lisp::Lisp* root = parser.parse(filename);
+ const lisp::Lisp* camconfig = root->get_lisp("camera-config");
+ if(camconfig == NULL)
+ throw std::runtime_error("file is not a camera config file.");
+
+ camconfig->get("xmode", xmode);
+ camconfig->get("ymode", ymode);
+ camconfig->get("target-x", target_x);
+ camconfig->get("target-y", target_y);
+ camconfig->get("max-speed-x", max_speed_x);
+ camconfig->get("max-speed-y", max_speed_y);
+ camconfig->get("dynamic-max-speed-x", dynamic_max_speed_x);
+ camconfig->get("dirchange-time", dirchange_time);
+ camconfig->get("clamp-x", clamp_x);
+ camconfig->get("clamp-y", clamp_y);
+ camconfig->get("kirby-rectsize-x", kirby_rectsize_x);
+ camconfig->get("kirby-rectsize-y", kirby_rectsize_y);
+ camconfig->get("edge-x", edge_x);
+ camconfig->get("sensitive-x", sensitive_x);
+ camconfig->get("dynamic-speed-sm", dynamic_speed_sm);
+ }
+};
+
+Camera::Camera(Sector* newsector, std::string name)
+ : mode(NORMAL), sector(newsector), lookahead_mode(LOOKAHEAD_NONE),
+ lookahead_pos(0)
+{
+ this->name = name;
+ config = new CameraConfig();
+ reload_config();
+}
+
+Camera::~Camera()
+{
+ delete config;
+}
+
+void
+Camera::expose(HSQUIRRELVM vm, SQInteger table_idx)
+{
+ if(name.empty()) return;
+ Scripting::Camera* interface = new Scripting::Camera(this);
+ expose_object(vm, table_idx, interface, name, true);
+}
+
+void
+Camera::unexpose(HSQUIRRELVM vm, SQInteger table_idx)
+{
+ if(name.empty()) return;
+ Scripting::unexpose_object(vm, table_idx, name);
+}
+
+void
+Camera::draw(DrawingContext& )
+{
+}
+
+const Vector&
+Camera::get_translation() const
+{
+ return translation;
+}
+
+void
+Camera::parse(const lisp::Lisp& reader)
+{
+ std::string modename;
+
+ reader.get("mode", modename);
+ if(modename == "normal") {
+ mode = NORMAL;
+ } else if(modename == "autoscroll") {
+ mode = AUTOSCROLL;
+
+ const lisp::Lisp* pathLisp = reader.get_lisp("path");
+ if(pathLisp == NULL)
+ throw std::runtime_error("No path specified in autoscroll camera.");
+
+ autoscroll_path.reset(new Path());
+ autoscroll_path->read(*pathLisp);
+ autoscroll_walker.reset(new PathWalker(autoscroll_path.get()));
+ } else if(modename == "manual") {
+ mode = MANUAL;
+ } else {
+ std::stringstream str;
+ str << "invalid camera mode '" << modename << "'found in worldfile.";
+ throw std::runtime_error(str.str());
+ }
+}
+
+void
+Camera::write(lisp::Writer& writer)
+{
+ writer.start_list("camera");
+
+ if(mode == NORMAL) {
+ writer.write_string("mode", "normal");
+ } else if(mode == AUTOSCROLL) {
+ writer.write_string("mode", "autoscroll");
+ autoscroll_path->write(writer);
+ } else if(mode == MANUAL) {
+ writer.write_string("mode", "manual");
+ }
+
+ writer.end_list("camera");
+}
+
+void
+Camera::reset(const Vector& tuxpos)
+{
+ translation.x = tuxpos.x - SCREEN_WIDTH/3 * 2;
+ translation.y = tuxpos.y - SCREEN_HEIGHT/2;
+ shakespeed = 0;
+ shaketimer.stop();
+ keep_in_bounds(translation);
+}
+
+void
+Camera::shake(float time, float x, float y)
+{
+ shaketimer.start(time);
+ shakedepth_x = x;
+ shakedepth_y = y;
+ shakespeed = M_PI/2 / time;
+}
+
+void
+Camera::scroll_to(const Vector& goal, float scrolltime)
+{
+ scroll_from = translation;
+ scroll_goal = goal;
+ keep_in_bounds(scroll_goal);
+
+ scroll_to_pos = 0;
+ scrollspeed = 1.0 / scrolltime;
+ mode = SCROLLTO;
+}
+
+static const float EPSILON = .00001f;
+static const float MAX_SPEED_Y = 140;
+
+void
+Camera::update(float elapsed_time)
+{
+ switch(mode) {
+ case NORMAL:
+ update_scroll_normal(elapsed_time);
+ break;
+ case AUTOSCROLL:
+ update_scroll_autoscroll(elapsed_time);
+ break;
+ case SCROLLTO:
+ update_scroll_to(elapsed_time);
+ break;
+ default:
+ break;
+ }
+ shake();
+}
+
+void
+Camera::reload_config()
+{
+ config->load("camera.cfg");
+}
+
+float clamp(float val, float min, float max)
+{
+ if(val < min)
+ return min;
+ if(val > max)
+ return max;
+
+ return val;
+}
+
+void
+Camera::keep_in_bounds(Vector& translation)
+{
+ float width = sector->get_width();
+ float height = sector->get_height();
+
+ // don't scroll before the start or after the level's end
+ translation.x = clamp(translation.x, 0, width - SCREEN_WIDTH);
+ translation.y = clamp(translation.y, 0, height - SCREEN_HEIGHT);
+
+ if (height < SCREEN_HEIGHT)
+ translation.y = height/2.0 - SCREEN_HEIGHT/2.0;
+ if (width < SCREEN_WIDTH)
+ translation.x = width/2.0 - SCREEN_WIDTH/2.0;
+}
+
+void
+Camera::shake()
+{
+ if(shaketimer.started()) {
+ translation.x -= sin(shaketimer.get_timegone() * shakespeed) * shakedepth_x;
+ translation.y -= sin(shaketimer.get_timegone() * shakespeed) * shakedepth_y;
+ }
+}
+
+void
+Camera::update_scroll_normal(float elapsed_time)
+{
+ const CameraConfig& config = *(this->config);
+ Player* player = sector->player;
+ const Vector& player_pos = player->get_bbox().get_middle();
+ static Vector last_player_pos = player_pos;
+ Vector player_delta = player_pos - last_player_pos;
+ last_player_pos = player_pos;
+
+ // check that we don't have division by zero later
+ if(elapsed_time < EPSILON)
+ return;
+
+ /****** Vertical Scrolling part ******/
+ int xmode = config.xmode;
+ int ymode = config.ymode;
+
+ if(player->is_dying() || sector->get_height() == 19*32) {
+ ymode = 0;
+ }
+ if(player->is_dying())
+ xmode = 0;
+
+ if(ymode == 1) {
+ translation.y = player_pos.y - SCREEN_HEIGHT * config.target_y;
+ }
+ if(ymode == 2) {
+ // target_y is the high we target our scrolling at. This is not always the
+ // high of the player, but if he is jumping upwards we should use the
+ // position where he last touched the ground. (this probably needs
+ // exceptions for trampolines and similar things in the future)
+ float target_y;
+ if(player->fall_mode == Player::JUMPING)
+ target_y = player->last_ground_y + player->get_bbox().get_height();
+ else
+ target_y = player->get_bbox().p2.y;
+ target_y -= SCREEN_HEIGHT * config.target_y;
+
+ // delta_y is the distance we'd have to travel to directly reach target_y
+ float delta_y = translation.y - target_y;
+ // speed is the speed the camera would need to reach target_y in this frame
+ float speed_y = delta_y / elapsed_time;
+
+ // limit the camera speed when jumping upwards
+ if(player->fall_mode != Player::FALLING
+ && player->fall_mode != Player::TRAMPOLINE_JUMP) {
+ speed_y = clamp(speed_y, -config.max_speed_y, config.max_speed_y);
+ }
+
+ // scroll with calculated speed
+ translation.y -= speed_y * elapsed_time;
+ }
+ if(ymode == 3) {
+ float halfsize = config.kirby_rectsize_y * 0.5f;
+ translation.y = clamp(translation.y,
+ player_pos.y - SCREEN_HEIGHT * (0.5f + halfsize),
+ player_pos.y - SCREEN_HEIGHT * (0.5f - halfsize));
+ }
+ if(ymode == 4) {
+ // TODO...
+ }
+
+ if(ymode != 0 && config.clamp_y > 0) {
+ translation.y = clamp(translation.y,
+ player_pos.y - SCREEN_HEIGHT * (1-config.clamp_y),
+ player_pos.y - SCREEN_HEIGHT * config.clamp_y);
+ }
+
+ /****** Horizontal scrolling part *******/
+
+ if(xmode == 1) {
+ translation.x = player_pos.x - SCREEN_WIDTH * config.target_x;
+ }
+ if(xmode == 2) {
+ // our camera is either in leftscrolling, rightscrolling or
+ // nonscrollingmode.
+ //
+ // when suddenly changing directions while scrolling into the other
+ // direction abort scrolling, since tux might be going left/right at a
+ // relatively small part of the map (like when jumping upwards)
+
+ // Find out direction in which the player moves
+ LookaheadMode walkDirection;
+ if (player_delta.x < -EPSILON) walkDirection = LOOKAHEAD_LEFT;
+ else if (player_delta.x > EPSILON) walkDirection = LOOKAHEAD_RIGHT;
+ else if (player->dir == ::LEFT) walkDirection = LOOKAHEAD_LEFT;
+ else walkDirection = LOOKAHEAD_RIGHT;
+
+ float LEFTEND, RIGHTEND;
+ if(config.sensitive_x > 0) {
+ LEFTEND = SCREEN_WIDTH * config.sensitive_x;
+ RIGHTEND = SCREEN_WIDTH * (1-config.sensitive_x);
+ } else {
+ LEFTEND = SCREEN_WIDTH;
+ RIGHTEND = 0;
+ }
+
+ if(lookahead_mode == LOOKAHEAD_NONE) {
+ /* if we're undecided then look if we crossed the left or right
+ * "sensitive" area */
+ if(player_pos.x < translation.x + LEFTEND) {
+ lookahead_mode = LOOKAHEAD_LEFT;
+ } else if(player_pos.x > translation.x + RIGHTEND) {
+ lookahead_mode = LOOKAHEAD_RIGHT;
+ }
+ /* at the ends of a level it's obvious which way we will go */
+ if(player_pos.x < SCREEN_WIDTH*0.5) {
+ lookahead_mode = LOOKAHEAD_RIGHT;
+ } else if(player_pos.x >= sector->get_width() - SCREEN_WIDTH*0.5) {
+ lookahead_mode = LOOKAHEAD_LEFT;
+ }
+
+ changetime = -1;
+ } else if(lookahead_mode != walkDirection) {
+ /* player changed direction while camera was scrolling...
+ * he has to do this for a certain time to add robustness against
+ * sudden changes */
+ if(changetime < 0) {
+ changetime = game_time;
+ } else if(game_time - changetime > config.dirchange_time) {
+ if(lookahead_mode == LOOKAHEAD_LEFT &&
+ player_pos.x > translation.x + RIGHTEND) {
+ lookahead_mode = LOOKAHEAD_RIGHT;
+ } else if(lookahead_mode == LOOKAHEAD_RIGHT &&
+ player_pos.x < translation.x + LEFTEND) {
+ lookahead_mode = LOOKAHEAD_LEFT;
+ } else {
+ lookahead_mode = LOOKAHEAD_NONE;
+ }
+ }
+ } else {
+ changetime = -1;
+ }
+
+ LEFTEND = SCREEN_WIDTH * config.edge_x;
+ RIGHTEND = SCREEN_WIDTH * (1-config.edge_x);
+
+ // calculate our scroll target depending on scroll mode
+ float target_x;
+ if(lookahead_mode == LOOKAHEAD_LEFT)
+ target_x = player_pos.x - RIGHTEND;
+ else if(lookahead_mode == LOOKAHEAD_RIGHT)
+ target_x = player_pos.x - LEFTEND;
+ else
+ target_x = translation.x;
+
+ // that's the distance we would have to travel to reach target_x
+ float delta_x = translation.x - target_x;
+ // the speed we'd need to travel to reach target_x in this frame
+ float speed_x = delta_x / elapsed_time;
+
+ // limit our speed
+ float player_speed_x = player_delta.x / elapsed_time;
+ float maxv = config.max_speed_x + (fabsf(player_speed_x * config.dynamic_max_speed_x));
+ speed_x = clamp(speed_x, -maxv, maxv);
+
+ // If player is peeking scroll in that direction. Fast.
+ if(player->peeking_direction() == ::LEFT) {
+ speed_x = config.max_speed_x;
+ }
+ if(player->peeking_direction() == ::RIGHT) {
+ speed_x = -config.max_speed_x;
+ }
+
+ // apply scrolling
+ translation.x -= speed_x * elapsed_time;
+ }
+ if(xmode == 3) {
+ float halfsize = config.kirby_rectsize_x * 0.5f;
+ translation.x = clamp(translation.x,
+ player_pos.x - SCREEN_WIDTH * (0.5f + halfsize),
+ player_pos.x - SCREEN_WIDTH * (0.5f - halfsize));
+ }
+ if(xmode == 4) {
+ float LEFTEND = SCREEN_WIDTH * config.edge_x;
+ float RIGHTEND = SCREEN_WIDTH * (1 - config.edge_x);
+
+ if (player_delta.x < -EPSILON) {
+ // walking left
+ lookahead_pos -= player_delta.x * config.dynamic_speed_sm;
+
+ if(lookahead_pos > RIGHTEND) {
+ lookahead_pos = RIGHTEND;
+ }
+ } else if (player_delta.x > EPSILON) {
+ // walking right
+ lookahead_pos -= player_delta.x * config.dynamic_speed_sm;
+ if(lookahead_pos < LEFTEND) {
+ lookahead_pos = LEFTEND;
+ }
+ }
+
+ if(player->peeking_direction() == ::LEFT) {
+ lookahead_pos += config.max_speed_x * elapsed_time * 3.0f;
+ } else if(player->peeking_direction() == ::RIGHT) {
+ lookahead_pos -= config.max_speed_x * elapsed_time * 3.0f;
+ }
+
+ // adjust for level ends
+ if (player_pos.x < LEFTEND) {
+ lookahead_pos = LEFTEND;
+ }
+ if (player_pos.x > sector->get_width() - LEFTEND) {
+ lookahead_pos = RIGHTEND;
+ }
+
+ translation.x = player_pos.x - lookahead_pos;
+ }
+
+ if(xmode != 0 && config.clamp_x > 0) {
+ translation.x = clamp(translation.x,
+ player_pos.x - SCREEN_WIDTH * (1-config.clamp_x),
+ player_pos.x - SCREEN_WIDTH * config.clamp_x);
+ }
+
+ keep_in_bounds(translation);
+}
+
+void
+Camera::update_scroll_autoscroll(float elapsed_time)
+{
+ Player* player = sector->player;
+ if(player->is_dying())
+ return;
+
+ translation = autoscroll_walker->advance(elapsed_time);
+
+ keep_in_bounds(translation);
+}
+
+void
+Camera::update_scroll_to(float elapsed_time)
+{
+ scroll_to_pos += elapsed_time * scrollspeed;
+ if(scroll_to_pos >= 1.0) {
+ mode = MANUAL;
+ translation = scroll_goal;
+ return;
+ }
+
+ translation = scroll_from + (scroll_goal - scroll_from) * scroll_to_pos;
+}
--- /dev/null
+// $Id$
+//
+// SuperTux
+// Copyright (C) 2006 Matthias Braun <matze@braunis.de>
+//
+// 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_CAMERA_H
+#define SUPERTUX_CAMERA_H
+
+#include <vector>
+#include <cassert>
+#include <memory>
+
+#include "math/vector.hpp"
+#include "game_object.hpp"
+#include "video/drawing_context.hpp"
+#include "serializable.hpp"
+#include "timer.hpp"
+#include "script_interface.hpp"
+
+namespace lisp {
+class Lisp;
+}
+
+class Sector;
+class Path;
+class PathWalker;
+class CameraConfig;
+
+class Camera : public GameObject, public Serializable, public ScriptInterface
+{
+public:
+ Camera(Sector* sector, std::string name = "");
+ virtual ~Camera();
+
+ /// parse camera mode from lisp file
+ void parse(const lisp::Lisp& reader);
+ /// write camera mode to a lisp file
+ virtual void write(lisp::Writer& writer);
+
+ /// reset camera postion
+ void reset(const Vector& tuxpos);
+
+ /** return camera position */
+ const Vector& get_translation() const;
+
+ virtual void update(float elapsed_time);
+
+ virtual void draw(DrawingContext& );
+
+ virtual void expose(HSQUIRRELVM vm, SQInteger table_idx);
+ virtual void unexpose(HSQUIRRELVM vm, SQInteger table_idx);
+
+ // shake camera in a direction 1 time
+ void shake(float speed, float x, float y);
+
+ void set_scrolling(int scroll_x, int scroll_y)
+ {
+ translation.x = scroll_x;
+ translation.y = scroll_y;
+ }
+
+ /**
+ * scroll the upper left edge of the camera in scrolltime seconds
+ * to the position goal
+ */
+ void scroll_to(const Vector& goal, float scrolltime);
+
+ void reload_config();
+
+ enum CameraMode
+ {
+ NORMAL, AUTOSCROLL, SCROLLTO, MANUAL
+ };
+ CameraMode mode;
+
+private:
+ void update_scroll_normal(float elapsed_time);
+ void update_scroll_autoscroll(float elapsed_time);
+ void update_scroll_to(float elapsed_time);
+ void keep_in_bounds(Vector& vector);
+ void shake();
+
+ /**
+ * The camera basically provides lookeahead on the left or right side
+ * or is undecided.
+ */
+ enum LookaheadMode {
+ LOOKAHEAD_NONE, LOOKAHEAD_LEFT, LOOKAHEAD_RIGHT
+ };
+
+ Vector translation;
+
+ Sector* sector;
+
+ // normal mode
+ LookaheadMode lookahead_mode;
+ float changetime;
+ float lookahead_pos;
+
+ // autoscroll mode
+ std::auto_ptr<Path> autoscroll_path;
+ std::auto_ptr<PathWalker> autoscroll_walker;
+
+ // shaking
+ Timer shaketimer;
+ float shakespeed;
+ float shakedepth_x;
+ float shakedepth_y;
+
+ // scrollto mode
+ Vector scroll_from;
+ Vector scroll_goal;
+ float scroll_to_pos;
+ float scrollspeed;
+
+ CameraConfig *config;
+};
+
+#endif /*SUPERTUX_CAMERA_H*/
--- /dev/null
+// $Id$
+//
+// SuperTux
+// Copyright (C) 2006 Christoph Sommer <christoph.sommer@2006.expires.deltadevelopment.de>
+//
+// 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 <config.h>
+
+#include "candle.hpp"
+#include "scripting/candle.hpp"
+#include "scripting/squirrel_util.hpp"
+#include "sector.hpp"
+#include "object/sprite_particle.hpp"
+#include "object_factory.hpp"
+#include "random_generator.hpp"
+
+Candle::Candle(const lisp::Lisp& lisp)
+ : MovingSprite(lisp, "images/objects/candle/candle.sprite", LAYER_BACKGROUNDTILES+1, COLGROUP_DISABLED), burning(true),
+ candle_light_1("images/objects/candle/candle-light-1.png"),
+ candle_light_2("images/objects/candle/candle-light-2.png")
+{
+ lisp.get("name", name);
+ lisp.get("burning", burning);
+
+ if (burning) {
+ sprite->set_action("on");
+ } else {
+ sprite->set_action("off");
+ }
+
+}
+
+void
+Candle::draw(DrawingContext& context)
+{
+ // draw regular sprite
+ sprite->draw(context, get_pos(), layer);
+
+ // draw on lightmap
+ if (burning) {
+ Vector pos = get_pos() + (bbox.get_size() - candle_light_1.get_size()) / 2;
+ context.push_target();
+ context.set_target(DrawingContext::LIGHTMAP);
+ // draw approx. 1 in 10 frames darker. Makes the candle flicker
+ if (systemRandom.rand(10) != 0) {
+ context.draw_surface(&candle_light_1, pos, layer);
+ } else {
+ context.draw_surface(&candle_light_2, pos, layer);
+ }
+ context.pop_target();
+ }
+}
+
+HitResponse
+Candle::collision(GameObject&, const CollisionHit& )
+{
+ return FORCE_MOVE;
+}
+
+void
+Candle::expose(HSQUIRRELVM vm, SQInteger table_idx)
+{
+ if (name.empty()) return;
+ Scripting::Candle* interface = new Scripting::Candle(this);
+ expose_object(vm, table_idx, interface, name, true);
+}
+
+void
+Candle::unexpose(HSQUIRRELVM vm, SQInteger table_idx)
+{
+ if (name.empty()) return;
+ Scripting::unexpose_object(vm, table_idx, name);
+}
+
+void
+Candle::puff_smoke()
+{
+ Vector ppos = bbox.get_middle();
+ Vector pspeed = Vector(0, -150);
+ Vector paccel = Vector(0,0);
+ Sector::current()->add_object(new SpriteParticle("images/objects/particles/smoke.sprite", "default", ppos, ANCHOR_MIDDLE, pspeed, paccel, LAYER_BACKGROUNDTILES+2));
+}
+
+bool
+Candle::get_burning()
+{
+ return burning;
+}
+
+void
+Candle::set_burning(bool burning)
+{
+ if (this->burning == burning) return;
+ this->burning = burning;
+ if (burning) {
+ sprite->set_action("on");
+ puff_smoke();
+ } else {
+ sprite->set_action("off");
+ puff_smoke();
+ }
+}
+
+IMPLEMENT_FACTORY(Candle, "candle");
--- /dev/null
+// $Id$
+//
+// SuperTux
+// Copyright (C) 2006 Christoph Sommer <christoph.sommer@2006.expires.deltadevelopment.de>
+//
+// 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 __CANDLE_H__
+#define __CANDLE_H__
+
+#include <string>
+
+#include "lisp/lisp.hpp"
+#include "object/moving_sprite.hpp"
+#include "script_interface.hpp"
+#include "video/surface.hpp"
+
+/**
+ * A burning candle: Simple, scriptable level decoration.
+ */
+class Candle : public MovingSprite, public ScriptInterface
+{
+public:
+ Candle(const lisp::Lisp& lisp);
+ virtual Candle* clone() const { return new Candle(*this); }
+ virtual void draw(DrawingContext& context);
+
+ HitResponse collision(GameObject& other, const CollisionHit& hit);
+
+ virtual void expose(HSQUIRRELVM vm, SQInteger table_idx);
+ virtual void unexpose(HSQUIRRELVM vm, SQInteger table_idx);
+
+ /**
+ * @name Scriptable Methods
+ * @{
+ */
+ void puff_smoke(); /**< spawn a puff of smoke */
+ bool get_burning(); /**< returns true if candle is lighted */
+ void set_burning(bool burning); /**< true: light candle, false: extinguish candle */
+ /**
+ * @}
+ */
+
+private:
+ bool burning; /**< true if candle is currently lighted */
+ Surface candle_light_1; /**< drawn to lightmap */
+ Surface candle_light_2; /**< drawn to lightmap (alternative image) */
+
+};
+
+#endif
--- /dev/null
+// $Id$
+//
+// SuperTux
+// Copyright (C) 2006 Matthias Braun <matze@braunis.de>
+//
+// 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 <config.h>
+
+#include "coin.hpp"
+#include "resources.hpp"
+#include "video/drawing_context.hpp"
+#include "sprite/sprite_manager.hpp"
+#include "player.hpp"
+#include "sector.hpp"
+#include "player_status.hpp"
+#include "gameobjs.hpp"
+#include "statistics.hpp"
+#include "object_factory.hpp"
+#include "level.hpp"
+#include "random_generator.hpp"
+#include "audio/sound_source.hpp"
+#include "audio/sound_manager.hpp"
+#include "timer.hpp"
+
+Coin::Coin(const Vector& pos)
+ : MovingSprite(pos, "images/objects/coin/coin.sprite", LAYER_TILES, COLGROUP_TOUCHABLE)
+{
+ sound_manager->preload("sounds/coin.wav");
+}
+
+Coin::Coin(const lisp::Lisp& reader)
+ : MovingSprite(reader, "images/objects/coin/coin.sprite", LAYER_TILES, COLGROUP_TOUCHABLE)
+{
+ sound_manager->preload("sounds/coin.wav");
+}
+
+void
+Coin::collect()
+{
+ // TODO: commented out musical code. Maybe fork this for a special "MusicalCoin" object?
+ /*
+ static Timer sound_timer;
+ static int pitch_one = 128;
+ static float last_pitch = 1;
+ float pitch = 1;
+
+ int tile = static_cast<int>(get_pos().y / 32);
+
+ if (!sound_timer.started()) {
+ pitch_one = tile;
+ pitch = 1;
+ last_pitch = 1;
+ }
+ else if (sound_timer.get_timegone() < 0.02) {
+ pitch = last_pitch;
+ }
+ else
+ {
+ switch ((pitch_one - tile) % 7) {
+ case -6:
+ pitch = 1.0/2;
+ break;
+ case -5:
+ pitch = 5.0/8;
+ break;
+ case -4:
+ pitch = 4.0/6;
+ break;
+ case -3:
+ pitch = 3.0/4;
+ break;
+ case -2:
+ pitch = 5.0/6;
+ break;
+ case -1:
+ pitch = 9.0/10;
+ break;
+ case 0:
+ pitch = 1.0;
+ break;
+ case 1:
+ pitch = 9.0/8;
+ break;
+ case 2:
+ pitch = 5.0/4;
+ break;
+ case 3:
+ pitch = 4.0/3;
+ break;
+ case 4:
+ pitch = 3.0/2;
+ break;
+ case 5:
+ pitch = 5.0/3;
+ break;
+ case 6:
+ pitch = 9.0/5;
+ break;
+ }
+ last_pitch = pitch;
+ }
+ sound_timer.start(1);
+
+ SoundSource* soundSource = sound_manager->create_sound_source("sounds/coin.wav");
+ soundSource->set_position(get_pos());
+ soundSource->set_pitch(pitch);
+ soundSource->play();
+ sound_manager->manage_source(soundSource);
+*/
+ Sector::current()->player->get_status()->add_coins(1);
+ Sector::current()->add_object(new BouncyCoin(get_pos()));
+ Sector::current()->get_level()->stats.coins++;
+ remove_me();
+}
+
+HitResponse
+Coin::collision(GameObject& other, const CollisionHit& )
+{
+ Player* player = dynamic_cast<Player*>(&other);
+ if(player == 0)
+ return ABORT_MOVE;
+
+ collect();
+ return ABORT_MOVE;
+}
+
+IMPLEMENT_FACTORY(Coin, "coin");
--- /dev/null
+// $Id$
+//
+// SuperTux
+// Copyright (C) 2006 Matthias Braun <matze@braunis.de>
+//
+// 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 __COIN_H__
+#define __COIN_H__
+
+#include "moving_sprite.hpp"
+#include "lisp/lisp.hpp"
+
+class Coin : public MovingSprite
+{
+public:
+ Coin(const Vector& pos);
+ Coin(const lisp::Lisp& reader);
+
+ HitResponse collision(GameObject& other, const CollisionHit& hit);
+
+ void collect();
+};
+
+#endif
--- /dev/null
+// $Id$
+//
+// SuperTux
+// Copyright (C) 2006 Matthias Braun <matze@braunis.de>
+//
+// 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 <config.h>
+#include "display_effect.hpp"
+
+#include <assert.h>
+#include "video/drawing_context.hpp"
+#include "scripting/squirrel_util.hpp"
+#include "main.hpp"
+
+static const float BORDER_SIZE = 75;
+
+DisplayEffect::DisplayEffect(std::string name)
+ : screen_fade(NO_FADE), screen_fadetime(0), screen_fading(0),
+ border_fade(NO_FADE), border_fadetime(0), border_size(0), black(false),
+ borders(false)
+{
+ this->name = name;
+}
+
+DisplayEffect::~DisplayEffect()
+{
+}
+
+void
+DisplayEffect::expose(HSQUIRRELVM vm, SQInteger table_idx)
+{
+ if (name.empty()) return;
+ expose_object(vm, table_idx, dynamic_cast<Scripting::DisplayEffect *>(this), name, false);
+}
+
+void
+DisplayEffect::unexpose(HSQUIRRELVM vm, SQInteger table_idx)
+{
+ if (name.empty()) return;
+ Scripting::unexpose_object(vm, table_idx, name);
+}
+
+void
+DisplayEffect::update(float elapsed_time)
+{
+ switch(screen_fade) {
+ case NO_FADE:
+ break;
+ case FADE_IN:
+ screen_fading -= elapsed_time;
+ if(screen_fading < 0) {
+ screen_fade = NO_FADE;
+ }
+ break;
+ case FADE_OUT:
+ screen_fading -= elapsed_time;
+ if(screen_fading < 0) {
+ screen_fade = NO_FADE;
+ black = true;
+ }
+ break;
+ default:
+ assert(false);
+ }
+
+ switch(border_fade) {
+ case NO_FADE:
+ break;
+ case FADE_IN:
+ border_fading -= elapsed_time;
+ if(border_fading < 0) {
+ border_fade = NO_FADE;
+ }
+ border_size = (border_fadetime - border_fading)
+ / border_fadetime * BORDER_SIZE;
+ break;
+ case FADE_OUT:
+ border_fading -= elapsed_time;
+ if(border_fading < 0) {
+ borders = false;
+ border_fade = NO_FADE;
+ }
+ border_size = border_fading / border_fadetime * BORDER_SIZE;
+ break;
+ default:
+ assert(false);
+ }
+}
+
+void
+DisplayEffect::draw(DrawingContext& context)
+{
+ context.push_transform();
+ context.set_translation(Vector(0, 0));
+
+ if(black || screen_fade != NO_FADE) {
+ float alpha;
+ if(black) {
+ alpha = 1.0f;
+ } else {
+ switch(screen_fade) {
+ case FADE_IN:
+ alpha = screen_fading / screen_fadetime;
+ break;
+ case FADE_OUT:
+ alpha = (screen_fadetime - screen_fading) / screen_fadetime;
+ break;
+ default:
+ alpha = 0;
+ assert(false);
+ }
+ }
+ context.draw_filled_rect(Vector(0, 0), Vector(SCREEN_WIDTH, SCREEN_HEIGHT),
+ Color(0, 0, 0, alpha), LAYER_GUI-10);
+ }
+
+ if (borders) {
+ context.draw_filled_rect(Vector(0, 0), Vector(SCREEN_WIDTH, border_size),
+ Color(0, 0, 0, 1.0f), LAYER_GUI-10);
+ context.draw_filled_rect(Vector(0, SCREEN_HEIGHT - border_size), Vector(SCREEN_WIDTH, border_size),
+ Color(0, 0, 0, 1.0f), LAYER_GUI-10);
+ }
+
+ context.pop_transform();
+}
+
+void
+DisplayEffect::fade_out(float fadetime)
+{
+ black = false;
+ screen_fadetime = fadetime;
+ screen_fading = fadetime;
+ screen_fade = FADE_OUT;
+}
+
+void
+DisplayEffect::fade_in(float fadetime)
+{
+ black = false;
+ this->screen_fadetime = fadetime;
+ screen_fading = fadetime;
+ screen_fade = FADE_IN;
+}
+
+void
+DisplayEffect::set_black(bool enabled)
+{
+ black = enabled;
+}
+
+bool
+DisplayEffect::is_black()
+{
+ return black;
+}
+
+void
+DisplayEffect::sixteen_to_nine(float fadetime)
+{
+ if(fadetime == 0) {
+ borders = true;
+ border_size = BORDER_SIZE;
+ } else {
+ borders = true;
+ border_size = 0;
+ border_fade = FADE_IN;
+ border_fadetime = fadetime;
+ border_fading = border_fadetime;
+ }
+}
+
+void
+DisplayEffect::four_to_three(float fadetime)
+{
+ if(fadetime == 0) {
+ borders = false;
+ } else {
+ border_size = BORDER_SIZE;
+ border_fade = FADE_OUT;
+ border_fadetime = fadetime;
+ border_fading = border_fadetime;
+ }
+}
--- /dev/null
+// $Id$
+//
+// SuperTux
+// Copyright (C) 2006 Matthias Braun <matze@braunis.de>
+//
+// 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 __OBJECT_DISPLAY_EFFECT_H__
+#define __OBJECT_DISPLAY_EFFECT_H__
+
+#include "scripting/display_effect.hpp"
+#include "game_object.hpp"
+#include "script_interface.hpp"
+
+class DisplayEffect : public GameObject, public Scripting::DisplayEffect,
+ public ScriptInterface
+{
+public:
+ DisplayEffect(std::string name = "");
+ virtual ~DisplayEffect();
+
+ void expose(HSQUIRRELVM vm, SQInteger table_idx);
+ void unexpose(HSQUIRRELVM vm, SQInteger table_idx);
+
+ void update(float elapsed_time);
+ void draw(DrawingContext& context);
+
+
+ /**
+ * @name Scriptable Methods
+ * @{
+ */
+
+ void fade_out(float fadetime);
+ void fade_in(float fadetime);
+ void set_black(bool enabled);
+ bool is_black();
+ void sixteen_to_nine(float fadetime);
+ void four_to_three(float fadetime);
+
+ /**
+ * @}
+ */
+
+private:
+ enum FadeType {
+ NO_FADE, FADE_IN, FADE_OUT
+ };
+ FadeType screen_fade;
+ float screen_fadetime;
+ float screen_fading;
+ FadeType border_fade;
+ float border_fadetime;
+ float border_fading;
+ float border_size;
+
+ bool black;
+ bool borders;
+};
+
+#endif
--- /dev/null
+// $Id$
+//
+// SuperTux
+// Copyright (C) 2006 Matthias Braun <matze@braunis.de>
+//
+// 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 <config.h>
+#include "electrifier.hpp"
+#include "sector.hpp"
+#include "object/tilemap.hpp"
+#include "tile.hpp"
+
+
+Electrifier::Electrifier(uint32_t oldtile, uint32_t newtile, float seconds)
+{
+ duration.start(seconds);
+ change_from = oldtile;
+ change_to = newtile;
+ Sector::current()->change_solid_tiles(change_from,change_to);
+}
+
+Electrifier::~Electrifier() {
+}
+
+void
+Electrifier::update(float )
+{
+ if (duration.check()) {
+ Sector::current()->change_solid_tiles(change_to,change_from);
+ remove_me();
+ }
+}
+
+void
+Electrifier::draw(DrawingContext& )
+{
+}
--- /dev/null
+// $Id$
+//
+// SuperTux
+// Copyright (C) 2006 Matthias Braun <matze@braunis.de>
+//
+// 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 __ELECTRIFIER_H__
+#define __ELECTRIFIER_H__
+
+#include "resources.hpp"
+#include "game_object.hpp"
+#include "timer.hpp"
+#include <stdint.h>
+
+//Changes all tiles with the given ID to a new one for a given amount of time, then removes itself
+//Used by the Kugelblitz to electrify water - can be used for other effects, too
+class Electrifier : public GameObject
+{
+public:
+ Electrifier(uint32_t oldtile, uint32_t newtile, float seconds);
+ ~Electrifier();
+protected:
+ virtual void update(float time);
+ virtual void draw(DrawingContext& context);
+private:
+ uint32_t change_from;
+ uint32_t change_to;
+ Timer duration;
+};
+
+#endif
--- /dev/null
+// $Id$
+//
+// SuperTux - End Sequence
+// Copyright (C) 2007 Christoph Sommer <christoph.sommer@2006.expires.deltadevelopment.de>
+//
+// 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 <config.h>
+
+#include "endsequence.hpp"
+
+#include <stdexcept>
+#include <iostream>
+#include <sstream>
+#include "main.hpp"
+#include "resources.hpp"
+#include "sector.hpp"
+#include "gettext.hpp"
+#include "object_factory.hpp"
+#include "object/player.hpp"
+#include "video/drawing_context.hpp"
+#include "lisp/list_iterator.hpp"
+#include "log.hpp"
+#include "scripting/level_time.hpp"
+#include "scripting/squirrel_util.hpp"
+
+EndSequence::EndSequence()
+: isrunning(false), isdone(false), tux_may_walk(true)
+{
+ end_sequence_controller = 0;
+}
+
+EndSequence::~EndSequence()
+{
+ delete end_sequence_controller;
+}
+
+void
+EndSequence::update(float elapsed_time)
+{
+ if (!isrunning) return;
+ running(elapsed_time);
+}
+
+void
+EndSequence::draw(DrawingContext& /*context*/)
+{
+}
+
+void
+EndSequence::start()
+{
+ if (isrunning) return;
+ isrunning = true;
+ isdone = false;
+
+ Player& tux = *Sector::current()->player;
+ end_sequence_controller = new CodeController();
+ tux.set_controller(end_sequence_controller);
+ tux.set_speedlimit(230); //MAX_WALK_XM
+
+ starting();
+}
+
+void
+EndSequence::stop_tux()
+{
+ tux_may_walk = false;
+}
+
+void
+EndSequence::stop()
+{
+ if (!isrunning) return;
+ isrunning = false;
+ isdone = true;
+ stopping();
+}
+
+bool
+EndSequence::is_tux_stopped()
+{
+ return !tux_may_walk;
+}
+
+ bool
+EndSequence::is_done()
+{
+ return isdone;
+}
+
+void
+EndSequence::starting()
+{
+}
+
+void
+EndSequence::running(float /*elapsed_time*/)
+{
+}
+
+void
+EndSequence::stopping()
+{
+}
--- /dev/null
+// $Id$
+//
+// SuperTux - End Sequence
+// Copyright (C) 2007 Christoph Sommer <christoph.sommer@2006.expires.deltadevelopment.de>
+//
+// 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 __ENDSEQUENCE_H__
+#define __ENDSEQUENCE_H__
+
+#include <memory>
+#include "game_object.hpp"
+#include "timer.hpp"
+#include "lisp/lisp.hpp"
+#include "control/codecontroller.hpp"
+
+class EndSequence : public GameObject
+{
+public:
+ EndSequence();
+ virtual ~EndSequence();
+
+ virtual void update(float elapsed_time);
+ virtual void draw(DrawingContext& context);
+
+ void start(); /**< play EndSequence */
+ void stop_tux(); /**< called when Tux has reached his final position */
+ void stop(); /**< stop playing EndSequence, mark it as done playing */
+ bool is_tux_stopped(); /**< returns true if Tux has reached his final position */
+ bool is_done(); /**< returns true if EndSequence has finished playing */
+
+protected:
+ virtual void starting(); /**< called when EndSequence starts */
+ virtual void running(float elapsed_time); /**< called while the EndSequence is running */
+ virtual void stopping(); /**< called when EndSequence stops */
+
+ bool isrunning; /**< true while EndSequence plays */
+ bool isdone; /**< true if EndSequence has finished playing */
+ bool tux_may_walk; /**< true while tux is allowed to walk */
+ CodeController* end_sequence_controller;
+
+};
+
+#endif
--- /dev/null
+// $Id$
+//
+// SuperTux - End Sequence: Tux walks right
+// Copyright (C) 2007 Christoph Sommer <christoph.sommer@2006.expires.deltadevelopment.de>
+//
+// 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 <config.h>
+#include "endsequence_fireworks.hpp"
+#include "sector.hpp"
+#include "mainloop.hpp"
+#include "object/player.hpp"
+#include "object/fireworks.hpp"
+
+EndSequenceFireworks::EndSequenceFireworks()
+: EndSequence()
+{
+}
+
+EndSequenceFireworks::~EndSequenceFireworks()
+{
+}
+
+void
+EndSequenceFireworks::draw(DrawingContext& /*context*/)
+{
+}
+
+void
+EndSequenceFireworks::starting()
+{
+ EndSequence::starting();
+ endsequence_timer.start(7.3f * main_loop->get_speed());
+ Sector::current()->add_object(new Fireworks());
+}
+
+void
+EndSequenceFireworks::running(float elapsed_time)
+{
+ EndSequence::running(elapsed_time);
+ //Player& tux = *Sector::current()->player;
+
+ if (tux_may_walk) {
+ end_sequence_controller->press(Controller::JUMP);
+ }
+
+ if (endsequence_timer.check()) isdone = true;
+}
+
+void
+EndSequenceFireworks::stopping()
+{
+ EndSequence::stopping();
+}
--- /dev/null
+// $Id$
+//
+// SuperTux - End Sequence: Tux walks right
+// Copyright (C) 2007 Christoph Sommer <christoph.sommer@2006.expires.deltadevelopment.de>
+//
+// 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 __ENDSEQUENCE_FIREWORKS_H__
+#define __ENDSEQUENCE_FIREWORKS_H__
+
+#include <memory>
+#include "object/endsequence.hpp"
+#include "timer.hpp"
+
+class EndSequenceFireworks : public EndSequence
+{
+public:
+ EndSequenceFireworks();
+ virtual ~EndSequenceFireworks();
+ virtual void draw(DrawingContext& context);
+
+protected:
+ virtual void starting(); /**< called when EndSequence starts */
+ virtual void running(float elapsed_time); /**< called while the EndSequence is running */
+ virtual void stopping(); /**< called when EndSequence stops */
+
+ Timer endsequence_timer;
+};
+
+#endif
--- /dev/null
+// $Id$
+//
+// SuperTux - End Sequence: Tux walks right
+// Copyright (C) 2007 Christoph Sommer <christoph.sommer@2006.expires.deltadevelopment.de>
+//
+// 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 <config.h>
+#include "endsequence_walkleft.hpp"
+#include "sector.hpp"
+#include "mainloop.hpp"
+#include "object/player.hpp"
+
+EndSequenceWalkLeft::EndSequenceWalkLeft()
+: EndSequence()
+{
+}
+
+EndSequenceWalkLeft::~EndSequenceWalkLeft()
+{
+}
+
+void
+EndSequenceWalkLeft::draw(DrawingContext& /*context*/)
+{
+}
+
+void
+EndSequenceWalkLeft::starting()
+{
+ EndSequence::starting();
+ last_x_pos = -1;
+ endsequence_timer.start(7.3f * main_loop->get_speed());
+}
+
+void
+EndSequenceWalkLeft::running(float elapsed_time)
+{
+ EndSequence::running(elapsed_time);
+ Player& tux = *Sector::current()->player;
+
+ if (tux_may_walk) {
+ end_sequence_controller->press(Controller::LEFT);
+ if (int(last_x_pos) == int(tux.get_pos().x)) {
+ end_sequence_controller->press(Controller::JUMP);
+ }
+ }
+
+ last_x_pos = tux.get_pos().x;
+
+ if (endsequence_timer.check()) isdone = true;
+}
+
+void
+EndSequenceWalkLeft::stopping()
+{
+ EndSequence::stopping();
+}
--- /dev/null
+// $Id$
+//
+// SuperTux - End Sequence: Tux walks right
+// Copyright (C) 2007 Christoph Sommer <christoph.sommer@2006.expires.deltadevelopment.de>
+//
+// 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 __ENDSEQUENCE_WALKLEFT_H__
+#define __ENDSEQUENCE_WALKLEFT_H__
+
+#include <memory>
+#include "object/endsequence.hpp"
+#include "timer.hpp"
+
+class EndSequenceWalkLeft : public EndSequence
+{
+public:
+ EndSequenceWalkLeft();
+ virtual ~EndSequenceWalkLeft();
+ virtual void draw(DrawingContext& context);
+
+protected:
+ virtual void starting(); /**< called when EndSequence starts */
+ virtual void running(float elapsed_time); /**< called while the EndSequence is running */
+ virtual void stopping(); /**< called when EndSequence stops */
+
+ float last_x_pos;
+ Timer endsequence_timer;
+};
+
+#endif
--- /dev/null
+// $Id$
+//
+// SuperTux - End Sequence: Tux walks right
+// Copyright (C) 2007 Christoph Sommer <christoph.sommer@2006.expires.deltadevelopment.de>
+//
+// 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 <config.h>
+#include "endsequence_walkright.hpp"
+#include "sector.hpp"
+#include "mainloop.hpp"
+#include "object/player.hpp"
+
+EndSequenceWalkRight::EndSequenceWalkRight()
+: EndSequence()
+{
+}
+
+EndSequenceWalkRight::~EndSequenceWalkRight()
+{
+}
+
+void
+EndSequenceWalkRight::draw(DrawingContext& /*context*/)
+{
+}
+
+void
+EndSequenceWalkRight::starting()
+{
+ EndSequence::starting();
+ last_x_pos = -1;
+ endsequence_timer.start(7.3f * main_loop->get_speed());
+}
+
+void
+EndSequenceWalkRight::running(float elapsed_time)
+{
+ EndSequence::running(elapsed_time);
+ Player& tux = *Sector::current()->player;
+
+ if (tux_may_walk) {
+ end_sequence_controller->press(Controller::RIGHT);
+ if (int(last_x_pos) == int(tux.get_pos().x)) {
+ end_sequence_controller->press(Controller::JUMP);
+ }
+ }
+
+ last_x_pos = tux.get_pos().x;
+
+ if (endsequence_timer.check()) isdone = true;
+}
+
+void
+EndSequenceWalkRight::stopping()
+{
+ EndSequence::stopping();
+}
--- /dev/null
+// $Id$
+//
+// SuperTux - End Sequence: Tux walks right
+// Copyright (C) 2007 Christoph Sommer <christoph.sommer@2006.expires.deltadevelopment.de>
+//
+// 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 __ENDSEQUENCE_WALKRIGHT_H__
+#define __ENDSEQUENCE_WALKRIGHT_H__
+
+#include <memory>
+#include "object/endsequence.hpp"
+#include "timer.hpp"
+
+class EndSequenceWalkRight : public EndSequence
+{
+public:
+ EndSequenceWalkRight();
+ virtual ~EndSequenceWalkRight();
+ virtual void draw(DrawingContext& context);
+
+protected:
+ virtual void starting(); /**< called when EndSequence starts */
+ virtual void running(float elapsed_time); /**< called while the EndSequence is running */
+ virtual void stopping(); /**< called when EndSequence stops */
+
+ float last_x_pos;
+ Timer endsequence_timer;
+};
+
+#endif
--- /dev/null
+// $Id$
+//
+// SuperTux -- Explosion object
+// Copyright (C) 2007 Christoph Sommer <christoph.sommer@2007.expires.deltadevelopment.de>
+//
+// 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 <config.h>
+
+#include "explosion.hpp"
+#include "badguy/badguy.hpp"
+#include "object/sprite_particle.hpp"
+#include "random_generator.hpp"
+
+Explosion::Explosion(const Vector& pos)
+ : MovingSprite(pos, "images/objects/explosion/explosion.sprite", LAYER_OBJECTS+40, COLGROUP_TOUCHABLE), state(STATE_WAITING)
+{
+ sound_manager->preload("sounds/explosion.wav");
+ set_pos(get_pos() - (get_bbox().get_middle() - get_pos()));
+}
+
+Explosion::Explosion(const lisp::Lisp& reader)
+ : MovingSprite(reader, "images/objects/explosion/explosion.sprite", LAYER_OBJECTS+40, COLGROUP_TOUCHABLE), state(STATE_WAITING)
+{
+ sound_manager->preload("sounds/explosion.wav");
+}
+
+void
+Explosion::explode()
+{
+ if (state != STATE_WAITING) return;
+ state = STATE_EXPLODING;
+
+ set_action("default", 1);
+ sprite->set_animation_loops(1); //TODO: this is necessary because set_action will not set "loops" when "action" is the default action
+ sound_manager->play("sounds/explosion.wav", get_pos());
+
+ // spawn some particles
+ // TODO: provide convenience function in MovingSprite or MovingObject?
+ for (int i = 0; i < 100; i++) {
+ Vector ppos = bbox.get_middle();
+ float angle = systemRandom.randf(-M_PI_2, M_PI_2);
+ float velocity = systemRandom.randf(450, 900);
+ float vx = sin(angle)*velocity;
+ float vy = -cos(angle)*velocity;
+ Vector pspeed = Vector(vx, vy);
+ Vector paccel = Vector(0, 1000);
+ Sector::current()->add_object(new SpriteParticle("images/objects/particles/explosion.sprite", "default", ppos, ANCHOR_MIDDLE, pspeed, paccel, LAYER_OBJECTS-1));
+ }
+}
+
+void
+Explosion::update(float )
+{
+ switch(state) {
+ case STATE_WAITING:
+ explode();
+ break;
+ case STATE_EXPLODING:
+ if(sprite->animation_done()) {
+ remove_me();
+ }
+ break;
+ }
+}
+
+HitResponse
+Explosion::collision(GameObject& other, const CollisionHit& )
+{
+ if(state != STATE_EXPLODING) return ABORT_MOVE;
+
+ Player* player = dynamic_cast<Player*>(&other);
+ if(player != 0) {
+ player->kill(false);
+ }
+
+ BadGuy* badguy = dynamic_cast<BadGuy*>(&other);
+ if(badguy != 0) {
+ badguy->kill_fall();
+ }
+
+ return ABORT_MOVE;
+}
+
+IMPLEMENT_FACTORY(Explosion, "explosion");
+
--- /dev/null
+// $Id$
+//
+// SuperTux -- Explosion object
+// Copyright (C) 2007 Christoph Sommer <christoph.sommer@2007.expires.deltadevelopment.de>
+//
+// 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 __EXPLOSION_H__
+#define __EXPLOSION_H__
+
+#include "moving_sprite.hpp"
+#include "lisp/lisp.hpp"
+
+/**
+ * Just your average explosion - goes boom, hurts Tux
+ */
+class Explosion : public MovingSprite
+{
+public:
+ /**
+ * Create new Explosion centered(!) at @c pos
+ */
+ Explosion(const Vector& pos);
+ Explosion(const lisp::Lisp& reader);
+
+ void update(float elapsed_time);
+ HitResponse collision(GameObject& other, const CollisionHit& hit);
+
+protected:
+ /**
+ * plays sound, starts animation
+ */
+ void explode();
+
+private:
+ enum State {
+ STATE_WAITING,
+ STATE_EXPLODING
+ };
+ State state;
+
+};
+
+#endif
+
--- /dev/null
+// $Id$
+//
+// SuperTux
+// Copyright (C) 2006 Ondrej Hosek <ondra.hosek@gmail.com>
+//
+// 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 <config.h>
+
+#include "falling_coin.hpp"
+#include "player.hpp"
+#include "sprite/sprite_manager.hpp"
+#include "resources.hpp"
+#include "main.hpp"
+
+FallingCoin::FallingCoin(const Vector& start_position, const int vel_x)
+{
+ pos = start_position;
+ sprite = sprite_manager->create("images/objects/coin/coin.sprite");
+ physic.set_velocity_y(-800);
+ physic.set_velocity_x(vel_x);
+}
+
+FallingCoin::~FallingCoin()
+{
+ delete sprite;
+}
+
+void
+FallingCoin::draw(DrawingContext& context)
+{
+ sprite->draw(context, pos, LAYER_FLOATINGOBJECTS + 5);
+}
+
+void
+FallingCoin::update(float elapsed_time)
+{
+ pos += physic.get_movement(elapsed_time);
+ if (pos.y > SCREEN_HEIGHT)
+ remove_me();
+}
--- /dev/null
+// $Id$
+//
+// SuperTux
+// Copyright (C) 2006 Ondrej Hosek <ondra.hosek@gmail.com>
+//
+// 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 __FALLING_COIN_H__
+#define __FALLING_COIN_H__
+
+#include "game_object.hpp"
+#include "math/vector.hpp"
+#include "sprite/sprite.hpp"
+#include "video/drawing_context.hpp"
+#include "physic.hpp"
+
+class FallingCoin : public GameObject, private UsesPhysic
+{
+public:
+ FallingCoin(const Vector& start_position, const int x_vel);
+ ~FallingCoin();
+
+ void draw(DrawingContext& context);
+ void update(float elapsed_time);
+private:
+ Vector pos;
+ Sprite* sprite;
+};
+
+#endif
--- /dev/null
+// $Id$
+//
+// SuperTux
+// Copyright (C) 2006 Matthias Braun <matze@braunis.de>
+//
+// 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 <config.h>
+
+#include "firefly.hpp"
+#include "resources.hpp"
+#include "sprite/sprite_manager.hpp"
+#include "video/drawing_context.hpp"
+#include "player.hpp"
+#include "object_factory.hpp"
+#include "game_session.hpp"
+#include "sector.hpp"
+#include "random_generator.hpp"
+#include "object/sprite_particle.hpp"
+
+Firefly::Firefly(const lisp::Lisp& lisp)
+ : MovingSprite(lisp, "images/objects/resetpoints/default-resetpoint.sprite", LAYER_TILES, COLGROUP_TOUCHABLE), activated(false)
+{
+ initial_position = get_pos();
+ if( !lisp.get( "sprite", sprite_name ) ){
+ reactivate();
+ return;
+ }
+ if( sprite_name == "" ){
+ sprite_name = "images/objects/resetpoints/default-resetpoint.sprite";
+ reactivate();
+ return;
+ }
+ //Replace sprite
+ sprite = sprite_manager->create( sprite_name );
+ bbox.set_size(sprite->get_current_hitbox_width(), sprite->get_current_hitbox_height());
+ reactivate();
+}
+
+void
+Firefly::reactivate()
+{
+ if(GameSession::current()->get_reset_point_pos() == initial_position){
+ // TODO: && GameSession::current()->get_reset_point_sectorname() == <sector this firefly is in>
+ // GameSession::current()->get_current_sector()->get_name() is not yet initialized.
+ // Worst case a resetpoint in a different sector at the same position as the real
+ // resetpoint the player is spawning is set to ringing, too. Until we can check the sector, too, dont set
+ // activated = true; here.
+ sprite->set_action("ringing");
+ }
+}
+
+void
+Firefly::write(lisp::Writer& writer)
+{
+ writer.start_list("firefly");
+ writer.write_float("x", bbox.p1.x);
+ writer.write_float("y", bbox.p1.y);
+ writer.end_list("firefly");
+}
+
+HitResponse
+Firefly::collision(GameObject& other, const CollisionHit& )
+{
+ if(activated)
+ return ABORT_MOVE;
+
+ Player* player = dynamic_cast<Player*> (&other);
+ if(player) {
+ activated = true;
+// spawn some particles
+// TODO: provide convenience function in MovingSprite or MovingObject?
+ for (int i = 0; i < 5; i++) {
+ Vector ppos = bbox.get_middle();
+ float angle = systemRandom.randf(-M_PI_2, M_PI_2);
+ float velocity = systemRandom.randf(450, 900);
+ float vx = sin(angle)*velocity;
+ float vy = -cos(angle)*velocity;
+ Vector pspeed = Vector(vx, vy);
+ Vector paccel = Vector(0, 1000);
+ Sector::current()->add_object(new SpriteParticle("images/objects/particles/reset.sprite", "default", ppos, ANCHOR_MIDDLE, pspeed, paccel, LAYER_OBJECTS-1));
+ }
+ // TODO play sound
+ sprite->set_action("ringing");
+ GameSession::current()->set_reset_point(Sector::current()->get_name(),
+ initial_position);
+ }
+
+ return ABORT_MOVE;
+}
+
+IMPLEMENT_FACTORY(Firefly, "firefly");
--- /dev/null
+// $Id$
+//
+// SuperTux
+// Copyright (C) 2006 Matthias Braun <matze@braunis.de>
+//
+// 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 __FIREFLY_H__
+#define __FIREFLY_H__
+
+#include "lisp/lisp.hpp"
+#include "object/moving_sprite.hpp"
+#include "serializable.hpp"
+#include "badguy/badguy.hpp"
+
+/**
+ * A Firefly: When tux touches it, it begins buzzing and you will respawn at this
+ * position.
+ */
+class Firefly : public MovingSprite, public Serializable
+{
+public:
+ Firefly(const lisp::Lisp& lisp);
+ virtual Firefly* clone() const { return new Firefly(*this); }
+
+ void write(lisp::Writer& writer);
+ HitResponse collision(GameObject& other, const CollisionHit& hit);
+
+private:
+ bool activated;
+ Vector initial_position; /**< position as in level file. This is where Tux will have to respawn, as the level is reset every time */
+ void reactivate();
+};
+
+#endif
--- /dev/null
+// $Id$
+//
+// SuperTux
+// Copyright (C) 2006 Matthias Braun <matze@braunis.de>
+//
+// 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 <config.h>
+
+#include "fireworks.hpp"
+#include "resources.hpp"
+#include "sector.hpp"
+#include "camera.hpp"
+#include "particles.hpp"
+#include "main.hpp"
+#include "video/drawing_context.hpp"
+#include "audio/sound_manager.hpp"
+#include "random_generator.hpp"
+
+Fireworks::Fireworks()
+{
+ timer.start(.2f);
+ sound_manager->preload("sounds/fireworks.wav");
+}
+
+Fireworks::~Fireworks()
+{
+}
+
+void
+Fireworks::update(float )
+{
+ if(timer.check()) {
+ Sector* sector = Sector::current();
+ Vector pos = sector->camera->get_translation();
+ pos += Vector(systemRandom.randf(SCREEN_WIDTH),
+ systemRandom.randf(SCREEN_HEIGHT/2));
+
+ float red = systemRandom.randf(1.0);
+ float green = systemRandom.randf(1.0);
+ //float red = 0.7;
+ //float green = 0.9;
+ (void) red;
+ (void) green;
+ sector->add_object(new Particles(pos, 0, 360, Vector(140, 140),
+ Vector(0, 0), 45, Color(red, green, 0), 3, 1.3f,
+ LAYER_FOREGROUND1+1));
+ sound_manager->play("sounds/fireworks.wav");
+ timer.start(systemRandom.randf(1.0, 1.5));
+ }
+}
+
+void
+Fireworks::draw(DrawingContext& )
+{
+}
--- /dev/null
+// $Id$
+//
+// SuperTux
+// Copyright (C) 2006 Matthias Braun <matze@braunis.de>
+//
+// 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 __FIREWORKS_H__
+#define __FIREWORKS_H__
+
+#include "video/drawing_context.hpp"
+#include "game_object.hpp"
+#include "timer.hpp"
+
+class Fireworks : public GameObject
+{
+public:
+ Fireworks();
+ ~Fireworks();
+
+ virtual void update(float elapsed_time);
+ virtual void draw(DrawingContext& context);
+
+private:
+ Timer timer;
+};
+
+#endif
--- /dev/null
+// $Id$
+//
+// SuperTux
+// Copyright (C) 2006 Matthias Braun <matze@braunis.de>
+//
+// 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 <config.h>
+
+#include <stdexcept>
+#include "resources.hpp"
+#include "main.hpp"
+#include "math/rect.hpp"
+#include "sprite/sprite_manager.hpp"
+#include "sprite/sprite.hpp"
+#include "video/drawing_context.hpp"
+#include "lisp/lisp.hpp"
+#include "floating_image.hpp"
+
+
+FloatingImage::FloatingImage(const std::string& spritefile)
+ : layer(LAYER_FOREGROUND1 + 1), visible(false), anchor(ANCHOR_MIDDLE), fading(0), fadetime(0)
+{
+ sprite.reset(sprite_manager->create(spritefile));
+}
+
+FloatingImage::~FloatingImage()
+{
+}
+
+void
+FloatingImage::update(float elapsed_time)
+{
+ if(fading > 0) {
+ fading -= elapsed_time;
+ if(fading <= 0) {
+ fading = 0;
+ visible = true;
+ }
+ } else if(fading < 0) {
+ fading += elapsed_time;
+ if(fading >= 0) {
+ fading = 0;
+ visible = false;
+ }
+ }
+
+// (void) elapsed_time;
+}
+
+void
+FloatingImage::set_action(const std::string& action)
+{
+ sprite->set_action(action);
+}
+
+std::string
+FloatingImage::get_action()
+{
+ return sprite->get_action();
+}
+
+void
+FloatingImage::fade_in(float fadetime)
+{
+ this->fadetime = fadetime;
+ fading = fadetime;
+}
+
+void
+FloatingImage::fade_out(float fadetime)
+{
+ this->fadetime = fadetime;
+ fading = -fadetime;
+}
+
+
+void
+FloatingImage::draw(DrawingContext& context)
+{
+ context.push_transform();
+ context.set_translation(Vector(0, 0));
+
+ if(fading > 0) {
+ context.set_alpha((fadetime-fading) / fadetime);
+ } else if(fading < 0) {
+ context.set_alpha(-fading / fadetime);
+ } else if(!visible) {
+ context.pop_transform();
+ return;
+ }
+
+ Vector spos = pos + get_anchor_pos(Rect(0, 0, SCREEN_WIDTH, SCREEN_HEIGHT),
+ sprite->get_width(), sprite->get_height(), anchor);
+
+ sprite->draw(context, spos, layer);
+
+ context.pop_transform();
+}
--- /dev/null
+// $Id$
+//
+// SuperTux
+// Copyright (C) 2006 Matthias Braun <matze@braunis.de>
+//
+// 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 __FLOATING_IMAGE_H__
+#define __FLOATING_IMAGE_H__
+
+#include "game_object.hpp"
+#include "math/vector.hpp"
+#include "anchor_point.hpp"
+#include <memory>
+
+class Sprite;
+
+class FloatingImage : public GameObject
+{
+public:
+ FloatingImage(const std::string& sprite);
+ virtual ~FloatingImage();
+
+ void set_layer(int layer) {
+ this->layer = layer;
+ }
+
+ int get_layer() const {
+ return layer;
+ }
+
+ void set_pos(const Vector& pos) {
+ this->pos = pos;
+ }
+ const Vector& get_pos() const {
+ return pos;
+ }
+
+ void set_anchor_point(AnchorPoint anchor) {
+ this->anchor = anchor;
+ }
+ AnchorPoint get_anchor_point() const {
+ return anchor;
+ }
+
+ void set_visible(bool visible) {
+ this->visible = visible;
+ }
+ bool get_visible() const {
+ return visible;
+ }
+
+ void set_action(const std::string& action);
+ std::string get_action();
+
+ void fade_in(float fadetime);
+ void fade_out(float fadetime);
+
+ void update(float elapsed_time);
+ void draw(DrawingContext& context);
+
+private:
+ std::auto_ptr<Sprite> sprite;
+ int layer;
+ bool visible;
+ AnchorPoint anchor;
+ Vector pos;
+ float fading;
+ float fadetime;
+};
+
+#endif
--- /dev/null
+// $Id$
+//
+// SuperTux
+// Copyright (C) 2006 Matthias Braun <matze@braunis.de>
+//
+// 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 <config.h>
+
+#include <math.h>
+#include <assert.h>
+#include "flower.hpp"
+#include "resources.hpp"
+#include "camera.hpp"
+#include "sector.hpp"
+#include "player.hpp"
+#include "audio/sound_manager.hpp"
+#include "sprite/sprite_manager.hpp"
+
+Flower::Flower(BonusType _type)
+ : type(_type)
+{
+ bbox.set_size(32, 32);
+
+ if(type == FIRE_BONUS) {
+ sprite = sprite_manager->create("images/powerups/fireflower/fireflower.sprite");
+ sound_manager->preload("sounds/fire-flower.wav");
+ }
+ else if(type == ICE_BONUS) {
+ sprite = sprite_manager->create("images/powerups/iceflower/iceflower.sprite");
+ } else {
+ assert(false);
+ }
+
+ set_group(COLGROUP_TOUCHABLE);
+}
+
+Flower::~Flower()
+{
+ delete sprite;
+}
+
+void
+Flower::update(float )
+{
+}
+
+void
+Flower::draw(DrawingContext& context)
+{
+ sprite->draw(context, get_pos(), LAYER_OBJECTS);
+}
+
+HitResponse
+Flower::collision(GameObject& other, const CollisionHit& )
+{
+ Player* player = dynamic_cast<Player*>(&other);
+ if(!player)
+ return ABORT_MOVE;
+
+ if(!player->add_bonus(type, true))
+ return FORCE_MOVE;
+
+ sound_manager->play("sounds/fire-flower.wav");
+ remove_me();
+ return ABORT_MOVE;
+}
--- /dev/null
+// $Id$
+//
+// SuperTux
+// Copyright (C) 2006 Matthias Braun <matze@braunis.de>
+//
+// 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 __FLOWER_H__
+#define __FLOWER_H__
+
+#include "moving_object.hpp"
+#include "sprite/sprite.hpp"
+#include "player_status.hpp"
+
+class Flower : public MovingObject
+{
+public:
+ Flower(BonusType type);
+ ~Flower();
+
+ virtual void update(float elapsed_time);
+ virtual void draw(DrawingContext& context);
+ virtual HitResponse collision(GameObject& other, const CollisionHit& hit);
+
+private:
+ BonusType type;
+ Sprite* sprite;
+};
+
+#endif
--- /dev/null
+// $Id$
+//
+// SuperTux
+// Copyright (C) 2006 Matthias Braun <matze@braunis.de>
+//
+// 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 <config.h>
+
+#include <algorithm>
+#include <iostream>
+#include <cmath>
+
+#include "tile.hpp"
+#include "tile_manager.hpp"
+#include "game_session.hpp"
+#include "gameobjs.hpp"
+#include "sprite/sprite_manager.hpp"
+#include "sprite/sprite.hpp"
+#include "resources.hpp"
+#include "sector.hpp"
+#include "tilemap.hpp"
+#include "video/drawing_context.hpp"
+#include "camera.hpp"
+#include "main.hpp"
+#include "random_generator.hpp"
+
+BouncyCoin::BouncyCoin(const Vector& pos)
+ : position(pos)
+{
+ timer.start(.3f);
+ sprite = sprite_manager->create("images/objects/coin/coin.sprite");
+ sprite->set_action("still");
+}
+
+BouncyCoin::~BouncyCoin()
+{
+ delete sprite;
+}
+
+void
+BouncyCoin::update(float elapsed_time)
+{
+ position.y += -200 * elapsed_time;
+
+ if(timer.check())
+ remove_me();
+}
+
+void
+BouncyCoin::draw(DrawingContext& context)
+{
+ sprite->draw(context, position, LAYER_OBJECTS + 5);
+}
+
+//---------------------------------------------------------------------------
+
+BrokenBrick::BrokenBrick(Sprite* nsprite,
+ const Vector& pos, const Vector& nmovement)
+ : sprite(new Sprite(*nsprite)), position(pos), movement(nmovement)
+{
+ timer.start(.2f);
+}
+
+BrokenBrick::~BrokenBrick()
+{
+ delete sprite;
+}
+
+void
+BrokenBrick::update(float elapsed_time)
+{
+ position += movement * elapsed_time;
+
+ if (timer.check())
+ remove_me();
+}
+
+void
+BrokenBrick::draw(DrawingContext& context)
+{
+ sprite->draw_part(context,
+ Vector(systemRandom.rand(16), systemRandom.rand(16)), Vector(16, 16),
+ position, LAYER_OBJECTS + 1);
+}
+
+//---------------------------------------------------------------------------
+
+FloatingText::FloatingText(const Vector& pos, const std::string& text_)
+ : position(pos), text(text_)
+{
+ timer.start(.1f);
+ position.x -= text.size() * 8;
+}
+
+FloatingText::FloatingText(const Vector& pos, int score)
+ : position(pos)
+{
+ timer.start(.1f);
+
+ // turn int into a string
+ char str[10];
+ snprintf(str, 10, "%d", score);
+ text = str;
+
+ position.x -= text.size() * 8;
+}
+
+void
+FloatingText::update(float elapsed_time)
+{
+ position.y -= 1.4 * elapsed_time;
+
+ if(timer.check())
+ remove_me();
+}
+
+#define FADING_TIME .350
+
+void
+FloatingText::draw(DrawingContext& context)
+{
+ // make an alpha animation when disapearing
+ int alpha;
+ if(timer.get_timeleft() < FADING_TIME)
+ alpha = int(timer.get_timeleft() * 255 / FADING_TIME);
+ else
+ alpha = 255;
+
+ context.push_transform();
+ context.set_alpha(alpha);
+
+ context.draw_text(gold_text, text, position, ALIGN_LEFT, LAYER_OBJECTS+1);
+
+ context.pop_transform();
+}
+
+Sprite *img_smoke_cloud = 0;
+
+SmokeCloud::SmokeCloud(const Vector& pos)
+ : position(pos)
+{
+ timer.start(.3f);
+ sprite = sprite_manager->create("images/objects/particles/stomp.sprite");
+}
+
+SmokeCloud::~SmokeCloud()
+{
+ delete sprite;
+}
+
+void
+SmokeCloud::update(float elapsed_time)
+{
+ position.y -= 120 * elapsed_time;
+
+ if(timer.check())
+ remove_me();
+}
+
+void
+SmokeCloud::draw(DrawingContext& context)
+{
+ sprite->draw(context, position, LAYER_OBJECTS+1);
+}
--- /dev/null
+// $Id$
+//
+// SuperTux
+// Copyright (C) 2006 Matthias Braun <matze@braunis.de>
+//
+// 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_GAMEOBJS_H
+#define SUPERTUX_GAMEOBJS_H
+
+#include "video/surface.hpp"
+#include "timer.hpp"
+#include "game_object.hpp"
+#include "moving_object.hpp"
+#include "serializable.hpp"
+#include "video/color.hpp"
+
+/* Bounciness of distros: */
+#define NO_BOUNCE 0
+#define BOUNCE 1
+
+class Sprite;
+
+class BouncyCoin : public GameObject
+{
+public:
+ BouncyCoin(const Vector& pos);
+ ~BouncyCoin();
+ virtual void update(float elapsed_time);
+ virtual void draw(DrawingContext& context);
+
+private:
+ Sprite* sprite;
+ Vector position;
+ Timer timer;
+};
+
+class BrokenBrick : public GameObject
+{
+public:
+ BrokenBrick(Sprite* sprite, const Vector& pos, const Vector& movement);
+ ~BrokenBrick();
+
+ virtual void update(float elapsed_time);
+ virtual void draw(DrawingContext& context);
+
+private:
+ Timer timer;
+ Sprite* sprite;
+ Vector position;
+ Vector movement;
+};
+
+class FloatingText : public GameObject
+{
+public:
+ FloatingText(const Vector& pos, const std::string& text_);
+ FloatingText(const Vector& pos, int s); // use this for score, for instance
+
+ virtual void update(float elapsed_time);
+ virtual void draw(DrawingContext& context);
+
+private:
+ Vector position;
+ std::string text;
+ Timer timer;
+};
+
+class SmokeCloud : public GameObject
+{
+public:
+ SmokeCloud(const Vector& pos);
+ ~SmokeCloud();
+
+ virtual void update(float elapsed_time);
+ virtual void draw(DrawingContext& context);
+
+private:
+ Sprite* sprite;
+ Timer timer;
+ Vector position;
+};
+
+#endif
+
+/* Local Variables: */
+/* mode:c++ */
+/* End: */
--- /dev/null
+// $Id$
+//
+// SuperTux
+// Copyright (C) 2006 Matthias Braun <matze@braunis.de>
+//
+// 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 <config.h>
+
+#include <stdexcept>
+#include "gradient.hpp"
+#include "camera.hpp"
+#include "video/drawing_context.hpp"
+#include "lisp/lisp.hpp"
+#include "lisp/writer.hpp"
+#include "object_factory.hpp"
+#include "resources.hpp"
+#include "main.hpp"
+#include "log.hpp"
+
+Gradient::Gradient()
+ : layer(LAYER_BACKGROUND0)
+{
+}
+
+Gradient::Gradient(const lisp::Lisp& reader)
+ : layer(LAYER_BACKGROUND0)
+{
+ reader.get("layer", layer);
+ std::vector<float> bkgd_top_color, bkgd_bottom_color;
+ if(!reader.get_vector("top_color", bkgd_top_color) ||
+ !reader.get_vector("bottom_color", bkgd_bottom_color))
+ throw std::runtime_error("Must specify top_color and bottom_color in gradient");
+
+ gradient_top = Color(bkgd_top_color);
+ gradient_bottom = Color(bkgd_bottom_color);
+}
+
+Gradient::~Gradient()
+{
+}
+
+void
+Gradient::write(lisp::Writer& writer)
+{
+ writer.start_list("gradient");
+
+ std::vector<float> bkgd_top_color, bkgd_bottom_color;
+ bkgd_top_color.push_back(gradient_top.red);
+ bkgd_top_color.push_back(gradient_top.green);
+ bkgd_top_color.push_back(gradient_top.blue);
+ bkgd_bottom_color.push_back(gradient_bottom.red);
+ bkgd_bottom_color.push_back(gradient_bottom.green);
+ bkgd_bottom_color.push_back(gradient_bottom.blue);
+ writer.write_float_vector("top_color", bkgd_top_color);
+ writer.write_float_vector("bottom_color", bkgd_bottom_color);
+
+ writer.write_int("layer", layer);
+
+ writer.end_list("gradient");
+}
+
+void
+Gradient::update(float)
+{
+}
+
+void
+Gradient::set_gradient(Color top, Color bottom)
+{
+ gradient_top = top;
+ gradient_bottom = bottom;
+
+ if (gradient_top.red > 1.0 || gradient_top.green > 1.0
+ || gradient_top.blue > 1.0 || gradient_top.alpha > 1.0)
+ log_warning << "top gradient color has values above 1.0" << std::endl;
+ if (gradient_bottom.red > 1.0 || gradient_bottom.green > 1.0
+ || gradient_bottom.blue > 1.0 || gradient_bottom.alpha > 1.0)
+ log_warning << "bottom gradient color has values above 1.0" << std::endl;
+}
+
+void
+Gradient::draw(DrawingContext& context)
+{
+ context.push_transform();
+ context.set_translation(Vector(0, 0));
+ context.draw_gradient(gradient_top, gradient_bottom, layer);
+ context.pop_transform();
+}
+
+IMPLEMENT_FACTORY(Gradient, "gradient");
--- /dev/null
+// $Id$
+//
+// SuperTux
+// Copyright (C) 2006 Matthias Braun <matze@braunis.de>
+//
+// 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_GRADIENT_H
+#define SUPERTUX_GRADIENT_H
+
+#include <memory>
+#include "video/surface.hpp"
+#include "video/drawing_context.hpp"
+#include "game_object.hpp"
+#include "serializable.hpp"
+
+class DisplayManager;
+
+namespace lisp {
+class Lisp;
+}
+
+class Gradient : public GameObject, public Serializable
+{
+public:
+ Gradient();
+ Gradient(const lisp::Lisp& reader);
+ virtual ~Gradient();
+
+ virtual void write(lisp::Writer& writer);
+
+ void set_gradient(Color top, Color bottom);
+
+ Color get_gradient_top() const
+ { return gradient_top; }
+
+ Color get_gradient_bottom() const
+ { return gradient_bottom; }
+
+ virtual void update(float elapsed_time);
+
+ virtual void draw(DrawingContext& context);
+
+private:
+ int layer;
+ Color gradient_top, gradient_bottom;
+};
+
+#endif /*SUPERTUX_BACKGROUND_H*/
--- /dev/null
+// $Id$
+//
+// SuperTux
+// Copyright (C) 2006 Matthias Braun <matze@braunis.de>
+//
+// 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 <config.h>
+
+#include <math.h>
+#include "growup.hpp"
+#include "resources.hpp"
+#include "camera.hpp"
+#include "sector.hpp"
+#include "player.hpp"
+#include "audio/sound_manager.hpp"
+
+GrowUp::GrowUp(Direction direction)
+ : MovingSprite(Vector(0,0), "images/powerups/egg/egg.sprite", LAYER_OBJECTS, COLGROUP_MOVING)
+{
+ physic.enable_gravity(true);
+ physic.set_velocity_x((direction == LEFT)?-100:100);
+ sound_manager->preload("sounds/grow.wav");
+}
+
+void
+GrowUp::update(float elapsed_time)
+{
+ movement = physic.get_movement(elapsed_time);
+}
+
+void
+GrowUp::collision_solid(const CollisionHit& hit)
+{
+ if(hit.top || hit.bottom)
+ physic.set_velocity_y(0);
+ if(hit.left || hit.right)
+ physic.set_velocity_x(-physic.get_velocity_x());
+}
+
+HitResponse
+GrowUp::collision(GameObject& other, const CollisionHit& )
+{
+ Player* player = dynamic_cast<Player*>(&other);
+ if(player != 0) {
+ if(!player->add_bonus(GROWUP_BONUS, true))
+ return FORCE_MOVE;
+
+ sound_manager->play("sounds/grow.wav");
+ remove_me();
+
+ return ABORT_MOVE;
+ }
+
+ return FORCE_MOVE;
+}
--- /dev/null
+// $Id$
+//
+// SuperTux
+// Copyright (C) 2006 Matthias Braun <matze@braunis.de>
+//
+// 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 __GROWUP_H__
+#define __GROWUP_H__
+
+#include "object/moving_sprite.hpp"
+#include "physic.hpp"
+#include "direction.hpp"
+
+class GrowUp : public MovingSprite, private UsesPhysic
+{
+public:
+ GrowUp(Direction direction = RIGHT);
+ virtual GrowUp* clone() const { return new GrowUp(*this); }
+
+ virtual void update(float elapsed_time);
+ virtual void collision_solid(const CollisionHit& hit);
+ virtual HitResponse collision(GameObject& other, const CollisionHit& hit);
+};
+
+#endif
--- /dev/null
+// $Id$
+//
+// SuperTux - Hurting Platform
+// Copyright (C) 2006 Christoph Sommer <christoph.sommer@2006.expires.deltadevelopment.de>
+//
+// 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 <config.h>
+#include <stdexcept>
+
+#include "hurting_platform.hpp"
+
+#include "log.hpp"
+#include "player.hpp"
+#include "badguy/badguy.hpp"
+#include "object_factory.hpp"
+
+HurtingPlatform::HurtingPlatform(const lisp::Lisp& reader)
+ : Platform(reader)
+{
+ set_group(COLGROUP_TOUCHABLE);
+}
+
+HitResponse
+HurtingPlatform::collision(GameObject& other, const CollisionHit& )
+{
+ Player* player = dynamic_cast<Player*>(&other);
+ if (player) {
+ player->kill(false);
+ }
+ BadGuy* badguy = dynamic_cast<BadGuy*>(&other);
+ if (badguy) {
+ badguy->kill_fall();
+ }
+
+ return FORCE_MOVE;
+}
+
+IMPLEMENT_FACTORY(HurtingPlatform, "hurting_platform");
--- /dev/null
+// $Id$
+//
+// SuperTux - Hurting Platform
+// Copyright (C) 2006 Christoph Sommer <christoph.sommer@2006.expires.deltadevelopment.de>
+//
+// 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 __HURTING_PLATFORM_H__
+#define __HURTING_PLATFORM_H__
+
+#include <memory>
+#include "object/platform.hpp"
+
+/**
+ * Platform that hurts Tux and Badguys when touched
+ */
+class HurtingPlatform : public Platform
+{
+public:
+ HurtingPlatform(const lisp::Lisp& reader);
+ virtual HurtingPlatform* clone() const { return new HurtingPlatform(*this); }
+
+ virtual HitResponse collision(GameObject& other, const CollisionHit& hit);
+
+private:
+
+};
+
+#endif
--- /dev/null
+// $Id$
+//
+// SuperTux
+// Copyright (C) 2006 Matthias Braun <matze@braunis.de>
+//
+// 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 <config.h>
+
+#include "infoblock.hpp"
+#include "game_session.hpp"
+#include "resources.hpp"
+#include "sprite/sprite_manager.hpp"
+#include "object_factory.hpp"
+#include "lisp/lisp.hpp"
+#include "sector.hpp"
+#include "log.hpp"
+#include "object/player.hpp"
+#include "main.hpp"
+
+namespace {
+ const float SCROLL_DELAY = 0.5;
+ const float SCROLL_DISTANCE = 16;
+ const float WIDTH = 400;
+ const float HEIGHT = 200;
+}
+
+InfoBlock::InfoBlock(const lisp::Lisp& lisp)
+ : Block(sprite_manager->create("images/objects/bonus_block/infoblock.sprite")), shown_pct(0), dest_pct(0)
+{
+ Vector pos;
+ lisp.get("x", pos.x);
+ lisp.get("y", pos.y);
+ bbox.set_pos(pos);
+
+ if(!lisp.get("message", message)) {
+ log_warning << "No message in InfoBlock" << std::endl;
+ }
+ //stopped = false;
+ //ringing = new AmbientSound(get_pos(), 0.5, 300, 1, "sounds/phone.wav");
+ //Sector::current()->add_object(ringing);
+
+ // Split text string lines into a vector
+ lines = InfoBoxLine::split(message, 400);
+ lines_height = 0;
+ for(size_t i = 0; i < lines.size(); ++i) lines_height+=lines[i]->get_height();
+}
+
+InfoBlock::~InfoBlock()
+{
+ for(std::vector<InfoBoxLine*>::iterator i = lines.begin(); i != lines.end(); i++) {
+ delete *i;
+ }
+}
+
+void
+InfoBlock::hit(Player& )
+{
+ start_bounce();
+
+ //if (!stopped) {
+ // ringing->remove_me();
+ // stopped = true;
+ //}
+
+ if (dest_pct != 1) {
+
+ // first hide all other InfoBlocks' messages in same sector
+ Sector* parent = Sector::current();
+ if (!parent) return;
+ for (Sector::GameObjects::iterator i = parent->gameobjects.begin(); i != parent->gameobjects.end(); i++) {
+ InfoBlock* block = dynamic_cast<InfoBlock*>(*i);
+ if (!block) continue;
+ if (block != this) block->hide_message();
+ }
+
+ // show our message
+ show_message();
+
+ } else {
+ hide_message();
+ }
+}
+
+Player*
+InfoBlock::get_nearest_player()
+{
+ // FIXME: does not really return nearest player
+
+ std::vector<Player*> players = Sector::current()->get_players();
+ for (std::vector<Player*>::iterator playerIter = players.begin(); playerIter != players.end(); ++playerIter) {
+ Player* player = *playerIter;
+ return player;
+ }
+
+ return 0;
+}
+
+void
+InfoBlock::update(float delta)
+{
+ Block::update(delta);
+
+ if (delta == 0) return;
+
+ // hide message if player is too far away or above infoblock
+ if (dest_pct > 0) {
+ Player* player = get_nearest_player();
+ if (player) {
+ Vector p1 = this->get_pos() + (this->get_bbox().p2 - this->get_bbox().p1) / 2;
+ Vector p2 = player->get_pos() + (player->get_bbox().p2 - player->get_bbox().p1) / 2;
+ Vector dist = (p2 - p1);
+ float d = dist.norm();
+ if (d > 128 || dist.y < 0) dest_pct = 0;
+ }
+ }
+
+ // handle soft fade-in and fade-out
+ if (shown_pct != dest_pct) {
+ if (dest_pct > shown_pct) shown_pct = std::min(shown_pct + 2*delta, dest_pct);
+ if (dest_pct < shown_pct) shown_pct = std::max(shown_pct - 2*delta, dest_pct);
+ }
+}
+
+void
+InfoBlock::draw(DrawingContext& context)
+{
+ Block::draw(context);
+
+ if (shown_pct <= 0) return;
+
+ context.push_transform();
+ //context.set_translation(Vector(0, 0));
+ context.set_alpha(shown_pct);
+
+ //float x1 = SCREEN_WIDTH/2-200;
+ //float y1 = SCREEN_HEIGHT/2-200;
+ float border = 8;
+ float width = 400; // this is the text width only
+ float height = lines_height; // this is the text height only
+ float x1 = (get_bbox().p1.x + get_bbox().p2.x)/2 - width/2;
+ float x2 = (get_bbox().p1.x + get_bbox().p2.x)/2 + width/2;
+ float y1 = original_y - height;
+
+ // lines_height includes one ITEMS_SPACE too much, so the bottom border is reduced by 4px
+ context.draw_filled_rect(Vector(x1-border, y1-border), Vector(width+2*border, height+2*border-4), Color(0.6f, 0.7f, 0.8f, 0.5f), LAYER_GUI-50);
+
+ float y = y1;
+ for(size_t i = 0; i < lines.size(); ++i) {
+ if(y >= y1 + height) {
+ //log_warning << "Too many lines of text in InfoBlock" << std::endl;
+ //dest_pct = 0;
+ //shown_pct = 0;
+ break;
+ }
+
+ lines[i]->draw(context, Rect(x1, y, x2, y), LAYER_GUI-50+1);
+ y += lines[i]->get_height();
+ }
+
+ context.pop_transform();
+}
+
+void
+InfoBlock::show_message()
+{
+ dest_pct = 1;
+}
+
+void
+InfoBlock::hide_message()
+{
+ dest_pct = 0;
+}
+
+IMPLEMENT_FACTORY(InfoBlock, "infoblock")
--- /dev/null
+// $Id$
+//
+// SuperTux
+// Copyright (C) 2006 Matthias Braun <matze@braunis.de>
+//
+// 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 __INFOBLOCK_H__
+#define __INFOBLOCK_H__
+
+#include "block.hpp"
+//#include "object/ambient_sound.hpp"
+#include "textscroller.hpp"
+
+class InfoBlock : public Block
+{
+public:
+ InfoBlock(const lisp::Lisp& lisp);
+ virtual ~InfoBlock();
+ void update(float elapsed_time);
+ void draw(DrawingContext& context);
+
+ void show_message();
+ void hide_message();
+
+protected:
+ virtual void hit(Player& player);
+ std::string message;
+ //AmbientSound* ringing;
+ //bool stopped;
+ float shown_pct; /**< Value in the range of 0..1, depending on how much of the infobox is currently shown */
+ float dest_pct; /**< With each call to update(), shown_pct will slowly transition to this value */
+
+ Player* get_nearest_player();
+
+ std::vector<InfoBoxLine*> lines; /**< lines of text (or images) to display */
+ float lines_height;
+};
+
+#endif
--- /dev/null
+// $Id$
+//
+// SuperTux
+// Copyright (C) 2006 Matthias Braun <matze@braunis.de>
+//
+// 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 <config.h>
+
+#include "invisible_block.hpp"
+#include "resources.hpp"
+#include "sprite/sprite.hpp"
+#include "sprite/sprite_manager.hpp"
+#include "video/drawing_context.hpp"
+#include "audio/sound_manager.hpp"
+#include "object_factory.hpp"
+#include "object/player.hpp"
+
+InvisibleBlock::InvisibleBlock(const Vector& pos)
+ : Block(sprite_manager->create("images/objects/bonus_block/invisibleblock.sprite")), visible(false)
+{
+ bbox.set_pos(pos);
+ sound_manager->preload("sounds/brick.wav");
+ sound_manager->preload("sounds/brick.wav");
+}
+
+void
+InvisibleBlock::draw(DrawingContext& context)
+{
+ if(visible)
+ sprite->draw(context, get_pos(), LAYER_OBJECTS);
+}
+
+bool
+InvisibleBlock::collides(GameObject& other, const CollisionHit& )
+{
+ if(visible)
+ return true;
+
+ // if we're not visible, only register a collision if this will make us visible
+ Player* player = dynamic_cast<Player*> (&other);
+ if ((player)
+ && (player->get_movement().y <= 0)
+ && (player->get_bbox().get_top() > get_bbox().get_bottom() - 7.0)) {
+ return true;
+ }
+
+ return false;
+}
+
+HitResponse
+InvisibleBlock::collision(GameObject& other, const CollisionHit& hit)
+{
+ return Block::collision(other, hit);
+}
+
+void
+InvisibleBlock::hit(Player& )
+{
+ sound_manager->play("sounds/brick.wav");
+
+ if(visible)
+ return;
+
+ sprite->set_action("empty");
+ start_bounce();
+ set_group(COLGROUP_STATIC);
+ visible = true;
+}
+
+//IMPLEMENT_FACTORY(InvisibleBlock, "invisible_block");
--- /dev/null
+// $Id$
+//
+// SuperTux
+// Copyright (C) 2006 Matthias Braun <matze@braunis.de>
+//
+// 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 __INVISIBLE_BLOCK_H__
+#define __INVISIBLE_BLOCK_H__
+
+#include "block.hpp"
+
+class InvisibleBlock : public Block
+{
+public:
+ InvisibleBlock(const Vector& pos);
+
+ virtual void draw(DrawingContext& context);
+ virtual bool collides(GameObject& other, const CollisionHit& hit);
+ virtual HitResponse collision(GameObject& other, const CollisionHit& hit);
+
+protected:
+ virtual void hit(Player& player);
+
+private:
+ bool visible;
+};
+
+#endif
--- /dev/null
+// $Id$
+//
+// SuperTux
+// Copyright (C) 2006 Matthias Braun <matze@braunis.de>
+//
+// 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 <config.h>
+
+#include "invisible_wall.hpp"
+#include "lisp/lisp.hpp"
+#include "object_factory.hpp"
+#include "sprite/sprite.hpp"
+
+InvisibleWall::InvisibleWall(const lisp::Lisp& lisp)
+ : MovingSprite(lisp, "images/objects/invisible/invisible.sprite", LAYER_TILES, COLGROUP_STATIC), width(32), height(32)
+{
+ lisp.get("width", width);
+ lisp.get("height", height);
+ bbox.set_size(width, height);
+}
+
+HitResponse
+InvisibleWall::collision(GameObject& , const CollisionHit& )
+{
+ return FORCE_MOVE;
+}
+
+IMPLEMENT_FACTORY(InvisibleWall, "invisible_wall");
--- /dev/null
+// $Id$
+//
+// SuperTux
+// Copyright (C) 2006 Matthias Braun <matze@braunis.de>
+//
+// 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 __INVISIBLE_WALL_H__
+#define __INVISIBLE_WALL_H__
+
+#include "object/moving_sprite.hpp"
+#include "lisp/lisp.hpp"
+#include "physic.hpp"
+#include "timer.hpp"
+
+class Player;
+
+/** A tile that starts falling down if tux stands to long on it */
+class InvisibleWall : public MovingSprite, private UsesPhysic
+{
+public:
+ InvisibleWall(const lisp::Lisp& lisp);
+ virtual InvisibleWall* clone() const { return new InvisibleWall(*this); }
+
+ HitResponse collision(GameObject& other, const CollisionHit& hit);
+
+private:
+ float width, height;
+};
+
+#endif
--- /dev/null
+// $Id$
+//
+// SuperTux - Ispy
+// Copyright (C) 2007 Christoph Sommer <christoph.sommer@2007.expires.deltadevelopment.de>
+//
+// 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 <config.h>
+
+#include "ispy.hpp"
+#include "resources.hpp"
+#include "sprite/sprite_manager.hpp"
+#include "video/drawing_context.hpp"
+#include "player.hpp"
+#include "object_factory.hpp"
+#include "game_session.hpp"
+#include "sector.hpp"
+#include "tile.hpp"
+#include "object/tilemap.hpp"
+#include "random_generator.hpp"
+#include "object/sprite_particle.hpp"
+
+Ispy::Ispy(const lisp::Lisp& reader)
+ : MovingSprite(reader, "images/objects/ispy/ispy.sprite", LAYER_TILES+5, COLGROUP_DISABLED), state(ISPYSTATE_IDLE), dir(AUTO)
+{
+ // read script to execute
+ reader.get("script", script);
+
+ // read direction to face in
+ std::string dir_str;
+ bool facing_down = false;
+ reader.get("direction", dir_str);
+ if( dir_str == "left" ) dir = LEFT;
+ if( dir_str == "right" ) dir = RIGHT;
+ reader.get("facing-down", facing_down);
+ if (facing_down) dir = DOWN;
+ if (dir == AUTO) log_warning << "Setting an Ispy's direction to AUTO is no good idea" << std::endl;
+
+ // set initial sprite action
+ sprite->set_action((dir == DOWN) ? "idle-down" : ((dir == LEFT) ? "idle-left" : "idle-right"));
+}
+
+void
+Ispy::write(lisp::Writer& writer)
+{
+ writer.start_list("ispy");
+ writer.write_float("x", bbox.p1.x);
+ writer.write_float("y", bbox.p1.y);
+ writer.write_string("script", script);
+ switch (dir)
+ {
+ case DOWN:
+ writer.write_string("direction", "down"); break;
+ case LEFT:
+ writer.write_string("direction", "left"); break;
+ case RIGHT:
+ writer.write_string("direction", "right"); break;
+ default: break;
+ }
+ writer.end_list("ispy");
+}
+
+HitResponse
+Ispy::collision(GameObject& , const CollisionHit& )
+{
+ return ABORT_MOVE;
+}
+
+bool
+Ispy::line_intersects_line(Vector line1_start, Vector line1_end, Vector line2_start, Vector line2_end) {
+ // Adapted from Striker, (C) 1999 Joris van der Hoeven, GPL
+
+ float a1 = line1_start.x, b1 = line1_start.y, a2 = line1_end.x, b2 = line1_end.y;
+ float c1 = line2_start.x, d1 = line2_start.y, c2 = line2_end.x, d2 = line2_end.y;
+
+ float num = (b2-b1)*(c2-c1) - (a2-a1)*(d2-d1);
+ float den1 = (d2-b2)*(c1-c2) + (a2-c2)*(d1-d2);
+ float den2 = (d2-b2)*(a1-a2) + (a2-c2)*(b1-b2);
+
+ // normalize to positive numerator
+ if (num < 0) {
+ num =- num;
+ den1 =- den1;
+ den2 =- den2;
+ }
+
+ // numerator is zero -> Check for parallel or coinciding lines
+ if (num == 0) {
+ if ((b1-b2)*(c1-a2) != (a1-a2)*(d1-b2)) return false;
+ if (a1 == a2) {
+ std::swap(a1, b1);
+ std::swap(a2, b2);
+ std::swap(c1, d1);
+ std::swap(c2, d2);
+ }
+ if (a1 > a2) std::swap(a1, a2);
+ if (c1 > c2) std::swap(c1, c2);
+ return ((a1 <= c2) && (a2 >= c1));
+ }
+
+ // Standard check
+ return (den1>=0) && (den1<=num) && (den2>=0) && (den2<=num);
+
+}
+
+bool
+Ispy::intersects_line(Rect r, Vector line_start, Vector line_end)
+{
+ Vector p1 = r.p1;
+ Vector p2 = Vector(r.p2.x, r.p1.y);
+ Vector p3 = r.p2;
+ Vector p4 = Vector(r.p1.x, r.p2.y);
+ if (line_intersects_line(p1, p2, line_start, line_end)) return true;
+ if (line_intersects_line(p2, p3, line_start, line_end)) return true;
+ if (line_intersects_line(p3, p4, line_start, line_end)) return true;
+ if (line_intersects_line(p4, p1, line_start, line_end)) return true;
+ return false;
+}
+
+bool
+Ispy::free_line_of_sight(Vector line_start, Vector line_end, const MovingObject* ignore_object)
+{
+
+ // check if no tile is in the way
+ float lsx = std::min(line_start.x, line_end.x);
+ float lex = std::max(line_start.x, line_end.x);
+ float lsy = std::min(line_start.y, line_end.y);
+ float ley = std::max(line_start.y, line_end.y);
+ std::list<TileMap*> solid_tilemaps = Sector::current()->solid_tilemaps;
+ for (float test_x = lsx; test_x <= lex; test_x += 16) {
+ for (float test_y = lsy; test_y <= ley; test_y += 16) {
+ for(std::list<TileMap*>::const_iterator i = solid_tilemaps.begin(); i != solid_tilemaps.end(); i++) {
+ TileMap* solids = *i;
+ const Tile* tile = solids->get_tile_at(Vector(test_x, test_y));
+ if(!tile) continue;
+ // FIXME: check collision with slope tiles
+ if((tile->getAttributes() & Tile::SOLID)) return false;
+ }
+ }
+ }
+
+ // check if no object is in the way
+ using namespace collision;
+ Sector::MovingObjects& moving_objects = Sector::current()->moving_objects;
+ for(Sector::MovingObjects::const_iterator i = moving_objects.begin();
+ i != moving_objects.end(); ++i) {
+ const MovingObject* moving_object = *i;
+ if (moving_object == ignore_object) continue;
+ if (!moving_object->is_valid()) continue;
+ if ((moving_object->get_group() == COLGROUP_MOVING)
+ || (moving_object->get_group() == COLGROUP_MOVING_STATIC)
+ || (moving_object->get_group() == COLGROUP_STATIC)) {
+ if(intersects_line(moving_object->get_bbox(), line_start, line_end)) return false;
+ }
+ }
+
+ return true;
+}
+
+void
+Ispy::update(float )
+{
+
+ if (state == ISPYSTATE_IDLE) {
+ // check if a player has been spotted
+ bool playerSpotted = false;
+ std::vector<Player*> players = Sector::current()->get_players();
+ for (std::vector<Player*>::iterator playerIter = players.begin(); playerIter != players.end(); ++playerIter) {
+ Player* player = *playerIter;
+
+ Vector eye = get_bbox().get_middle();
+ if (dir == LEFT) eye = Vector(get_bbox().p1.x, get_bbox().get_middle().y);
+ if (dir == RIGHT) eye = Vector(get_bbox().p2.x, get_bbox().get_middle().y);
+ if (dir == UP) eye = Vector(get_bbox().get_middle().x, get_bbox().p1.y);
+ if (dir == DOWN) eye = Vector(get_bbox().get_middle().x, get_bbox().p2.y);
+
+ // test for free line of sight to any of all four corners and the middle of a player's bounding box
+ if (free_line_of_sight(eye, player->get_bbox().p1, player)) playerSpotted = true;
+ if (free_line_of_sight(eye, Vector(player->get_bbox().p2.x, player->get_bbox().p1.y), player)) playerSpotted = true;
+ if (free_line_of_sight(eye, player->get_bbox().p2, player)) playerSpotted = true;
+ if (free_line_of_sight(eye, Vector(player->get_bbox().p1.x, player->get_bbox().p2.y), player)) playerSpotted = true;
+ if (free_line_of_sight(eye, player->get_bbox().get_middle(), player)) playerSpotted = true;
+ }
+
+ if (playerSpotted) {
+ sprite->set_action((dir == DOWN) ? "alert-down" : ((dir == LEFT) ? "alert-left" : "alert-right"), 1);
+ state = ISPYSTATE_ALERT;
+
+ std::istringstream stream(script);
+ Sector::current()->run_script(stream, "Ispy");
+ }
+ }
+ if (state == ISPYSTATE_ALERT) {
+ if (sprite->animation_done()) {
+ sprite->set_action((dir == DOWN) ? "hiding-down" : ((dir == LEFT) ? "hiding-left" : "hiding-right"), 1);
+ state = ISPYSTATE_HIDING;
+ }
+ }
+ if (state == ISPYSTATE_HIDING) {
+ if (sprite->animation_done()) {
+ sprite->set_action((dir == DOWN) ? "showing-down" : ((dir == LEFT) ? "showing-left" : "showing-right"), 1);
+ state = ISPYSTATE_SHOWING;
+ }
+ }
+ if (state == ISPYSTATE_SHOWING) {
+ if (sprite->animation_done()) {
+ sprite->set_action((dir == DOWN) ? "idle-down" : ((dir == LEFT) ? "idle-left" : "idle-right"));
+ state = ISPYSTATE_IDLE;
+ }
+ }
+}
+
+IMPLEMENT_FACTORY(Ispy, "ispy");
+
+
--- /dev/null
+// $Id$
+//
+// SuperTux - Ispy
+// Copyright (C) 2007 Christoph Sommer <christoph.sommer@2007.expires.deltadevelopment.de>
+//
+// 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 __OBJECT_ISPY_H__
+#define __OBJECT_ISPY_H__
+
+#include "lisp/lisp.hpp"
+#include "object/moving_sprite.hpp"
+#include "serializable.hpp"
+#include "badguy/badguy.hpp"
+#include "direction.hpp"
+
+/**
+ * An Ispy: When it spots Tux, a script will run.
+ */
+class Ispy : public MovingSprite, public Serializable
+{
+public:
+ Ispy(const lisp::Lisp& lisp);
+
+ void write(lisp::Writer& writer);
+ HitResponse collision(GameObject& other, const CollisionHit& hit);
+
+ virtual void update(float elapsed_time);
+
+private:
+ bool line_intersects_line(Vector line1_start, Vector line1_end, Vector line2_start, Vector line2_end);
+ bool intersects_line(Rect r, Vector line_start, Vector line_end);
+ bool free_line_of_sight(Vector p1, Vector p2, const MovingObject* ignore_object);
+
+ enum IspyState {
+ ISPYSTATE_IDLE,
+ ISPYSTATE_ALERT,
+ ISPYSTATE_HIDING,
+ ISPYSTATE_SHOWING
+ };
+ IspyState state; /**< current state */
+
+ std::string script; /**< script to execute when Tux is spotted */
+ Direction dir;
+
+};
+
+#endif
+
--- /dev/null
+// $Id$
+//
+// SuperTux - Lantern
+// Copyright (C) 2006 Wolfgang Becker <uafr@gmx.de>
+//
+// 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 <config.h>
+
+#include "lantern.hpp"
+#include "sprite/sprite_manager.hpp"
+#include "object_factory.hpp"
+#include "badguy/willowisp.hpp"
+#include "badguy/treewillowisp.hpp"
+
+Lantern::Lantern(const lisp::Lisp& reader)
+ : Rock(reader, "images/objects/lantern/lantern.sprite"),
+ lightcolor(1.0f, 1.0f, 1.0f)
+{
+ //get color from lisp
+ std::vector<float> vColor;
+ reader.get_vector("color", vColor);
+ lightcolor = Color(vColor);
+ lightsprite = sprite_manager->create("images/objects/lightmap_light/lightmap_light.sprite");
+ lightsprite->set_blend(Blend(GL_SRC_ALPHA, GL_ONE));
+ updateColor();
+ sound_manager->preload("sounds/willocatch.wav");
+}
+
+Lantern::Lantern(const Vector& pos)
+ : Rock(pos, "images/objects/lantern/lantern.sprite"),
+ lightcolor(0.0f, 0.0f, 0.0f)
+{
+ lightsprite = sprite_manager->create("images/objects/lightmap_light/lightmap_light.sprite");
+ lightsprite->set_blend(Blend(GL_SRC_ALPHA, GL_ONE));
+ updateColor();
+ sound_manager->preload("sounds/willocatch.wav");
+}
+
+Lantern::~Lantern()
+{
+ delete lightsprite;
+}
+
+void
+Lantern::updateColor(){
+ lightsprite->set_color(lightcolor);
+ //Turn lantern off if light is black
+ if(lightcolor.red == 0 && lightcolor.green == 0 && lightcolor.blue == 0){
+ sprite->set_action("off");
+ } else {
+ sprite->set_action("normal");
+ sprite->set_color(lightcolor);
+ }
+}
+
+void
+Lantern::draw(DrawingContext& context){
+ //Draw the Sprite.
+ MovingSprite::draw(context);
+ //Let there be light.
+ context.push_target();
+ context.set_target(DrawingContext::LIGHTMAP);
+
+ lightsprite->draw(context, get_bbox().get_middle(), 0);
+
+ context.pop_target();
+}
+
+HitResponse Lantern::collision(GameObject& other, const CollisionHit& hit) {
+ if (is_open()) {
+ WillOWisp* wow = dynamic_cast<WillOWisp*>(&other);
+ if (wow) {
+ // collided with WillOWisp while grabbed and unlit
+ sound_manager->play("sounds/willocatch.wav");
+ lightcolor = Color(0,1,0);
+ updateColor();
+ wow->vanish();
+ }
+ TreeWillOWisp* twow = dynamic_cast<TreeWillOWisp*>(&other);
+ if (twow) {
+ // collided with TreeWillOWisp while grabbed and unlit
+ sound_manager->play("sounds/willocatch.wav");
+ lightcolor = twow->get_color();
+ updateColor();
+ twow->vanish();
+ }
+ }
+ return Rock::collision(other, hit);
+}
+
+void
+Lantern::grab(MovingObject& object, const Vector& pos, Direction dir)
+{
+ Rock::grab(object, pos, dir);
+
+ // if lantern is not lit, draw it as opened
+ if (is_open()) {
+ sprite->set_action("off-open");
+ }
+
+}
+
+void
+Lantern::ungrab(MovingObject& object, Direction dir)
+{
+ // if lantern is not lit, it was drawn as opened while grabbed. Now draw it as closed again
+ if (is_open()) {
+ sprite->set_action("off");
+ }
+
+ Rock::ungrab(object, dir);
+}
+
+bool
+Lantern::is_open()
+{
+ return ((grabbed) && lightcolor.red == 0 && lightcolor.green == 0 && lightcolor.blue == 0);
+}
+
+IMPLEMENT_FACTORY(Lantern, "lantern");
+
--- /dev/null
+// $Id$
+//
+// SuperTux - Lantern
+// Copyright (C) 2006 Wolfgang Becker <uafr@gmx.de>
+//
+// 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_LANTERN_H__
+#define __SUPERTUX_LANTERN_H___
+
+#include "object/moving_sprite.hpp"
+#include "object/rock.hpp"
+
+/**
+ * Lantern. A portable Light Source.
+ */
+class Lantern : public Rock
+{
+public:
+ Lantern(const Vector& pos);
+ Lantern(const lisp::Lisp& reader);
+ void draw(DrawingContext& context);
+ ~Lantern();
+
+ HitResponse collision(GameObject& other, const CollisionHit& hit);
+
+ void grab(MovingObject& object, const Vector& pos, Direction dir);
+ void ungrab(MovingObject& object, Direction dir);
+
+ /**
+ * returns true if lamp is currently open
+ */
+ bool is_open();
+
+ /**
+ * returns the lamp's color
+ */
+ Color get_color() const {
+ return lightcolor;
+ }
+
+private:
+ Color lightcolor;
+ Sprite* lightsprite;
+ void updateColor();
+};
+
+#endif
--- /dev/null
+// $Id$
+//
+// SuperTux
+// Copyright (C) 2006 Matthias Braun <matze@braunis.de>
+//
+// 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 <config.h>
+
+#include "level_time.hpp"
+
+#include <stdexcept>
+#include <iostream>
+#include <sstream>
+#include "main.hpp"
+#include "resources.hpp"
+#include "sector.hpp"
+#include "gettext.hpp"
+#include "object_factory.hpp"
+#include "object/player.hpp"
+#include "video/drawing_context.hpp"
+#include "lisp/list_iterator.hpp"
+#include "log.hpp"
+#include "scripting/level_time.hpp"
+#include "scripting/squirrel_util.hpp"
+
+/** When to alert player they're low on time! */
+static const float TIME_WARNING = 20;
+
+LevelTime::LevelTime(const lisp::Lisp& reader)
+: running(true), time_left(0)
+{
+ reader.get("name", name);
+ reader.get("time", time_left);
+ if(time_left <= 0) throw std::runtime_error("No or invalid leveltime specified");
+ time_surface.reset(new Surface("images/engine/hud/time-0.png"));
+}
+
+void
+LevelTime::expose(HSQUIRRELVM vm, SQInteger table_idx)
+{
+ if (name.empty()) return;
+ Scripting::LevelTime* interface = new Scripting::LevelTime(this);
+ expose_object(vm, table_idx, interface, name, true);
+}
+
+void
+LevelTime::unexpose(HSQUIRRELVM vm, SQInteger table_idx)
+{
+ if (name.empty()) return;
+ Scripting::unexpose_object(vm, table_idx, name);
+}
+
+void
+LevelTime::update(float elapsed_time)
+{
+ if (!running) return;
+
+ int prev_time = (int) floor(time_left*5);
+ time_left -= elapsed_time;
+ if(time_left <= 0) {
+ if(time_left <= -5 || !Sector::current()->player->get_coins())
+ {
+ Sector::current()->player->kill(true);
+ stop();
+ }
+ if(prev_time != (int) floor(time_left*5))
+ {
+ Sector::current()->player->add_coins(-1);
+ }
+ }
+}
+
+void
+LevelTime::draw(DrawingContext& context)
+{
+ context.push_transform();
+ context.set_translation(Vector(0, 0));
+
+ if ((time_left > TIME_WARNING) || (int(game_time * 2.5) % 2)) {
+ std::stringstream ss;
+ ss << int(time_left);
+ std::string time_text = ss.str();
+
+ Surface* time_surf = time_surface.get();
+ if (time_surf) {
+ float all_width = time_surf->get_width() + white_text->get_text_width(time_text);
+ context.draw_surface(time_surf, Vector((SCREEN_WIDTH - all_width)/2, BORDER_Y + 1), LAYER_FOREGROUND1);
+ context.draw_text(gold_text, time_text, Vector((SCREEN_WIDTH - all_width)/2 + time_surf->get_width(), BORDER_Y), ALIGN_LEFT, LAYER_FOREGROUND1);
+ }
+ }
+
+ context.pop_transform();
+}
+
+void
+LevelTime::start()
+{
+ running = true;
+}
+
+void
+LevelTime::stop()
+{
+ running = false;
+}
+
+float
+LevelTime::get_time()
+{
+ return time_left;
+}
+
+void
+LevelTime::set_time(float time_left)
+{
+ this->time_left = std::min(std::max(time_left, 0.0f), 999.0f);
+}
+
+IMPLEMENT_FACTORY(LevelTime, "leveltime");
--- /dev/null
+// $Id$
+//
+// SuperTux
+// Copyright (C) 2006 Matthias Braun <matze@braunis.de>
+//
+// 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 __LEVELTIME_H__
+#define __LEVELTIME_H__
+
+#include <memory>
+#include "game_object.hpp"
+#include "timer.hpp"
+#include "lisp/lisp.hpp"
+#include "video/surface.hpp"
+#include "script_interface.hpp"
+
+class LevelTime : public GameObject, public ScriptInterface
+{
+public:
+ LevelTime(const lisp::Lisp& reader);
+
+ virtual void expose(HSQUIRRELVM vm, SQInteger table_idx);
+ virtual void unexpose(HSQUIRRELVM vm, SQInteger table_idx);
+
+ void update(float elapsed_time);
+ void draw(DrawingContext& context);
+
+ /**
+ * @name Scriptable Methods
+ * @{
+ */
+
+ /**
+ * Resumes the countdown
+ */
+ void start();
+
+ /**
+ * Pauses the countdown
+ */
+ void stop();
+
+ /**
+ * Returns the number of seconds left on the clock
+ */
+ float get_time();
+
+ /**
+ * Changes the number of seconds left on the clock
+ */
+ void set_time(float time_left);
+
+ /**
+ * @}
+ */
+
+private:
+ std::auto_ptr<Surface> time_surface;
+ bool running;
+ float time_left;
+};
+
+#endif
--- /dev/null
+// $Id$
+//
+// SuperTux
+// Copyright (C) 2006 Matthias Braun <matze@braunis.de>
+//
+// 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 <config.h>
+
+#include "light.hpp"
+#include "sprite/sprite_manager.hpp"
+#include "resources.hpp"
+#include "video/drawing_context.hpp"
+#include "object_factory.hpp"
+#include "player.hpp"
+#include "sector.hpp"
+
+Light::Light(const Vector& center, const Color& color) : position(center), color(color)
+{
+ sprite = sprite_manager->create("images/objects/lightmap_light/lightmap_light.sprite");
+}
+
+Light::~Light()
+{
+ delete sprite;
+}
+
+void
+Light::update(float )
+{
+}
+
+void
+Light::draw(DrawingContext& context)
+{
+ context.push_target();
+ context.set_target(DrawingContext::LIGHTMAP);
+
+ sprite->set_color(color);
+ sprite->set_blend(Blend(GL_SRC_ALPHA, GL_ONE));
+ sprite->set_angle(90); // FIXME: color won't get applied for angle=0
+ sprite->draw(context, position, 0);
+
+ context.pop_target();
+}
--- /dev/null
+// $Id$
+//
+// SuperTux
+// Copyright (C) 2006 Matthias Braun <matze@braunis.de>
+//
+// 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 __LIGHT_HPP__
+#define __LIGHT_HPP__
+
+#include "game_object.hpp"
+#include "lisp/lisp.hpp"
+#include "math/vector.hpp"
+#include "video/color.hpp"
+
+class Sprite;
+
+class Light : public GameObject
+{
+public:
+ Light(const Vector& center, const Color& color = Color(1.0, 1.0, 1.0, 1.0));
+ virtual ~Light();
+
+ void update(float elapsed_time);
+ void draw(DrawingContext& context);
+
+protected:
+ Vector position;
+ Color color;
+ Sprite* sprite;
+};
+
+#endif
--- /dev/null
+// $Id$
+//
+// SuperTux - MagicBlock
+//
+// Magic Blocks are tile-like game objects that are sensitive to
+// lighting conditions. They are rendered in a color and
+// will only be solid as long as light of the same color shines
+// on the block.
+//
+// Copyright (C) 2006 Wolfgang Becker <uafr@gmx.de>
+//
+// 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 <config.h>
+#include <vector>
+
+#include "object/camera.hpp"
+#include "magicblock.hpp"
+#include "object_factory.hpp"
+#include "sprite/sprite_manager.hpp"
+#include "sector.hpp"
+#include "main.hpp"
+
+namespace {
+ const float MIN_INTENSITY = 0.8f;
+ const float ALPHA_SOLID = 0.7f;
+ const float ALPHA_NONSOLID = 0.3f;
+ const float MIN_SOLIDTIME = 1.0f;
+ const float SWITCH_DELAY = 0.1f; /**< seconds to wait for stable conditions until switching solidity */
+}
+
+MagicBlock::MagicBlock(const lisp::Lisp& lisp)
+ : MovingSprite(lisp, "images/objects/magicblock/magicblock.sprite"),
+ is_solid(false), solid_time(0), switch_delay(0), light(1.0f,1.0f,1.0f)
+{
+ set_group(COLGROUP_STATIC);
+ //get color from lisp
+ std::vector<float> vColor;
+ lisp.get_vector("color", vColor );
+ color = Color( vColor );
+
+ //all alpha to make the sprite still visible
+ color.alpha = ALPHA_SOLID;
+
+ //set trigger
+ if(color.red == 0 && color.green == 0 && color.blue == 0) { //is it black?
+ black = true;
+ trigger_red = MIN_INTENSITY;
+ trigger_green = MIN_INTENSITY;
+ trigger_blue = MIN_INTENSITY;
+ } else {
+ black = false;
+ trigger_red = (color.red == 1.0f ? MIN_INTENSITY : 0);
+ trigger_green = (color.green == 1.0f ? MIN_INTENSITY : 0);
+ trigger_blue = (color.blue == 1.0f ? MIN_INTENSITY : 0);
+ }
+
+ center = get_bbox().get_middle();
+}
+
+void
+MagicBlock::update(float elapsed_time)
+{
+ //Check if center of this block is on screen.
+ //Don't update if not, because there is no light off screen.
+ float screen_left = Sector::current()->camera->get_translation().x;
+ float screen_top = Sector::current()->camera->get_translation().y;
+ float screen_right = screen_left+ SCREEN_WIDTH;
+ float screen_bottom = screen_top + SCREEN_HEIGHT;
+ if((center.x > screen_right ) || ( center.y > screen_bottom) ||
+ ( center.x < screen_left) || ( center.y < screen_top)) {
+ switch_delay = SWITCH_DELAY;
+ return;
+ }
+
+ bool lighting_ok;
+ if(black) {
+ lighting_ok = (light.red >= trigger_red || light.green >= trigger_green
+ || light.blue >= trigger_blue);
+ }else{
+ lighting_ok = (light.red >= trigger_red && light.green >= trigger_green
+ && light.blue >= trigger_blue);
+ }
+
+ // overrule lighting_ok if switch_delay has not yet passed
+ if (lighting_ok == is_solid) {
+ switch_delay = SWITCH_DELAY;
+ } else {
+ if (switch_delay > 0) {
+ lighting_ok = is_solid;
+ switch_delay -= elapsed_time;
+ }
+ }
+
+ if (lighting_ok) {
+ // lighting suggests going solid
+
+ if (!is_solid) {
+ if (Sector::current()->is_free_of_movingstatics(get_bbox(), this)) {
+ is_solid = true;
+ solid_time = 0;
+ switch_delay = SWITCH_DELAY;
+ }
+ }
+ } else {
+ // lighting suggests going nonsolid
+
+ if( solid_time >= MIN_SOLIDTIME ){
+ is_solid = false;
+ }
+ }
+
+ //Update Sprite.
+ if(is_solid) {
+ solid_time+=elapsed_time;
+ color.alpha = ALPHA_SOLID;
+ sprite->set_action("solid");
+ } else {
+ color.alpha = ALPHA_NONSOLID;
+ sprite->set_action("normal");
+ }
+}
+
+void
+MagicBlock::draw(DrawingContext& context){
+ //Ask for update about lightmap at center of this block
+ context.get_light( center, &light );
+
+ //Draw the Sprite.
+ MovingSprite::draw(context);
+ //Add the color.
+ context.draw_filled_rect( get_bbox(), color, layer);
+}
+
+bool
+MagicBlock::collides(GameObject& /*other*/, const CollisionHit& /*hit*/)
+{
+ return is_solid;
+}
+
+HitResponse
+MagicBlock::collision(GameObject& /*other*/, const CollisionHit& /*hit*/)
+{
+ return SOLID;
+}
+
+IMPLEMENT_FACTORY(MagicBlock, "magicblock");
--- /dev/null
+// $Id$
+//
+// SuperTux - MagicBlock
+//
+// Magic Blocks are tile-like game objects that are sensitive to
+// lighting conditions. They are rendered in a color and
+// will only be solid as long as light of the same color shines
+// on the block. The black block becomes solid, if any kind of
+// light is above MIN_INTENSITY.
+//
+// Copyright (C) 2006 Wolfgang Becker <uafr@gmx.de>
+//
+// 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_TRAMPOLINE_H
+#define SUPERTUX_TRAMPOLINE_H
+
+#include "moving_sprite.hpp"
+#include "lisp/lisp.hpp"
+
+class MagicBlock: public MovingSprite
+{
+public:
+ MagicBlock(const lisp::Lisp& reader);
+
+ bool collides(GameObject& other, const CollisionHit& hit);
+ HitResponse collision(GameObject& other, const CollisionHit& hit);
+ void update(float elapsed_time);
+ void draw(DrawingContext& context);
+
+private:
+ bool is_solid;
+ float trigger_red;
+ float trigger_green;
+ float trigger_blue;
+ float solid_time;
+ float switch_delay; /**< seconds until switching solidity */
+ Color color;
+ Color light;
+ Vector center;
+ bool black;
+};
+
+#endif
--- /dev/null
+// $Id$
+//
+// SuperTux - MovingSprite Base Class
+// Copyright (C) 2006 Christoph Sommer <christoph.sommer@2006.expires.deltadevelopment.de>
+//
+// 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 <config.h>
+
+#include <stdexcept>
+
+#include "moving_sprite.hpp"
+#include "video/drawing_context.hpp"
+#include "sprite/sprite_manager.hpp"
+#include "player.hpp"
+#include "sector.hpp"
+#include "player_status.hpp"
+#include "gameobjs.hpp"
+#include "statistics.hpp"
+#include "object_factory.hpp"
+#include "level.hpp"
+#include "random_generator.hpp"
+#include "audio/sound_source.hpp"
+#include "audio/sound_manager.hpp"
+#include "timer.hpp"
+
+MovingSprite::MovingSprite(const Vector& pos, const std::string& sprite_name, int layer, CollisionGroup collision_group)
+ : sprite_name(sprite_name), layer(layer)
+{
+ bbox.set_pos(pos);
+ sprite = sprite_manager->create(sprite_name);
+ bbox.set_size(sprite->get_current_hitbox_width(), sprite->get_current_hitbox_height());
+ set_group(collision_group);
+}
+
+MovingSprite::MovingSprite(const lisp::Lisp& reader, const Vector& pos, int layer, CollisionGroup collision_group)
+ : layer(layer)
+{
+ bbox.set_pos(pos);
+ if (!reader.get("sprite", sprite_name))
+ throw std::runtime_error("no sprite name set");
+
+ sprite = sprite_manager->create(sprite_name);
+ bbox.set_size(sprite->get_current_hitbox_width(), sprite->get_current_hitbox_height());
+ set_group(collision_group);
+}
+
+MovingSprite::MovingSprite(const lisp::Lisp& reader, const std::string& sprite_name, int layer, CollisionGroup collision_group)
+ : sprite_name(sprite_name), layer(layer)
+{
+ if (!reader.get("x", bbox.p1.x))
+ throw std::runtime_error("no x position set");
+ if (!reader.get("y", bbox.p1.y))
+ throw std::runtime_error("no y position set");
+
+ sprite = sprite_manager->create(sprite_name);
+ bbox.set_size(sprite->get_current_hitbox_width(), sprite->get_current_hitbox_height());
+ set_group(collision_group);
+}
+
+MovingSprite::MovingSprite(const lisp::Lisp& reader, int layer, CollisionGroup collision_group)
+ : layer(layer)
+{
+ if (!reader.get("x", bbox.p1.x))
+ throw std::runtime_error("no x position set");
+ if (!reader.get("y", bbox.p1.y))
+ throw std::runtime_error("no y position set");
+ if (!reader.get("sprite", sprite_name))
+ throw std::runtime_error("no sprite name set");
+
+ sprite = sprite_manager->create(sprite_name);
+ bbox.set_size(sprite->get_current_hitbox_width(), sprite->get_current_hitbox_height());
+ set_group(collision_group);
+}
+
+MovingSprite::MovingSprite(const MovingSprite& other)
+ : MovingObject(other), layer(other.layer)
+{
+ sprite = new Sprite(*other.sprite);
+}
+
+MovingSprite&
+MovingSprite::operator=(const MovingSprite& other)
+{
+ if (this == &other)
+ return *this;
+
+ delete sprite;
+ sprite = new Sprite(*other.sprite);
+
+ layer = other.layer;
+
+ return *this;
+}
+
+MovingSprite::~MovingSprite()
+{
+ delete sprite;
+}
+
+void
+MovingSprite::draw(DrawingContext& context)
+{
+ sprite->draw(context, get_pos(), layer);
+}
+
+void
+MovingSprite::update(float )
+{
+}
+
+void
+MovingSprite::set_action(const std::string& action, int loops)
+{
+ sprite->set_action(action, loops);
+ set_size(sprite->get_current_hitbox_width(), sprite->get_current_hitbox_height());
+}
+
+void
+MovingSprite::set_action_centered(const std::string& action, int loops)
+{
+ Vector old_size = bbox.get_size();
+ sprite->set_action(action, loops);
+ set_size(sprite->get_current_hitbox_width(), sprite->get_current_hitbox_height());
+ set_pos(get_pos() - (bbox.get_size() - old_size) / 2);
+}
+
+void
+MovingSprite::set_action(const std::string& action, int loops, AnchorPoint anchorPoint)
+{
+ Rect old_bbox = bbox;
+ sprite->set_action(action, loops);
+ float w = sprite->get_current_hitbox_width();
+ float h = sprite->get_current_hitbox_height();
+ set_size(w, h);
+ set_pos(get_anchor_pos(old_bbox, w, h, anchorPoint));
+}
--- /dev/null
+// $Id$
+//
+// SuperTux - MovingSprite Base Class
+// Copyright (C) 2006 Christoph Sommer <christoph.sommer@2006.expires.deltadevelopment.de>
+//
+// 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 __MOVING_SPRITE_H__
+#define __MOVING_SPRITE_H__
+
+#include <string>
+#include "moving_object.hpp"
+#include "sprite/sprite.hpp"
+#include "video/drawing_context.hpp"
+#include "object/anchor_point.hpp"
+
+/**
+ * Abstract base class for MovingObjects that are represented by a Sprite
+ */
+class MovingSprite : public MovingObject
+{
+public:
+ MovingSprite(const Vector& pos, const std::string& sprite_name, int layer = LAYER_OBJECTS, CollisionGroup collision_group = COLGROUP_MOVING);
+ MovingSprite(const lisp::Lisp& reader, const Vector& pos, int layer = LAYER_OBJECTS, CollisionGroup collision_group = COLGROUP_MOVING);
+ MovingSprite(const lisp::Lisp& reader, const std::string& sprite_name, int layer = LAYER_OBJECTS, CollisionGroup collision_group = COLGROUP_MOVING);
+ MovingSprite(const lisp::Lisp& reader, int layer = LAYER_OBJECTS, CollisionGroup collision_group = COLGROUP_MOVING);
+ MovingSprite(const MovingSprite& moving_sprite);
+ MovingSprite& operator=(const MovingSprite& moving_sprite);
+ ~MovingSprite();
+
+ virtual void draw(DrawingContext& context);
+ virtual void update(float elapsed_time);
+
+protected:
+ std::string sprite_name;
+ Sprite* sprite;
+ int layer; /**< Sprite's z-position. Refer to video/drawing_context.hpp for sensible values. */
+
+ /**
+ * set new action for sprite and resize bounding box.
+ * use with care as you can easily get stuck when resizing the bounding box.
+ */
+ void set_action(const std::string& action, int loops);
+
+ /**
+ * set new action for sprite and re-center bounding box.
+ * use with care as you can easily get stuck when resizing the bounding box.
+ */
+ void set_action_centered(const std::string& action, int loops);
+
+ /**
+ * set new action for sprite and align bounding boxes at anchorPoint.
+ * use with care as you can easily get stuck when resizing the bounding box.
+ */
+ void set_action(const std::string& action, int loops, AnchorPoint anchorPoint);
+};
+
+#endif
--- /dev/null
+// $Id$
+//
+// SuperTux
+// Copyright (C) 2006 Matthias Braun <matze@braunis.de>
+//
+// 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 <config.h>
+
+#include "oneup.hpp"
+#include "resources.hpp"
+#include "player.hpp"
+#include "player_status.hpp"
+#include "sector.hpp"
+#include "level.hpp"
+#include "statistics.hpp"
+#include "video/drawing_context.hpp"
+
+OneUp::OneUp(const Vector& pos, Direction direction)
+ : MovingSprite(pos, "images/powerups/1up/1up.sprite", LAYER_FLOATINGOBJECTS, COLGROUP_TOUCHABLE)
+{
+ physic.set_velocity((direction == LEFT)?-100:100, -400);
+}
+
+void
+OneUp::update(float elapsed_time)
+{
+ if(!Sector::current()->inside(bbox))
+ remove_me();
+
+ movement = physic.get_movement(elapsed_time);
+}
+
+HitResponse
+OneUp::collision(GameObject& other, const CollisionHit& )
+{
+ Player* player = dynamic_cast<Player*> (&other);
+ if(player) {
+ player->get_status()->add_coins(100);
+#if 0
+ // FIXME: do we want this? q.v. src/level.cpp
+ Sector::current()->get_level()->stats.coins += 100;
+#endif
+ remove_me();
+ return ABORT_MOVE;
+ }
+ return FORCE_MOVE;
+}
--- /dev/null
+// $Id$
+//
+// SuperTux
+// Copyright (C) 2006 Matthias Braun <matze@braunis.de>
+//
+// 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 __ONEUP_H__
+#define __ONEUP_H__
+
+#include "object/moving_sprite.hpp"
+#include "physic.hpp"
+#include "direction.hpp"
+
+class OneUp : public MovingSprite, private UsesPhysic
+{
+public:
+ OneUp(const Vector& pos, Direction direction = RIGHT);
+ virtual OneUp* clone() const { return new OneUp(*this); }
+
+ virtual void update(float elapsed_time);
+ virtual HitResponse collision(GameObject& other, const CollisionHit& hit);
+};
+
+#endif
--- /dev/null
+// $Id$
+//
+// SuperTux
+// Copyright (C) 2006 Matthias Braun <matze@braunis.de>
+//
+// 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 <config.h>
+
+#include <math.h>
+
+#include "particles.hpp"
+#include "sector.hpp"
+#include "camera.hpp"
+#include "main.hpp"
+#include "random_generator.hpp"
+
+Particles::Particles(const Vector& epicenter, int min_angle, int max_angle,
+ const Vector& initial_velocity, const Vector& acceleration, int number,
+ Color color_, int size_, float life_time, int drawing_layer_)
+ : accel(acceleration), color(color_), size(size_), drawing_layer(drawing_layer_)
+{
+ if(life_time == 0) {
+ live_forever = true;
+ } else {
+ live_forever = false;
+ timer.start(life_time);
+ }
+
+ // create particles
+ for(int p = 0; p < number; p++)
+ {
+ Particle* particle = new Particle;
+ particle->pos = epicenter;
+
+ float angle = systemRandom.rand(min_angle, max_angle)
+ * (M_PI / 180); // convert to radius (radians?)
+ particle->vel.x = /*fabs*/(sin(angle)) * initial_velocity.x;
+// if(angle >= M_PI && angle < M_PI*2)
+// particle->vel.x *= -1; // work around to fix signal
+ particle->vel.y = /*fabs*/(cos(angle)) * initial_velocity.y;
+// if(angle >= M_PI_2 && angle < 3*M_PI_2)
+// particle->vel.y *= -1;
+
+ particles.push_back(particle);
+ }
+}
+
+Particles::~Particles()
+{
+ // free particles
+ for(std::vector<Particle*>::iterator i = particles.begin();
+ i < particles.end(); i++)
+ delete (*i);
+}
+
+void
+Particles::update(float elapsed_time)
+{
+ Vector camera = Sector::current()->camera->get_translation();
+
+ // update particles
+ for(std::vector<Particle*>::iterator i = particles.begin();
+ i != particles.end(); ) {
+ (*i)->pos.x += (*i)->vel.x * elapsed_time;
+ (*i)->pos.y += (*i)->vel.y * elapsed_time;
+
+ (*i)->vel.x += accel.x * elapsed_time;
+ (*i)->vel.y += accel.y * elapsed_time;
+
+ if((*i)->pos.x < camera.x || (*i)->pos.x > SCREEN_WIDTH + camera.x ||
+ (*i)->pos.y < camera.y || (*i)->pos.y > SCREEN_HEIGHT + camera.y) {
+ delete (*i);
+ i = particles.erase(i);
+ } else {
+ ++i;
+ }
+ }
+
+ if((timer.check() && !live_forever) || particles.size() == 0)
+ remove_me();
+}
+
+void
+Particles::draw(DrawingContext& context)
+{
+ // draw particles
+ for(std::vector<Particle*>::iterator i = particles.begin();
+ i != particles.end(); i++) {
+ context.draw_filled_rect((*i)->pos, Vector(size,size), color,drawing_layer);
+ }
+}
--- /dev/null
+// $Id$
+//
+// SuperTux
+// Copyright (C) 2006 Matthias Braun <matze@braunis.de>
+//
+// 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_PARTICLES_HPP
+#define SUPERTUX_PARTICLES_HPP
+
+#include "math/vector.hpp"
+#include "game_object.hpp"
+#include "timer.hpp"
+#include "video/color.hpp"
+
+class Particles : public GameObject
+{
+public:
+ Particles(const Vector& epicenter, int min_angle, int max_angle,
+ const Vector& initial_velocity, const Vector& acceleration,
+ int number, Color color, int size, float life_time,
+ int drawing_layer);
+ ~Particles();
+
+ virtual void update(float elapsed_time);
+ virtual void draw(DrawingContext& context);
+
+private:
+ Vector accel;
+ Timer timer;
+ bool live_forever;
+
+ Color color;
+ float size;
+ int drawing_layer;
+
+ struct Particle {
+ Vector pos, vel;
+// float angle;
+ };
+ std::vector <Particle*> particles;
+};
+
+#endif
--- /dev/null
+// $Id$
+//
+// SuperTux
+// Copyright (C) 2006 Matthias Braun <matze@braunis.de>
+//
+// 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 <config.h>
+
+#include <iostream>
+#include <cmath>
+
+#include "particlesystem.hpp"
+#include "video/drawing_context.hpp"
+#include "lisp/parser.hpp"
+#include "lisp/lisp.hpp"
+#include "lisp/writer.hpp"
+#include "resources.hpp"
+#include "main.hpp"
+#include "object/camera.hpp"
+#include "random_generator.hpp"
+
+ParticleSystem::ParticleSystem(float max_particle_size)
+ : max_particle_size(max_particle_size)
+{
+ virtual_width = SCREEN_WIDTH + max_particle_size * 2;
+ virtual_height = SCREEN_HEIGHT + max_particle_size *2;
+ z_pos = LAYER_BACKGROUND1;
+}
+
+ParticleSystem::~ParticleSystem()
+{
+ std::vector<Particle*>::iterator i;
+ for(i = particles.begin(); i != particles.end(); ++i) {
+ delete *i;
+ }
+}
+
+void ParticleSystem::draw(DrawingContext& context)
+{
+ float scrollx = context.get_translation().x;
+ float scrolly = context.get_translation().y;
+
+ context.push_transform();
+ context.set_translation(Vector(max_particle_size,max_particle_size));
+
+ std::vector<Particle*>::iterator i;
+ for(i = particles.begin(); i != particles.end(); ++i) {
+ Particle* particle = *i;
+
+ // remap x,y coordinates onto screencoordinates
+ Vector pos;
+
+ pos.x = fmodf(particle->pos.x - scrollx, virtual_width);
+ if(pos.x < 0) pos.x += virtual_width;
+
+ pos.y = fmodf(particle->pos.y - scrolly, virtual_height);
+ if(pos.y < 0) pos.y += virtual_height;
+
+
+ //if(pos.x > virtual_width) pos.x -= virtual_width;
+ //if(pos.y > virtual_height) pos.y -= virtual_height;
+
+ context.draw_surface(particle->texture, pos, z_pos);
+ }
+
+ context.pop_transform();
+}
+
+SnowParticleSystem::SnowParticleSystem()
+{
+ snowimages[0] = new Surface("images/objects/particles/snow2.png");
+ snowimages[1] = new Surface("images/objects/particles/snow1.png");
+ snowimages[2] = new Surface("images/objects/particles/snow0.png");
+
+ virtual_width = SCREEN_WIDTH * 2;
+
+ // create some random snowflakes
+ size_t snowflakecount = size_t(virtual_width/10.0);
+ for(size_t i=0; i<snowflakecount; ++i) {
+ SnowParticle* particle = new SnowParticle;
+ int snowsize = systemRandom.rand(3);
+
+ particle->pos.x = systemRandom.randf(virtual_width);
+ particle->pos.y = systemRandom.randf(SCREEN_HEIGHT);
+ particle->anchorx = particle->pos.x + (systemRandom.randf(-0.5, 0.5) * 16);
+ particle->drift_speed = systemRandom.randf(-0.5, 0.5) * 0.3;
+ particle->wobble = 0.0;
+
+ particle->texture = snowimages[snowsize];
+
+ particle->speed = 1 + (2 - snowsize)/2 + systemRandom.randf(1.8);
+ particle->speed *= 20; // gravity
+
+ particles.push_back(particle);
+ }
+}
+
+void
+SnowParticleSystem::parse(const lisp::Lisp& reader)
+{
+ reader.get("z-pos", z_pos);
+}
+
+void
+SnowParticleSystem::write(lisp::Writer& writer)
+{
+ writer.start_list("particles-snow");
+ writer.write_int("z-pos", z_pos);
+ writer.end_list("particles-snow");
+}
+
+SnowParticleSystem::~SnowParticleSystem()
+{
+ for(int i=0;i<3;++i)
+ delete snowimages[i];
+}
+
+void SnowParticleSystem::update(float elapsed_time)
+{
+ std::vector<Particle*>::iterator i;
+
+ for(i = particles.begin(); i != particles.end(); ++i) {
+ SnowParticle* particle = (SnowParticle*) *i;
+ float anchor_delta;
+
+ particle->pos.y += particle->speed * elapsed_time;
+ particle->pos.x += particle->wobble * elapsed_time /* * particle->speed * 0.125*/;
+
+ anchor_delta = (particle->anchorx - particle->pos.x);
+ particle->wobble += (4 * anchor_delta * 0.05) + systemRandom.randf(-0.5, 0.5);
+ particle->wobble *= 0.99f;
+ particle->anchorx += particle->drift_speed * elapsed_time;
+ }
+}
+
+//FIXME: Sometimes both ghosts have the same image
+// Ghosts don't change their movement pattern - not random
+GhostParticleSystem::GhostParticleSystem()
+{
+ ghosts[0] = new Surface("images/objects/particles/ghost0.png");
+ ghosts[1] = new Surface("images/objects/particles/ghost1.png");
+
+ virtual_width = SCREEN_WIDTH * 2;
+
+ // create two ghosts
+ size_t ghostcount = 2;
+ for(size_t i=0; i<ghostcount; ++i) {
+ GhostParticle* particle = new GhostParticle;
+ particle->pos.x = systemRandom.randf(virtual_width);
+ particle->pos.y = systemRandom.randf(SCREEN_HEIGHT);
+ int size = systemRandom.rand(2);
+ particle->texture = ghosts[size];
+ particle->speed = systemRandom.randf(std::max(50, (size * 10)), 180 + (size * 10));
+ particles.push_back(particle);
+ }
+}
+
+void
+GhostParticleSystem::parse(const lisp::Lisp& reader)
+{
+ reader.get("z-pos", z_pos);
+}
+
+void
+GhostParticleSystem::write(lisp::Writer& writer)
+{
+ writer.start_list("particles-ghosts");
+ writer.write_int("z-pos", z_pos);
+ writer.end_list("particles-ghosts");
+}
+
+GhostParticleSystem::~GhostParticleSystem()
+{
+ for(int i=0;i<2;++i)
+ delete ghosts[i];
+}
+
+void GhostParticleSystem::update(float elapsed_time)
+{
+ std::vector<Particle*>::iterator i;
+ for(i = particles.begin(); i != particles.end(); ++i) {
+ GhostParticle* particle = (GhostParticle*) *i;
+ particle->pos.y -= particle->speed * elapsed_time;
+ particle->pos.x -= particle->speed * elapsed_time;
+ if(particle->pos.y > SCREEN_HEIGHT) {
+ particle->pos.y = fmodf(particle->pos.y , virtual_height);
+ particle->pos.x = systemRandom.rand(static_cast<int>(virtual_width));
+ }
+ }
+}
+
+CloudParticleSystem::CloudParticleSystem()
+ : ParticleSystem(128)
+{
+ cloudimage = new Surface("images/objects/particles/cloud.png");
+
+ virtual_width = 2000.0;
+
+ // create some random clouds
+ for(size_t i=0; i<15; ++i) {
+ CloudParticle* particle = new CloudParticle;
+ particle->pos.x = systemRandom.rand(static_cast<int>(virtual_width));
+ particle->pos.y = systemRandom.rand(static_cast<int>(virtual_height));
+ particle->texture = cloudimage;
+ particle->speed = -systemRandom.randf(25.0, 54.0);
+
+ particles.push_back(particle);
+ }
+}
+
+void
+CloudParticleSystem::parse(const lisp::Lisp& reader)
+{
+ reader.get("z-pos", z_pos);
+}
+
+void
+CloudParticleSystem::write(lisp::Writer& writer)
+{
+ writer.start_list("particles-clouds");
+ writer.write_int("z-pos", z_pos);
+ writer.end_list("particles-clouds");
+}
+
+CloudParticleSystem::~CloudParticleSystem()
+{
+ delete cloudimage;
+}
+
+void CloudParticleSystem::update(float elapsed_time)
+{
+ std::vector<Particle*>::iterator i;
+ for(i = particles.begin(); i != particles.end(); ++i) {
+ CloudParticle* particle = (CloudParticle*) *i;
+ particle->pos.x += particle->speed * elapsed_time;
+ }
+}
--- /dev/null
+// $Id$
+//
+// SuperTux
+// Copyright (C) 2006 Matthias Braun <matze@braunis.de>
+//
+// 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_PARTICLESYSTEM_H
+#define SUPERTUX_PARTICLESYSTEM_H
+
+#include <vector>
+
+#include "video/surface.hpp"
+#include "game_object.hpp"
+#include "serializable.hpp"
+#include "sector.hpp"
+#include "math/vector.hpp"
+
+namespace lisp {
+class Lisp;
+}
+
+class DisplayManager;
+
+/**
+ * This is the base class for particle systems. It is responsible for storing a
+ * set of particles with each having an x- and y-coordinate the number of the
+ * layer where it should be drawn and a texture.
+ * The coordinate system used here is a virtual one. It would be a bad idea to
+ * populate whole levels with particles. So we're using a virtual rectangle
+ * here that is tiled onto the level when drawing. This rect.has the size
+ * (virtual_width, virtual_height). We're using modulo on the particle
+ * coordinates, so when a particle leaves left, it'll reenter at the right
+ * side.
+ *
+ * Classes that implement a particle system should subclass from this class,
+ * initialize particles in the constructor and move them in the simulate
+ * function.
+ */
+class ParticleSystem : public GameObject
+{
+public:
+ ParticleSystem(float max_particle_size = 60);
+ virtual ~ParticleSystem();
+
+ virtual void draw(DrawingContext& context);
+
+protected:
+ float max_particle_size;
+ int z_pos;
+
+ class Particle
+ {
+ public:
+ virtual ~Particle()
+ { }
+
+ Vector pos;
+ Surface* texture;
+ };
+
+ std::vector<Particle*> particles;
+ float virtual_width, virtual_height;
+};
+
+class SnowParticleSystem : public ParticleSystem, public Serializable
+{
+public:
+ SnowParticleSystem();
+ virtual ~SnowParticleSystem();
+
+ void parse(const lisp::Lisp& lisp);
+ void write(lisp::Writer& writer);
+
+ virtual void update(float elapsed_time);
+
+ std::string type() const
+ { return "SnowParticleSystem"; }
+
+private:
+ class SnowParticle : public Particle
+ {
+ public:
+ float speed;
+ float wobble;
+ float anchorx;
+ float drift_speed;
+ };
+
+ Surface* snowimages[3];
+};
+
+class GhostParticleSystem : public ParticleSystem, public Serializable
+{
+public:
+ GhostParticleSystem();
+ virtual ~GhostParticleSystem();
+
+ void parse(const lisp::Lisp& lisp);
+ void write(lisp::Writer& writer);
+
+ virtual void update(float elapsed_time);
+
+ std::string type() const
+ { return "GhostParticleSystem"; }
+
+private:
+ class GhostParticle : public Particle
+ {
+ public:
+ float speed;
+ };
+
+ Surface* ghosts[2];
+};
+
+class CloudParticleSystem : public ParticleSystem, public Serializable
+{
+public:
+ CloudParticleSystem();
+ virtual ~CloudParticleSystem();
+
+ void parse(const lisp::Lisp& lisp);
+ void write(lisp::Writer& writer);
+
+ virtual void update(float elapsed_time);
+
+ std::string type() const
+ { return "CloudParticleSystem"; }
+
+private:
+ class CloudParticle : public Particle
+ {
+ public:
+ float speed;
+ };
+
+ Surface* cloudimage;
+};
+
+#endif
--- /dev/null
+// $Id$
+//
+// SuperTux
+// Copyright (C) 2006 Matthias Braun <matze@braunis.de>
+//
+// 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 <config.h>
+
+#include <iostream>
+#include <cmath>
+
+#include "particlesystem_interactive.hpp"
+#include "video/drawing_context.hpp"
+#include "lisp/parser.hpp"
+#include "lisp/lisp.hpp"
+#include "lisp/writer.hpp"
+#include "resources.hpp"
+#include "main.hpp"
+
+#include "tile.hpp"
+#include "tilemap.hpp"
+#include "math/aatriangle.hpp"
+#include "collision.hpp"
+#include "collision_hit.hpp"
+#include "object/camera.hpp"
+#include "object/rainsplash.hpp"
+#include "badguy/bomb.hpp"
+#include "random_generator.hpp"
+
+//TODO: Find a way to make rain collide with objects like bonus blocks
+// Add an option to set rain strength
+// Fix rain being "respawned" over solid tiles
+ParticleSystem_Interactive::ParticleSystem_Interactive()
+{
+ virtual_width = SCREEN_WIDTH;
+ virtual_height = SCREEN_HEIGHT;
+ z_pos = 0;
+}
+
+ParticleSystem_Interactive::~ParticleSystem_Interactive()
+{
+ std::vector<Particle*>::iterator i;
+ for(i = particles.begin(); i != particles.end(); ++i) {
+ delete *i;
+ }
+}
+
+void ParticleSystem_Interactive::draw(DrawingContext& context)
+{
+ context.push_transform();
+
+ std::vector<Particle*>::iterator i;
+ for(i = particles.begin(); i != particles.end(); ++i) {
+ Particle* particle = *i;
+ context.draw_surface(particle->texture, particle->pos, z_pos);
+ }
+
+ context.pop_transform();
+}
+
+int
+ParticleSystem_Interactive::collision(Particle* object, Vector movement)
+{
+ using namespace collision;
+
+ // calculate rectangle where the object will move
+ float x1, x2;
+ float y1, y2;
+ x1 = object->pos.x;
+ x2 = x1 + 32 + movement.x;
+ y1 = object->pos.y;
+ y2 = y1 + 32 + movement.y;
+ bool water = false;
+
+ // test with all tiles in this rectangle
+ int starttilex = int(x1-1) / 32;
+ int starttiley = int(y1-1) / 32;
+ int max_x = int(x2+1);
+ int max_y = int(y2+1);
+
+ Rect dest = Rect(x1, y1, x2, y2);
+ dest.move(movement);
+ Constraints constraints;
+
+ for(std::list<TileMap*>::const_iterator i = Sector::current()->solid_tilemaps.begin(); i != Sector::current()->solid_tilemaps.end(); i++) {
+ TileMap* solids = *i;
+ for(int x = starttilex; x*32 < max_x; ++x) {
+ for(int y = starttiley; y*32 < max_y; ++y) {
+ const Tile* tile = solids->get_tile(x, y);
+ if(!tile)
+ continue;
+ // skip non-solid tiles, except water
+ if(! (tile->getAttributes() & (Tile::WATER | Tile::SOLID)))
+ continue;
+
+ if(tile->getAttributes() & Tile::SLOPE) { // slope tile
+ AATriangle triangle;
+ Vector p1(x*32, y*32);
+ Vector p2((x+1)*32, (y+1)*32);
+ triangle = AATriangle(p1, p2, tile->getData());
+
+ if(rectangle_aatriangle(&constraints, dest, triangle)) {
+ if(tile->getAttributes() & Tile::WATER)
+ water = true;
+ }
+ } else { // normal rectangular tile
+ Rect rect(x*32, y*32, (x+1)*32, (y+1)*32);
+ if(intersects(dest, rect)) {
+ if(tile->getAttributes() & Tile::WATER)
+ water = true;
+ set_rectangle_rectangle_constraints(&constraints, dest, rect);
+ }
+ }
+ }
+ }
+ }
+
+
+ // TODO don't use magic numbers here...
+
+ // did we collide at all?
+ if(!constraints.has_constraints())
+ return -1;
+
+ const CollisionHit& hit = constraints.hit;
+ if (water) {
+ return 0; //collision with water tile - don't draw splash
+ } else {
+ if (hit.right || hit.left) {
+ return 2; //collision from right
+ } else {
+ return 1; //collision from above
+ }
+ }
+
+ return 0;
+}
+
+RainParticleSystem::RainParticleSystem()
+{
+ rainimages[0] = new Surface("images/objects/particles/rain0.png");
+ rainimages[1] = new Surface("images/objects/particles/rain1.png");
+
+ virtual_width = SCREEN_WIDTH * 2;
+
+ // create some random raindrops
+ size_t raindropcount = size_t(virtual_width/6.0);
+ for(size_t i=0; i<raindropcount; ++i) {
+ RainParticle* particle = new RainParticle;
+ particle->pos.x = systemRandom.rand(int(virtual_width));
+ particle->pos.y = systemRandom.rand(int(virtual_height));
+ int rainsize = systemRandom.rand(2);
+ particle->texture = rainimages[rainsize];
+ do {
+ particle->speed = (rainsize+1)*45 + systemRandom.randf(3.6);
+ } while(particle->speed < 1);
+ particle->speed *= 10; // gravity
+
+ particles.push_back(particle);
+ }
+}
+
+void
+RainParticleSystem::parse(const lisp::Lisp& reader)
+{
+ reader.get("z-pos", z_pos);
+}
+
+void
+RainParticleSystem::write(lisp::Writer& writer)
+{
+ writer.start_list("particles-rain");
+ writer.write_int("z-pos", z_pos);
+ writer.end_list("particles-rain");
+}
+
+RainParticleSystem::~RainParticleSystem()
+{
+ for(int i=0;i<2;++i)
+ delete rainimages[i];
+}
+
+void RainParticleSystem::update(float elapsed_time)
+{
+ std::vector<Particle*>::iterator i;
+ for(
+ i = particles.begin(); i != particles.end(); ++i) {
+ RainParticle* particle = (RainParticle*) *i;
+ float movement = particle->speed * elapsed_time;
+ float abs_x = Sector::current()->camera->get_translation().x;
+ float abs_y = Sector::current()->camera->get_translation().y;
+ particle->pos.y += movement;
+ particle->pos.x -= movement;
+ int col = collision(particle, Vector(-movement, movement));
+ if ((particle->pos.y > SCREEN_HEIGHT + abs_y) || (col >= 0)) {
+ //Create rainsplash
+ if ((particle->pos.y <= SCREEN_HEIGHT + abs_y) && (col >= 1)){
+ bool vertical = (col == 2);
+ int splash_x, splash_y;
+ if (!vertical) { //check if collision happened from above
+ splash_x = int(particle->pos.x);
+ splash_y = int(particle->pos.y) - (int(particle->pos.y) % 32) + 32;
+ Sector::current()->add_object(new RainSplash(Vector(splash_x, splash_y),vertical));
+ }
+ // Uncomment the following to display vertical splashes, too
+ /* else {
+ splash_x = int(particle->pos.x) - (int(particle->pos.x) % 32) + 32;
+ splash_y = int(particle->pos.y);
+ Sector::current()->add_object(new RainSplash(Vector(splash_x, splash_y),vertical));
+ } */
+ }
+ int new_x = systemRandom.rand(int(virtual_width)) + int(abs_x);
+ int new_y = 0;
+ //FIXME: Don't move particles over solid tiles
+ particle->pos.x = new_x;
+ particle->pos.y = new_y;
+ }
+ }
+}
+
+CometParticleSystem::CometParticleSystem()
+{
+ cometimages[0] = new Surface("images/creatures/mr_bomb/exploding-left-0.png");
+ cometimages[1] = new Surface("images/creatures/mr_bomb/exploding-left-0.png");
+
+ virtual_width = SCREEN_WIDTH * 2;
+
+ // create some random comets
+ size_t cometcount = 2;
+ for(size_t i=0; i<cometcount; ++i) {
+ CometParticle* particle = new CometParticle;
+ particle->pos.x = systemRandom.rand(int(virtual_width));
+ particle->pos.y = systemRandom.rand(int(virtual_height));
+ int cometsize = systemRandom.rand(2);
+ particle->texture = cometimages[cometsize];
+ do {
+ particle->speed = (cometsize+1)*30 + systemRandom.randf(3.6);
+ } while(particle->speed < 1);
+ particle->speed *= 10; // gravity
+
+ particles.push_back(particle);
+ }
+}
+
+void
+CometParticleSystem::parse(const lisp::Lisp& reader)
+{
+ reader.get("z-pos", z_pos);
+}
+
+void
+CometParticleSystem::write(lisp::Writer& writer)
+{
+ writer.start_list("particles-comets");
+ writer.write_int("z-pos", z_pos);
+ writer.end_list("particles-comets");
+}
+
+CometParticleSystem::~CometParticleSystem()
+{
+ for(int i=0;i<2;++i)
+ delete cometimages[i];
+}
+
+void CometParticleSystem::update(float elapsed_time)
+{
+ (void) elapsed_time;
+#if 0
+ std::vector<Particle*>::iterator i;
+ for(
+ i = particles.begin(); i != particles.end(); ++i) {
+ CometParticle* particle = (CometParticle*) *i;
+ float movement = particle->speed * elapsed_time;
+ float abs_x = Sector::current()->camera->get_translation().x;
+ float abs_y = Sector::current()->camera->get_translation().y;
+ particle->pos.y += movement;
+ particle->pos.x -= movement;
+ int col = collision(particle, Vector(-movement, movement));
+ if ((particle->pos.y > SCREEN_HEIGHT + abs_y) || (col >= 0)) {
+ if ((particle->pos.y <= SCREEN_HEIGHT + abs_y) && (col >= 1)) {
+ Sector::current()->add_object(new Bomb(particle->pos, LEFT));
+ }
+ int new_x = systemRandom.rand(int(virtual_width)) + int(abs_x);
+ int new_y = 0;
+ //FIXME: Don't move particles over solid tiles
+ particle->pos.x = new_x;
+ particle->pos.y = new_y;
+ }
+ }
+#endif
+}
--- /dev/null
+// $Id$
+//
+// SuperTux
+// Copyright (C) 2006 Matthias Braun <matze@braunis.de>
+//
+// 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_PARTICLESYSTEM_INTERACTIVE_H
+#define SUPERTUX_PARTICLESYSTEM_INTERACTIVE_H
+
+#include <vector>
+
+#include "video/surface.hpp"
+#include "game_object.hpp"
+#include "serializable.hpp"
+#include "sector.hpp"
+#include "math/vector.hpp"
+
+namespace lisp {
+class Lisp;
+}
+
+class DisplayManager;
+
+/**
+ * This is an alternative class for particle systems. It is responsible for storing a
+ * set of particles with each having an x- and y-coordinate the number of the
+ * layer where it should be drawn and a texture.
+ * This version of the particle system class doesn't use virtual screen coordinates,
+ * but Interactive ones. Particle systems which need Interactive levels coordinates, such
+ * as rain, should be implemented here.
+ * Classes that implement a particle system should subclass from this class,
+ * initialize particles in the constructor and move them in the simulate
+ * function.
+ */
+class ParticleSystem_Interactive : public GameObject
+{
+public:
+ ParticleSystem_Interactive();
+ virtual ~ParticleSystem_Interactive();
+
+ virtual void draw(DrawingContext& context);
+
+protected:
+ int z_pos;
+
+ class Particle
+ {
+ public:
+ virtual ~Particle()
+ { }
+
+ Vector pos;
+ Surface* texture;
+ };
+
+ std::vector<Particle*> particles;
+ float virtual_width, virtual_height;
+ int collision(Particle* particle, Vector movement);
+};
+
+class RainParticleSystem : public ParticleSystem_Interactive, public Serializable
+{
+public:
+ RainParticleSystem();
+ virtual ~RainParticleSystem();
+
+ void parse(const lisp::Lisp& lisp);
+ void write(lisp::Writer& writer);
+
+ virtual void update(float elapsed_time);
+
+ std::string type() const
+ { return "RainParticleSystem"; }
+
+private:
+ class RainParticle : public Particle
+ {
+ public:
+ float speed;
+ };
+
+ Surface* rainimages[2];
+};
+
+class CometParticleSystem : public ParticleSystem_Interactive, public Serializable
+{
+public:
+ CometParticleSystem();
+ virtual ~CometParticleSystem();
+
+ void parse(const lisp::Lisp& lisp);
+ void write(lisp::Writer& writer);
+
+ virtual void update(float elapsed_time);
+
+ std::string type() const
+ { return "CometParticleSystem"; }
+
+private:
+ class CometParticle : public Particle
+ {
+ public:
+ float speed;
+ };
+
+ Surface* cometimages[2];
+};
+
+#endif
--- /dev/null
+// $Id$
+//
+// SuperTux Path
+// Copyright (C) 2005 Philipp <balinor@pnxs.de>
+// Copyright (C) 2006 Christoph Sommer <christoph.sommer@2006.expires.deltadevelopment.de>
+// Copyright (C) 2006 Matthias Braun <matze@braunis.de>
+//
+// 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 <config.h>
+
+#include "path.hpp"
+
+#include "lisp/writer.hpp"
+#include "lisp/lisp.hpp"
+#include "lisp/list_iterator.hpp"
+#include "object_factory.hpp"
+#include "log.hpp"
+
+#include <assert.h>
+#include <iostream>
+#include <stdexcept>
+#include <sstream>
+
+Path::Path()
+{
+}
+
+Path::~Path()
+{
+}
+
+void
+Path::read(const lisp::Lisp& reader)
+{
+ lisp::ListIterator iter(&reader);
+
+ mode = CIRCULAR;
+ while(iter.next()) {
+ if(iter.item() == "mode") {
+ std::string mode_string;
+ if(!iter.value()->get(mode_string))
+ throw std::runtime_error("Pathmode not a string");
+
+ if(mode_string == "oneshot")
+ mode = ONE_SHOT;
+ else if(mode_string == "pingpong")
+ mode = PING_PONG;
+ else if(mode_string == "circular")
+ mode = CIRCULAR;
+ else {
+ std::ostringstream msg;
+ msg << "Unknown pathmode '" << mode_string << "' found";
+ throw std::runtime_error(msg.str());
+ }
+ continue;
+ }
+
+ if(iter.item() != "node") {
+ log_warning << "unknown token '" << iter.item() << "' in Path nodes list. Ignored." << std::endl;
+ continue;
+ }
+ const lisp::Lisp* node_lisp = iter.lisp();
+
+ // each new node will inherit all values from the last one
+ Node node;
+ node.time = 1;
+ if( (!node_lisp->get("x", node.position.x) ||
+ !node_lisp->get("y", node.position.y)))
+ throw std::runtime_error("Path node without x and y coordinate specified");
+ node_lisp->get("time", node.time);
+
+ if(node.time <= 0)
+ throw std::runtime_error("Path node with non-positive time");
+
+ nodes.push_back(node);
+ }
+
+ if (nodes.empty())
+ throw std::runtime_error("Path with zero nodes");
+}
+
+void
+Path::write(lisp::Writer& writer)
+{
+ writer.start_list("path");
+
+ switch(mode) {
+ case ONE_SHOT:
+ writer.write_string("mode", "oneshot");
+ break;
+ case PING_PONG:
+ writer.write_string("mode", "pingpong");
+ break;
+ case CIRCULAR:
+ writer.write_string("mode", "circular");
+ break;
+ default:
+ log_warning << "Don't know how to write mode " << (int) mode << " ?!?" << std::endl;
+ break;
+ }
+
+ for (size_t i=0; i < nodes.size(); i++) {
+ const Node& node = nodes[i];
+
+ writer.start_list("node");
+ writer.write_float("x", node.position.x);
+ writer.write_float("y", node.position.y);
+ writer.write_float("time", node.time);
+
+ writer.end_list("node");
+ }
+
+ writer.end_list("path");
+}
+
+Vector
+Path::get_base() const
+{
+ if(nodes.empty())
+ return Vector(0, 0);
+
+ return nodes[0].position;
+}
+
+int
+Path::get_nearest_node_no(Vector reference_point) const
+{
+ int nearest_node_id = -1;
+ float nearest_node_dist = 0;
+ int id = 0;
+ for (std::vector<Node>::const_iterator i = nodes.begin(); i != nodes.end(); i++, id++) {
+ float dist = (i->position - reference_point).norm();
+ if ((nearest_node_id == -1) || (dist < nearest_node_dist)) {
+ nearest_node_id = id;
+ nearest_node_dist = dist;
+ }
+ }
+ return nearest_node_id;
+}
+
+int
+Path::get_farthest_node_no(Vector reference_point) const
+{
+ int farthest_node_id = -1;
+ float farthest_node_dist = 0;
+ int id = 0;
+ for (std::vector<Node>::const_iterator i = nodes.begin(); i != nodes.end(); i++, id++) {
+ float dist = (i->position - reference_point).norm();
+ if ((farthest_node_id == -1) || (dist > farthest_node_dist)) {
+ farthest_node_id = id;
+ farthest_node_dist = dist;
+ }
+ }
+ return farthest_node_id;
+}
+
--- /dev/null
+// $Id$
+//
+// SuperTux Path
+// Copyright (C) 2005 Philipp <balinor@pnxs.de>
+// Copyright (C) 2006 Christoph Sommer <christoph.sommer@2006.expires.deltadevelopment.de>
+// Copyright (C) 2006 Matthias Braun <matze@braunis.de>
+//
+// 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 __PATH_HPP__
+#define __PATH_HPP__
+
+#include <vector>
+#include "math/vector.hpp"
+#include "lisp/lisp.hpp"
+#include "serializable.hpp"
+
+class Path : public Serializable
+{
+public:
+ Path();
+ ~Path();
+
+ void read(const lisp::Lisp& reader);
+ void write(lisp::Writer& writer);
+
+ Vector get_base() const;
+
+ /**
+ * Helper class that stores an individual node of a Path
+ */
+ class Node
+ {
+ public:
+ Vector position; /**< the position of this node */
+ float time; /**< time (in seconds) to get from this node to next node */
+ };
+
+ std::vector<Node> nodes;
+
+ /**
+ * returns Node index nearest to reference_point or -1 if not applicable
+ */
+ int get_nearest_node_no(Vector reference_point) const;
+
+ /**
+ * returns Node index farthest from reference_point or -1 if not applicable
+ */
+ int get_farthest_node_no(Vector reference_point) const;
+
+private:
+ friend class PathWalker;
+
+ enum WalkMode {
+ // moves from first to last path node and stops
+ ONE_SHOT,
+ // moves from first to last node then in reverse order back to first
+ PING_PONG,
+ // moves from last node back to the first node
+ CIRCULAR
+ };
+
+ WalkMode mode;
+};
+
+#endif
--- /dev/null
+// $Id$
+//
+// SuperTux
+// Copyright (C) 2006 Matthias Braun <matze@braunis.de>
+//
+// 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 <config.h>
+
+#include <math.h>
+#include <assert.h>
+#include "path_walker.hpp"
+
+PathWalker::PathWalker(const Path* path, bool running)
+ : path(path), running(running), current_node_nr(0), next_node_nr(0), stop_at_node_nr(running?-1:0), node_time(0),
+ walking_speed(1.0)
+{
+ node_mult = 1 / path->nodes[0].time;
+ next_node_nr = path->nodes.size() > 1 ? 1 : 0;
+}
+
+PathWalker::~PathWalker()
+{
+}
+
+Vector
+PathWalker::advance(float elapsed_time)
+{
+ if (!running) return path->nodes[current_node_nr].position;
+
+ assert(elapsed_time >= 0);
+
+ elapsed_time *= fabsf(walking_speed);
+
+ const Path::Node* current_node = & (path->nodes[current_node_nr]);
+ while(node_time + elapsed_time * node_mult >= 1) {
+ elapsed_time -= (1 - node_time) / node_mult;
+
+ if(walking_speed > 0) {
+ advance_node();
+ } else if(walking_speed < 0) {
+ goback_node();
+ }
+
+ current_node = & (path->nodes[current_node_nr]);
+ node_time = 0;
+ if(walking_speed > 0) {
+ node_mult = 1 / current_node->time;
+ } else {
+ node_mult = 1 / path->nodes[next_node_nr].time;
+ }
+ }
+
+ const Path::Node* next_node = & (path->nodes[next_node_nr]);
+ node_time += elapsed_time * node_mult;
+
+ Vector new_pos = current_node->position +
+ (next_node->position - current_node->position) * node_time;
+
+ return new_pos;
+}
+
+void
+PathWalker::goto_node(int node_no)
+{
+ if (node_no == stop_at_node_nr) return;
+ running = true;
+ stop_at_node_nr = node_no;
+}
+
+void
+PathWalker::start_moving()
+{
+ running = true;
+ stop_at_node_nr = -1;
+}
+
+void
+PathWalker::stop_moving()
+{
+ stop_at_node_nr = next_node_nr;
+}
+
+
+void
+PathWalker::advance_node()
+{
+ current_node_nr = next_node_nr;
+ if (static_cast<int>(current_node_nr) == stop_at_node_nr) running = false;
+
+ if(next_node_nr + 1 < path->nodes.size()) {
+ next_node_nr++;
+ return;
+ }
+
+ switch(path->mode) {
+ case Path::ONE_SHOT:
+ next_node_nr = path->nodes.size() - 1;
+ walking_speed = 0;
+ return;
+
+ case Path::PING_PONG:
+ walking_speed = -walking_speed;
+ next_node_nr = path->nodes.size() > 1 ? path->nodes.size() - 2 : 0;
+ return;
+
+ case Path::CIRCULAR:
+ next_node_nr = 0;
+ return;
+ }
+
+ // we shouldn't get here
+ assert(false);
+ next_node_nr = path->nodes.size() - 1;
+ walking_speed = 0;
+}
+
+void
+PathWalker::goback_node()
+{
+ current_node_nr = next_node_nr;
+
+ if(next_node_nr > 0) {
+ next_node_nr--;
+ return;
+ }
+
+ switch(path->mode) {
+ case Path::PING_PONG:
+ walking_speed = -walking_speed;
+ next_node_nr = path->nodes.size() > 1 ? 1 : 0;
+ return;
+ default:
+ break;
+ }
+
+ assert(false);
+ next_node_nr = 0;
+ walking_speed = 0;
+}
--- /dev/null
+// $Id$
+//
+// SuperTux
+// Copyright (C) 2006 Matthias Braun <matze@braunis.de>
+//
+// 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 __PATH_WALKER_HPP__
+#define __PATH_WALKER_HPP__
+
+#include "path.hpp"
+#include "math/vector.hpp"
+#include "game_object.hpp"
+#include "lisp/lisp.hpp"
+#include "serializable.hpp"
+
+/**
+ * A walker that travels along a path
+ */
+class PathWalker
+{
+public:
+ PathWalker(const Path* path, bool running = true);
+ virtual ~PathWalker();
+
+ /**
+ * advanves the path walker on the path and returns the position delta
+ * to the last position
+ */
+ virtual Vector advance(float elapsed_time);
+
+ /** advance until at given node, then stop */
+ void goto_node(int node_no);
+
+ /** start advancing automatically */
+ void start_moving();
+
+ /** stop advancing automatically */
+ void stop_moving();
+
+ /** returns true if PathWalker is currently moving */
+ bool is_moving() {
+ return running;
+ }
+
+ const Path* path;
+
+private:
+ void advance_node();
+ void goback_node();
+
+ /**
+ * set to false to immediately stop advancing
+ */
+ bool running;
+
+ size_t current_node_nr;
+ size_t next_node_nr;
+
+ /**
+ * stop advancing automatically when this node is reached
+ */
+ int stop_at_node_nr;
+
+ /**
+ * the position between the current node and the next node as fraction
+ * between 0 and 1
+ */
+ float node_time;
+ float node_mult;
+
+ float walking_speed;
+};
+
+#endif
--- /dev/null
+// $Id$
+//
+// SuperTux
+// Copyright (C) 2006 Matthias Braun <matze@braunis.de>
+//
+// 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 <config.h>
+
+#include "platform.hpp"
+
+#include <stdexcept>
+#include "log.hpp"
+#include "video/drawing_context.hpp"
+#include "resources.hpp"
+#include "player.hpp"
+#include "path.hpp"
+#include "path_walker.hpp"
+#include "sprite/sprite.hpp"
+#include "lisp/lisp.hpp"
+#include "object_factory.hpp"
+#include "scripting/platform.hpp"
+#include "scripting/squirrel_util.hpp"
+#include "sector.hpp"
+
+Platform::Platform(const lisp::Lisp& reader)
+ : MovingSprite(reader, Vector(0,0), LAYER_OBJECTS, COLGROUP_STATIC),
+ speed(Vector(0,0)),
+ automatic(false), player_contact(false), last_player_contact(false)
+{
+ bool running = true;
+ reader.get("name", name);
+ reader.get("running", running);
+ if ((name == "") && (!running)) automatic=true;
+ const lisp::Lisp* pathLisp = reader.get_lisp("path");
+ if(pathLisp == NULL)
+ throw std::runtime_error("No path specified for platform");
+ path.reset(new Path());
+ path->read(*pathLisp);
+ walker.reset(new PathWalker(path.get(), running));
+ bbox.set_pos(path->get_base());
+}
+
+Platform::Platform(const Platform& other)
+ : MovingSprite(other), ScriptInterface(other),
+ speed(other.speed),
+ automatic(other.automatic), player_contact(false), last_player_contact(false)
+{
+ name = other.name;
+ path.reset(new Path(*other.path));
+ walker.reset(new PathWalker(*other.walker));
+ walker->path = &*path;
+}
+
+HitResponse
+Platform::collision(GameObject& other, const CollisionHit& )
+{
+ if (dynamic_cast<Player*>(&other)) player_contact = true;
+ return FORCE_MOVE;
+}
+
+void
+Platform::update(float elapsed_time)
+{
+ // check if Platform should automatically pick a destination
+ if (automatic) {
+
+ if (!player_contact && !walker->is_moving()) {
+ // Player doesn't touch platform and Platform is not moving
+
+ // Travel to node nearest to nearest player
+ // FIXME: does not really use nearest player
+ Player* player = 0;
+ std::vector<Player*> players = Sector::current()->get_players();
+ for (std::vector<Player*>::iterator playerIter = players.begin(); playerIter != players.end(); ++playerIter) {
+ player = *playerIter;
+ }
+ if (player) {
+ int nearest_node_id = path->get_nearest_node_no(player->get_bbox().p2);
+ if (nearest_node_id != -1) {
+ goto_node(nearest_node_id);
+ }
+ }
+ }
+
+ if (player_contact && !last_player_contact && !walker->is_moving()) {
+ // Player touched platform, didn't touch last frame and Platform is not moving
+
+ // Travel to node farthest from current position
+ int farthest_node_id = path->get_farthest_node_no(get_pos());
+ if (farthest_node_id != -1) {
+ goto_node(farthest_node_id);
+ }
+ }
+
+ // Clear player_contact flag set by collision() method
+ last_player_contact = player_contact;
+ player_contact = false;
+ }
+
+ movement = walker->advance(elapsed_time) - get_pos();
+ speed = movement / elapsed_time;
+}
+
+void
+Platform::goto_node(int node_no)
+{
+ walker->goto_node(node_no);
+}
+
+void
+Platform::start_moving()
+{
+ walker->start_moving();
+}
+
+void
+Platform::stop_moving()
+{
+ walker->stop_moving();
+}
+
+void
+Platform::expose(HSQUIRRELVM vm, SQInteger table_idx)
+{
+ if (name.empty()) return;
+ Scripting::Platform* interface = new Scripting::Platform(this);
+ expose_object(vm, table_idx, interface, name, true);
+}
+
+void
+Platform::unexpose(HSQUIRRELVM vm, SQInteger table_idx)
+{
+ if (name.empty()) return;
+ Scripting::unexpose_object(vm, table_idx, name);
+}
+
+IMPLEMENT_FACTORY(Platform, "platform");
--- /dev/null
+// $Id$
+//
+// SuperTux
+// Copyright (C) 2006 Matthias Braun <matze@braunis.de>
+//
+// 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 __PLATFORM_H__
+#define __PLATFORM_H__
+
+#include <memory>
+#include <string>
+#include "object/moving_sprite.hpp"
+#include "object/path.hpp"
+#include "object/path_walker.hpp"
+#include "script_interface.hpp"
+
+/**
+ * This class is the base class for platforms that tux can stand on
+ */
+class Platform : public MovingSprite, public ScriptInterface
+{
+public:
+ Platform(const lisp::Lisp& reader);
+ Platform(const Platform& platform);
+ virtual Platform* clone() const { return new Platform(*this); }
+
+ virtual HitResponse collision(GameObject& other, const CollisionHit& hit);
+ virtual void update(float elapsed_time);
+
+ const Vector& get_speed() const
+ {
+ return speed;
+ }
+
+ /**
+ * @name Scriptable Methods
+ * @{
+ */
+
+ /** Move platform until at given node, then stop */
+ void goto_node(int node_no);
+
+ /** Start moving platform */
+ void start_moving();
+
+ /** Stop platform at next node */
+ void stop_moving();
+
+ /**
+ * @}
+ */
+
+ virtual void expose(HSQUIRRELVM vm, SQInteger table_idx);
+ virtual void unexpose(HSQUIRRELVM vm, SQInteger table_idx);
+
+ Path& get_path() {
+ return *path.get();
+ }
+
+private:
+ std::auto_ptr<Path> path;
+ std::auto_ptr<PathWalker> walker;
+
+ Vector speed;
+
+ bool automatic; /**< true if Platform will automatically pick a destination based on collisions and current Player position */
+ bool player_contact; /**< true if a Player touched the Platform during the last round of collision detections */
+ bool last_player_contact; /**< true if a Player touched the Platform during the round before the last round of collision detections */
+
+};
+
+#endif
--- /dev/null
+// $Id$
+//
+// SuperTux
+// Copyright (C) 2006 Matthias Braun <matze@braunis.de>
+//
+// 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 <config.h>
+
+#include <typeinfo>
+#include <cmath>
+#include <iostream>
+#include <cassert>
+
+#include "gettext.hpp"
+#include "sprite/sprite_manager.hpp"
+#include "audio/sound_manager.hpp"
+#include "player.hpp"
+#include "tile.hpp"
+#include "sprite/sprite.hpp"
+#include "sector.hpp"
+#include "resources.hpp"
+#include "statistics.hpp"
+#include "game_session.hpp"
+#include "object/tilemap.hpp"
+#include "object/camera.hpp"
+#include "object/particles.hpp"
+#include "object/portable.hpp"
+#include "object/bullet.hpp"
+#include "trigger/trigger_base.hpp"
+#include "control/joystickkeyboardcontroller.hpp"
+#include "scripting/squirrel_util.hpp"
+#include "main.hpp"
+#include "platform.hpp"
+#include "badguy/badguy.hpp"
+#include "player_status.hpp"
+#include "log.hpp"
+#include "falling_coin.hpp"
+#include "random_generator.hpp"
+#include "object/sprite_particle.hpp"
+#include "trigger/climbable.hpp"
+
+//#define SWIMMING
+
+static const int TILES_FOR_BUTTJUMP = 3;
+static const float SHOOTING_TIME = .150f;
+/// time before idle animation starts
+static const float IDLE_TIME = 2.5f;
+
+/** acceleration in horizontal direction when walking
+ * (all acceleratiosn are in pixel/s^2) */
+static const float WALK_ACCELERATION_X = 300;
+/** acceleration in horizontal direction when running */
+static const float RUN_ACCELERATION_X = 400;
+/** acceleration when skidding */
+static const float SKID_XM = 200;
+/** time of skidding in seconds */
+static const float SKID_TIME = .3f;
+/** maximum walk velocity (pixel/s) */
+static const float MAX_WALK_XM = 230;
+/** maximum run velcoity (pixel/s) */
+static const float MAX_RUN_XM = 320;
+/** maximum horizontal climb velocity */
+static const float MAX_CLIMB_XM = 48;
+/** maximum vertical climb velocity */
+static const float MAX_CLIMB_YM = 128;
+/** instant velocity when tux starts to walk */
+static const float WALK_SPEED = 100;
+
+/** time of the kick (kicking mriceblock) animation */
+static const float KICK_TIME = .3f;
+/** time of tux cheering (currently unused) */
+static const float CHEER_TIME = 1.0f;
+
+/** if Tux cannot unduck for this long, he will get hurt */
+static const float UNDUCK_HURT_TIME = 0.25f;
+
+// growing animation
+Surface* growingtux_left[GROWING_FRAMES];
+Surface* growingtux_right[GROWING_FRAMES];
+
+Surface* tux_life = 0;
+
+TuxBodyParts* small_tux = 0;
+TuxBodyParts* big_tux = 0;
+TuxBodyParts* fire_tux = 0;
+TuxBodyParts* ice_tux = 0;
+
+namespace{
+ bool no_water = true;
+}
+void
+TuxBodyParts::set_action(std::string action, int loops)
+{
+ if(head != NULL)
+ head->set_action(action, loops);
+ if(body != NULL)
+ body->set_action(action, loops);
+ if(arms != NULL)
+ arms->set_action(action, loops);
+ if(feet != NULL)
+ feet->set_action(action, loops);
+}
+
+void
+TuxBodyParts::draw(DrawingContext& context, const Vector& pos, int layer, Portable* grabbed_object)
+{
+ if(head != NULL)
+ head->draw(context, pos, layer-2);
+ if(body != NULL)
+ body->draw(context, pos, layer-4);
+ if(arms != NULL)
+ arms->draw(context, pos, layer-1 + (grabbed_object?10:0));
+ if(feet != NULL)
+ feet->draw(context, pos, layer-3);
+}
+
+Player::Player(PlayerStatus* _player_status, const std::string& name)
+ : player_status(_player_status), grabbed_object(NULL), ghost_mode(false), climbing(0)
+{
+ this->name = name;
+ controller = main_controller;
+ smalltux_gameover = sprite_manager->create("images/creatures/tux_small/smalltux-gameover.sprite");
+ smalltux_star = sprite_manager->create("images/creatures/tux_small/smalltux-star.sprite");
+ bigtux_star = sprite_manager->create("images/creatures/tux_big/bigtux-star.sprite");
+ airarrow.reset(new Surface("images/engine/hud/airarrow.png"));
+
+ sound_manager->preload("sounds/bigjump.wav");
+ sound_manager->preload("sounds/jump.wav");
+ sound_manager->preload("sounds/hurt.wav");
+ sound_manager->preload("sounds/skid.wav");
+ sound_manager->preload("sounds/flip.wav");
+ sound_manager->preload("sounds/invincible.wav");
+ sound_manager->preload("sounds/splash.ogg");
+
+ init();
+}
+
+Player::~Player()
+{
+ if (climbing) stop_climbing(*climbing);
+ delete smalltux_gameover;
+ delete smalltux_star;
+ delete bigtux_star;
+}
+
+void
+Player::init()
+{
+ if(is_big())
+ set_size(31.8f, 62.8f);
+ else
+ set_size(31.8f, 30.8f);
+
+ dir = RIGHT;
+ old_dir = dir;
+ duck = false;
+ dead = false;
+
+ dying = false;
+ peeking = AUTO;
+ last_ground_y = 0;
+ fall_mode = ON_GROUND;
+ jumping = false;
+ can_jump = true;
+ butt_jump = false;
+ deactivated = false;
+ backflipping = false;
+ backflip_direction = 0;
+ visible = true;
+ swimming = false;
+ speedlimit = 0; //no special limit
+
+ on_ground_flag = false;
+ grabbed_object = NULL;
+
+ climbing = 0;
+
+ physic.reset();
+}
+
+void
+Player::expose(HSQUIRRELVM vm, SQInteger table_idx)
+{
+ if (name.empty())
+ return;
+
+ Scripting::expose_object(vm, table_idx, dynamic_cast<Scripting::Player *>(this), name, false);
+}
+
+void
+Player::unexpose(HSQUIRRELVM vm, SQInteger table_idx)
+{
+ if (name.empty())
+ return;
+
+ Scripting::unexpose_object(vm, table_idx, name);
+}
+
+float
+Player::get_speedlimit()
+{
+ return speedlimit;
+}
+
+void
+Player::set_speedlimit(float newlimit)
+{
+ speedlimit=newlimit;
+}
+
+void
+Player::set_controller(Controller* controller)
+{
+ this->controller = controller;
+}
+
+bool
+Player::adjust_height(float new_height)
+{
+ Rect bbox2 = bbox;
+ bbox2.move(Vector(0, bbox.get_height() - new_height));
+ bbox2.set_height(new_height);
+
+ if(new_height > bbox.get_height()) {
+ Rect additional_space = bbox2;
+ additional_space.set_height(new_height - bbox.get_height());
+ if(!Sector::current()->is_free_of_statics(additional_space, this, true))
+ return false;
+ }
+
+ // adjust bbox accordingly
+ // note that we use members of moving_object for this, so we can run this during CD, too
+ set_pos(bbox2.p1);
+ set_size(bbox2.get_width(), bbox2.get_height());
+ return true;
+}
+
+void
+Player::trigger_sequence(std::string sequence_name)
+{
+ if (climbing) stop_climbing(*climbing);
+ GameSession::current()->start_sequence(sequence_name);
+}
+
+void
+Player::update(float elapsed_time)
+{
+ if( no_water ){
+ swimming = false;
+ }
+ no_water = true;
+
+ if(dying && dying_timer.check()) {
+ dead = true;
+ return;
+ }
+
+ if(!dying && !deactivated)
+ handle_input();
+
+ // handle_input() calls apply_friction() when Tux is not walking, so we'll have to do this ourselves
+ if (deactivated)
+ apply_friction();
+
+ // extend/shrink tux collision rectangle so that we fall through/walk over 1
+ // tile holes
+ if(fabsf(physic.get_velocity_x()) > MAX_WALK_XM) {
+ set_width(34);
+ } else {
+ set_width(31.8f);
+ }
+
+ // on downward slopes, adjust vertical velocity so tux walks smoothly down
+ if (on_ground()) {
+ if(floor_normal.y != 0) {
+ if ((floor_normal.x * physic.get_velocity_x()) >= 0) {
+ physic.set_velocity_y(250);
+ }
+ }
+ }
+
+ // handle backflipping
+ if (backflipping) {
+ //prevent player from changing direction when backflipping
+ dir = (backflip_direction == 1) ? LEFT : RIGHT;
+ if (backflip_timer.started()) physic.set_velocity_x(100 * backflip_direction);
+ }
+
+ // set fall mode...
+ if(on_ground()) {
+ fall_mode = ON_GROUND;
+ last_ground_y = get_pos().y;
+ } else {
+ if(get_pos().y > last_ground_y)
+ fall_mode = FALLING;
+ else if(fall_mode == ON_GROUND)
+ fall_mode = JUMPING;
+ }
+
+ // check if we landed
+ if(on_ground()) {
+ jumping = false;
+ if (backflipping && (!backflip_timer.started())) {
+ backflipping = false;
+ backflip_direction = 0;
+
+ // if controls are currently deactivated, we take care of standing up ourselves
+ if (deactivated)
+ do_standup();
+ }
+ }
+
+ // calculate movement for this frame
+ movement = physic.get_movement(elapsed_time);
+
+ if(grabbed_object != NULL && !dying) {
+ Vector pos = get_pos() +
+ Vector(dir == LEFT ? -16 : 16, get_bbox().get_height()*0.66666 - 32);
+ grabbed_object->grab(*this, pos, dir);
+ }
+
+ if(grabbed_object != NULL && dying){
+ grabbed_object->ungrab(*this, dir);
+ grabbed_object = NULL;
+ }
+
+ on_ground_flag = false;
+
+ // when invincible, spawn particles
+ if (invincible_timer.started() && !dying)
+ {
+ if (systemRandom.rand(0, 2) == 0) {
+ float px = systemRandom.randf(bbox.p1.x+0, bbox.p2.x-0);
+ float py = systemRandom.randf(bbox.p1.y+0, bbox.p2.y-0);
+ Vector ppos = Vector(px, py);
+ Vector pspeed = Vector(0, 0);
+ Vector paccel = Vector(0, 0);
+ // draw bright sparkle when there is lots of time left, dark sparkle when invincibility is about to end
+ if (invincible_timer.get_timeleft() > TUX_INVINCIBLE_TIME_WARNING) {
+ // make every other a longer sparkle to make trail a bit fuzzy
+ if (size_t(game_time*20)%2) {
+ Sector::current()->add_object(new SpriteParticle("images/objects/particles/sparkle.sprite", "small", ppos, ANCHOR_MIDDLE, pspeed, paccel, LAYER_OBJECTS+1+5));
+ } else {
+ Sector::current()->add_object(new SpriteParticle("images/objects/particles/sparkle.sprite", "medium", ppos, ANCHOR_MIDDLE, pspeed, paccel, LAYER_OBJECTS+1+5));
+ }
+ } else {
+ Sector::current()->add_object(new SpriteParticle("images/objects/particles/sparkle.sprite", "dark", ppos, ANCHOR_MIDDLE, pspeed, paccel, LAYER_OBJECTS+1+5));
+ }
+ }
+ }
+
+}
+
+bool
+Player::on_ground()
+{
+ return on_ground_flag;
+}
+
+bool
+Player::is_big()
+{
+ if(player_status->bonus == NO_BONUS)
+ return false;
+
+ return true;
+}
+
+void
+Player::apply_friction()
+{
+ if ((on_ground()) && (fabs(physic.get_velocity_x()) < WALK_SPEED)) {
+ physic.set_velocity_x(0);
+ physic.set_acceleration_x(0);
+ } else if(physic.get_velocity_x() < 0) {
+ physic.set_acceleration_x(WALK_ACCELERATION_X * 1.5);
+ } else if(physic.get_velocity_x() > 0) {
+ physic.set_acceleration_x(WALK_ACCELERATION_X * -1.5);
+ }
+}
+
+void
+Player::handle_horizontal_input()
+{
+ float vx = physic.get_velocity_x();
+ float vy = physic.get_velocity_y();
+ float ax = physic.get_acceleration_x();
+ float ay = physic.get_acceleration_y();
+
+ float dirsign = 0;
+ if(!duck || physic.get_velocity_y() != 0) {
+ if(controller->hold(Controller::LEFT) && !controller->hold(Controller::RIGHT)) {
+ old_dir = dir;
+ dir = LEFT;
+ dirsign = -1;
+ } else if(!controller->hold(Controller::LEFT)
+ && controller->hold(Controller::RIGHT)) {
+ old_dir = dir;
+ dir = RIGHT;
+ dirsign = 1;
+ }
+ }
+
+ // do not run if action key is pressed or we're holding something
+ // so tux can only walk while shooting
+ if ( controller->hold(Controller::ACTION) || grabbed_object ) {
+ ax = dirsign * WALK_ACCELERATION_X;
+ // limit speed
+ if(vx >= MAX_WALK_XM && dirsign > 0) {
+ vx = MAX_WALK_XM;
+ ax = 0;
+ } else if(vx <= -MAX_WALK_XM && dirsign < 0) {
+ vx = -MAX_WALK_XM;
+ ax = 0;
+ }
+ } else {
+ if( vx * dirsign < MAX_WALK_XM ) {
+ ax = dirsign * WALK_ACCELERATION_X;
+ } else {
+ ax = dirsign * RUN_ACCELERATION_X;
+ }
+ // limit speed
+ if(vx >= MAX_RUN_XM && dirsign > 0) {
+ vx = MAX_RUN_XM;
+ ax = 0;
+ } else if(vx <= -MAX_RUN_XM && dirsign < 0) {
+ vx = -MAX_RUN_XM;
+ ax = 0;
+ }
+ }
+
+ // we can reach WALK_SPEED without any acceleration
+ if(dirsign != 0 && fabs(vx) < WALK_SPEED) {
+ vx = dirsign * WALK_SPEED;
+ }
+
+ //Check speedlimit.
+ if( speedlimit > 0 && vx * dirsign >= speedlimit ) {
+ vx = dirsign * speedlimit;
+ ax = 0;
+ }
+
+ // changing directions?
+ if(on_ground() && ((vx < 0 && dirsign >0) || (vx>0 && dirsign<0))) {
+ // let's skid!
+ if(fabs(vx)>SKID_XM && !skidding_timer.started()) {
+ skidding_timer.start(SKID_TIME);
+ sound_manager->play("sounds/skid.wav");
+ // dust some particles
+ Sector::current()->add_object(
+ new Particles(
+ Vector(dir == RIGHT ? get_bbox().p2.x : get_bbox().p1.x, get_bbox().p2.y),
+ dir == RIGHT ? 270+20 : 90-40, dir == RIGHT ? 270+40 : 90-20,
+ Vector(280, -260), Vector(0, 300), 3, Color(.4f, .4f, .4f), 3, .8f,
+ LAYER_OBJECTS+1));
+
+ ax *= 2.5;
+ } else {
+ ax *= 2;
+ }
+ }
+
+ physic.set_velocity(vx, vy);
+ physic.set_acceleration(ax, ay);
+
+ // we get slower when not pressing any keys
+ if(dirsign == 0) {
+ apply_friction();
+ }
+
+}
+
+void
+Player::do_cheer()
+{
+ do_duck();
+ do_backflip();
+ do_standup();
+}
+
+void
+Player::do_duck() {
+ if (duck)
+ return;
+ if (!is_big())
+ return;
+
+ if (physic.get_velocity_y() != 0)
+ return;
+ if (!on_ground())
+ return;
+
+ if (adjust_height(31.8f)) {
+ duck = true;
+ unduck_hurt_timer.stop();
+ } else {
+ // FIXME: what now?
+ }
+}
+
+void
+Player::do_standup() {
+ if (!duck)
+ return;
+ if (!is_big())
+ return;
+ if (backflipping)
+ return;
+
+ if (adjust_height(63.8f)) {
+ duck = false;
+ unduck_hurt_timer.stop();
+ } else {
+ // if timer is not already running, start it.
+ if (unduck_hurt_timer.get_period() == 0) {
+ unduck_hurt_timer.start(UNDUCK_HURT_TIME);
+ }
+ else if (unduck_hurt_timer.check()) {
+ kill(false);
+ }
+ }
+
+}
+
+void
+Player::do_backflip() {
+ if (!duck)
+ return;
+ if (!on_ground())
+ return;
+
+ backflip_direction = (dir == LEFT)?(+1):(-1);
+ backflipping = true;
+ do_jump(-580);
+ sound_manager->play("sounds/flip.wav");
+ backflip_timer.start(0.15f);
+}
+
+void
+Player::do_jump(float yspeed) {
+ if (!on_ground())
+ return;
+
+ physic.set_velocity_y(yspeed);
+ //bbox.move(Vector(0, -1));
+ jumping = true;
+ on_ground_flag = false;
+ can_jump = false;
+
+ // play sound
+ if (is_big()) {
+ sound_manager->play("sounds/bigjump.wav");
+ } else {
+ sound_manager->play("sounds/jump.wav");
+ }
+}
+
+void
+Player::handle_vertical_input()
+{
+ // Press jump key
+ if(controller->pressed(Controller::JUMP) && (can_jump)) {
+ if (duck) {
+ // when running, only jump a little bit; else do a backflip
+ if ((physic.get_velocity_x() != 0) || (controller->hold(Controller::LEFT)) || (controller->hold(Controller::RIGHT))) do_jump(-300); else do_backflip();
+ } else {
+ // jump a bit higher if we are running; else do a normal jump
+ if (fabs(physic.get_velocity_x()) > MAX_WALK_XM) do_jump(-580); else do_jump(-520);
+ }
+ }
+ // Let go of jump key
+ else if(!controller->hold(Controller::JUMP)) {
+ if (!backflipping && jumping && physic.get_velocity_y() < 0) {
+ jumping = false;
+ physic.set_velocity_y(0);
+ }
+ }
+
+ /* In case the player has pressed Down while in a certain range of air,
+ enable butt jump action */
+ if (controller->hold(Controller::DOWN) && !butt_jump && !duck && is_big() && jumping) {
+ butt_jump = true;
+ }
+
+ /* When Down is not held anymore, disable butt jump */
+ if(butt_jump && !controller->hold(Controller::DOWN))
+ butt_jump = false;
+
+ // swimming
+ physic.set_acceleration_y(0);
+#ifdef SWIMMING
+ if (swimming) {
+ if (controller->hold(Controller::UP) || controller->hold(Controller::JUMP))
+ physic.set_acceleration_y(-2000);
+ physic.set_velocity_y(physic.get_velocity_y() * 0.94);
+ }
+#endif
+}
+
+void
+Player::handle_input()
+{
+ if (ghost_mode) {
+ handle_input_ghost();
+ return;
+ }
+ if (climbing) {
+ handle_input_climbing();
+ return;
+ }
+
+ /* Peeking */
+ if( controller->released( Controller::PEEK_LEFT ) ) {
+ peeking = AUTO;
+ }
+ if( controller->released( Controller::PEEK_RIGHT ) ) {
+ peeking = AUTO;
+ }
+ if( controller->released( Controller::UP ) ) {
+ peeking = AUTO;
+ }
+ if( controller->released( Controller::DOWN ) ) {
+ peeking = AUTO;
+ }
+ if( controller->pressed( Controller::PEEK_LEFT ) ) {
+ peeking = LEFT;
+ }
+ if( controller->pressed( Controller::PEEK_RIGHT ) ) {
+ peeking = RIGHT;
+ }
+ if( controller->pressed( Controller::UP ) ) {
+ peeking = UP;
+ }
+ if( controller->pressed( Controller::DOWN ) ) {
+ peeking = DOWN;
+ }
+
+ /* Handle horizontal movement: */
+ if (!backflipping) handle_horizontal_input();
+
+ /* Jump/jumping? */
+ if (on_ground() && !controller->hold(Controller::JUMP))
+ can_jump = true;
+
+ /* Handle vertical movement: */
+ handle_vertical_input();
+
+ /* Shoot! */
+ if (controller->pressed(Controller::ACTION) && (player_status->bonus == FIRE_BONUS || player_status->bonus == ICE_BONUS)) {
+ if(Sector::current()->add_bullet(
+ get_pos() + ((dir == LEFT)? Vector(0, bbox.get_height()/2)
+ : Vector(32, bbox.get_height()/2)),
+ physic.get_velocity_x(), dir))
+ shooting_timer.start(SHOOTING_TIME);
+ }
+
+ /* Duck or Standup! */
+ if (controller->hold(Controller::DOWN)) {
+ do_duck();
+ } else {
+ do_standup();
+ }
+
+ /* grabbing */
+ try_grab();
+
+ if(!controller->hold(Controller::ACTION) && grabbed_object) {
+ // move the grabbed object a bit away from tux
+ Vector pos = get_pos() +
+ Vector(dir == LEFT ? -bbox.get_width()-1 : bbox.get_width()+1,
+ bbox.get_height()*0.66666 - 32);
+ Rect dest(pos, pos + Vector(32, 32));
+ if(Sector::current()->is_free_of_movingstatics(dest)) {
+ MovingObject* moving_object = dynamic_cast<MovingObject*> (grabbed_object);
+ if(moving_object) {
+ moving_object->set_pos(pos);
+ } else {
+ log_debug << "Non MovingObject grabbed?!?" << std::endl;
+ }
+ if(controller->hold(Controller::UP)) {
+ grabbed_object->ungrab(*this, UP);
+ } else {
+ grabbed_object->ungrab(*this, dir);
+ }
+ grabbed_object = NULL;
+ }
+ }
+}
+
+void
+Player::try_grab()
+{
+ if(controller->hold(Controller::ACTION) && !grabbed_object
+ && !duck) {
+ Sector* sector = Sector::current();
+ Vector pos;
+ if(dir == LEFT) {
+ pos = Vector(bbox.get_left() - 5, bbox.get_bottom() - 16);
+ } else {
+ pos = Vector(bbox.get_right() + 5, bbox.get_bottom() - 16);
+ }
+
+ for(Sector::Portables::iterator i = sector->portables.begin();
+ i != sector->portables.end(); ++i) {
+ Portable* portable = *i;
+ if(!portable->is_portable())
+ continue;
+
+ // make sure the Portable is a MovingObject
+ MovingObject* moving_object = dynamic_cast<MovingObject*> (portable);
+ assert(moving_object);
+ if(moving_object == NULL)
+ continue;
+
+ // make sure the Portable isn't currently non-solid
+ if(moving_object->get_group() == COLGROUP_DISABLED) continue;
+
+ // check if we are within reach
+ if(moving_object->get_bbox().contains(pos)) {
+ if (climbing) stop_climbing(*climbing);
+ grabbed_object = portable;
+ grabbed_object->grab(*this, get_pos(), dir);
+ break;
+ }
+ }
+ }
+}
+
+void
+Player::handle_input_ghost()
+{
+ float vx = 0;
+ float vy = 0;
+ if (controller->hold(Controller::LEFT)) {
+ dir = LEFT;
+ vx -= MAX_RUN_XM * 2;
+ }
+ if (controller->hold(Controller::RIGHT)) {
+ dir = RIGHT;
+ vx += MAX_RUN_XM * 2;
+ }
+ if ((controller->hold(Controller::UP)) || (controller->hold(Controller::JUMP))) {
+ vy -= MAX_RUN_XM * 2;
+ }
+ if (controller->hold(Controller::DOWN)) {
+ vy += MAX_RUN_XM * 2;
+ }
+ if (controller->hold(Controller::ACTION)) {
+ set_ghost_mode(false);
+ }
+ physic.set_velocity(vx, vy);
+ physic.set_acceleration(0, 0);
+}
+
+void
+Player::add_coins(int count)
+{
+ player_status->add_coins(count);
+}
+
+int
+Player::get_coins()
+{
+ return player_status->coins;
+}
+
+bool
+Player::add_bonus(const std::string& bonustype)
+{
+ BonusType type = NO_BONUS;
+
+ if(bonustype == "grow") {
+ type = GROWUP_BONUS;
+ } else if(bonustype == "fireflower") {
+ type = FIRE_BONUS;
+ } else if(bonustype == "iceflower") {
+ type = ICE_BONUS;
+ } else if(bonustype == "none") {
+ type = NO_BONUS;
+ } else {
+ std::ostringstream msg;
+ msg << "Unknown bonus type " << bonustype;
+ throw std::runtime_error(msg.str());
+ }
+
+ return add_bonus(type);
+}
+
+bool
+Player::add_bonus(BonusType type, bool animate)
+{
+ // always ignore NO_BONUS
+ if (type == NO_BONUS) {
+ return true;
+ }
+
+ // ignore GROWUP_BONUS if we're already big
+ if (type == GROWUP_BONUS) {
+ if (player_status->bonus == GROWUP_BONUS)
+ return true;
+ if (player_status->bonus == FIRE_BONUS)
+ return true;
+ if (player_status->bonus == ICE_BONUS)
+ return true;
+ }
+
+ return set_bonus(type, animate);
+}
+
+bool
+Player::set_bonus(BonusType type, bool animate)
+{
+ if(player_status->bonus == NO_BONUS) {
+ if (!adjust_height(62.8f)) {
+ printf("can't adjust\n");
+ return false;
+ }
+ if(animate)
+ growing_timer.start(GROWING_TIME);
+ if (climbing) stop_climbing(*climbing);
+ }
+
+ if ((type == NO_BONUS) || (type == GROWUP_BONUS)) {
+ if ((player_status->bonus == FIRE_BONUS) && (animate)) {
+ // visually lose helmet
+ Vector ppos = Vector((bbox.p1.x + bbox.p2.x) / 2, bbox.p1.y);
+ Vector pspeed = Vector(((dir==LEFT) ? +100 : -100), -300);
+ Vector paccel = Vector(0, 1000);
+ std::string action = (dir==LEFT)?"left":"right";
+ Sector::current()->add_object(new SpriteParticle("images/objects/particles/firetux-helmet.sprite", action, ppos, ANCHOR_TOP, pspeed, paccel, LAYER_OBJECTS-1));
+ if (climbing) stop_climbing(*climbing);
+ }
+ if ((player_status->bonus == ICE_BONUS) && (animate)) {
+ // visually lose cap
+ Vector ppos = Vector((bbox.p1.x + bbox.p2.x) / 2, bbox.p1.y);
+ Vector pspeed = Vector(((dir==LEFT) ? +100 : -100), -300);
+ Vector paccel = Vector(0, 1000);
+ std::string action = (dir==LEFT)?"left":"right";
+ Sector::current()->add_object(new SpriteParticle("images/objects/particles/icetux-cap.sprite", action, ppos, ANCHOR_TOP, pspeed, paccel, LAYER_OBJECTS-1));
+ if (climbing) stop_climbing(*climbing);
+ }
+ player_status->max_fire_bullets = 0;
+ player_status->max_ice_bullets = 0;
+ }
+ if (type == FIRE_BONUS) player_status->max_fire_bullets++;
+ if (type == ICE_BONUS) player_status->max_ice_bullets++;
+
+ player_status->bonus = type;
+ return true;
+}
+
+void
+Player::set_visible(bool visible)
+{
+ this->visible = visible;
+ if( visible )
+ set_group(COLGROUP_MOVING);
+ else
+ set_group(COLGROUP_DISABLED);
+}
+
+bool
+Player::get_visible()
+{
+ return visible;
+}
+
+void
+Player::kick()
+{
+ kick_timer.start(KICK_TIME);
+}
+
+void
+Player::draw(DrawingContext& context)
+{
+ if(!visible)
+ return;
+
+ // if Tux is above camera, draw little "air arrow" to show where he is x-wise
+ if (Sector::current() && Sector::current()->camera && (get_bbox().p2.y - 16 < Sector::current()->camera->get_translation().y)) {
+ float px = get_pos().x + (get_bbox().p2.x - get_bbox().p1.x - airarrow.get()->get_width()) / 2;
+ float py = Sector::current()->camera->get_translation().y;
+ py += std::min(((py - (get_bbox().p2.y + 16)) / 4), 16.0f);
+ context.draw_surface(airarrow.get(), Vector(px, py), LAYER_HUD - 1);
+ }
+
+ TuxBodyParts* tux_body;
+
+ if (player_status->bonus == GROWUP_BONUS)
+ tux_body = big_tux;
+ else if (player_status->bonus == FIRE_BONUS)
+ tux_body = fire_tux;
+ else if (player_status->bonus == ICE_BONUS)
+ tux_body = ice_tux;
+ else
+ tux_body = small_tux;
+
+ int layer = LAYER_OBJECTS + 1;
+
+ /* Set Tux sprite action */
+ if (climbing)
+ {
+ tux_body->set_action("skid-left");
+ }
+ else if (backflipping)
+ {
+ if(dir == LEFT)
+ tux_body->set_action("backflip-left");
+ else // dir == RIGHT
+ tux_body->set_action("backflip-right");
+ }
+ else if (duck && is_big())
+ {
+ if(dir == LEFT)
+ tux_body->set_action("duck-left");
+ else // dir == RIGHT
+ tux_body->set_action("duck-right");
+ }
+ else if (skidding_timer.started() && !skidding_timer.check())
+ {
+ if(dir == LEFT)
+ tux_body->set_action("skid-left");
+ else // dir == RIGHT
+ tux_body->set_action("skid-right");
+ }
+ else if (kick_timer.started() && !kick_timer.check())
+ {
+ if(dir == LEFT)
+ tux_body->set_action("kick-left");
+ else // dir == RIGHT
+ tux_body->set_action("kick-right");
+ }
+ else if (butt_jump && is_big())
+ {
+ if(dir == LEFT)
+ tux_body->set_action("buttjump-left");
+ else // dir == RIGHT
+ tux_body->set_action("buttjump-right");
+ }
+ else if (!on_ground())
+ {
+ if(dir == LEFT)
+ tux_body->set_action("jump-left");
+ else // dir == RIGHT
+ tux_body->set_action("jump-right");
+ }
+ else
+ {
+ if (fabsf(physic.get_velocity_x()) < 1.0f) // standing
+ {
+ if(dir == LEFT)
+ tux_body->set_action("stand-left");
+ else // dir == RIGHT
+ tux_body->set_action("stand-right");
+ }
+ else // moving
+ {
+ if(dir == LEFT)
+ tux_body->set_action("walk-left");
+ else // dir == RIGHT
+ tux_body->set_action("walk-right");
+ }
+ }
+
+ if(idle_timer.check())
+ {
+ if(is_big())
+ {
+ if(dir == LEFT)
+ tux_body->head->set_action("idle-left", 1);
+ else // dir == RIGHT
+ tux_body->head->set_action("idle-right", 1);
+ }
+
+ }
+
+ // Tux is holding something
+ if ((grabbed_object != 0 && physic.get_velocity_y() == 0) ||
+ (shooting_timer.get_timeleft() > 0 && !shooting_timer.check()))
+ {
+ if (duck)
+ {
+ if(dir == LEFT)
+ tux_body->arms->set_action("duck+grab-left");
+ else // dir == RIGHT
+ tux_body->arms->set_action("duck+grab-right");
+ }
+ else
+ {
+ if(dir == LEFT)
+ tux_body->arms->set_action("grab-left");
+ else // dir == RIGHT
+ tux_body->arms->set_action("grab-right");
+ }
+ }
+
+ /* Draw Tux */
+ if(dying) {
+ smalltux_gameover->draw(context, get_pos(), LAYER_FLOATINGOBJECTS + 1);
+ }
+ else if ((growing_timer.get_timeleft() > 0) && (!duck)) {
+ if (dir == RIGHT) {
+ context.draw_surface(growingtux_right[int((growing_timer.get_timegone() *
+ GROWING_FRAMES) / GROWING_TIME)], get_pos(), layer);
+ } else {
+ context.draw_surface(growingtux_left[int((growing_timer.get_timegone() *
+ GROWING_FRAMES) / GROWING_TIME)], get_pos(), layer);
+ }
+ }
+ else if (safe_timer.started() && size_t(game_time*40)%2)
+ ; // don't draw Tux
+ else
+ tux_body->draw(context, get_pos(), layer, grabbed_object);
+
+}
+
+void
+Player::collision_tile(uint32_t tile_attributes)
+{
+ if(tile_attributes & Tile::HURTS)
+ kill(false);
+
+#ifdef SWIMMING
+ if( swimming ){
+ if( tile_attributes & Tile::WATER ){
+ no_water = false;
+ } else {
+ swimming = false;
+ }
+ } else {
+ if( tile_attributes & Tile::WATER ){
+ swimming = true;
+ no_water = false;
+ sound_manager->play( "sounds/splash.ogg" );
+ }
+ }
+#endif
+}
+
+void
+Player::collision_solid(const CollisionHit& hit)
+{
+ if(hit.bottom) {
+ if(physic.get_velocity_y() > 0)
+ physic.set_velocity_y(0);
+
+ on_ground_flag = true;
+ floor_normal = hit.slope_normal;
+ } else if(hit.top) {
+ if(physic.get_velocity_y() < 0)
+ physic.set_velocity_y(.2f);
+ }
+
+ if(hit.left || hit.right) {
+ physic.set_velocity_x(0);
+ }
+
+ // crushed?
+ if(hit.crush) {
+ if(hit.left || hit.right) {
+ kill(true);
+ } else if(hit.top || hit.bottom) {
+ kill(false);
+ }
+ }
+}
+
+HitResponse
+Player::collision(GameObject& other, const CollisionHit& hit)
+{
+ Bullet* bullet = dynamic_cast<Bullet*> (&other);
+ if(bullet) {
+ return FORCE_MOVE;
+ }
+
+ if(hit.left || hit.right) {
+ try_grab(); //grab objects right now, in update it will be too late
+ }
+#ifdef DEBUG
+ assert(dynamic_cast<MovingObject*> (&other) != NULL);
+#endif
+ MovingObject* moving_object = static_cast<MovingObject*> (&other);
+ if(moving_object->get_group() == COLGROUP_TOUCHABLE) {
+ TriggerBase* trigger = dynamic_cast<TriggerBase*> (&other);
+ if(trigger) {
+ if(controller->pressed(Controller::UP))
+ trigger->event(*this, TriggerBase::EVENT_ACTIVATE);
+ }
+
+ return FORCE_MOVE;
+ }
+
+ BadGuy* badguy = dynamic_cast<BadGuy*> (&other);
+ if(badguy != NULL) {
+ if(safe_timer.started() || invincible_timer.started())
+ return FORCE_MOVE;
+
+ return CONTINUE;
+ }
+
+ return CONTINUE;
+}
+
+void
+Player::make_invincible()
+{
+ sound_manager->play("sounds/invincible.wav");
+ invincible_timer.start(TUX_INVINCIBLE_TIME);
+ Sector::current()->play_music(HERRING_MUSIC);
+}
+
+/* Kill Player! */
+void
+Player::kill(bool completely)
+{
+ if(dying || deactivated)
+ return;
+
+ if(!completely && (safe_timer.started() || invincible_timer.started()))
+ return;
+
+ sound_manager->play("sounds/hurt.wav");
+ if (climbing) stop_climbing(*climbing);
+
+ physic.set_velocity_x(0);
+
+ if(!completely && (is_big() || growing_timer.started())) {
+ if(player_status->bonus == FIRE_BONUS
+ || player_status->bonus == ICE_BONUS) {
+ safe_timer.start(TUX_SAFE_TIME);
+ set_bonus(GROWUP_BONUS, true);
+ } else if(player_status->bonus == GROWUP_BONUS) {
+ //growing_timer.start(GROWING_TIME);
+ safe_timer.start(TUX_SAFE_TIME /* + GROWING_TIME */);
+ adjust_height(30.8f);
+ duck = false;
+ set_bonus(NO_BONUS, true);
+ } else if(player_status->bonus == NO_BONUS) {
+ growing_timer.stop();
+ safe_timer.start(TUX_SAFE_TIME);
+ adjust_height(30.8f);
+ duck = false;
+ }
+ } else {
+ if (player_status->coins >= 25 && !GameSession::current()->get_reset_point_sectorname().empty())
+ {
+ for (int i = 0; i < 5; i++)
+ {
+ // the numbers: starting x, starting y, velocity y
+ Sector::current()->add_object(new FallingCoin(get_pos() +
+ Vector(systemRandom.rand(5), systemRandom.rand(-32,18)),
+ systemRandom.rand(-100,100)));
+ }
+ player_status->coins -= std::max(player_status->coins/10, 25);
+ }
+ else
+ {
+ GameSession::current()->set_reset_point("", Vector());
+ }
+ physic.enable_gravity(true);
+ physic.set_acceleration(0, 0);
+ physic.set_velocity(0, -700);
+ set_bonus(NO_BONUS, true);
+ dying = true;
+ dying_timer.start(3.0);
+ set_group(COLGROUP_DISABLED);
+
+ DisplayEffect* effect = new DisplayEffect();
+ effect->fade_out(3.0);
+ Sector::current()->add_object(effect);
+ sound_manager->stop_music(3.0);
+ }
+}
+
+void
+Player::move(const Vector& vector)
+{
+ set_pos(vector);
+
+ // TODO: do we need the following? Seems irrelevant to moving the player
+ if(is_big())
+ set_size(31.8f, 63.8f);
+ else
+ set_size(31.8f, 31.8f);
+ duck = false;
+ last_ground_y = vector.y;
+ if (climbing) stop_climbing(*climbing);
+
+ physic.reset();
+}
+
+void
+Player::check_bounds(Camera* camera)
+{
+ /* Keep tux in bounds: */
+ if (get_pos().x < 0) {
+ // Lock Tux to the size of the level, so that he doesn't fall of
+ // on the left side
+ set_pos(Vector(0, get_pos().y));
+ }
+
+ /* fallen out of the level? */
+ if (get_pos().y > Sector::current()->get_height()) {
+ kill(true);
+ return;
+ }
+
+ // can happen if back scrolling is disabled
+ if(get_pos().x < camera->get_translation().x) {
+ set_pos(Vector(camera->get_translation().x, get_pos().y));
+ }
+ if(get_pos().x >= camera->get_translation().x + SCREEN_WIDTH - bbox.get_width())
+ {
+ set_pos(Vector(
+ camera->get_translation().x + SCREEN_WIDTH - bbox.get_width(),
+ get_pos().y));
+ }
+}
+
+void
+Player::add_velocity(const Vector& velocity)
+{
+ physic.set_velocity(physic.get_velocity() + velocity);
+}
+
+void
+Player::add_velocity(const Vector& velocity, const Vector& end_speed)
+{
+ if (end_speed.x > 0)
+ physic.set_velocity_x(std::min(physic.get_velocity_x() + velocity.x, end_speed.x));
+ if (end_speed.x < 0)
+ physic.set_velocity_x(std::max(physic.get_velocity_x() + velocity.x, end_speed.x));
+ if (end_speed.y > 0)
+ physic.set_velocity_y(std::min(physic.get_velocity_y() + velocity.y, end_speed.y));
+ if (end_speed.y < 0)
+ physic.set_velocity_y(std::max(physic.get_velocity_y() + velocity.y, end_speed.y));
+}
+
+void
+Player::bounce(BadGuy& )
+{
+ if(controller->hold(Controller::JUMP))
+ physic.set_velocity_y(-520);
+ else
+ physic.set_velocity_y(-300);
+}
+
+//Scripting Functions Below
+
+void
+Player::deactivate()
+{
+ if (deactivated)
+ return;
+ deactivated = true;
+ physic.set_velocity_x(0);
+ physic.set_velocity_y(0);
+ physic.set_acceleration_x(0);
+ physic.set_acceleration_y(0);
+ if (climbing) stop_climbing(*climbing);
+}
+
+void
+Player::activate()
+{
+ if (!deactivated)
+ return;
+ deactivated = false;
+}
+
+void Player::walk(float speed)
+{
+ physic.set_velocity_x(speed);
+}
+
+void
+Player::set_ghost_mode(bool enable)
+{
+ if (ghost_mode == enable)
+ return;
+
+ if (climbing) stop_climbing(*climbing);
+
+ if (enable) {
+ ghost_mode = true;
+ set_group(COLGROUP_DISABLED);
+ physic.enable_gravity(false);
+ log_debug << "You feel lightheaded. Use movement controls to float around, press ACTION to scare badguys." << std::endl;
+ } else {
+ ghost_mode = false;
+ set_group(COLGROUP_MOVING);
+ physic.enable_gravity(true);
+ log_debug << "You feel solid again." << std::endl;
+ }
+}
+
+
+void
+Player::start_climbing(Climbable& climbable)
+{
+ if (climbing == &climbable) return;
+
+ climbing = &climbable;
+ physic.enable_gravity(false);
+ physic.set_velocity(0, 0);
+ physic.set_acceleration(0, 0);
+}
+
+void
+Player::stop_climbing(Climbable& /*climbable*/)
+{
+ if (!climbing) return;
+
+ climbing = 0;
+
+ if (grabbed_object) {
+ grabbed_object->ungrab(*this, dir);
+ grabbed_object = NULL;
+ }
+
+ physic.enable_gravity(true);
+ physic.set_velocity(0, 0);
+ physic.set_acceleration(0, 0);
+
+ if ((controller->hold(Controller::JUMP)) || (controller->hold(Controller::UP))) {
+ on_ground_flag = true;
+ // TODO: This won't help. Why?
+ do_jump(-300);
+ }
+}
+
+void
+Player::handle_input_climbing()
+{
+ if (!climbing) {
+ log_warning << "handle_input_climbing called with climbing set to 0. Input handling skipped" << std::endl;
+ return;
+ }
+
+ float vx = 0;
+ float vy = 0;
+ if (controller->hold(Controller::LEFT)) {
+ dir = LEFT;
+ vx -= MAX_CLIMB_XM;
+ }
+ if (controller->hold(Controller::RIGHT)) {
+ dir = RIGHT;
+ vx += MAX_CLIMB_XM;
+ }
+ if (controller->hold(Controller::UP)) {
+ vy -= MAX_CLIMB_YM;
+ }
+ if (controller->hold(Controller::DOWN)) {
+ vy += MAX_CLIMB_YM;
+ }
+ if (controller->hold(Controller::JUMP)) {
+ if (can_jump) {
+ stop_climbing(*climbing);
+ return;
+ }
+ } else {
+ can_jump = true;
+ }
+ if (controller->hold(Controller::ACTION)) {
+ stop_climbing(*climbing);
+ return;
+ }
+ physic.set_velocity(vx, vy);
+ physic.set_acceleration(0, 0);
+}
+
+
--- /dev/null
+// $Id$
+//
+// SuperTux
+// Copyright (C) 2006 Matthias Braun <matze@braunis.de>
+//
+// 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_PLAYER_H
+#define SUPERTUX_PLAYER_H
+
+#include <vector>
+#include <SDL.h>
+
+#include "timer.hpp"
+#include "direction.hpp"
+#include "video/surface.hpp"
+#include "moving_object.hpp"
+#include "sprite/sprite.hpp"
+#include "physic.hpp"
+#include "control/controller.hpp"
+#include "scripting/player.hpp"
+#include "player_status.hpp"
+#include "display_effect.hpp"
+#include "script_interface.hpp"
+#include "console.hpp"
+#include "coin.hpp"
+
+class BadGuy;
+class Portable;
+class Climbable;
+
+/* Times: */
+static const float TUX_SAFE_TIME = 1.8f;
+static const float TUX_INVINCIBLE_TIME = 10.0f;
+static const float TUX_INVINCIBLE_TIME_WARNING = 2.0f;
+static const float GROWING_TIME = 0.35f;
+static const int GROWING_FRAMES = 7;
+
+class Camera;
+class PlayerStatus;
+
+extern Surface* growingtux_left[GROWING_FRAMES];
+extern Surface* growingtux_right[GROWING_FRAMES];
+
+class TuxBodyParts
+{
+public:
+ TuxBodyParts()
+ : head(0), body(0), arms(0), feet(0)
+ { }
+ ~TuxBodyParts() {
+ delete head;
+ delete body;
+ delete arms;
+ delete feet;
+ }
+
+ void set_action(std::string action, int loops = -1);
+ void one_time_animation();
+ void draw(DrawingContext& context, const Vector& pos, int layer, Portable* grabbed_object);
+
+ Sprite* head;
+ Sprite* body;
+ Sprite* arms;
+ Sprite* feet;
+};
+
+extern TuxBodyParts* small_tux;
+extern TuxBodyParts* big_tux;
+extern TuxBodyParts* fire_tux;
+extern TuxBodyParts* ice_tux;
+
+class Player : public MovingObject, public UsesPhysic, public Scripting::Player, public ScriptInterface
+{
+public:
+ enum FallMode { ON_GROUND, JUMPING, TRAMPOLINE_JUMP, FALLING };
+
+ Controller* controller;
+ PlayerStatus* player_status;
+ bool duck;
+ bool dead;
+ //Tux can only go this fast. If set to 0 no special limit is used, only the default limits.
+ void set_speedlimit(float newlimit);
+ float get_speedlimit();
+
+private:
+ bool dying;
+ bool backflipping;
+ int backflip_direction;
+ Direction peeking;
+ bool swimming;
+ float speedlimit;
+
+public:
+ Direction dir;
+ Direction old_dir;
+
+ float last_ground_y;
+ FallMode fall_mode;
+
+ bool on_ground_flag;
+ bool jumping;
+ bool can_jump;
+ bool butt_jump;
+
+ Timer invincible_timer;
+ Timer skidding_timer;
+ Timer safe_timer;
+ Timer kick_timer;
+ Timer shooting_timer; // used to show the arm when Tux is shooting
+ Timer dying_timer;
+ Timer growing_timer;
+ Timer idle_timer;
+ Timer backflip_timer;
+
+public:
+ Player(PlayerStatus* player_status, const std::string& name);
+ virtual ~Player();
+
+ virtual void expose(HSQUIRRELVM vm, SQInteger table_idx);
+ virtual void unexpose(HSQUIRRELVM vm, SQInteger table_idx);
+
+ void set_controller(Controller* controller);
+ Controller* get_controller()
+ {
+ return controller;
+ }
+
+ virtual void update(float elapsed_time);
+ virtual void draw(DrawingContext& context);
+ virtual void collision_solid(const CollisionHit& hit);
+ virtual HitResponse collision(GameObject& other, const CollisionHit& hit);
+ virtual void collision_tile(uint32_t tile_attributes);
+
+ void make_invincible();
+ bool is_invincible() const
+ {
+ return invincible_timer.started();
+ }
+ bool is_dying() const
+ {
+ return dying;
+ }
+ Direction peeking_direction() const
+ {
+ return peeking;
+ }
+
+ void kill(bool completely);
+ void check_bounds(Camera* camera);
+ void move(const Vector& vector);
+
+ virtual bool add_bonus(const std::string& bonus);
+ virtual void add_coins(int count);
+ virtual int get_coins();
+
+ /**
+ * picks up a bonus, taking care not to pick up lesser bonus items than we already have
+ *
+ * @returns true if the bonus has been set (or was already good enough)
+ * false if the bonus could not be set (for example no space for big tux)
+ */
+ bool add_bonus(BonusType type, bool animate = false);
+ /**
+ * like add_bonus, but can also downgrade the bonus items carried
+ */
+ bool set_bonus(BonusType type, bool animate = false);
+
+ PlayerStatus* get_status()
+ {
+ return player_status;
+ }
+ // set kick animation
+ void kick();
+
+ /**
+ * play cheer animation.
+ * This might need some space and behave in an unpredictable way. Best to use this at level end.
+ */
+ void do_cheer();
+
+ /**
+ * duck down if possible.
+ * this won't last long as long as input is enabled.
+ */
+ void do_duck();
+
+ /**
+ * stand back up if possible.
+ */
+ void do_standup();
+
+ /**
+ * do a backflip if possible.
+ */
+ void do_backflip();
+
+ /**
+ * jump in the air if possible
+ * sensible values for yspeed are negative - unless we want to jump into the ground of course
+ */
+ void do_jump(float yspeed);
+
+ /**
+ * Adds velocity to the player (be carefull when using this)
+ */
+ void add_velocity(const Vector& velocity);
+
+ /**
+ * Adds velocity to the player until given end speed is reached
+ */
+ void add_velocity(const Vector& velocity, const Vector& end_speed);
+
+ void bounce(BadGuy& badguy);
+
+ bool is_dead() const
+ { return dead; }
+ bool is_big();
+
+ void set_visible(bool visible);
+ bool get_visible();
+
+ bool on_ground();
+
+ Portable* get_grabbed_object() const
+ {
+ return grabbed_object;
+ }
+
+ /**
+ * Switches ghost mode on/off.
+ * Lets Tux float around and through solid objects.
+ */
+ void set_ghost_mode(bool enable);
+
+ /**
+ * Returns whether ghost mode is currently enabled
+ */
+ bool get_ghost_mode() { return ghost_mode; }
+
+ /**
+ * Changes height of bounding box.
+ * Returns true if successful, false otherwise
+ */
+ bool adjust_height(float new_height);
+
+ /**
+ * Orders the current GameSession to start a sequence
+ */
+ void trigger_sequence(std::string sequence_name);
+
+ /**
+ * Requests that the player start climbing the given Climbable
+ */
+ void start_climbing(Climbable& climbable);
+
+ /**
+ * Requests that the player stop climbing the given Climbable
+ */
+ void stop_climbing(Climbable& climbable);
+
+private:
+ void handle_input();
+ void handle_input_ghost(); /**< input handling while in ghost mode */
+ void handle_input_climbing(); /**< input handling while climbing */
+ bool deactivated;
+
+ void init();
+
+ void handle_horizontal_input();
+ void handle_vertical_input();
+
+ void activate();
+ void deactivate();
+ void walk(float speed);
+
+ /**
+ * slows Tux down a little, based on where he's standing
+ */
+ void apply_friction();
+
+ bool visible;
+
+ Portable* grabbed_object;
+
+ Sprite* smalltux_gameover;
+ Sprite* smalltux_star;
+ Sprite* bigtux_star;
+
+ std::auto_ptr<Surface> airarrow; /**< arrow indicating Tux' position when he's above the camera */
+
+ Vector floor_normal;
+ void try_grab();
+
+ bool ghost_mode; /**< indicates if Tux should float around and through solid objects */
+
+ Timer unduck_hurt_timer; /**< if Tux wants to stand up again after ducking and cannot, this timer is started */
+
+ Climbable* climbing; /**< Climbable object we are currently climbing, null if none */
+};
+
+#endif /*SUPERTUX_PLAYER_H*/
--- /dev/null
+// $Id$
+//
+// SuperTux - PneumaticPlatform
+// Copyright (C) 2007 Christoph Sommer <christoph.sommer@2007.expires.deltadevelopment.de>
+//
+// 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 <config.h>
+
+#include "pneumatic_platform.hpp"
+
+#include <stdexcept>
+#include "log.hpp"
+#include "video/drawing_context.hpp"
+#include "resources.hpp"
+#include "player.hpp"
+#include "path.hpp"
+#include "path_walker.hpp"
+#include "sprite/sprite.hpp"
+#include "lisp/lisp.hpp"
+#include "object_factory.hpp"
+#include "sector.hpp"
+#include "object/portable.hpp"
+
+PneumaticPlatform::PneumaticPlatform(const lisp::Lisp& reader)
+ : MovingSprite(reader, LAYER_OBJECTS, COLGROUP_STATIC),
+ master(0), slave(0), start_y(0), offset_y(0), speed_y(0)
+{
+ start_y = get_pos().y;
+}
+
+PneumaticPlatform::PneumaticPlatform(PneumaticPlatform* master)
+ : MovingSprite(*master),
+ master(master), slave(this), start_y(master->start_y), offset_y(-master->offset_y), speed_y(0)
+{
+ set_pos(get_pos() + Vector(master->get_bbox().get_width(), 0));
+ master->master = master;
+ master->slave = this;
+}
+
+PneumaticPlatform::~PneumaticPlatform() {
+ if ((this == master) && (master)) {
+ slave->master = 0;
+ slave->slave = 0;
+ }
+ if ((master) && (this == slave)) {
+ master->master = 0;
+ master->slave = 0;
+ }
+ master = 0;
+ slave = 0;
+}
+
+HitResponse
+PneumaticPlatform::collision(GameObject& other, const CollisionHit& )
+{
+
+ // somehow the hit parameter does not get filled in, so to determine (hit.top == true) we do this:
+ MovingObject* mo = dynamic_cast<MovingObject*>(&other);
+ if (!mo) return FORCE_MOVE;
+ if ((mo->get_bbox().p2.y) > (get_bbox().p1.y + 2)) return FORCE_MOVE;
+
+ Player* pl = dynamic_cast<Player*>(mo);
+ if (pl) {
+ if (pl->is_big()) contacts.insert(0);
+ Portable* po = pl->get_grabbed_object();
+ MovingObject* pomo = dynamic_cast<MovingObject*>(po);
+ if (pomo) contacts.insert(pomo);
+ }
+
+ contacts.insert(&other);
+ return FORCE_MOVE;
+}
+
+void
+PneumaticPlatform::update(float elapsed_time)
+{
+ if (!slave) {
+ Sector::current()->add_object(new PneumaticPlatform(this));
+ return;
+ }
+ if (!master) {
+ return;
+ }
+ if (this == slave) {
+ offset_y = -master->offset_y;
+ movement = Vector(0, (start_y + offset_y) - get_pos().y);
+ }
+ if (this == master) {
+ int contact_diff = contacts.size() - slave->contacts.size();
+ contacts.clear();
+ slave->contacts.clear();
+
+ speed_y += ((float)contact_diff * elapsed_time) * 128.0f;
+ speed_y -= (offset_y * elapsed_time * 0.5f);
+ speed_y *= 1 - elapsed_time;
+ offset_y += speed_y * elapsed_time;
+ if (offset_y < -256) { offset_y = -256; speed_y = 0; }
+ if (offset_y > 256) { offset_y = 256; speed_y = -0; }
+ movement = Vector(0, (start_y + offset_y) - get_pos().y);
+ }
+}
+
+IMPLEMENT_FACTORY(PneumaticPlatform, "pneumatic-platform");
+
--- /dev/null
+// $Id$
+//
+// SuperTux - PneumaticPlatform
+// Copyright (C) 2007 Christoph Sommer <christoph.sommer@2007.expires.deltadevelopment.de>
+//
+// 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 __PNEUMATIC_PLATFORM_H__
+#define __PNEUMATIC_PLATFORM_H__
+
+#include <memory>
+#include <string>
+#include <set>
+#include "object/moving_sprite.hpp"
+#include "object/path.hpp"
+#include "object/path_walker.hpp"
+
+/**
+ * Used to construct a pair of pneumatic platforms: If one is pushed down, the other one rises
+ */
+class PneumaticPlatform : public MovingSprite
+{
+public:
+ PneumaticPlatform(const lisp::Lisp& reader);
+ PneumaticPlatform(PneumaticPlatform* master);
+ virtual ~PneumaticPlatform();
+
+ virtual HitResponse collision(GameObject& other, const CollisionHit& hit);
+ virtual void update(float elapsed_time);
+
+protected:
+ PneumaticPlatform* master; /**< pointer to PneumaticPlatform that does movement calculation */
+ PneumaticPlatform* slave; /**< pointer to PneumaticPlatform that reacts to master platform's movement calculation */
+ float start_y; /**< vertical start position */
+ float offset_y; /**< vertical offset from the start position in px */
+ float speed_y; /**< vertical speed */
+ std::set<GameObject*> contacts; /**< objects that are currently pushing on the platform */
+
+};
+
+#endif
--- /dev/null
+// $Id$
+//
+// SuperTux
+// Copyright (C) 2006 Matthias Braun <matze@braunis.de>
+//
+// 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 __PORTABLE_H__
+#define __PORTABLE_H__
+
+#include "moving_object.hpp"
+#include "direction.hpp"
+#include "refcounter.hpp"
+
+/**
+ * An object that inherits from this object is considered "portable" and can
+ * be carried around by the player.
+ * The object has to additionally set the PORTABLE flag (this allows to
+ * make the object only temporarily portable by resetting the flag)
+ */
+class Portable
+{
+public:
+ virtual ~Portable()
+ { }
+
+ /**
+ * called each frame when the object has been grabbed.
+ */
+ virtual void grab(MovingObject& object, const Vector& pos, Direction dir) = 0;
+
+ virtual void ungrab(MovingObject& , Direction )
+ {}
+
+ virtual bool is_portable() const
+ {
+ return true;
+ }
+};
+
+#endif
--- /dev/null
+// $Id$
+//
+// SuperTux
+// Copyright (C) 2006 Matthias Braun <matze@braunis.de>
+//
+// 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 <config.h>
+
+#include <stdexcept>
+#include <math.h>
+#include <stdexcept>
+#include "powerup.hpp"
+#include "resources.hpp"
+#include "player.hpp"
+#include "audio/sound_manager.hpp"
+#include "object_factory.hpp"
+#include "sector.hpp"
+#include "log.hpp"
+
+PowerUp::PowerUp(const lisp::Lisp& lisp)
+ : MovingSprite(lisp, LAYER_OBJECTS, COLGROUP_MOVING)
+{
+ lisp.get("script", script);
+ no_physics = false;
+ lisp.get("disable-physics", no_physics);
+ physic.enable_gravity(true);
+ sound_manager->preload("sounds/grow.wav");
+ sound_manager->preload("sounds/fire-flower.wav");
+}
+
+void
+PowerUp::collision_solid(const CollisionHit& hit)
+{
+ if(hit.bottom) {
+ physic.set_velocity_y(0);
+ }
+ if(hit.right || hit.left) {
+ physic.set_velocity_x(-physic.get_velocity_x());
+ }
+}
+
+HitResponse
+PowerUp::collision(GameObject& other, const CollisionHit&)
+{
+ Player* player = dynamic_cast<Player*>(&other);
+ if(player == 0)
+ return FORCE_MOVE;
+
+ if (script != "") {
+ std::istringstream stream(script);
+ Sector::current()->run_script(stream, "powerup-script");
+ remove_me();
+ return ABORT_MOVE;
+ }
+
+ // some defaults if no script has been set
+ if (sprite_name == "images/powerups/egg/egg.sprite") {
+ if(!player->add_bonus(GROWUP_BONUS, true))
+ return FORCE_MOVE;
+ sound_manager->play("sounds/grow.wav");
+ } else if (sprite_name == "images/powerups/fireflower/fireflower.sprite") {
+ if(!player->add_bonus(FIRE_BONUS, true))
+ return FORCE_MOVE;
+ sound_manager->play("sounds/fire-flower.wav");
+ } else if (sprite_name == "images/powerups/star/star.sprite") {
+ player->make_invincible();
+ } else if (sprite_name == "images/powerups/1up/1up.sprite") {
+ player->get_status()->add_coins(100);
+ }
+
+ remove_me();
+ return ABORT_MOVE;
+}
+
+void
+PowerUp::update(float elapsed_time)
+{
+ if (!no_physics)
+ movement = physic.get_movement(elapsed_time);
+}
+
+IMPLEMENT_FACTORY(PowerUp, "powerup");
--- /dev/null
+// $Id$
+//
+// SuperTux
+// Copyright (C) 2006 Matthias Braun <matze@braunis.de>
+//
+// 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 __POWERUP_H__
+#define __POWERUP_H__
+
+#include "object/moving_sprite.hpp"
+#include "lisp/lisp.hpp"
+#include "collision_hit.hpp"
+#include "physic.hpp"
+
+class PowerUp : public MovingSprite, private UsesPhysic
+{
+public:
+ PowerUp(const lisp::Lisp& lisp);
+
+ virtual void update(float elapsed_time);
+ virtual void collision_solid(const CollisionHit& hit);
+ virtual HitResponse collision(GameObject& other, const CollisionHit& hit);
+
+private:
+ std::string script;
+ bool no_physics;
+};
+
+#endif
--- /dev/null
+// $Id$
+//
+// SuperTux - Pulsing Light
+// Copyright (C) 2006 Christoph Sommer <christoph.sommer@2006.expires.deltadevelopment.de>
+//
+// 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 <config.h>
+
+#include "pulsing_light.hpp"
+#include "video/color.hpp"
+#include <math.h>
+#include "random_generator.hpp"
+
+PulsingLight::PulsingLight(const Vector& center, float cycle_len, float min_alpha, float max_alpha, const Color& color)
+ : Light(center, color), min_alpha(min_alpha), max_alpha(max_alpha), cycle_len(cycle_len), t(0)
+{
+ assert(cycle_len > 0);
+
+ // start with random phase offset
+ t = systemRandom.randf(0.0, cycle_len);
+}
+
+PulsingLight::~PulsingLight()
+{
+}
+
+void
+PulsingLight::update(float elapsed_time)
+{
+ Light::update(elapsed_time);
+
+ t += elapsed_time;
+ if (t > cycle_len) t -= cycle_len;
+}
+
+void
+PulsingLight::draw(DrawingContext& context)
+{
+ Color old_color = color;
+
+ color.alpha *= min_alpha + ((max_alpha - min_alpha) * cos(2 * M_PI * t / cycle_len));
+ Light::draw(context);
+
+ color = old_color;
+}
--- /dev/null
+// $Id$
+//
+// SuperTux - Pulsing Light
+// Copyright (C) 2006 Christoph Sommer <christoph.sommer@2006.expires.deltadevelopment.de>
+//
+// 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 __PULSING_LIGHT_HPP__
+#define __PULSING_LIGHT_HPP__
+
+#include "light.hpp"
+#include "game_object.hpp"
+#include "lisp/lisp.hpp"
+#include "math/vector.hpp"
+#include "video/color.hpp"
+
+class Sprite;
+
+/**
+ * Light source that changes alpha value to give the impression of a pulsating light
+ */
+class PulsingLight : public Light
+{
+public:
+ PulsingLight(const Vector& center, float cycle_len = 5.0, float min_alpha = 0.0, float max_alpha = 1.0, const Color& color = Color(1.0, 1.0, 1.0, 1.0));
+ virtual ~PulsingLight();
+
+ void update(float elapsed_time);
+ void draw(DrawingContext& context);
+
+protected:
+ float min_alpha; /**< minimum alpha */
+ float max_alpha; /**< maximum alpha */
+ float cycle_len; /**< length in seconds of one cycle */
+
+ float t; /**< local time in seconds */
+};
+
+#endif
--- /dev/null
+// $Id$
+//
+// SuperTux - PushButton running a script
+// Copyright (C) 2006 Christoph Sommer <christoph.sommer@2006.expires.deltadevelopment.de>
+//
+// 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 <config.h>
+#include <stdexcept>
+
+#include "pushbutton.hpp"
+#include "object_factory.hpp"
+#include "player.hpp"
+#include "audio/sound_manager.hpp"
+#include "sprite/sprite_manager.hpp"
+#include "sector.hpp"
+#include "log.hpp"
+
+namespace {
+ const std::string BUTTON_SOUND = "sounds/switch.ogg";
+ //14 -> 8
+}
+
+PushButton::PushButton(const lisp::Lisp& lisp)
+ : MovingSprite(lisp, "images/objects/pushbutton/pushbutton.sprite", LAYER_BACKGROUNDTILES+1, COLGROUP_MOVING), state(OFF)
+{
+ sound_manager->preload(BUTTON_SOUND);
+ set_action("off", -1);
+ bbox.set_size(sprite->get_current_hitbox_width(), sprite->get_current_hitbox_height());
+
+ if (!lisp.get("script", script)) throw std::runtime_error("no script set");
+}
+
+void
+PushButton::update(float /*elapsed_time*/)
+{
+}
+
+HitResponse
+PushButton::collision(GameObject& other, const CollisionHit& hit)
+{
+ Player* player = dynamic_cast<Player*>(&other);
+ if (!player) return FORCE_MOVE;
+ float vy = player->physic.get_velocity_y();
+
+ //player->add_velocity(Vector(0, -150));
+ player->physic.set_velocity_y(-150);
+
+ if (state != OFF) return FORCE_MOVE;
+ if (!hit.top) return FORCE_MOVE;
+ if (vy <= 0) return FORCE_MOVE;
+
+ // change appearance
+ state = ON;
+ float old_bbox_height = bbox.get_height();
+ set_action("on", -1);
+ float new_bbox_height = bbox.get_height();
+ set_pos(get_pos() + Vector(0, old_bbox_height - new_bbox_height));
+
+ // play sound
+ sound_manager->play(BUTTON_SOUND);
+
+ // run script
+ std::istringstream stream(script);
+ Sector::current()->run_script(stream, "PushButton");
+
+ return FORCE_MOVE;
+}
+
+IMPLEMENT_FACTORY(PushButton, "pushbutton");
--- /dev/null
+// $Id$
+//
+// SuperTux - PushButton running a script
+// Copyright (C) 2006 Christoph Sommer <christoph.sommer@2006.expires.deltadevelopment.de>
+//
+// 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 "moving_sprite.hpp"
+#include "lisp/lisp.hpp"
+
+/**
+ * PushButton - jump on it to run a script
+ */
+class PushButton : public MovingSprite
+{
+public:
+ PushButton(const lisp::Lisp& reader);
+
+ HitResponse collision(GameObject& other, const CollisionHit& hit);
+ void update(float elapsed_time);
+
+private:
+ enum PushButtonState {
+ OFF,
+ ON
+ };
+
+ std::string script;
+ PushButtonState state;
+};
+
+#endif
--- /dev/null
+// $Id$
+//
+// SuperTux
+// Copyright (C) 2006 Matthias Braun <matze@braunis.de>
+//
+// 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 <config.h>
+
+#include "rainsplash.hpp"
+#include "sector.hpp"
+
+RainSplash::RainSplash(Vector pos, bool vertical)
+{
+ frame = 0;
+ position = pos;
+ if (vertical) sprite = sprite_manager->create("images/objects/particles/rainsplash-vertical.sprite");
+ else sprite = sprite_manager->create("images/objects/particles/rainsplash.sprite");
+}
+
+RainSplash::~RainSplash() {
+ remove_me();
+}
+
+void
+RainSplash::hit(Player& )
+{
+}
+
+void
+RainSplash::update(float time)
+{
+ time = 0;//just so i don't get an "unused variable" error - don't know how to circumvent this
+ frame++;
+ if (frame >= 10) remove_me();
+}
+
+void
+RainSplash::draw(DrawingContext& context)
+{
+ sprite->draw(context, position, LAYER_OBJECTS);
+}
--- /dev/null
+// $Id$
+//
+// SuperTux
+// Copyright (C) 2006 Matthias Braun <matze@braunis.de>
+//
+// 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 __RAINSPLASH_H__
+#define __RAINSPLASH_H__
+
+
+#include "game_object.hpp"
+#include "resources.hpp"
+#include "player.hpp"
+#include "sprite/sprite.hpp"
+#include "sprite/sprite_manager.hpp"
+#include "video/drawing_context.hpp"
+
+class RainSplash : public GameObject
+{
+public:
+ RainSplash(Vector pos, bool vertical);
+ ~RainSplash();
+protected:
+ virtual void hit(Player& );
+ virtual void update(float time);
+ virtual void draw(DrawingContext& context);
+private:
+ Sprite* sprite;
+ Vector position;
+ int frame;
+};
+
+#endif
--- /dev/null
+// $Id$
+//
+// SuperTux
+// Copyright (C) 2006 Matthias Braun <matze@braunis.de>
+//
+// 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 <config.h>
+
+#include "rock.hpp"
+#include "lisp/writer.hpp"
+#include "object_factory.hpp"
+#include "audio/sound_manager.hpp"
+#include "tile.hpp"
+
+namespace {
+ const std::string ROCK_SOUND = "sounds/brick.wav"; //TODO use own sound.
+}
+
+Rock::Rock(const Vector& pos, std::string spritename)
+ : MovingSprite(pos, spritename)
+{
+ sound_manager->preload(ROCK_SOUND);
+ on_ground = false;
+ grabbed = false;
+ set_group(COLGROUP_MOVING_STATIC);
+}
+
+Rock::Rock(const lisp::Lisp& reader)
+ : MovingSprite(reader, "images/objects/rock/rock.sprite")
+{
+ sound_manager->preload(ROCK_SOUND);
+ on_ground = false;
+ grabbed = false;
+ set_group(COLGROUP_MOVING_STATIC);
+}
+
+Rock::Rock(const lisp::Lisp& reader, std::string spritename)
+ : MovingSprite(reader, spritename)
+{
+ sound_manager->preload(ROCK_SOUND);
+ on_ground = false;
+ grabbed = false;
+ set_group(COLGROUP_MOVING_STATIC);
+}
+
+void
+Rock::write(lisp::Writer& writer)
+{
+ writer.start_list("rock");
+
+ writer.write_float("x", bbox.p1.x);
+ writer.write_float("y", bbox.p1.y);
+
+ writer.end_list("rock");
+}
+
+void
+Rock::update(float elapsed_time)
+{
+ if( grabbed )
+ return;
+
+ if (on_ground) physic.set_velocity_x(0);
+
+ movement = physic.get_movement(elapsed_time);
+}
+
+void
+Rock::collision_solid(const CollisionHit& hit)
+{
+ if(grabbed) {
+ return;
+ }
+ if(hit.top || hit.bottom)
+ physic.set_velocity_y(0);
+ if(hit.left || hit.right)
+ physic.set_velocity_x(0);
+ if(hit.crush)
+ physic.set_velocity(0, 0);
+
+ if(hit.bottom && !on_ground && !grabbed) {
+ sound_manager->play(ROCK_SOUND, get_pos());
+ on_ground = true;
+ }
+}
+
+HitResponse
+Rock::collision(GameObject& other, const CollisionHit& hit)
+{
+ if(grabbed) {
+ return PASSTHROUGH;
+ }
+ if(!on_ground) {
+ if(hit.bottom && physic.get_velocity_y() > 200) {
+ MovingObject* moving_object = dynamic_cast<MovingObject*> (&other);
+ if(moving_object) {
+ //Getting a rock on the head hurts. A lot.
+ moving_object->collision_tile(Tile::HURTS);
+ }
+ }
+ return FORCE_MOVE;
+ }
+
+ return FORCE_MOVE;
+}
+
+void
+Rock::grab(MovingObject& , const Vector& pos, Direction)
+{
+ movement = pos - get_pos();
+ last_movement = movement;
+ set_group(COLGROUP_TOUCHABLE);
+ on_ground = false;
+ grabbed = true;
+}
+
+void
+Rock::ungrab(MovingObject& , Direction dir)
+{
+ set_group(COLGROUP_MOVING_STATIC);
+ on_ground = false;
+ if(dir == UP) {
+ physic.set_velocity(0, -500);
+ } else if (last_movement.norm() > 1) {
+ physic.set_velocity((dir == RIGHT) ? 200 : -200, -200);
+ } else {
+ physic.set_velocity(0, 0);
+ }
+ grabbed = false;
+}
+
+IMPLEMENT_FACTORY(Rock, "rock");
--- /dev/null
+// $Id$
+//
+// SuperTux
+// Copyright (C) 2006 Matthias Braun <matze@braunis.de>
+//
+// 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 __ROCK_H__
+#define __ROCK_H__
+
+#include "object/moving_sprite.hpp"
+#include "physic.hpp"
+#include "lisp/lisp.hpp"
+#include "portable.hpp"
+#include "serializable.hpp"
+
+class Sprite;
+
+class Rock : public MovingSprite, public Portable, protected UsesPhysic, public Serializable
+{
+public:
+ Rock(const Vector& pos, std::string spritename);
+ Rock(const lisp::Lisp& reader);
+ Rock(const lisp::Lisp& reader, std::string spritename);
+ virtual Rock* clone() const { return new Rock(*this); }
+
+ void collision_solid(const CollisionHit& hit);
+ HitResponse collision(GameObject& other, const CollisionHit& hit);
+ void update(float elapsed_time);
+ void write(lisp::Writer& writer);
+
+ void grab(MovingObject& object, const Vector& pos, Direction dir);
+ void ungrab(MovingObject& object, Direction dir);
+
+protected:
+ bool on_ground;
+ bool grabbed;
+ Vector last_movement;
+};
+
+#endif
--- /dev/null
+// $Id$
+//
+// SuperTux
+// Copyright (C) 2006 Matthias Braun <matze@braunis.de>
+//
+// 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 <config.h>
+
+#include <stdexcept>
+#include <math.h>
+
+#include "scripted_object.hpp"
+#include "video/drawing_context.hpp"
+#include "scripting/squirrel_util.hpp"
+#include "resources.hpp"
+#include "object_factory.hpp"
+#include "math/vector.hpp"
+
+ScriptedObject::ScriptedObject(const lisp::Lisp& lisp)
+ : MovingSprite(lisp, LAYER_OBJECTS, COLGROUP_MOVING_STATIC),
+ solid(true), physic_enabled(true), visible(true), new_vel_set(false)
+{
+ lisp.get("name", name);
+ if(name == "")
+ throw std::runtime_error("Scripted object must have a name specified");
+
+ // FIXME: do we need this? bbox is already set via .sprite file
+ float width = sprite->get_width();
+ float height = sprite->get_height();
+ lisp.get("width", width);
+ lisp.get("height", height);
+ bbox.set_size(width, height);
+
+ lisp.get("solid", solid);
+ lisp.get("physic-enabled", physic_enabled);
+ lisp.get("visible", visible);
+ lisp.get("z-pos", layer);
+ if( solid ){
+ set_group( COLGROUP_MOVING_STATIC );
+ } else {
+ set_group( COLGROUP_DISABLED );
+ }
+}
+
+void
+ScriptedObject::expose(HSQUIRRELVM vm, SQInteger table_idx)
+{
+ if (name.empty()) return;
+ expose_object(vm, table_idx, dynamic_cast<Scripting::ScriptedObject *>(this), name, false);
+}
+
+void
+ScriptedObject::unexpose(HSQUIRRELVM vm, SQInteger table_idx)
+{
+ if (name.empty()) return;
+ Scripting::unexpose_object(vm, table_idx, name);
+}
+
+void
+ScriptedObject::move(float x, float y)
+{
+ bbox.move(Vector(x, y));
+}
+
+void
+ScriptedObject::set_pos(float x, float y)
+{
+ printf("SetPos: %f %f\n", x, y);
+ bbox.set_pos(Vector(x, y));
+ physic.reset();
+}
+
+float
+ScriptedObject::get_pos_x()
+{
+ return get_pos().x;
+}
+
+float
+ScriptedObject::get_pos_y()
+{
+ return get_pos().y;
+}
+
+void
+ScriptedObject::set_velocity(float x, float y)
+{
+ new_vel = Vector(x, y);
+ new_vel_set = true;
+}
+
+float
+ScriptedObject::get_velocity_x()
+{
+ return physic.get_velocity_x();
+}
+
+float
+ScriptedObject::get_velocity_y()
+{
+ return physic.get_velocity_y();
+}
+
+void
+ScriptedObject::set_visible(bool visible)
+{
+ this->visible = visible;
+}
+
+bool
+ScriptedObject::is_visible()
+{
+ return visible;
+}
+
+void
+ScriptedObject::set_solid(bool solid)
+{
+ this->solid = solid;
+ if( solid ){
+ set_group( COLGROUP_MOVING_STATIC );
+ } else {
+ set_group( COLGROUP_DISABLED );
+ }
+}
+
+bool
+ScriptedObject::is_solid()
+{
+ return solid;
+}
+
+
+void
+ScriptedObject::set_action(const std::string& animation)
+{
+ sprite->set_action(animation);
+}
+
+std::string
+ScriptedObject::get_action()
+{
+ return sprite->get_action();
+}
+
+std::string
+ScriptedObject::get_name()
+{
+ return name;
+}
+
+void
+ScriptedObject::update(float elapsed_time)
+{
+ if(!physic_enabled)
+ return;
+
+ if(new_vel_set) {
+ physic.set_velocity(new_vel.x, new_vel.y);
+ new_vel_set = false;
+ }
+ movement = physic.get_movement(elapsed_time);
+}
+
+void
+ScriptedObject::draw(DrawingContext& context)
+{
+ if(!visible)
+ return;
+
+ sprite->draw(context, get_pos(), layer);
+}
+
+void
+ScriptedObject::collision_solid(const CollisionHit& hit)
+{
+ if(!physic_enabled)
+ return;
+
+ if(hit.bottom) {
+ if(physic.get_velocity_y() > 0)
+ physic.set_velocity_y(0);
+ } else if(hit.top) {
+ physic.set_velocity_y(.1f);
+ }
+
+ if(hit.left || hit.right) {
+ physic.set_velocity_x(0);
+ }
+}
+
+HitResponse
+ScriptedObject::collision(GameObject& , const CollisionHit& )
+{
+ return FORCE_MOVE;
+}
+
+IMPLEMENT_FACTORY(ScriptedObject, "scriptedobject");
--- /dev/null
+// $Id$
+//
+// SuperTux
+// Copyright (C) 2006 Matthias Braun <matze@braunis.de>
+//
+// 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 __SCRIPTED_OBJECT_H__
+#define __SCRIPTED_OBJECT_H__
+
+#include <string>
+#include "physic.hpp"
+#include "lisp/lisp.hpp"
+#include "object/moving_sprite.hpp"
+#include "script_interface.hpp"
+#include "scripting/scripted_object.hpp"
+
+class ScriptedObject : public MovingSprite, public UsesPhysic,
+ public Scripting::ScriptedObject, public ScriptInterface
+{
+public:
+ ScriptedObject(const lisp::Lisp& lisp);
+ virtual ScriptedObject* clone() const { return new ScriptedObject(*this); }
+
+ virtual void expose(HSQUIRRELVM vm, SQInteger table_idx);
+ virtual void unexpose(HSQUIRRELVM vm, SQInteger table_idx);
+
+ void update(float elapsed_time);
+ void draw(DrawingContext& context);
+
+ void collision_solid(const CollisionHit& hit);
+ HitResponse collision(GameObject& other, const CollisionHit& hit);
+
+ // --- Scripting Interface stuff ---
+
+ void set_action(const std::string& animation);
+ std::string get_action();
+
+ void move(float x, float y);
+ void set_pos(float x, float y);
+ float get_pos_x();
+ float get_pos_y();
+ void set_velocity(float x, float y);
+ float get_velocity_x();
+ float get_velocity_y();
+ void set_visible(bool visible);
+ bool is_visible();
+ void set_solid(bool solid);
+ bool is_solid();
+
+ std::string get_name();
+
+private:
+ std::string name;
+ bool solid;
+ bool physic_enabled;
+ bool visible;
+ bool new_vel_set;
+ Vector new_vel;
+};
+
+#endif
--- /dev/null
+// $Id$
+//
+// SuperTux
+// Copyright (C) 2006 Matthias Braun <matze@braunis.de>
+//
+// 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 <config.h>
+
+#include "skull_tile.hpp"
+#include "lisp/lisp.hpp"
+#include "object_factory.hpp"
+#include "player.hpp"
+#include "sector.hpp"
+#include "resources.hpp"
+#include "sprite/sprite.hpp"
+#include "random_generator.hpp"
+
+static const float CRACKTIME = 0.3f;
+static const float FALLTIME = 0.8f;
+
+SkullTile::SkullTile(const lisp::Lisp& lisp)
+ : MovingSprite(lisp, "images/objects/skull_tile/skull_tile.sprite", LAYER_TILES, COLGROUP_STATIC), hit(false), falling(false)
+{
+}
+
+HitResponse
+SkullTile::collision(GameObject& other, const CollisionHit& )
+{
+ Player* player = dynamic_cast<Player*> (&other);
+ if(player)
+ hit = true;
+
+ return FORCE_MOVE;
+}
+
+void
+SkullTile::draw(DrawingContext& context)
+{
+ Vector pos = get_pos();
+ // shacking
+ if(timer.get_timegone() > CRACKTIME) {
+ pos.x += systemRandom.rand(-3, 3);
+ }
+
+ sprite->draw(context, pos, layer);
+}
+
+void
+SkullTile::update(float elapsed_time)
+{
+ if(falling) {
+ movement = physic.get_movement(elapsed_time);
+ if(!Sector::current()->inside(bbox)) {
+ remove_me();
+ return;
+ }
+ } else if(hit) {
+ if(timer.check()) {
+ falling = true;
+ physic.enable_gravity(true);
+ timer.stop();
+ } else if(!timer.started()) {
+ timer.start(FALLTIME);
+ }
+ } else {
+ timer.stop();
+ }
+ hit = false;
+}
+
+IMPLEMENT_FACTORY(SkullTile, "skull_tile");
--- /dev/null
+// $Id$
+//
+// SuperTux
+// Copyright (C) 2006 Matthias Braun <matze@braunis.de>
+//
+// 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 __SKULL_TILE_H__
+#define __SKULL_TILE_H__
+
+#include "object/moving_sprite.hpp"
+#include "lisp/lisp.hpp"
+#include "physic.hpp"
+#include "timer.hpp"
+
+class Player;
+
+/** A tile that starts falling down if tux stands to long on it */
+class SkullTile : public MovingSprite, private UsesPhysic
+{
+public:
+ SkullTile(const lisp::Lisp& lisp);
+ virtual SkullTile* clone() const { return new SkullTile(*this); }
+
+ HitResponse collision(GameObject& other, const CollisionHit& hit);
+ void update(float elapsed_time);
+ void draw(DrawingContext& context);
+
+private:
+ Timer timer;
+ bool hit;
+ bool falling;
+};
+
+#endif
--- /dev/null
+// $Id$
+//
+// SuperTux
+// Copyright (C) 2006 Matthias Braun <matze@braunis.de>
+//
+// 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 <config.h>
+
+#include <math.h>
+#include "specialriser.hpp"
+#include "resources.hpp"
+#include "camera.hpp"
+#include "sector.hpp"
+#include "sprite/sprite_manager.hpp"
+
+SpecialRiser::SpecialRiser(Vector pos, MovingObject* _child)
+ : child(_child)
+{
+ _child->set_pos(pos - Vector(0, 32));
+ offset = 0;
+}
+
+SpecialRiser::~SpecialRiser()
+{
+}
+
+void
+SpecialRiser::update(float elapsed_time)
+{
+ offset += 50 * elapsed_time;
+ if(offset > 32) {
+ Sector::current()->add_object(child);
+ remove_me();
+ }
+}
+
+void
+SpecialRiser::draw(DrawingContext& context)
+{
+ context.push_transform();
+ context.set_translation(
+ context.get_translation() + Vector(0, -32 + offset));
+ child->draw(context);
+ context.pop_transform();
+}
--- /dev/null
+// $Id$
+//
+// SuperTux
+// Copyright (C) 2006 Matthias Braun <matze@braunis.de>
+//
+// 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 __SPECIALRISE_H__
+#define __SPECIALRISE_H__
+
+#include "moving_object.hpp"
+
+/**
+ * special object that contains another object and slowly rises it out of a
+ * bonus block.
+ */
+class SpecialRiser : public GameObject
+{
+public:
+ SpecialRiser(Vector pos, MovingObject* child);
+ ~SpecialRiser();
+
+ virtual void update(float elapsed_time);
+ virtual void draw(DrawingContext& context);
+
+private:
+ float offset;
+ MovingObject* child;
+};
+
+#endif
--- /dev/null
+// $Id$
+//
+// SuperTux
+// Copyright (C) 2006 Ingo Ruhnke <grumbel@gmx.de>
+//
+// 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 <config.h>
+
+#include "spotlight.hpp"
+#include "sprite/sprite_manager.hpp"
+#include "resources.hpp"
+#include "video/drawing_context.hpp"
+#include "object_factory.hpp"
+#include "player.hpp"
+#include "sector.hpp"
+
+Spotlight::Spotlight(const lisp::Lisp& lisp)
+ : angle(0.0f),
+ color(1.0f, 1.0f, 1.0f)
+{
+ lisp.get("x", position.x);
+ lisp.get("y", position.y);
+
+ lisp.get("angle", angle);
+
+ std::vector<float> vColor;
+ if( lisp.get_vector( "color", vColor ) ){
+ color = Color( vColor );
+ }
+
+ center = sprite_manager->create("images/objects/spotlight/spotlight_center.sprite");
+ base = sprite_manager->create("images/objects/spotlight/spotlight_base.sprite");
+ lights = sprite_manager->create("images/objects/spotlight/spotlight_lights.sprite");
+ lightcone = sprite_manager->create("images/objects/spotlight/lightcone.sprite");
+ light = sprite_manager->create("images/objects/spotlight/light.sprite");
+
+
+}
+
+Spotlight::~Spotlight()
+{
+ delete center;
+ delete base;
+ delete lights;
+ delete lightcone;
+ delete light;
+}
+
+void
+Spotlight::update(float delta)
+{
+ angle += delta * 50.0f;
+}
+
+void
+Spotlight::draw(DrawingContext& context)
+{
+ context.push_target();
+ context.set_target(DrawingContext::LIGHTMAP);
+
+ light->set_color(color);
+ light->set_blend(Blend(GL_SRC_ALPHA, GL_ONE));
+ light->set_angle(angle);
+ light->draw(context, position, 0);
+
+ //lightcone->set_angle(angle);
+ //lightcone->draw(context, position, 0);
+
+ context.set_target(DrawingContext::NORMAL);
+
+ lights->set_angle(angle);
+ lights->draw(context, position, 0);
+
+ base->set_angle(angle);
+ base->draw(context, position, 0);
+
+ center->draw(context, position, 0);
+
+ lightcone->set_angle(angle);
+ lightcone->draw(context, position, LAYER_FOREGROUND1 + 10);
+
+ context.pop_target();
+}
+
+IMPLEMENT_FACTORY(Spotlight, "spotlight");
--- /dev/null
+// $Id$
+//
+// SuperTux
+// Copyright (C) 2006 Matthias Braun <matze@braunis.de>
+//
+// 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 __SPOTLIGHT_HPP__
+#define __SPOTLIGHT_HPP__
+
+#include "game_object.hpp"
+#include "math/vector.hpp"
+#include "lisp/lisp.hpp"
+#include "video/color.hpp"
+
+class Sprite;
+
+class Spotlight : public GameObject
+{
+public:
+ Spotlight(const lisp::Lisp& reader);
+ virtual ~Spotlight();
+
+ void update(float elapsed_time);
+ void draw(DrawingContext& context);
+
+private:
+ Vector position;
+ float angle;
+ Sprite* center;
+ Sprite* base;
+ Sprite* lights;
+ Sprite* light;
+ Sprite* lightcone;
+
+ Color color;
+};
+
+#endif
--- /dev/null
+// $Id$
+//
+// SuperTux
+// Copyright (C) 2006 Matthias Braun <matze@braunis.de>
+// Copyright (C) 2006 Christoph Sommer <christoph.sommer@2006.expires.deltadevelopment.de>
+//
+// 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 <config.h>
+
+#include <stdexcept>
+#include "sprite_particle.hpp"
+#include "sector.hpp"
+#include "camera.hpp"
+#include "main.hpp"
+#include "log.hpp"
+
+SpriteParticle::SpriteParticle(std::string sprite_name, std::string action, Vector position, AnchorPoint anchor, Vector velocity, Vector acceleration, int drawing_layer)
+ : position(position), velocity(velocity), acceleration(acceleration), drawing_layer(drawing_layer)
+{
+ sprite = sprite_manager->create(sprite_name);
+ if (!sprite) throw std::runtime_error("Could not load sprite "+sprite_name);
+ sprite->set_action(action, 1);
+ sprite->set_animation_loops(1); //TODO: this is necessary because set_action will not set "loops" when "action" is the default action
+
+ this->position -= get_anchor_pos(sprite->get_current_hitbox(), anchor);
+}
+
+SpriteParticle::~SpriteParticle()
+{
+ remove_me();
+}
+
+void
+SpriteParticle::hit(Player& )
+{
+}
+
+void
+SpriteParticle::update(float elapsed_time)
+{
+ // die when animation is complete
+ if (sprite->animation_done()) {
+ remove_me();
+ return;
+ }
+
+ // calculate new position and velocity
+ position.x += velocity.x * elapsed_time;
+ position.y += velocity.y * elapsed_time;
+ velocity.x += acceleration.x * elapsed_time;
+ velocity.y += acceleration.y * elapsed_time;
+
+ // die when too far offscreen
+ Vector camera = Sector::current()->camera->get_translation();
+ if ((position.x < camera.x - 128) || (position.x > SCREEN_WIDTH + camera.x + 128) ||
+ (position.y < camera.y - 128) || (position.y > SCREEN_HEIGHT + camera.y + 128)) {
+ remove_me();
+ return;
+ }
+}
+
+void
+SpriteParticle::draw(DrawingContext& context)
+{
+ sprite->draw(context, position, drawing_layer);
+}
--- /dev/null
+// $Id$
+//
+// SuperTux
+// Copyright (C) 2006 Matthias Braun <matze@braunis.de>
+// Copyright (C) 2006 Christoph Sommer <christoph.sommer@2006.expires.deltadevelopment.de>
+//
+// 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 __SPRITE_PARTICLE_H__
+#define __SPRITE_PARTICLE_H__
+
+
+#include "game_object.hpp"
+#include "resources.hpp"
+#include "player.hpp"
+#include "object/anchor_point.hpp"
+#include "sprite/sprite.hpp"
+#include "sprite/sprite_manager.hpp"
+#include "video/drawing_context.hpp"
+
+class SpriteParticle : public GameObject
+{
+public:
+ SpriteParticle(std::string sprite_name, std::string action, Vector position, AnchorPoint anchor, Vector velocity, Vector acceleration, int drawing_layer = LAYER_OBJECTS-1);
+ ~SpriteParticle();
+protected:
+ virtual void hit(Player& player);
+ virtual void update(float elapsed_time);
+ virtual void draw(DrawingContext& context);
+private:
+ Sprite* sprite;
+ Vector position;
+ Vector velocity;
+ Vector acceleration;
+ int drawing_layer;
+};
+
+#endif
--- /dev/null
+// $Id$
+//
+// SuperTux
+// Copyright (C) 2006 Matthias Braun <matze@braunis.de>
+//
+// 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 <config.h>
+
+#include "star.hpp"
+#include "resources.hpp"
+#include "player.hpp"
+#include "player_status.hpp"
+#include "sprite/sprite_manager.hpp"
+#include "video/drawing_context.hpp"
+
+static const float INITIALJUMP = -400;
+static const float SPEED = 150;
+static const float JUMPSPEED = -300;
+
+Star::Star(const Vector& pos, Direction direction)
+ : MovingSprite(pos, "images/powerups/star/star.sprite", LAYER_OBJECTS, COLGROUP_MOVING)
+{
+ physic.set_velocity((direction == LEFT) ? -SPEED : SPEED, INITIALJUMP);
+}
+
+void
+Star::update(float elapsed_time)
+{
+ movement = physic.get_movement(elapsed_time);
+}
+
+void
+Star::collision_solid(const CollisionHit& hit)
+{
+ if(hit.bottom) {
+ physic.set_velocity_y(JUMPSPEED);
+ } else if(hit.top) {
+ physic.set_velocity_y(0);
+ } else if(hit.left || hit.right) {
+ physic.set_velocity_x(-physic.get_velocity_x());
+ }
+}
+
+HitResponse
+Star::collision(GameObject& other, const CollisionHit& )
+{
+ Player* player = dynamic_cast<Player*> (&other);
+ if(player) {
+ player->make_invincible();
+ remove_me();
+ return ABORT_MOVE;
+ }
+
+ return FORCE_MOVE;
+}
--- /dev/null
+// $Id$
+//
+// SuperTux
+// Copyright (C) 2006 Matthias Braun <matze@braunis.de>
+//
+// 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 __STAR_H__
+#define __STAR_H__
+
+#include "object/moving_sprite.hpp"
+#include "physic.hpp"
+#include "direction.hpp"
+
+class Star : public MovingSprite, private UsesPhysic
+{
+public:
+ Star(const Vector& pos, Direction direction = RIGHT);
+ virtual Star* clone() const { return new Star(*this); }
+
+ virtual void update(float elapsed_time);
+ virtual void collision_solid(const CollisionHit& hit);
+ virtual HitResponse collision(GameObject& other, const CollisionHit& hit);
+};
+
+#endif
--- /dev/null
+// $Id$
+//
+// SuperTux
+// Copyright (C) 2006 Matthias Braun <matze@braunis.de>
+//
+// 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 <config.h>
+
+#include "text_object.hpp"
+
+#include <iostream>
+#include "resources.hpp"
+#include "main.hpp"
+#include "video/drawing_context.hpp"
+#include "scripting/squirrel_util.hpp"
+#include "log.hpp"
+
+TextObject::TextObject(std::string name)
+ : fading(0), fadetime(0), visible(false), anchor(ANCHOR_MIDDLE),
+ pos(0, 0)
+{
+ this->name = name;
+ font = blue_text;
+ centered = false;
+}
+
+TextObject::~TextObject()
+{
+}
+
+void
+TextObject::expose(HSQUIRRELVM vm, SQInteger table_idx)
+{
+ if (name.empty())
+ return;
+
+ Scripting::expose_object(vm, table_idx, dynamic_cast<Scripting::Text *>(this), name, false);
+}
+
+void
+TextObject::unexpose(HSQUIRRELVM vm, SQInteger table_idx)
+{
+ if (name.empty())
+ return;
+
+ Scripting::unexpose_object(vm, table_idx, name);
+}
+
+void
+TextObject::set_font(const std::string& name)
+{
+ if(name == "gold") {
+ font = gold_text;
+ } else if(name == "white") {
+ font = white_text;
+ } else if(name == "blue") {
+ font = blue_text;
+ } else if(name == "gray") {
+ font = gray_text;
+ } else if(name == "big") {
+ font = white_big_text;
+ } else if(name == "small") {
+ font = white_small_text;
+ } else {
+ log_warning << "Unknown font '" << name << "'." << std::endl;
+ }
+}
+
+void
+TextObject::set_text(const std::string& text)
+{
+ this->text = text;
+}
+
+void
+TextObject::fade_in(float fadetime)
+{
+ this->fadetime = fadetime;
+ fading = fadetime;
+}
+
+void
+TextObject::fade_out(float fadetime)
+{
+ this->fadetime = fadetime;
+ fading = -fadetime;
+}
+
+void
+TextObject::set_visible(bool visible)
+{
+ this->visible = visible;
+ fading = 0;
+}
+
+void
+TextObject::set_centered(bool centered)
+{
+ this->centered = centered;
+}
+
+void
+TextObject::draw(DrawingContext& context)
+{
+ context.push_transform();
+ context.set_translation(Vector(0, 0));
+ if(fading > 0) {
+ context.set_alpha((fadetime-fading) / fadetime);
+ } else if(fading < 0) {
+ context.set_alpha(-fading / fadetime);
+ } else if(!visible) {
+ context.pop_transform();
+ return;
+ }
+
+ float width = 500;
+ float height = 70;
+ Vector spos = pos + get_anchor_pos(Rect(0, 0, SCREEN_WIDTH, SCREEN_HEIGHT),
+ width, height, anchor);
+
+ context.draw_filled_rect(spos, Vector(width, height),
+ Color(0.6f, 0.7f, 0.8f, 0.5f), LAYER_GUI-50);
+ if (centered) {
+ context.draw_center_text(font, text, spos, LAYER_GUI-40);
+ } else {
+ context.draw_text(font, text, spos + Vector(10, 10), ALIGN_LEFT, LAYER_GUI-40);
+ }
+
+ context.pop_transform();
+}
+
+void
+TextObject::update(float elapsed_time)
+{
+ if(fading > 0) {
+ fading -= elapsed_time;
+ if(fading <= 0) {
+ fading = 0;
+ visible = true;
+ }
+ } else if(fading < 0) {
+ fading += elapsed_time;
+ if(fading >= 0) {
+ fading = 0;
+ visible = false;
+ }
+ }
+}
--- /dev/null
+// $Id$
+//
+// SuperTux
+// Copyright (C) 2006 Matthias Braun <matze@braunis.de>
+//
+// 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 __TEXTOBJECT_H__
+#define __TEXTOBJECT_H__
+
+#include "game_object.hpp"
+#include "scripting/text.hpp"
+#include "script_interface.hpp"
+#include "anchor_point.hpp"
+
+class Font;
+
+/** A text object intended for scripts that want to tell a story */
+class TextObject : public GameObject, public Scripting::Text,
+ public ScriptInterface
+{
+public:
+ TextObject(std::string name = "");
+ virtual ~TextObject();
+
+ void expose(HSQUIRRELVM vm, SQInteger table_idx);
+ void unexpose(HSQUIRRELVM vm, SQInteger table_idx);
+
+ void set_text(const std::string& text);
+ void set_font(const std::string& name);
+ void fade_in(float fadetime);
+ void fade_out(float fadetime);
+ void set_visible(bool visible);
+ void set_centered(bool centered);
+ bool is_visible();
+
+ void set_anchor_point(AnchorPoint anchor) {
+ this->anchor = anchor;
+ }
+ AnchorPoint get_anchor_point() const {
+ return anchor;
+ }
+
+ void set_pos(const Vector& pos) {
+ this->pos = pos;
+ }
+ void set_pos(float x, float y) {
+ set_pos(Vector(x, y));
+ }
+ const Vector& get_pos() const {
+ return pos;
+ }
+ float get_pos_x() {
+ return pos.x;
+ }
+ float get_pos_y() {
+ return pos.y;
+ }
+
+ void set_anchor_point(int anchor) {
+ set_anchor_point((AnchorPoint) anchor);
+ }
+ int get_anchor_point() {
+ return (int) get_anchor_point();
+ }
+
+ void draw(DrawingContext& context);
+ void update(float elapsed_time);
+
+private:
+ Font* font;
+ std::string text;
+ float fading;
+ float fadetime;
+ bool visible;
+ bool centered;
+ AnchorPoint anchor;
+ Vector pos;
+};
+
+#endif
--- /dev/null
+// $Id$
+//
+// SuperTux - Thunderstorm Game Object
+// Copyright (C) 2006 Christoph Sommer <christoph.sommer@2006.expires.deltadevelopment.de>
+//
+// 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 <config.h>
+
+#include "thunderstorm.hpp"
+#include "scripting/squirrel_util.hpp"
+#include "audio/sound_manager.hpp"
+#include "video/drawing_context.hpp"
+#include "object_factory.hpp"
+#include "object/electrifier.hpp"
+
+#include <stdexcept>
+#include <iostream>
+#include "main.hpp"
+#include "resources.hpp"
+#include "sector.hpp"
+#include "gettext.hpp"
+#include "object/player.hpp"
+#include "lisp/list_iterator.hpp"
+#include "log.hpp"
+
+namespace {
+ const float LIGHTNING_DELAY = 2.0f;
+ const float FLASH_DISPLAY_TIME = 0.1f;
+}
+
+Thunderstorm::Thunderstorm(const lisp::Lisp& reader)
+ : running(true), interval(10.0f)
+{
+ reader.get("name", name);
+ reader.get("running", running);
+ reader.get("interval", interval);
+ if(interval <= 0) {
+ log_warning << "Running a thunderstorm with non-positive time interval is a bad idea" << std::endl;
+ }
+
+ sound_manager->preload("sounds/explosion.wav");
+ sound_manager->preload("sounds/upgrade.wav");
+
+ if (running) {
+ running = false; // else start() is ignored
+ start();
+ }
+}
+
+void
+Thunderstorm::update(float )
+{
+ if (!running) return;
+ if (time_to_thunder.check()) {
+ thunder();
+ time_to_lightning.start(LIGHTNING_DELAY);
+ }
+ if (time_to_lightning.check()) {
+ lightning();
+ time_to_thunder.start(interval);
+ }
+}
+
+void
+Thunderstorm::draw(DrawingContext& context)
+{
+ if (!flash_display_timer.started()) return;
+
+ float alpha = 0.33f;
+ context.push_transform();
+ context.set_translation(Vector(0, 0));
+ context.draw_filled_rect(Vector(0, 0), Vector(SCREEN_WIDTH, SCREEN_HEIGHT), Color(1, 1, 1, alpha), LAYER_BACKGROUNDTILES-1);
+ context.pop_transform();
+
+}
+
+void
+Thunderstorm::expose(HSQUIRRELVM vm, SQInteger table_idx)
+{
+ if (name == "") return;
+ Scripting::Thunderstorm* interface = new Scripting::Thunderstorm(this);
+ expose_object(vm, table_idx, interface, name, true);
+}
+
+void
+Thunderstorm::unexpose(HSQUIRRELVM vm, SQInteger table_idx)
+{
+ if (name == "") return;
+ Scripting::unexpose_object(vm, table_idx, name);
+}
+
+void
+Thunderstorm::start()
+{
+ if (running) return;
+ running = true;
+ time_to_thunder.start(interval);
+ time_to_lightning.stop();
+}
+
+void
+Thunderstorm::stop()
+{
+ if (!running) return;
+ running = false;
+ time_to_thunder.stop();
+ time_to_lightning.stop();
+}
+
+void
+Thunderstorm::thunder()
+{
+ sound_manager->play("sounds/explosion.wav");
+}
+
+void
+Thunderstorm::lightning()
+{
+ flash();
+ electrify();
+}
+
+void
+Thunderstorm::flash()
+{
+ sound_manager->play("sounds/upgrade.wav");
+ sound_manager->play("sounds/explosion.wav");
+ flash_display_timer.start(FLASH_DISPLAY_TIME);
+}
+
+void
+Thunderstorm::electrify()
+{
+ Sector::current()->add_object(new Electrifier(75, 1421, 0.5));
+ Sector::current()->add_object(new Electrifier(76, 1422, 0.5));
+}
+
+IMPLEMENT_FACTORY(Thunderstorm, "thunderstorm");
--- /dev/null
+// $Id$
+//
+// SuperTux - Thunderstorm Game Object
+// Copyright (C) 2006 Christoph Sommer <christoph.sommer@2006.expires.deltadevelopment.de>
+//
+// 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 __THUNDERSTORM_H__
+#define __THUNDERSTORM_H__
+
+#include "game_object.hpp"
+#include "timer.hpp"
+#include "lisp/lisp.hpp"
+#include "scripting/thunderstorm.hpp"
+#include "script_interface.hpp"
+
+/**
+ * Thunderstorm scriptable GameObject; plays thunder, lightning and electrifies water at regular interval
+ */
+class Thunderstorm : public GameObject, public ScriptInterface
+{
+public:
+ Thunderstorm(const lisp::Lisp& reader);
+
+ void update(float elapsed_time);
+ void draw(DrawingContext& context);
+
+ virtual void expose(HSQUIRRELVM vm, SQInteger table_idx);
+ virtual void unexpose(HSQUIRRELVM vm, SQInteger table_idx);
+
+ /**
+ * @name Scriptable Methods
+ * @{
+ */
+
+ /**
+ * Start playing thunder and lightning at configured interval
+ */
+ void start();
+
+ /**
+ * Stop playing thunder and lightning at configured interval
+ */
+ void stop();
+
+ /**
+ * Play thunder
+ */
+ void thunder();
+
+ /**
+ * Play lightning, i.e. call flash() and electrify()
+ */
+ void lightning();
+
+ /**
+ * Display a nice flash
+ */
+ void flash();
+
+ /**
+ * Electrify water throughout the whole sector for a short time
+ */
+ void electrify();
+
+ /**
+ * @}
+ */
+
+private:
+ bool running; /**< whether we currently automatically trigger lightnings */
+ float interval; /**< time between two lightnings */
+
+ Timer time_to_thunder; /**< counts down until next thunder */
+ Timer time_to_lightning; /**< counts down until next lightning */
+ Timer flash_display_timer; /**< counts down while flash is displayed */
+};
+
+#endif
--- /dev/null
+// $Id$
+//
+// SuperTux
+// Copyright (C) 2006 Matthias Braun <matze@braunis.de>
+//
+// 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 <config.h>
+
+#include <cassert>
+#include <algorithm>
+#include <iostream>
+#include <stdexcept>
+#include <math.h>
+
+#include "tilemap.hpp"
+#include "video/drawing_context.hpp"
+#include "level.hpp"
+#include "tile.hpp"
+#include "resources.hpp"
+#include "tile_manager.hpp"
+#include "lisp/lisp.hpp"
+#include "lisp/writer.hpp"
+#include "object_factory.hpp"
+#include "main.hpp"
+#include "log.hpp"
+#include "scripting/tilemap.hpp"
+#include "scripting/squirrel_util.hpp"
+
+TileMap::TileMap()
+ : solid(false), speed_x(1), speed_y(1), width(0), height(0), z_pos(0), x_offset(0), y_offset(0),
+ drawing_effect(NO_EFFECT), alpha(1.0), current_alpha(1.0), remaining_fade_time(0),
+ draw_target(DrawingContext::NORMAL)
+{
+ tilemanager = tile_manager;
+}
+
+TileMap::TileMap(const lisp::Lisp& reader, TileManager* new_tile_manager)
+ : solid(false), speed_x(1), speed_y(1), width(-1), height(-1), z_pos(0),
+ x_offset(0), y_offset(0),
+ drawing_effect(NO_EFFECT), alpha(1.0), current_alpha(1.0),
+ remaining_fade_time(0),
+ draw_target(DrawingContext::NORMAL)
+{
+ tilemanager = new_tile_manager;
+ if(tilemanager == 0)
+ tilemanager = tile_manager;
+
+ reader.get("name", name);
+ reader.get("z-pos", z_pos);
+ reader.get("solid", solid);
+ reader.get("speed", speed_x);
+ reader.get("speed-y", speed_y);
+
+ if(solid && ((speed_x != 1) || (speed_y != 1))) {
+ log_warning << "Speed of solid tilemap is not 1. fixing" << std::endl;
+ speed_x = 1;
+ speed_y = 1;
+ }
+
+ const lisp::Lisp* pathLisp = reader.get_lisp("path");
+ if (pathLisp) {
+ path.reset(new Path());
+ path->read(*pathLisp);
+ walker.reset(new PathWalker(path.get(), /*running*/false));
+ Vector v = path->get_base();
+ set_x_offset(v.x);
+ set_y_offset(v.y);
+ }
+
+ std::string draw_target_s = "normal";
+ reader.get("draw-target", draw_target_s);
+ if (draw_target_s == "normal") draw_target = DrawingContext::NORMAL;
+ if (draw_target_s == "lightmap") draw_target = DrawingContext::LIGHTMAP;
+
+ if (reader.get("alpha", alpha)) {
+ current_alpha = alpha;
+ }
+
+ reader.get("width", width);
+ reader.get("height", height);
+ if(width < 0 || height < 0)
+ throw std::runtime_error("Invalid/No width/height specified in tilemap.");
+
+ if(!reader.get_vector("tiles", tiles))
+ throw std::runtime_error("No tiles in tilemap.");
+
+ if(int(tiles.size()) != width*height) {
+ throw std::runtime_error("wrong number of tiles in tilemap.");
+ }
+
+ // make sure all tiles are loaded
+ for(Tiles::iterator i = tiles.begin(); i != tiles.end(); ++i)
+ tilemanager->get(*i);
+}
+
+TileMap::TileMap(std::string name, int z_pos, bool solid, size_t width, size_t height)
+ : solid(solid), speed_x(1), speed_y(1), width(0), height(0), z_pos(z_pos),
+ x_offset(0), y_offset(0), drawing_effect(NO_EFFECT), alpha(1.0),
+ current_alpha(1.0), remaining_fade_time(0),
+ draw_target(DrawingContext::NORMAL)
+{
+ this->name = name;
+ tilemanager = tile_manager;
+
+ resize(width, height);
+}
+
+TileMap::~TileMap()
+{
+}
+
+void
+TileMap::write(lisp::Writer& writer)
+{
+ writer.start_list("tilemap");
+
+ writer.write_int("z-pos", z_pos);
+
+ writer.write_bool("solid", solid);
+ writer.write_float("speed", speed_x);
+ writer.write_float("speed-y", speed_y);
+ writer.write_int("width", width);
+ writer.write_int("height", height);
+ writer.write_int_vector("tiles", tiles);
+
+ writer.end_list("tilemap");
+}
+
+void
+TileMap::update(float elapsed_time)
+{
+ // handle tilemap fading
+ if (current_alpha != alpha) {
+ remaining_fade_time = std::max(0.0f, remaining_fade_time - elapsed_time);
+ if (remaining_fade_time == 0.0f) {
+ current_alpha = alpha;
+ } else {
+ float amt = (alpha - current_alpha) / (remaining_fade_time / elapsed_time);
+ if (amt > 0) current_alpha = std::min(current_alpha + amt, alpha);
+ if (amt < 0) current_alpha = std::max(current_alpha + amt, alpha);
+ }
+ if ((alpha < 0.25) && (current_alpha < 0.25)) set_solid(false);
+ if ((alpha > 0.75) && (current_alpha > 0.75)) set_solid(true);
+ }
+
+ // if we have a path to follow, follow it
+ if (walker.get()) {
+ Vector v = walker->advance(elapsed_time);
+ set_x_offset(v.x);
+ set_y_offset(v.y);
+ }
+}
+
+void
+TileMap::draw(DrawingContext& context)
+{
+ // skip draw if current opacity is set to 0.0
+ if (current_alpha == 0.0) return;
+
+ context.push_transform();
+ context.push_target();
+ context.set_target(draw_target);
+
+ if(drawing_effect != 0) context.set_drawing_effect(drawing_effect);
+ if(current_alpha != 1.0) context.set_alpha(current_alpha);
+
+ float trans_x = roundf(context.get_translation().x);
+ float trans_y = roundf(context.get_translation().y);
+ context.set_translation(Vector(trans_x * speed_x, trans_y * speed_y));
+
+ /** if we don't round here, we'll have a 1 pixel gap on screen sometimes.
+ * I have no idea why */
+ float start_x = int((roundf(context.get_translation().x) - roundf(x_offset)) / 32) * 32 + roundf(x_offset);
+ float start_y = int((roundf(context.get_translation().y) - roundf(y_offset)) / 32) * 32 + roundf(y_offset);
+ float end_x = std::min(start_x + SCREEN_WIDTH + 32, float(width * 32 + roundf(x_offset)));
+ float end_y = std::min(start_y + SCREEN_HEIGHT + 32, float(height * 32 + roundf(y_offset)));
+ int tsx = int((start_x - roundf(x_offset)) / 32); // tilestartindex x
+ int tsy = int((start_y - roundf(y_offset)) / 32); // tilestartindex y
+
+ Vector pos;
+ int tx, ty;
+ for(pos.x = start_x, tx = tsx; pos.x < end_x; pos.x += 32, ++tx) {
+ for(pos.y = start_y, ty = tsy; pos.y < end_y; pos.y += 32, ++ty) {
+ if ((tx < 0) || (ty < 0)) continue;
+ const Tile* tile = tilemanager->get(tiles[ty*width + tx]);
+ assert(tile != 0);
+ tile->draw(context, pos, z_pos);
+ }
+ }
+
+ context.pop_target();
+ context.pop_transform();
+}
+
+void
+TileMap::goto_node(int node_no)
+{
+ if (!walker.get()) return;
+ walker->goto_node(node_no);
+}
+
+void
+TileMap::start_moving()
+{
+ if (!walker.get()) return;
+ walker->start_moving();
+}
+
+void
+TileMap::stop_moving()
+{
+ if (!walker.get()) return;
+ walker->stop_moving();
+}
+
+void
+TileMap::expose(HSQUIRRELVM vm, SQInteger table_idx)
+{
+ if (name.empty()) return;
+ Scripting::TileMap* interface = new Scripting::TileMap(this);
+ expose_object(vm, table_idx, interface, name, true);
+}
+
+void
+TileMap::unexpose(HSQUIRRELVM vm, SQInteger table_idx)
+{
+ if (name.empty()) return;
+ Scripting::unexpose_object(vm, table_idx, name);
+}
+
+void
+TileMap::set(int newwidth, int newheight, const std::vector<unsigned int>&newt,
+ int new_z_pos, bool newsolid)
+{
+ if(int(newt.size()) != newwidth * newheight)
+ throw std::runtime_error("Wrong tilecount count.");
+
+ width = newwidth;
+ height = newheight;
+
+ tiles.resize(newt.size());
+ tiles = newt;
+
+ z_pos = new_z_pos;
+ solid = newsolid;
+
+ // make sure all tiles are loaded
+ for(Tiles::iterator i = tiles.begin(); i != tiles.end(); ++i)
+ tilemanager->get(*i);
+}
+
+void
+TileMap::resize(int new_width, int new_height, int fill_id)
+{
+ if(new_width < width) {
+ // remap tiles for new width
+ for(int y = 0; y < height && y < new_height; ++y) {
+ for(int x = 0; x < new_width; ++x) {
+ tiles[y * new_width + x] = tiles[y * width + x];
+ }
+ }
+ }
+
+ tiles.resize(new_width * new_height, fill_id);
+
+ if(new_width > width) {
+ // remap tiles
+ for(int y = std::min(height, new_height)-1; y >= 0; --y) {
+ for(int x = new_width-1; x >= 0; --x) {
+ if(x >= width) {
+ tiles[y * new_width + x] = fill_id;
+ continue;
+ }
+
+ tiles[y * new_width + x] = tiles[y * width + x];
+ }
+ }
+ }
+
+ height = new_height;
+ width = new_width;
+}
+
+void
+TileMap::set_solid(bool solid)
+{
+ this->solid = solid;
+}
+
+const Tile*
+TileMap::get_tile(int x, int y) const
+{
+ if(x < 0 || x >= width || y < 0 || y >= height) {
+ //log_warning << "tile outside tilemap requested" << std::endl;
+ return tilemanager->get(0);
+ }
+
+ return tilemanager->get(tiles[y*width + x]);
+}
+
+const Tile*
+TileMap::get_tile_at(const Vector& pos) const
+{
+ return get_tile(int(pos.x - x_offset)/32, int(pos.y - y_offset)/32);
+}
+
+void
+TileMap::change(int x, int y, uint32_t newtile)
+{
+ assert(x >= 0 && x < width && y >= 0 && y < height);
+ tiles[y*width + x] = newtile;
+}
+
+void
+TileMap::change_at(const Vector& pos, uint32_t newtile)
+{
+ change(int(pos.x - x_offset)/32, int(pos.y - y_offset)/32, newtile);
+}
+
+void
+TileMap::change_all(uint32_t oldtile, uint32_t newtile)
+{
+ for (size_t x = 0; x < get_width(); x++)
+ for (size_t y = 0; y < get_height(); y++) {
+ if (get_tile(x,y)->getID() == oldtile) change(x,y,newtile);
+ }
+}
+
+void
+TileMap::fade(float alpha, float seconds)
+{
+ this->alpha = alpha;
+ this->remaining_fade_time = seconds;
+}
+
+
+void
+TileMap::set_alpha(float alpha)
+{
+ this->alpha = alpha;
+ this->current_alpha = alpha;
+ this->remaining_fade_time = 0;
+ if (current_alpha < 0.25) set_solid(false);
+ if (current_alpha > 0.75) set_solid(true);
+}
+
+float
+TileMap::get_alpha()
+{
+ return this->current_alpha;
+}
+
+IMPLEMENT_FACTORY(TileMap, "tilemap");
--- /dev/null
+// $Id$
+//
+// SuperTux
+// Copyright (C) 2006 Matthias Braun <matze@braunis.de>
+//
+// 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_TILEMAP_H
+#define SUPERTUX_TILEMAP_H
+
+#include <vector>
+#include <stdint.h>
+#include <string>
+
+#include "game_object.hpp"
+#include "serializable.hpp"
+#include "math/vector.hpp"
+#include "video/drawing_context.hpp"
+#include "object/path.hpp"
+#include "object/path_walker.hpp"
+#include "script_interface.hpp"
+
+namespace lisp {
+class Lisp;
+}
+
+class Level;
+class TileManager;
+class Tile;
+
+/**
+ * This class is reponsible for drawing the level tiles
+ */
+class TileMap : public GameObject, public Serializable, public ScriptInterface
+{
+public:
+ TileMap();
+ TileMap(const lisp::Lisp& reader, TileManager* tile_manager = 0);
+ TileMap(std::string name, int z_pos, bool solid_, size_t width_, size_t height_);
+ virtual ~TileMap();
+
+ virtual void write(lisp::Writer& writer);
+
+ virtual void update(float elapsed_time);
+ virtual void draw(DrawingContext& context);
+
+ /** Move tilemap until at given node, then stop */
+ void goto_node(int node_no);
+
+ /** Start moving tilemap */
+ void start_moving();
+
+ /** Stop tilemap at next node */
+ void stop_moving();
+
+ virtual void expose(HSQUIRRELVM vm, SQInteger table_idx);
+ virtual void unexpose(HSQUIRRELVM vm, SQInteger table_idx);
+
+ void set(int width, int height, const std::vector<unsigned int>& vec,
+ int z_pos, bool solid);
+
+ /** resizes the tilemap to a new width and height (tries to not destroy the
+ * existing map)
+ */
+ void resize(int newwidth, int newheight, int fill_id = 0);
+
+ size_t get_width() const
+ { return width; }
+
+ size_t get_height() const
+ { return height; }
+
+ float get_x_offset() const
+ { return x_offset; }
+
+ float get_y_offset() const
+ { return y_offset; }
+
+ void set_x_offset(float x_offset)
+ { this->x_offset = x_offset; }
+
+ void set_y_offset(float y_offset)
+ { this->y_offset = y_offset; }
+
+ int get_layer() const
+ { return z_pos; }
+
+ bool is_solid() const
+ { return solid; }
+
+ /**
+ * Changes Tilemap's solidity, i.e. whether to consider it when doing collision detection.
+ */
+ void set_solid(bool solid = true);
+
+ /// returns tile in row y and column y (of the tilemap)
+ const Tile* get_tile(int x, int y) const;
+ /// returns tile at position pos (in world coordinates)
+ const Tile* get_tile_at(const Vector& pos) const;
+
+ void change(int x, int y, uint32_t newtile);
+
+ void change_at(const Vector& pos, uint32_t newtile);
+
+ /// changes all tiles with the given ID
+ void change_all(uint32_t oldtile, uint32_t newtile);
+
+ TileManager* get_tilemanager() const
+ {
+ return tilemanager;
+ }
+
+ void set_drawing_effect(DrawingEffect effect)
+ {
+ drawing_effect = effect;
+ }
+
+ DrawingEffect get_drawing_effect()
+ {
+ return drawing_effect;
+ }
+
+ /**
+ * Start fading the tilemap to opacity given by @c alpha.
+ * Destination opacity will be reached after @c seconds seconds. Also influences solidity.
+ */
+ void fade(float alpha, float seconds = 0);
+
+ /**
+ * Instantly switch tilemap's opacity to @c alpha. Also influences solidity.
+ */
+ void set_alpha(float alpha);
+
+ /**
+ * Return tilemap's opacity. Note that while the tilemap is fading in or out, this will return the current alpha value, not the target alpha.
+ */
+ float get_alpha();
+
+private:
+ typedef std::vector<uint32_t> Tiles;
+ Tiles tiles;
+
+private:
+ TileManager* tilemanager;
+ bool solid;
+ float speed_x;
+ float speed_y;
+ int width, height;
+ int z_pos;
+ float x_offset;
+ float y_offset;
+
+ DrawingEffect drawing_effect;
+ float alpha; /**< requested tilemap opacity */
+ float current_alpha; /**< current tilemap opacity */
+ float remaining_fade_time; /**< seconds until requested tilemap opacity is reached */
+
+ std::auto_ptr<Path> path;
+ std::auto_ptr<PathWalker> walker;
+
+ DrawingContext::Target draw_target; /**< set to LIGHTMAP to draw to lightmap */
+};
+
+#endif /*SUPERTUX_TILEMAP_H*/
--- /dev/null
+// $Id$
+//
+// SuperTux - Trampoline
+// Copyright (C) 2006 Wolfgang Becker <uafr@gmx.de>
+//
+// 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 <config.h>
+
+#include "trampoline.hpp"
+#include "object_factory.hpp"
+#include "player.hpp"
+#include "audio/sound_manager.hpp"
+#include "sprite/sprite_manager.hpp"
+#include "badguy/walking_badguy.hpp"
+
+/* Trampoline will accelerate player to to VY_INITIAL, if
+ * he jumps on it to VY_MIN. */
+namespace {
+ const std::string TRAMPOLINE_SOUND = "sounds/trampoline.wav";
+ const float VY_MIN = -900; //negative, upwards
+ const float VY_INITIAL = -500;
+}
+
+Trampoline::Trampoline(const lisp::Lisp& lisp)
+ : Rock(lisp, "images/objects/trampoline/trampoline.sprite")
+{
+ sound_manager->preload(TRAMPOLINE_SOUND);
+
+ portable = true;
+ //Check if this trampoline is not portable
+ if(lisp.get("portable", portable)) {
+ if(!portable) {
+ //we need another sprite
+ sprite_name = "images/objects/trampoline/trampoline_fix.sprite";
+ sprite = sprite_manager->create(sprite_name);
+ sprite->set_action("normal");
+ }
+ }
+}
+
+void
+Trampoline::update(float elapsed_time)
+{
+ if(sprite->animation_done()) {
+ sprite->set_action("normal");
+ }
+
+ Rock::update(elapsed_time);
+}
+
+HitResponse
+Trampoline::collision(GameObject& other, const CollisionHit& hit)
+{
+
+ //Tramponine has to be on ground to work.
+ if(on_ground) {
+ Player* player = dynamic_cast<Player*> (&other);
+ //Trampoline works for player
+ if(player) {
+ float vy = player->physic.get_velocity_y();
+ //player is falling down on trampoline
+ if(hit.top && vy >= 0) {
+ if(player->get_controller()->hold(Controller::JUMP)) {
+ vy = VY_MIN;
+ } else {
+ vy = VY_INITIAL;
+ }
+ player->physic.set_velocity_y(vy);
+ sound_manager->play(TRAMPOLINE_SOUND);
+ sprite->set_action("swinging", 1);
+ return FORCE_MOVE;
+ }
+ }
+ WalkingBadguy* walking_badguy = dynamic_cast<WalkingBadguy*> (&other);
+ //Trampoline also works for WalkingBadguy
+ if(walking_badguy) {
+ float vy = walking_badguy->get_velocity_y();
+ //walking_badguy is falling down on trampoline
+ if(hit.top && vy >= 0) {
+ vy = VY_INITIAL;
+ walking_badguy->set_velocity_y(vy);
+ sound_manager->play(TRAMPOLINE_SOUND);
+ sprite->set_action("swinging", 1);
+ return FORCE_MOVE;
+ }
+ }
+ }
+
+ return Rock::collision(other, hit);
+}
+
+void
+Trampoline::collision_solid(const CollisionHit& hit) {
+ Rock::collision_solid(hit);
+}
+
+void
+Trampoline::grab(MovingObject& object, const Vector& pos, Direction dir) {
+ sprite->set_animation_loops(0);
+ Rock::grab(object, pos, dir);
+}
+
+void
+Trampoline::ungrab(MovingObject& object, Direction dir) {
+ Rock::ungrab(object, dir);
+}
+
+bool
+Trampoline::is_portable() const
+{
+ return Rock::is_portable() && portable;
+}
+
+IMPLEMENT_FACTORY(Trampoline, "trampoline");
--- /dev/null
+// $Id$
+//
+// SuperTux - Trampolin
+// Copyright (C) 2006 Wolfgang Becker <uafr@gmx.de>
+//
+// 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_TRAMPOLINE_H
+#define SUPERTUX_TRAMPOLINE_H
+
+#include "moving_sprite.hpp"
+#include "lisp/lisp.hpp"
+#include "object/rock.hpp"
+
+/**
+ * Jumping on a trampolin makes tux jump higher.
+ */
+class Trampoline : public Rock
+{
+public:
+ Trampoline(const lisp::Lisp& reader);
+
+ HitResponse collision(GameObject& other, const CollisionHit& hit);
+ void collision_solid(const CollisionHit& hit);
+ void update(float elapsed_time);
+
+ void grab(MovingObject&, const Vector& pos, Direction);
+ void ungrab(MovingObject&, Direction);
+ bool is_portable() const;
+
+private:
+ bool portable;
+
+};
+
+#endif
--- /dev/null
+// $Id$
+//
+// SuperTux - Unstable Tile
+// Copyright (C) 2006 Matthias Braun <matze@braunis.de>
+// Copyright (C) 2006 Christoph Sommer <christoph.sommer@2006.expires.deltadevelopment.de>
+//
+// 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 <config.h>
+
+#include "unstable_tile.hpp"
+#include "lisp/lisp.hpp"
+#include "object_factory.hpp"
+#include "player.hpp"
+#include "sector.hpp"
+#include "resources.hpp"
+#include "sprite/sprite.hpp"
+#include "random_generator.hpp"
+#include "object/bullet.hpp"
+
+UnstableTile::UnstableTile(const lisp::Lisp& lisp)
+ : MovingSprite(lisp, LAYER_TILES, COLGROUP_STATIC), state(STATE_NORMAL)
+{
+ sprite->set_action("normal");
+}
+
+HitResponse
+UnstableTile::collision(GameObject& other, const CollisionHit& )
+{
+ if(state == STATE_NORMAL) {
+ Player* player = dynamic_cast<Player*> (&other);
+ if(player != NULL &&
+ player->get_bbox().get_bottom() < get_bbox().get_top() + 7.0) {
+ state = STATE_CRUMBLING;
+ sprite->set_action("crumbling", 1);
+ }
+ }
+ return SOLID;
+}
+
+void
+UnstableTile::update(float elapsed_time)
+{
+ switch (state) {
+
+ case STATE_NORMAL:
+ break;
+
+ case STATE_CRUMBLING:
+ if (sprite->animation_done()) {
+ state = STATE_DISINTEGRATING;
+ sprite->set_action("disintegrating", 1);
+ set_group(COLGROUP_DISABLED);
+ physic.enable_gravity(true);
+ }
+ break;
+
+ case STATE_DISINTEGRATING:
+ movement = physic.get_movement(elapsed_time);
+ if (sprite->animation_done()) {
+ remove_me();
+ return;
+ }
+ break;
+ }
+}
+
+IMPLEMENT_FACTORY(UnstableTile, "unstable_tile");
--- /dev/null
+// $Id$
+//
+// SuperTux - Unstable Tile
+// Copyright (C) 2006 Matthias Braun <matze@braunis.de>
+// Copyright (C) 2006 Christoph Sommer <christoph.sommer@2006.expires.deltadevelopment.de>
+//
+// 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 __UNSTABLE_TILE_H__
+#define __UNSTABLE_TILE_H__
+
+#include "object/moving_sprite.hpp"
+#include "lisp/lisp.hpp"
+#include "physic.hpp"
+#include "timer.hpp"
+
+/**
+ * A block that disintegrates when stood on
+ */
+class UnstableTile : public MovingSprite, public UsesPhysic
+{
+public:
+ UnstableTile(const lisp::Lisp& lisp);
+ virtual UnstableTile* clone() const { return new UnstableTile(*this); }
+
+ HitResponse collision(GameObject& other, const CollisionHit& hit);
+ void update(float elapsed_time);
+
+private:
+ enum State {
+ STATE_NORMAL, /**< default state */
+ STATE_CRUMBLING, /**< crumbling, still solid */
+ STATE_DISINTEGRATING /**< disintegrating, no longer solid */
+ };
+ State state;
+};
+
+#endif
--- /dev/null
+// $Id$
+//
+// SuperTux - Weak Block
+// Copyright (C) 2006 Matthias Braun <matze@braunis.de>
+// Copyright (C) 2006 Christoph Sommer <christoph.sommer@2006.expires.deltadevelopment.de>
+//
+// 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 <config.h>
+
+#include "weak_block.hpp"
+#include "lisp/lisp.hpp"
+#include "object_factory.hpp"
+#include "player.hpp"
+#include "sector.hpp"
+#include "resources.hpp"
+#include "sprite/sprite.hpp"
+#include "random_generator.hpp"
+#include "object/bullet.hpp"
+
+WeakBlock::WeakBlock(const lisp::Lisp& lisp)
+ : MovingSprite(lisp, "images/objects/strawbox/strawbox.sprite", LAYER_TILES, COLGROUP_STATIC), state(STATE_NORMAL)
+{
+ sprite->set_action("normal");
+}
+
+HitResponse
+WeakBlock::collision(GameObject& other, const CollisionHit& )
+{
+ switch (state) {
+
+ case STATE_NORMAL:
+ if (dynamic_cast<Bullet*>(&other)) {
+ startBurning();
+ return FORCE_MOVE;
+ }
+ return FORCE_MOVE;
+ break;
+
+ case STATE_BURNING:
+ return FORCE_MOVE;
+ break;
+
+ case STATE_DISINTEGRATING:
+ return FORCE_MOVE;
+ break;
+
+ }
+
+ log_debug << "unhandled state" << std::endl;
+ return FORCE_MOVE;
+}
+
+void
+WeakBlock::update(float )
+{
+ switch (state) {
+
+ case STATE_NORMAL:
+ break;
+
+ case STATE_BURNING:
+ if (sprite->animation_done()) {
+ state = STATE_DISINTEGRATING;
+ sprite->set_action("disintegrating", 1);
+ spreadHit();
+ set_group(COLGROUP_DISABLED);
+ }
+ break;
+
+ case STATE_DISINTEGRATING:
+ if (sprite->animation_done()) {
+ remove_me();
+ return;
+ }
+ break;
+
+ }
+}
+
+void
+WeakBlock::startBurning()
+{
+ if (state != STATE_NORMAL) return;
+ state = STATE_BURNING;
+ sprite->set_action("burning", 1);
+}
+
+void
+WeakBlock::spreadHit()
+{
+ Sector* sector = Sector::current();
+ if (!sector) {
+ log_debug << "no current sector" << std::endl;
+ return;
+ }
+ for(Sector::GameObjects::iterator i = sector->gameobjects.begin(); i != sector->gameobjects.end(); ++i) {
+ WeakBlock* wb = dynamic_cast<WeakBlock*>(*i);
+ if (!wb) continue;
+ if (wb == this) continue;
+ if (wb->state != STATE_NORMAL) continue;
+ float dx = fabsf(wb->get_pos().x - this->get_pos().x);
+ float dy = fabsf(wb->get_pos().y - this->get_pos().y);
+ if ((dx <= 32.5) && (dy <= 32.5)) wb->startBurning();
+ }
+}
+
+
+IMPLEMENT_FACTORY(WeakBlock, "weak_block");
--- /dev/null
+// $Id$
+//
+// SuperTux - Weak Block
+// Copyright (C) 2006 Matthias Braun <matze@braunis.de>
+// Copyright (C) 2006 Christoph Sommer <christoph.sommer@2006.expires.deltadevelopment.de>
+//
+// 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 __WEAK_BLOCK_H__
+#define __WEAK_BLOCK_H__
+
+#include "object/moving_sprite.hpp"
+#include "lisp/lisp.hpp"
+#include "physic.hpp"
+#include "timer.hpp"
+
+/**
+ * A block that can be destroyed by Bullet hits
+ */
+class WeakBlock : public MovingSprite, public UsesPhysic
+{
+public:
+ WeakBlock(const lisp::Lisp& lisp);
+
+ HitResponse collision(GameObject& other, const CollisionHit& hit);
+ void update(float elapsed_time);
+
+protected:
+ /**
+ * called by self when hit by a bullet
+ */
+ void startBurning();
+
+ /**
+ * pass hit to nearby WeakBlock objects
+ */
+ void spreadHit();
+
+private:
+ enum State {
+ STATE_NORMAL, /**< default state */
+ STATE_BURNING, /**< on fire, still solid */
+ STATE_DISINTEGRATING /**< crumbling to dust, no longer solid */
+ };
+ State state;
+};
+
+#endif
--- /dev/null
+// $Id$
+//
+// SuperTux - Wind
+// Copyright (C) 2006 Christoph Sommer <christoph.sommer@2006.expires.deltadevelopment.de>
+//
+// 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 <config.h>
+
+#include "wind.hpp"
+#include "video/drawing_context.hpp"
+#include "object/player.hpp"
+#include "object_factory.hpp"
+#include "random_generator.hpp"
+#include "sector.hpp"
+#include "particles.hpp"
+#include "scripting/wind.hpp"
+#include "scripting/squirrel_util.hpp"
+
+Wind::Wind(const lisp::Lisp& reader)
+ : blowing(true), acceleration(100), elapsed_time(0)
+{
+ reader.get("name", name);
+ reader.get("x", bbox.p1.x);
+ reader.get("y", bbox.p1.y);
+ float w = 32, h = 32;
+ reader.get("width", w);
+ reader.get("height", h);
+ bbox.set_size(w, h);
+
+ reader.get("blowing", blowing);
+
+ float speed_x = 0, speed_y = 0;
+ reader.get("speed-x", speed_x);
+ reader.get("speed-y", speed_y);
+ speed = Vector(speed_x, speed_y);
+
+ reader.get("acceleration", acceleration);
+
+ set_group(COLGROUP_TOUCHABLE);
+}
+
+void
+Wind::update(float elapsed_time)
+{
+ this->elapsed_time = elapsed_time;
+
+ if (!blowing) return;
+
+ // TODO: nicer, configurable particles for wind?
+ if (systemRandom.rand(0, 100) < 20) {
+ // emit a particle
+ Vector ppos = Vector(systemRandom.randf(bbox.p1.x+8, bbox.p2.x-8), systemRandom.randf(bbox.p1.y+8, bbox.p2.y-8));
+ Vector pspeed = Vector(speed.x, speed.y);
+ Sector::current()->add_object(new Particles(ppos, 44, 46, pspeed, Vector(0,0), 1, Color(.4f, .4f, .4f), 3, .1f, LAYER_BACKGROUNDTILES+1));
+ }
+}
+
+void
+Wind::draw(DrawingContext& )
+{
+}
+
+HitResponse
+Wind::collision(GameObject& other, const CollisionHit& )
+{
+ if (!blowing) return ABORT_MOVE;
+
+ Player* player = dynamic_cast<Player*> (&other);
+ if (player) {
+ if (!player->on_ground()) {
+ player->add_velocity(speed * acceleration * elapsed_time, speed);
+ }
+ }
+
+ return ABORT_MOVE;
+}
+
+void
+Wind::expose(HSQUIRRELVM vm, SQInteger table_idx)
+{
+ if (name == "")
+ return;
+
+ Scripting::Wind* interface = new Scripting::Wind(this);
+ expose_object(vm, table_idx, interface, name, true);
+}
+
+void
+Wind::unexpose(HSQUIRRELVM vm, SQInteger table_idx)
+{
+ if (name == "")
+ return;
+
+ Scripting::unexpose_object(vm, table_idx, name);
+}
+
+void
+Wind::start()
+{
+ blowing = true;
+}
+
+void
+Wind::stop()
+{
+ blowing = false;
+}
+
+IMPLEMENT_FACTORY(Wind, "wind");
--- /dev/null
+// $Id$
+//
+// SuperTux - Wind
+// Copyright (C) 2006 Christoph Sommer <christoph.sommer@2006.expires.deltadevelopment.de>
+//
+// 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_WIND_H
+#define SUPERTUX_WIND_H
+
+#include <set>
+#include "moving_object.hpp"
+#include "math/rect.hpp"
+#include "sprite/sprite.hpp"
+#include "script_interface.hpp"
+
+class Player;
+
+/**
+ * Defines an area that will gently push Players in one direction
+ */
+class Wind : public MovingObject, public ScriptInterface
+{
+public:
+ Wind(const lisp::Lisp& reader);
+
+ void update(float elapsed_time);
+ void draw(DrawingContext& context);
+ HitResponse collision(GameObject& other, const CollisionHit& hit);
+
+ /**
+ * @name Scriptable Methods
+ * @{
+ */
+
+ /**
+ * start blowing
+ */
+ void start();
+
+ /**
+ * stop blowing
+ */
+ void stop();
+
+ /**
+ * @}
+ */
+
+ virtual void expose(HSQUIRRELVM vm, SQInteger table_idx);
+ virtual void unexpose(HSQUIRRELVM vm, SQInteger table_idx);
+
+private:
+ bool blowing; /**< true if wind is currently switched on */
+ Vector speed;
+ float acceleration;
+
+ float elapsed_time; /**< stores last elapsed_time gotten at update() */
+};
+
+#endif
--- /dev/null
+// $Id$
+//
+// SuperTux
+// Copyright (C) 2004 Ricardo Cruz <rick2@aeiou.pt>
+// Copyright (C) 2006 Matthias Braun <matze@braunis.de>
+//
+// 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 <config.h>
+
+#include <sstream>
+#include <stdexcept>
+
+#include "lisp/lisp.hpp"
+#include "lisp/parser.hpp"
+#include "object_factory.hpp"
+#include "math/vector.hpp"
+
+GameObject* create_object(const std::string& name, const lisp::Lisp& reader)
+{
+ Factory::Factories::iterator i = Factory::get_factories().find(name);
+ if(i == Factory::get_factories().end()) {
+ std::stringstream msg;
+ msg << "No factory for object '" << name << "' found.";
+ throw std::runtime_error(msg.str());
+ }
+
+ return i->second->create_object(reader);
+}
+
+GameObject* create_object(const std::string& name, const Vector& pos)
+{
+ std::stringstream lisptext;
+ lisptext << "(" << name
+ << " (x " << pos.x << ")"
+ << " (y " << pos.y << "))";
+
+ lisp::Parser parser;
+ const lisp::Lisp* lisp = parser.parse(lisptext, "create_object");
+ GameObject* object = create_object(name, *lisp);
+
+ return object;
+}
--- /dev/null
+// $Id$
+//
+// SuperTux
+// Copyright (C) 2004 Ricardo Cruz <rick2@aeiou.pt>
+// Copyright (C) 2006 Matthias Braun <matze@braunis.de>
+//
+// 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 OBJECT_FACTORY_H
+#define OBJECT_FACTORY_H
+
+#include <string>
+#include <map>
+
+namespace lisp { class Lisp; }
+class Vector;
+class GameObject;
+
+class Factory
+{
+public:
+ virtual ~Factory()
+ { }
+
+ /** Creates a new gameobject from a lisp node.
+ * Remember to delete the objects later
+ */
+ virtual GameObject* create_object(const lisp::Lisp& reader) = 0;
+
+ typedef std::map<std::string, Factory*> Factories;
+ static Factories &get_factories()
+ {
+ static Factories object_factories;
+ return object_factories;
+ }
+};
+
+GameObject* create_object(const std::string& name, const lisp::Lisp& reader);
+GameObject* create_object(const std::string& name, const Vector& pos);
+
+/** comment from Matze:
+ * Yes I know macros are evil, but in this specific case they save
+ * A LOT of typing and evil code duplication.
+ * I'll happily accept alternatives if someone can present me one that does
+ * not involve typing 4 or more lines for each object class
+ */
+#define IMPLEMENT_FACTORY(CLASS, NAME) \
+class INTERN_##CLASS##Factory : public Factory \
+{ \
+public: \
+ INTERN_##CLASS##Factory() \
+ { \
+ get_factories()[NAME] = this; \
+ } \
+ \
+ ~INTERN_##CLASS##Factory() \
+ { \
+ get_factories().erase(NAME); \
+ } \
+ \
+ virtual GameObject* create_object(const lisp::Lisp& reader) \
+ { \
+ return new CLASS(reader); \
+ } \
+}; \
+static INTERN_##CLASS##Factory factory_##CLASS;
+
+#endif
--- /dev/null
+// $Id$
+//
+// SuperTux
+// Copyright (C) 2006 Matthias Braun <matze@braunis.de>
+//
+// 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 __OBJECT_REMOVE_LISTENER_H__
+#define __OBJECT_REMOVE_LISTENER_H__
+
+class GameObject;
+
+class ObjectRemoveListener
+{
+public:
+ virtual ~ObjectRemoveListener()
+ {}
+
+ virtual void object_removed(GameObject* object) = 0;
+};
+
+#endif
--- /dev/null
+/* obstack.c - subroutines used implicitly by object stack macros
+ Copyright (C) 1988, 1989, 1990, 1991, 1992, 1993, 1994, 1996, 1997, 1998,
+ 1999, 2000, 2001, 2002, 2003, 2004, 2005 Free Software Foundation, Inc.
+ This file is part of the GNU C Library.
+
+ The GNU C Library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Lesser General Public
+ License as published by the Free Software Foundation; either
+ version 2.1 of the License, or (at your option) any later version.
+
+ The GNU C 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
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public
+ License along with the GNU C Library; if not, write to the Free
+ Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ Boston, MA 02110-1301, USA. */
+#ifdef HAVE_CONFIG_H
+# include <config.h>
+#endif
+
+#include "obstack.h"
+
+/* NOTE BEFORE MODIFYING THIS FILE: This version number must be
+ incremented whenever callers compiled using an old obstack.h can no
+ longer properly call the functions in this obstack.c. */
+#define OBSTACK_INTERFACE_VERSION 1
+
+#include <stdio.h> /* Random thing to get __GNU_LIBRARY__. */
+#include <stddef.h>
+#include <stdint.h>
+
+/* Determine default alignment. */
+union fooround
+{
+ uintmax_t i;
+ long double d;
+ void *p;
+};
+struct fooalign
+{
+ char c;
+ union fooround u;
+};
+/* If malloc were really smart, it would round addresses to DEFAULT_ALIGNMENT.
+ But in fact it might be less smart and round addresses to as much as
+ DEFAULT_ROUNDING. So we prepare for it to do that. */
+enum
+ {
+ DEFAULT_ALIGNMENT = offsetof (struct fooalign, u),
+ DEFAULT_ROUNDING = sizeof (union fooround)
+ };
+
+/* When we copy a long block of data, this is the unit to do it with.
+ On some machines, copying successive ints does not work;
+ in such a case, redefine COPYING_UNIT to `long' (if that works)
+ or `char' as a last resort. */
+# ifndef COPYING_UNIT
+# define COPYING_UNIT int
+# endif
+
+
+/* The functions allocating more room by calling `obstack_chunk_alloc'
+ jump to the handler pointed to by `obstack_alloc_failed_handler'.
+ This can be set to a user defined function which should either
+ abort gracefully or use longjump - but shouldn't return. This
+ variable by default points to the internal function
+ `print_and_abort'. */
+static void print_and_abort (void);
+void (*obstack_alloc_failed_handler) (void) = print_and_abort;
+
+/* Exit value used when `print_and_abort' is used. */
+# include <stdlib.h>
+int obstack_exit_failure = EXIT_FAILURE;
+
+/* Define a macro that either calls functions with the traditional malloc/free
+ calling interface, or calls functions with the mmalloc/mfree interface
+ (that adds an extra first argument), based on the state of use_extra_arg.
+ For free, do not use ?:, since some compilers, like the MIPS compilers,
+ do not allow (expr) ? void : void. */
+
+# define CALL_CHUNKFUN(h, size) \
+ (((h) -> use_extra_arg) \
+ ? (*(h)->chunkfun) ((h)->extra_arg, (size)) \
+ : (*(struct _obstack_chunk *(*) (long)) (h)->chunkfun) ((size)))
+
+# define CALL_FREEFUN(h, old_chunk) \
+ do { \
+ if ((h) -> use_extra_arg) \
+ (*(h)->freefun) ((h)->extra_arg, (old_chunk)); \
+ else \
+ (*(void (*) (void *)) (h)->freefun) ((old_chunk)); \
+ } while (0)
+
+\f
+/* Initialize an obstack H for use. Specify chunk size SIZE (0 means default).
+ Objects start on multiples of ALIGNMENT (0 means use default).
+ CHUNKFUN is the function to use to allocate chunks,
+ and FREEFUN the function to free them.
+
+ Return nonzero if successful, calls obstack_alloc_failed_handler if
+ allocation fails. */
+
+int
+_obstack_begin (struct obstack *h,
+ int size, int alignment,
+ void *(*chunkfun) (long),
+ void (*freefun) (void *))
+{
+ register struct _obstack_chunk *chunk; /* points to new chunk */
+
+ if (alignment == 0)
+ alignment = DEFAULT_ALIGNMENT;
+ if (size == 0)
+ /* Default size is what GNU malloc can fit in a 4096-byte block. */
+ {
+ /* 12 is sizeof (mhead) and 4 is EXTRA from GNU malloc.
+ Use the values for range checking, because if range checking is off,
+ the extra bytes won't be missed terribly, but if range checking is on
+ and we used a larger request, a whole extra 4096 bytes would be
+ allocated.
+
+ These number are irrelevant to the new GNU malloc. I suspect it is
+ less sensitive to the size of the request. */
+ int extra = ((((12 + DEFAULT_ROUNDING - 1) & ~(DEFAULT_ROUNDING - 1))
+ + 4 + DEFAULT_ROUNDING - 1)
+ & ~(DEFAULT_ROUNDING - 1));
+ size = 4096 - extra;
+ }
+
+ h->chunkfun = (struct _obstack_chunk * (*)(void *, long)) chunkfun;
+ h->freefun = (void (*) (void *, struct _obstack_chunk *)) freefun;
+ h->chunk_size = size;
+ h->alignment_mask = alignment - 1;
+ h->use_extra_arg = 0;
+
+ chunk = h->chunk = CALL_CHUNKFUN (h, h -> chunk_size);
+ if (!chunk)
+ (*obstack_alloc_failed_handler) ();
+ h->next_free = h->object_base = __PTR_ALIGN ((char *) chunk, chunk->contents,
+ alignment - 1);
+ h->chunk_limit = chunk->limit
+ = (char *) chunk + h->chunk_size;
+ chunk->prev = 0;
+ /* The initial chunk now contains no empty object. */
+ h->maybe_empty_object = 0;
+ h->alloc_failed = 0;
+ return 1;
+}
+
+int
+_obstack_begin_1 (struct obstack *h, int size, int alignment,
+ void *(*chunkfun) (void *, long),
+ void (*freefun) (void *, void *),
+ void *arg)
+{
+ register struct _obstack_chunk *chunk; /* points to new chunk */
+
+ if (alignment == 0)
+ alignment = DEFAULT_ALIGNMENT;
+ if (size == 0)
+ /* Default size is what GNU malloc can fit in a 4096-byte block. */
+ {
+ /* 12 is sizeof (mhead) and 4 is EXTRA from GNU malloc.
+ Use the values for range checking, because if range checking is off,
+ the extra bytes won't be missed terribly, but if range checking is on
+ and we used a larger request, a whole extra 4096 bytes would be
+ allocated.
+
+ These number are irrelevant to the new GNU malloc. I suspect it is
+ less sensitive to the size of the request. */
+ int extra = ((((12 + DEFAULT_ROUNDING - 1) & ~(DEFAULT_ROUNDING - 1))
+ + 4 + DEFAULT_ROUNDING - 1)
+ & ~(DEFAULT_ROUNDING - 1));
+ size = 4096 - extra;
+ }
+
+ h->chunkfun = (struct _obstack_chunk * (*)(void *,long)) chunkfun;
+ h->freefun = (void (*) (void *, struct _obstack_chunk *)) freefun;
+ h->chunk_size = size;
+ h->alignment_mask = alignment - 1;
+ h->extra_arg = arg;
+ h->use_extra_arg = 1;
+
+ chunk = h->chunk = CALL_CHUNKFUN (h, h -> chunk_size);
+ if (!chunk)
+ (*obstack_alloc_failed_handler) ();
+ h->next_free = h->object_base = __PTR_ALIGN ((char *) chunk, chunk->contents,
+ alignment - 1);
+ h->chunk_limit = chunk->limit
+ = (char *) chunk + h->chunk_size;
+ chunk->prev = 0;
+ /* The initial chunk now contains no empty object. */
+ h->maybe_empty_object = 0;
+ h->alloc_failed = 0;
+ return 1;
+}
+
+/* Allocate a new current chunk for the obstack *H
+ on the assumption that LENGTH bytes need to be added
+ to the current object, or a new object of length LENGTH allocated.
+ Copies any partial object from the end of the old chunk
+ to the beginning of the new one. */
+
+void
+_obstack_newchunk (struct obstack *h, int length)
+{
+ register struct _obstack_chunk *old_chunk = h->chunk;
+ register struct _obstack_chunk *new_chunk;
+ register long new_size;
+ register long obj_size = h->next_free - h->object_base;
+ register long i;
+ long already;
+ char *object_base;
+
+ /* Compute size for new chunk. */
+ new_size = (obj_size + length) + (obj_size >> 3) + h->alignment_mask + 100;
+ if (new_size < h->chunk_size)
+ new_size = h->chunk_size;
+
+ /* Allocate and initialize the new chunk. */
+ new_chunk = CALL_CHUNKFUN (h, new_size);
+ if (!new_chunk)
+ (*obstack_alloc_failed_handler) ();
+ h->chunk = new_chunk;
+ new_chunk->prev = old_chunk;
+ new_chunk->limit = h->chunk_limit = (char *) new_chunk + new_size;
+
+ /* Compute an aligned object_base in the new chunk */
+ object_base =
+ __PTR_ALIGN ((char *) new_chunk, new_chunk->contents, h->alignment_mask);
+
+ /* Move the existing object to the new chunk.
+ Word at a time is fast and is safe if the object
+ is sufficiently aligned. */
+ if (h->alignment_mask + 1 >= DEFAULT_ALIGNMENT)
+ {
+ for (i = obj_size / sizeof (COPYING_UNIT) - 1;
+ i >= 0; i--)
+ ((COPYING_UNIT *)object_base)[i]
+ = ((COPYING_UNIT *)h->object_base)[i];
+ /* We used to copy the odd few remaining bytes as one extra COPYING_UNIT,
+ but that can cross a page boundary on a machine
+ which does not do strict alignment for COPYING_UNITS. */
+ already = obj_size / sizeof (COPYING_UNIT) * sizeof (COPYING_UNIT);
+ }
+ else
+ already = 0;
+ /* Copy remaining bytes one by one. */
+ for (i = already; i < obj_size; i++)
+ object_base[i] = h->object_base[i];
+
+ /* If the object just copied was the only data in OLD_CHUNK,
+ free that chunk and remove it from the chain.
+ But not if that chunk might contain an empty object. */
+ if (! h->maybe_empty_object
+ && (h->object_base
+ == __PTR_ALIGN ((char *) old_chunk, old_chunk->contents,
+ h->alignment_mask)))
+ {
+ new_chunk->prev = old_chunk->prev;
+ CALL_FREEFUN (h, old_chunk);
+ }
+
+ h->object_base = object_base;
+ h->next_free = h->object_base + obj_size;
+ /* The new chunk certainly contains no empty object yet. */
+ h->maybe_empty_object = 0;
+}
+
+/* Return nonzero if object OBJ has been allocated from obstack H.
+ This is here for debugging.
+ If you use it in a program, you are probably losing. */
+
+/* Suppress -Wmissing-prototypes warning. We don't want to declare this in
+ obstack.h because it is just for debugging. */
+int _obstack_allocated_p (struct obstack *h, void *obj);
+
+int
+_obstack_allocated_p (struct obstack *h, void *obj)
+{
+ register struct _obstack_chunk *lp; /* below addr of any objects in this chunk */
+ register struct _obstack_chunk *plp; /* point to previous chunk if any */
+
+ lp = (h)->chunk;
+ /* We use >= rather than > since the object cannot be exactly at
+ the beginning of the chunk but might be an empty object exactly
+ at the end of an adjacent chunk. */
+ while (lp != 0 && ((void *) lp >= obj || (void *) (lp)->limit < obj))
+ {
+ plp = lp->prev;
+ lp = plp;
+ }
+ return lp != 0;
+}
+\f
+/* Free objects in obstack H, including OBJ and everything allocate
+ more recently than OBJ. If OBJ is zero, free everything in H. */
+
+# undef obstack_free
+
+void
+obstack_free (struct obstack *h, void *obj)
+{
+ register struct _obstack_chunk *lp; /* below addr of any objects in this chunk */
+ register struct _obstack_chunk *plp; /* point to previous chunk if any */
+
+ lp = h->chunk;
+ /* We use >= because there cannot be an object at the beginning of a chunk.
+ But there can be an empty object at that address
+ at the end of another chunk. */
+ while (lp != 0 && ((void *) lp >= obj || (void *) (lp)->limit < obj))
+ {
+ plp = lp->prev;
+ CALL_FREEFUN (h, lp);
+ lp = plp;
+ /* If we switch chunks, we can't tell whether the new current
+ chunk contains an empty object, so assume that it may. */
+ h->maybe_empty_object = 1;
+ }
+ if (lp)
+ {
+ h->object_base = h->next_free = (char *) (obj);
+ h->chunk_limit = lp->limit;
+ h->chunk = lp;
+ }
+ else if (obj != 0)
+ /* obj is not in any of the chunks! */
+ abort ();
+}
+
+int
+_obstack_memory_used (struct obstack *h)
+{
+ register struct _obstack_chunk* lp;
+ register int nbytes = 0;
+
+ for (lp = h->chunk; lp != 0; lp = lp->prev)
+ {
+ nbytes += lp->limit - (char *) lp;
+ }
+ return nbytes;
+}
+
+static void
+__attribute__ ((noreturn))
+print_and_abort (void)
+{
+ /* Don't change any of these strings. Yes, it would be possible to add
+ the newline to the string and use fputs or so. But this must not
+ happen because the "memory exhausted" message appears in other places
+ like this and the translation should be reused instead of creating
+ a very similar string which requires a separate translation. */
+ fprintf (stderr, "%s\n", "memory exhausted");
+ exit (obstack_exit_failure);
+}
+
--- /dev/null
+/* obstack.h - object stack macros
+ Copyright (C) 1988-1994,1996-1999,2003,2004,2005
+ Free Software Foundation, Inc.
+ This file is part of the GNU C Library.
+
+ The GNU C Library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Lesser General Public
+ License as published by the Free Software Foundation; either
+ version 2.1 of the License, or (at your option) any later version.
+
+ The GNU C 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
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public
+ License along with the GNU C Library; if not, write to the Free
+ Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ Boston, MA 02110-1301, USA. */
+
+/* Summary:
+
+All the apparent functions defined here are macros. The idea
+is that you would use these pre-tested macros to solve a
+very specific set of problems, and they would run fast.
+Caution: no side-effects in arguments please!! They may be
+evaluated MANY times!!
+
+These macros operate a stack of objects. Each object starts life
+small, and may grow to maturity. (Consider building a word syllable
+by syllable.) An object can move while it is growing. Once it has
+been "finished" it never changes address again. So the "top of the
+stack" is typically an immature growing object, while the rest of the
+stack is of mature, fixed size and fixed address objects.
+
+These routines grab large chunks of memory, using a function you
+supply, called `obstack_chunk_alloc'. On occasion, they free chunks,
+by calling `obstack_chunk_free'. You must define them and declare
+them before using any obstack macros.
+
+Each independent stack is represented by a `struct obstack'.
+Each of the obstack macros expects a pointer to such a structure
+as the first argument.
+
+One motivation for this package is the problem of growing char strings
+in symbol tables. Unless you are "fascist pig with a read-only mind"
+--Gosper's immortal quote from HAKMEM item 154, out of context--you
+would not like to put any arbitrary upper limit on the length of your
+symbols.
+
+In practice this often means you will build many short symbols and a
+few long symbols. At the time you are reading a symbol you don't know
+how long it is. One traditional method is to read a symbol into a
+buffer, realloc()ating the buffer every time you try to read a symbol
+that is longer than the buffer. This is beaut, but you still will
+want to copy the symbol from the buffer to a more permanent
+symbol-table entry say about half the time.
+
+With obstacks, you can work differently. Use one obstack for all symbol
+names. As you read a symbol, grow the name in the obstack gradually.
+When the name is complete, finalize it. Then, if the symbol exists already,
+free the newly read name.
+
+The way we do this is to take a large chunk, allocating memory from
+low addresses. When you want to build a symbol in the chunk you just
+add chars above the current "high water mark" in the chunk. When you
+have finished adding chars, because you got to the end of the symbol,
+you know how long the chars are, and you can create a new object.
+Mostly the chars will not burst over the highest address of the chunk,
+because you would typically expect a chunk to be (say) 100 times as
+long as an average object.
+
+In case that isn't clear, when we have enough chars to make up
+the object, THEY ARE ALREADY CONTIGUOUS IN THE CHUNK (guaranteed)
+so we just point to it where it lies. No moving of chars is
+needed and this is the second win: potentially long strings need
+never be explicitly shuffled. Once an object is formed, it does not
+change its address during its lifetime.
+
+When the chars burst over a chunk boundary, we allocate a larger
+chunk, and then copy the partly formed object from the end of the old
+chunk to the beginning of the new larger chunk. We then carry on
+accreting characters to the end of the object as we normally would.
+
+A special macro is provided to add a single char at a time to a
+growing object. This allows the use of register variables, which
+break the ordinary 'growth' macro.
+
+Summary:
+ We allocate large chunks.
+ We carve out one object at a time from the current chunk.
+ Once carved, an object never moves.
+ We are free to append data of any size to the currently
+ growing object.
+ Exactly one object is growing in an obstack at any one time.
+ You can run one obstack per control block.
+ You may have as many control blocks as you dare.
+ Because of the way we do it, you can `unwind' an obstack
+ back to a previous state. (You may remove objects much
+ as you would with a stack.)
+*/
+
+
+/* Don't do the contents of this file more than once. */
+
+#ifndef _OBSTACK_H
+#define _OBSTACK_H 1
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+\f
+/* We need the type of a pointer subtraction. If __PTRDIFF_TYPE__ is
+ defined, as with GNU C, use that; that way we don't pollute the
+ namespace with <stddef.h>'s symbols. Otherwise, include <stddef.h>
+ and use ptrdiff_t. */
+
+#ifdef __PTRDIFF_TYPE__
+# define PTR_INT_TYPE __PTRDIFF_TYPE__
+#else
+# include <stddef.h>
+# define PTR_INT_TYPE ptrdiff_t
+#endif
+
+/* If B is the base of an object addressed by P, return the result of
+ aligning P to the next multiple of A + 1. B and P must be of type
+ char *. A + 1 must be a power of 2. */
+
+#define __BPTR_ALIGN(B, P, A) ((B) + (((P) - (B) + (A)) & ~(A)))
+
+/* Similiar to _BPTR_ALIGN (B, P, A), except optimize the common case
+ where pointers can be converted to integers, aligned as integers,
+ and converted back again. If PTR_INT_TYPE is narrower than a
+ pointer (e.g., the AS/400), play it safe and compute the alignment
+ relative to B. Otherwise, use the faster strategy of computing the
+ alignment relative to 0. */
+
+#define __PTR_ALIGN(B, P, A) \
+ __BPTR_ALIGN (sizeof (PTR_INT_TYPE) < sizeof (void *) ? (B) : (char *) 0, \
+ P, A)
+
+#include <string.h>
+
+struct _obstack_chunk /* Lives at front of each chunk. */
+{
+ char *limit; /* 1 past end of this chunk */
+ struct _obstack_chunk *prev; /* address of prior chunk or NULL */
+ char contents[4]; /* objects begin here */
+};
+
+struct obstack /* control current object in current chunk */
+{
+ long chunk_size; /* preferred size to allocate chunks in */
+ struct _obstack_chunk *chunk; /* address of current struct obstack_chunk */
+ char *object_base; /* address of object we are building */
+ char *next_free; /* where to add next char to current object */
+ char *chunk_limit; /* address of char after current chunk */
+ union
+ {
+ PTR_INT_TYPE tempint;
+ void *tempptr;
+ } temp; /* Temporary for some macros. */
+ int alignment_mask; /* Mask of alignment for each object. */
+ /* These prototypes vary based on `use_extra_arg', and we use
+ casts to the prototypeless function type in all assignments,
+ but having prototypes here quiets -Wstrict-prototypes. */
+ struct _obstack_chunk *(*chunkfun) (void *, long);
+ void (*freefun) (void *, struct _obstack_chunk *);
+ void *extra_arg; /* first arg for chunk alloc/dealloc funcs */
+ unsigned use_extra_arg:1; /* chunk alloc/dealloc funcs take extra arg */
+ unsigned maybe_empty_object:1;/* There is a possibility that the current
+ chunk contains a zero-length object. This
+ prevents freeing the chunk if we allocate
+ a bigger chunk to replace it. */
+ unsigned alloc_failed:1; /* No longer used, as we now call the failed
+ handler on error, but retained for binary
+ compatibility. */
+};
+
+/* Declare the external functions we use; they are in obstack.c. */
+
+extern void _obstack_newchunk (struct obstack *, int);
+extern int _obstack_begin (struct obstack *, int, int,
+ void *(*) (long), void (*) (void *));
+extern int _obstack_begin_1 (struct obstack *, int, int,
+ void *(*) (void *, long),
+ void (*) (void *, void *), void *);
+extern int _obstack_memory_used (struct obstack *);
+
+void obstack_free (struct obstack *obstack, void *block);
+
+\f
+/* Error handler called when `obstack_chunk_alloc' failed to allocate
+ more memory. This can be set to a user defined function which
+ should either abort gracefully or use longjump - but shouldn't
+ return. The default action is to print a message and abort. */
+extern void (*obstack_alloc_failed_handler) (void);
+
+/* Exit value used when `print_and_abort' is used. */
+extern int obstack_exit_failure;
+\f
+/* Pointer to beginning of object being allocated or to be allocated next.
+ Note that this might not be the final address of the object
+ because a new chunk might be needed to hold the final size. */
+
+#define obstack_base(h) ((void *) (h)->object_base)
+
+/* Size for allocating ordinary chunks. */
+
+#define obstack_chunk_size(h) ((h)->chunk_size)
+
+/* Pointer to next byte not yet allocated in current chunk. */
+
+#define obstack_next_free(h) ((h)->next_free)
+
+/* Mask specifying low bits that should be clear in address of an object. */
+
+#define obstack_alignment_mask(h) ((h)->alignment_mask)
+
+/* To prevent prototype warnings provide complete argument list. */
+#define obstack_init(h) \
+ _obstack_begin ((h), 0, 0, \
+ (void *(*) (long)) obstack_chunk_alloc, \
+ (void (*) (void *)) obstack_chunk_free)
+
+#define obstack_begin(h, size) \
+ _obstack_begin ((h), (size), 0, \
+ (void *(*) (long)) obstack_chunk_alloc, \
+ (void (*) (void *)) obstack_chunk_free)
+
+#define obstack_specify_allocation(h, size, alignment, chunkfun, freefun) \
+ _obstack_begin ((h), (size), (alignment), \
+ (void *(*) (long)) (chunkfun), \
+ (void (*) (void *)) (freefun))
+
+#define obstack_specify_allocation_with_arg(h, size, alignment, chunkfun, freefun, arg) \
+ _obstack_begin_1 ((h), (size), (alignment), \
+ (void *(*) (void *, long)) (chunkfun), \
+ (void (*) (void *, void *)) (freefun), (arg))
+
+#define obstack_chunkfun(h, newchunkfun) \
+ ((h) -> chunkfun = (struct _obstack_chunk *(*)(void *, long)) (newchunkfun))
+
+#define obstack_freefun(h, newfreefun) \
+ ((h) -> freefun = (void (*)(void *, struct _obstack_chunk *)) (newfreefun))
+
+#define obstack_1grow_fast(h,achar) (*((h)->next_free)++ = (achar))
+
+#define obstack_blank_fast(h,n) ((h)->next_free += (n))
+
+#define obstack_memory_used(h) _obstack_memory_used (h)
+\f
+#if defined __GNUC__ && defined __STDC__ && __STDC__
+/* NextStep 2.0 cc is really gcc 1.93 but it defines __GNUC__ = 2 and
+ does not implement __extension__. But that compiler doesn't define
+ __GNUC_MINOR__. */
+# if __GNUC__ < 2 || (__NeXT__ && !__GNUC_MINOR__)
+# define __extension__
+# endif
+
+/* For GNU C, if not -traditional,
+ we can define these macros to compute all args only once
+ without using a global variable.
+ Also, we can avoid using the `temp' slot, to make faster code. */
+
+# define obstack_object_size(OBSTACK) \
+ __extension__ \
+ ({ struct obstack const *__o = (OBSTACK); \
+ (unsigned) (__o->next_free - __o->object_base); })
+
+# define obstack_room(OBSTACK) \
+ __extension__ \
+ ({ struct obstack const *__o = (OBSTACK); \
+ (unsigned) (__o->chunk_limit - __o->next_free); })
+
+# define obstack_make_room(OBSTACK,length) \
+__extension__ \
+({ struct obstack *__o = (OBSTACK); \
+ int __len = (length); \
+ if (__o->chunk_limit - __o->next_free < __len) \
+ _obstack_newchunk (__o, __len); \
+ (void) 0; })
+
+# define obstack_empty_p(OBSTACK) \
+ __extension__ \
+ ({ struct obstack const *__o = (OBSTACK); \
+ (__o->chunk->prev == 0 \
+ && __o->next_free == __PTR_ALIGN ((char *) __o->chunk, \
+ __o->chunk->contents, \
+ __o->alignment_mask)); })
+
+# define obstack_grow(OBSTACK,where,length) \
+__extension__ \
+({ struct obstack *__o = (OBSTACK); \
+ int __len = (length); \
+ if (__o->next_free + __len > __o->chunk_limit) \
+ _obstack_newchunk (__o, __len); \
+ memcpy (__o->next_free, where, __len); \
+ __o->next_free += __len; \
+ (void) 0; })
+
+# define obstack_grow0(OBSTACK,where,length) \
+__extension__ \
+({ struct obstack *__o = (OBSTACK); \
+ int __len = (length); \
+ if (__o->next_free + __len + 1 > __o->chunk_limit) \
+ _obstack_newchunk (__o, __len + 1); \
+ memcpy (__o->next_free, where, __len); \
+ __o->next_free += __len; \
+ *(__o->next_free)++ = 0; \
+ (void) 0; })
+
+# define obstack_1grow(OBSTACK,datum) \
+__extension__ \
+({ struct obstack *__o = (OBSTACK); \
+ if (__o->next_free + 1 > __o->chunk_limit) \
+ _obstack_newchunk (__o, 1); \
+ obstack_1grow_fast (__o, datum); \
+ (void) 0; })
+
+/* These assume that the obstack alignment is good enough for pointers
+ or ints, and that the data added so far to the current object
+ shares that much alignment. */
+
+# define obstack_ptr_grow(OBSTACK,datum) \
+__extension__ \
+({ struct obstack *__o = (OBSTACK); \
+ if (__o->next_free + sizeof (void *) > __o->chunk_limit) \
+ _obstack_newchunk (__o, sizeof (void *)); \
+ obstack_ptr_grow_fast (__o, datum); }) \
+
+# define obstack_int_grow(OBSTACK,datum) \
+__extension__ \
+({ struct obstack *__o = (OBSTACK); \
+ if (__o->next_free + sizeof (int) > __o->chunk_limit) \
+ _obstack_newchunk (__o, sizeof (int)); \
+ obstack_int_grow_fast (__o, datum); })
+
+# define obstack_ptr_grow_fast(OBSTACK,aptr) \
+__extension__ \
+({ struct obstack *__o1 = (OBSTACK); \
+ *(const void **) __o1->next_free = (aptr); \
+ __o1->next_free += sizeof (const void *); \
+ (void) 0; })
+
+# define obstack_int_grow_fast(OBSTACK,aint) \
+__extension__ \
+({ struct obstack *__o1 = (OBSTACK); \
+ *(int *) __o1->next_free = (aint); \
+ __o1->next_free += sizeof (int); \
+ (void) 0; })
+
+# define obstack_blank(OBSTACK,length) \
+__extension__ \
+({ struct obstack *__o = (OBSTACK); \
+ int __len = (length); \
+ if (__o->chunk_limit - __o->next_free < __len) \
+ _obstack_newchunk (__o, __len); \
+ obstack_blank_fast (__o, __len); \
+ (void) 0; })
+
+# define obstack_alloc(OBSTACK,length) \
+__extension__ \
+({ struct obstack *__h = (OBSTACK); \
+ obstack_blank (__h, (length)); \
+ obstack_finish (__h); })
+
+# define obstack_copy(OBSTACK,where,length) \
+__extension__ \
+({ struct obstack *__h = (OBSTACK); \
+ obstack_grow (__h, (where), (length)); \
+ obstack_finish (__h); })
+
+# define obstack_copy0(OBSTACK,where,length) \
+__extension__ \
+({ struct obstack *__h = (OBSTACK); \
+ obstack_grow0 (__h, (where), (length)); \
+ obstack_finish (__h); })
+
+/* The local variable is named __o1 to avoid a name conflict
+ when obstack_blank is called. */
+# define obstack_finish(OBSTACK) \
+__extension__ \
+({ struct obstack *__o1 = (OBSTACK); \
+ void *__value = (void *) __o1->object_base; \
+ if (__o1->next_free == __value) \
+ __o1->maybe_empty_object = 1; \
+ __o1->next_free \
+ = __PTR_ALIGN (__o1->object_base, __o1->next_free, \
+ __o1->alignment_mask); \
+ if (__o1->next_free - (char *)__o1->chunk \
+ > __o1->chunk_limit - (char *)__o1->chunk) \
+ __o1->next_free = __o1->chunk_limit; \
+ __o1->object_base = __o1->next_free; \
+ __value; })
+
+# define obstack_free(OBSTACK, OBJ) \
+__extension__ \
+({ struct obstack *__o = (OBSTACK); \
+ void *__obj = (OBJ); \
+ if (__obj > (void *)__o->chunk && __obj < (void *)__o->chunk_limit) \
+ __o->next_free = __o->object_base = (char *)__obj; \
+ else (obstack_free) (__o, __obj); })
+\f
+#else /* not __GNUC__ or not __STDC__ */
+
+# define obstack_object_size(h) \
+ (unsigned) ((h)->next_free - (h)->object_base)
+
+# define obstack_room(h) \
+ (unsigned) ((h)->chunk_limit - (h)->next_free)
+
+# define obstack_empty_p(h) \
+ ((h)->chunk->prev == 0 \
+ && (h)->next_free == __PTR_ALIGN ((char *) (h)->chunk, \
+ (h)->chunk->contents, \
+ (h)->alignment_mask))
+
+/* Note that the call to _obstack_newchunk is enclosed in (..., 0)
+ so that we can avoid having void expressions
+ in the arms of the conditional expression.
+ Casting the third operand to void was tried before,
+ but some compilers won't accept it. */
+
+# define obstack_make_room(h,length) \
+( (h)->temp.tempint = (length), \
+ (((h)->next_free + (h)->temp.tempint > (h)->chunk_limit) \
+ ? (_obstack_newchunk ((h), (h)->temp.tempint), 0) : 0))
+
+# define obstack_grow(h,where,length) \
+( (h)->temp.tempint = (length), \
+ (((h)->next_free + (h)->temp.tempint > (h)->chunk_limit) \
+ ? (_obstack_newchunk ((h), (h)->temp.tempint), 0) : 0), \
+ memcpy ((h)->next_free, where, (h)->temp.tempint), \
+ (h)->next_free += (h)->temp.tempint)
+
+# define obstack_grow0(h,where,length) \
+( (h)->temp.tempint = (length), \
+ (((h)->next_free + (h)->temp.tempint + 1 > (h)->chunk_limit) \
+ ? (_obstack_newchunk ((h), (h)->temp.tempint + 1), 0) : 0), \
+ memcpy ((h)->next_free, where, (h)->temp.tempint), \
+ (h)->next_free += (h)->temp.tempint, \
+ *((h)->next_free)++ = 0)
+
+# define obstack_1grow(h,datum) \
+( (((h)->next_free + 1 > (h)->chunk_limit) \
+ ? (_obstack_newchunk ((h), 1), 0) : 0), \
+ obstack_1grow_fast (h, datum))
+
+# define obstack_ptr_grow(h,datum) \
+( (((h)->next_free + sizeof (char *) > (h)->chunk_limit) \
+ ? (_obstack_newchunk ((h), sizeof (char *)), 0) : 0), \
+ obstack_ptr_grow_fast (h, datum))
+
+# define obstack_int_grow(h,datum) \
+( (((h)->next_free + sizeof (int) > (h)->chunk_limit) \
+ ? (_obstack_newchunk ((h), sizeof (int)), 0) : 0), \
+ obstack_int_grow_fast (h, datum))
+
+# define obstack_ptr_grow_fast(h,aptr) \
+ (((const void **) ((h)->next_free += sizeof (void *)))[-1] = (aptr))
+
+# define obstack_int_grow_fast(h,aint) \
+ (((int *) ((h)->next_free += sizeof (int)))[-1] = (aint))
+
+# define obstack_blank(h,length) \
+( (h)->temp.tempint = (length), \
+ (((h)->chunk_limit - (h)->next_free < (h)->temp.tempint) \
+ ? (_obstack_newchunk ((h), (h)->temp.tempint), 0) : 0), \
+ obstack_blank_fast (h, (h)->temp.tempint))
+
+# define obstack_alloc(h,length) \
+ (obstack_blank ((h), (length)), obstack_finish ((h)))
+
+# define obstack_copy(h,where,length) \
+ (obstack_grow ((h), (where), (length)), obstack_finish ((h)))
+
+# define obstack_copy0(h,where,length) \
+ (obstack_grow0 ((h), (where), (length)), obstack_finish ((h)))
+
+# define obstack_finish(h) \
+( ((h)->next_free == (h)->object_base \
+ ? (((h)->maybe_empty_object = 1), 0) \
+ : 0), \
+ (h)->temp.tempptr = (h)->object_base, \
+ (h)->next_free \
+ = __PTR_ALIGN ((h)->object_base, (h)->next_free, \
+ (h)->alignment_mask), \
+ (((h)->next_free - (char *) (h)->chunk \
+ > (h)->chunk_limit - (char *) (h)->chunk) \
+ ? ((h)->next_free = (h)->chunk_limit) : 0), \
+ (h)->object_base = (h)->next_free, \
+ (h)->temp.tempptr)
+
+# define obstack_free(h,obj) \
+( (h)->temp.tempint = (char *) (obj) - (char *) (h)->chunk, \
+ ((((h)->temp.tempint > 0 \
+ && (h)->temp.tempint < (h)->chunk_limit - (char *) (h)->chunk)) \
+ ? (int) ((h)->next_free = (h)->object_base \
+ = (h)->temp.tempint + (char *) (h)->chunk) \
+ : (((obstack_free) ((h), (h)->temp.tempint + (char *) (h)->chunk), 0), 0)))
+
+#endif /* not __GNUC__ or not __STDC__ */
+
+#ifdef __cplusplus
+} /* C++ */
+#endif
+
+#endif /* obstack.h */
--- /dev/null
+// $Id$
+//
+// SuperTux
+// Copyright (C) 2007 Matthias Braun <matze@braunis.de>
+//
+// 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 OBSTACKPP_H_
+#define OBSTACKPP_H_
+
+#include "obstack.h"
+
+inline void*
+operator new (size_t bytes, struct obstack& obst)
+{
+ return obstack_alloc(&obst, bytes);
+}
+
+inline void*
+operator new[] (size_t bytes, struct obstack& obst)
+{
+ return obstack_alloc(&obst, bytes);
+}
+
+static inline void* obstack_chunk_alloc(size_t size)
+{
+ return new char[size];
+}
+
+static inline void obstack_chunk_free(void* data)
+{
+ char* ptr = static_cast<char*> (data);
+ delete[] ptr;
+}
+
+#endif
+
--- /dev/null
+// $Id$
+//
+// SuperTux
+// Copyright (C) 2004 Tobas Glaesser <tobi.web@gmx.de>
+// Copyright (C) 2006 Matthias Braun <matze@braunis.de>
+//
+// 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 <config.h>
+
+#include "options_menu.hpp"
+#include "gui/menu.hpp"
+#include "audio/sound_manager.hpp"
+#include "control/joystickkeyboardcontroller.hpp"
+#include "main.hpp"
+#include "gettext.hpp"
+#include "gameconfig.hpp"
+
+Menu* options_menu = 0;
+
+enum OptionsMenuIDs {
+ MNID_FULLSCREEN,
+ MNID_SOUND,
+ MNID_MUSIC
+};
+
+class LanguageMenu : public Menu
+{
+public:
+ LanguageMenu() {
+ add_label(_("Language"));
+ add_hl();
+ add_entry(0, std::string("(")+_("auto-detect language")+")");
+ add_entry(1, "English");
+
+ int mnid = 10;
+ std::set<std::string> languages = dictionary_manager.get_languages();
+ for (std::set<std::string>::iterator i = languages.begin(); i != languages.end(); i++) {
+ std::string locale_name = *i;
+ TinyGetText::LanguageDef ldef = TinyGetText::get_language_def(locale_name);
+ std::string locale_fullname = locale_name;
+ if (std::string(ldef.code) == locale_name) {
+ locale_fullname = ldef.name;
+ }
+ add_entry(mnid++, locale_fullname);
+ }
+
+ add_hl();
+ add_back(_("Back"));
+ }
+
+ virtual void menu_action(MenuItem* item) {
+ if (item->id == 0) {
+ config->locale = "";
+ dictionary_manager.set_language(config->locale);
+ config->save();
+ Menu::set_current(0);
+ }
+ else if (item->id == 1) {
+ config->locale = "en";
+ dictionary_manager.set_language(config->locale);
+ config->save();
+ Menu::set_current(0);
+ }
+ int mnid = 10;
+ std::set<std::string> languages = dictionary_manager.get_languages();
+ for (std::set<std::string>::iterator i = languages.begin(); i != languages.end(); i++) {
+ std::string locale_name = *i;
+ if (item->id == mnid++) {
+ config->locale = locale_name;
+ dictionary_manager.set_language(config->locale);
+ config->save();
+ Menu::set_current(0);
+ }
+ }
+ }
+};
+
+
+class OptionsMenu : public Menu
+{
+public:
+ OptionsMenu();
+ virtual ~OptionsMenu();
+
+ virtual void menu_action(MenuItem* item);
+
+protected:
+ std::auto_ptr<LanguageMenu> language_menu;
+
+};
+
+OptionsMenu::OptionsMenu()
+{
+ language_menu.reset(new LanguageMenu());
+
+ add_label(_("Options"));
+ add_hl();
+ add_toggle(MNID_FULLSCREEN,_("Fullscreen"), config->use_fullscreen);
+ add_submenu(_("Language"), language_menu.get());
+ if (sound_manager->is_audio_enabled()) {
+ add_toggle(MNID_SOUND, _("Sound"), config->sound_enabled);
+ add_toggle(MNID_MUSIC, _("Music"), config->music_enabled);
+ } else {
+ add_deactive(MNID_SOUND, _("Sound (disabled)"));
+ add_deactive(MNID_SOUND, _("Music (disabled)"));
+ }
+ add_submenu(_("Setup Keyboard"), main_controller->get_key_options_menu());
+ add_submenu(_("Setup Joystick"),main_controller->get_joystick_options_menu());
+ add_hl();
+ add_back(_("Back"));
+}
+
+OptionsMenu::~OptionsMenu()
+{
+}
+
+void
+OptionsMenu::menu_action(MenuItem* item)
+{
+ switch (item->id) {
+ case MNID_FULLSCREEN:
+ if(config->use_fullscreen != options_menu->is_toggled(MNID_FULLSCREEN)) {
+ config->use_fullscreen = !config->use_fullscreen;
+ init_video();
+ config->save();
+ }
+ break;
+ case MNID_SOUND:
+ if(config->sound_enabled != options_menu->is_toggled(MNID_SOUND)) {
+ config->sound_enabled = !config->sound_enabled;
+ sound_manager->enable_sound(config->sound_enabled);
+ config->save();
+ }
+ break;
+ case MNID_MUSIC:
+ if(config->music_enabled != options_menu->is_toggled(MNID_MUSIC)) {
+ config->music_enabled = !config->music_enabled;
+ sound_manager->enable_music(config->music_enabled);
+ config->save();
+ }
+ break;
+ default:
+ break;
+ }
+}
+
+Menu* get_options_menu()
+{
+ //static OptionsMenu menu;
+ options_menu = new OptionsMenu();
+ return options_menu;
+}
+
+void free_options_menu()
+{
+ delete options_menu;
+ options_menu = 0;
+}
--- /dev/null
+// $Id$
+//
+// SuperTux
+// Copyright (C) 2004 Tobas Glaesser <tobi.web@gmx.de>
+// Copyright (C) 2006 Matthias Braun <matze@braunis.de>
+//
+// 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 __OPTIONS_MENU_HPP__
+#define __OPTIONS_MENU_HPP__
+
+class Menu;
+Menu* get_options_menu();
+void free_options_menu();
+
+#endif
--- /dev/null
+// $Id$
+//
+// SuperTux
+// Copyright (C) 2006 Matthias Braun <matze@braunis.de>
+//
+// 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 <config.h>
+
+#include "physfs_sdl.hpp"
+
+#include <physfs.h>
+
+#include <stdexcept>
+#include <sstream>
+#include <iostream>
+
+#include <assert.h>
+#include "log.hpp"
+
+static int funcSeek(struct SDL_RWops* context, int offset, int whence)
+{
+ PHYSFS_file* file = (PHYSFS_file*) context->hidden.unknown.data1;
+ int res;
+ switch(whence) {
+ case SEEK_SET:
+ res = PHYSFS_seek(file, offset);
+ break;
+ case SEEK_CUR:
+ res = PHYSFS_seek(file, PHYSFS_tell(file) + offset);
+ break;
+ case SEEK_END:
+ res = PHYSFS_seek(file, PHYSFS_fileLength(file) + offset);
+ break;
+ default:
+ res = 0;
+ assert(false);
+ break;
+ }
+ if(res == 0) {
+ log_warning << "Error seeking in file: " << PHYSFS_getLastError() << std::endl;
+ return -1;
+ }
+
+ return (int) PHYSFS_tell(file);
+}
+
+static int funcRead(struct SDL_RWops* context, void* ptr, int size, int maxnum)
+{
+ PHYSFS_file* file = (PHYSFS_file*) context->hidden.unknown.data1;
+
+ int res = PHYSFS_read(file, ptr, size, maxnum);
+ return res;
+}
+
+static int funcClose(struct SDL_RWops* context)
+{
+ PHYSFS_file* file = (PHYSFS_file*) context->hidden.unknown.data1;
+
+ PHYSFS_close(file);
+ delete context;
+
+ return 0;
+}
+
+SDL_RWops* get_physfs_SDLRWops(const std::string& filename)
+{
+ // check this as PHYSFS seems to be buggy and still returns a
+ // valid pointer in this case
+ if(filename == "") {
+ throw std::runtime_error("Couldn't open file: empty filename");
+ }
+
+ PHYSFS_file* file = (PHYSFS_file*) PHYSFS_openRead(filename.c_str());
+ if(!file) {
+ std::stringstream msg;
+ msg << "Couldn't open '" << filename << "': "
+ << PHYSFS_getLastError();
+ throw std::runtime_error(msg.str());
+ }
+
+ SDL_RWops* ops = new SDL_RWops();
+ ops->type = 0;
+ ops->hidden.unknown.data1 = file;
+ ops->seek = funcSeek;
+ ops->read = funcRead;
+ ops->write = 0;
+ ops->close = funcClose;
+ return ops;
+}
--- /dev/null
+// $Id$
+//
+// SuperTux
+// Copyright (C) 2006 Matthias Braun <matze@braunis.de>
+//
+// 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 __PHYSFSSDL_HPP__
+#define __PHYSFSSDL_HPP__
+
+#include <SDL.h>
+#include <string>
+
+SDL_RWops* get_physfs_SDLRWops(const std::string& filename);
+
+#endif
--- /dev/null
+// $Id$
+//
+// SuperTux
+// Copyright (C) 2006 Matthias Braun <matze@braunis.de>
+//
+// 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 <config.h>
+
+#include "physfs_stream.hpp"
+
+#include <assert.h>
+#include <physfs.h>
+#include <stdexcept>
+#include <sstream>
+
+IFileStreambuf::IFileStreambuf(const std::string& filename)
+{
+ // check this as PHYSFS seems to be buggy and still returns a
+ // valid pointer in this case
+ if(filename == "") {
+ throw std::runtime_error("Couldn't open file: empty filename");
+ }
+ file = PHYSFS_openRead(filename.c_str());
+ if(file == 0) {
+ std::stringstream msg;
+ msg << "Couldn't open file '" << filename << "': "
+ << PHYSFS_getLastError();
+ throw std::runtime_error(msg.str());
+ }
+}
+
+IFileStreambuf::~IFileStreambuf()
+{
+ PHYSFS_close(file);
+}
+
+int
+IFileStreambuf::underflow()
+{
+ if(PHYSFS_eof(file)) {
+ return traits_type::eof();
+ }
+
+ PHYSFS_sint64 bytesread = PHYSFS_read(file, buf, 1, sizeof(buf));
+ if(bytesread <= 0) {
+ return traits_type::eof();
+ }
+ setg(buf, buf, buf + bytesread);
+
+ return buf[0];
+}
+
+IFileStreambuf::pos_type
+IFileStreambuf::seekpos(pos_type pos, std::ios_base::openmode)
+{
+ if(PHYSFS_seek(file, static_cast<PHYSFS_uint64> (pos)) == 0) {
+ return pos_type(off_type(-1));
+ }
+
+ // the seek invalidated the buffer
+ setg(buf, buf, buf);
+ return pos;
+}
+
+IFileStreambuf::pos_type
+IFileStreambuf::seekoff(off_type off, std::ios_base::seekdir dir,
+ std::ios_base::openmode mode)
+{
+ off_type pos = off;
+ PHYSFS_sint64 ptell = PHYSFS_tell(file);
+
+ switch(dir) {
+ case std::ios_base::beg:
+ break;
+ case std::ios_base::cur:
+ if(off == 0)
+ return static_cast<pos_type> (ptell) - static_cast<pos_type> (egptr() - gptr());
+ pos += static_cast<off_type> (ptell) - static_cast<off_type> (egptr() - gptr());
+ break;
+ case std::ios_base::end:
+ pos += static_cast<off_type> (PHYSFS_fileLength(file));
+ break;
+ default:
+#ifdef DEBUG
+ assert(false);
+#else
+ return pos_type(off_type(-1));
+#endif
+ }
+
+ return seekpos(static_cast<pos_type> (pos), mode);
+}
+
+//---------------------------------------------------------------------------
+
+OFileStreambuf::OFileStreambuf(const std::string& filename)
+{
+ file = PHYSFS_openWrite(filename.c_str());
+ if(file == 0) {
+ std::stringstream msg;
+ msg << "Couldn't open file '" << filename << "': "
+ << PHYSFS_getLastError();
+ throw std::runtime_error(msg.str());
+ }
+
+ setp(buf, buf+sizeof(buf));
+}
+
+OFileStreambuf::~OFileStreambuf()
+{
+ sync();
+ PHYSFS_close(file);
+}
+
+int
+OFileStreambuf::overflow(int c)
+{
+ char c2 = (char)c;
+
+ if(pbase() == pptr())
+ return 0;
+
+ size_t size = pptr() - pbase();
+ PHYSFS_sint64 res = PHYSFS_write(file, pbase(), 1, size);
+ if(res <= 0)
+ return traits_type::eof();
+
+ if(c != traits_type::eof()) {
+ PHYSFS_sint64 res = PHYSFS_write(file, &c2, 1, 1);
+ if(res <= 0)
+ return traits_type::eof();
+ }
+
+ setp(buf, buf + res);
+ return 0;
+}
+
+int
+OFileStreambuf::sync()
+{
+ return overflow(traits_type::eof());
+}
+
+//---------------------------------------------------------------------------
+
+IFileStream::IFileStream(const std::string& filename)
+ : std::istream(new IFileStreambuf(filename))
+{
+}
+
+IFileStream::~IFileStream()
+{
+ delete rdbuf();
+}
+
+//---------------------------------------------------------------------------
+
+OFileStream::OFileStream(const std::string& filename)
+ : std::ostream(new OFileStreambuf(filename))
+{
+}
+
+OFileStream::~OFileStream()
+{
+ delete rdbuf();
+}
--- /dev/null
+// $Id$
+//
+// SuperTux
+// Copyright (C) 2006 Matthias Braun <matze@braunis.de>
+//
+// 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 __PHYSFSSTREAM_HPP__
+#define __PHYSFSSTREAM_HPP__
+
+#include <stddef.h>
+#include <physfs.h>
+#include <string>
+#include <streambuf>
+#include <iostream>
+
+/** This class implements a C++ streambuf object for physfs files.
+ * So that you can use normal istream operations on them
+ */
+class IFileStreambuf : public std::streambuf
+{
+public:
+ IFileStreambuf(const std::string& filename);
+ ~IFileStreambuf();
+
+protected:
+ virtual int underflow();
+ virtual pos_type seekoff(off_type pos, std::ios_base::seekdir,
+ std::ios_base::openmode);
+ virtual pos_type seekpos(pos_type pos, std::ios_base::openmode);
+
+private:
+ PHYSFS_file* file;
+ char buf[1024];
+};
+
+class OFileStreambuf : public std::streambuf
+{
+public:
+ OFileStreambuf(const std::string& filename);
+ ~OFileStreambuf();
+
+protected:
+ virtual int overflow(int c);
+ virtual int sync();
+
+private:
+ PHYSFS_file* file;
+ char buf[1024];
+};
+
+class IFileStream : public std::istream
+{
+public:
+ IFileStream(const std::string& filename);
+ ~IFileStream();
+};
+
+class OFileStream : public std::ostream
+{
+public:
+ OFileStream(const std::string& filename);
+ ~OFileStream();
+};
+
+#endif
--- /dev/null
+// $Id$
+//
+// SuperTux
+// Copyright (C) 2004 Tobias Glaesser <tobi.web@gmx.de>
+// Copyright (C) 2006 Matthias Braun <matze@braunis.de>
+//
+// 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 <config.h>
+
+#include "physic.hpp"
+
+Physic::Physic()
+ : ax(0), ay(0), vx(0), vy(0), gravity_enabled_flag(true), gravity(10 * 100)
+{
+}
+
+Physic::~Physic()
+{
+}
+
+void
+Physic::reset()
+{
+ ax = ay = vx = vy = 0;
+ gravity_enabled_flag = 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::set_velocity(const Vector& vector)
+{
+ vx = vector.x;
+ vy = vector.y;
+}
+
+void Physic::inverse_velocity_x()
+{
+ vx = -vx;
+}
+
+void Physic::inverse_velocity_y()
+{
+ vy = -vy;
+}
+
+float
+Physic::get_velocity_x() const
+{
+ return vx;
+}
+
+float
+Physic::get_velocity_y() const
+{
+ return vy;
+}
+
+Vector
+Physic::get_velocity() const
+{
+ return Vector(vx, 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() const
+{
+ return ax;
+}
+
+float
+Physic::get_acceleration_y() const
+{
+ return ay;
+}
+
+Vector
+Physic::get_acceleration() const
+{
+ return Vector(ax, ay);
+}
+
+void
+Physic::enable_gravity(bool enable_gravity)
+{
+ gravity_enabled_flag = enable_gravity;
+}
+
+bool
+Physic::gravity_enabled() const
+{
+ return gravity_enabled_flag;
+}
+
+void
+Physic::set_gravity(float gravity)
+{
+ this->gravity = gravity * 100;
+}
+
+float
+Physic::get_gravity() const
+{
+ return gravity / 100;
+}
+
+Vector
+Physic::get_movement(float elapsed_time)
+{
+ float grav = gravity_enabled_flag ? gravity : 0;
+
+ Vector result(
+ vx * elapsed_time + ax * elapsed_time * elapsed_time,
+ vy * elapsed_time + (ay + grav) * elapsed_time * elapsed_time
+ );
+ vx += ax * elapsed_time;
+ vy += (ay + grav) * elapsed_time;
+
+ return result;
+}
--- /dev/null
+// $Id$
+//
+// SuperTux
+// Copyright (C) 2004 Tobias Glaesser <tobi.web@gmx.de>
+// Copyright (C) 2006 Matthias Braun <matze@braunis.de>
+//
+// 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.hpp"
+
+/// Physics engine.
+/** 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(const Vector& vector);
+
+ void set_velocity_x(float vx);
+ void set_velocity_y(float vy);
+
+ /// Velocities invertion.
+ void inverse_velocity_x();
+ void inverse_velocity_y();
+
+ Vector get_velocity() const;
+ float get_velocity_x() const;
+ float get_velocity_y() const;
+
+ /// Set acceleration.
+ /** 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);
+
+ Vector get_acceleration() const;
+ float get_acceleration_x() const;
+ float get_acceleration_y() const;
+
+ /// Enables or disables handling of gravity.
+ void enable_gravity(bool gravity_enabled);
+ bool gravity_enabled() const;
+
+ /// Set gravity to apply to object when enabled
+ void set_gravity(float gravity);
+
+ /// Get gravity to apply to object when enabled
+ float get_gravity() const;
+
+ Vector get_movement(float elapsed_time);
+
+private:
+ /// horizontal and vertical acceleration
+ float ax, ay;
+ /// horizontal and vertical velocity
+ float vx, vy;
+ /// should we respect gravity in our calculations?
+ bool gravity_enabled_flag;
+ /// current gravity (multiplied by 100) to apply to object, if enabled
+ float gravity;
+};
+
+class UsesPhysic
+{
+public:
+ Physic physic;
+ friend class Sector;
+};
+
+#endif
--- /dev/null
+// $Id$
+//
+// SuperTux
+// Copyright (C) 2003 Tobias Glaesser <tobi.web@gmx.de>
+// Copyright (C) 2006 Matthias Braun <matze@braunis.de>
+//
+// 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 <config.h>
+
+#include <math.h>
+#include "lisp/writer.hpp"
+#include "lisp/lisp.hpp"
+#include "player_status.hpp"
+#include "resources.hpp"
+#include "gettext.hpp"
+#include "video/drawing_context.hpp"
+#include "audio/sound_manager.hpp"
+#include "sprite/sprite_manager.hpp"
+#include "math/vector.hpp"
+#include "main.hpp"
+#include "log.hpp"
+#include "timer.hpp"
+
+static const int START_COINS = 100;
+static const int MAX_COINS = 99999;
+
+PlayerStatus* player_status = 0;
+
+PlayerStatus::PlayerStatus()
+ : coins(START_COINS),
+ bonus(NO_BONUS),
+ max_fire_bullets(0),
+ max_ice_bullets(0),
+ score_multiplier(1),
+ max_score_multiplier(1)
+{
+ reset();
+
+ coin_surface.reset(new Surface("images/engine/hud/coins-0.png"));
+}
+
+PlayerStatus::~PlayerStatus()
+{
+}
+
+void PlayerStatus::reset()
+{
+ coins = START_COINS;
+ bonus = NO_BONUS;
+ score_multiplier = 1;
+ max_score_multiplier = 1;
+}
+
+void
+PlayerStatus::add_coins(int count, bool play_sound)
+{
+ static float sound_played_time = 0;
+ coins = std::min(coins + count, MAX_COINS);
+ if(play_sound) {
+ if(count >= 100)
+ sound_manager->play("sounds/lifeup.wav");
+ else if (real_time > sound_played_time + 0.010) {
+ sound_manager->play("sounds/coin.wav");
+ sound_played_time = real_time;
+ }
+ }
+}
+
+void
+PlayerStatus::write(lisp::Writer& writer)
+{
+ switch(bonus) {
+ case NO_BONUS:
+ writer.write_string("bonus", "none");
+ break;
+ case GROWUP_BONUS:
+ writer.write_string("bonus", "growup");
+ break;
+ case FIRE_BONUS:
+ writer.write_string("bonus", "fireflower");
+ break;
+ case ICE_BONUS:
+ writer.write_string("bonus", "iceflower");
+ break;
+ default:
+ log_warning << "Unknown bonus type." << std::endl;
+ writer.write_string("bonus", "none");
+ }
+ writer.write_int("fireflowers", max_fire_bullets);
+ writer.write_int("iceflowers", max_ice_bullets);
+
+ writer.write_int("coins", coins);
+ writer.write_int("max-score-multiplier", max_score_multiplier);
+}
+
+void
+PlayerStatus::read(const lisp::Lisp& lisp)
+{
+ reset();
+
+ std::string bonusname;
+ if(lisp.get("bonus", bonusname)) {
+ if(bonusname == "none") {
+ bonus = NO_BONUS;
+ } else if(bonusname == "growup") {
+ bonus = GROWUP_BONUS;
+ } else if(bonusname == "fireflower") {
+ bonus = FIRE_BONUS;
+ } else if(bonusname == "iceflower") {
+ bonus = ICE_BONUS;
+ } else {
+ log_warning << "Unknown bonus '" << bonusname << "' in savefile" << std::endl;
+ bonus = NO_BONUS;
+ }
+ }
+ lisp.get("fireflowers", max_fire_bullets);
+ lisp.get("iceflowers", max_ice_bullets);
+
+ lisp.get("coins", coins);
+ lisp.get("max-score-multiplier", max_score_multiplier);
+}
+
+void
+PlayerStatus::draw(DrawingContext& context)
+{
+ static int displayed_coins = -1;
+ static int next_count = 0;
+
+ if ((displayed_coins == -1) || (fabsf(displayed_coins - coins) > 100)) {
+ displayed_coins = coins;
+ }
+ if (++next_count > 2) {
+ next_count = 0;
+ if (displayed_coins < coins) displayed_coins++;
+ if (displayed_coins > coins) displayed_coins--;
+ }
+ displayed_coins = std::min(std::max(displayed_coins, 0), 9999);
+
+ std::stringstream ss;
+ ss << displayed_coins;
+ std::string coins_text = ss.str();
+
+ context.push_transform();
+ context.set_translation(Vector(0, 0));
+
+ Surface* coin_surf = coin_surface.get();
+ if (coin_surf) {
+ context.draw_surface(coin_surf, Vector(SCREEN_WIDTH - BORDER_X - coin_surf->get_width() - gold_fixed_text->get_text_width(coins_text), BORDER_Y + 1), LAYER_HUD);
+ }
+ context.draw_text(gold_fixed_text, coins_text, Vector(SCREEN_WIDTH - BORDER_X, BORDER_Y), ALIGN_RIGHT, LAYER_HUD);
+
+ context.pop_transform();
+}
+
+void
+PlayerStatus::operator= (const PlayerStatus& other)
+{
+ coins = other.coins;
+ bonus = other.bonus;
+ score_multiplier = other.score_multiplier;
+ max_score_multiplier = other.max_score_multiplier;
+}
--- /dev/null
+// $Id$
+//
+// SuperTux
+// Copyright (C) 2003 Tobias Glaesser <tobi.web@gmx.de>
+// Copyright (C) 2006 Matthias Braun <matze@braunis.de>
+//
+// 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_PLAYERSTATUS_H
+#define SUPERTUX_PLAYERSTATUS_H
+
+#include <memory>
+#include "serializable.hpp"
+
+namespace lisp{ class Writer; }
+namespace lisp{ class Lisp; }
+class Surface;
+
+static const float BORDER_X = 10;
+static const float BORDER_Y = 10;
+
+enum BonusType {
+ NO_BONUS, GROWUP_BONUS, FIRE_BONUS, ICE_BONUS
+};
+class DrawingContext;
+
+/**
+ * This class memorizes player status between different game sessions (for
+ * example when switching maps in the worldmap)
+ */
+class PlayerStatus : public Serializable
+{
+public:
+ PlayerStatus();
+ ~PlayerStatus();
+ void reset();
+ void add_coins(int count, bool play_sound = true);
+
+ void write(lisp::Writer& writer);
+ void read(const lisp::Lisp& lisp);
+
+ void draw(DrawingContext& context);
+
+ int coins;
+ BonusType bonus;
+ int max_fire_bullets; /**< maximum number of fire bullets in play */
+ int max_ice_bullets; /**< maximum number of ice bullets in play */
+
+ int score_multiplier;
+ int max_score_multiplier;
+
+ void operator= (const PlayerStatus& other);
+
+private:
+ // don't use this
+ PlayerStatus(const PlayerStatus& other);
+
+ std::auto_ptr<Surface> coin_surface;
+};
+
+// global player state
+extern PlayerStatus* player_status;
+
+#endif
--- /dev/null
+// $Id$
+//
+// A strong random number generator
+//
+// Copyright (C) 2006 Allen King
+// Copyright (C) 2002 Michael Ringgaard. All rights reserved.
+// Copyright (C) 1983, 1993 The Regents of the University of California.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions
+// are met:
+//
+// 1. Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+// 2. Redistributions in binary form must reproduce the above copyright
+// notice, this list of conditions and the following disclaimer in the
+// documentation and/or other materials provided with the distribution.
+// 3. Neither the name of the project nor the names of its contributors
+// may be used to endorse or promote products derived from this software
+// without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
+// ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE
+// FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+// DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+// OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+// HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+// LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+// OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+// SUCH DAMAGE.
+
+// Transliterated into C++ Allen King 060417, from sources on
+// http://www.jbox.dk/sanos/source/lib/random.c.html
+#include <config.h>
+
+
+#include <stdexcept>
+#include <time.h>
+#include <cassert>
+#include "random_generator.hpp"
+
+RandomGenerator systemRandom; // global random number generator
+
+RandomGenerator::RandomGenerator() {
+ assert(sizeof(int) >= 4);
+ initialized = 0;
+ debug = 0; // change this by hand for debug
+ initialize();
+}
+
+RandomGenerator::~RandomGenerator() {
+}
+
+int RandomGenerator::srand(int x) {
+ int x0 = x;
+ while (x <= 0) // random seed of zero means
+ x = time(0) % RandomGenerator::rand_max; // randomize with time
+
+ if (debug > 0)
+ printf("==== srand(%10d) (%10d) rand_max=%x =====\n",
+ x, x0, RandomGenerator::rand_max);
+
+ RandomGenerator::srandom(x);
+ return x; // let caller know seed used
+}
+
+int RandomGenerator::rand() {
+ int rv; // a posative int
+ while ((rv = RandomGenerator::random()) <= 0) // neg or zero causes probs
+ ;
+ if (debug > 0)
+ printf("==== rand(): %10d =====\n", rv);
+ return rv;
+}
+
+int RandomGenerator::rand(int v) {
+ assert(v >= 0 && v <= RandomGenerator::rand_max); // illegal arg
+
+ // remove biases, esp. when v is large (e.g. v == (rand_max/4)*3;)
+ int rv, maxV =(RandomGenerator::rand_max / v) * v;
+ assert(maxV <= RandomGenerator::rand_max);
+ while ((rv = RandomGenerator::random()) >= maxV)
+ ;
+ return rv % v; // mod it down to 0..(maxV-1)
+}
+
+int RandomGenerator::rand(int u, int v) {
+ assert(v > u);
+ return u + RandomGenerator::rand(v-u);
+}
+
+double RandomGenerator::randf(double v) {
+ float rv;
+ do {
+ rv = ((double)RandomGenerator::random())/RandomGenerator::rand_max * v;
+ } while (rv >= v); // rounding might cause rv==v
+
+ if (debug > 0)
+ printf("==== rand(): %f =====\n", rv);
+ return rv;
+}
+
+double RandomGenerator::randf(double u, double v) {
+ return u + RandomGenerator::randf(v-u);
+}
+
+//-----------------------------------------------------------------------
+//
+// Copyright (C) 2002 Michael Ringgaard. All rights reserved.
+// Copyright (C) 1983, 1993 The Regents of the University of California.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions
+// are met:
+//
+// 1. Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+// 2. Redistributions in binary form must reproduce the above copyright
+// notice, this list of conditions and the following disclaimer in the
+// documentation and/or other materials provided with the distribution.
+// 3. Neither the name of the project nor the names of its contributors
+// may be used to endorse or promote products derived from this software
+// without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
+// ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE
+// FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+// DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+// OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+// HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+// LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+// OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+// SUCH DAMAGE.
+//
+
+//**#include <os.h>
+
+//
+// An improved random number generation package. In addition to the standard
+// rand()/srand() like interface, this package also has a special state info
+// interface. The initstate() routine is called with a seed, an array of
+// bytes, and a count of how many bytes are being passed in; this array is
+// then initialized to contain information for random number generation with
+// that much state information. Good sizes for the amount of state
+// information are 32, 64, 128, and 256 bytes. The state can be switched by
+// calling the setstate() routine with the same array as was initiallized
+// with initstate(). By default, the package runs with 128 bytes of state
+// information and generates far better random numbers than a linear
+// congruential generator. If the amount of state information is less than
+// 32 bytes, a simple linear congruential R.N.G. is used.
+//
+// Internally, the state information is treated as an array of longs; the
+// zeroeth element of the array is the type of R.N.G. being used (small
+// integer); the remainder of the array is the state information for the
+// R.N.G. Thus, 32 bytes of state information will give 7 longs worth of
+// state information, which will allow a degree seven polynomial. (Note:
+// the zeroeth word of state information also has some other information
+// stored in it -- see setstate() for details).
+//
+// The random number generation technique is a linear feedback shift register
+// approach, employing trinomials (since there are fewer terms to sum up that
+// way). In this approach, the least significant bit of all the numbers in
+// the state table will act as a linear feedback shift register, and will
+// have period 2^deg - 1 (where deg is the degree of the polynomial being
+// used, assuming that the polynomial is irreducible and primitive). The
+// higher order bits will have longer periods, since their values are also
+// influenced by pseudo-random carries out of the lower bits. The total
+// period of the generator is approximately deg*(2**deg - 1); thus doubling
+// the amount of state information has a vast influence on the period of the
+// generator. Note: the deg*(2**deg - 1) is an approximation only good for
+// large deg, when the period of the shift is the dominant factor.
+// With deg equal to seven, the period is actually much longer than the
+// 7*(2**7 - 1) predicted by this formula.
+//
+// Modified 28 December 1994 by Jacob S. Rosenberg.
+//
+
+//
+// For each of the currently supported random number generators, we have a
+// break value on the amount of state information (you need at least this
+// many bytes of state info to support this random number generator), a degree
+// for the polynomial (actually a trinomial) that the R.N.G. is based on, and
+// the separation between the two lower order coefficients of the trinomial.
+
+void RandomGenerator::initialize() {
+
+#define NSHUFF 100 // To drop part of seed -> 1st value correlation
+
+//static long degrees[MAX_TYPES] = { DEG_0, DEG_1, DEG_2, DEG_3, DEG_4 };
+//static long seps [MAX_TYPES] = { SEP_0, SEP_1, SEP_2, SEP_3, SEP_4 };
+
+ degrees[0] = DEG_0;
+ degrees[1] = DEG_1;
+ degrees[2] = DEG_2;
+ degrees[3] = DEG_3;
+ degrees[4] = DEG_4;
+
+ seps [0] = SEP_0;
+ seps [1] = SEP_1;
+ seps [2] = SEP_2;
+ seps [3] = SEP_3;
+ seps [4] = SEP_4;
+
+//
+// Initially, everything is set up as if from:
+//
+// initstate(1, randtbl, 128);
+//
+// Note that this initialization takes advantage of the fact that srandom()
+// advances the front and rear pointers 10*rand_deg times, and hence the
+// rear pointer which starts at 0 will also end up at zero; thus the zeroeth
+// element of the state information, which contains info about the current
+// position of the rear pointer is just
+//
+// MAX_TYPES * (rptr - state) + TYPE_3 == TYPE_3.
+
+ randtbl[ 0] = TYPE_3;
+ randtbl[ 1] = 0x991539b1;
+ randtbl[ 2] = 0x16a5bce3;
+ randtbl[ 3] = 0x6774a4cd;
+ randtbl[ 4] = 0x3e01511e;
+ randtbl[ 5] = 0x4e508aaa;
+ randtbl[ 6] = 0x61048c05;
+ randtbl[ 7] = 0xf5500617;
+ randtbl[ 8] = 0x846b7115;
+ randtbl[ 9] = 0x6a19892c;
+ randtbl[10] = 0x896a97af;
+ randtbl[11] = 0xdb48f936;
+ randtbl[12] = 0x14898454;
+ randtbl[13] = 0x37ffd106;
+ randtbl[14] = 0xb58bff9c;
+ randtbl[15] = 0x59e17104;
+ randtbl[16] = 0xcf918a49;
+ randtbl[17] = 0x09378c83;
+ randtbl[18] = 0x52c7a471;
+ randtbl[19] = 0x8d293ea9;
+ randtbl[20] = 0x1f4fc301;
+ randtbl[21] = 0xc3db71be;
+ randtbl[22] = 0x39b44e1c;
+ randtbl[23] = 0xf8a44ef9;
+ randtbl[24] = 0x4c8b80b1;
+ randtbl[25] = 0x19edc328;
+ randtbl[26] = 0x87bf4bdd;
+ randtbl[27] = 0xc9b240e5;
+ randtbl[28] = 0xe9ee4b1b;
+ randtbl[29] = 0x4382aee7;
+ randtbl[30] = 0x535b6b41;
+ randtbl[31] = 0xf3bec5da;
+
+// static long randtbl[DEG_3 + 1] =
+// {
+// TYPE_3;
+// 0x991539b1, 0x16a5bce3, 0x6774a4cd, 0x3e01511e, 0x4e508aaa, 0x61048c05,
+// 0xf5500617, 0x846b7115, 0x6a19892c, 0x896a97af, 0xdb48f936, 0x14898454,
+// 0x37ffd106, 0xb58bff9c, 0x59e17104, 0xcf918a49, 0x09378c83, 0x52c7a471,
+// 0x8d293ea9, 0x1f4fc301, 0xc3db71be, 0x39b44e1c, 0xf8a44ef9, 0x4c8b80b1,
+// 0x19edc328, 0x87bf4bdd, 0xc9b240e5, 0xe9ee4b1b, 0x4382aee7, 0x535b6b41,
+// 0xf3bec5da
+// };
+
+
+//
+// fptr and rptr are two pointers into the state info, a front and a rear
+// pointer. These two pointers are always rand_sep places aparts, as they
+// cycle cyclically through the state information. (Yes, this does mean we
+// could get away with just one pointer, but the code for random() is more
+// efficient this way). The pointers are left positioned as they would be
+// from the call
+//
+// initstate(1, randtbl, 128);
+//
+// (The position of the rear pointer, rptr, is really 0 (as explained above
+// in the initialization of randtbl) because the state table pointer is set
+// to point to randtbl[1] (as explained below).
+//
+
+ fptr = &randtbl[SEP_3 + 1];
+ rptr = &randtbl[1];
+
+//
+// The following things are the pointer to the state information table, the
+// type of the current generator, the degree of the current polynomial being
+// used, and the separation between the two pointers. Note that for efficiency
+// of random(), we remember the first location of the state information, not
+// the zeroeth. Hence it is valid to access state[-1], which is used to
+// store the type of the R.N.G. Also, we remember the last location, since
+// this is more efficient than indexing every time to find the address of
+// the last element to see if the front and rear pointers have wrapped.
+//
+
+ state = &randtbl[1];
+ rand_type = TYPE_3;
+ rand_deg = DEG_3;
+ rand_sep = SEP_3;
+ end_ptr = &randtbl[DEG_3 + 1];
+
+}
+
+//
+// Compute x = (7^5 * x) mod (2^31 - 1)
+// wihout overflowing 31 bits:
+// (2^31 - 1) = 127773 * (7^5) + 2836
+// From "Random number generators: good ones are hard to find",
+// Park and Miller, Communications of the ACM, vol. 31, no. 10,
+// October 1988, p. 1195.
+//
+
+__inline static long good_rand(long x)
+{
+ long hi, lo;
+
+ // Can't be initialized with 0, so use another value.
+ if (x == 0) x = 123459876;
+ hi = x / 127773;
+ lo = x % 127773;
+ x = 16807 * lo - 2836 * hi;
+ if (x < 0) x += 0x7fffffff;
+ return x;
+}
+
+//
+// srandom
+//
+// Initialize the random number generator based on the given seed. If the
+// type is the trivial no-state-information type, just remember the seed.
+// Otherwise, initializes state[] based on the given "seed" via a linear
+// congruential generator. Then, the pointers are set to known locations
+// that are exactly rand_sep places apart. Lastly, it cycles the state
+// information a given number of times to get rid of any initial dependencies
+// introduced by the L.C.R.N.G. Note that the initialization of randtbl[]
+// for default usage relies on values produced by this routine.
+
+void RandomGenerator::srandom(unsigned long x)
+{
+ long i, lim;
+
+ state[0] = x;
+ if (rand_type == TYPE_0)
+ lim = NSHUFF;
+ else
+ {
+ for (i = 1; i < rand_deg; i++) state[i] = good_rand(state[i - 1]);
+ fptr = &state[rand_sep];
+ rptr = &state[0];
+ lim = 10 * rand_deg;
+ }
+
+ initialized = 1;
+ for (i = 0; i < lim; i++) random();
+}
+
+#ifdef NOT_FOR_SUPERTUX // use in supertux doesn't require these methods,
+ // which are not portable to as many platforms as
+ // SDL. The cost is that the variability of the
+ // initial seed is reduced to only 32 bits of
+ // randomness, seemingly enough. PAK 060420
+//
+// srandomdev
+//
+// Many programs choose the seed value in a totally predictable manner.
+// This often causes problems. We seed the generator using the much more
+// secure random() interface. Note that this particular seeding
+// procedure can generate states which are impossible to reproduce by
+// calling srandom() with any value, since the succeeding terms in the
+// state buffer are no longer derived from the LC algorithm applied to
+// a fixed seed.
+
+void RandomGenerator::srandomdev()
+{
+ int fd, done;
+ size_t len;
+
+ if (rand_type == TYPE_0)
+ len = sizeof state[0];
+ else
+ len = rand_deg * sizeof state[0];
+
+ done = 0;
+ fd = open("/dev/urandom", O_RDONLY);
+ if (fd >= 0)
+ {
+ if (read(fd, state, len) == len) done = 1;
+ close(fd);
+ }
+
+ if (!done)
+ {
+ struct timeval tv;
+
+ gettimeofday(&tv, NULL);
+ srandom(tv.tv_sec ^ tv.tv_usec);
+ return;
+ }
+
+ if (rand_type != TYPE_0)
+ {
+ fptr = &state[rand_sep];
+ rptr = &state[0];
+ }
+ initialized = 1;
+}
+
+//
+// initstate
+//
+// Initialize the state information in the given array of n bytes for future
+// random number generation. Based on the number of bytes we are given, and
+// the break values for the different R.N.G.'s, we choose the best (largest)
+// one we can and set things up for it. srandom() is then called to
+// initialize the state information.
+//
+// Note that on return from srandom(), we set state[-1] to be the type
+// multiplexed with the current value of the rear pointer; this is so
+// successive calls to initstate() won't lose this information and will be
+// able to restart with setstate().
+//
+// Note: the first thing we do is save the current state, if any, just like
+// setstate() so that it doesn't matter when initstate is called.
+//
+// Returns a pointer to the old state.
+//
+
+char * RandomGenerator::initstate(unsigned long seed, char *arg_state, long n)
+{
+ char *ostate = (char *) (&state[-1]);
+ long *long_arg_state = (long *) arg_state;
+
+ if (rand_type == TYPE_0)
+ state[-1] = rand_type;
+ else
+ state[-1] = MAX_TYPES * (rptr - state) + rand_type;
+
+ if (n < BREAK_0) return NULL;
+
+ if (n < BREAK_1)
+ {
+ rand_type = TYPE_0;
+ rand_deg = DEG_0;
+ rand_sep = SEP_0;
+ }
+ else if (n < BREAK_2)
+ {
+ rand_type = TYPE_1;
+ rand_deg = DEG_1;
+ rand_sep = SEP_1;
+ }
+ else if (n < BREAK_3)
+ {
+ rand_type = TYPE_2;
+ rand_deg = DEG_2;
+ rand_sep = SEP_2;
+ }
+ else if (n < BREAK_4)
+ {
+ rand_type = TYPE_3;
+ rand_deg = DEG_3;
+ rand_sep = SEP_3;
+ }
+ else
+ {
+ rand_type = TYPE_4;
+ rand_deg = DEG_4;
+ rand_sep = SEP_4;
+ }
+
+ state = (long *) (long_arg_state + 1); // First location
+ end_ptr = &state[rand_deg]; // Must set end_ptr before srandom
+ srandom(seed);
+
+ if (rand_type == TYPE_0)
+ long_arg_state[0] = rand_type;
+ else
+ long_arg_state[0] = MAX_TYPES * (rptr - state) + rand_type;
+
+ initialized = 1;
+ return ostate;
+}
+
+//
+// setstate
+//
+// Restore the state from the given state array.
+//
+// Note: it is important that we also remember the locations of the pointers
+// in the current state information, and restore the locations of the pointers
+// from the old state information. This is done by multiplexing the pointer
+// location into the zeroeth word of the state information.
+//
+// Note that due to the order in which things are done, it is OK to call
+// setstate() with the same state as the current state.
+//
+// Returns a pointer to the old state information.
+//
+
+char * RandomGenerator::setstate(char *arg_state)
+{
+ long *new_state = (long *) arg_state;
+ long type = new_state[0] % MAX_TYPES;
+ long rear = new_state[0] / MAX_TYPES;
+ char *ostate = (char *) (&state[-1]);
+
+ if (rand_type == TYPE_0)
+ state[-1] = rand_type;
+ else
+ state[-1] = MAX_TYPES * (rptr - state) + rand_type;
+
+ switch(type)
+ {
+ case TYPE_0:
+ case TYPE_1:
+ case TYPE_2:
+ case TYPE_3:
+ case TYPE_4:
+ rand_type = type;
+ rand_deg = degrees[type];
+ rand_sep = seps[type];
+ break;
+ }
+
+ state = (long *) (new_state + 1);
+ if (rand_type != TYPE_0)
+ {
+ rptr = &state[rear];
+ fptr = &state[(rear + rand_sep) % rand_deg];
+ }
+ end_ptr = &state[rand_deg]; // Set end_ptr too
+
+ initialized = 1;
+ return ostate;
+}
+#endif //NOT_FOR_SUPERTUX
+//
+// random:
+//
+// If we are using the trivial TYPE_0 R.N.G., just do the old linear
+// congruential bit. Otherwise, we do our fancy trinomial stuff, which is
+// the same in all the other cases due to all the global variables that have
+// been set up. The basic operation is to add the number at the rear pointer
+// into the one at the front pointer. Then both pointers are advanced to
+// the next location cyclically in the table. The value returned is the sum
+// generated, reduced to 31 bits by throwing away the "least random" low bit.
+//
+// Note: the code takes advantage of the fact that both the front and
+// rear pointers can't wrap on the same call by not testing the rear
+// pointer if the front one has wrapped.
+//
+// Returns a 31-bit random number.
+//
+
+long RandomGenerator::random()
+{
+ long i;
+ long *f, *r;
+ if (!initialized) {
+ throw std::runtime_error("uninitialized RandomGenerator object");
+ }
+
+ if (rand_type == TYPE_0)
+ {
+ i = state[0];
+ state[0] = i = (good_rand(i)) & 0x7fffffff;
+ }
+ else
+ {
+ f = fptr; r = rptr;
+ *f += *r;
+ i = (*f >> 1) & 0x7fffffff; // Chucking least random bit
+ if (++f >= end_ptr)
+ {
+ f = state;
+ ++r;
+ }
+ else if (++r >= end_ptr)
+ r = state;
+
+ fptr = f; rptr = r;
+ }
+
+ return i;
+}
--- /dev/null
+// $Id$
+//
+// A strong random number generator
+//
+// Copyright (C) 2006 Allen King
+// Copyright (C) 2002 Michael Ringgaard. All rights reserved.
+// Copyright (C) 1983, 1993 The Regents of the University of California.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions
+// are met:
+//
+// 1. Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+// 2. Redistributions in binary form must reproduce the above copyright
+// notice, this list of conditions and the following disclaimer in the
+// documentation and/or other materials provided with the distribution.
+// 3. Neither the name of the project nor the names of its contributors
+// may be used to endorse or promote products derived from this software
+// without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
+// ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE
+// FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+// DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+// OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+// HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+// LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+// OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+// SUCH DAMAGE.
+
+#ifndef __RANDOM_GENERATOR__
+#define __RANDOM_GENERATOR__
+
+class RandomGenerator
+{
+private:
+// Array versions of the above information to make code run faster --
+// relies on fact that TYPE_i == i.
+ static const int TYPE_0 = 0; // Linear congruential
+ static const int BREAK_0 = 8;
+ static const int DEG_0 = 0;
+ static const int SEP_0 = 0;
+
+ static const int TYPE_1 = 1; // x**7 + x**3 + 1
+ static const int BREAK_1 = 32;
+ static const int DEG_1 = 7;
+ static const int SEP_1 = 3;
+
+ static const int TYPE_2 = 2; // x**15 + x + 1
+ static const int BREAK_2 = 64;
+ static const int DEG_2 = 15;
+ static const int SEP_2 = 1;
+
+ static const int TYPE_3 = 3; // x**31 + x**3 + 1
+ static const int BREAK_3 = 128;
+ static const int DEG_3 = 31;
+ static const int SEP_3 = 3;
+
+ static const int TYPE_4 = 4; // x**63 + x + 1
+ static const int BREAK_4 = 256;
+ static const int DEG_4 = 63;
+ static const int SEP_4 = 1;
+
+ static const int MAX_TYPES = 5; // Max number of types above
+
+ bool initialized;
+ long degrees[MAX_TYPES];
+ long seps [MAX_TYPES];
+ long randtbl[DEG_3 + 1];
+
+ long *fptr;
+ long *rptr;
+
+ long *state;
+ long rand_type;
+ long rand_deg;
+ long rand_sep;
+ long *end_ptr;
+ int debug;
+ static const int rand_max = 0x7fffffff; // biggest signed Uint32
+
+public:
+ RandomGenerator();
+ ~RandomGenerator();
+
+// Documentation of user-visible calls:
+
+ // Initialize the RNG with a 31-bit seed
+ // if x is zero or absent, calls to time() will get a time-randomized seed
+ // the value returned is the value of the seed used.
+ int srand(int x=0);
+
+ // generate random 31-bit numbers
+ // calls to the following return a value evenly distributed between u (or
+ // 0 if not specified) and v (or rand_max if not specified). Return
+ // values may include u, but never v.
+ int rand();
+ int rand(int v);
+ int rand(int u, int v);
+ double randf(double v);
+ double randf(double u, double v);
+
+ // For Squirrel wrapper, since miniswig (and even squirrel?) doesn't
+ // support function overloading or doubles
+ int rand1i(int v) { return rand(v); }
+ int rand2i(int u, int v) { return rand(u, v); }
+ float rand1f(float v)
+ { return static_cast<float>(randf(static_cast<double>(v))); }
+ float rand2f(float u, float v)
+ { return static_cast<float>(randf(static_cast<double>(u),
+ static_cast<double>(v))); }
+
+//private:
+ void initialize();
+ void srandom(unsigned long x);
+// void srandomdev();
+// char *initstate(unsigned long seed, char *arg_state, long n);
+// char *setstate(char *arg_state);
+ long random();
+};
+
+extern RandomGenerator systemRandom;
+
+#endif //__RANDOM_GENERATOR__
--- /dev/null
+// $Id$
+//
+// SuperTux
+// Copyright (C) 2006 Matthias Braun <matze@braunis.de>
+//
+// 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 __REF_HPP__
+#define __REF_HPP__
+
+/** This class behaves like a pointer to a refcounted object, but increments the
+ * reference count when new objects are assigned and decrements the refcounter
+ * when it's lifetime has experied. (similar to std::auto_ptr)
+ */
+template<typename T>
+class Ref
+{
+public:
+ Ref(T* object = 0)
+ : object(object)
+ {
+ if(object)
+ object->ref();
+ }
+ Ref(const Ref<T>& other)
+ : object(other.object)
+ {
+ if(object)
+ object->ref();
+ }
+ ~Ref()
+ {
+ if(object)
+ object->unref();
+ }
+
+ void operator= (const Ref<T>& other)
+ {
+ *this = other.get();
+ }
+
+ void operator= (T* object)
+ {
+ if(object)
+ object->ref();
+ if(this->object)
+ this->object->unref();
+ this->object = object;
+ }
+
+ T* operator ->() const
+ {
+ return object;
+ }
+
+ T& operator* () const
+ {
+ return *object;
+ }
+
+ operator const T* () const
+ {
+ return object;
+ }
+
+ T* get() const
+ {
+ return object;
+ }
+
+private:
+ T* object;
+};
+
+#endif
--- /dev/null
+// $Id$
+//
+// Windstille - A Jump'n Shoot Game
+// Copyright (C) 2005 Matthias Braun <matze@braunis.de>
+//
+// 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 __REFCOUNTER_HPP__
+#define __REFCOUNTER_HPP__
+
+#include <assert.h>
+
+/**
+ * A base class that provides reference counting facilities
+ */
+class RefCounter
+{
+public:
+ RefCounter()
+ : refcount(0)
+ { }
+
+ /** increases reference count */
+ void ref()
+ {
+ refcount++;
+ }
+ /** decreases reference count. Destroys the object if the reference count
+ * reaches 0
+ */
+ void unref()
+ {
+ refcount--;
+ if(refcount <= 0) {
+ delete this;
+ }
+ }
+
+protected:
+ virtual ~RefCounter()
+ {
+ assert(refcount == 0);
+ }
+
+private:
+ int refcount;
+};
+
+#endif
--- /dev/null
+// $Id$
+//
+// SuperTux
+// Copyright (C) 2003 Tobias Glaesser <tobi.web@gmx.de>
+// Copyright (C) 2006 Matthias Braun <matze@braunis.de>
+//
+// 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 <config.h>
+
+#include "sprite/sprite_manager.hpp"
+#include "gui/menu.hpp"
+#include "gui/button.hpp"
+#include "resources.hpp"
+#include "file_system.hpp"
+#include "tile_manager.hpp"
+#include "object/gameobjs.hpp"
+#include "object/player.hpp"
+
+MouseCursor* mouse_cursor = NULL;
+
+Font* gold_text = NULL;
+Font* gold_fixed_text = NULL;
+Font* blue_text = NULL;
+Font* gray_text = NULL;
+Font* white_text = NULL;
+Font* white_small_text = NULL;
+Font* white_big_text = NULL;
+
+/* Load graphics/sounds shared between all levels: */
+void load_shared()
+{
+ /* Load the mouse-cursor */
+ mouse_cursor = new MouseCursor("images/engine/menu/mousecursor.png");
+ MouseCursor::set_current(mouse_cursor);
+
+ /* Load global images: */
+ gold_text = new Font(Font::VARIABLE,
+ "images/engine/fonts/gold.png",
+ "images/engine/fonts/shadow.png", 16, 18);
+ gold_fixed_text = new Font(Font::FIXED,
+ "images/engine/fonts/gold.png",
+ "images/engine/fonts/shadow.png", 16, 18);
+ blue_text = new Font(Font::VARIABLE,
+ "images/engine/fonts/blue.png",
+ "images/engine/fonts/shadow.png", 16, 18, 3);
+ white_text = new Font(Font::VARIABLE,
+ "images/engine/fonts/white.png",
+ "images/engine/fonts/shadow.png", 16, 18);
+ gray_text = new Font(Font::VARIABLE,
+ "images/engine/fonts/gray.png",
+ "images/engine/fonts/shadow.png", 16, 18);
+ white_small_text = new Font(Font::VARIABLE,
+ "images/engine/fonts/white-small.png",
+ "images/engine/fonts/shadow-small.png", 8, 9, 1);
+ white_big_text = new Font(Font::VARIABLE,
+ "images/engine/fonts/white-big.png",
+ "images/engine/fonts/shadow-big.png", 20, 22, 3);
+
+ Menu::default_font = white_text;
+ Menu::active_font = blue_text;
+ Menu::deactive_font = gray_text;
+ Menu::label_font = white_big_text;
+ Menu::field_font = gold_text;
+
+ Button::info_font = white_small_text;
+
+ sprite_manager = new SpriteManager();
+ tile_manager = new TileManager("images/tiles.strf");
+
+ /* Tuxes: */
+ char img_name[1024];
+ for (int i = 0; i < GROWING_FRAMES; i++)
+ {
+ snprintf(img_name, sizeof(img_name), "images/creatures/tux_grow/left-%i.png", i+1);
+ growingtux_left[i] = new Surface(img_name);
+
+ snprintf(img_name, sizeof(img_name), "images/creatures/tux_grow/right-%i.png", i+1);
+ growingtux_right[i] = new Surface(img_name);
+ }
+
+ small_tux = new TuxBodyParts();
+ small_tux->head = 0;
+ small_tux->body = sprite_manager->create("images/creatures/tux_small/small-tux-body.sprite");
+ small_tux->arms = sprite_manager->create("images/creatures/tux_small/small-tux-arms.sprite");
+ small_tux->feet = 0;
+
+ big_tux = new TuxBodyParts();
+ big_tux->head = sprite_manager->create("images/creatures/tux_big/big-tux-head.sprite");
+ big_tux->body = sprite_manager->create("images/creatures/tux_big/big-tux-body.sprite");
+ big_tux->arms = sprite_manager->create("images/creatures/tux_big/big-tux-arms.sprite");
+ big_tux->feet = sprite_manager->create("images/creatures/tux_big/big-tux-feet.sprite");
+
+ fire_tux = new TuxBodyParts();
+ fire_tux->head = sprite_manager->create("images/creatures/tux_big/big-fire-tux-head.sprite");
+ fire_tux->body = sprite_manager->create("images/creatures/tux_big/big-tux-body.sprite");
+ fire_tux->arms = sprite_manager->create("images/creatures/tux_big/big-tux-arms.sprite");
+ fire_tux->feet = sprite_manager->create("images/creatures/tux_big/big-tux-feet.sprite");
+
+ ice_tux = new TuxBodyParts();
+ ice_tux->head = sprite_manager->create("images/creatures/tux_big/big-ice-tux-head.sprite");
+ ice_tux->body = sprite_manager->create("images/creatures/tux_big/big-tux-body.sprite");
+ ice_tux->arms = sprite_manager->create("images/creatures/tux_big/big-tux-arms.sprite");
+ ice_tux->feet = sprite_manager->create("images/creatures/tux_big/big-tux-feet.sprite");
+
+ player_status = new PlayerStatus();
+}
+
+/* Free shared data: */
+void unload_shared()
+{
+ /* Free global images: */
+ delete gold_text;
+ delete gold_fixed_text;
+ delete white_text;
+ delete blue_text;
+ delete gray_text;
+ delete white_small_text;
+ delete white_big_text;
+
+ delete small_tux;
+ delete big_tux;
+ delete fire_tux;
+ delete ice_tux;
+
+ for (int i = 0; i < GROWING_FRAMES; i++) {
+ delete growingtux_left[i];
+ delete growingtux_right[i];
+ }
+
+ delete sprite_manager;
+ sprite_manager = NULL;
+ delete tile_manager;
+ tile_manager = NULL;
+
+ /* Free mouse-cursor */
+ delete mouse_cursor;
+
+ delete player_status;
+ player_status = NULL;
+}
--- /dev/null
+// $Id$
+//
+// SuperTux
+// Copyright (C) 2003 Tobias Glaesser <tobi.web@gmx.de>
+// Copyright (C) 2006 Matthias Braun <matze@braunis.de>
+//
+// 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_RESOURCES_H
+#define SUPERTUX_RESOURCES_H
+
+class Font;
+class MouseCursor;
+
+extern MouseCursor* mouse_cursor;
+
+extern Font* gold_text;
+extern Font* gold_fixed_text;
+extern Font* white_text;
+extern Font* blue_text;
+extern Font* gray_text;
+extern Font* white_small_text;
+extern Font* white_big_text;
+
+void load_shared();
+void unload_shared();
+
+#endif
--- /dev/null
+// $Id$
+//
+// SuperTux
+// Copyright (C) 2006 Matthias Braun <matze@braunis.de>
+//
+// 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 __SCREEN_HPP__
+#define __SCREEN_HPP__
+
+class DrawingContext;
+
+class Screen
+{
+public:
+ virtual ~Screen()
+ {}
+
+ /**
+ * gets called before this screen gets activated (which is at least once
+ * before the first draw or update call
+ */
+ virtual void setup()
+ {}
+ /** gets called when the current screen is temporarily suspended */
+ virtual void leave()
+ {}
+
+ /**
+ * gets called once per frame. The screen should draw itself in this function.
+ * State changes should not be done in this function, but rather in update
+ */
+ virtual void draw(DrawingContext& context) = 0;
+
+ /**
+ * gets called for once (per logical) frame. Screens should do their state
+ * updates and logic here
+ */
+ virtual void update(float elapsed_time) = 0;
+};
+
+#endif
--- /dev/null
+// $Id$
+//
+// SuperTux
+// Copyright (C) 2006 Matthias Braun <matze@braunis.de>
+//
+// 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 __SCREENFADE_HPP__
+#define __SCREENFADE_HPP__
+
+#include "screen.hpp"
+
+/**
+ * A ScreenFade screen is displayed simultaneously with another screen. This
+ * is intended to be used for transitional effects like fade-out or shrink-fade
+ */
+class ScreenFade : public Screen
+{
+public:
+ virtual ~ScreenFade()
+ {}
+
+ /// returns true if the effect is completed
+ virtual bool done() = 0;
+};
+
+#endif
--- /dev/null
+// $Id$
+//
+// SuperTux
+// Copyright (C) 2006 Matthias Braun <matze@braunis.de>
+//
+// 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 __SCRIPT_INTERFACE_HPP__
+#define __SCRIPT_INTERFACE_HPP__
+
+#include <squirrel.h>
+
+/**
+ * Objects that want to expose themself to the scripting environment
+ * should implement this interface
+ */
+class ScriptInterface
+{
+public:
+ virtual ~ScriptInterface()
+ {}
+
+ virtual void expose(HSQUIRRELVM vm, SQInteger table_idx) = 0;
+ virtual void unexpose(HSQUIRRELVM vm, SQInteger table_idx) = 0;
+};
+
+#endif
--- /dev/null
+SubDir TOP src scripting ;
+
+if $(MINISWIG)
+{
+ ## MiniSwigRule outputcppfile : inputfile : modulename : flags
+ rule MiniSwigRule
+ {
+ local sources = [ SearchSource $(>) ] ;
+ local cppfile = [ LocateTarget $(<) : $(SUBDIR) ] ;
+ local headerfile = [ LocateTarget $(<:S=.hpp) : $(SUBDIR) ] ;
+ SEARCH on $(headerfile) = $(SOURCH_SOURCE) ;
+
+ MiniSwig $(cppfile) : $(sources) ;
+
+ CPPFLAGS on $(cppfile) = $(CPPFLAGS) -DSCRIPTING_API ;
+ headerfile on $(cppfile) = $(headerfile) ;
+ modulename on $(cppfile) = $(3) ;
+ FLAGS on $(cppfile) = $(4) ;
+
+ local h = $(headerfile:G=) ;
+ h = $(h:D=) ;
+ Includes $(h) : $(headerfile) ;
+ Includes $(headerfile) : $(cppfile) ;
+
+ local object = [ CompileObject $(cppfile) ] ;
+
+ return $(object) ;
+ }
+
+ rule MiniSwig
+ {
+ Depends $(<) : $(>) $(MINISWIG) ;
+ }
+
+ actions MiniSwig bind headerfile
+ {
+ $(CPP) -x c -CC $(CPPFLAGS) $(>) -o $(LOCATE_OBJECTS)/miniswig.tmp
+ ./miniswig --output-cpp $(<) --input $(LOCATE_OBJECTS)/miniswig.tmp --output-hpp $(headerfile) --module $(modulename) $(FLAGS)
+# rm -f $(LOCATE_OBJECTS)/miniswig.tmp
+ }
+}
+
+wrapper_sources = [ Filter [ Wildcard *.cpp *.hpp ] : wrapper.cpp wrapper.hpp ] ;
+if ! $(MINISWIG)
+{
+ wrapper_sources += [ SearchSource wrapper.cpp ] ;
+}
+wrapper_objects = [ CompileObjects $(wrapper_sources) ] ;
+if $(MINISWIG)
+{
+ wrapper_objects +=
+ [ MiniSwigRule wrapper.cpp : wrapper.interface.hpp : supertux : --select-namespace Scripting ] ;
+}
+
--- /dev/null
+#ifndef __SCRIPTING_AMBIENT_SOUND_H__
+#define __SCRIPTING_AMBIENT_SOUND_H__
+
+namespace Scripting
+{
+
+class AmbientSound
+{
+public:
+#ifndef SCRIPTING_API
+ virtual ~AmbientSound()
+ {}
+#endif
+
+ virtual void set_pos(float x, float y) = 0;
+ virtual float get_pos_x() const = 0;
+ virtual float get_pos_y() const = 0;
+};
+
+}
+
+#endif
--- /dev/null
+// $Id$
+//
+// SuperTux
+// Copyright (C) 2006 Matthias Braun <matze@braunis.de>
+//
+// 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 __ANCHOR_POINTS_HPP__
+#define __ANCHOR_POINTS_HPP__
+
+namespace Scripting {
+
+// TODO get these from the definitions in anchor.h (needs miniswig update)
+static const int ANCHOR_TOP = 0x0010;
+static const int ANCHOR_BOTTOM = 0x0020;
+static const int ANCHOR_LEFT = 0x0001;
+static const int ANCHOR_RIGHT = 0x0002;
+static const int ANCHOR_MIDDLE = 0x0000;
+static const int ANCHOR_TOP_LEFT = 0x0011;
+static const int ANCHOR_TOP_RIGHT = 0x0012;
+static const int ANCHOR_BOTTOM_LEFT = 0x0021;
+static const int ANCHOR_BOTTOM_RIGHT = 0x0022;
+
+}
+
+#endif
--- /dev/null
+// $Id$
+//
+// SuperTux
+// Copyright (C) 2006 Matthias Braun <matze@braunis.de>
+//
+// 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 <config.h>
+
+#include <string>
+#include <stdio.h>
+#include "object/camera.hpp"
+#include "scripting/camera.hpp"
+#include "math/vector.hpp"
+
+#define NOIMPL log_fatal << __FUNCTION__ << " not implemented."
+
+namespace Scripting
+{
+ Camera::Camera(::Camera* camera)
+ : camera(camera)
+ { }
+
+ Camera::~Camera()
+ { }
+
+ void
+ Camera::reload_config()
+ {
+ camera->reload_config();
+ }
+
+ void
+ Camera::shake(float speed, float x, float y)
+ {
+ camera->shake(speed, x, y);
+ }
+
+ void
+ Camera::set_pos(float , float )
+ {
+ }
+
+ void
+ Camera::set_mode(const std::string& mode)
+ {
+ if(mode == "normal") {
+ camera->mode = ::Camera::NORMAL;
+ } else if(mode == "manual") {
+ camera->mode = ::Camera::MANUAL;
+ } else {
+ log_fatal << "Camera mode '" << mode << "' unknown.";
+ }
+ }
+
+ void
+ Camera::scroll_to(float x, float y, float scrolltime)
+ {
+ camera->scroll_to(Vector(x, y), scrolltime);
+ }
+}
--- /dev/null
+// $Id$
+//
+// SuperTux
+// Copyright (C) 2006 Matthias Braun <matze@braunis.de>
+//
+// 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 __CAMERA_H__
+#define __CAMERA_H__
+
+#ifndef SCRIPTING_API
+class Camera;
+typedef Camera _Camera;
+#endif
+
+namespace Scripting
+{
+
+class Camera
+{
+public:
+#ifndef SCRIPTING_API
+ Camera(_Camera* camera);
+ ~Camera();
+#endif
+
+ void reload_config();
+
+ /** Shake the camera */
+ void shake(float speed, float x, float y);
+ /** Set camera to a specific coordinate */
+ void set_pos(float x, float y);
+ /** Set camera to a specific mode, can be "normal", "manual" */
+ void set_mode(const std::string& mode);
+ /** Scroll camera to position x,y in scrolltime seconds */
+ void scroll_to(float x, float y, float scrolltime);
+
+#ifndef SCRIPTING_API
+ _Camera* camera;
+#endif
+};
+
+}
+
+#endif
--- /dev/null
+// $Id$
+//
+// SuperTux
+// Copyright (C) 2006 Matthias Braun <matze@braunis.de>
+//
+// 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 <config.h>
+
+#include <string>
+#include <stdio.h>
+#include "object/candle.hpp"
+#include "scripting/candle.hpp"
+#include "math/vector.hpp"
+
+#define NOIMPL log_fatal << __PRETTY_FUNCTION__ << " not implemented."
+
+namespace Scripting
+{
+
+ Candle::Candle(::Candle* candle)
+ : candle(candle)
+ { }
+
+ Candle::~Candle()
+ { }
+
+ bool Candle::get_burning()
+ {
+ return candle->get_burning();
+ }
+
+ void Candle::set_burning(bool burning)
+ {
+ candle->set_burning(burning);
+ }
+
+}
--- /dev/null
+// $Id$
+//
+// SuperTux
+// Copyright (C) 2006 Matthias Braun <matze@braunis.de>
+//
+// 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 __SCRIPTING_CANDLE_H__
+#define __SCRIPTING_CANDLE_H__
+
+#ifndef SCRIPTING_API
+class Candle;
+typedef Candle _Candle;
+#endif
+
+namespace Scripting
+{
+
+class Candle
+{
+public:
+#ifndef SCRIPTING_API
+ Candle(_Candle* candle);
+ ~Candle();
+#endif
+
+ bool get_burning(); /**< returns true if candle is lighted */
+ void set_burning(bool burning); /**< true: light candle, false: extinguish candle */
+
+#ifndef SCRIPTING_API
+ _Candle* candle;
+#endif
+};
+
+}
+
+#endif
--- /dev/null
+// $Id$
+//
+// SuperTux
+// Copyright (C) 2006 Matthias Braun <matze@braunis.de>
+//
+// 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 __SCRIPTING_DISPLAY_EFFECT_H__
+#define __SCRIPTING_DISPLAY_EFFECT_H__
+
+namespace Scripting
+{
+
+class DisplayEffect
+{
+public:
+#ifndef SCRIPTING_API
+ virtual ~DisplayEffect()
+ {}
+#endif
+
+ /// fade display to black
+ virtual void fade_out(float fadetime) = 0;
+ /// fade display from black to normal
+ virtual void fade_in(float fadetime) = 0;
+ /// set display black (or back to normal)
+ virtual void set_black(bool enabled) = 0;
+ /// check if display is set to black
+ virtual bool is_black() = 0;
+ /// set black borders for cutscenes
+ virtual void sixteen_to_nine(float fadetime) = 0;
+ /// deactivate borders
+ virtual void four_to_three(float fadetime) = 0;
+
+ // fade display until just a small visible circle is left
+ // (like what happens in some cartoons at the end)
+ // void shrink_fade(Vector goal, float radius, float fadetime);
+};
+
+}
+
+#endif
--- /dev/null
+// $Id$
+//
+// SuperTux
+// Copyright (C) 2006 Matthias Braun <matze@braunis.de>
+//
+// 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 <config.h>
+
+#include <assert.h>
+#include <stdexcept>
+#include "floating_image.hpp"
+#include "sector.hpp"
+#include "object/floating_image.hpp"
+#include "worldmap/worldmap.hpp"
+
+namespace Scripting
+{
+
+FloatingImage::FloatingImage(const std::string& spritefile)
+{
+ using namespace WorldMapNS;
+
+ floating_image = new _FloatingImage(spritefile);
+ if(Sector::current() != NULL) {
+ Sector::current()->add_object(floating_image.get());
+ } else if(WorldMap::current() != NULL) {
+ WorldMap::current()->add_object(floating_image.get());
+ } else {
+ throw new std::runtime_error("Neither sector nor worldmap active");
+ }
+}
+
+FloatingImage::~FloatingImage()
+{
+ floating_image->remove_me();
+}
+
+void
+FloatingImage::set_layer(int layer)
+{
+ floating_image->set_layer(layer);
+}
+
+int
+FloatingImage::get_layer()
+{
+ return floating_image->get_layer();
+}
+
+void
+FloatingImage::set_pos(float x, float y)
+{
+ floating_image->set_pos(Vector(x, y));
+}
+
+float
+FloatingImage::get_pos_x()
+{
+ return floating_image->get_pos().x;
+}
+
+float
+FloatingImage::get_pos_y()
+{
+ return floating_image->get_pos().y;
+}
+
+void
+FloatingImage::set_anchor_point(int anchor)
+{
+ floating_image->set_anchor_point((AnchorPoint) anchor);
+}
+
+int
+FloatingImage::get_anchor_point()
+{
+ return (int) floating_image->get_anchor_point();
+}
+
+bool
+FloatingImage::get_visible()
+{
+ return floating_image->get_visible();
+}
+
+void
+FloatingImage::set_visible(bool visible)
+{
+ floating_image->set_visible(visible);
+}
+
+void
+FloatingImage::set_action(const std::string& action)
+{
+ floating_image->set_action(action);
+}
+
+std::string
+FloatingImage::get_action()
+{
+ return floating_image->get_action();
+}
+
+void
+FloatingImage::fade_in(float fadetime)
+{
+ floating_image->fade_in(fadetime);
+}
+
+void
+FloatingImage::fade_out(float fadetime)
+{
+ floating_image->fade_out(fadetime);
+}
+
+
+}
--- /dev/null
+// $Id$
+//
+// SuperTux
+// Copyright (C) 2006 Matthias Braun <matze@braunis.de>
+//
+// 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 __FLOATING_IMAGE_HPP__
+#define __FLOATING_IMAGE_HPP__
+
+#ifndef SCRIPTING_API
+#define __suspend
+#include <string>
+#include "ref.hpp"
+
+class FloatingImage;
+typedef FloatingImage _FloatingImage;
+#endif
+
+namespace Scripting
+{
+
+class FloatingImage
+{
+public:
+ FloatingImage(const std::string& spritefile);
+ ~FloatingImage();
+
+ void set_layer(int layer);
+ int get_layer();
+ void set_pos(float x, float y);
+ float get_pos_x();
+ float get_pos_y();
+ void set_anchor_point(int anchor);
+ int get_anchor_point();
+ void set_visible(bool visible);
+ bool get_visible();
+ void set_action(const std::string& action);
+ std::string get_action();
+ void fade_in(float fadetime);
+ void fade_out(float fadetime);
+
+#ifndef SCRIPTING_API
+private:
+ Ref<_FloatingImage> floating_image;
+#endif
+};
+
+}
+
+#endif
--- /dev/null
+// $Id$
+//
+// SuperTux
+// Copyright (C) 2006 Matthias Braun <matze@braunis.de>
+//
+// 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 <config.h>
+
+#include <memory>
+#include <stdio.h>
+#include <string>
+#include <squirrel.h>
+#include <sqstdio.h>
+#include "textscroller.hpp"
+#include "functions.hpp"
+#include "game_session.hpp"
+#include "tinygettext/tinygettext.hpp"
+#include "physfs/physfs_stream.hpp"
+#include "resources.hpp"
+#include "gettext.hpp"
+#include "log.hpp"
+#include "mainloop.hpp"
+#include "worldmap/worldmap.hpp"
+#include "world.hpp"
+#include "sector.hpp"
+#include "gameconfig.hpp"
+#include "object/player.hpp"
+#include "object/tilemap.hpp"
+#include "main.hpp"
+#include "fadeout.hpp"
+#include "shrinkfade.hpp"
+#include "object/camera.hpp"
+#include "flip_level_transformer.hpp"
+#include "audio/sound_manager.hpp"
+#include "random_generator.hpp"
+
+#include "squirrel_error.hpp"
+#include "squirrel_util.hpp"
+#include "time_scheduler.hpp"
+
+namespace Scripting
+{
+
+SQInteger display(HSQUIRRELVM vm)
+{
+ Console::output << squirrel2string(vm, -1) << std::endl;
+ return 0;
+}
+
+void print_stacktrace(HSQUIRRELVM vm)
+{
+ print_squirrel_stack(vm);
+}
+
+SQInteger get_current_thread(HSQUIRRELVM vm)
+{
+ sq_pushobject(vm, vm_to_object(vm));
+ return 1;
+}
+
+void wait(HSQUIRRELVM vm, float seconds)
+{
+ TimeScheduler::instance->schedule_thread(vm, game_time + seconds);
+}
+
+void wait_for_screenswitch(HSQUIRRELVM vm)
+{
+ main_loop->waiting_threads.add(vm);
+}
+
+void exit_screen()
+{
+ main_loop->exit_screen();
+}
+
+void fadeout_screen(float seconds)
+{
+ main_loop->set_screen_fade(new FadeOut(seconds));
+}
+
+void shrink_screen(float dest_x, float dest_y, float seconds)
+{
+ main_loop->set_screen_fade(new ShrinkFade(Vector(dest_x, dest_y), seconds));
+}
+
+void abort_screenfade()
+{
+ main_loop->set_screen_fade(NULL);
+}
+
+std::string translate(const std::string& text)
+{
+ return dictionary_manager.get_dictionary().translate(text);
+}
+
+void display_text_file(const std::string& filename)
+{
+ main_loop->push_screen(new TextScroller(filename));
+}
+
+void load_worldmap(const std::string& filename)
+{
+ using namespace WorldMapNS;
+
+ main_loop->push_screen(new WorldMap(filename));
+}
+
+void load_level(const std::string& filename)
+{
+ main_loop->push_screen(new GameSession(filename));
+}
+
+static SQInteger squirrel_read_char(SQUserPointer file)
+{
+ std::istream* in = reinterpret_cast<std::istream*> (file);
+ char c = in->get();
+ if(in->eof())
+ return 0;
+
+ return c;
+}
+
+void import(HSQUIRRELVM vm, const std::string& filename)
+{
+ IFileStream in(filename);
+
+ if(SQ_FAILED(sq_compile(vm, squirrel_read_char, &in,
+ filename.c_str(), SQTrue)))
+ throw SquirrelError(vm, "Couldn't parse script");
+
+ sq_pushroottable(vm);
+ if(SQ_FAILED(sq_call(vm, 1, SQFalse, SQTrue))) {
+ sq_pop(vm, 1);
+ throw SquirrelError(vm, "Couldn't execute script");
+ }
+ sq_pop(vm, 1);
+}
+
+void debug_collrects(bool enable)
+{
+ Sector::show_collrects = enable;
+}
+
+void debug_show_fps(bool enable)
+{
+ config->show_fps = enable;
+}
+
+void debug_draw_solids_only(bool enable)
+{
+ Sector::draw_solids_only = enable;
+}
+
+void save_state()
+{
+ using namespace WorldMapNS;
+
+ if(World::current() == NULL || WorldMap::current() == NULL)
+ throw std::runtime_error("Can't save state without active World");
+
+ WorldMap::current()->save_state();
+ World::current()->save_state();
+}
+
+void update_worldmap()
+{
+ using namespace WorldMapNS;
+
+ if(WorldMap::current() == NULL)
+ throw std::runtime_error("Can't update Worldmap: none active");
+
+ WorldMap::current()->load_state();
+}
+
+// not added to header, function to only be used by others
+// in this file
+bool validate_sector_player()
+{
+ if (Sector::current() == 0)
+ {
+ log_info << "No current sector." << std::endl;
+ return false;
+ }
+
+ if (Sector::current()->player == 0)
+ {
+ log_info << "No player." << std::endl;
+ return false;
+ }
+ return true;
+}
+
+void play_music(const std::string& filename)
+{
+ sound_manager->play_music(filename);
+}
+
+void play_sound(const std::string& filename)
+{
+ sound_manager->play(filename);
+}
+
+void grease()
+{
+ if (!validate_sector_player()) return;
+ ::Player* tux = Sector::current()->player; // Scripting::Player != ::Player
+ tux->physic.set_velocity_x(tux->physic.get_velocity_x()*3);
+}
+
+void invincible()
+{
+ if (!validate_sector_player()) return;
+ ::Player* tux = Sector::current()->player;
+ tux->invincible_timer.start(10000);
+}
+
+void ghost()
+{
+ if (!validate_sector_player()) return;
+ ::Player* tux = Sector::current()->player;
+ tux->set_ghost_mode(true);
+}
+
+void mortal()
+{
+ if (!validate_sector_player()) return;
+ ::Player* tux = Sector::current()->player;
+ tux->invincible_timer.stop();
+ tux->set_ghost_mode(false);
+}
+
+void restart()
+{
+ if (GameSession::current() == 0)
+ {
+ log_info << "No game session" << std::endl;
+ return;
+ }
+ GameSession::current()->restart_level();
+}
+
+void whereami()
+{
+ if (!validate_sector_player()) return;
+ ::Player* tux = Sector::current()->player;
+ log_info << "You are at x " << tux->get_pos().x << ", y " << tux->get_pos().y << std::endl;
+}
+
+void gotoend()
+{
+ if (!validate_sector_player()) return;
+ ::Player* tux = Sector::current()->player;
+ tux->move(Vector(
+ (Sector::current()->get_width()) - (SCREEN_WIDTH*2), 0));
+ Sector::current()->camera->reset(
+ Vector(tux->get_pos().x, tux->get_pos().y));
+}
+
+void camera()
+{
+ if (!validate_sector_player()) return;
+ log_info << "Camera is at " << Sector::current()->camera->get_translation().x << "," << Sector::current()->camera->get_translation().y << std::endl;
+}
+
+void quit()
+{
+ main_loop->quit();
+}
+
+int rand()
+{
+ return systemRandom.rand();
+}
+
+}
--- /dev/null
+// $Id$
+//
+// SuperTux
+// Copyright (C) 2006 Matthias Braun <matze@braunis.de>
+//
+// 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 __FUNCTIONS_H__
+#define __FUNCTIONS_H__
+
+#ifndef SCRIPTING_API
+#define __suspend
+#define __custom
+#include <string>
+#include "player_status.hpp"
+#endif
+
+namespace Scripting
+{
+
+/**
+ * Display the value of the argument. This is usefull for inspecting tables.
+ */
+SQInteger display(HSQUIRRELVM vm) __custom;
+
+/**
+ * Displays contents of the current stack
+ */
+void print_stacktrace(HSQUIRRELVM vm);
+
+/**
+ * returns the currently running thread
+ */
+SQInteger get_current_thread(HSQUIRRELVM vm) __custom;
+
+/**
+ * Display a text file and scrolls it over the screen (on next screenswitch)
+ */
+void display_text_file(const std::string& filename);
+
+/**
+ * Load and display a worldmap (on next screenswitch)
+ */
+void load_worldmap(const std::string& filename);
+
+/**
+ * Load and display a level (on next screenswitch)
+ */
+void load_level(const std::string& filename);
+
+/**
+ * Suspend the script execution for the specified number of seconds
+ */
+void wait(HSQUIRRELVM vm, float seconds) __suspend;
+
+/**
+ * Suspend the script execution until the current screen has been changed
+ */
+void wait_for_screenswitch(HSQUIRRELVM vm) __suspend;
+
+/**
+ * Exits the currently running screen (force exit from worldmap or scrolling
+ * text for example)
+ */
+void exit_screen();
+
+/**
+ * Does a fadeout for the specified number of seconds before next screenchange
+ */
+void fadeout_screen(float seconds);
+
+/**
+ * Does a shrinking fade towards the destposition for the specified number of
+ * seconds before next screenchange
+ */
+void shrink_screen(float dest_x, float dest_y, float seconds);
+
+/**
+ * Aborts any kind of previous screen fade; the screenchange will happen
+ * anyway.
+ */
+void abort_screenfade();
+
+/**
+ * Translate a text into the users language (by looking it up in the .po
+ * files)
+ */
+std::string translate(const std::string& text);
+
+/**
+ * Load a script file and executes it. This is typically used to import
+ * functions from external files.
+ */
+void import(HSQUIRRELVM v, const std::string& filename);
+
+/**
+ * Save world state to savegame
+ */
+void save_state();
+
+/**
+ * Update worldmap from worldmap state (state.world variable)
+ */
+void update_worldmap();
+
+/**
+ * enable/disable drawing of collision rectangles
+ */
+void debug_collrects(bool enable);
+
+/**
+ * enable/disable drawing of fps
+ */
+void debug_show_fps(bool enable);
+
+/**
+ * enable/disable drawing of non-solid layers
+ */
+void debug_draw_solids_only(bool enable);
+
+/**
+ * Changes music to musicfile
+ */
+void play_music(const std::string& musicfile);
+
+/**
+ * Plays a soundfile
+ */
+void play_sound(const std::string& soundfile);
+
+/**
+ * speeds Tux up
+ */
+void grease();
+
+/**
+ * makes Tux invincible for 10000 units of time
+ */
+void invincible();
+
+/**
+ * makes Tux a ghost, i.e. lets him float around and through solid objects
+ */
+void ghost();
+
+/**
+ * recall Tux's invincibility and ghost status
+ */
+void mortal();
+
+/**
+ * reinitialise and respawn Tux at the beginning of the current level
+ */
+void restart();
+
+/**
+ * print Tux's current coordinates in a level
+ */
+void whereami();
+
+/**
+ * move Tux near the end of the level
+ */
+void gotoend();
+
+/**
+ * show the camera's coordinates
+ */
+void camera();
+
+/**
+ * exit the game
+ */
+void quit();
+
+/**
+ * Returns a random integer
+ */
+int rand();
+
+}
+
+#endif
--- /dev/null
+// $Id$
+//
+// SuperTux
+// Copyright (C) 2006 Matthias Braun <matze@braunis.de>
+//
+// 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 <config.h>
+
+#include <string>
+#include <stdio.h>
+#include "level.hpp"
+#include "game_session.hpp"
+#include "flip_level_transformer.hpp"
+
+namespace Scripting
+{
+ Level::Level()
+ {}
+
+ Level::~Level()
+ {}
+
+ void
+ Level::finish(bool win)
+ {
+ if(GameSession::current() == NULL)
+ return;
+
+ GameSession::current()->finish(win);
+ }
+
+ void
+ Level::spawn(const std::string& sector, const std::string& spawnpoint)
+ {
+ if(GameSession::current() == NULL)
+ return;
+
+ GameSession::current()->respawn(sector, spawnpoint);
+ }
+
+ void
+ Level::flip_vertically()
+ {
+ FlipLevelTransformer flip_transformer;
+ flip_transformer.transform(GameSession::current()->get_current_level());
+ }
+
+ void
+ Level::toggle_pause()
+ {
+ if(GameSession::current() == NULL)
+ return;
+ GameSession::current()->toggle_pause();
+ }
+}
--- /dev/null
+// $Id$
+//
+// SuperTux
+// Copyright (C) 2006 Matthias Braun <matze@braunis.de>
+//
+// 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 __LEVEL_H__
+#define __LEVEL_H__
+
+namespace Scripting
+{
+
+class Level
+{
+public:
+#ifndef SCRIPTING_API
+ Level();
+ ~Level();
+#endif
+
+ /** Instantly finish the currently played level */
+ void finish(bool win);
+ /** spawn tux at specified sector and spawnpoint */
+ void spawn(const std::string& sector, const std::string& spawnpoint);
+ /** Flip level vertically */
+ void flip_vertically();
+ /** toggle pause */
+ void toggle_pause();
+};
+
+}
+
+#endif
--- /dev/null
+// $Id$
+//
+// SuperTux
+// Copyright (C) 2006 Matthias Braun <matze@braunis.de>
+//
+// 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 <config.h>
+
+#include <string>
+#include <stdio.h>
+#include "object/level_time.hpp"
+#include "scripting/level_time.hpp"
+#include "math/vector.hpp"
+
+#define NOIMPL log_fatal << __PRETTY_FUNCTION__ << " not implemented."
+
+namespace Scripting
+{
+
+ LevelTime::LevelTime(::LevelTime* level_time)
+ : level_time(level_time)
+ { }
+
+ LevelTime::~LevelTime()
+ { }
+
+ void LevelTime::start()
+ {
+ level_time->start();
+ }
+
+ void LevelTime::stop()
+ {
+ level_time->stop();
+ }
+
+ float LevelTime::get_time()
+ {
+ return level_time->get_time();
+ }
+
+ void LevelTime::set_time(float time_left)
+ {
+ level_time->set_time(time_left);
+ }
+
+}
--- /dev/null
+// $Id$
+//
+// SuperTux
+// Copyright (C) 2006 Matthias Braun <matze@braunis.de>
+//
+// 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 __SCRIPTING_LEVELTIME_H__
+#define __SCRIPTING_LEVELTIME_H__
+
+#ifndef SCRIPTING_API
+class LevelTime;
+typedef LevelTime _LevelTime;
+#endif
+
+namespace Scripting
+{
+
+class LevelTime
+{
+public:
+#ifndef SCRIPTING_API
+ LevelTime(_LevelTime* level_time);
+ ~LevelTime();
+#endif
+
+ /**
+ * Resumes the countdown
+ */
+ void start();
+
+ /**
+ * Pauses the countdown
+ */
+ void stop();
+
+ /**
+ * Returns the number of seconds left on the clock
+ */
+ float get_time();
+
+ /**
+ * Changes the number of seconds left on the clock
+ */
+ void set_time(float time_left);
+
+#ifndef SCRIPTING_API
+ _LevelTime* level_time;
+#endif
+};
+
+}
+
+#endif
--- /dev/null
+// $Id$
+//
+// SuperTux
+// Copyright (C) 2006 Matthias Braun <matze@braunis.de>
+//
+// 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 <config.h>
+
+#include <string>
+#include <stdio.h>
+#include "object/platform.hpp"
+#include "scripting/platform.hpp"
+#include "math/vector.hpp"
+
+#define NOIMPL log_fatal << __PRETTY_FUNCTION__ << " not implemented."
+
+namespace Scripting
+{
+
+ Platform::Platform(::Platform* platform)
+ : platform(platform)
+ { }
+
+ Platform::~Platform()
+ { }
+
+ void Platform::goto_node(int node_no)
+ {
+ platform->goto_node(node_no);
+ }
+
+ void Platform::start_moving()
+ {
+ platform->start_moving();
+ }
+
+ void Platform::stop_moving()
+ {
+ platform->stop_moving();
+ }
+
+}
--- /dev/null
+// $Id$
+//
+// SuperTux
+// Copyright (C) 2006 Matthias Braun <matze@braunis.de>
+//
+// 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 __SCRIPTING_PLATFORM_H__
+#define __SCRIPTING_PLATFORM_H__
+
+#ifndef SCRIPTING_API
+class Platform;
+typedef Platform _Platform;
+#endif
+
+namespace Scripting
+{
+
+class Platform
+{
+public:
+#ifndef SCRIPTING_API
+ Platform(_Platform* platform);
+ ~Platform();
+#endif
+
+ /** Move platform until at given node, then stop */
+ void goto_node(int node_no);
+
+ /** Start moving platform */
+ void start_moving();
+
+ /** Stop platform at next node */
+ void stop_moving();
+
+#ifndef SCRIPTING_API
+ _Platform* platform;
+#endif
+};
+
+}
+
+#endif
--- /dev/null
+// $Id$
+//
+// SuperTux
+// Copyright (C) 2006 Matthias Braun <matze@braunis.de>
+//
+// 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 __SCRIPTING_PLAYER_H__
+#define __SCRIPTING_PLAYER_H__
+
+namespace Scripting
+{
+
+class Player
+{
+public:
+#ifndef SCRIPTING_API
+ virtual ~Player()
+ {}
+#endif
+
+ /**
+ * Set tux bonus.
+ * This can be "grow", "fireflower" or "iceflower" at the moment
+ */
+ virtual bool add_bonus(const std::string& bonus) = 0;
+ /**
+ * Give tux more coins
+ */
+ virtual void add_coins(int count) = 0;
+ /**
+ * Make tux invicible for a short amount of time
+ */
+ virtual void make_invincible() = 0;
+ /**
+ * Deactivate user input for Tux
+ */
+ virtual void deactivate() = 0;
+ /**
+ * Give control back to user
+ */
+ virtual void activate() = 0;
+ /**
+ * Make Tux walk
+ */
+ virtual void walk(float speed) = 0;
+ /**
+ * Set player visible or not visible
+ */
+ virtual void set_visible(bool visible) = 0;
+ /**
+ * returns true if the player is currently visible (that is he was not set
+ * inivisible by the set_visible method)
+ */
+ virtual bool get_visible() = 0;
+
+ /**
+ * Hurts a player, if completely=true then the player will be killed even
+ * if he had grow or fireflower bonus
+ */
+ virtual void kill(bool completely) = 0;
+
+ /**
+ * Switches ghost mode on/off.
+ * Lets Tux float around and through solid objects.
+ */
+ virtual void set_ghost_mode(bool enable) = 0;
+
+ /**
+ * Returns whether ghost mode is currently enabled
+ */
+ virtual bool get_ghost_mode() = 0;
+
+ /**
+ * play cheer animation.
+ * This might need some space and behave in an unpredictable way. Best to use this at level end.
+ */
+ virtual void do_cheer() = 0;
+
+ /**
+ * duck down if possible.
+ * this won't last long as long as input is enabled.
+ */
+ virtual void do_duck() = 0;
+
+ /**
+ * stand back up if possible.
+ */
+ virtual void do_standup() = 0;
+
+ /**
+ * do a backflip if possible.
+ */
+ virtual void do_backflip() = 0;
+
+ /**
+ * jump in the air if possible
+ * sensible values for yspeed are negative - unless we want to jump into the ground of course
+ */
+ virtual void do_jump(float yspeed) = 0;
+
+ /**
+ * Orders the current GameSession to start a sequence
+ */
+ virtual void trigger_sequence(std::string sequence_name) = 0;
+
+};
+
+}
+
+#endif
--- /dev/null
+// $Id$
+//
+// SuperTux
+// Copyright (C) 2006 Matthias Braun <matze@braunis.de>
+//
+// 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 __SCRIPTED_OBJECT_INTERFACE_H__
+#define __SCRIPTED_OBJECT_INTERFACE_H__
+
+namespace Scripting
+{
+
+class ScriptedObject
+{
+public:
+#ifndef SCRIPTING_API
+ virtual ~ScriptedObject()
+ {}
+#endif
+
+ virtual void set_action(const std::string& animation) = 0;
+ virtual std::string get_action() = 0;
+
+ virtual void move(float x, float y) = 0;
+ virtual void set_pos(float x, float y) = 0;
+ virtual float get_pos_x() = 0;
+ virtual float get_pos_y() = 0;
+
+ virtual void set_velocity(float x, float y) = 0;
+ virtual float get_velocity_x() = 0;
+ virtual float get_velocity_y() = 0;
+
+ virtual void set_visible(bool visible) = 0;
+ virtual bool is_visible() = 0;
+
+ virtual void set_solid(bool solid) = 0;
+ virtual bool is_solid() = 0;
+
+ virtual std::string get_name() = 0;
+};
+
+}
+
+#endif
--- /dev/null
+// $Id$
+//
+// SuperTux
+// Copyright (C) 2006 Matthias Braun <matze@braunis.de>
+//
+// 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 <config.h>
+
+#include "serialize.hpp"
+
+#include <memory>
+#include <assert.h>
+#include "lisp/lisp.hpp"
+#include "lisp/list_iterator.hpp"
+#include "lisp/parser.hpp"
+#include "lisp/writer.hpp"
+#include "squirrel_error.hpp"
+
+namespace Scripting
+{
+
+void load_squirrel_table(HSQUIRRELVM vm, SQInteger table_idx, const lisp::Lisp* lisp)
+{
+ using namespace lisp;
+
+ if(table_idx < 0)
+ table_idx -= 2;
+
+ lisp::ListIterator iter(lisp);
+ while(iter.next() && iter.lisp() != NULL) {
+ const std::string& token = iter.item();
+ sq_pushstring(vm, token.c_str(), token.size());
+
+ const lisp::Lisp* value = iter.value();
+ switch(value->get_type()) {
+ case Lisp::TYPE_CONS:
+ sq_newtable(vm);
+ load_squirrel_table(vm, sq_gettop(vm), iter.lisp());
+ break;
+ case Lisp::TYPE_INTEGER:
+ sq_pushinteger(vm, value->get_int());
+ break;
+ case Lisp::TYPE_REAL:
+ sq_pushfloat(vm, value->get_float());
+ break;
+ case Lisp::TYPE_STRING:
+ sq_pushstring(vm, value->get_string().c_str(), -1);
+ break;
+ case Lisp::TYPE_BOOLEAN:
+ sq_pushbool(vm, value->get_bool() ? SQTrue : SQFalse);
+ break;
+ case Lisp::TYPE_SYMBOL:
+ std::cerr << "Unexpected symbol in lisp file...";
+ sq_pushnull(vm);
+ break;
+ default:
+ assert(false);
+ break;
+ }
+
+ if(SQ_FAILED(sq_createslot(vm, table_idx)))
+ throw Scripting::SquirrelError(vm, "Couldn't create new index");
+ }
+}
+
+void save_squirrel_table(HSQUIRRELVM vm, SQInteger table_idx, lisp::Writer& writer)
+{
+ // offset because of sq_pushnull
+ if(table_idx < 0)
+ table_idx -= 1;
+
+ //iterator table
+ sq_pushnull(vm);
+ while(SQ_SUCCEEDED(sq_next(vm, table_idx))) {
+ if(sq_gettype(vm, -2) != OT_STRING) {
+ std::cerr << "Table contains non-string key\n";
+ continue;
+ }
+ const SQChar* key;
+ sq_getstring(vm, -2, &key);
+
+ switch(sq_gettype(vm, -1)) {
+ case OT_INTEGER: {
+ SQInteger val;
+ sq_getinteger(vm, -1, &val);
+ writer.write_int(key, static_cast<int> (val));
+ break;
+ }
+ case OT_FLOAT: {
+ SQFloat val;
+ sq_getfloat(vm, -1, &val);
+ writer.write_float(key, static_cast<float> (val));
+ break;
+ }
+ case OT_BOOL: {
+ SQBool val;
+ sq_getbool(vm, -1, &val);
+ writer.write_bool(key, val == SQTrue);
+ break;
+ }
+ case OT_STRING: {
+ const SQChar* str;
+ sq_getstring(vm, -1, &str);
+ writer.write_string(key, reinterpret_cast<const char*> (str));
+ break;
+ }
+ case OT_TABLE: {
+ writer.start_list(key, true);
+ save_squirrel_table(vm, -1, writer);
+ writer.end_list(key);
+ break;
+ }
+ case OT_CLOSURE:
+ break; // ignore
+ case OT_NATIVECLOSURE:
+ break;
+ default:
+ std::cerr << "Can't serialize key '" << key << "' in table.\n";
+ break;
+ }
+ sq_pop(vm, 2);
+ }
+ sq_pop(vm, 1);
+}
+
+}
--- /dev/null
+// $Id$
+//
+// SuperTux
+// Copyright (C) 2006 Matthias Braun <matze@braunis.de>
+//
+// 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 __SERIALIZE_HPP__
+#define __SERIALIZE_HPP__
+
+#include <squirrel.h>
+#include <string>
+#include "lisp/lisp.hpp"
+#include "lisp/writer.hpp"
+
+namespace Scripting
+{
+
+ void save_squirrel_table(HSQUIRRELVM vm, SQInteger table_idx, lisp::Writer& writer);
+ void load_squirrel_table(HSQUIRRELVM vm, SQInteger table_idx, const lisp::Lisp* lisp);
+
+}
+
+#endif
--- /dev/null
+// $Id$
+//
+// SuperTux
+// Copyright (C) 2006 Matthias Braun <matze@braunis.de>
+//
+// 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 <config.h>
+
+#include "squirrel_error.hpp"
+#include <sstream>
+
+namespace Scripting
+{
+
+SquirrelError::SquirrelError(HSQUIRRELVM v, const std::string& message) throw()
+{
+ std::ostringstream msg;
+ msg << "Squirrel error: " << message << " (";
+ const char* lasterr;
+ sq_getlasterror(v);
+ if(sq_gettype(v, -1) != OT_STRING)
+ {
+ lasterr = "no error info";
+ }
+ else
+ {
+ sq_getstring(v, -1, &lasterr);
+ }
+ msg << lasterr << ")";
+ sq_pop(v, 1);
+ this->message = msg.str();
+}
+
+SquirrelError::~SquirrelError() throw()
+{}
+
+const char*
+SquirrelError::what() const throw()
+{
+ return message.c_str();
+}
+
+}
--- /dev/null
+// $Id$
+//
+// SuperTux
+// Copyright (C) 2006 Matthias Braun <matze@braunis.de>
+//
+// 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 __SQUIRREL_ERROR_HPP__
+#define __SQUIRREL_ERROR_HPP__
+
+#include <squirrel.h>
+#include <stdexcept>
+
+namespace Scripting
+{
+
+/** Exception class for squirrel errors, it takes a squirrelvm and uses
+ * sq_geterror() to retrieve additional information about the last error that
+ * occured and creates a readable message from that.
+ */
+class SquirrelError : public std::exception
+{
+public:
+ SquirrelError(HSQUIRRELVM v, const std::string& message) throw();
+ virtual ~SquirrelError() throw();
+
+ const char* what() const throw();
+private:
+ std::string message;
+};
+
+}
+
+#endif
--- /dev/null
+// $Id$
+//
+// SuperTux
+// Copyright (C) 2006 Matthias Braun <matze@braunis.de>
+//
+// 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 <config.h>
+
+#include <stdexcept>
+#include <sstream>
+#include <stdarg.h>
+#include <squirrel.h>
+#include <sqstdmath.h>
+#include <sqstdblob.h>
+#include <sqstdstring.h>
+#include <sqstdaux.h>
+#include <sqstdio.h>
+#include "squirrel_util.hpp"
+#include "log.hpp"
+#include "level.hpp"
+#include "physfs/physfs_stream.hpp"
+#include "../random_generator.hpp"
+
+#ifdef ENABLE_SQDBG
+#include <sqdbg/sqrdbg.h>
+
+static HSQREMOTEDBG debugger = NULL;
+#endif
+
+namespace Scripting
+{
+
+HSQUIRRELVM global_vm = NULL;
+
+static void printfunc(HSQUIRRELVM, const char* str, ...)
+{
+ char buf[4096];
+ va_list arglist;
+ va_start(arglist, str);
+ vsprintf(buf, str, arglist);
+ Console::output << (const char*) buf << std::flush;
+ va_end(arglist);
+}
+
+void init_squirrel(bool enable_debugger)
+{
+ global_vm = sq_open(64);
+ if(global_vm == NULL)
+ throw std::runtime_error("Couldn't initialize squirrel vm");
+
+ if(enable_debugger) {
+#ifdef ENABLE_SQDBG
+ sq_enabledebuginfo(global_vm, SQTrue);
+ debugger = sq_rdbg_init(global_vm, 1234, SQFalse);
+ if(debugger == NULL)
+ throw SquirrelError(global_vm, "Couldn't initialize squirrel debugger");
+
+ sq_enabledebuginfo(global_vm, SQTrue);
+ log_info << "Waiting for debug client..." << std::endl;
+ if(SQ_FAILED(sq_rdbg_waitforconnections(debugger)))
+ throw SquirrelError(global_vm, "Waiting for debug clients failed");
+ log_info << "debug client connected." << std::endl;
+#endif
+ }
+
+ sq_pushroottable(global_vm);
+ if(SQ_FAILED(sqstd_register_bloblib(global_vm)))
+ throw SquirrelError(global_vm, "Couldn't register blob lib");
+ if(SQ_FAILED(sqstd_register_mathlib(global_vm)))
+ throw SquirrelError(global_vm, "Couldn't register math lib");
+ if(SQ_FAILED(sqstd_register_stringlib(global_vm)))
+ throw SquirrelError(global_vm, "Couldn't register string lib");
+
+ // remove rand and srand calls from sqstdmath, we'll provide our own
+ sq_pushstring(global_vm, "srand", -1);
+ sq_deleteslot(global_vm, -2, SQFalse);
+ sq_pushstring(global_vm, "rand", -1);
+ sq_deleteslot(global_vm, -2, SQFalse);
+
+ // register supertux API
+ register_supertux_wrapper(global_vm);
+
+ // TODO remove this at some point... it shoud just be functions not an object
+ expose_object(global_vm, -1, new Scripting::Level(), "Level", true);
+
+ sq_pop(global_vm, 1);
+
+ // register print function
+ sq_setprintfunc(global_vm, printfunc);
+ // register default error handlers
+ sqstd_seterrorhandlers(global_vm);
+
+ // try to load default script
+ try {
+ std::string filename = "scripts/default.nut";
+ IFileStream stream(filename);
+ Scripting::compile_and_run(global_vm, stream, filename);
+ } catch(std::exception& e) {
+ log_warning << "Couldn't load default.nut: " << e.what() << std::endl;
+ }
+}
+
+void exit_squirrel()
+{
+#ifdef ENABLE_SQDBG
+ if(debugger != NULL) {
+ sq_rdbg_shutdown(debugger);
+ debugger = NULL;
+ }
+#endif
+
+ if (global_vm)
+ sq_close(global_vm);
+
+ global_vm = NULL;
+}
+
+void update_debugger()
+{
+#ifdef ENABLE_SQDBG
+ if(debugger != NULL)
+ sq_rdbg_update(debugger);
+#endif
+}
+
+std::string squirrel2string(HSQUIRRELVM v, SQInteger i)
+{
+ std::ostringstream os;
+ switch(sq_gettype(v, i))
+ {
+ case OT_NULL:
+ os << "<null>";
+ break;
+ case OT_BOOL: {
+ SQBool p;
+ sq_getbool(v, i, &p);
+ if (p)
+ os << "true";
+ else
+ os << "false";
+ break;
+ }
+ case OT_INTEGER: {
+ SQInteger val;
+ sq_getinteger(v, i, &val);
+ os << val;
+ break;
+ }
+ case OT_FLOAT: {
+ SQFloat val;
+ sq_getfloat(v, i, &val);
+ os << val;
+ break;
+ }
+ case OT_STRING: {
+ const SQChar* val;
+ sq_getstring(v, i, &val);
+ os << "\"" << val << "\"";
+ break;
+ }
+ case OT_TABLE: {
+ bool first = true;
+ os << "{";
+ sq_pushnull(v); //null iterator
+ while(SQ_SUCCEEDED(sq_next(v,i-1)))
+ {
+ if (!first) {
+ os << ", ";
+ }
+ first = false;
+
+ //here -1 is the value and -2 is the key
+ os << squirrel2string(v, -2) << " => "
+ << squirrel2string(v, -1);
+
+ sq_pop(v,2); //pops key and val before the nex iteration
+ }
+ sq_pop(v, 1);
+ os << "}";
+ break;
+ }
+ case OT_ARRAY: {
+ bool first = true;
+ os << "[";
+ sq_pushnull(v); //null iterator
+ while(SQ_SUCCEEDED(sq_next(v,i-1)))
+ {
+ if (!first) {
+ os << ", ";
+ }
+ first = false;
+
+ //here -1 is the value and -2 is the key
+ // we ignore the key, since that is just the index in an array
+ os << squirrel2string(v, -1);
+
+ sq_pop(v,2); //pops key and val before the nex iteration
+ }
+ sq_pop(v, 1);
+ os << "]";
+ break;
+ }
+ case OT_USERDATA:
+ os << "<userdata>";
+ break;
+ case OT_CLOSURE:
+ os << "<closure>";
+ break;
+ case OT_NATIVECLOSURE:
+ os << "<native closure>";
+ break;
+ case OT_GENERATOR:
+ os << "<generator>";
+ break;
+ case OT_USERPOINTER:
+ os << "userpointer";
+ break;
+ case OT_THREAD:
+ os << "<thread>";
+ break;
+ case OT_CLASS:
+ os << "<class>";
+ break;
+ case OT_INSTANCE:
+ os << "<instance>";
+ break;
+ case OT_WEAKREF:
+ os << "<weakref>";
+ break;
+ default:
+ os << "<unknown>";
+ break;
+ }
+ return os.str();
+}
+
+void print_squirrel_stack(HSQUIRRELVM v)
+{
+ printf("--------------------------------------------------------------\n");
+ int count = sq_gettop(v);
+ for(int i = 1; i <= count; ++i) {
+ printf("%d: ",i);
+ switch(sq_gettype(v, i))
+ {
+ case OT_NULL:
+ printf("null");
+ break;
+ case OT_INTEGER: {
+ SQInteger val;
+ sq_getinteger(v, i, &val);
+ printf("integer (%d)", static_cast<int> (val));
+ break;
+ }
+ case OT_FLOAT: {
+ SQFloat val;
+ sq_getfloat(v, i, &val);
+ printf("float (%f)", val);
+ break;
+ }
+ case OT_STRING: {
+ const SQChar* val;
+ sq_getstring(v, i, &val);
+ printf("string (%s)", val);
+ break;
+ }
+ case OT_TABLE:
+ printf("table");
+ break;
+ case OT_ARRAY:
+ printf("array");
+ break;
+ case OT_USERDATA:
+ printf("userdata");
+ break;
+ case OT_CLOSURE:
+ printf("closure(function)");
+ break;
+ case OT_NATIVECLOSURE:
+ printf("native closure(C function)");
+ break;
+ case OT_GENERATOR:
+ printf("generator");
+ break;
+ case OT_USERPOINTER:
+ printf("userpointer");
+ break;
+ case OT_THREAD:
+ printf("thread");
+ break;
+ case OT_CLASS:
+ printf("class");
+ break;
+ case OT_INSTANCE:
+ printf("instance");
+ break;
+ case OT_WEAKREF:
+ printf("weakref");
+ break;
+ default:
+ printf("unknown?!?");
+ break;
+ }
+ printf("\n");
+ }
+ printf("--------------------------------------------------------------\n");
+}
+
+static SQInteger squirrel_read_char(SQUserPointer file)
+{
+ std::istream* in = reinterpret_cast<std::istream*> (file);
+ char c = in->get();
+ if(in->eof())
+ return 0;
+ return c;
+}
+
+void compile_script(HSQUIRRELVM vm, std::istream& in, const std::string& sourcename)
+{
+ if(SQ_FAILED(sq_compile(vm, squirrel_read_char, &in, sourcename.c_str(), true)))
+ throw SquirrelError(vm, "Couldn't parse script");
+}
+
+void compile_and_run(HSQUIRRELVM vm, std::istream& in,
+ const std::string& sourcename)
+{
+ compile_script(vm, in, sourcename);
+
+ SQInteger oldtop = sq_gettop(vm);
+
+ try {
+ sq_pushroottable(vm);
+ if(SQ_FAILED(sq_call(vm, 1, SQFalse, SQTrue)))
+ throw SquirrelError(vm, "Couldn't start script");
+ } catch(...) {
+ sq_settop(vm, oldtop);
+ throw;
+ }
+
+ // we can remove the closure in case the script was not suspended
+ if(sq_getvmstate(vm) != SQ_VMSTATE_SUSPENDED) {
+ sq_settop(vm, oldtop-1);
+ }
+}
+
+HSQOBJECT create_thread(HSQUIRRELVM vm)
+{
+ HSQUIRRELVM new_vm = sq_newthread(vm, 64);
+ if(new_vm == NULL)
+ throw SquirrelError(vm, "Couldn't create new VM");
+
+ HSQOBJECT vm_object;
+ sq_resetobject(&vm_object);
+ if(SQ_FAILED(sq_getstackobj(vm, -1, &vm_object)))
+ throw SquirrelError(vm, "Couldn't get squirrel thread from stack");
+ sq_addref(vm, &vm_object);
+
+ sq_pop(vm, 1);
+
+ return vm_object;
+}
+
+HSQOBJECT vm_to_object(HSQUIRRELVM vm)
+{
+ HSQOBJECT object;
+ sq_resetobject(&object);
+ object._unVal.pThread = vm;
+ object._type = OT_THREAD;
+
+ return object;
+}
+
+HSQUIRRELVM object_to_vm(HSQOBJECT object)
+{
+ if(object._type != OT_THREAD)
+ return NULL;
+
+ return object._unVal.pThread;
+}
+
+// begin: serialization functions
+
+void store_float(HSQUIRRELVM vm, const char* name, float val)
+{
+ sq_pushstring(vm, name, -1);
+ sq_pushfloat(vm, val);
+ if(SQ_FAILED(sq_createslot(vm, -3)))
+ throw Scripting::SquirrelError(vm, "Couldn't add float value to table");
+}
+
+void store_int(HSQUIRRELVM vm, const char* name, int val)
+{
+ sq_pushstring(vm, name, -1);
+ sq_pushinteger(vm, val);
+ if(SQ_FAILED(sq_createslot(vm, -3)))
+ throw Scripting::SquirrelError(vm, "Couldn't add int value to table");
+}
+
+void store_string(HSQUIRRELVM vm, const char* name, const std::string& val)
+{
+ sq_pushstring(vm, name, -1);
+ sq_pushstring(vm, val.c_str(), val.length());
+ if(SQ_FAILED(sq_createslot(vm, -3)))
+ throw Scripting::SquirrelError(vm, "Couldn't add float value to table");
+}
+
+void store_bool(HSQUIRRELVM vm, const char* name, bool val)
+{
+ sq_pushstring(vm, name, -1);
+ sq_pushbool(vm, val ? SQTrue : SQFalse);
+ if(SQ_FAILED(sq_createslot(vm, -3)))
+ throw Scripting::SquirrelError(vm, "Couldn't add float value to table");
+}
+
+bool has_float(HSQUIRRELVM vm, const char* name)
+{
+ sq_pushstring(vm, name, -1);
+ if (SQ_FAILED(sq_get(vm, -2))) return false;
+ sq_pop(vm, 1);
+ return true;
+}
+
+bool has_int(HSQUIRRELVM vm, const char* name)
+{
+ return has_float(vm, name);
+}
+
+bool has_string(HSQUIRRELVM vm, const char* name)
+{
+ return has_float(vm, name);
+}
+
+bool has_bool(HSQUIRRELVM vm, const char* name)
+{
+ return has_float(vm, name);
+}
+
+float read_float(HSQUIRRELVM vm, const char* name)
+{
+ sq_pushstring(vm, name, -1);
+ if(SQ_FAILED(sq_get(vm, -2))) {
+ std::ostringstream msg;
+ msg << "Couldn't get float value for '" << name << "' from table";
+ throw Scripting::SquirrelError(vm, msg.str());
+ }
+
+ float result;
+ if(SQ_FAILED(sq_getfloat(vm, -1, &result))) {
+ std::ostringstream msg;
+ msg << "Couldn't get float value for '" << name << "' from table";
+ throw Scripting::SquirrelError(vm, msg.str());
+ }
+ sq_pop(vm, 1);
+
+ return result;
+}
+
+int read_int(HSQUIRRELVM vm, const char* name)
+{
+ sq_pushstring(vm, name, -1);
+ if(SQ_FAILED(sq_get(vm, -2))) {
+ std::ostringstream msg;
+ msg << "Couldn't get int value for '" << name << "' from table";
+ throw Scripting::SquirrelError(vm, msg.str());
+ }
+
+ SQInteger result;
+ if(SQ_FAILED(sq_getinteger(vm, -1, &result))) {
+ std::ostringstream msg;
+ msg << "Couldn't get int value for '" << name << "' from table";
+ throw Scripting::SquirrelError(vm, msg.str());
+ }
+ sq_pop(vm, 1);
+
+ return result;
+}
+
+
+std::string read_string(HSQUIRRELVM vm, const char* name)
+{
+ sq_pushstring(vm, name, -1);
+ if(SQ_FAILED(sq_get(vm, -2))) {
+ std::ostringstream msg;
+ msg << "Couldn't get string value for '" << name << "' from table";
+ throw Scripting::SquirrelError(vm, msg.str());
+ }
+
+ const char* result;
+ if(SQ_FAILED(sq_getstring(vm, -1, &result))) {
+ std::ostringstream msg;
+ msg << "Couldn't get string value for '" << name << "' from table";
+ throw Scripting::SquirrelError(vm, msg.str());
+ }
+ sq_pop(vm, 1);
+
+ return std::string(result);
+}
+
+bool read_bool(HSQUIRRELVM vm, const char* name)
+{
+ sq_pushstring(vm, name, -1);
+ if(SQ_FAILED(sq_get(vm, -2))) {
+ std::ostringstream msg;
+ msg << "Couldn't get bool value for '" << name << "' from table";
+ throw Scripting::SquirrelError(vm, msg.str());
+ }
+
+ SQBool result;
+ if(SQ_FAILED(sq_getbool(vm, -1, &result))) {
+ std::ostringstream msg;
+ msg << "Couldn't get bool value for '" << name << "' from table";
+ throw Scripting::SquirrelError(vm, msg.str());
+ }
+ sq_pop(vm, 1);
+
+ return result == SQTrue;
+}
+
+bool get_float(HSQUIRRELVM vm, const char* name, float& val) {
+ if (!has_float(vm, name)) return false;
+ val = read_float(vm, name);
+ return true;
+}
+
+bool get_int(HSQUIRRELVM vm, const char* name, int& val) {
+ if (!has_int(vm, name)) return false;
+ val = read_int(vm, name);
+ return true;
+}
+
+bool get_string(HSQUIRRELVM vm, const char* name, std::string& val) {
+ if (!has_string(vm, name)) return false;
+ val = read_string(vm, name);
+ return true;
+}
+
+bool get_bool(HSQUIRRELVM vm, const char* name, bool& val) {
+ if (!has_bool(vm, name)) return false;
+ val = read_bool(vm, name);
+ return true;
+}
+
+// end: serialization functions
+
+}
--- /dev/null
+// $Id$
+//
+// SuperTux
+// Copyright (C) 2006 Matthias Braun <matze@braunis.de>
+//
+// 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 __SQUIRREL_UTIL_HPP__
+#define __SQUIRREL_UTIL_HPP__
+
+#include <squirrel.h>
+#include <sstream>
+#include <stdexcept>
+#include <string>
+#include "wrapper.hpp"
+#include "squirrel_error.hpp"
+
+namespace Scripting
+{
+
+ extern HSQUIRRELVM global_vm;
+
+ void init_squirrel(bool enable_debugger);
+ void exit_squirrel();
+ void update_debugger();
+
+ std::string squirrel2string(HSQUIRRELVM vm, SQInteger i);
+ void print_squirrel_stack(HSQUIRRELVM vm);
+
+ HSQOBJECT create_thread(HSQUIRRELVM vm);
+ SQObject vm_to_object(HSQUIRRELVM vm);
+ HSQUIRRELVM object_to_vm(HSQOBJECT object);
+
+ void compile_script(HSQUIRRELVM vm, std::istream& in,
+ const std::string& sourcename);
+ void compile_and_run(HSQUIRRELVM vm, std::istream& in,
+ const std::string& sourcename);
+
+ template<typename T>
+ void expose_object(HSQUIRRELVM v, SQInteger table_idx, T* object,
+ const std::string& name, bool free = false)
+ {
+ sq_pushstring(v, name.c_str(), -1);
+ Scripting::create_squirrel_instance(v, object, free);
+
+ if(table_idx < 0)
+ table_idx -= 2;
+
+ // register instance in root table
+ if(SQ_FAILED(sq_createslot(v, table_idx))) {
+ std::ostringstream msg;
+ msg << "Couldn't register object '" << name << "' in squirrel table";
+ throw Scripting::SquirrelError(v, msg.str());
+ }
+ }
+
+ static inline void unexpose_object(HSQUIRRELVM v, SQInteger table_idx,
+ const std::string& name)
+ {
+ sq_pushstring(v, name.c_str(), name.length());
+
+ if(table_idx < 0)
+ table_idx -= 1;
+
+ if(SQ_FAILED(sq_deleteslot(v, table_idx, SQFalse))) {
+ std::ostringstream msg;
+ msg << "Couldn't unregister object '" << name << "' in squirrel root table";
+ throw Scripting::SquirrelError(v, msg.str());
+ }
+ }
+
+ // begin serialization functions
+ void store_float(HSQUIRRELVM vm, const char* name, float val);
+ void store_int(HSQUIRRELVM vm, const char* name, int val);
+ void store_string(HSQUIRRELVM vm, const char* name, const std::string& val);
+ void store_bool(HSQUIRRELVM vm, const char* name, bool val);
+
+ bool has_float(HSQUIRRELVM vm, const char* name);
+ bool has_int(HSQUIRRELVM vm, const char* name);
+ bool has_string(HSQUIRRELVM vm, const char* name);
+ bool has_bool(HSQUIRRELVM vm, const char* name);
+
+ bool get_float(HSQUIRRELVM vm, const char* name, float& val);
+ bool get_int(HSQUIRRELVM vm, const char* name, int& val);
+ bool get_string(HSQUIRRELVM vm, const char* name, std::string& val);
+ bool get_bool(HSQUIRRELVM vm, const char* name, bool& val);
+
+ float read_float(HSQUIRRELVM vm, const char* name);
+ int read_int(HSQUIRRELVM vm, const char* name);
+ std::string read_string(HSQUIRRELVM vm, const char* name);
+ bool read_bool(HSQUIRRELVM vm, const char* name);
+ // end serialization functions
+
+}
+
+#endif
--- /dev/null
+// $Id$
+//
+// SuperTux - Sector Scripting
+// Copyright (C) 2006 Wolfgang Becker <uafr@gmx.de>
+//
+// 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 __SECTOR_H__
+#define __SECTOR_H__
+
+namespace Scripting
+{
+
+class SSector
+{
+public:
+#ifndef SCRIPTING_API
+ virtual ~SSector()
+ {}
+#endif
+ virtual void set_ambient_light(float red, float green, float blue) = 0;
+ virtual float get_ambient_red() = 0;
+ virtual float get_ambient_green() = 0;
+ virtual float get_ambient_blue() = 0;
+};
+
+}
+
+#endif
--- /dev/null
+// $Id$
+//
+// SuperTux
+// Copyright (C) 2006 Matthias Braun <matze@braunis.de>
+//
+// 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 __TEXT_H__
+#define __TEXT_H__
+
+namespace Scripting
+{
+
+class Text
+{
+public:
+#ifndef SCRIPTING_API
+ virtual ~Text()
+ { }
+#endif
+
+ virtual void set_text(const std::string& text) = 0;
+ virtual void set_font(const std::string& fontname) = 0;
+ virtual void fade_in(float fadetime) = 0;
+ virtual void fade_out(float fadetime) = 0;
+ virtual void set_visible(bool visible) = 0;
+ virtual void set_centered(bool centered) = 0;
+ virtual void set_pos(float x, float y) = 0;
+ virtual float get_pos_x() = 0;
+ virtual float get_pos_y() = 0;
+ virtual void set_anchor_point(int anchor) = 0;
+ virtual int get_anchor_point() = 0;
+};
+
+}
+
+#endif
--- /dev/null
+// $Id$
+//
+// SuperTux
+// Copyright (C) 2006 Matthias Braun <matze@braunis.de>
+//
+// 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 <config.h>
+
+#include "thread_queue.hpp"
+#include "squirrel_util.hpp"
+#include "log.hpp"
+
+namespace Scripting
+{
+
+ThreadQueue::ThreadQueue()
+{
+}
+
+ThreadQueue::~ThreadQueue()
+{
+}
+
+void
+ThreadQueue::add(HSQUIRRELVM vm)
+{
+ // create a weakref to the VM
+ HSQOBJECT vm_obj = vm_to_object(vm);
+ sq_pushobject(global_vm, vm_obj);
+ sq_weakref(global_vm, -1);
+
+ HSQOBJECT object;
+ if(SQ_FAILED(sq_getstackobj(global_vm, -1, &object))) {
+ sq_pop(global_vm, 2);
+ throw SquirrelError(global_vm, "Couldn't get thread weakref from vm");
+ }
+ sq_addref(global_vm, &object);
+ threads.push_back(object);
+
+ sq_pop(global_vm, 2);
+}
+
+void
+ThreadQueue::wakeup()
+{
+ // we traverse the list in reverse orders and use indices. This should be
+ // robust for scripts that add new entries to the list while we're traversing
+ // it
+ size_t i = threads.size() - 1;
+ size_t end = (size_t) 0 - 1;
+ size_t size_begin = threads.size();
+ while(i != end) {
+ HSQOBJECT object = threads[i];
+
+ sq_pushobject(global_vm, object);
+ sq_getweakrefval(global_vm, -1);
+
+ HSQUIRRELVM scheduled_vm;
+ if(sq_gettype(global_vm, -1) == OT_THREAD &&
+ SQ_SUCCEEDED(sq_getthread(global_vm, -1, &scheduled_vm))) {
+ if(SQ_FAILED(sq_wakeupvm(scheduled_vm, SQFalse, SQFalse, SQTrue))) {
+ log_warning << "Couldn't wakeup scheduled squirrel VM" << std::endl;
+ }
+ }
+
+ sq_release(global_vm, &object);
+ sq_pop(global_vm, 1);
+ i--;
+ }
+
+ threads.erase(threads.begin(), threads.begin() + size_begin);
+}
+
+}
--- /dev/null
+// $Id$
+//
+// SuperTux
+// Copyright (C) 2006 Matthias Braun <matze@braunis.de>
+//
+// 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 __THREAD_QUEUE_HPP__
+#define __THREAD_QUEUE_HPP__
+
+#include <vector>
+#include <squirrel.h>
+
+namespace Scripting
+{
+
+/**
+ * Keeps a list of SquirrelThreads that wait for a wakeup event
+ */
+class ThreadQueue
+{
+public:
+ ThreadQueue();
+ virtual ~ThreadQueue();
+
+ /// adds a thread (actually a weakref to the thread)
+ void add(HSQUIRRELVM vm);
+ /// wakes up threads in the list
+ void wakeup();
+
+private:
+ typedef std::vector<HSQOBJECT> ThreadList;
+ ThreadList threads;
+};
+
+}
+
+#endif
--- /dev/null
+// $Id$
+//
+// SuperTux
+// Copyright (C) 2006 Matthias Braun <matze@braunis.de>
+//
+// 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 <config.h>
+
+#include <string>
+#include <stdio.h>
+#include "object/thunderstorm.hpp"
+#include "scripting/thunderstorm.hpp"
+#include "math/vector.hpp"
+
+#define NOIMPL log_fatal << __PRETTY_FUNCTION__ << " not implemented."
+
+namespace Scripting
+{
+
+ Thunderstorm::Thunderstorm(::Thunderstorm* thunderstorm)
+ : thunderstorm(thunderstorm)
+ {
+ }
+
+ Thunderstorm::~Thunderstorm()
+ {
+ }
+
+ void Thunderstorm::start()
+ {
+ thunderstorm->start();
+ }
+
+ void Thunderstorm::stop()
+ {
+ thunderstorm->stop();
+ }
+
+ void Thunderstorm::thunder()
+ {
+ thunderstorm->thunder();
+ }
+
+ void Thunderstorm::lightning()
+ {
+ thunderstorm->lightning();
+ }
+
+ void Thunderstorm::flash()
+ {
+ thunderstorm->flash();
+ }
+
+ void Thunderstorm::electrify()
+ {
+ thunderstorm->electrify();
+ }
+
+}
--- /dev/null
+// $Id$
+//
+// SuperTux
+// Copyright (C) 2006 Matthias Braun <matze@braunis.de>
+//
+// 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 __SCRIPTING_THUNDERSTORM_H__
+#define __SCRIPTING_THUNDERSTORM_H__
+
+#ifndef SCRIPTING_API
+class Thunderstorm;
+typedef Thunderstorm _Thunderstorm;
+#endif
+
+namespace Scripting
+{
+
+class Thunderstorm
+{
+public:
+#ifndef SCRIPTING_API
+ Thunderstorm(_Thunderstorm* thunderstorm);
+ ~Thunderstorm();
+#endif
+
+ /**
+ * Start playing thunder and lightning at configured interval
+ */
+ void start();
+
+ /**
+ * Stop playing thunder and lightning at configured interval
+ */
+ void stop();
+
+ /**
+ * Play thunder
+ */
+ void thunder();
+
+ /**
+ * Play lightning, i.e. call flash() and electrify()
+ */
+ void lightning();
+
+ /**
+ * Display a nice flash
+ */
+ void flash();
+
+ /**
+ * Electrify water throughout the whole sector for a short time
+ */
+ void electrify();
+
+#ifndef SCRIPTING_API
+ _Thunderstorm* thunderstorm;
+#endif
+};
+
+}
+
+#endif
--- /dev/null
+// $Id$
+//
+// SuperTux
+// Copyright (C) 2006 Matthias Braun <matze@braunis.de>
+//
+// 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 <config.h>
+
+#include <string>
+#include <stdio.h>
+#include "object/tilemap.hpp"
+#include "scripting/tilemap.hpp"
+#include "math/vector.hpp"
+
+#define NOIMPL log_fatal << __PRETTY_FUNCTION__ << " not implemented."
+
+namespace Scripting
+{
+
+ TileMap::TileMap(::TileMap* tilemap)
+ : tilemap(tilemap)
+ { }
+
+ TileMap::~TileMap()
+ { }
+
+ void TileMap::goto_node(int node_no)
+ {
+ tilemap->goto_node(node_no);
+ }
+
+ void TileMap::start_moving()
+ {
+ tilemap->start_moving();
+ }
+
+ void TileMap::stop_moving()
+ {
+ tilemap->stop_moving();
+ }
+
+ void TileMap::fade(float alpha, float seconds)
+ {
+ tilemap->fade(alpha, seconds);
+ }
+
+ void TileMap::set_alpha(float alpha)
+ {
+ tilemap->set_alpha(alpha);
+ }
+
+ float TileMap::get_alpha()
+ {
+ return tilemap->get_alpha();
+ }
+
+}
--- /dev/null
+// $Id$
+//
+// SuperTux
+// Copyright (C) 2006 Matthias Braun <matze@braunis.de>
+//
+// 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 __SCRIPTING_TILEMAP_H__
+#define __SCRIPTING_TILEMAP_H__
+
+#ifndef SCRIPTING_API
+class TileMap;
+typedef TileMap _TileMap;
+#endif
+
+namespace Scripting
+{
+
+class TileMap
+{
+public:
+#ifndef SCRIPTING_API
+ TileMap(_TileMap* tilemap);
+ ~TileMap();
+#endif
+
+ /** Move tilemap until at given node, then stop */
+ void goto_node(int node_no);
+
+ /** Start moving tilemap */
+ void start_moving();
+
+ /** Stop tilemap at next node */
+ void stop_moving();
+
+ /**
+ * Start fading the tilemap to opacity given by @c alpha.
+ * Destination opacity will be reached after @c seconds seconds. Also influences solidity.
+ */
+ void fade(float alpha, float seconds);
+
+ /**
+ * Instantly switch tilemap's opacity to @c alpha. Also influences solidity.
+ */
+ void set_alpha(float alpha);
+
+ /**
+ * Return tilemap's opacity. Note that while the tilemap is fading in or out, this will return the current alpha value, not the target alpha.
+ */
+ float get_alpha();
+
+#ifndef SCRIPTING_API
+ _TileMap* tilemap;
+#endif
+};
+
+}
+
+#endif
--- /dev/null
+// $Id$
+//
+// SuperTux
+// Copyright (C) 2006 Matthias Braun <matze@braunis.de>
+//
+// 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 <config.h>
+
+#include <algorithm>
+
+#include "time_scheduler.hpp"
+#include "squirrel_util.hpp"
+#include "squirrel_error.hpp"
+#include "log.hpp"
+
+namespace Scripting
+{
+
+TimeScheduler* TimeScheduler::instance = NULL;
+
+TimeScheduler::TimeScheduler()
+{
+}
+
+TimeScheduler::~TimeScheduler()
+{
+}
+
+void
+TimeScheduler::update(float time)
+{
+ while(!schedule.empty() && schedule.front().wakeup_time < time) {
+ HSQOBJECT thread_ref = schedule.front().thread_ref;
+
+ sq_pushobject(global_vm, thread_ref);
+ sq_getweakrefval(global_vm, -1);
+
+ HSQUIRRELVM scheduled_vm;
+ if(sq_gettype(global_vm, -1) == OT_THREAD &&
+ SQ_SUCCEEDED(sq_getthread(global_vm, -1, &scheduled_vm))) {
+ if(SQ_FAILED(sq_wakeupvm(scheduled_vm, SQFalse, SQFalse, SQTrue))) {
+ std::ostringstream msg;
+ msg << "Couldn't wakeup scheduled squirrel VM: ";
+ sq_getlasterror(scheduled_vm);
+ if(sq_gettype(scheduled_vm, -1) != OT_STRING) {
+ msg << "(no info)";
+ } else {
+ const char* lasterr;
+ sq_getstring(scheduled_vm, -1, &lasterr);
+ msg << lasterr;
+ }
+ log_warning << msg.str() << std::endl;
+ sq_pop(scheduled_vm, 1);
+ }
+ }
+
+ sq_release(global_vm, &thread_ref);
+ sq_pop(global_vm, 2);
+
+ std::pop_heap(schedule.begin(), schedule.end());
+ schedule.pop_back();
+ }
+}
+
+void
+TimeScheduler::schedule_thread(HSQUIRRELVM scheduled_vm, float time)
+{
+ // create a weakref to the VM
+ SQObject vm_obj = vm_to_object(scheduled_vm);
+ sq_pushobject(global_vm, vm_obj);
+ sq_weakref(global_vm, -1);
+
+ ScheduleEntry entry;
+ if(SQ_FAILED(sq_getstackobj(global_vm, -1, & entry.thread_ref))) {
+ sq_pop(global_vm, 2);
+ throw SquirrelError(global_vm, "Couldn't get thread weakref from vm");
+ }
+ entry.wakeup_time = time;
+
+ sq_addref(global_vm, & entry.thread_ref);
+ sq_pop(global_vm, 2);
+
+ schedule.push_back(entry);
+ std::push_heap(schedule.begin(), schedule.end());
+}
+
+}
--- /dev/null
+// $Id$
+//
+// SuperTux
+// Copyright (C) 2006 Matthias Braun <matze@braunis.de>
+//
+// 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 __TIME_SCHEDULER_HPP__
+#define __TIME_SCHEDULER_HPP__
+
+#include <vector>
+#include <squirrel.h>
+
+namespace Scripting
+{
+
+/**
+ * This class keeps a list of squirrel threads that are scheduled for a certain
+ * time. (the typical result of a wait() command in a squirrel script)
+ */
+class TimeScheduler
+{
+public:
+ TimeScheduler();
+ virtual ~TimeScheduler();
+
+ void update(float time);
+ void schedule_thread(HSQUIRRELVM vm, float time);
+
+ static TimeScheduler* instance;
+
+private:
+ struct ScheduleEntry {
+ /// weak reference to the squirrel vm object
+ HSQOBJECT thread_ref;
+ /// time when the thread should be woken up
+ float wakeup_time;
+
+ bool operator<(const ScheduleEntry& other) const
+ {
+ // we need the smallest value on top
+ return wakeup_time > other.wakeup_time;
+ }
+ };
+
+ typedef std::vector<ScheduleEntry> ScheduleHeap;
+ ScheduleHeap schedule;
+};
+
+}
+
+#endif
--- /dev/null
+// $Id$
+//
+// SuperTux
+// Copyright (C) 2007 Matthias Braun <matze@braunis.de>
+//
+// 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 __SCRIPTING_WILLOWISP_H__
+#define __SCRIPTING_WILLOWISP_H__
+
+namespace Scripting
+{
+
+class WillOWisp
+{
+public:
+#ifndef SCRIPTING_API
+ virtual ~WillOWisp()
+ {}
+#endif
+
+ /** Move willowisp to given node */
+ virtual void goto_node(int node_no) = 0;
+
+ /** set willowisp state can be:
+ * -stopped willowisp doesn't move
+ * -move_path willowisp moves along the path (call goto_node)
+ * -move_path_track willowisp moves along path but catchs tux when he is near
+ * -normal "normal" mode starts tracking tux when he is near enough
+ * -vanish vanish
+ */
+ virtual void set_state(const std::string& state) = 0;
+
+ virtual void start_moving() = 0;
+ virtual void stop_moving() = 0;
+};
+
+}
+
+#endif
--- /dev/null
+// $Id$
+//
+// SuperTux
+// Copyright (C) 2006 Matthias Braun <matze@braunis.de>
+//
+// 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 <config.h>
+
+#include <string>
+#include <stdio.h>
+#include "object/wind.hpp"
+#include "scripting/wind.hpp"
+#include "math/vector.hpp"
+
+#define NOIMPL log_fatal << __PRETTY_FUNCTION__ << " not implemented."
+
+namespace Scripting
+{
+
+ Wind::Wind(::Wind* wind)
+ : wind(wind)
+ { }
+
+ Wind::~Wind()
+ { }
+
+ void Wind::start()
+ {
+ wind->start();
+ }
+
+ void Wind::stop()
+ {
+ wind->stop();
+ }
+
+}
--- /dev/null
+// $Id$
+//
+// SuperTux
+// Copyright (C) 2006 Matthias Braun <matze@braunis.de>
+//
+// 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 __SCRIPTING_WIND_H__
+#define __SCRIPTING_WIND_H__
+
+#ifndef SCRIPTING_API
+class Wind;
+typedef Wind _Wind;
+#endif
+
+namespace Scripting
+{
+
+class Wind
+{
+public:
+#ifndef SCRIPTING_API
+ Wind(_Wind* wind);
+ ~Wind();
+#endif
+
+ /** Start wind */
+ void start();
+
+ /** Stop wind */
+ void stop();
+
+#ifndef SCRIPTING_API
+ _Wind* wind;
+#endif
+};
+
+}
+
+#endif
--- /dev/null
+/**
+ * WARNING: This file is automatically generated from:
+ * 'src/scripting/wrapper.interface.hpp'
+ * DO NOT CHANGE
+ */
+#include <config.h>
+
+#include <new>
+#include <assert.h>
+#include <string>
+#include <sstream>
+#include <squirrel.h>
+#include "squirrel_error.hpp"
+#include "wrapper.interface.hpp"
+
+namespace Scripting
+{
+namespace Wrapper
+{
+
+static SQInteger DisplayEffect_release_hook(SQUserPointer ptr, SQInteger )
+{
+ Scripting::DisplayEffect* _this = reinterpret_cast<Scripting::DisplayEffect*> (ptr);
+ delete _this;
+ return 0;
+}
+
+static SQInteger DisplayEffect_fade_out_wrapper(HSQUIRRELVM vm)
+{
+ SQUserPointer data;
+ if(SQ_FAILED(sq_getinstanceup(vm, 1, &data, 0))) {
+ sq_throwerror(vm, _SC("'fade_out' called without instance"));
+ return SQ_ERROR;
+ }
+ Scripting::DisplayEffect* _this = reinterpret_cast<Scripting::DisplayEffect*> (data);
+ SQFloat arg0;
+ if(SQ_FAILED(sq_getfloat(vm, 2, &arg0))) {
+ sq_throwerror(vm, _SC("Argument 1 not a float"));
+ return SQ_ERROR;
+ }
+
+ try {
+ _this->fade_out(static_cast<float> (arg0));
+
+ return 0;
+
+ } catch(std::exception& e) {
+ sq_throwerror(vm, e.what());
+ return SQ_ERROR;
+ } catch(...) {
+ sq_throwerror(vm, _SC("Unexpected exception while executing function 'fade_out'"));
+ return SQ_ERROR;
+ }
+
+}
+
+static SQInteger DisplayEffect_fade_in_wrapper(HSQUIRRELVM vm)
+{
+ SQUserPointer data;
+ if(SQ_FAILED(sq_getinstanceup(vm, 1, &data, 0))) {
+ sq_throwerror(vm, _SC("'fade_in' called without instance"));
+ return SQ_ERROR;
+ }
+ Scripting::DisplayEffect* _this = reinterpret_cast<Scripting::DisplayEffect*> (data);
+ SQFloat arg0;
+ if(SQ_FAILED(sq_getfloat(vm, 2, &arg0))) {
+ sq_throwerror(vm, _SC("Argument 1 not a float"));
+ return SQ_ERROR;
+ }
+
+ try {
+ _this->fade_in(static_cast<float> (arg0));
+
+ return 0;
+
+ } catch(std::exception& e) {
+ sq_throwerror(vm, e.what());
+ return SQ_ERROR;
+ } catch(...) {
+ sq_throwerror(vm, _SC("Unexpected exception while executing function 'fade_in'"));
+ return SQ_ERROR;
+ }
+
+}
+
+static SQInteger DisplayEffect_set_black_wrapper(HSQUIRRELVM vm)
+{
+ SQUserPointer data;
+ if(SQ_FAILED(sq_getinstanceup(vm, 1, &data, 0))) {
+ sq_throwerror(vm, _SC("'set_black' called without instance"));
+ return SQ_ERROR;
+ }
+ Scripting::DisplayEffect* _this = reinterpret_cast<Scripting::DisplayEffect*> (data);
+ SQBool arg0;
+ if(SQ_FAILED(sq_getbool(vm, 2, &arg0))) {
+ sq_throwerror(vm, _SC("Argument 1 not a bool"));
+ return SQ_ERROR;
+ }
+
+ try {
+ _this->set_black(arg0 == SQTrue);
+
+ return 0;
+
+ } catch(std::exception& e) {
+ sq_throwerror(vm, e.what());
+ return SQ_ERROR;
+ } catch(...) {
+ sq_throwerror(vm, _SC("Unexpected exception while executing function 'set_black'"));
+ return SQ_ERROR;
+ }
+
+}
+
+static SQInteger DisplayEffect_is_black_wrapper(HSQUIRRELVM vm)
+{
+ SQUserPointer data;
+ if(SQ_FAILED(sq_getinstanceup(vm, 1, &data, 0))) {
+ sq_throwerror(vm, _SC("'is_black' called without instance"));
+ return SQ_ERROR;
+ }
+ Scripting::DisplayEffect* _this = reinterpret_cast<Scripting::DisplayEffect*> (data);
+
+ try {
+ bool return_value = _this->is_black();
+
+ sq_pushbool(vm, return_value);
+ return 1;
+
+ } catch(std::exception& e) {
+ sq_throwerror(vm, e.what());
+ return SQ_ERROR;
+ } catch(...) {
+ sq_throwerror(vm, _SC("Unexpected exception while executing function 'is_black'"));
+ return SQ_ERROR;
+ }
+
+}
+
+static SQInteger DisplayEffect_sixteen_to_nine_wrapper(HSQUIRRELVM vm)
+{
+ SQUserPointer data;
+ if(SQ_FAILED(sq_getinstanceup(vm, 1, &data, 0))) {
+ sq_throwerror(vm, _SC("'sixteen_to_nine' called without instance"));
+ return SQ_ERROR;
+ }
+ Scripting::DisplayEffect* _this = reinterpret_cast<Scripting::DisplayEffect*> (data);
+ SQFloat arg0;
+ if(SQ_FAILED(sq_getfloat(vm, 2, &arg0))) {
+ sq_throwerror(vm, _SC("Argument 1 not a float"));
+ return SQ_ERROR;
+ }
+
+ try {
+ _this->sixteen_to_nine(static_cast<float> (arg0));
+
+ return 0;
+
+ } catch(std::exception& e) {
+ sq_throwerror(vm, e.what());
+ return SQ_ERROR;
+ } catch(...) {
+ sq_throwerror(vm, _SC("Unexpected exception while executing function 'sixteen_to_nine'"));
+ return SQ_ERROR;
+ }
+
+}
+
+static SQInteger DisplayEffect_four_to_three_wrapper(HSQUIRRELVM vm)
+{
+ SQUserPointer data;
+ if(SQ_FAILED(sq_getinstanceup(vm, 1, &data, 0))) {
+ sq_throwerror(vm, _SC("'four_to_three' called without instance"));
+ return SQ_ERROR;
+ }
+ Scripting::DisplayEffect* _this = reinterpret_cast<Scripting::DisplayEffect*> (data);
+ SQFloat arg0;
+ if(SQ_FAILED(sq_getfloat(vm, 2, &arg0))) {
+ sq_throwerror(vm, _SC("Argument 1 not a float"));
+ return SQ_ERROR;
+ }
+
+ try {
+ _this->four_to_three(static_cast<float> (arg0));
+
+ return 0;
+
+ } catch(std::exception& e) {
+ sq_throwerror(vm, e.what());
+ return SQ_ERROR;
+ } catch(...) {
+ sq_throwerror(vm, _SC("Unexpected exception while executing function 'four_to_three'"));
+ return SQ_ERROR;
+ }
+
+}
+
+static SQInteger Camera_release_hook(SQUserPointer ptr, SQInteger )
+{
+ Scripting::Camera* _this = reinterpret_cast<Scripting::Camera*> (ptr);
+ delete _this;
+ return 0;
+}
+
+static SQInteger Camera_reload_config_wrapper(HSQUIRRELVM vm)
+{
+ SQUserPointer data;
+ if(SQ_FAILED(sq_getinstanceup(vm, 1, &data, 0))) {
+ sq_throwerror(vm, _SC("'reload_config' called without instance"));
+ return SQ_ERROR;
+ }
+ Scripting::Camera* _this = reinterpret_cast<Scripting::Camera*> (data);
+
+ try {
+ _this->reload_config();
+
+ return 0;
+
+ } catch(std::exception& e) {
+ sq_throwerror(vm, e.what());
+ return SQ_ERROR;
+ } catch(...) {
+ sq_throwerror(vm, _SC("Unexpected exception while executing function 'reload_config'"));
+ return SQ_ERROR;
+ }
+
+}
+
+static SQInteger Camera_shake_wrapper(HSQUIRRELVM vm)
+{
+ SQUserPointer data;
+ if(SQ_FAILED(sq_getinstanceup(vm, 1, &data, 0))) {
+ sq_throwerror(vm, _SC("'shake' called without instance"));
+ return SQ_ERROR;
+ }
+ Scripting::Camera* _this = reinterpret_cast<Scripting::Camera*> (data);
+ SQFloat arg0;
+ if(SQ_FAILED(sq_getfloat(vm, 2, &arg0))) {
+ sq_throwerror(vm, _SC("Argument 1 not a float"));
+ return SQ_ERROR;
+ }
+ SQFloat arg1;
+ if(SQ_FAILED(sq_getfloat(vm, 3, &arg1))) {
+ sq_throwerror(vm, _SC("Argument 2 not a float"));
+ return SQ_ERROR;
+ }
+ SQFloat arg2;
+ if(SQ_FAILED(sq_getfloat(vm, 4, &arg2))) {
+ sq_throwerror(vm, _SC("Argument 3 not a float"));
+ return SQ_ERROR;
+ }
+
+ try {
+ _this->shake(static_cast<float> (arg0), static_cast<float> (arg1), static_cast<float> (arg2));
+
+ return 0;
+
+ } catch(std::exception& e) {
+ sq_throwerror(vm, e.what());
+ return SQ_ERROR;
+ } catch(...) {
+ sq_throwerror(vm, _SC("Unexpected exception while executing function 'shake'"));
+ return SQ_ERROR;
+ }
+
+}
+
+static SQInteger Camera_set_pos_wrapper(HSQUIRRELVM vm)
+{
+ SQUserPointer data;
+ if(SQ_FAILED(sq_getinstanceup(vm, 1, &data, 0))) {
+ sq_throwerror(vm, _SC("'set_pos' called without instance"));
+ return SQ_ERROR;
+ }
+ Scripting::Camera* _this = reinterpret_cast<Scripting::Camera*> (data);
+ SQFloat arg0;
+ if(SQ_FAILED(sq_getfloat(vm, 2, &arg0))) {
+ sq_throwerror(vm, _SC("Argument 1 not a float"));
+ return SQ_ERROR;
+ }
+ SQFloat arg1;
+ if(SQ_FAILED(sq_getfloat(vm, 3, &arg1))) {
+ sq_throwerror(vm, _SC("Argument 2 not a float"));
+ return SQ_ERROR;
+ }
+
+ try {
+ _this->set_pos(static_cast<float> (arg0), static_cast<float> (arg1));
+
+ return 0;
+
+ } catch(std::exception& e) {
+ sq_throwerror(vm, e.what());
+ return SQ_ERROR;
+ } catch(...) {
+ sq_throwerror(vm, _SC("Unexpected exception while executing function 'set_pos'"));
+ return SQ_ERROR;
+ }
+
+}
+
+static SQInteger Camera_set_mode_wrapper(HSQUIRRELVM vm)
+{
+ SQUserPointer data;
+ if(SQ_FAILED(sq_getinstanceup(vm, 1, &data, 0))) {
+ sq_throwerror(vm, _SC("'set_mode' called without instance"));
+ return SQ_ERROR;
+ }
+ Scripting::Camera* _this = reinterpret_cast<Scripting::Camera*> (data);
+ const SQChar* arg0;
+ if(SQ_FAILED(sq_getstring(vm, 2, &arg0))) {
+ sq_throwerror(vm, _SC("Argument 1 not a string"));
+ return SQ_ERROR;
+ }
+
+ try {
+ _this->set_mode(arg0);
+
+ return 0;
+
+ } catch(std::exception& e) {
+ sq_throwerror(vm, e.what());
+ return SQ_ERROR;
+ } catch(...) {
+ sq_throwerror(vm, _SC("Unexpected exception while executing function 'set_mode'"));
+ return SQ_ERROR;
+ }
+
+}
+
+static SQInteger Camera_scroll_to_wrapper(HSQUIRRELVM vm)
+{
+ SQUserPointer data;
+ if(SQ_FAILED(sq_getinstanceup(vm, 1, &data, 0))) {
+ sq_throwerror(vm, _SC("'scroll_to' called without instance"));
+ return SQ_ERROR;
+ }
+ Scripting::Camera* _this = reinterpret_cast<Scripting::Camera*> (data);
+ SQFloat arg0;
+ if(SQ_FAILED(sq_getfloat(vm, 2, &arg0))) {
+ sq_throwerror(vm, _SC("Argument 1 not a float"));
+ return SQ_ERROR;
+ }
+ SQFloat arg1;
+ if(SQ_FAILED(sq_getfloat(vm, 3, &arg1))) {
+ sq_throwerror(vm, _SC("Argument 2 not a float"));
+ return SQ_ERROR;
+ }
+ SQFloat arg2;
+ if(SQ_FAILED(sq_getfloat(vm, 4, &arg2))) {
+ sq_throwerror(vm, _SC("Argument 3 not a float"));
+ return SQ_ERROR;
+ }
+
+ try {
+ _this->scroll_to(static_cast<float> (arg0), static_cast<float> (arg1), static_cast<float> (arg2));
+
+ return 0;
+
+ } catch(std::exception& e) {
+ sq_throwerror(vm, e.what());
+ return SQ_ERROR;
+ } catch(...) {
+ sq_throwerror(vm, _SC("Unexpected exception while executing function 'scroll_to'"));
+ return SQ_ERROR;
+ }
+
+}
+
+static SQInteger Level_release_hook(SQUserPointer ptr, SQInteger )
+{
+ Scripting::Level* _this = reinterpret_cast<Scripting::Level*> (ptr);
+ delete _this;
+ return 0;
+}
+
+static SQInteger Level_finish_wrapper(HSQUIRRELVM vm)
+{
+ SQUserPointer data;
+ if(SQ_FAILED(sq_getinstanceup(vm, 1, &data, 0))) {
+ sq_throwerror(vm, _SC("'finish' called without instance"));
+ return SQ_ERROR;
+ }
+ Scripting::Level* _this = reinterpret_cast<Scripting::Level*> (data);
+ SQBool arg0;
+ if(SQ_FAILED(sq_getbool(vm, 2, &arg0))) {
+ sq_throwerror(vm, _SC("Argument 1 not a bool"));
+ return SQ_ERROR;
+ }
+
+ try {
+ _this->finish(arg0 == SQTrue);
+
+ return 0;
+
+ } catch(std::exception& e) {
+ sq_throwerror(vm, e.what());
+ return SQ_ERROR;
+ } catch(...) {
+ sq_throwerror(vm, _SC("Unexpected exception while executing function 'finish'"));
+ return SQ_ERROR;
+ }
+
+}
+
+static SQInteger Level_spawn_wrapper(HSQUIRRELVM vm)
+{
+ SQUserPointer data;
+ if(SQ_FAILED(sq_getinstanceup(vm, 1, &data, 0))) {
+ sq_throwerror(vm, _SC("'spawn' called without instance"));
+ return SQ_ERROR;
+ }
+ Scripting::Level* _this = reinterpret_cast<Scripting::Level*> (data);
+ const SQChar* arg0;
+ if(SQ_FAILED(sq_getstring(vm, 2, &arg0))) {
+ sq_throwerror(vm, _SC("Argument 1 not a string"));
+ return SQ_ERROR;
+ }
+ const SQChar* arg1;
+ if(SQ_FAILED(sq_getstring(vm, 3, &arg1))) {
+ sq_throwerror(vm, _SC("Argument 2 not a string"));
+ return SQ_ERROR;
+ }
+
+ try {
+ _this->spawn(arg0, arg1);
+
+ return 0;
+
+ } catch(std::exception& e) {
+ sq_throwerror(vm, e.what());
+ return SQ_ERROR;
+ } catch(...) {
+ sq_throwerror(vm, _SC("Unexpected exception while executing function 'spawn'"));
+ return SQ_ERROR;
+ }
+
+}
+
+static SQInteger Level_flip_vertically_wrapper(HSQUIRRELVM vm)
+{
+ SQUserPointer data;
+ if(SQ_FAILED(sq_getinstanceup(vm, 1, &data, 0))) {
+ sq_throwerror(vm, _SC("'flip_vertically' called without instance"));
+ return SQ_ERROR;
+ }
+ Scripting::Level* _this = reinterpret_cast<Scripting::Level*> (data);
+
+ try {
+ _this->flip_vertically();
+
+ return 0;
+
+ } catch(std::exception& e) {
+ sq_throwerror(vm, e.what());
+ return SQ_ERROR;
+ } catch(...) {
+ sq_throwerror(vm, _SC("Unexpected exception while executing function 'flip_vertically'"));
+ return SQ_ERROR;
+ }
+
+}
+
+static SQInteger Level_toggle_pause_wrapper(HSQUIRRELVM vm)
+{
+ SQUserPointer data;
+ if(SQ_FAILED(sq_getinstanceup(vm, 1, &data, 0))) {
+ sq_throwerror(vm, _SC("'toggle_pause' called without instance"));
+ return SQ_ERROR;
+ }
+ Scripting::Level* _this = reinterpret_cast<Scripting::Level*> (data);
+
+ try {
+ _this->toggle_pause();
+
+ return 0;
+
+ } catch(std::exception& e) {
+ sq_throwerror(vm, e.what());
+ return SQ_ERROR;
+ } catch(...) {
+ sq_throwerror(vm, _SC("Unexpected exception while executing function 'toggle_pause'"));
+ return SQ_ERROR;
+ }
+
+}
+
+static SQInteger ScriptedObject_release_hook(SQUserPointer ptr, SQInteger )
+{
+ Scripting::ScriptedObject* _this = reinterpret_cast<Scripting::ScriptedObject*> (ptr);
+ delete _this;
+ return 0;
+}
+
+static SQInteger ScriptedObject_set_action_wrapper(HSQUIRRELVM vm)
+{
+ SQUserPointer data;
+ if(SQ_FAILED(sq_getinstanceup(vm, 1, &data, 0))) {
+ sq_throwerror(vm, _SC("'set_action' called without instance"));
+ return SQ_ERROR;
+ }
+ Scripting::ScriptedObject* _this = reinterpret_cast<Scripting::ScriptedObject*> (data);
+ const SQChar* arg0;
+ if(SQ_FAILED(sq_getstring(vm, 2, &arg0))) {
+ sq_throwerror(vm, _SC("Argument 1 not a string"));
+ return SQ_ERROR;
+ }
+
+ try {
+ _this->set_action(arg0);
+
+ return 0;
+
+ } catch(std::exception& e) {
+ sq_throwerror(vm, e.what());
+ return SQ_ERROR;
+ } catch(...) {
+ sq_throwerror(vm, _SC("Unexpected exception while executing function 'set_action'"));
+ return SQ_ERROR;
+ }
+
+}
+
+static SQInteger ScriptedObject_get_action_wrapper(HSQUIRRELVM vm)
+{
+ SQUserPointer data;
+ if(SQ_FAILED(sq_getinstanceup(vm, 1, &data, 0))) {
+ sq_throwerror(vm, _SC("'get_action' called without instance"));
+ return SQ_ERROR;
+ }
+ Scripting::ScriptedObject* _this = reinterpret_cast<Scripting::ScriptedObject*> (data);
+
+ try {
+ std::string return_value = _this->get_action();
+
+ sq_pushstring(vm, return_value.c_str(), return_value.size());
+ return 1;
+
+ } catch(std::exception& e) {
+ sq_throwerror(vm, e.what());
+ return SQ_ERROR;
+ } catch(...) {
+ sq_throwerror(vm, _SC("Unexpected exception while executing function 'get_action'"));
+ return SQ_ERROR;
+ }
+
+}
+
+static SQInteger ScriptedObject_move_wrapper(HSQUIRRELVM vm)
+{
+ SQUserPointer data;
+ if(SQ_FAILED(sq_getinstanceup(vm, 1, &data, 0))) {
+ sq_throwerror(vm, _SC("'move' called without instance"));
+ return SQ_ERROR;
+ }
+ Scripting::ScriptedObject* _this = reinterpret_cast<Scripting::ScriptedObject*> (data);
+ SQFloat arg0;
+ if(SQ_FAILED(sq_getfloat(vm, 2, &arg0))) {
+ sq_throwerror(vm, _SC("Argument 1 not a float"));
+ return SQ_ERROR;
+ }
+ SQFloat arg1;
+ if(SQ_FAILED(sq_getfloat(vm, 3, &arg1))) {
+ sq_throwerror(vm, _SC("Argument 2 not a float"));
+ return SQ_ERROR;
+ }
+
+ try {
+ _this->move(static_cast<float> (arg0), static_cast<float> (arg1));
+
+ return 0;
+
+ } catch(std::exception& e) {
+ sq_throwerror(vm, e.what());
+ return SQ_ERROR;
+ } catch(...) {
+ sq_throwerror(vm, _SC("Unexpected exception while executing function 'move'"));
+ return SQ_ERROR;
+ }
+
+}
+
+static SQInteger ScriptedObject_set_pos_wrapper(HSQUIRRELVM vm)
+{
+ SQUserPointer data;
+ if(SQ_FAILED(sq_getinstanceup(vm, 1, &data, 0))) {
+ sq_throwerror(vm, _SC("'set_pos' called without instance"));
+ return SQ_ERROR;
+ }
+ Scripting::ScriptedObject* _this = reinterpret_cast<Scripting::ScriptedObject*> (data);
+ SQFloat arg0;
+ if(SQ_FAILED(sq_getfloat(vm, 2, &arg0))) {
+ sq_throwerror(vm, _SC("Argument 1 not a float"));
+ return SQ_ERROR;
+ }
+ SQFloat arg1;
+ if(SQ_FAILED(sq_getfloat(vm, 3, &arg1))) {
+ sq_throwerror(vm, _SC("Argument 2 not a float"));
+ return SQ_ERROR;
+ }
+
+ try {
+ _this->set_pos(static_cast<float> (arg0), static_cast<float> (arg1));
+
+ return 0;
+
+ } catch(std::exception& e) {
+ sq_throwerror(vm, e.what());
+ return SQ_ERROR;
+ } catch(...) {
+ sq_throwerror(vm, _SC("Unexpected exception while executing function 'set_pos'"));
+ return SQ_ERROR;
+ }
+
+}
+
+static SQInteger ScriptedObject_get_pos_x_wrapper(HSQUIRRELVM vm)
+{
+ SQUserPointer data;
+ if(SQ_FAILED(sq_getinstanceup(vm, 1, &data, 0))) {
+ sq_throwerror(vm, _SC("'get_pos_x' called without instance"));
+ return SQ_ERROR;
+ }
+ Scripting::ScriptedObject* _this = reinterpret_cast<Scripting::ScriptedObject*> (data);
+
+ try {
+ float return_value = _this->get_pos_x();
+
+ sq_pushfloat(vm, return_value);
+ return 1;
+
+ } catch(std::exception& e) {
+ sq_throwerror(vm, e.what());
+ return SQ_ERROR;
+ } catch(...) {
+ sq_throwerror(vm, _SC("Unexpected exception while executing function 'get_pos_x'"));
+ return SQ_ERROR;
+ }
+
+}
+
+static SQInteger ScriptedObject_get_pos_y_wrapper(HSQUIRRELVM vm)
+{
+ SQUserPointer data;
+ if(SQ_FAILED(sq_getinstanceup(vm, 1, &data, 0))) {
+ sq_throwerror(vm, _SC("'get_pos_y' called without instance"));
+ return SQ_ERROR;
+ }
+ Scripting::ScriptedObject* _this = reinterpret_cast<Scripting::ScriptedObject*> (data);
+
+ try {
+ float return_value = _this->get_pos_y();
+
+ sq_pushfloat(vm, return_value);
+ return 1;
+
+ } catch(std::exception& e) {
+ sq_throwerror(vm, e.what());
+ return SQ_ERROR;
+ } catch(...) {
+ sq_throwerror(vm, _SC("Unexpected exception while executing function 'get_pos_y'"));
+ return SQ_ERROR;
+ }
+
+}
+
+static SQInteger ScriptedObject_set_velocity_wrapper(HSQUIRRELVM vm)
+{
+ SQUserPointer data;
+ if(SQ_FAILED(sq_getinstanceup(vm, 1, &data, 0))) {
+ sq_throwerror(vm, _SC("'set_velocity' called without instance"));
+ return SQ_ERROR;
+ }
+ Scripting::ScriptedObject* _this = reinterpret_cast<Scripting::ScriptedObject*> (data);
+ SQFloat arg0;
+ if(SQ_FAILED(sq_getfloat(vm, 2, &arg0))) {
+ sq_throwerror(vm, _SC("Argument 1 not a float"));
+ return SQ_ERROR;
+ }
+ SQFloat arg1;
+ if(SQ_FAILED(sq_getfloat(vm, 3, &arg1))) {
+ sq_throwerror(vm, _SC("Argument 2 not a float"));
+ return SQ_ERROR;
+ }
+
+ try {
+ _this->set_velocity(static_cast<float> (arg0), static_cast<float> (arg1));
+
+ return 0;
+
+ } catch(std::exception& e) {
+ sq_throwerror(vm, e.what());
+ return SQ_ERROR;
+ } catch(...) {
+ sq_throwerror(vm, _SC("Unexpected exception while executing function 'set_velocity'"));
+ return SQ_ERROR;
+ }
+
+}
+
+static SQInteger ScriptedObject_get_velocity_x_wrapper(HSQUIRRELVM vm)
+{
+ SQUserPointer data;
+ if(SQ_FAILED(sq_getinstanceup(vm, 1, &data, 0))) {
+ sq_throwerror(vm, _SC("'get_velocity_x' called without instance"));
+ return SQ_ERROR;
+ }
+ Scripting::ScriptedObject* _this = reinterpret_cast<Scripting::ScriptedObject*> (data);
+
+ try {
+ float return_value = _this->get_velocity_x();
+
+ sq_pushfloat(vm, return_value);
+ return 1;
+
+ } catch(std::exception& e) {
+ sq_throwerror(vm, e.what());
+ return SQ_ERROR;
+ } catch(...) {
+ sq_throwerror(vm, _SC("Unexpected exception while executing function 'get_velocity_x'"));
+ return SQ_ERROR;
+ }
+
+}
+
+static SQInteger ScriptedObject_get_velocity_y_wrapper(HSQUIRRELVM vm)
+{
+ SQUserPointer data;
+ if(SQ_FAILED(sq_getinstanceup(vm, 1, &data, 0))) {
+ sq_throwerror(vm, _SC("'get_velocity_y' called without instance"));
+ return SQ_ERROR;
+ }
+ Scripting::ScriptedObject* _this = reinterpret_cast<Scripting::ScriptedObject*> (data);
+
+ try {
+ float return_value = _this->get_velocity_y();
+
+ sq_pushfloat(vm, return_value);
+ return 1;
+
+ } catch(std::exception& e) {
+ sq_throwerror(vm, e.what());
+ return SQ_ERROR;
+ } catch(...) {
+ sq_throwerror(vm, _SC("Unexpected exception while executing function 'get_velocity_y'"));
+ return SQ_ERROR;
+ }
+
+}
+
+static SQInteger ScriptedObject_set_visible_wrapper(HSQUIRRELVM vm)
+{
+ SQUserPointer data;
+ if(SQ_FAILED(sq_getinstanceup(vm, 1, &data, 0))) {
+ sq_throwerror(vm, _SC("'set_visible' called without instance"));
+ return SQ_ERROR;
+ }
+ Scripting::ScriptedObject* _this = reinterpret_cast<Scripting::ScriptedObject*> (data);
+ SQBool arg0;
+ if(SQ_FAILED(sq_getbool(vm, 2, &arg0))) {
+ sq_throwerror(vm, _SC("Argument 1 not a bool"));
+ return SQ_ERROR;
+ }
+
+ try {
+ _this->set_visible(arg0 == SQTrue);
+
+ return 0;
+
+ } catch(std::exception& e) {
+ sq_throwerror(vm, e.what());
+ return SQ_ERROR;
+ } catch(...) {
+ sq_throwerror(vm, _SC("Unexpected exception while executing function 'set_visible'"));
+ return SQ_ERROR;
+ }
+
+}
+
+static SQInteger ScriptedObject_is_visible_wrapper(HSQUIRRELVM vm)
+{
+ SQUserPointer data;
+ if(SQ_FAILED(sq_getinstanceup(vm, 1, &data, 0))) {
+ sq_throwerror(vm, _SC("'is_visible' called without instance"));
+ return SQ_ERROR;
+ }
+ Scripting::ScriptedObject* _this = reinterpret_cast<Scripting::ScriptedObject*> (data);
+
+ try {
+ bool return_value = _this->is_visible();
+
+ sq_pushbool(vm, return_value);
+ return 1;
+
+ } catch(std::exception& e) {
+ sq_throwerror(vm, e.what());
+ return SQ_ERROR;
+ } catch(...) {
+ sq_throwerror(vm, _SC("Unexpected exception while executing function 'is_visible'"));
+ return SQ_ERROR;
+ }
+
+}
+
+static SQInteger ScriptedObject_set_solid_wrapper(HSQUIRRELVM vm)
+{
+ SQUserPointer data;
+ if(SQ_FAILED(sq_getinstanceup(vm, 1, &data, 0))) {
+ sq_throwerror(vm, _SC("'set_solid' called without instance"));
+ return SQ_ERROR;
+ }
+ Scripting::ScriptedObject* _this = reinterpret_cast<Scripting::ScriptedObject*> (data);
+ SQBool arg0;
+ if(SQ_FAILED(sq_getbool(vm, 2, &arg0))) {
+ sq_throwerror(vm, _SC("Argument 1 not a bool"));
+ return SQ_ERROR;
+ }
+
+ try {
+ _this->set_solid(arg0 == SQTrue);
+
+ return 0;
+
+ } catch(std::exception& e) {
+ sq_throwerror(vm, e.what());
+ return SQ_ERROR;
+ } catch(...) {
+ sq_throwerror(vm, _SC("Unexpected exception while executing function 'set_solid'"));
+ return SQ_ERROR;
+ }
+
+}
+
+static SQInteger ScriptedObject_is_solid_wrapper(HSQUIRRELVM vm)
+{
+ SQUserPointer data;
+ if(SQ_FAILED(sq_getinstanceup(vm, 1, &data, 0))) {
+ sq_throwerror(vm, _SC("'is_solid' called without instance"));
+ return SQ_ERROR;
+ }
+ Scripting::ScriptedObject* _this = reinterpret_cast<Scripting::ScriptedObject*> (data);
+
+ try {
+ bool return_value = _this->is_solid();
+
+ sq_pushbool(vm, return_value);
+ return 1;
+
+ } catch(std::exception& e) {
+ sq_throwerror(vm, e.what());
+ return SQ_ERROR;
+ } catch(...) {
+ sq_throwerror(vm, _SC("Unexpected exception while executing function 'is_solid'"));
+ return SQ_ERROR;
+ }
+
+}
+
+static SQInteger ScriptedObject_get_name_wrapper(HSQUIRRELVM vm)
+{
+ SQUserPointer data;
+ if(SQ_FAILED(sq_getinstanceup(vm, 1, &data, 0))) {
+ sq_throwerror(vm, _SC("'get_name' called without instance"));
+ return SQ_ERROR;
+ }
+ Scripting::ScriptedObject* _this = reinterpret_cast<Scripting::ScriptedObject*> (data);
+
+ try {
+ std::string return_value = _this->get_name();
+
+ sq_pushstring(vm, return_value.c_str(), return_value.size());
+ return 1;
+
+ } catch(std::exception& e) {
+ sq_throwerror(vm, e.what());
+ return SQ_ERROR;
+ } catch(...) {
+ sq_throwerror(vm, _SC("Unexpected exception while executing function 'get_name'"));
+ return SQ_ERROR;
+ }
+
+}
+
+static SQInteger Text_release_hook(SQUserPointer ptr, SQInteger )
+{
+ Scripting::Text* _this = reinterpret_cast<Scripting::Text*> (ptr);
+ delete _this;
+ return 0;
+}
+
+static SQInteger Text_set_text_wrapper(HSQUIRRELVM vm)
+{
+ SQUserPointer data;
+ if(SQ_FAILED(sq_getinstanceup(vm, 1, &data, 0))) {
+ sq_throwerror(vm, _SC("'set_text' called without instance"));
+ return SQ_ERROR;
+ }
+ Scripting::Text* _this = reinterpret_cast<Scripting::Text*> (data);
+ const SQChar* arg0;
+ if(SQ_FAILED(sq_getstring(vm, 2, &arg0))) {
+ sq_throwerror(vm, _SC("Argument 1 not a string"));
+ return SQ_ERROR;
+ }
+
+ try {
+ _this->set_text(arg0);
+
+ return 0;
+
+ } catch(std::exception& e) {
+ sq_throwerror(vm, e.what());
+ return SQ_ERROR;
+ } catch(...) {
+ sq_throwerror(vm, _SC("Unexpected exception while executing function 'set_text'"));
+ return SQ_ERROR;
+ }
+
+}
+
+static SQInteger Text_set_font_wrapper(HSQUIRRELVM vm)
+{
+ SQUserPointer data;
+ if(SQ_FAILED(sq_getinstanceup(vm, 1, &data, 0))) {
+ sq_throwerror(vm, _SC("'set_font' called without instance"));
+ return SQ_ERROR;
+ }
+ Scripting::Text* _this = reinterpret_cast<Scripting::Text*> (data);
+ const SQChar* arg0;
+ if(SQ_FAILED(sq_getstring(vm, 2, &arg0))) {
+ sq_throwerror(vm, _SC("Argument 1 not a string"));
+ return SQ_ERROR;
+ }
+
+ try {
+ _this->set_font(arg0);
+
+ return 0;
+
+ } catch(std::exception& e) {
+ sq_throwerror(vm, e.what());
+ return SQ_ERROR;
+ } catch(...) {
+ sq_throwerror(vm, _SC("Unexpected exception while executing function 'set_font'"));
+ return SQ_ERROR;
+ }
+
+}
+
+static SQInteger Text_fade_in_wrapper(HSQUIRRELVM vm)
+{
+ SQUserPointer data;
+ if(SQ_FAILED(sq_getinstanceup(vm, 1, &data, 0))) {
+ sq_throwerror(vm, _SC("'fade_in' called without instance"));
+ return SQ_ERROR;
+ }
+ Scripting::Text* _this = reinterpret_cast<Scripting::Text*> (data);
+ SQFloat arg0;
+ if(SQ_FAILED(sq_getfloat(vm, 2, &arg0))) {
+ sq_throwerror(vm, _SC("Argument 1 not a float"));
+ return SQ_ERROR;
+ }
+
+ try {
+ _this->fade_in(static_cast<float> (arg0));
+
+ return 0;
+
+ } catch(std::exception& e) {
+ sq_throwerror(vm, e.what());
+ return SQ_ERROR;
+ } catch(...) {
+ sq_throwerror(vm, _SC("Unexpected exception while executing function 'fade_in'"));
+ return SQ_ERROR;
+ }
+
+}
+
+static SQInteger Text_fade_out_wrapper(HSQUIRRELVM vm)
+{
+ SQUserPointer data;
+ if(SQ_FAILED(sq_getinstanceup(vm, 1, &data, 0))) {
+ sq_throwerror(vm, _SC("'fade_out' called without instance"));
+ return SQ_ERROR;
+ }
+ Scripting::Text* _this = reinterpret_cast<Scripting::Text*> (data);
+ SQFloat arg0;
+ if(SQ_FAILED(sq_getfloat(vm, 2, &arg0))) {
+ sq_throwerror(vm, _SC("Argument 1 not a float"));
+ return SQ_ERROR;
+ }
+
+ try {
+ _this->fade_out(static_cast<float> (arg0));
+
+ return 0;
+
+ } catch(std::exception& e) {
+ sq_throwerror(vm, e.what());
+ return SQ_ERROR;
+ } catch(...) {
+ sq_throwerror(vm, _SC("Unexpected exception while executing function 'fade_out'"));
+ return SQ_ERROR;
+ }
+
+}
+
+static SQInteger Text_set_visible_wrapper(HSQUIRRELVM vm)
+{
+ SQUserPointer data;
+ if(SQ_FAILED(sq_getinstanceup(vm, 1, &data, 0))) {
+ sq_throwerror(vm, _SC("'set_visible' called without instance"));
+ return SQ_ERROR;
+ }
+ Scripting::Text* _this = reinterpret_cast<Scripting::Text*> (data);
+ SQBool arg0;
+ if(SQ_FAILED(sq_getbool(vm, 2, &arg0))) {
+ sq_throwerror(vm, _SC("Argument 1 not a bool"));
+ return SQ_ERROR;
+ }
+
+ try {
+ _this->set_visible(arg0 == SQTrue);
+
+ return 0;
+
+ } catch(std::exception& e) {
+ sq_throwerror(vm, e.what());
+ return SQ_ERROR;
+ } catch(...) {
+ sq_throwerror(vm, _SC("Unexpected exception while executing function 'set_visible'"));
+ return SQ_ERROR;
+ }
+
+}
+
+static SQInteger Text_set_centered_wrapper(HSQUIRRELVM vm)
+{
+ SQUserPointer data;
+ if(SQ_FAILED(sq_getinstanceup(vm, 1, &data, 0))) {
+ sq_throwerror(vm, _SC("'set_centered' called without instance"));
+ return SQ_ERROR;
+ }
+ Scripting::Text* _this = reinterpret_cast<Scripting::Text*> (data);
+ SQBool arg0;
+ if(SQ_FAILED(sq_getbool(vm, 2, &arg0))) {
+ sq_throwerror(vm, _SC("Argument 1 not a bool"));
+ return SQ_ERROR;
+ }
+
+ try {
+ _this->set_centered(arg0 == SQTrue);
+
+ return 0;
+
+ } catch(std::exception& e) {
+ sq_throwerror(vm, e.what());
+ return SQ_ERROR;
+ } catch(...) {
+ sq_throwerror(vm, _SC("Unexpected exception while executing function 'set_centered'"));
+ return SQ_ERROR;
+ }
+
+}
+
+static SQInteger Text_set_pos_wrapper(HSQUIRRELVM vm)
+{
+ SQUserPointer data;
+ if(SQ_FAILED(sq_getinstanceup(vm, 1, &data, 0))) {
+ sq_throwerror(vm, _SC("'set_pos' called without instance"));
+ return SQ_ERROR;
+ }
+ Scripting::Text* _this = reinterpret_cast<Scripting::Text*> (data);
+ SQFloat arg0;
+ if(SQ_FAILED(sq_getfloat(vm, 2, &arg0))) {
+ sq_throwerror(vm, _SC("Argument 1 not a float"));
+ return SQ_ERROR;
+ }
+ SQFloat arg1;
+ if(SQ_FAILED(sq_getfloat(vm, 3, &arg1))) {
+ sq_throwerror(vm, _SC("Argument 2 not a float"));
+ return SQ_ERROR;
+ }
+
+ try {
+ _this->set_pos(static_cast<float> (arg0), static_cast<float> (arg1));
+
+ return 0;
+
+ } catch(std::exception& e) {
+ sq_throwerror(vm, e.what());
+ return SQ_ERROR;
+ } catch(...) {
+ sq_throwerror(vm, _SC("Unexpected exception while executing function 'set_pos'"));
+ return SQ_ERROR;
+ }
+
+}
+
+static SQInteger Text_get_pos_x_wrapper(HSQUIRRELVM vm)
+{
+ SQUserPointer data;
+ if(SQ_FAILED(sq_getinstanceup(vm, 1, &data, 0))) {
+ sq_throwerror(vm, _SC("'get_pos_x' called without instance"));
+ return SQ_ERROR;
+ }
+ Scripting::Text* _this = reinterpret_cast<Scripting::Text*> (data);
+
+ try {
+ float return_value = _this->get_pos_x();
+
+ sq_pushfloat(vm, return_value);
+ return 1;
+
+ } catch(std::exception& e) {
+ sq_throwerror(vm, e.what());
+ return SQ_ERROR;
+ } catch(...) {
+ sq_throwerror(vm, _SC("Unexpected exception while executing function 'get_pos_x'"));
+ return SQ_ERROR;
+ }
+
+}
+
+static SQInteger Text_get_pos_y_wrapper(HSQUIRRELVM vm)
+{
+ SQUserPointer data;
+ if(SQ_FAILED(sq_getinstanceup(vm, 1, &data, 0))) {
+ sq_throwerror(vm, _SC("'get_pos_y' called without instance"));
+ return SQ_ERROR;
+ }
+ Scripting::Text* _this = reinterpret_cast<Scripting::Text*> (data);
+
+ try {
+ float return_value = _this->get_pos_y();
+
+ sq_pushfloat(vm, return_value);
+ return 1;
+
+ } catch(std::exception& e) {
+ sq_throwerror(vm, e.what());
+ return SQ_ERROR;
+ } catch(...) {
+ sq_throwerror(vm, _SC("Unexpected exception while executing function 'get_pos_y'"));
+ return SQ_ERROR;
+ }
+
+}
+
+static SQInteger Text_set_anchor_point_wrapper(HSQUIRRELVM vm)
+{
+ SQUserPointer data;
+ if(SQ_FAILED(sq_getinstanceup(vm, 1, &data, 0))) {
+ sq_throwerror(vm, _SC("'set_anchor_point' called without instance"));
+ return SQ_ERROR;
+ }
+ Scripting::Text* _this = reinterpret_cast<Scripting::Text*> (data);
+ SQInteger arg0;
+ if(SQ_FAILED(sq_getinteger(vm, 2, &arg0))) {
+ sq_throwerror(vm, _SC("Argument 1 not an integer"));
+ return SQ_ERROR;
+ }
+
+ try {
+ _this->set_anchor_point(static_cast<int> (arg0));
+
+ return 0;
+
+ } catch(std::exception& e) {
+ sq_throwerror(vm, e.what());
+ return SQ_ERROR;
+ } catch(...) {
+ sq_throwerror(vm, _SC("Unexpected exception while executing function 'set_anchor_point'"));
+ return SQ_ERROR;
+ }
+
+}
+
+static SQInteger Text_get_anchor_point_wrapper(HSQUIRRELVM vm)
+{
+ SQUserPointer data;
+ if(SQ_FAILED(sq_getinstanceup(vm, 1, &data, 0))) {
+ sq_throwerror(vm, _SC("'get_anchor_point' called without instance"));
+ return SQ_ERROR;
+ }
+ Scripting::Text* _this = reinterpret_cast<Scripting::Text*> (data);
+
+ try {
+ int return_value = _this->get_anchor_point();
+
+ sq_pushinteger(vm, return_value);
+ return 1;
+
+ } catch(std::exception& e) {
+ sq_throwerror(vm, e.what());
+ return SQ_ERROR;
+ } catch(...) {
+ sq_throwerror(vm, _SC("Unexpected exception while executing function 'get_anchor_point'"));
+ return SQ_ERROR;
+ }
+
+}
+
+static SQInteger Player_release_hook(SQUserPointer ptr, SQInteger )
+{
+ Scripting::Player* _this = reinterpret_cast<Scripting::Player*> (ptr);
+ delete _this;
+ return 0;
+}
+
+static SQInteger Player_add_bonus_wrapper(HSQUIRRELVM vm)
+{
+ SQUserPointer data;
+ if(SQ_FAILED(sq_getinstanceup(vm, 1, &data, 0))) {
+ sq_throwerror(vm, _SC("'add_bonus' called without instance"));
+ return SQ_ERROR;
+ }
+ Scripting::Player* _this = reinterpret_cast<Scripting::Player*> (data);
+ const SQChar* arg0;
+ if(SQ_FAILED(sq_getstring(vm, 2, &arg0))) {
+ sq_throwerror(vm, _SC("Argument 1 not a string"));
+ return SQ_ERROR;
+ }
+
+ try {
+ bool return_value = _this->add_bonus(arg0);
+
+ sq_pushbool(vm, return_value);
+ return 1;
+
+ } catch(std::exception& e) {
+ sq_throwerror(vm, e.what());
+ return SQ_ERROR;
+ } catch(...) {
+ sq_throwerror(vm, _SC("Unexpected exception while executing function 'add_bonus'"));
+ return SQ_ERROR;
+ }
+
+}
+
+static SQInteger Player_add_coins_wrapper(HSQUIRRELVM vm)
+{
+ SQUserPointer data;
+ if(SQ_FAILED(sq_getinstanceup(vm, 1, &data, 0))) {
+ sq_throwerror(vm, _SC("'add_coins' called without instance"));
+ return SQ_ERROR;
+ }
+ Scripting::Player* _this = reinterpret_cast<Scripting::Player*> (data);
+ SQInteger arg0;
+ if(SQ_FAILED(sq_getinteger(vm, 2, &arg0))) {
+ sq_throwerror(vm, _SC("Argument 1 not an integer"));
+ return SQ_ERROR;
+ }
+
+ try {
+ _this->add_coins(static_cast<int> (arg0));
+
+ return 0;
+
+ } catch(std::exception& e) {
+ sq_throwerror(vm, e.what());
+ return SQ_ERROR;
+ } catch(...) {
+ sq_throwerror(vm, _SC("Unexpected exception while executing function 'add_coins'"));
+ return SQ_ERROR;
+ }
+
+}
+
+static SQInteger Player_make_invincible_wrapper(HSQUIRRELVM vm)
+{
+ SQUserPointer data;
+ if(SQ_FAILED(sq_getinstanceup(vm, 1, &data, 0))) {
+ sq_throwerror(vm, _SC("'make_invincible' called without instance"));
+ return SQ_ERROR;
+ }
+ Scripting::Player* _this = reinterpret_cast<Scripting::Player*> (data);
+
+ try {
+ _this->make_invincible();
+
+ return 0;
+
+ } catch(std::exception& e) {
+ sq_throwerror(vm, e.what());
+ return SQ_ERROR;
+ } catch(...) {
+ sq_throwerror(vm, _SC("Unexpected exception while executing function 'make_invincible'"));
+ return SQ_ERROR;
+ }
+
+}
+
+static SQInteger Player_deactivate_wrapper(HSQUIRRELVM vm)
+{
+ SQUserPointer data;
+ if(SQ_FAILED(sq_getinstanceup(vm, 1, &data, 0))) {
+ sq_throwerror(vm, _SC("'deactivate' called without instance"));
+ return SQ_ERROR;
+ }
+ Scripting::Player* _this = reinterpret_cast<Scripting::Player*> (data);
+
+ try {
+ _this->deactivate();
+
+ return 0;
+
+ } catch(std::exception& e) {
+ sq_throwerror(vm, e.what());
+ return SQ_ERROR;
+ } catch(...) {
+ sq_throwerror(vm, _SC("Unexpected exception while executing function 'deactivate'"));
+ return SQ_ERROR;
+ }
+
+}
+
+static SQInteger Player_activate_wrapper(HSQUIRRELVM vm)
+{
+ SQUserPointer data;
+ if(SQ_FAILED(sq_getinstanceup(vm, 1, &data, 0))) {
+ sq_throwerror(vm, _SC("'activate' called without instance"));
+ return SQ_ERROR;
+ }
+ Scripting::Player* _this = reinterpret_cast<Scripting::Player*> (data);
+
+ try {
+ _this->activate();
+
+ return 0;
+
+ } catch(std::exception& e) {
+ sq_throwerror(vm, e.what());
+ return SQ_ERROR;
+ } catch(...) {
+ sq_throwerror(vm, _SC("Unexpected exception while executing function 'activate'"));
+ return SQ_ERROR;
+ }
+
+}
+
+static SQInteger Player_walk_wrapper(HSQUIRRELVM vm)
+{
+ SQUserPointer data;
+ if(SQ_FAILED(sq_getinstanceup(vm, 1, &data, 0))) {
+ sq_throwerror(vm, _SC("'walk' called without instance"));
+ return SQ_ERROR;
+ }
+ Scripting::Player* _this = reinterpret_cast<Scripting::Player*> (data);
+ SQFloat arg0;
+ if(SQ_FAILED(sq_getfloat(vm, 2, &arg0))) {
+ sq_throwerror(vm, _SC("Argument 1 not a float"));
+ return SQ_ERROR;
+ }
+
+ try {
+ _this->walk(static_cast<float> (arg0));
+
+ return 0;
+
+ } catch(std::exception& e) {
+ sq_throwerror(vm, e.what());
+ return SQ_ERROR;
+ } catch(...) {
+ sq_throwerror(vm, _SC("Unexpected exception while executing function 'walk'"));
+ return SQ_ERROR;
+ }
+
+}
+
+static SQInteger Player_set_visible_wrapper(HSQUIRRELVM vm)
+{
+ SQUserPointer data;
+ if(SQ_FAILED(sq_getinstanceup(vm, 1, &data, 0))) {
+ sq_throwerror(vm, _SC("'set_visible' called without instance"));
+ return SQ_ERROR;
+ }
+ Scripting::Player* _this = reinterpret_cast<Scripting::Player*> (data);
+ SQBool arg0;
+ if(SQ_FAILED(sq_getbool(vm, 2, &arg0))) {
+ sq_throwerror(vm, _SC("Argument 1 not a bool"));
+ return SQ_ERROR;
+ }
+
+ try {
+ _this->set_visible(arg0 == SQTrue);
+
+ return 0;
+
+ } catch(std::exception& e) {
+ sq_throwerror(vm, e.what());
+ return SQ_ERROR;
+ } catch(...) {
+ sq_throwerror(vm, _SC("Unexpected exception while executing function 'set_visible'"));
+ return SQ_ERROR;
+ }
+
+}
+
+static SQInteger Player_get_visible_wrapper(HSQUIRRELVM vm)
+{
+ SQUserPointer data;
+ if(SQ_FAILED(sq_getinstanceup(vm, 1, &data, 0))) {
+ sq_throwerror(vm, _SC("'get_visible' called without instance"));
+ return SQ_ERROR;
+ }
+ Scripting::Player* _this = reinterpret_cast<Scripting::Player*> (data);
+
+ try {
+ bool return_value = _this->get_visible();
+
+ sq_pushbool(vm, return_value);
+ return 1;
+
+ } catch(std::exception& e) {
+ sq_throwerror(vm, e.what());
+ return SQ_ERROR;
+ } catch(...) {
+ sq_throwerror(vm, _SC("Unexpected exception while executing function 'get_visible'"));
+ return SQ_ERROR;
+ }
+
+}
+
+static SQInteger Player_kill_wrapper(HSQUIRRELVM vm)
+{
+ SQUserPointer data;
+ if(SQ_FAILED(sq_getinstanceup(vm, 1, &data, 0))) {
+ sq_throwerror(vm, _SC("'kill' called without instance"));
+ return SQ_ERROR;
+ }
+ Scripting::Player* _this = reinterpret_cast<Scripting::Player*> (data);
+ SQBool arg0;
+ if(SQ_FAILED(sq_getbool(vm, 2, &arg0))) {
+ sq_throwerror(vm, _SC("Argument 1 not a bool"));
+ return SQ_ERROR;
+ }
+
+ try {
+ _this->kill(arg0 == SQTrue);
+
+ return 0;
+
+ } catch(std::exception& e) {
+ sq_throwerror(vm, e.what());
+ return SQ_ERROR;
+ } catch(...) {
+ sq_throwerror(vm, _SC("Unexpected exception while executing function 'kill'"));
+ return SQ_ERROR;
+ }
+
+}
+
+static SQInteger Player_set_ghost_mode_wrapper(HSQUIRRELVM vm)
+{
+ SQUserPointer data;
+ if(SQ_FAILED(sq_getinstanceup(vm, 1, &data, 0))) {
+ sq_throwerror(vm, _SC("'set_ghost_mode' called without instance"));
+ return SQ_ERROR;
+ }
+ Scripting::Player* _this = reinterpret_cast<Scripting::Player*> (data);
+ SQBool arg0;
+ if(SQ_FAILED(sq_getbool(vm, 2, &arg0))) {
+ sq_throwerror(vm, _SC("Argument 1 not a bool"));
+ return SQ_ERROR;
+ }
+
+ try {
+ _this->set_ghost_mode(arg0 == SQTrue);
+
+ return 0;
+
+ } catch(std::exception& e) {
+ sq_throwerror(vm, e.what());
+ return SQ_ERROR;
+ } catch(...) {
+ sq_throwerror(vm, _SC("Unexpected exception while executing function 'set_ghost_mode'"));
+ return SQ_ERROR;
+ }
+
+}
+
+static SQInteger Player_get_ghost_mode_wrapper(HSQUIRRELVM vm)
+{
+ SQUserPointer data;
+ if(SQ_FAILED(sq_getinstanceup(vm, 1, &data, 0))) {
+ sq_throwerror(vm, _SC("'get_ghost_mode' called without instance"));
+ return SQ_ERROR;
+ }
+ Scripting::Player* _this = reinterpret_cast<Scripting::Player*> (data);
+
+ try {
+ bool return_value = _this->get_ghost_mode();
+
+ sq_pushbool(vm, return_value);
+ return 1;
+
+ } catch(std::exception& e) {
+ sq_throwerror(vm, e.what());
+ return SQ_ERROR;
+ } catch(...) {
+ sq_throwerror(vm, _SC("Unexpected exception while executing function 'get_ghost_mode'"));
+ return SQ_ERROR;
+ }
+
+}
+
+static SQInteger Player_do_cheer_wrapper(HSQUIRRELVM vm)
+{
+ SQUserPointer data;
+ if(SQ_FAILED(sq_getinstanceup(vm, 1, &data, 0))) {
+ sq_throwerror(vm, _SC("'do_cheer' called without instance"));
+ return SQ_ERROR;
+ }
+ Scripting::Player* _this = reinterpret_cast<Scripting::Player*> (data);
+
+ try {
+ _this->do_cheer();
+
+ return 0;
+
+ } catch(std::exception& e) {
+ sq_throwerror(vm, e.what());
+ return SQ_ERROR;
+ } catch(...) {
+ sq_throwerror(vm, _SC("Unexpected exception while executing function 'do_cheer'"));
+ return SQ_ERROR;
+ }
+
+}
+
+static SQInteger Player_do_duck_wrapper(HSQUIRRELVM vm)
+{
+ SQUserPointer data;
+ if(SQ_FAILED(sq_getinstanceup(vm, 1, &data, 0))) {
+ sq_throwerror(vm, _SC("'do_duck' called without instance"));
+ return SQ_ERROR;
+ }
+ Scripting::Player* _this = reinterpret_cast<Scripting::Player*> (data);
+
+ try {
+ _this->do_duck();
+
+ return 0;
+
+ } catch(std::exception& e) {
+ sq_throwerror(vm, e.what());
+ return SQ_ERROR;
+ } catch(...) {
+ sq_throwerror(vm, _SC("Unexpected exception while executing function 'do_duck'"));
+ return SQ_ERROR;
+ }
+
+}
+
+static SQInteger Player_do_standup_wrapper(HSQUIRRELVM vm)
+{
+ SQUserPointer data;
+ if(SQ_FAILED(sq_getinstanceup(vm, 1, &data, 0))) {
+ sq_throwerror(vm, _SC("'do_standup' called without instance"));
+ return SQ_ERROR;
+ }
+ Scripting::Player* _this = reinterpret_cast<Scripting::Player*> (data);
+
+ try {
+ _this->do_standup();
+
+ return 0;
+
+ } catch(std::exception& e) {
+ sq_throwerror(vm, e.what());
+ return SQ_ERROR;
+ } catch(...) {
+ sq_throwerror(vm, _SC("Unexpected exception while executing function 'do_standup'"));
+ return SQ_ERROR;
+ }
+
+}
+
+static SQInteger Player_do_backflip_wrapper(HSQUIRRELVM vm)
+{
+ SQUserPointer data;
+ if(SQ_FAILED(sq_getinstanceup(vm, 1, &data, 0))) {
+ sq_throwerror(vm, _SC("'do_backflip' called without instance"));
+ return SQ_ERROR;
+ }
+ Scripting::Player* _this = reinterpret_cast<Scripting::Player*> (data);
+
+ try {
+ _this->do_backflip();
+
+ return 0;
+
+ } catch(std::exception& e) {
+ sq_throwerror(vm, e.what());
+ return SQ_ERROR;
+ } catch(...) {
+ sq_throwerror(vm, _SC("Unexpected exception while executing function 'do_backflip'"));
+ return SQ_ERROR;
+ }
+
+}
+
+static SQInteger Player_do_jump_wrapper(HSQUIRRELVM vm)
+{
+ SQUserPointer data;
+ if(SQ_FAILED(sq_getinstanceup(vm, 1, &data, 0))) {
+ sq_throwerror(vm, _SC("'do_jump' called without instance"));
+ return SQ_ERROR;
+ }
+ Scripting::Player* _this = reinterpret_cast<Scripting::Player*> (data);
+ SQFloat arg0;
+ if(SQ_FAILED(sq_getfloat(vm, 2, &arg0))) {
+ sq_throwerror(vm, _SC("Argument 1 not a float"));
+ return SQ_ERROR;
+ }
+
+ try {
+ _this->do_jump(static_cast<float> (arg0));
+
+ return 0;
+
+ } catch(std::exception& e) {
+ sq_throwerror(vm, e.what());
+ return SQ_ERROR;
+ } catch(...) {
+ sq_throwerror(vm, _SC("Unexpected exception while executing function 'do_jump'"));
+ return SQ_ERROR;
+ }
+
+}
+
+static SQInteger Player_trigger_sequence_wrapper(HSQUIRRELVM vm)
+{
+ SQUserPointer data;
+ if(SQ_FAILED(sq_getinstanceup(vm, 1, &data, 0))) {
+ sq_throwerror(vm, _SC("'trigger_sequence' called without instance"));
+ return SQ_ERROR;
+ }
+ Scripting::Player* _this = reinterpret_cast<Scripting::Player*> (data);
+ const SQChar* arg0;
+ if(SQ_FAILED(sq_getstring(vm, 2, &arg0))) {
+ sq_throwerror(vm, _SC("Argument 1 not a string"));
+ return SQ_ERROR;
+ }
+
+ try {
+ _this->trigger_sequence(arg0);
+
+ return 0;
+
+ } catch(std::exception& e) {
+ sq_throwerror(vm, e.what());
+ return SQ_ERROR;
+ } catch(...) {
+ sq_throwerror(vm, _SC("Unexpected exception while executing function 'trigger_sequence'"));
+ return SQ_ERROR;
+ }
+
+}
+
+static SQInteger FloatingImage_release_hook(SQUserPointer ptr, SQInteger )
+{
+ Scripting::FloatingImage* _this = reinterpret_cast<Scripting::FloatingImage*> (ptr);
+ delete _this;
+ return 0;
+}
+
+static SQInteger FloatingImage_constructor_wrapper(HSQUIRRELVM vm)
+{
+ const SQChar* arg0;
+ if(SQ_FAILED(sq_getstring(vm, 2, &arg0))) {
+ sq_throwerror(vm, _SC("Argument 1 not a string"));
+ return SQ_ERROR;
+ }
+
+ try {
+ Scripting::FloatingImage* _this = new Scripting::FloatingImage(arg0);
+ if(SQ_FAILED(sq_setinstanceup(vm, 1, _this))) {
+ sq_throwerror(vm, _SC("Couldn't setup instance of 'FloatingImage' class"));
+ return SQ_ERROR;
+ }
+ sq_setreleasehook(vm, 1, FloatingImage_release_hook);
+
+ return 0;
+
+ } catch(std::exception& e) {
+ sq_throwerror(vm, e.what());
+ return SQ_ERROR;
+ } catch(...) {
+ sq_throwerror(vm, _SC("Unexpected exception while executing function 'constructor'"));
+ return SQ_ERROR;
+ }
+
+}
+
+static SQInteger FloatingImage_set_layer_wrapper(HSQUIRRELVM vm)
+{
+ SQUserPointer data;
+ if(SQ_FAILED(sq_getinstanceup(vm, 1, &data, 0))) {
+ sq_throwerror(vm, _SC("'set_layer' called without instance"));
+ return SQ_ERROR;
+ }
+ Scripting::FloatingImage* _this = reinterpret_cast<Scripting::FloatingImage*> (data);
+ SQInteger arg0;
+ if(SQ_FAILED(sq_getinteger(vm, 2, &arg0))) {
+ sq_throwerror(vm, _SC("Argument 1 not an integer"));
+ return SQ_ERROR;
+ }
+
+ try {
+ _this->set_layer(static_cast<int> (arg0));
+
+ return 0;
+
+ } catch(std::exception& e) {
+ sq_throwerror(vm, e.what());
+ return SQ_ERROR;
+ } catch(...) {
+ sq_throwerror(vm, _SC("Unexpected exception while executing function 'set_layer'"));
+ return SQ_ERROR;
+ }
+
+}
+
+static SQInteger FloatingImage_get_layer_wrapper(HSQUIRRELVM vm)
+{
+ SQUserPointer data;
+ if(SQ_FAILED(sq_getinstanceup(vm, 1, &data, 0))) {
+ sq_throwerror(vm, _SC("'get_layer' called without instance"));
+ return SQ_ERROR;
+ }
+ Scripting::FloatingImage* _this = reinterpret_cast<Scripting::FloatingImage*> (data);
+
+ try {
+ int return_value = _this->get_layer();
+
+ sq_pushinteger(vm, return_value);
+ return 1;
+
+ } catch(std::exception& e) {
+ sq_throwerror(vm, e.what());
+ return SQ_ERROR;
+ } catch(...) {
+ sq_throwerror(vm, _SC("Unexpected exception while executing function 'get_layer'"));
+ return SQ_ERROR;
+ }
+
+}
+
+static SQInteger FloatingImage_set_pos_wrapper(HSQUIRRELVM vm)
+{
+ SQUserPointer data;
+ if(SQ_FAILED(sq_getinstanceup(vm, 1, &data, 0))) {
+ sq_throwerror(vm, _SC("'set_pos' called without instance"));
+ return SQ_ERROR;
+ }
+ Scripting::FloatingImage* _this = reinterpret_cast<Scripting::FloatingImage*> (data);
+ SQFloat arg0;
+ if(SQ_FAILED(sq_getfloat(vm, 2, &arg0))) {
+ sq_throwerror(vm, _SC("Argument 1 not a float"));
+ return SQ_ERROR;
+ }
+ SQFloat arg1;
+ if(SQ_FAILED(sq_getfloat(vm, 3, &arg1))) {
+ sq_throwerror(vm, _SC("Argument 2 not a float"));
+ return SQ_ERROR;
+ }
+
+ try {
+ _this->set_pos(static_cast<float> (arg0), static_cast<float> (arg1));
+
+ return 0;
+
+ } catch(std::exception& e) {
+ sq_throwerror(vm, e.what());
+ return SQ_ERROR;
+ } catch(...) {
+ sq_throwerror(vm, _SC("Unexpected exception while executing function 'set_pos'"));
+ return SQ_ERROR;
+ }
+
+}
+
+static SQInteger FloatingImage_get_pos_x_wrapper(HSQUIRRELVM vm)
+{
+ SQUserPointer data;
+ if(SQ_FAILED(sq_getinstanceup(vm, 1, &data, 0))) {
+ sq_throwerror(vm, _SC("'get_pos_x' called without instance"));
+ return SQ_ERROR;
+ }
+ Scripting::FloatingImage* _this = reinterpret_cast<Scripting::FloatingImage*> (data);
+
+ try {
+ float return_value = _this->get_pos_x();
+
+ sq_pushfloat(vm, return_value);
+ return 1;
+
+ } catch(std::exception& e) {
+ sq_throwerror(vm, e.what());
+ return SQ_ERROR;
+ } catch(...) {
+ sq_throwerror(vm, _SC("Unexpected exception while executing function 'get_pos_x'"));
+ return SQ_ERROR;
+ }
+
+}
+
+static SQInteger FloatingImage_get_pos_y_wrapper(HSQUIRRELVM vm)
+{
+ SQUserPointer data;
+ if(SQ_FAILED(sq_getinstanceup(vm, 1, &data, 0))) {
+ sq_throwerror(vm, _SC("'get_pos_y' called without instance"));
+ return SQ_ERROR;
+ }
+ Scripting::FloatingImage* _this = reinterpret_cast<Scripting::FloatingImage*> (data);
+
+ try {
+ float return_value = _this->get_pos_y();
+
+ sq_pushfloat(vm, return_value);
+ return 1;
+
+ } catch(std::exception& e) {
+ sq_throwerror(vm, e.what());
+ return SQ_ERROR;
+ } catch(...) {
+ sq_throwerror(vm, _SC("Unexpected exception while executing function 'get_pos_y'"));
+ return SQ_ERROR;
+ }
+
+}
+
+static SQInteger FloatingImage_set_anchor_point_wrapper(HSQUIRRELVM vm)
+{
+ SQUserPointer data;
+ if(SQ_FAILED(sq_getinstanceup(vm, 1, &data, 0))) {
+ sq_throwerror(vm, _SC("'set_anchor_point' called without instance"));
+ return SQ_ERROR;
+ }
+ Scripting::FloatingImage* _this = reinterpret_cast<Scripting::FloatingImage*> (data);
+ SQInteger arg0;
+ if(SQ_FAILED(sq_getinteger(vm, 2, &arg0))) {
+ sq_throwerror(vm, _SC("Argument 1 not an integer"));
+ return SQ_ERROR;
+ }
+
+ try {
+ _this->set_anchor_point(static_cast<int> (arg0));
+
+ return 0;
+
+ } catch(std::exception& e) {
+ sq_throwerror(vm, e.what());
+ return SQ_ERROR;
+ } catch(...) {
+ sq_throwerror(vm, _SC("Unexpected exception while executing function 'set_anchor_point'"));
+ return SQ_ERROR;
+ }
+
+}
+
+static SQInteger FloatingImage_get_anchor_point_wrapper(HSQUIRRELVM vm)
+{
+ SQUserPointer data;
+ if(SQ_FAILED(sq_getinstanceup(vm, 1, &data, 0))) {
+ sq_throwerror(vm, _SC("'get_anchor_point' called without instance"));
+ return SQ_ERROR;
+ }
+ Scripting::FloatingImage* _this = reinterpret_cast<Scripting::FloatingImage*> (data);
+
+ try {
+ int return_value = _this->get_anchor_point();
+
+ sq_pushinteger(vm, return_value);
+ return 1;
+
+ } catch(std::exception& e) {
+ sq_throwerror(vm, e.what());
+ return SQ_ERROR;
+ } catch(...) {
+ sq_throwerror(vm, _SC("Unexpected exception while executing function 'get_anchor_point'"));
+ return SQ_ERROR;
+ }
+
+}
+
+static SQInteger FloatingImage_set_visible_wrapper(HSQUIRRELVM vm)
+{
+ SQUserPointer data;
+ if(SQ_FAILED(sq_getinstanceup(vm, 1, &data, 0))) {
+ sq_throwerror(vm, _SC("'set_visible' called without instance"));
+ return SQ_ERROR;
+ }
+ Scripting::FloatingImage* _this = reinterpret_cast<Scripting::FloatingImage*> (data);
+ SQBool arg0;
+ if(SQ_FAILED(sq_getbool(vm, 2, &arg0))) {
+ sq_throwerror(vm, _SC("Argument 1 not a bool"));
+ return SQ_ERROR;
+ }
+
+ try {
+ _this->set_visible(arg0 == SQTrue);
+
+ return 0;
+
+ } catch(std::exception& e) {
+ sq_throwerror(vm, e.what());
+ return SQ_ERROR;
+ } catch(...) {
+ sq_throwerror(vm, _SC("Unexpected exception while executing function 'set_visible'"));
+ return SQ_ERROR;
+ }
+
+}
+
+static SQInteger FloatingImage_get_visible_wrapper(HSQUIRRELVM vm)
+{
+ SQUserPointer data;
+ if(SQ_FAILED(sq_getinstanceup(vm, 1, &data, 0))) {
+ sq_throwerror(vm, _SC("'get_visible' called without instance"));
+ return SQ_ERROR;
+ }
+ Scripting::FloatingImage* _this = reinterpret_cast<Scripting::FloatingImage*> (data);
+
+ try {
+ bool return_value = _this->get_visible();
+
+ sq_pushbool(vm, return_value);
+ return 1;
+
+ } catch(std::exception& e) {
+ sq_throwerror(vm, e.what());
+ return SQ_ERROR;
+ } catch(...) {
+ sq_throwerror(vm, _SC("Unexpected exception while executing function 'get_visible'"));
+ return SQ_ERROR;
+ }
+
+}
+
+static SQInteger FloatingImage_set_action_wrapper(HSQUIRRELVM vm)
+{
+ SQUserPointer data;
+ if(SQ_FAILED(sq_getinstanceup(vm, 1, &data, 0))) {
+ sq_throwerror(vm, _SC("'set_action' called without instance"));
+ return SQ_ERROR;
+ }
+ Scripting::FloatingImage* _this = reinterpret_cast<Scripting::FloatingImage*> (data);
+ const SQChar* arg0;
+ if(SQ_FAILED(sq_getstring(vm, 2, &arg0))) {
+ sq_throwerror(vm, _SC("Argument 1 not a string"));
+ return SQ_ERROR;
+ }
+
+ try {
+ _this->set_action(arg0);
+
+ return 0;
+
+ } catch(std::exception& e) {
+ sq_throwerror(vm, e.what());
+ return SQ_ERROR;
+ } catch(...) {
+ sq_throwerror(vm, _SC("Unexpected exception while executing function 'set_action'"));
+ return SQ_ERROR;
+ }
+
+}
+
+static SQInteger FloatingImage_get_action_wrapper(HSQUIRRELVM vm)
+{
+ SQUserPointer data;
+ if(SQ_FAILED(sq_getinstanceup(vm, 1, &data, 0))) {
+ sq_throwerror(vm, _SC("'get_action' called without instance"));
+ return SQ_ERROR;
+ }
+ Scripting::FloatingImage* _this = reinterpret_cast<Scripting::FloatingImage*> (data);
+
+ try {
+ std::string return_value = _this->get_action();
+
+ sq_pushstring(vm, return_value.c_str(), return_value.size());
+ return 1;
+
+ } catch(std::exception& e) {
+ sq_throwerror(vm, e.what());
+ return SQ_ERROR;
+ } catch(...) {
+ sq_throwerror(vm, _SC("Unexpected exception while executing function 'get_action'"));
+ return SQ_ERROR;
+ }
+
+}
+
+static SQInteger FloatingImage_fade_in_wrapper(HSQUIRRELVM vm)
+{
+ SQUserPointer data;
+ if(SQ_FAILED(sq_getinstanceup(vm, 1, &data, 0))) {
+ sq_throwerror(vm, _SC("'fade_in' called without instance"));
+ return SQ_ERROR;
+ }
+ Scripting::FloatingImage* _this = reinterpret_cast<Scripting::FloatingImage*> (data);
+ SQFloat arg0;
+ if(SQ_FAILED(sq_getfloat(vm, 2, &arg0))) {
+ sq_throwerror(vm, _SC("Argument 1 not a float"));
+ return SQ_ERROR;
+ }
+
+ try {
+ _this->fade_in(static_cast<float> (arg0));
+
+ return 0;
+
+ } catch(std::exception& e) {
+ sq_throwerror(vm, e.what());
+ return SQ_ERROR;
+ } catch(...) {
+ sq_throwerror(vm, _SC("Unexpected exception while executing function 'fade_in'"));
+ return SQ_ERROR;
+ }
+
+}
+
+static SQInteger FloatingImage_fade_out_wrapper(HSQUIRRELVM vm)
+{
+ SQUserPointer data;
+ if(SQ_FAILED(sq_getinstanceup(vm, 1, &data, 0))) {
+ sq_throwerror(vm, _SC("'fade_out' called without instance"));
+ return SQ_ERROR;
+ }
+ Scripting::FloatingImage* _this = reinterpret_cast<Scripting::FloatingImage*> (data);
+ SQFloat arg0;
+ if(SQ_FAILED(sq_getfloat(vm, 2, &arg0))) {
+ sq_throwerror(vm, _SC("Argument 1 not a float"));
+ return SQ_ERROR;
+ }
+
+ try {
+ _this->fade_out(static_cast<float> (arg0));
+
+ return 0;
+
+ } catch(std::exception& e) {
+ sq_throwerror(vm, e.what());
+ return SQ_ERROR;
+ } catch(...) {
+ sq_throwerror(vm, _SC("Unexpected exception while executing function 'fade_out'"));
+ return SQ_ERROR;
+ }
+
+}
+
+static SQInteger Platform_release_hook(SQUserPointer ptr, SQInteger )
+{
+ Scripting::Platform* _this = reinterpret_cast<Scripting::Platform*> (ptr);
+ delete _this;
+ return 0;
+}
+
+static SQInteger Platform_goto_node_wrapper(HSQUIRRELVM vm)
+{
+ SQUserPointer data;
+ if(SQ_FAILED(sq_getinstanceup(vm, 1, &data, 0))) {
+ sq_throwerror(vm, _SC("'goto_node' called without instance"));
+ return SQ_ERROR;
+ }
+ Scripting::Platform* _this = reinterpret_cast<Scripting::Platform*> (data);
+ SQInteger arg0;
+ if(SQ_FAILED(sq_getinteger(vm, 2, &arg0))) {
+ sq_throwerror(vm, _SC("Argument 1 not an integer"));
+ return SQ_ERROR;
+ }
+
+ try {
+ _this->goto_node(static_cast<int> (arg0));
+
+ return 0;
+
+ } catch(std::exception& e) {
+ sq_throwerror(vm, e.what());
+ return SQ_ERROR;
+ } catch(...) {
+ sq_throwerror(vm, _SC("Unexpected exception while executing function 'goto_node'"));
+ return SQ_ERROR;
+ }
+
+}
+
+static SQInteger Platform_start_moving_wrapper(HSQUIRRELVM vm)
+{
+ SQUserPointer data;
+ if(SQ_FAILED(sq_getinstanceup(vm, 1, &data, 0))) {
+ sq_throwerror(vm, _SC("'start_moving' called without instance"));
+ return SQ_ERROR;
+ }
+ Scripting::Platform* _this = reinterpret_cast<Scripting::Platform*> (data);
+
+ try {
+ _this->start_moving();
+
+ return 0;
+
+ } catch(std::exception& e) {
+ sq_throwerror(vm, e.what());
+ return SQ_ERROR;
+ } catch(...) {
+ sq_throwerror(vm, _SC("Unexpected exception while executing function 'start_moving'"));
+ return SQ_ERROR;
+ }
+
+}
+
+static SQInteger Platform_stop_moving_wrapper(HSQUIRRELVM vm)
+{
+ SQUserPointer data;
+ if(SQ_FAILED(sq_getinstanceup(vm, 1, &data, 0))) {
+ sq_throwerror(vm, _SC("'stop_moving' called without instance"));
+ return SQ_ERROR;
+ }
+ Scripting::Platform* _this = reinterpret_cast<Scripting::Platform*> (data);
+
+ try {
+ _this->stop_moving();
+
+ return 0;
+
+ } catch(std::exception& e) {
+ sq_throwerror(vm, e.what());
+ return SQ_ERROR;
+ } catch(...) {
+ sq_throwerror(vm, _SC("Unexpected exception while executing function 'stop_moving'"));
+ return SQ_ERROR;
+ }
+
+}
+
+static SQInteger Candle_release_hook(SQUserPointer ptr, SQInteger )
+{
+ Scripting::Candle* _this = reinterpret_cast<Scripting::Candle*> (ptr);
+ delete _this;
+ return 0;
+}
+
+static SQInteger Candle_get_burning_wrapper(HSQUIRRELVM vm)
+{
+ SQUserPointer data;
+ if(SQ_FAILED(sq_getinstanceup(vm, 1, &data, 0))) {
+ sq_throwerror(vm, _SC("'get_burning' called without instance"));
+ return SQ_ERROR;
+ }
+ Scripting::Candle* _this = reinterpret_cast<Scripting::Candle*> (data);
+
+ try {
+ bool return_value = _this->get_burning();
+
+ sq_pushbool(vm, return_value);
+ return 1;
+
+ } catch(std::exception& e) {
+ sq_throwerror(vm, e.what());
+ return SQ_ERROR;
+ } catch(...) {
+ sq_throwerror(vm, _SC("Unexpected exception while executing function 'get_burning'"));
+ return SQ_ERROR;
+ }
+
+}
+
+static SQInteger Candle_set_burning_wrapper(HSQUIRRELVM vm)
+{
+ SQUserPointer data;
+ if(SQ_FAILED(sq_getinstanceup(vm, 1, &data, 0))) {
+ sq_throwerror(vm, _SC("'set_burning' called without instance"));
+ return SQ_ERROR;
+ }
+ Scripting::Candle* _this = reinterpret_cast<Scripting::Candle*> (data);
+ SQBool arg0;
+ if(SQ_FAILED(sq_getbool(vm, 2, &arg0))) {
+ sq_throwerror(vm, _SC("Argument 1 not a bool"));
+ return SQ_ERROR;
+ }
+
+ try {
+ _this->set_burning(arg0 == SQTrue);
+
+ return 0;
+
+ } catch(std::exception& e) {
+ sq_throwerror(vm, e.what());
+ return SQ_ERROR;
+ } catch(...) {
+ sq_throwerror(vm, _SC("Unexpected exception while executing function 'set_burning'"));
+ return SQ_ERROR;
+ }
+
+}
+
+static SQInteger Wind_release_hook(SQUserPointer ptr, SQInteger )
+{
+ Scripting::Wind* _this = reinterpret_cast<Scripting::Wind*> (ptr);
+ delete _this;
+ return 0;
+}
+
+static SQInteger Wind_start_wrapper(HSQUIRRELVM vm)
+{
+ SQUserPointer data;
+ if(SQ_FAILED(sq_getinstanceup(vm, 1, &data, 0))) {
+ sq_throwerror(vm, _SC("'start' called without instance"));
+ return SQ_ERROR;
+ }
+ Scripting::Wind* _this = reinterpret_cast<Scripting::Wind*> (data);
+
+ try {
+ _this->start();
+
+ return 0;
+
+ } catch(std::exception& e) {
+ sq_throwerror(vm, e.what());
+ return SQ_ERROR;
+ } catch(...) {
+ sq_throwerror(vm, _SC("Unexpected exception while executing function 'start'"));
+ return SQ_ERROR;
+ }
+
+}
+
+static SQInteger Wind_stop_wrapper(HSQUIRRELVM vm)
+{
+ SQUserPointer data;
+ if(SQ_FAILED(sq_getinstanceup(vm, 1, &data, 0))) {
+ sq_throwerror(vm, _SC("'stop' called without instance"));
+ return SQ_ERROR;
+ }
+ Scripting::Wind* _this = reinterpret_cast<Scripting::Wind*> (data);
+
+ try {
+ _this->stop();
+
+ return 0;
+
+ } catch(std::exception& e) {
+ sq_throwerror(vm, e.what());
+ return SQ_ERROR;
+ } catch(...) {
+ sq_throwerror(vm, _SC("Unexpected exception while executing function 'stop'"));
+ return SQ_ERROR;
+ }
+
+}
+
+static SQInteger AmbientSound_release_hook(SQUserPointer ptr, SQInteger )
+{
+ Scripting::AmbientSound* _this = reinterpret_cast<Scripting::AmbientSound*> (ptr);
+ delete _this;
+ return 0;
+}
+
+static SQInteger AmbientSound_set_pos_wrapper(HSQUIRRELVM vm)
+{
+ SQUserPointer data;
+ if(SQ_FAILED(sq_getinstanceup(vm, 1, &data, 0))) {
+ sq_throwerror(vm, _SC("'set_pos' called without instance"));
+ return SQ_ERROR;
+ }
+ Scripting::AmbientSound* _this = reinterpret_cast<Scripting::AmbientSound*> (data);
+ SQFloat arg0;
+ if(SQ_FAILED(sq_getfloat(vm, 2, &arg0))) {
+ sq_throwerror(vm, _SC("Argument 1 not a float"));
+ return SQ_ERROR;
+ }
+ SQFloat arg1;
+ if(SQ_FAILED(sq_getfloat(vm, 3, &arg1))) {
+ sq_throwerror(vm, _SC("Argument 2 not a float"));
+ return SQ_ERROR;
+ }
+
+ try {
+ _this->set_pos(static_cast<float> (arg0), static_cast<float> (arg1));
+
+ return 0;
+
+ } catch(std::exception& e) {
+ sq_throwerror(vm, e.what());
+ return SQ_ERROR;
+ } catch(...) {
+ sq_throwerror(vm, _SC("Unexpected exception while executing function 'set_pos'"));
+ return SQ_ERROR;
+ }
+
+}
+
+static SQInteger AmbientSound_get_pos_x_wrapper(HSQUIRRELVM vm)
+{
+ SQUserPointer data;
+ if(SQ_FAILED(sq_getinstanceup(vm, 1, &data, 0))) {
+ sq_throwerror(vm, _SC("'get_pos_x' called without instance"));
+ return SQ_ERROR;
+ }
+ Scripting::AmbientSound* _this = reinterpret_cast<Scripting::AmbientSound*> (data);
+
+ try {
+ float return_value = _this->get_pos_x();
+
+ sq_pushfloat(vm, return_value);
+ return 1;
+
+ } catch(std::exception& e) {
+ sq_throwerror(vm, e.what());
+ return SQ_ERROR;
+ } catch(...) {
+ sq_throwerror(vm, _SC("Unexpected exception while executing function 'get_pos_x'"));
+ return SQ_ERROR;
+ }
+
+}
+
+static SQInteger AmbientSound_get_pos_y_wrapper(HSQUIRRELVM vm)
+{
+ SQUserPointer data;
+ if(SQ_FAILED(sq_getinstanceup(vm, 1, &data, 0))) {
+ sq_throwerror(vm, _SC("'get_pos_y' called without instance"));
+ return SQ_ERROR;
+ }
+ Scripting::AmbientSound* _this = reinterpret_cast<Scripting::AmbientSound*> (data);
+
+ try {
+ float return_value = _this->get_pos_y();
+
+ sq_pushfloat(vm, return_value);
+ return 1;
+
+ } catch(std::exception& e) {
+ sq_throwerror(vm, e.what());
+ return SQ_ERROR;
+ } catch(...) {
+ sq_throwerror(vm, _SC("Unexpected exception while executing function 'get_pos_y'"));
+ return SQ_ERROR;
+ }
+
+}
+
+static SQInteger Thunderstorm_release_hook(SQUserPointer ptr, SQInteger )
+{
+ Scripting::Thunderstorm* _this = reinterpret_cast<Scripting::Thunderstorm*> (ptr);
+ delete _this;
+ return 0;
+}
+
+static SQInteger Thunderstorm_start_wrapper(HSQUIRRELVM vm)
+{
+ SQUserPointer data;
+ if(SQ_FAILED(sq_getinstanceup(vm, 1, &data, 0))) {
+ sq_throwerror(vm, _SC("'start' called without instance"));
+ return SQ_ERROR;
+ }
+ Scripting::Thunderstorm* _this = reinterpret_cast<Scripting::Thunderstorm*> (data);
+
+ try {
+ _this->start();
+
+ return 0;
+
+ } catch(std::exception& e) {
+ sq_throwerror(vm, e.what());
+ return SQ_ERROR;
+ } catch(...) {
+ sq_throwerror(vm, _SC("Unexpected exception while executing function 'start'"));
+ return SQ_ERROR;
+ }
+
+}
+
+static SQInteger Thunderstorm_stop_wrapper(HSQUIRRELVM vm)
+{
+ SQUserPointer data;
+ if(SQ_FAILED(sq_getinstanceup(vm, 1, &data, 0))) {
+ sq_throwerror(vm, _SC("'stop' called without instance"));
+ return SQ_ERROR;
+ }
+ Scripting::Thunderstorm* _this = reinterpret_cast<Scripting::Thunderstorm*> (data);
+
+ try {
+ _this->stop();
+
+ return 0;
+
+ } catch(std::exception& e) {
+ sq_throwerror(vm, e.what());
+ return SQ_ERROR;
+ } catch(...) {
+ sq_throwerror(vm, _SC("Unexpected exception while executing function 'stop'"));
+ return SQ_ERROR;
+ }
+
+}
+
+static SQInteger Thunderstorm_thunder_wrapper(HSQUIRRELVM vm)
+{
+ SQUserPointer data;
+ if(SQ_FAILED(sq_getinstanceup(vm, 1, &data, 0))) {
+ sq_throwerror(vm, _SC("'thunder' called without instance"));
+ return SQ_ERROR;
+ }
+ Scripting::Thunderstorm* _this = reinterpret_cast<Scripting::Thunderstorm*> (data);
+
+ try {
+ _this->thunder();
+
+ return 0;
+
+ } catch(std::exception& e) {
+ sq_throwerror(vm, e.what());
+ return SQ_ERROR;
+ } catch(...) {
+ sq_throwerror(vm, _SC("Unexpected exception while executing function 'thunder'"));
+ return SQ_ERROR;
+ }
+
+}
+
+static SQInteger Thunderstorm_lightning_wrapper(HSQUIRRELVM vm)
+{
+ SQUserPointer data;
+ if(SQ_FAILED(sq_getinstanceup(vm, 1, &data, 0))) {
+ sq_throwerror(vm, _SC("'lightning' called without instance"));
+ return SQ_ERROR;
+ }
+ Scripting::Thunderstorm* _this = reinterpret_cast<Scripting::Thunderstorm*> (data);
+
+ try {
+ _this->lightning();
+
+ return 0;
+
+ } catch(std::exception& e) {
+ sq_throwerror(vm, e.what());
+ return SQ_ERROR;
+ } catch(...) {
+ sq_throwerror(vm, _SC("Unexpected exception while executing function 'lightning'"));
+ return SQ_ERROR;
+ }
+
+}
+
+static SQInteger Thunderstorm_flash_wrapper(HSQUIRRELVM vm)
+{
+ SQUserPointer data;
+ if(SQ_FAILED(sq_getinstanceup(vm, 1, &data, 0))) {
+ sq_throwerror(vm, _SC("'flash' called without instance"));
+ return SQ_ERROR;
+ }
+ Scripting::Thunderstorm* _this = reinterpret_cast<Scripting::Thunderstorm*> (data);
+
+ try {
+ _this->flash();
+
+ return 0;
+
+ } catch(std::exception& e) {
+ sq_throwerror(vm, e.what());
+ return SQ_ERROR;
+ } catch(...) {
+ sq_throwerror(vm, _SC("Unexpected exception while executing function 'flash'"));
+ return SQ_ERROR;
+ }
+
+}
+
+static SQInteger Thunderstorm_electrify_wrapper(HSQUIRRELVM vm)
+{
+ SQUserPointer data;
+ if(SQ_FAILED(sq_getinstanceup(vm, 1, &data, 0))) {
+ sq_throwerror(vm, _SC("'electrify' called without instance"));
+ return SQ_ERROR;
+ }
+ Scripting::Thunderstorm* _this = reinterpret_cast<Scripting::Thunderstorm*> (data);
+
+ try {
+ _this->electrify();
+
+ return 0;
+
+ } catch(std::exception& e) {
+ sq_throwerror(vm, e.what());
+ return SQ_ERROR;
+ } catch(...) {
+ sq_throwerror(vm, _SC("Unexpected exception while executing function 'electrify'"));
+ return SQ_ERROR;
+ }
+
+}
+
+static SQInteger TileMap_release_hook(SQUserPointer ptr, SQInteger )
+{
+ Scripting::TileMap* _this = reinterpret_cast<Scripting::TileMap*> (ptr);
+ delete _this;
+ return 0;
+}
+
+static SQInteger TileMap_goto_node_wrapper(HSQUIRRELVM vm)
+{
+ SQUserPointer data;
+ if(SQ_FAILED(sq_getinstanceup(vm, 1, &data, 0))) {
+ sq_throwerror(vm, _SC("'goto_node' called without instance"));
+ return SQ_ERROR;
+ }
+ Scripting::TileMap* _this = reinterpret_cast<Scripting::TileMap*> (data);
+ SQInteger arg0;
+ if(SQ_FAILED(sq_getinteger(vm, 2, &arg0))) {
+ sq_throwerror(vm, _SC("Argument 1 not an integer"));
+ return SQ_ERROR;
+ }
+
+ try {
+ _this->goto_node(static_cast<int> (arg0));
+
+ return 0;
+
+ } catch(std::exception& e) {
+ sq_throwerror(vm, e.what());
+ return SQ_ERROR;
+ } catch(...) {
+ sq_throwerror(vm, _SC("Unexpected exception while executing function 'goto_node'"));
+ return SQ_ERROR;
+ }
+
+}
+
+static SQInteger TileMap_start_moving_wrapper(HSQUIRRELVM vm)
+{
+ SQUserPointer data;
+ if(SQ_FAILED(sq_getinstanceup(vm, 1, &data, 0))) {
+ sq_throwerror(vm, _SC("'start_moving' called without instance"));
+ return SQ_ERROR;
+ }
+ Scripting::TileMap* _this = reinterpret_cast<Scripting::TileMap*> (data);
+
+ try {
+ _this->start_moving();
+
+ return 0;
+
+ } catch(std::exception& e) {
+ sq_throwerror(vm, e.what());
+ return SQ_ERROR;
+ } catch(...) {
+ sq_throwerror(vm, _SC("Unexpected exception while executing function 'start_moving'"));
+ return SQ_ERROR;
+ }
+
+}
+
+static SQInteger TileMap_stop_moving_wrapper(HSQUIRRELVM vm)
+{
+ SQUserPointer data;
+ if(SQ_FAILED(sq_getinstanceup(vm, 1, &data, 0))) {
+ sq_throwerror(vm, _SC("'stop_moving' called without instance"));
+ return SQ_ERROR;
+ }
+ Scripting::TileMap* _this = reinterpret_cast<Scripting::TileMap*> (data);
+
+ try {
+ _this->stop_moving();
+
+ return 0;
+
+ } catch(std::exception& e) {
+ sq_throwerror(vm, e.what());
+ return SQ_ERROR;
+ } catch(...) {
+ sq_throwerror(vm, _SC("Unexpected exception while executing function 'stop_moving'"));
+ return SQ_ERROR;
+ }
+
+}
+
+static SQInteger TileMap_fade_wrapper(HSQUIRRELVM vm)
+{
+ SQUserPointer data;
+ if(SQ_FAILED(sq_getinstanceup(vm, 1, &data, 0))) {
+ sq_throwerror(vm, _SC("'fade' called without instance"));
+ return SQ_ERROR;
+ }
+ Scripting::TileMap* _this = reinterpret_cast<Scripting::TileMap*> (data);
+ SQFloat arg0;
+ if(SQ_FAILED(sq_getfloat(vm, 2, &arg0))) {
+ sq_throwerror(vm, _SC("Argument 1 not a float"));
+ return SQ_ERROR;
+ }
+ SQFloat arg1;
+ if(SQ_FAILED(sq_getfloat(vm, 3, &arg1))) {
+ sq_throwerror(vm, _SC("Argument 2 not a float"));
+ return SQ_ERROR;
+ }
+
+ try {
+ _this->fade(static_cast<float> (arg0), static_cast<float> (arg1));
+
+ return 0;
+
+ } catch(std::exception& e) {
+ sq_throwerror(vm, e.what());
+ return SQ_ERROR;
+ } catch(...) {
+ sq_throwerror(vm, _SC("Unexpected exception while executing function 'fade'"));
+ return SQ_ERROR;
+ }
+
+}
+
+static SQInteger TileMap_set_alpha_wrapper(HSQUIRRELVM vm)
+{
+ SQUserPointer data;
+ if(SQ_FAILED(sq_getinstanceup(vm, 1, &data, 0))) {
+ sq_throwerror(vm, _SC("'set_alpha' called without instance"));
+ return SQ_ERROR;
+ }
+ Scripting::TileMap* _this = reinterpret_cast<Scripting::TileMap*> (data);
+ SQFloat arg0;
+ if(SQ_FAILED(sq_getfloat(vm, 2, &arg0))) {
+ sq_throwerror(vm, _SC("Argument 1 not a float"));
+ return SQ_ERROR;
+ }
+
+ try {
+ _this->set_alpha(static_cast<float> (arg0));
+
+ return 0;
+
+ } catch(std::exception& e) {
+ sq_throwerror(vm, e.what());
+ return SQ_ERROR;
+ } catch(...) {
+ sq_throwerror(vm, _SC("Unexpected exception while executing function 'set_alpha'"));
+ return SQ_ERROR;
+ }
+
+}
+
+static SQInteger TileMap_get_alpha_wrapper(HSQUIRRELVM vm)
+{
+ SQUserPointer data;
+ if(SQ_FAILED(sq_getinstanceup(vm, 1, &data, 0))) {
+ sq_throwerror(vm, _SC("'get_alpha' called without instance"));
+ return SQ_ERROR;
+ }
+ Scripting::TileMap* _this = reinterpret_cast<Scripting::TileMap*> (data);
+
+ try {
+ float return_value = _this->get_alpha();
+
+ sq_pushfloat(vm, return_value);
+ return 1;
+
+ } catch(std::exception& e) {
+ sq_throwerror(vm, e.what());
+ return SQ_ERROR;
+ } catch(...) {
+ sq_throwerror(vm, _SC("Unexpected exception while executing function 'get_alpha'"));
+ return SQ_ERROR;
+ }
+
+}
+
+static SQInteger SSector_release_hook(SQUserPointer ptr, SQInteger )
+{
+ Scripting::SSector* _this = reinterpret_cast<Scripting::SSector*> (ptr);
+ delete _this;
+ return 0;
+}
+
+static SQInteger SSector_set_ambient_light_wrapper(HSQUIRRELVM vm)
+{
+ SQUserPointer data;
+ if(SQ_FAILED(sq_getinstanceup(vm, 1, &data, 0))) {
+ sq_throwerror(vm, _SC("'set_ambient_light' called without instance"));
+ return SQ_ERROR;
+ }
+ Scripting::SSector* _this = reinterpret_cast<Scripting::SSector*> (data);
+ SQFloat arg0;
+ if(SQ_FAILED(sq_getfloat(vm, 2, &arg0))) {
+ sq_throwerror(vm, _SC("Argument 1 not a float"));
+ return SQ_ERROR;
+ }
+ SQFloat arg1;
+ if(SQ_FAILED(sq_getfloat(vm, 3, &arg1))) {
+ sq_throwerror(vm, _SC("Argument 2 not a float"));
+ return SQ_ERROR;
+ }
+ SQFloat arg2;
+ if(SQ_FAILED(sq_getfloat(vm, 4, &arg2))) {
+ sq_throwerror(vm, _SC("Argument 3 not a float"));
+ return SQ_ERROR;
+ }
+
+ try {
+ _this->set_ambient_light(static_cast<float> (arg0), static_cast<float> (arg1), static_cast<float> (arg2));
+
+ return 0;
+
+ } catch(std::exception& e) {
+ sq_throwerror(vm, e.what());
+ return SQ_ERROR;
+ } catch(...) {
+ sq_throwerror(vm, _SC("Unexpected exception while executing function 'set_ambient_light'"));
+ return SQ_ERROR;
+ }
+
+}
+
+static SQInteger SSector_get_ambient_red_wrapper(HSQUIRRELVM vm)
+{
+ SQUserPointer data;
+ if(SQ_FAILED(sq_getinstanceup(vm, 1, &data, 0))) {
+ sq_throwerror(vm, _SC("'get_ambient_red' called without instance"));
+ return SQ_ERROR;
+ }
+ Scripting::SSector* _this = reinterpret_cast<Scripting::SSector*> (data);
+
+ try {
+ float return_value = _this->get_ambient_red();
+
+ sq_pushfloat(vm, return_value);
+ return 1;
+
+ } catch(std::exception& e) {
+ sq_throwerror(vm, e.what());
+ return SQ_ERROR;
+ } catch(...) {
+ sq_throwerror(vm, _SC("Unexpected exception while executing function 'get_ambient_red'"));
+ return SQ_ERROR;
+ }
+
+}
+
+static SQInteger SSector_get_ambient_green_wrapper(HSQUIRRELVM vm)
+{
+ SQUserPointer data;
+ if(SQ_FAILED(sq_getinstanceup(vm, 1, &data, 0))) {
+ sq_throwerror(vm, _SC("'get_ambient_green' called without instance"));
+ return SQ_ERROR;
+ }
+ Scripting::SSector* _this = reinterpret_cast<Scripting::SSector*> (data);
+
+ try {
+ float return_value = _this->get_ambient_green();
+
+ sq_pushfloat(vm, return_value);
+ return 1;
+
+ } catch(std::exception& e) {
+ sq_throwerror(vm, e.what());
+ return SQ_ERROR;
+ } catch(...) {
+ sq_throwerror(vm, _SC("Unexpected exception while executing function 'get_ambient_green'"));
+ return SQ_ERROR;
+ }
+
+}
+
+static SQInteger SSector_get_ambient_blue_wrapper(HSQUIRRELVM vm)
+{
+ SQUserPointer data;
+ if(SQ_FAILED(sq_getinstanceup(vm, 1, &data, 0))) {
+ sq_throwerror(vm, _SC("'get_ambient_blue' called without instance"));
+ return SQ_ERROR;
+ }
+ Scripting::SSector* _this = reinterpret_cast<Scripting::SSector*> (data);
+
+ try {
+ float return_value = _this->get_ambient_blue();
+
+ sq_pushfloat(vm, return_value);
+ return 1;
+
+ } catch(std::exception& e) {
+ sq_throwerror(vm, e.what());
+ return SQ_ERROR;
+ } catch(...) {
+ sq_throwerror(vm, _SC("Unexpected exception while executing function 'get_ambient_blue'"));
+ return SQ_ERROR;
+ }
+
+}
+
+static SQInteger LevelTime_release_hook(SQUserPointer ptr, SQInteger )
+{
+ Scripting::LevelTime* _this = reinterpret_cast<Scripting::LevelTime*> (ptr);
+ delete _this;
+ return 0;
+}
+
+static SQInteger LevelTime_start_wrapper(HSQUIRRELVM vm)
+{
+ SQUserPointer data;
+ if(SQ_FAILED(sq_getinstanceup(vm, 1, &data, 0))) {
+ sq_throwerror(vm, _SC("'start' called without instance"));
+ return SQ_ERROR;
+ }
+ Scripting::LevelTime* _this = reinterpret_cast<Scripting::LevelTime*> (data);
+
+ try {
+ _this->start();
+
+ return 0;
+
+ } catch(std::exception& e) {
+ sq_throwerror(vm, e.what());
+ return SQ_ERROR;
+ } catch(...) {
+ sq_throwerror(vm, _SC("Unexpected exception while executing function 'start'"));
+ return SQ_ERROR;
+ }
+
+}
+
+static SQInteger LevelTime_stop_wrapper(HSQUIRRELVM vm)
+{
+ SQUserPointer data;
+ if(SQ_FAILED(sq_getinstanceup(vm, 1, &data, 0))) {
+ sq_throwerror(vm, _SC("'stop' called without instance"));
+ return SQ_ERROR;
+ }
+ Scripting::LevelTime* _this = reinterpret_cast<Scripting::LevelTime*> (data);
+
+ try {
+ _this->stop();
+
+ return 0;
+
+ } catch(std::exception& e) {
+ sq_throwerror(vm, e.what());
+ return SQ_ERROR;
+ } catch(...) {
+ sq_throwerror(vm, _SC("Unexpected exception while executing function 'stop'"));
+ return SQ_ERROR;
+ }
+
+}
+
+static SQInteger LevelTime_get_time_wrapper(HSQUIRRELVM vm)
+{
+ SQUserPointer data;
+ if(SQ_FAILED(sq_getinstanceup(vm, 1, &data, 0))) {
+ sq_throwerror(vm, _SC("'get_time' called without instance"));
+ return SQ_ERROR;
+ }
+ Scripting::LevelTime* _this = reinterpret_cast<Scripting::LevelTime*> (data);
+
+ try {
+ float return_value = _this->get_time();
+
+ sq_pushfloat(vm, return_value);
+ return 1;
+
+ } catch(std::exception& e) {
+ sq_throwerror(vm, e.what());
+ return SQ_ERROR;
+ } catch(...) {
+ sq_throwerror(vm, _SC("Unexpected exception while executing function 'get_time'"));
+ return SQ_ERROR;
+ }
+
+}
+
+static SQInteger LevelTime_set_time_wrapper(HSQUIRRELVM vm)
+{
+ SQUserPointer data;
+ if(SQ_FAILED(sq_getinstanceup(vm, 1, &data, 0))) {
+ sq_throwerror(vm, _SC("'set_time' called without instance"));
+ return SQ_ERROR;
+ }
+ Scripting::LevelTime* _this = reinterpret_cast<Scripting::LevelTime*> (data);
+ SQFloat arg0;
+ if(SQ_FAILED(sq_getfloat(vm, 2, &arg0))) {
+ sq_throwerror(vm, _SC("Argument 1 not a float"));
+ return SQ_ERROR;
+ }
+
+ try {
+ _this->set_time(static_cast<float> (arg0));
+
+ return 0;
+
+ } catch(std::exception& e) {
+ sq_throwerror(vm, e.what());
+ return SQ_ERROR;
+ } catch(...) {
+ sq_throwerror(vm, _SC("Unexpected exception while executing function 'set_time'"));
+ return SQ_ERROR;
+ }
+
+}
+
+static SQInteger WillOWisp_release_hook(SQUserPointer ptr, SQInteger )
+{
+ Scripting::WillOWisp* _this = reinterpret_cast<Scripting::WillOWisp*> (ptr);
+ delete _this;
+ return 0;
+}
+
+static SQInteger WillOWisp_goto_node_wrapper(HSQUIRRELVM vm)
+{
+ SQUserPointer data;
+ if(SQ_FAILED(sq_getinstanceup(vm, 1, &data, 0))) {
+ sq_throwerror(vm, _SC("'goto_node' called without instance"));
+ return SQ_ERROR;
+ }
+ Scripting::WillOWisp* _this = reinterpret_cast<Scripting::WillOWisp*> (data);
+ SQInteger arg0;
+ if(SQ_FAILED(sq_getinteger(vm, 2, &arg0))) {
+ sq_throwerror(vm, _SC("Argument 1 not an integer"));
+ return SQ_ERROR;
+ }
+
+ try {
+ _this->goto_node(static_cast<int> (arg0));
+
+ return 0;
+
+ } catch(std::exception& e) {
+ sq_throwerror(vm, e.what());
+ return SQ_ERROR;
+ } catch(...) {
+ sq_throwerror(vm, _SC("Unexpected exception while executing function 'goto_node'"));
+ return SQ_ERROR;
+ }
+
+}
+
+static SQInteger WillOWisp_set_state_wrapper(HSQUIRRELVM vm)
+{
+ SQUserPointer data;
+ if(SQ_FAILED(sq_getinstanceup(vm, 1, &data, 0))) {
+ sq_throwerror(vm, _SC("'set_state' called without instance"));
+ return SQ_ERROR;
+ }
+ Scripting::WillOWisp* _this = reinterpret_cast<Scripting::WillOWisp*> (data);
+ const SQChar* arg0;
+ if(SQ_FAILED(sq_getstring(vm, 2, &arg0))) {
+ sq_throwerror(vm, _SC("Argument 1 not a string"));
+ return SQ_ERROR;
+ }
+
+ try {
+ _this->set_state(arg0);
+
+ return 0;
+
+ } catch(std::exception& e) {
+ sq_throwerror(vm, e.what());
+ return SQ_ERROR;
+ } catch(...) {
+ sq_throwerror(vm, _SC("Unexpected exception while executing function 'set_state'"));
+ return SQ_ERROR;
+ }
+
+}
+
+static SQInteger WillOWisp_start_moving_wrapper(HSQUIRRELVM vm)
+{
+ SQUserPointer data;
+ if(SQ_FAILED(sq_getinstanceup(vm, 1, &data, 0))) {
+ sq_throwerror(vm, _SC("'start_moving' called without instance"));
+ return SQ_ERROR;
+ }
+ Scripting::WillOWisp* _this = reinterpret_cast<Scripting::WillOWisp*> (data);
+
+ try {
+ _this->start_moving();
+
+ return 0;
+
+ } catch(std::exception& e) {
+ sq_throwerror(vm, e.what());
+ return SQ_ERROR;
+ } catch(...) {
+ sq_throwerror(vm, _SC("Unexpected exception while executing function 'start_moving'"));
+ return SQ_ERROR;
+ }
+
+}
+
+static SQInteger WillOWisp_stop_moving_wrapper(HSQUIRRELVM vm)
+{
+ SQUserPointer data;
+ if(SQ_FAILED(sq_getinstanceup(vm, 1, &data, 0))) {
+ sq_throwerror(vm, _SC("'stop_moving' called without instance"));
+ return SQ_ERROR;
+ }
+ Scripting::WillOWisp* _this = reinterpret_cast<Scripting::WillOWisp*> (data);
+
+ try {
+ _this->stop_moving();
+
+ return 0;
+
+ } catch(std::exception& e) {
+ sq_throwerror(vm, e.what());
+ return SQ_ERROR;
+ } catch(...) {
+ sq_throwerror(vm, _SC("Unexpected exception while executing function 'stop_moving'"));
+ return SQ_ERROR;
+ }
+
+}
+
+static SQInteger display_wrapper(HSQUIRRELVM vm)
+{
+ return Scripting::display(vm);
+}
+
+static SQInteger print_stacktrace_wrapper(HSQUIRRELVM vm)
+{
+ HSQUIRRELVM arg0 = vm;
+
+ try {
+ Scripting::print_stacktrace(arg0);
+
+ return 0;
+
+ } catch(std::exception& e) {
+ sq_throwerror(vm, e.what());
+ return SQ_ERROR;
+ } catch(...) {
+ sq_throwerror(vm, _SC("Unexpected exception while executing function 'print_stacktrace'"));
+ return SQ_ERROR;
+ }
+
+}
+
+static SQInteger get_current_thread_wrapper(HSQUIRRELVM vm)
+{
+ return Scripting::get_current_thread(vm);
+}
+
+static SQInteger display_text_file_wrapper(HSQUIRRELVM vm)
+{
+ const SQChar* arg0;
+ if(SQ_FAILED(sq_getstring(vm, 2, &arg0))) {
+ sq_throwerror(vm, _SC("Argument 1 not a string"));
+ return SQ_ERROR;
+ }
+
+ try {
+ Scripting::display_text_file(arg0);
+
+ return 0;
+
+ } catch(std::exception& e) {
+ sq_throwerror(vm, e.what());
+ return SQ_ERROR;
+ } catch(...) {
+ sq_throwerror(vm, _SC("Unexpected exception while executing function 'display_text_file'"));
+ return SQ_ERROR;
+ }
+
+}
+
+static SQInteger load_worldmap_wrapper(HSQUIRRELVM vm)
+{
+ const SQChar* arg0;
+ if(SQ_FAILED(sq_getstring(vm, 2, &arg0))) {
+ sq_throwerror(vm, _SC("Argument 1 not a string"));
+ return SQ_ERROR;
+ }
+
+ try {
+ Scripting::load_worldmap(arg0);
+
+ return 0;
+
+ } catch(std::exception& e) {
+ sq_throwerror(vm, e.what());
+ return SQ_ERROR;
+ } catch(...) {
+ sq_throwerror(vm, _SC("Unexpected exception while executing function 'load_worldmap'"));
+ return SQ_ERROR;
+ }
+
+}
+
+static SQInteger load_level_wrapper(HSQUIRRELVM vm)
+{
+ const SQChar* arg0;
+ if(SQ_FAILED(sq_getstring(vm, 2, &arg0))) {
+ sq_throwerror(vm, _SC("Argument 1 not a string"));
+ return SQ_ERROR;
+ }
+
+ try {
+ Scripting::load_level(arg0);
+
+ return 0;
+
+ } catch(std::exception& e) {
+ sq_throwerror(vm, e.what());
+ return SQ_ERROR;
+ } catch(...) {
+ sq_throwerror(vm, _SC("Unexpected exception while executing function 'load_level'"));
+ return SQ_ERROR;
+ }
+
+}
+
+static SQInteger wait_wrapper(HSQUIRRELVM vm)
+{
+ HSQUIRRELVM arg0 = vm;
+ SQFloat arg1;
+ if(SQ_FAILED(sq_getfloat(vm, 2, &arg1))) {
+ sq_throwerror(vm, _SC("Argument 1 not a float"));
+ return SQ_ERROR;
+ }
+
+ try {
+ Scripting::wait(arg0, static_cast<float> (arg1));
+
+ return sq_suspendvm(vm);
+
+ } catch(std::exception& e) {
+ sq_throwerror(vm, e.what());
+ return SQ_ERROR;
+ } catch(...) {
+ sq_throwerror(vm, _SC("Unexpected exception while executing function 'wait'"));
+ return SQ_ERROR;
+ }
+
+}
+
+static SQInteger wait_for_screenswitch_wrapper(HSQUIRRELVM vm)
+{
+ HSQUIRRELVM arg0 = vm;
+
+ try {
+ Scripting::wait_for_screenswitch(arg0);
+
+ return sq_suspendvm(vm);
+
+ } catch(std::exception& e) {
+ sq_throwerror(vm, e.what());
+ return SQ_ERROR;
+ } catch(...) {
+ sq_throwerror(vm, _SC("Unexpected exception while executing function 'wait_for_screenswitch'"));
+ return SQ_ERROR;
+ }
+
+}
+
+static SQInteger exit_screen_wrapper(HSQUIRRELVM vm)
+{
+ (void) vm;
+
+ try {
+ Scripting::exit_screen();
+
+ return 0;
+
+ } catch(std::exception& e) {
+ sq_throwerror(vm, e.what());
+ return SQ_ERROR;
+ } catch(...) {
+ sq_throwerror(vm, _SC("Unexpected exception while executing function 'exit_screen'"));
+ return SQ_ERROR;
+ }
+
+}
+
+static SQInteger fadeout_screen_wrapper(HSQUIRRELVM vm)
+{
+ SQFloat arg0;
+ if(SQ_FAILED(sq_getfloat(vm, 2, &arg0))) {
+ sq_throwerror(vm, _SC("Argument 1 not a float"));
+ return SQ_ERROR;
+ }
+
+ try {
+ Scripting::fadeout_screen(static_cast<float> (arg0));
+
+ return 0;
+
+ } catch(std::exception& e) {
+ sq_throwerror(vm, e.what());
+ return SQ_ERROR;
+ } catch(...) {
+ sq_throwerror(vm, _SC("Unexpected exception while executing function 'fadeout_screen'"));
+ return SQ_ERROR;
+ }
+
+}
+
+static SQInteger shrink_screen_wrapper(HSQUIRRELVM vm)
+{
+ SQFloat arg0;
+ if(SQ_FAILED(sq_getfloat(vm, 2, &arg0))) {
+ sq_throwerror(vm, _SC("Argument 1 not a float"));
+ return SQ_ERROR;
+ }
+ SQFloat arg1;
+ if(SQ_FAILED(sq_getfloat(vm, 3, &arg1))) {
+ sq_throwerror(vm, _SC("Argument 2 not a float"));
+ return SQ_ERROR;
+ }
+ SQFloat arg2;
+ if(SQ_FAILED(sq_getfloat(vm, 4, &arg2))) {
+ sq_throwerror(vm, _SC("Argument 3 not a float"));
+ return SQ_ERROR;
+ }
+
+ try {
+ Scripting::shrink_screen(static_cast<float> (arg0), static_cast<float> (arg1), static_cast<float> (arg2));
+
+ return 0;
+
+ } catch(std::exception& e) {
+ sq_throwerror(vm, e.what());
+ return SQ_ERROR;
+ } catch(...) {
+ sq_throwerror(vm, _SC("Unexpected exception while executing function 'shrink_screen'"));
+ return SQ_ERROR;
+ }
+
+}
+
+static SQInteger abort_screenfade_wrapper(HSQUIRRELVM vm)
+{
+ (void) vm;
+
+ try {
+ Scripting::abort_screenfade();
+
+ return 0;
+
+ } catch(std::exception& e) {
+ sq_throwerror(vm, e.what());
+ return SQ_ERROR;
+ } catch(...) {
+ sq_throwerror(vm, _SC("Unexpected exception while executing function 'abort_screenfade'"));
+ return SQ_ERROR;
+ }
+
+}
+
+static SQInteger translate_wrapper(HSQUIRRELVM vm)
+{
+ const SQChar* arg0;
+ if(SQ_FAILED(sq_getstring(vm, 2, &arg0))) {
+ sq_throwerror(vm, _SC("Argument 1 not a string"));
+ return SQ_ERROR;
+ }
+
+ try {
+ std::string return_value = Scripting::translate(arg0);
+
+ sq_pushstring(vm, return_value.c_str(), return_value.size());
+ return 1;
+
+ } catch(std::exception& e) {
+ sq_throwerror(vm, e.what());
+ return SQ_ERROR;
+ } catch(...) {
+ sq_throwerror(vm, _SC("Unexpected exception while executing function 'translate'"));
+ return SQ_ERROR;
+ }
+
+}
+
+static SQInteger import_wrapper(HSQUIRRELVM vm)
+{
+ HSQUIRRELVM arg0 = vm;
+ const SQChar* arg1;
+ if(SQ_FAILED(sq_getstring(vm, 2, &arg1))) {
+ sq_throwerror(vm, _SC("Argument 1 not a string"));
+ return SQ_ERROR;
+ }
+
+ try {
+ Scripting::import(arg0, arg1);
+
+ return 0;
+
+ } catch(std::exception& e) {
+ sq_throwerror(vm, e.what());
+ return SQ_ERROR;
+ } catch(...) {
+ sq_throwerror(vm, _SC("Unexpected exception while executing function 'import'"));
+ return SQ_ERROR;
+ }
+
+}
+
+static SQInteger save_state_wrapper(HSQUIRRELVM vm)
+{
+ (void) vm;
+
+ try {
+ Scripting::save_state();
+
+ return 0;
+
+ } catch(std::exception& e) {
+ sq_throwerror(vm, e.what());
+ return SQ_ERROR;
+ } catch(...) {
+ sq_throwerror(vm, _SC("Unexpected exception while executing function 'save_state'"));
+ return SQ_ERROR;
+ }
+
+}
+
+static SQInteger update_worldmap_wrapper(HSQUIRRELVM vm)
+{
+ (void) vm;
+
+ try {
+ Scripting::update_worldmap();
+
+ return 0;
+
+ } catch(std::exception& e) {
+ sq_throwerror(vm, e.what());
+ return SQ_ERROR;
+ } catch(...) {
+ sq_throwerror(vm, _SC("Unexpected exception while executing function 'update_worldmap'"));
+ return SQ_ERROR;
+ }
+
+}
+
+static SQInteger debug_collrects_wrapper(HSQUIRRELVM vm)
+{
+ SQBool arg0;
+ if(SQ_FAILED(sq_getbool(vm, 2, &arg0))) {
+ sq_throwerror(vm, _SC("Argument 1 not a bool"));
+ return SQ_ERROR;
+ }
+
+ try {
+ Scripting::debug_collrects(arg0 == SQTrue);
+
+ return 0;
+
+ } catch(std::exception& e) {
+ sq_throwerror(vm, e.what());
+ return SQ_ERROR;
+ } catch(...) {
+ sq_throwerror(vm, _SC("Unexpected exception while executing function 'debug_collrects'"));
+ return SQ_ERROR;
+ }
+
+}
+
+static SQInteger debug_show_fps_wrapper(HSQUIRRELVM vm)
+{
+ SQBool arg0;
+ if(SQ_FAILED(sq_getbool(vm, 2, &arg0))) {
+ sq_throwerror(vm, _SC("Argument 1 not a bool"));
+ return SQ_ERROR;
+ }
+
+ try {
+ Scripting::debug_show_fps(arg0 == SQTrue);
+
+ return 0;
+
+ } catch(std::exception& e) {
+ sq_throwerror(vm, e.what());
+ return SQ_ERROR;
+ } catch(...) {
+ sq_throwerror(vm, _SC("Unexpected exception while executing function 'debug_show_fps'"));
+ return SQ_ERROR;
+ }
+
+}
+
+static SQInteger debug_draw_solids_only_wrapper(HSQUIRRELVM vm)
+{
+ SQBool arg0;
+ if(SQ_FAILED(sq_getbool(vm, 2, &arg0))) {
+ sq_throwerror(vm, _SC("Argument 1 not a bool"));
+ return SQ_ERROR;
+ }
+
+ try {
+ Scripting::debug_draw_solids_only(arg0 == SQTrue);
+
+ return 0;
+
+ } catch(std::exception& e) {
+ sq_throwerror(vm, e.what());
+ return SQ_ERROR;
+ } catch(...) {
+ sq_throwerror(vm, _SC("Unexpected exception while executing function 'debug_draw_solids_only'"));
+ return SQ_ERROR;
+ }
+
+}
+
+static SQInteger play_music_wrapper(HSQUIRRELVM vm)
+{
+ const SQChar* arg0;
+ if(SQ_FAILED(sq_getstring(vm, 2, &arg0))) {
+ sq_throwerror(vm, _SC("Argument 1 not a string"));
+ return SQ_ERROR;
+ }
+
+ try {
+ Scripting::play_music(arg0);
+
+ return 0;
+
+ } catch(std::exception& e) {
+ sq_throwerror(vm, e.what());
+ return SQ_ERROR;
+ } catch(...) {
+ sq_throwerror(vm, _SC("Unexpected exception while executing function 'play_music'"));
+ return SQ_ERROR;
+ }
+
+}
+
+static SQInteger play_sound_wrapper(HSQUIRRELVM vm)
+{
+ const SQChar* arg0;
+ if(SQ_FAILED(sq_getstring(vm, 2, &arg0))) {
+ sq_throwerror(vm, _SC("Argument 1 not a string"));
+ return SQ_ERROR;
+ }
+
+ try {
+ Scripting::play_sound(arg0);
+
+ return 0;
+
+ } catch(std::exception& e) {
+ sq_throwerror(vm, e.what());
+ return SQ_ERROR;
+ } catch(...) {
+ sq_throwerror(vm, _SC("Unexpected exception while executing function 'play_sound'"));
+ return SQ_ERROR;
+ }
+
+}
+
+static SQInteger grease_wrapper(HSQUIRRELVM vm)
+{
+ (void) vm;
+
+ try {
+ Scripting::grease();
+
+ return 0;
+
+ } catch(std::exception& e) {
+ sq_throwerror(vm, e.what());
+ return SQ_ERROR;
+ } catch(...) {
+ sq_throwerror(vm, _SC("Unexpected exception while executing function 'grease'"));
+ return SQ_ERROR;
+ }
+
+}
+
+static SQInteger invincible_wrapper(HSQUIRRELVM vm)
+{
+ (void) vm;
+
+ try {
+ Scripting::invincible();
+
+ return 0;
+
+ } catch(std::exception& e) {
+ sq_throwerror(vm, e.what());
+ return SQ_ERROR;
+ } catch(...) {
+ sq_throwerror(vm, _SC("Unexpected exception while executing function 'invincible'"));
+ return SQ_ERROR;
+ }
+
+}
+
+static SQInteger ghost_wrapper(HSQUIRRELVM vm)
+{
+ (void) vm;
+
+ try {
+ Scripting::ghost();
+
+ return 0;
+
+ } catch(std::exception& e) {
+ sq_throwerror(vm, e.what());
+ return SQ_ERROR;
+ } catch(...) {
+ sq_throwerror(vm, _SC("Unexpected exception while executing function 'ghost'"));
+ return SQ_ERROR;
+ }
+
+}
+
+static SQInteger mortal_wrapper(HSQUIRRELVM vm)
+{
+ (void) vm;
+
+ try {
+ Scripting::mortal();
+
+ return 0;
+
+ } catch(std::exception& e) {
+ sq_throwerror(vm, e.what());
+ return SQ_ERROR;
+ } catch(...) {
+ sq_throwerror(vm, _SC("Unexpected exception while executing function 'mortal'"));
+ return SQ_ERROR;
+ }
+
+}
+
+static SQInteger restart_wrapper(HSQUIRRELVM vm)
+{
+ (void) vm;
+
+ try {
+ Scripting::restart();
+
+ return 0;
+
+ } catch(std::exception& e) {
+ sq_throwerror(vm, e.what());
+ return SQ_ERROR;
+ } catch(...) {
+ sq_throwerror(vm, _SC("Unexpected exception while executing function 'restart'"));
+ return SQ_ERROR;
+ }
+
+}
+
+static SQInteger whereami_wrapper(HSQUIRRELVM vm)
+{
+ (void) vm;
+
+ try {
+ Scripting::whereami();
+
+ return 0;
+
+ } catch(std::exception& e) {
+ sq_throwerror(vm, e.what());
+ return SQ_ERROR;
+ } catch(...) {
+ sq_throwerror(vm, _SC("Unexpected exception while executing function 'whereami'"));
+ return SQ_ERROR;
+ }
+
+}
+
+static SQInteger gotoend_wrapper(HSQUIRRELVM vm)
+{
+ (void) vm;
+
+ try {
+ Scripting::gotoend();
+
+ return 0;
+
+ } catch(std::exception& e) {
+ sq_throwerror(vm, e.what());
+ return SQ_ERROR;
+ } catch(...) {
+ sq_throwerror(vm, _SC("Unexpected exception while executing function 'gotoend'"));
+ return SQ_ERROR;
+ }
+
+}
+
+static SQInteger camera_wrapper(HSQUIRRELVM vm)
+{
+ (void) vm;
+
+ try {
+ Scripting::camera();
+
+ return 0;
+
+ } catch(std::exception& e) {
+ sq_throwerror(vm, e.what());
+ return SQ_ERROR;
+ } catch(...) {
+ sq_throwerror(vm, _SC("Unexpected exception while executing function 'camera'"));
+ return SQ_ERROR;
+ }
+
+}
+
+static SQInteger quit_wrapper(HSQUIRRELVM vm)
+{
+ (void) vm;
+
+ try {
+ Scripting::quit();
+
+ return 0;
+
+ } catch(std::exception& e) {
+ sq_throwerror(vm, e.what());
+ return SQ_ERROR;
+ } catch(...) {
+ sq_throwerror(vm, _SC("Unexpected exception while executing function 'quit'"));
+ return SQ_ERROR;
+ }
+
+}
+
+static SQInteger rand_wrapper(HSQUIRRELVM vm)
+{
+
+ try {
+ int return_value = Scripting::rand();
+
+ sq_pushinteger(vm, return_value);
+ return 1;
+
+ } catch(std::exception& e) {
+ sq_throwerror(vm, e.what());
+ return SQ_ERROR;
+ } catch(...) {
+ sq_throwerror(vm, _SC("Unexpected exception while executing function 'rand'"));
+ return SQ_ERROR;
+ }
+
+}
+
+} // end of namespace Wrapper
+void create_squirrel_instance(HSQUIRRELVM v, Scripting::DisplayEffect* object, bool setup_releasehook)
+{
+ using namespace Wrapper;
+
+ sq_pushroottable(v);
+ sq_pushstring(v, "DisplayEffect", -1);
+ if(SQ_FAILED(sq_get(v, -2))) {
+ std::ostringstream msg;
+ msg << "Couldn't resolved squirrel type 'DisplayEffect'";
+ throw SquirrelError(v, msg.str());
+ }
+
+ if(SQ_FAILED(sq_createinstance(v, -1)) || SQ_FAILED(sq_setinstanceup(v, -1, object))) {
+ std::ostringstream msg;
+ msg << "Couldn't setup squirrel instance for object of type 'DisplayEffect'";
+ throw SquirrelError(v, msg.str());
+ }
+ sq_remove(v, -2); // remove object name
+
+ if(setup_releasehook) {
+ sq_setreleasehook(v, -1, DisplayEffect_release_hook);
+ }
+
+ sq_remove(v, -2); // remove root table
+}
+
+void create_squirrel_instance(HSQUIRRELVM v, Scripting::Camera* object, bool setup_releasehook)
+{
+ using namespace Wrapper;
+
+ sq_pushroottable(v);
+ sq_pushstring(v, "Camera", -1);
+ if(SQ_FAILED(sq_get(v, -2))) {
+ std::ostringstream msg;
+ msg << "Couldn't resolved squirrel type 'Camera'";
+ throw SquirrelError(v, msg.str());
+ }
+
+ if(SQ_FAILED(sq_createinstance(v, -1)) || SQ_FAILED(sq_setinstanceup(v, -1, object))) {
+ std::ostringstream msg;
+ msg << "Couldn't setup squirrel instance for object of type 'Camera'";
+ throw SquirrelError(v, msg.str());
+ }
+ sq_remove(v, -2); // remove object name
+
+ if(setup_releasehook) {
+ sq_setreleasehook(v, -1, Camera_release_hook);
+ }
+
+ sq_remove(v, -2); // remove root table
+}
+
+void create_squirrel_instance(HSQUIRRELVM v, Scripting::Level* object, bool setup_releasehook)
+{
+ using namespace Wrapper;
+
+ sq_pushroottable(v);
+ sq_pushstring(v, "Level", -1);
+ if(SQ_FAILED(sq_get(v, -2))) {
+ std::ostringstream msg;
+ msg << "Couldn't resolved squirrel type 'Level'";
+ throw SquirrelError(v, msg.str());
+ }
+
+ if(SQ_FAILED(sq_createinstance(v, -1)) || SQ_FAILED(sq_setinstanceup(v, -1, object))) {
+ std::ostringstream msg;
+ msg << "Couldn't setup squirrel instance for object of type 'Level'";
+ throw SquirrelError(v, msg.str());
+ }
+ sq_remove(v, -2); // remove object name
+
+ if(setup_releasehook) {
+ sq_setreleasehook(v, -1, Level_release_hook);
+ }
+
+ sq_remove(v, -2); // remove root table
+}
+
+void create_squirrel_instance(HSQUIRRELVM v, Scripting::ScriptedObject* object, bool setup_releasehook)
+{
+ using namespace Wrapper;
+
+ sq_pushroottable(v);
+ sq_pushstring(v, "ScriptedObject", -1);
+ if(SQ_FAILED(sq_get(v, -2))) {
+ std::ostringstream msg;
+ msg << "Couldn't resolved squirrel type 'ScriptedObject'";
+ throw SquirrelError(v, msg.str());
+ }
+
+ if(SQ_FAILED(sq_createinstance(v, -1)) || SQ_FAILED(sq_setinstanceup(v, -1, object))) {
+ std::ostringstream msg;
+ msg << "Couldn't setup squirrel instance for object of type 'ScriptedObject'";
+ throw SquirrelError(v, msg.str());
+ }
+ sq_remove(v, -2); // remove object name
+
+ if(setup_releasehook) {
+ sq_setreleasehook(v, -1, ScriptedObject_release_hook);
+ }
+
+ sq_remove(v, -2); // remove root table
+}
+
+void create_squirrel_instance(HSQUIRRELVM v, Scripting::Text* object, bool setup_releasehook)
+{
+ using namespace Wrapper;
+
+ sq_pushroottable(v);
+ sq_pushstring(v, "Text", -1);
+ if(SQ_FAILED(sq_get(v, -2))) {
+ std::ostringstream msg;
+ msg << "Couldn't resolved squirrel type 'Text'";
+ throw SquirrelError(v, msg.str());
+ }
+
+ if(SQ_FAILED(sq_createinstance(v, -1)) || SQ_FAILED(sq_setinstanceup(v, -1, object))) {
+ std::ostringstream msg;
+ msg << "Couldn't setup squirrel instance for object of type 'Text'";
+ throw SquirrelError(v, msg.str());
+ }
+ sq_remove(v, -2); // remove object name
+
+ if(setup_releasehook) {
+ sq_setreleasehook(v, -1, Text_release_hook);
+ }
+
+ sq_remove(v, -2); // remove root table
+}
+
+void create_squirrel_instance(HSQUIRRELVM v, Scripting::Player* object, bool setup_releasehook)
+{
+ using namespace Wrapper;
+
+ sq_pushroottable(v);
+ sq_pushstring(v, "Player", -1);
+ if(SQ_FAILED(sq_get(v, -2))) {
+ std::ostringstream msg;
+ msg << "Couldn't resolved squirrel type 'Player'";
+ throw SquirrelError(v, msg.str());
+ }
+
+ if(SQ_FAILED(sq_createinstance(v, -1)) || SQ_FAILED(sq_setinstanceup(v, -1, object))) {
+ std::ostringstream msg;
+ msg << "Couldn't setup squirrel instance for object of type 'Player'";
+ throw SquirrelError(v, msg.str());
+ }
+ sq_remove(v, -2); // remove object name
+
+ if(setup_releasehook) {
+ sq_setreleasehook(v, -1, Player_release_hook);
+ }
+
+ sq_remove(v, -2); // remove root table
+}
+
+void create_squirrel_instance(HSQUIRRELVM v, Scripting::FloatingImage* object, bool setup_releasehook)
+{
+ using namespace Wrapper;
+
+ sq_pushroottable(v);
+ sq_pushstring(v, "FloatingImage", -1);
+ if(SQ_FAILED(sq_get(v, -2))) {
+ std::ostringstream msg;
+ msg << "Couldn't resolved squirrel type 'FloatingImage'";
+ throw SquirrelError(v, msg.str());
+ }
+
+ if(SQ_FAILED(sq_createinstance(v, -1)) || SQ_FAILED(sq_setinstanceup(v, -1, object))) {
+ std::ostringstream msg;
+ msg << "Couldn't setup squirrel instance for object of type 'FloatingImage'";
+ throw SquirrelError(v, msg.str());
+ }
+ sq_remove(v, -2); // remove object name
+
+ if(setup_releasehook) {
+ sq_setreleasehook(v, -1, FloatingImage_release_hook);
+ }
+
+ sq_remove(v, -2); // remove root table
+}
+
+void create_squirrel_instance(HSQUIRRELVM v, Scripting::Platform* object, bool setup_releasehook)
+{
+ using namespace Wrapper;
+
+ sq_pushroottable(v);
+ sq_pushstring(v, "Platform", -1);
+ if(SQ_FAILED(sq_get(v, -2))) {
+ std::ostringstream msg;
+ msg << "Couldn't resolved squirrel type 'Platform'";
+ throw SquirrelError(v, msg.str());
+ }
+
+ if(SQ_FAILED(sq_createinstance(v, -1)) || SQ_FAILED(sq_setinstanceup(v, -1, object))) {
+ std::ostringstream msg;
+ msg << "Couldn't setup squirrel instance for object of type 'Platform'";
+ throw SquirrelError(v, msg.str());
+ }
+ sq_remove(v, -2); // remove object name
+
+ if(setup_releasehook) {
+ sq_setreleasehook(v, -1, Platform_release_hook);
+ }
+
+ sq_remove(v, -2); // remove root table
+}
+
+void create_squirrel_instance(HSQUIRRELVM v, Scripting::Candle* object, bool setup_releasehook)
+{
+ using namespace Wrapper;
+
+ sq_pushroottable(v);
+ sq_pushstring(v, "Candle", -1);
+ if(SQ_FAILED(sq_get(v, -2))) {
+ std::ostringstream msg;
+ msg << "Couldn't resolved squirrel type 'Candle'";
+ throw SquirrelError(v, msg.str());
+ }
+
+ if(SQ_FAILED(sq_createinstance(v, -1)) || SQ_FAILED(sq_setinstanceup(v, -1, object))) {
+ std::ostringstream msg;
+ msg << "Couldn't setup squirrel instance for object of type 'Candle'";
+ throw SquirrelError(v, msg.str());
+ }
+ sq_remove(v, -2); // remove object name
+
+ if(setup_releasehook) {
+ sq_setreleasehook(v, -1, Candle_release_hook);
+ }
+
+ sq_remove(v, -2); // remove root table
+}
+
+void create_squirrel_instance(HSQUIRRELVM v, Scripting::Wind* object, bool setup_releasehook)
+{
+ using namespace Wrapper;
+
+ sq_pushroottable(v);
+ sq_pushstring(v, "Wind", -1);
+ if(SQ_FAILED(sq_get(v, -2))) {
+ std::ostringstream msg;
+ msg << "Couldn't resolved squirrel type 'Wind'";
+ throw SquirrelError(v, msg.str());
+ }
+
+ if(SQ_FAILED(sq_createinstance(v, -1)) || SQ_FAILED(sq_setinstanceup(v, -1, object))) {
+ std::ostringstream msg;
+ msg << "Couldn't setup squirrel instance for object of type 'Wind'";
+ throw SquirrelError(v, msg.str());
+ }
+ sq_remove(v, -2); // remove object name
+
+ if(setup_releasehook) {
+ sq_setreleasehook(v, -1, Wind_release_hook);
+ }
+
+ sq_remove(v, -2); // remove root table
+}
+
+void create_squirrel_instance(HSQUIRRELVM v, Scripting::AmbientSound* object, bool setup_releasehook)
+{
+ using namespace Wrapper;
+
+ sq_pushroottable(v);
+ sq_pushstring(v, "AmbientSound", -1);
+ if(SQ_FAILED(sq_get(v, -2))) {
+ std::ostringstream msg;
+ msg << "Couldn't resolved squirrel type 'AmbientSound'";
+ throw SquirrelError(v, msg.str());
+ }
+
+ if(SQ_FAILED(sq_createinstance(v, -1)) || SQ_FAILED(sq_setinstanceup(v, -1, object))) {
+ std::ostringstream msg;
+ msg << "Couldn't setup squirrel instance for object of type 'AmbientSound'";
+ throw SquirrelError(v, msg.str());
+ }
+ sq_remove(v, -2); // remove object name
+
+ if(setup_releasehook) {
+ sq_setreleasehook(v, -1, AmbientSound_release_hook);
+ }
+
+ sq_remove(v, -2); // remove root table
+}
+
+void create_squirrel_instance(HSQUIRRELVM v, Scripting::Thunderstorm* object, bool setup_releasehook)
+{
+ using namespace Wrapper;
+
+ sq_pushroottable(v);
+ sq_pushstring(v, "Thunderstorm", -1);
+ if(SQ_FAILED(sq_get(v, -2))) {
+ std::ostringstream msg;
+ msg << "Couldn't resolved squirrel type 'Thunderstorm'";
+ throw SquirrelError(v, msg.str());
+ }
+
+ if(SQ_FAILED(sq_createinstance(v, -1)) || SQ_FAILED(sq_setinstanceup(v, -1, object))) {
+ std::ostringstream msg;
+ msg << "Couldn't setup squirrel instance for object of type 'Thunderstorm'";
+ throw SquirrelError(v, msg.str());
+ }
+ sq_remove(v, -2); // remove object name
+
+ if(setup_releasehook) {
+ sq_setreleasehook(v, -1, Thunderstorm_release_hook);
+ }
+
+ sq_remove(v, -2); // remove root table
+}
+
+void create_squirrel_instance(HSQUIRRELVM v, Scripting::TileMap* object, bool setup_releasehook)
+{
+ using namespace Wrapper;
+
+ sq_pushroottable(v);
+ sq_pushstring(v, "TileMap", -1);
+ if(SQ_FAILED(sq_get(v, -2))) {
+ std::ostringstream msg;
+ msg << "Couldn't resolved squirrel type 'TileMap'";
+ throw SquirrelError(v, msg.str());
+ }
+
+ if(SQ_FAILED(sq_createinstance(v, -1)) || SQ_FAILED(sq_setinstanceup(v, -1, object))) {
+ std::ostringstream msg;
+ msg << "Couldn't setup squirrel instance for object of type 'TileMap'";
+ throw SquirrelError(v, msg.str());
+ }
+ sq_remove(v, -2); // remove object name
+
+ if(setup_releasehook) {
+ sq_setreleasehook(v, -1, TileMap_release_hook);
+ }
+
+ sq_remove(v, -2); // remove root table
+}
+
+void create_squirrel_instance(HSQUIRRELVM v, Scripting::SSector* object, bool setup_releasehook)
+{
+ using namespace Wrapper;
+
+ sq_pushroottable(v);
+ sq_pushstring(v, "SSector", -1);
+ if(SQ_FAILED(sq_get(v, -2))) {
+ std::ostringstream msg;
+ msg << "Couldn't resolved squirrel type 'SSector'";
+ throw SquirrelError(v, msg.str());
+ }
+
+ if(SQ_FAILED(sq_createinstance(v, -1)) || SQ_FAILED(sq_setinstanceup(v, -1, object))) {
+ std::ostringstream msg;
+ msg << "Couldn't setup squirrel instance for object of type 'SSector'";
+ throw SquirrelError(v, msg.str());
+ }
+ sq_remove(v, -2); // remove object name
+
+ if(setup_releasehook) {
+ sq_setreleasehook(v, -1, SSector_release_hook);
+ }
+
+ sq_remove(v, -2); // remove root table
+}
+
+void create_squirrel_instance(HSQUIRRELVM v, Scripting::LevelTime* object, bool setup_releasehook)
+{
+ using namespace Wrapper;
+
+ sq_pushroottable(v);
+ sq_pushstring(v, "LevelTime", -1);
+ if(SQ_FAILED(sq_get(v, -2))) {
+ std::ostringstream msg;
+ msg << "Couldn't resolved squirrel type 'LevelTime'";
+ throw SquirrelError(v, msg.str());
+ }
+
+ if(SQ_FAILED(sq_createinstance(v, -1)) || SQ_FAILED(sq_setinstanceup(v, -1, object))) {
+ std::ostringstream msg;
+ msg << "Couldn't setup squirrel instance for object of type 'LevelTime'";
+ throw SquirrelError(v, msg.str());
+ }
+ sq_remove(v, -2); // remove object name
+
+ if(setup_releasehook) {
+ sq_setreleasehook(v, -1, LevelTime_release_hook);
+ }
+
+ sq_remove(v, -2); // remove root table
+}
+
+void create_squirrel_instance(HSQUIRRELVM v, Scripting::WillOWisp* object, bool setup_releasehook)
+{
+ using namespace Wrapper;
+
+ sq_pushroottable(v);
+ sq_pushstring(v, "WillOWisp", -1);
+ if(SQ_FAILED(sq_get(v, -2))) {
+ std::ostringstream msg;
+ msg << "Couldn't resolved squirrel type 'WillOWisp'";
+ throw SquirrelError(v, msg.str());
+ }
+
+ if(SQ_FAILED(sq_createinstance(v, -1)) || SQ_FAILED(sq_setinstanceup(v, -1, object))) {
+ std::ostringstream msg;
+ msg << "Couldn't setup squirrel instance for object of type 'WillOWisp'";
+ throw SquirrelError(v, msg.str());
+ }
+ sq_remove(v, -2); // remove object name
+
+ if(setup_releasehook) {
+ sq_setreleasehook(v, -1, WillOWisp_release_hook);
+ }
+
+ sq_remove(v, -2); // remove root table
+}
+
+void register_supertux_wrapper(HSQUIRRELVM v)
+{
+ using namespace Wrapper;
+
+ sq_pushstring(v, "ANCHOR_TOP", -1);
+ sq_pushinteger(v, 16);
+ if(SQ_FAILED(sq_createslot(v, -3))) {
+ throw SquirrelError(v, "Couldn't register constant 'ANCHOR_TOP'");
+ }
+
+ sq_pushstring(v, "ANCHOR_BOTTOM", -1);
+ sq_pushinteger(v, 32);
+ if(SQ_FAILED(sq_createslot(v, -3))) {
+ throw SquirrelError(v, "Couldn't register constant 'ANCHOR_BOTTOM'");
+ }
+
+ sq_pushstring(v, "ANCHOR_LEFT", -1);
+ sq_pushinteger(v, 1);
+ if(SQ_FAILED(sq_createslot(v, -3))) {
+ throw SquirrelError(v, "Couldn't register constant 'ANCHOR_LEFT'");
+ }
+
+ sq_pushstring(v, "ANCHOR_RIGHT", -1);
+ sq_pushinteger(v, 2);
+ if(SQ_FAILED(sq_createslot(v, -3))) {
+ throw SquirrelError(v, "Couldn't register constant 'ANCHOR_RIGHT'");
+ }
+
+ sq_pushstring(v, "ANCHOR_MIDDLE", -1);
+ sq_pushinteger(v, 0);
+ if(SQ_FAILED(sq_createslot(v, -3))) {
+ throw SquirrelError(v, "Couldn't register constant 'ANCHOR_MIDDLE'");
+ }
+
+ sq_pushstring(v, "ANCHOR_TOP_LEFT", -1);
+ sq_pushinteger(v, 17);
+ if(SQ_FAILED(sq_createslot(v, -3))) {
+ throw SquirrelError(v, "Couldn't register constant 'ANCHOR_TOP_LEFT'");
+ }
+
+ sq_pushstring(v, "ANCHOR_TOP_RIGHT", -1);
+ sq_pushinteger(v, 18);
+ if(SQ_FAILED(sq_createslot(v, -3))) {
+ throw SquirrelError(v, "Couldn't register constant 'ANCHOR_TOP_RIGHT'");
+ }
+
+ sq_pushstring(v, "ANCHOR_BOTTOM_LEFT", -1);
+ sq_pushinteger(v, 33);
+ if(SQ_FAILED(sq_createslot(v, -3))) {
+ throw SquirrelError(v, "Couldn't register constant 'ANCHOR_BOTTOM_LEFT'");
+ }
+
+ sq_pushstring(v, "ANCHOR_BOTTOM_RIGHT", -1);
+ sq_pushinteger(v, 34);
+ if(SQ_FAILED(sq_createslot(v, -3))) {
+ throw SquirrelError(v, "Couldn't register constant 'ANCHOR_BOTTOM_RIGHT'");
+ }
+
+ sq_pushstring(v, "display", -1);
+ sq_newclosure(v, &display_wrapper, 0);
+ if(SQ_FAILED(sq_createslot(v, -3))) {
+ throw SquirrelError(v, "Couldn't register function 'display'");
+ }
+
+ sq_pushstring(v, "print_stacktrace", -1);
+ sq_newclosure(v, &print_stacktrace_wrapper, 0);
+ if(SQ_FAILED(sq_createslot(v, -3))) {
+ throw SquirrelError(v, "Couldn't register function 'print_stacktrace'");
+ }
+
+ sq_pushstring(v, "get_current_thread", -1);
+ sq_newclosure(v, &get_current_thread_wrapper, 0);
+ if(SQ_FAILED(sq_createslot(v, -3))) {
+ throw SquirrelError(v, "Couldn't register function 'get_current_thread'");
+ }
+
+ sq_pushstring(v, "display_text_file", -1);
+ sq_newclosure(v, &display_text_file_wrapper, 0);
+ if(SQ_FAILED(sq_createslot(v, -3))) {
+ throw SquirrelError(v, "Couldn't register function 'display_text_file'");
+ }
+
+ sq_pushstring(v, "load_worldmap", -1);
+ sq_newclosure(v, &load_worldmap_wrapper, 0);
+ if(SQ_FAILED(sq_createslot(v, -3))) {
+ throw SquirrelError(v, "Couldn't register function 'load_worldmap'");
+ }
+
+ sq_pushstring(v, "load_level", -1);
+ sq_newclosure(v, &load_level_wrapper, 0);
+ if(SQ_FAILED(sq_createslot(v, -3))) {
+ throw SquirrelError(v, "Couldn't register function 'load_level'");
+ }
+
+ sq_pushstring(v, "wait", -1);
+ sq_newclosure(v, &wait_wrapper, 0);
+ if(SQ_FAILED(sq_createslot(v, -3))) {
+ throw SquirrelError(v, "Couldn't register function 'wait'");
+ }
+
+ sq_pushstring(v, "wait_for_screenswitch", -1);
+ sq_newclosure(v, &wait_for_screenswitch_wrapper, 0);
+ if(SQ_FAILED(sq_createslot(v, -3))) {
+ throw SquirrelError(v, "Couldn't register function 'wait_for_screenswitch'");
+ }
+
+ sq_pushstring(v, "exit_screen", -1);
+ sq_newclosure(v, &exit_screen_wrapper, 0);
+ if(SQ_FAILED(sq_createslot(v, -3))) {
+ throw SquirrelError(v, "Couldn't register function 'exit_screen'");
+ }
+
+ sq_pushstring(v, "fadeout_screen", -1);
+ sq_newclosure(v, &fadeout_screen_wrapper, 0);
+ if(SQ_FAILED(sq_createslot(v, -3))) {
+ throw SquirrelError(v, "Couldn't register function 'fadeout_screen'");
+ }
+
+ sq_pushstring(v, "shrink_screen", -1);
+ sq_newclosure(v, &shrink_screen_wrapper, 0);
+ if(SQ_FAILED(sq_createslot(v, -3))) {
+ throw SquirrelError(v, "Couldn't register function 'shrink_screen'");
+ }
+
+ sq_pushstring(v, "abort_screenfade", -1);
+ sq_newclosure(v, &abort_screenfade_wrapper, 0);
+ if(SQ_FAILED(sq_createslot(v, -3))) {
+ throw SquirrelError(v, "Couldn't register function 'abort_screenfade'");
+ }
+
+ sq_pushstring(v, "translate", -1);
+ sq_newclosure(v, &translate_wrapper, 0);
+ if(SQ_FAILED(sq_createslot(v, -3))) {
+ throw SquirrelError(v, "Couldn't register function 'translate'");
+ }
+
+ sq_pushstring(v, "import", -1);
+ sq_newclosure(v, &import_wrapper, 0);
+ if(SQ_FAILED(sq_createslot(v, -3))) {
+ throw SquirrelError(v, "Couldn't register function 'import'");
+ }
+
+ sq_pushstring(v, "save_state", -1);
+ sq_newclosure(v, &save_state_wrapper, 0);
+ if(SQ_FAILED(sq_createslot(v, -3))) {
+ throw SquirrelError(v, "Couldn't register function 'save_state'");
+ }
+
+ sq_pushstring(v, "update_worldmap", -1);
+ sq_newclosure(v, &update_worldmap_wrapper, 0);
+ if(SQ_FAILED(sq_createslot(v, -3))) {
+ throw SquirrelError(v, "Couldn't register function 'update_worldmap'");
+ }
+
+ sq_pushstring(v, "debug_collrects", -1);
+ sq_newclosure(v, &debug_collrects_wrapper, 0);
+ if(SQ_FAILED(sq_createslot(v, -3))) {
+ throw SquirrelError(v, "Couldn't register function 'debug_collrects'");
+ }
+
+ sq_pushstring(v, "debug_show_fps", -1);
+ sq_newclosure(v, &debug_show_fps_wrapper, 0);
+ if(SQ_FAILED(sq_createslot(v, -3))) {
+ throw SquirrelError(v, "Couldn't register function 'debug_show_fps'");
+ }
+
+ sq_pushstring(v, "debug_draw_solids_only", -1);
+ sq_newclosure(v, &debug_draw_solids_only_wrapper, 0);
+ if(SQ_FAILED(sq_createslot(v, -3))) {
+ throw SquirrelError(v, "Couldn't register function 'debug_draw_solids_only'");
+ }
+
+ sq_pushstring(v, "play_music", -1);
+ sq_newclosure(v, &play_music_wrapper, 0);
+ if(SQ_FAILED(sq_createslot(v, -3))) {
+ throw SquirrelError(v, "Couldn't register function 'play_music'");
+ }
+
+ sq_pushstring(v, "play_sound", -1);
+ sq_newclosure(v, &play_sound_wrapper, 0);
+ if(SQ_FAILED(sq_createslot(v, -3))) {
+ throw SquirrelError(v, "Couldn't register function 'play_sound'");
+ }
+
+ sq_pushstring(v, "grease", -1);
+ sq_newclosure(v, &grease_wrapper, 0);
+ if(SQ_FAILED(sq_createslot(v, -3))) {
+ throw SquirrelError(v, "Couldn't register function 'grease'");
+ }
+
+ sq_pushstring(v, "invincible", -1);
+ sq_newclosure(v, &invincible_wrapper, 0);
+ if(SQ_FAILED(sq_createslot(v, -3))) {
+ throw SquirrelError(v, "Couldn't register function 'invincible'");
+ }
+
+ sq_pushstring(v, "ghost", -1);
+ sq_newclosure(v, &ghost_wrapper, 0);
+ if(SQ_FAILED(sq_createslot(v, -3))) {
+ throw SquirrelError(v, "Couldn't register function 'ghost'");
+ }
+
+ sq_pushstring(v, "mortal", -1);
+ sq_newclosure(v, &mortal_wrapper, 0);
+ if(SQ_FAILED(sq_createslot(v, -3))) {
+ throw SquirrelError(v, "Couldn't register function 'mortal'");
+ }
+
+ sq_pushstring(v, "restart", -1);
+ sq_newclosure(v, &restart_wrapper, 0);
+ if(SQ_FAILED(sq_createslot(v, -3))) {
+ throw SquirrelError(v, "Couldn't register function 'restart'");
+ }
+
+ sq_pushstring(v, "whereami", -1);
+ sq_newclosure(v, &whereami_wrapper, 0);
+ if(SQ_FAILED(sq_createslot(v, -3))) {
+ throw SquirrelError(v, "Couldn't register function 'whereami'");
+ }
+
+ sq_pushstring(v, "gotoend", -1);
+ sq_newclosure(v, &gotoend_wrapper, 0);
+ if(SQ_FAILED(sq_createslot(v, -3))) {
+ throw SquirrelError(v, "Couldn't register function 'gotoend'");
+ }
+
+ sq_pushstring(v, "camera", -1);
+ sq_newclosure(v, &camera_wrapper, 0);
+ if(SQ_FAILED(sq_createslot(v, -3))) {
+ throw SquirrelError(v, "Couldn't register function 'camera'");
+ }
+
+ sq_pushstring(v, "quit", -1);
+ sq_newclosure(v, &quit_wrapper, 0);
+ if(SQ_FAILED(sq_createslot(v, -3))) {
+ throw SquirrelError(v, "Couldn't register function 'quit'");
+ }
+
+ sq_pushstring(v, "rand", -1);
+ sq_newclosure(v, &rand_wrapper, 0);
+ if(SQ_FAILED(sq_createslot(v, -3))) {
+ throw SquirrelError(v, "Couldn't register function 'rand'");
+ }
+
+ // Register class DisplayEffect
+ sq_pushstring(v, "DisplayEffect", -1);
+ if(sq_newclass(v, SQFalse) < 0) {
+ std::ostringstream msg;
+ msg << "Couldn't create new class 'DisplayEffect'";
+ throw SquirrelError(v, msg.str());
+ }
+ sq_pushstring(v, "fade_out", -1);
+ sq_newclosure(v, &DisplayEffect_fade_out_wrapper, 0);
+ if(SQ_FAILED(sq_createslot(v, -3))) {
+ throw SquirrelError(v, "Couldn't register function 'fade_out'");
+ }
+
+ sq_pushstring(v, "fade_in", -1);
+ sq_newclosure(v, &DisplayEffect_fade_in_wrapper, 0);
+ if(SQ_FAILED(sq_createslot(v, -3))) {
+ throw SquirrelError(v, "Couldn't register function 'fade_in'");
+ }
+
+ sq_pushstring(v, "set_black", -1);
+ sq_newclosure(v, &DisplayEffect_set_black_wrapper, 0);
+ if(SQ_FAILED(sq_createslot(v, -3))) {
+ throw SquirrelError(v, "Couldn't register function 'set_black'");
+ }
+
+ sq_pushstring(v, "is_black", -1);
+ sq_newclosure(v, &DisplayEffect_is_black_wrapper, 0);
+ if(SQ_FAILED(sq_createslot(v, -3))) {
+ throw SquirrelError(v, "Couldn't register function 'is_black'");
+ }
+
+ sq_pushstring(v, "sixteen_to_nine", -1);
+ sq_newclosure(v, &DisplayEffect_sixteen_to_nine_wrapper, 0);
+ if(SQ_FAILED(sq_createslot(v, -3))) {
+ throw SquirrelError(v, "Couldn't register function 'sixteen_to_nine'");
+ }
+
+ sq_pushstring(v, "four_to_three", -1);
+ sq_newclosure(v, &DisplayEffect_four_to_three_wrapper, 0);
+ if(SQ_FAILED(sq_createslot(v, -3))) {
+ throw SquirrelError(v, "Couldn't register function 'four_to_three'");
+ }
+
+ if(SQ_FAILED(sq_createslot(v, -3))) {
+ throw SquirrelError(v, "Couldn't register class 'DisplayEffect'");
+ }
+
+ // Register class Camera
+ sq_pushstring(v, "Camera", -1);
+ if(sq_newclass(v, SQFalse) < 0) {
+ std::ostringstream msg;
+ msg << "Couldn't create new class 'Camera'";
+ throw SquirrelError(v, msg.str());
+ }
+ sq_pushstring(v, "reload_config", -1);
+ sq_newclosure(v, &Camera_reload_config_wrapper, 0);
+ if(SQ_FAILED(sq_createslot(v, -3))) {
+ throw SquirrelError(v, "Couldn't register function 'reload_config'");
+ }
+
+ sq_pushstring(v, "shake", -1);
+ sq_newclosure(v, &Camera_shake_wrapper, 0);
+ if(SQ_FAILED(sq_createslot(v, -3))) {
+ throw SquirrelError(v, "Couldn't register function 'shake'");
+ }
+
+ sq_pushstring(v, "set_pos", -1);
+ sq_newclosure(v, &Camera_set_pos_wrapper, 0);
+ if(SQ_FAILED(sq_createslot(v, -3))) {
+ throw SquirrelError(v, "Couldn't register function 'set_pos'");
+ }
+
+ sq_pushstring(v, "set_mode", -1);
+ sq_newclosure(v, &Camera_set_mode_wrapper, 0);
+ if(SQ_FAILED(sq_createslot(v, -3))) {
+ throw SquirrelError(v, "Couldn't register function 'set_mode'");
+ }
+
+ sq_pushstring(v, "scroll_to", -1);
+ sq_newclosure(v, &Camera_scroll_to_wrapper, 0);
+ if(SQ_FAILED(sq_createslot(v, -3))) {
+ throw SquirrelError(v, "Couldn't register function 'scroll_to'");
+ }
+
+ if(SQ_FAILED(sq_createslot(v, -3))) {
+ throw SquirrelError(v, "Couldn't register class 'Camera'");
+ }
+
+ // Register class Level
+ sq_pushstring(v, "Level", -1);
+ if(sq_newclass(v, SQFalse) < 0) {
+ std::ostringstream msg;
+ msg << "Couldn't create new class 'Level'";
+ throw SquirrelError(v, msg.str());
+ }
+ sq_pushstring(v, "finish", -1);
+ sq_newclosure(v, &Level_finish_wrapper, 0);
+ if(SQ_FAILED(sq_createslot(v, -3))) {
+ throw SquirrelError(v, "Couldn't register function 'finish'");
+ }
+
+ sq_pushstring(v, "spawn", -1);
+ sq_newclosure(v, &Level_spawn_wrapper, 0);
+ if(SQ_FAILED(sq_createslot(v, -3))) {
+ throw SquirrelError(v, "Couldn't register function 'spawn'");
+ }
+
+ sq_pushstring(v, "flip_vertically", -1);
+ sq_newclosure(v, &Level_flip_vertically_wrapper, 0);
+ if(SQ_FAILED(sq_createslot(v, -3))) {
+ throw SquirrelError(v, "Couldn't register function 'flip_vertically'");
+ }
+
+ sq_pushstring(v, "toggle_pause", -1);
+ sq_newclosure(v, &Level_toggle_pause_wrapper, 0);
+ if(SQ_FAILED(sq_createslot(v, -3))) {
+ throw SquirrelError(v, "Couldn't register function 'toggle_pause'");
+ }
+
+ if(SQ_FAILED(sq_createslot(v, -3))) {
+ throw SquirrelError(v, "Couldn't register class 'Level'");
+ }
+
+ // Register class ScriptedObject
+ sq_pushstring(v, "ScriptedObject", -1);
+ if(sq_newclass(v, SQFalse) < 0) {
+ std::ostringstream msg;
+ msg << "Couldn't create new class 'ScriptedObject'";
+ throw SquirrelError(v, msg.str());
+ }
+ sq_pushstring(v, "set_action", -1);
+ sq_newclosure(v, &ScriptedObject_set_action_wrapper, 0);
+ if(SQ_FAILED(sq_createslot(v, -3))) {
+ throw SquirrelError(v, "Couldn't register function 'set_action'");
+ }
+
+ sq_pushstring(v, "get_action", -1);
+ sq_newclosure(v, &ScriptedObject_get_action_wrapper, 0);
+ if(SQ_FAILED(sq_createslot(v, -3))) {
+ throw SquirrelError(v, "Couldn't register function 'get_action'");
+ }
+
+ sq_pushstring(v, "move", -1);
+ sq_newclosure(v, &ScriptedObject_move_wrapper, 0);
+ if(SQ_FAILED(sq_createslot(v, -3))) {
+ throw SquirrelError(v, "Couldn't register function 'move'");
+ }
+
+ sq_pushstring(v, "set_pos", -1);
+ sq_newclosure(v, &ScriptedObject_set_pos_wrapper, 0);
+ if(SQ_FAILED(sq_createslot(v, -3))) {
+ throw SquirrelError(v, "Couldn't register function 'set_pos'");
+ }
+
+ sq_pushstring(v, "get_pos_x", -1);
+ sq_newclosure(v, &ScriptedObject_get_pos_x_wrapper, 0);
+ if(SQ_FAILED(sq_createslot(v, -3))) {
+ throw SquirrelError(v, "Couldn't register function 'get_pos_x'");
+ }
+
+ sq_pushstring(v, "get_pos_y", -1);
+ sq_newclosure(v, &ScriptedObject_get_pos_y_wrapper, 0);
+ if(SQ_FAILED(sq_createslot(v, -3))) {
+ throw SquirrelError(v, "Couldn't register function 'get_pos_y'");
+ }
+
+ sq_pushstring(v, "set_velocity", -1);
+ sq_newclosure(v, &ScriptedObject_set_velocity_wrapper, 0);
+ if(SQ_FAILED(sq_createslot(v, -3))) {
+ throw SquirrelError(v, "Couldn't register function 'set_velocity'");
+ }
+
+ sq_pushstring(v, "get_velocity_x", -1);
+ sq_newclosure(v, &ScriptedObject_get_velocity_x_wrapper, 0);
+ if(SQ_FAILED(sq_createslot(v, -3))) {
+ throw SquirrelError(v, "Couldn't register function 'get_velocity_x'");
+ }
+
+ sq_pushstring(v, "get_velocity_y", -1);
+ sq_newclosure(v, &ScriptedObject_get_velocity_y_wrapper, 0);
+ if(SQ_FAILED(sq_createslot(v, -3))) {
+ throw SquirrelError(v, "Couldn't register function 'get_velocity_y'");
+ }
+
+ sq_pushstring(v, "set_visible", -1);
+ sq_newclosure(v, &ScriptedObject_set_visible_wrapper, 0);
+ if(SQ_FAILED(sq_createslot(v, -3))) {
+ throw SquirrelError(v, "Couldn't register function 'set_visible'");
+ }
+
+ sq_pushstring(v, "is_visible", -1);
+ sq_newclosure(v, &ScriptedObject_is_visible_wrapper, 0);
+ if(SQ_FAILED(sq_createslot(v, -3))) {
+ throw SquirrelError(v, "Couldn't register function 'is_visible'");
+ }
+
+ sq_pushstring(v, "set_solid", -1);
+ sq_newclosure(v, &ScriptedObject_set_solid_wrapper, 0);
+ if(SQ_FAILED(sq_createslot(v, -3))) {
+ throw SquirrelError(v, "Couldn't register function 'set_solid'");
+ }
+
+ sq_pushstring(v, "is_solid", -1);
+ sq_newclosure(v, &ScriptedObject_is_solid_wrapper, 0);
+ if(SQ_FAILED(sq_createslot(v, -3))) {
+ throw SquirrelError(v, "Couldn't register function 'is_solid'");
+ }
+
+ sq_pushstring(v, "get_name", -1);
+ sq_newclosure(v, &ScriptedObject_get_name_wrapper, 0);
+ if(SQ_FAILED(sq_createslot(v, -3))) {
+ throw SquirrelError(v, "Couldn't register function 'get_name'");
+ }
+
+ if(SQ_FAILED(sq_createslot(v, -3))) {
+ throw SquirrelError(v, "Couldn't register class 'ScriptedObject'");
+ }
+
+ // Register class Text
+ sq_pushstring(v, "Text", -1);
+ if(sq_newclass(v, SQFalse) < 0) {
+ std::ostringstream msg;
+ msg << "Couldn't create new class 'Text'";
+ throw SquirrelError(v, msg.str());
+ }
+ sq_pushstring(v, "set_text", -1);
+ sq_newclosure(v, &Text_set_text_wrapper, 0);
+ if(SQ_FAILED(sq_createslot(v, -3))) {
+ throw SquirrelError(v, "Couldn't register function 'set_text'");
+ }
+
+ sq_pushstring(v, "set_font", -1);
+ sq_newclosure(v, &Text_set_font_wrapper, 0);
+ if(SQ_FAILED(sq_createslot(v, -3))) {
+ throw SquirrelError(v, "Couldn't register function 'set_font'");
+ }
+
+ sq_pushstring(v, "fade_in", -1);
+ sq_newclosure(v, &Text_fade_in_wrapper, 0);
+ if(SQ_FAILED(sq_createslot(v, -3))) {
+ throw SquirrelError(v, "Couldn't register function 'fade_in'");
+ }
+
+ sq_pushstring(v, "fade_out", -1);
+ sq_newclosure(v, &Text_fade_out_wrapper, 0);
+ if(SQ_FAILED(sq_createslot(v, -3))) {
+ throw SquirrelError(v, "Couldn't register function 'fade_out'");
+ }
+
+ sq_pushstring(v, "set_visible", -1);
+ sq_newclosure(v, &Text_set_visible_wrapper, 0);
+ if(SQ_FAILED(sq_createslot(v, -3))) {
+ throw SquirrelError(v, "Couldn't register function 'set_visible'");
+ }
+
+ sq_pushstring(v, "set_centered", -1);
+ sq_newclosure(v, &Text_set_centered_wrapper, 0);
+ if(SQ_FAILED(sq_createslot(v, -3))) {
+ throw SquirrelError(v, "Couldn't register function 'set_centered'");
+ }
+
+ sq_pushstring(v, "set_pos", -1);
+ sq_newclosure(v, &Text_set_pos_wrapper, 0);
+ if(SQ_FAILED(sq_createslot(v, -3))) {
+ throw SquirrelError(v, "Couldn't register function 'set_pos'");
+ }
+
+ sq_pushstring(v, "get_pos_x", -1);
+ sq_newclosure(v, &Text_get_pos_x_wrapper, 0);
+ if(SQ_FAILED(sq_createslot(v, -3))) {
+ throw SquirrelError(v, "Couldn't register function 'get_pos_x'");
+ }
+
+ sq_pushstring(v, "get_pos_y", -1);
+ sq_newclosure(v, &Text_get_pos_y_wrapper, 0);
+ if(SQ_FAILED(sq_createslot(v, -3))) {
+ throw SquirrelError(v, "Couldn't register function 'get_pos_y'");
+ }
+
+ sq_pushstring(v, "set_anchor_point", -1);
+ sq_newclosure(v, &Text_set_anchor_point_wrapper, 0);
+ if(SQ_FAILED(sq_createslot(v, -3))) {
+ throw SquirrelError(v, "Couldn't register function 'set_anchor_point'");
+ }
+
+ sq_pushstring(v, "get_anchor_point", -1);
+ sq_newclosure(v, &Text_get_anchor_point_wrapper, 0);
+ if(SQ_FAILED(sq_createslot(v, -3))) {
+ throw SquirrelError(v, "Couldn't register function 'get_anchor_point'");
+ }
+
+ if(SQ_FAILED(sq_createslot(v, -3))) {
+ throw SquirrelError(v, "Couldn't register class 'Text'");
+ }
+
+ // Register class Player
+ sq_pushstring(v, "Player", -1);
+ if(sq_newclass(v, SQFalse) < 0) {
+ std::ostringstream msg;
+ msg << "Couldn't create new class 'Player'";
+ throw SquirrelError(v, msg.str());
+ }
+ sq_pushstring(v, "add_bonus", -1);
+ sq_newclosure(v, &Player_add_bonus_wrapper, 0);
+ if(SQ_FAILED(sq_createslot(v, -3))) {
+ throw SquirrelError(v, "Couldn't register function 'add_bonus'");
+ }
+
+ sq_pushstring(v, "add_coins", -1);
+ sq_newclosure(v, &Player_add_coins_wrapper, 0);
+ if(SQ_FAILED(sq_createslot(v, -3))) {
+ throw SquirrelError(v, "Couldn't register function 'add_coins'");
+ }
+
+ sq_pushstring(v, "make_invincible", -1);
+ sq_newclosure(v, &Player_make_invincible_wrapper, 0);
+ if(SQ_FAILED(sq_createslot(v, -3))) {
+ throw SquirrelError(v, "Couldn't register function 'make_invincible'");
+ }
+
+ sq_pushstring(v, "deactivate", -1);
+ sq_newclosure(v, &Player_deactivate_wrapper, 0);
+ if(SQ_FAILED(sq_createslot(v, -3))) {
+ throw SquirrelError(v, "Couldn't register function 'deactivate'");
+ }
+
+ sq_pushstring(v, "activate", -1);
+ sq_newclosure(v, &Player_activate_wrapper, 0);
+ if(SQ_FAILED(sq_createslot(v, -3))) {
+ throw SquirrelError(v, "Couldn't register function 'activate'");
+ }
+
+ sq_pushstring(v, "walk", -1);
+ sq_newclosure(v, &Player_walk_wrapper, 0);
+ if(SQ_FAILED(sq_createslot(v, -3))) {
+ throw SquirrelError(v, "Couldn't register function 'walk'");
+ }
+
+ sq_pushstring(v, "set_visible", -1);
+ sq_newclosure(v, &Player_set_visible_wrapper, 0);
+ if(SQ_FAILED(sq_createslot(v, -3))) {
+ throw SquirrelError(v, "Couldn't register function 'set_visible'");
+ }
+
+ sq_pushstring(v, "get_visible", -1);
+ sq_newclosure(v, &Player_get_visible_wrapper, 0);
+ if(SQ_FAILED(sq_createslot(v, -3))) {
+ throw SquirrelError(v, "Couldn't register function 'get_visible'");
+ }
+
+ sq_pushstring(v, "kill", -1);
+ sq_newclosure(v, &Player_kill_wrapper, 0);
+ if(SQ_FAILED(sq_createslot(v, -3))) {
+ throw SquirrelError(v, "Couldn't register function 'kill'");
+ }
+
+ sq_pushstring(v, "set_ghost_mode", -1);
+ sq_newclosure(v, &Player_set_ghost_mode_wrapper, 0);
+ if(SQ_FAILED(sq_createslot(v, -3))) {
+ throw SquirrelError(v, "Couldn't register function 'set_ghost_mode'");
+ }
+
+ sq_pushstring(v, "get_ghost_mode", -1);
+ sq_newclosure(v, &Player_get_ghost_mode_wrapper, 0);
+ if(SQ_FAILED(sq_createslot(v, -3))) {
+ throw SquirrelError(v, "Couldn't register function 'get_ghost_mode'");
+ }
+
+ sq_pushstring(v, "do_cheer", -1);
+ sq_newclosure(v, &Player_do_cheer_wrapper, 0);
+ if(SQ_FAILED(sq_createslot(v, -3))) {
+ throw SquirrelError(v, "Couldn't register function 'do_cheer'");
+ }
+
+ sq_pushstring(v, "do_duck", -1);
+ sq_newclosure(v, &Player_do_duck_wrapper, 0);
+ if(SQ_FAILED(sq_createslot(v, -3))) {
+ throw SquirrelError(v, "Couldn't register function 'do_duck'");
+ }
+
+ sq_pushstring(v, "do_standup", -1);
+ sq_newclosure(v, &Player_do_standup_wrapper, 0);
+ if(SQ_FAILED(sq_createslot(v, -3))) {
+ throw SquirrelError(v, "Couldn't register function 'do_standup'");
+ }
+
+ sq_pushstring(v, "do_backflip", -1);
+ sq_newclosure(v, &Player_do_backflip_wrapper, 0);
+ if(SQ_FAILED(sq_createslot(v, -3))) {
+ throw SquirrelError(v, "Couldn't register function 'do_backflip'");
+ }
+
+ sq_pushstring(v, "do_jump", -1);
+ sq_newclosure(v, &Player_do_jump_wrapper, 0);
+ if(SQ_FAILED(sq_createslot(v, -3))) {
+ throw SquirrelError(v, "Couldn't register function 'do_jump'");
+ }
+
+ sq_pushstring(v, "trigger_sequence", -1);
+ sq_newclosure(v, &Player_trigger_sequence_wrapper, 0);
+ if(SQ_FAILED(sq_createslot(v, -3))) {
+ throw SquirrelError(v, "Couldn't register function 'trigger_sequence'");
+ }
+
+ if(SQ_FAILED(sq_createslot(v, -3))) {
+ throw SquirrelError(v, "Couldn't register class 'Player'");
+ }
+
+ // Register class FloatingImage
+ sq_pushstring(v, "FloatingImage", -1);
+ if(sq_newclass(v, SQFalse) < 0) {
+ std::ostringstream msg;
+ msg << "Couldn't create new class 'FloatingImage'";
+ throw SquirrelError(v, msg.str());
+ }
+ sq_pushstring(v, "constructor", -1);
+ sq_newclosure(v, &FloatingImage_constructor_wrapper, 0);
+ if(SQ_FAILED(sq_createslot(v, -3))) {
+ throw SquirrelError(v, "Couldn't register function 'constructor'");
+ }
+
+ sq_pushstring(v, "set_layer", -1);
+ sq_newclosure(v, &FloatingImage_set_layer_wrapper, 0);
+ if(SQ_FAILED(sq_createslot(v, -3))) {
+ throw SquirrelError(v, "Couldn't register function 'set_layer'");
+ }
+
+ sq_pushstring(v, "get_layer", -1);
+ sq_newclosure(v, &FloatingImage_get_layer_wrapper, 0);
+ if(SQ_FAILED(sq_createslot(v, -3))) {
+ throw SquirrelError(v, "Couldn't register function 'get_layer'");
+ }
+
+ sq_pushstring(v, "set_pos", -1);
+ sq_newclosure(v, &FloatingImage_set_pos_wrapper, 0);
+ if(SQ_FAILED(sq_createslot(v, -3))) {
+ throw SquirrelError(v, "Couldn't register function 'set_pos'");
+ }
+
+ sq_pushstring(v, "get_pos_x", -1);
+ sq_newclosure(v, &FloatingImage_get_pos_x_wrapper, 0);
+ if(SQ_FAILED(sq_createslot(v, -3))) {
+ throw SquirrelError(v, "Couldn't register function 'get_pos_x'");
+ }
+
+ sq_pushstring(v, "get_pos_y", -1);
+ sq_newclosure(v, &FloatingImage_get_pos_y_wrapper, 0);
+ if(SQ_FAILED(sq_createslot(v, -3))) {
+ throw SquirrelError(v, "Couldn't register function 'get_pos_y'");
+ }
+
+ sq_pushstring(v, "set_anchor_point", -1);
+ sq_newclosure(v, &FloatingImage_set_anchor_point_wrapper, 0);
+ if(SQ_FAILED(sq_createslot(v, -3))) {
+ throw SquirrelError(v, "Couldn't register function 'set_anchor_point'");
+ }
+
+ sq_pushstring(v, "get_anchor_point", -1);
+ sq_newclosure(v, &FloatingImage_get_anchor_point_wrapper, 0);
+ if(SQ_FAILED(sq_createslot(v, -3))) {
+ throw SquirrelError(v, "Couldn't register function 'get_anchor_point'");
+ }
+
+ sq_pushstring(v, "set_visible", -1);
+ sq_newclosure(v, &FloatingImage_set_visible_wrapper, 0);
+ if(SQ_FAILED(sq_createslot(v, -3))) {
+ throw SquirrelError(v, "Couldn't register function 'set_visible'");
+ }
+
+ sq_pushstring(v, "get_visible", -1);
+ sq_newclosure(v, &FloatingImage_get_visible_wrapper, 0);
+ if(SQ_FAILED(sq_createslot(v, -3))) {
+ throw SquirrelError(v, "Couldn't register function 'get_visible'");
+ }
+
+ sq_pushstring(v, "set_action", -1);
+ sq_newclosure(v, &FloatingImage_set_action_wrapper, 0);
+ if(SQ_FAILED(sq_createslot(v, -3))) {
+ throw SquirrelError(v, "Couldn't register function 'set_action'");
+ }
+
+ sq_pushstring(v, "get_action", -1);
+ sq_newclosure(v, &FloatingImage_get_action_wrapper, 0);
+ if(SQ_FAILED(sq_createslot(v, -3))) {
+ throw SquirrelError(v, "Couldn't register function 'get_action'");
+ }
+
+ sq_pushstring(v, "fade_in", -1);
+ sq_newclosure(v, &FloatingImage_fade_in_wrapper, 0);
+ if(SQ_FAILED(sq_createslot(v, -3))) {
+ throw SquirrelError(v, "Couldn't register function 'fade_in'");
+ }
+
+ sq_pushstring(v, "fade_out", -1);
+ sq_newclosure(v, &FloatingImage_fade_out_wrapper, 0);
+ if(SQ_FAILED(sq_createslot(v, -3))) {
+ throw SquirrelError(v, "Couldn't register function 'fade_out'");
+ }
+
+ if(SQ_FAILED(sq_createslot(v, -3))) {
+ throw SquirrelError(v, "Couldn't register class 'FloatingImage'");
+ }
+
+ // Register class Platform
+ sq_pushstring(v, "Platform", -1);
+ if(sq_newclass(v, SQFalse) < 0) {
+ std::ostringstream msg;
+ msg << "Couldn't create new class 'Platform'";
+ throw SquirrelError(v, msg.str());
+ }
+ sq_pushstring(v, "goto_node", -1);
+ sq_newclosure(v, &Platform_goto_node_wrapper, 0);
+ if(SQ_FAILED(sq_createslot(v, -3))) {
+ throw SquirrelError(v, "Couldn't register function 'goto_node'");
+ }
+
+ sq_pushstring(v, "start_moving", -1);
+ sq_newclosure(v, &Platform_start_moving_wrapper, 0);
+ if(SQ_FAILED(sq_createslot(v, -3))) {
+ throw SquirrelError(v, "Couldn't register function 'start_moving'");
+ }
+
+ sq_pushstring(v, "stop_moving", -1);
+ sq_newclosure(v, &Platform_stop_moving_wrapper, 0);
+ if(SQ_FAILED(sq_createslot(v, -3))) {
+ throw SquirrelError(v, "Couldn't register function 'stop_moving'");
+ }
+
+ if(SQ_FAILED(sq_createslot(v, -3))) {
+ throw SquirrelError(v, "Couldn't register class 'Platform'");
+ }
+
+ // Register class Candle
+ sq_pushstring(v, "Candle", -1);
+ if(sq_newclass(v, SQFalse) < 0) {
+ std::ostringstream msg;
+ msg << "Couldn't create new class 'Candle'";
+ throw SquirrelError(v, msg.str());
+ }
+ sq_pushstring(v, "get_burning", -1);
+ sq_newclosure(v, &Candle_get_burning_wrapper, 0);
+ if(SQ_FAILED(sq_createslot(v, -3))) {
+ throw SquirrelError(v, "Couldn't register function 'get_burning'");
+ }
+
+ sq_pushstring(v, "set_burning", -1);
+ sq_newclosure(v, &Candle_set_burning_wrapper, 0);
+ if(SQ_FAILED(sq_createslot(v, -3))) {
+ throw SquirrelError(v, "Couldn't register function 'set_burning'");
+ }
+
+ if(SQ_FAILED(sq_createslot(v, -3))) {
+ throw SquirrelError(v, "Couldn't register class 'Candle'");
+ }
+
+ // Register class Wind
+ sq_pushstring(v, "Wind", -1);
+ if(sq_newclass(v, SQFalse) < 0) {
+ std::ostringstream msg;
+ msg << "Couldn't create new class 'Wind'";
+ throw SquirrelError(v, msg.str());
+ }
+ sq_pushstring(v, "start", -1);
+ sq_newclosure(v, &Wind_start_wrapper, 0);
+ if(SQ_FAILED(sq_createslot(v, -3))) {
+ throw SquirrelError(v, "Couldn't register function 'start'");
+ }
+
+ sq_pushstring(v, "stop", -1);
+ sq_newclosure(v, &Wind_stop_wrapper, 0);
+ if(SQ_FAILED(sq_createslot(v, -3))) {
+ throw SquirrelError(v, "Couldn't register function 'stop'");
+ }
+
+ if(SQ_FAILED(sq_createslot(v, -3))) {
+ throw SquirrelError(v, "Couldn't register class 'Wind'");
+ }
+
+ // Register class AmbientSound
+ sq_pushstring(v, "AmbientSound", -1);
+ if(sq_newclass(v, SQFalse) < 0) {
+ std::ostringstream msg;
+ msg << "Couldn't create new class 'AmbientSound'";
+ throw SquirrelError(v, msg.str());
+ }
+ sq_pushstring(v, "set_pos", -1);
+ sq_newclosure(v, &AmbientSound_set_pos_wrapper, 0);
+ if(SQ_FAILED(sq_createslot(v, -3))) {
+ throw SquirrelError(v, "Couldn't register function 'set_pos'");
+ }
+
+ sq_pushstring(v, "get_pos_x", -1);
+ sq_newclosure(v, &AmbientSound_get_pos_x_wrapper, 0);
+ if(SQ_FAILED(sq_createslot(v, -3))) {
+ throw SquirrelError(v, "Couldn't register function 'get_pos_x'");
+ }
+
+ sq_pushstring(v, "get_pos_y", -1);
+ sq_newclosure(v, &AmbientSound_get_pos_y_wrapper, 0);
+ if(SQ_FAILED(sq_createslot(v, -3))) {
+ throw SquirrelError(v, "Couldn't register function 'get_pos_y'");
+ }
+
+ if(SQ_FAILED(sq_createslot(v, -3))) {
+ throw SquirrelError(v, "Couldn't register class 'AmbientSound'");
+ }
+
+ // Register class Thunderstorm
+ sq_pushstring(v, "Thunderstorm", -1);
+ if(sq_newclass(v, SQFalse) < 0) {
+ std::ostringstream msg;
+ msg << "Couldn't create new class 'Thunderstorm'";
+ throw SquirrelError(v, msg.str());
+ }
+ sq_pushstring(v, "start", -1);
+ sq_newclosure(v, &Thunderstorm_start_wrapper, 0);
+ if(SQ_FAILED(sq_createslot(v, -3))) {
+ throw SquirrelError(v, "Couldn't register function 'start'");
+ }
+
+ sq_pushstring(v, "stop", -1);
+ sq_newclosure(v, &Thunderstorm_stop_wrapper, 0);
+ if(SQ_FAILED(sq_createslot(v, -3))) {
+ throw SquirrelError(v, "Couldn't register function 'stop'");
+ }
+
+ sq_pushstring(v, "thunder", -1);
+ sq_newclosure(v, &Thunderstorm_thunder_wrapper, 0);
+ if(SQ_FAILED(sq_createslot(v, -3))) {
+ throw SquirrelError(v, "Couldn't register function 'thunder'");
+ }
+
+ sq_pushstring(v, "lightning", -1);
+ sq_newclosure(v, &Thunderstorm_lightning_wrapper, 0);
+ if(SQ_FAILED(sq_createslot(v, -3))) {
+ throw SquirrelError(v, "Couldn't register function 'lightning'");
+ }
+
+ sq_pushstring(v, "flash", -1);
+ sq_newclosure(v, &Thunderstorm_flash_wrapper, 0);
+ if(SQ_FAILED(sq_createslot(v, -3))) {
+ throw SquirrelError(v, "Couldn't register function 'flash'");
+ }
+
+ sq_pushstring(v, "electrify", -1);
+ sq_newclosure(v, &Thunderstorm_electrify_wrapper, 0);
+ if(SQ_FAILED(sq_createslot(v, -3))) {
+ throw SquirrelError(v, "Couldn't register function 'electrify'");
+ }
+
+ if(SQ_FAILED(sq_createslot(v, -3))) {
+ throw SquirrelError(v, "Couldn't register class 'Thunderstorm'");
+ }
+
+ // Register class TileMap
+ sq_pushstring(v, "TileMap", -1);
+ if(sq_newclass(v, SQFalse) < 0) {
+ std::ostringstream msg;
+ msg << "Couldn't create new class 'TileMap'";
+ throw SquirrelError(v, msg.str());
+ }
+ sq_pushstring(v, "goto_node", -1);
+ sq_newclosure(v, &TileMap_goto_node_wrapper, 0);
+ if(SQ_FAILED(sq_createslot(v, -3))) {
+ throw SquirrelError(v, "Couldn't register function 'goto_node'");
+ }
+
+ sq_pushstring(v, "start_moving", -1);
+ sq_newclosure(v, &TileMap_start_moving_wrapper, 0);
+ if(SQ_FAILED(sq_createslot(v, -3))) {
+ throw SquirrelError(v, "Couldn't register function 'start_moving'");
+ }
+
+ sq_pushstring(v, "stop_moving", -1);
+ sq_newclosure(v, &TileMap_stop_moving_wrapper, 0);
+ if(SQ_FAILED(sq_createslot(v, -3))) {
+ throw SquirrelError(v, "Couldn't register function 'stop_moving'");
+ }
+
+ sq_pushstring(v, "fade", -1);
+ sq_newclosure(v, &TileMap_fade_wrapper, 0);
+ if(SQ_FAILED(sq_createslot(v, -3))) {
+ throw SquirrelError(v, "Couldn't register function 'fade'");
+ }
+
+ sq_pushstring(v, "set_alpha", -1);
+ sq_newclosure(v, &TileMap_set_alpha_wrapper, 0);
+ if(SQ_FAILED(sq_createslot(v, -3))) {
+ throw SquirrelError(v, "Couldn't register function 'set_alpha'");
+ }
+
+ sq_pushstring(v, "get_alpha", -1);
+ sq_newclosure(v, &TileMap_get_alpha_wrapper, 0);
+ if(SQ_FAILED(sq_createslot(v, -3))) {
+ throw SquirrelError(v, "Couldn't register function 'get_alpha'");
+ }
+
+ if(SQ_FAILED(sq_createslot(v, -3))) {
+ throw SquirrelError(v, "Couldn't register class 'TileMap'");
+ }
+
+ // Register class SSector
+ sq_pushstring(v, "SSector", -1);
+ if(sq_newclass(v, SQFalse) < 0) {
+ std::ostringstream msg;
+ msg << "Couldn't create new class 'SSector'";
+ throw SquirrelError(v, msg.str());
+ }
+ sq_pushstring(v, "set_ambient_light", -1);
+ sq_newclosure(v, &SSector_set_ambient_light_wrapper, 0);
+ if(SQ_FAILED(sq_createslot(v, -3))) {
+ throw SquirrelError(v, "Couldn't register function 'set_ambient_light'");
+ }
+
+ sq_pushstring(v, "get_ambient_red", -1);
+ sq_newclosure(v, &SSector_get_ambient_red_wrapper, 0);
+ if(SQ_FAILED(sq_createslot(v, -3))) {
+ throw SquirrelError(v, "Couldn't register function 'get_ambient_red'");
+ }
+
+ sq_pushstring(v, "get_ambient_green", -1);
+ sq_newclosure(v, &SSector_get_ambient_green_wrapper, 0);
+ if(SQ_FAILED(sq_createslot(v, -3))) {
+ throw SquirrelError(v, "Couldn't register function 'get_ambient_green'");
+ }
+
+ sq_pushstring(v, "get_ambient_blue", -1);
+ sq_newclosure(v, &SSector_get_ambient_blue_wrapper, 0);
+ if(SQ_FAILED(sq_createslot(v, -3))) {
+ throw SquirrelError(v, "Couldn't register function 'get_ambient_blue'");
+ }
+
+ if(SQ_FAILED(sq_createslot(v, -3))) {
+ throw SquirrelError(v, "Couldn't register class 'SSector'");
+ }
+
+ // Register class LevelTime
+ sq_pushstring(v, "LevelTime", -1);
+ if(sq_newclass(v, SQFalse) < 0) {
+ std::ostringstream msg;
+ msg << "Couldn't create new class 'LevelTime'";
+ throw SquirrelError(v, msg.str());
+ }
+ sq_pushstring(v, "start", -1);
+ sq_newclosure(v, &LevelTime_start_wrapper, 0);
+ if(SQ_FAILED(sq_createslot(v, -3))) {
+ throw SquirrelError(v, "Couldn't register function 'start'");
+ }
+
+ sq_pushstring(v, "stop", -1);
+ sq_newclosure(v, &LevelTime_stop_wrapper, 0);
+ if(SQ_FAILED(sq_createslot(v, -3))) {
+ throw SquirrelError(v, "Couldn't register function 'stop'");
+ }
+
+ sq_pushstring(v, "get_time", -1);
+ sq_newclosure(v, &LevelTime_get_time_wrapper, 0);
+ if(SQ_FAILED(sq_createslot(v, -3))) {
+ throw SquirrelError(v, "Couldn't register function 'get_time'");
+ }
+
+ sq_pushstring(v, "set_time", -1);
+ sq_newclosure(v, &LevelTime_set_time_wrapper, 0);
+ if(SQ_FAILED(sq_createslot(v, -3))) {
+ throw SquirrelError(v, "Couldn't register function 'set_time'");
+ }
+
+ if(SQ_FAILED(sq_createslot(v, -3))) {
+ throw SquirrelError(v, "Couldn't register class 'LevelTime'");
+ }
+
+ // Register class WillOWisp
+ sq_pushstring(v, "WillOWisp", -1);
+ if(sq_newclass(v, SQFalse) < 0) {
+ std::ostringstream msg;
+ msg << "Couldn't create new class 'WillOWisp'";
+ throw SquirrelError(v, msg.str());
+ }
+ sq_pushstring(v, "goto_node", -1);
+ sq_newclosure(v, &WillOWisp_goto_node_wrapper, 0);
+ if(SQ_FAILED(sq_createslot(v, -3))) {
+ throw SquirrelError(v, "Couldn't register function 'goto_node'");
+ }
+
+ sq_pushstring(v, "set_state", -1);
+ sq_newclosure(v, &WillOWisp_set_state_wrapper, 0);
+ if(SQ_FAILED(sq_createslot(v, -3))) {
+ throw SquirrelError(v, "Couldn't register function 'set_state'");
+ }
+
+ sq_pushstring(v, "start_moving", -1);
+ sq_newclosure(v, &WillOWisp_start_moving_wrapper, 0);
+ if(SQ_FAILED(sq_createslot(v, -3))) {
+ throw SquirrelError(v, "Couldn't register function 'start_moving'");
+ }
+
+ sq_pushstring(v, "stop_moving", -1);
+ sq_newclosure(v, &WillOWisp_stop_moving_wrapper, 0);
+ if(SQ_FAILED(sq_createslot(v, -3))) {
+ throw SquirrelError(v, "Couldn't register function 'stop_moving'");
+ }
+
+ if(SQ_FAILED(sq_createslot(v, -3))) {
+ throw SquirrelError(v, "Couldn't register class 'WillOWisp'");
+ }
+
+}
+
+} // end of namespace Scripting
--- /dev/null
+/**
+ * WARNING: This file is automatically generated from:
+ * 'src/scripting/wrapper.interface.hpp'
+ * DO NOT CHANGE
+ */
+#ifndef __supertux_WRAPPER_H__
+#define __supertux_WRAPPER_H__
+
+#include <squirrel.h>
+#include "wrapper.interface.hpp"
+
+namespace Scripting
+{
+
+void register_supertux_wrapper(HSQUIRRELVM v);
+
+void create_squirrel_instance(HSQUIRRELVM v, Scripting::DisplayEffect* object, bool setup_releasehook = false);
+void create_squirrel_instance(HSQUIRRELVM v, Scripting::Camera* object, bool setup_releasehook = false);
+void create_squirrel_instance(HSQUIRRELVM v, Scripting::Level* object, bool setup_releasehook = false);
+void create_squirrel_instance(HSQUIRRELVM v, Scripting::ScriptedObject* object, bool setup_releasehook = false);
+void create_squirrel_instance(HSQUIRRELVM v, Scripting::Text* object, bool setup_releasehook = false);
+void create_squirrel_instance(HSQUIRRELVM v, Scripting::Player* object, bool setup_releasehook = false);
+void create_squirrel_instance(HSQUIRRELVM v, Scripting::FloatingImage* object, bool setup_releasehook = false);
+void create_squirrel_instance(HSQUIRRELVM v, Scripting::Platform* object, bool setup_releasehook = false);
+void create_squirrel_instance(HSQUIRRELVM v, Scripting::Candle* object, bool setup_releasehook = false);
+void create_squirrel_instance(HSQUIRRELVM v, Scripting::Wind* object, bool setup_releasehook = false);
+void create_squirrel_instance(HSQUIRRELVM v, Scripting::AmbientSound* object, bool setup_releasehook = false);
+void create_squirrel_instance(HSQUIRRELVM v, Scripting::Thunderstorm* object, bool setup_releasehook = false);
+void create_squirrel_instance(HSQUIRRELVM v, Scripting::TileMap* object, bool setup_releasehook = false);
+void create_squirrel_instance(HSQUIRRELVM v, Scripting::SSector* object, bool setup_releasehook = false);
+void create_squirrel_instance(HSQUIRRELVM v, Scripting::LevelTime* object, bool setup_releasehook = false);
+void create_squirrel_instance(HSQUIRRELVM v, Scripting::WillOWisp* object, bool setup_releasehook = false);
+
+}
+
+#endif
--- /dev/null
+/* This file is processed by miniswig to produce the scripting API */
+#include "display_effect.hpp"
+#include "camera.hpp"
+#include "level.hpp"
+#include "scripted_object.hpp"
+#include "text.hpp"
+#include "functions.hpp"
+#include "player.hpp"
+#include "floating_image.hpp"
+#include "anchor_points.hpp"
+#include "platform.hpp"
+#include "candle.hpp"
+#include "wind.hpp"
+#include "ambient_sound.hpp"
+#include "thunderstorm.hpp"
+#include "tilemap.hpp"
+#include "ssector.hpp"
+#include "level_time.hpp"
+#include "willowisp.hpp"
--- /dev/null
+// $Id$
+//
+// SuperTux - A Jump'n Run
+// Copyright (C) 2006 Matthias Braun <matze@braunis.de>
+//
+// 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 <config.h>
+
+#include <memory>
+#include <algorithm>
+#include <stdexcept>
+#include <iostream>
+#include <fstream>
+#include <sstream>
+#include <stdexcept>
+#include <float.h>
+#include <math.h>
+#include <limits>
+#include <physfs.h>
+
+#include "sector.hpp"
+#include "object/player.hpp"
+#include "object/gameobjs.hpp"
+#include "object/camera.hpp"
+#include "object/background.hpp"
+#include "object/gradient.hpp"
+#include "object/particlesystem.hpp"
+#include "object/particlesystem_interactive.hpp"
+#include "object/tilemap.hpp"
+#include "lisp/parser.hpp"
+#include "lisp/lisp.hpp"
+#include "lisp/writer.hpp"
+#include "lisp/list_iterator.hpp"
+#include "tile.hpp"
+#include "audio/sound_manager.hpp"
+#include "game_session.hpp"
+#include "resources.hpp"
+#include "statistics.hpp"
+#include "object_factory.hpp"
+#include "collision.hpp"
+#include "spawn_point.hpp"
+#include "math/rect.hpp"
+#include "math/aatriangle.hpp"
+#include "object/coin.hpp"
+#include "object/block.hpp"
+#include "object/invisible_block.hpp"
+#include "object/light.hpp"
+#include "object/pulsing_light.hpp"
+#include "object/bullet.hpp"
+#include "object/text_object.hpp"
+#include "object/portable.hpp"
+#include "badguy/jumpy.hpp"
+#include "trigger/sequence_trigger.hpp"
+#include "player_status.hpp"
+#include "scripting/squirrel_util.hpp"
+#include "script_interface.hpp"
+#include "log.hpp"
+#include "main.hpp"
+
+Sector* Sector::_current = 0;
+
+bool Sector::show_collrects = false;
+bool Sector::draw_solids_only = false;
+
+Sector::Sector(Level* parent)
+ : level(parent), currentmusic(LEVEL_MUSIC),
+ ambient_light( 1.0f, 1.0f, 1.0f, 1.0f ), gravity(10.0), player(0), camera(0)
+{
+ add_object(new Player(player_status, "Tux"));
+ add_object(new DisplayEffect("Effect"));
+ add_object(new TextObject("Text"));
+
+ // create a new squirrel table for the sector
+ using namespace Scripting;
+
+ sq_collectgarbage(global_vm);
+
+ sq_newtable(global_vm);
+ sq_pushroottable(global_vm);
+ if(SQ_FAILED(sq_setdelegate(global_vm, -2)))
+ throw Scripting::SquirrelError(global_vm, "Couldn't set sector_table delegate");
+
+ sq_resetobject(§or_table);
+ if(SQ_FAILED(sq_getstackobj(global_vm, -1, §or_table)))
+ throw Scripting::SquirrelError(global_vm, "Couldn't get sector table");
+ sq_addref(global_vm, §or_table);
+ sq_pop(global_vm, 1);
+}
+
+Sector::~Sector()
+{
+ using namespace Scripting;
+
+ deactivate();
+
+ for(ScriptList::iterator i = scripts.begin();
+ i != scripts.end(); ++i) {
+ HSQOBJECT& object = *i;
+ sq_release(global_vm, &object);
+ }
+ sq_release(global_vm, §or_table);
+ sq_collectgarbage(global_vm);
+
+ update_game_objects();
+ assert(gameobjects_new.size() == 0);
+
+ for(GameObjects::iterator i = gameobjects.begin();
+ i != gameobjects.end(); ++i) {
+ GameObject* object = *i;
+ before_object_remove(object);
+ object->unref();
+ }
+
+ for(SpawnPoints::iterator i = spawnpoints.begin(); i != spawnpoints.end();
+ ++i)
+ delete *i;
+}
+
+Level*
+Sector::get_level()
+{
+ return level;
+}
+
+GameObject*
+Sector::parse_object(const std::string& name, const lisp::Lisp& reader)
+{
+ if(name == "camera") {
+ Camera* camera = new Camera(this, "Camera");
+ camera->parse(reader);
+ return camera;
+ } else if(name == "particles-snow") {
+ SnowParticleSystem* partsys = new SnowParticleSystem();
+ partsys->parse(reader);
+ return partsys;
+ } else if(name == "particles-rain") {
+ RainParticleSystem* partsys = new RainParticleSystem();
+ partsys->parse(reader);
+ return partsys;
+ } else if(name == "particles-comets") {
+ CometParticleSystem* partsys = new CometParticleSystem();
+ partsys->parse(reader);
+ return partsys;
+ } else if(name == "particles-ghosts") {
+ GhostParticleSystem* partsys = new GhostParticleSystem();
+ partsys->parse(reader);
+ return partsys;
+ } else if(name == "particles-clouds") {
+ CloudParticleSystem* partsys = new CloudParticleSystem();
+ partsys->parse(reader);
+ return partsys;
+ } else if(name == "money") { // for compatibility with old maps
+ return new Jumpy(reader);
+ }
+
+ try {
+ return create_object(name, reader);
+ } catch(std::exception& e) {
+ log_warning << e.what() << "" << std::endl;
+ }
+
+ return 0;
+}
+
+void
+Sector::parse(const lisp::Lisp& sector)
+{
+ bool has_background = false;
+ lisp::ListIterator iter(§or);
+ while(iter.next()) {
+ const std::string& token = iter.item();
+ if(token == "name") {
+ iter.value()->get(name);
+ } else if(token == "gravity") {
+ iter.value()->get(gravity);
+ } else if(token == "music") {
+ iter.value()->get(music);
+ } else if(token == "spawnpoint") {
+ SpawnPoint* sp = new SpawnPoint(iter.lisp());
+ spawnpoints.push_back(sp);
+ } else if(token == "init-script") {
+ iter.value()->get(init_script);
+ } else if(token == "ambient-light") {
+ std::vector<float> vColor;
+ sector.get_vector( "ambient-light", vColor );
+ if(vColor.size() < 3) {
+ log_warning << "(ambient-light) requires a color as argument" << std::endl;
+ } else {
+ ambient_light = Color( vColor );
+ }
+ } else {
+ GameObject* object = parse_object(token, *(iter.lisp()));
+ if(object) {
+ if(dynamic_cast<Background *>(object)) {
+ has_background = true;
+ } else if(dynamic_cast<Gradient *>(object)) {
+ has_background = true;
+ }
+ add_object(object);
+ }
+ }
+ }
+
+ if(!has_background) {
+ Gradient* gradient = new Gradient();
+ gradient->set_gradient(Color(0.3, 0.4, 0.75), Color(1, 1, 1));
+ add_object(gradient);
+ }
+
+ update_game_objects();
+
+ if(solid_tilemaps.size() < 1) log_warning << "sector '" << name << "' does not contain a solid tile layer." << std::endl;
+
+ fix_old_tiles();
+ if(!camera) {
+ log_warning << "sector '" << name << "' does not contain a camera." << std::endl;
+ update_game_objects();
+ add_object(new Camera(this, "Camera"));
+ }
+
+ update_game_objects();
+}
+
+void
+Sector::parse_old_format(const lisp::Lisp& reader)
+{
+ name = "main";
+ reader.get("gravity", gravity);
+
+ std::string backgroundimage;
+ if (reader.get("background", backgroundimage) && (backgroundimage != "")) {
+ if (backgroundimage == "arctis.png") backgroundimage = "arctis.jpg";
+ if (backgroundimage == "arctis2.jpg") backgroundimage = "arctis.jpg";
+ if (backgroundimage == "ocean.png") backgroundimage = "ocean.jpg";
+ backgroundimage = "images/background/" + backgroundimage;
+ if (!PHYSFS_exists(backgroundimage.c_str())) {
+ log_warning << "Background image \"" << backgroundimage << "\" not found. Ignoring." << std::endl;
+ backgroundimage = "";
+ }
+ }
+
+ float bgspeed = .5;
+ reader.get("bkgd_speed", bgspeed);
+ bgspeed /= 100;
+
+ Color bkgd_top, bkgd_bottom;
+ int r = 0, g = 0, b = 128;
+ reader.get("bkgd_red_top", r);
+ reader.get("bkgd_green_top", g);
+ reader.get("bkgd_blue_top", b);
+ bkgd_top.red = static_cast<float> (r) / 255.0f;
+ bkgd_top.green = static_cast<float> (g) / 255.0f;
+ bkgd_top.blue = static_cast<float> (b) / 255.0f;
+
+ reader.get("bkgd_red_bottom", r);
+ reader.get("bkgd_green_bottom", g);
+ reader.get("bkgd_blue_bottom", b);
+ bkgd_bottom.red = static_cast<float> (r) / 255.0f;
+ bkgd_bottom.green = static_cast<float> (g) / 255.0f;
+ bkgd_bottom.blue = static_cast<float> (b) / 255.0f;
+
+ if(backgroundimage != "") {
+ Background* background = new Background();
+ background->set_image(backgroundimage, bgspeed);
+ add_object(background);
+ } else {
+ Gradient* gradient = new Gradient();
+ gradient->set_gradient(bkgd_top, bkgd_bottom);
+ add_object(gradient);
+ }
+
+ std::string particlesystem;
+ reader.get("particle_system", particlesystem);
+ if(particlesystem == "clouds")
+ add_object(new CloudParticleSystem());
+ else if(particlesystem == "snow")
+ add_object(new SnowParticleSystem());
+ else if(particlesystem == "rain")
+ add_object(new RainParticleSystem());
+
+ Vector startpos(100, 170);
+ reader.get("start_pos_x", startpos.x);
+ reader.get("start_pos_y", startpos.y);
+
+ SpawnPoint* spawn = new SpawnPoint;
+ spawn->pos = startpos;
+ spawn->name = "main";
+ spawnpoints.push_back(spawn);
+
+ music = "chipdisko.ogg";
+ // skip reading music filename. It's all .ogg now, anyway
+ /*
+ reader.get("music", music);
+ */
+ music = "music/" + music;
+
+ int width = 30, height = 15;
+ reader.get("width", width);
+ reader.get("height", height);
+
+ std::vector<unsigned int> tiles;
+ if(reader.get_vector("interactive-tm", tiles)
+ || reader.get_vector("tilemap", tiles)) {
+ TileMap* tilemap = new TileMap();
+ tilemap->set(width, height, tiles, LAYER_TILES, true);
+
+ // replace tile id 112 (old invisible tile) with 1311 (new invisible tile)
+ for(size_t x=0; x < tilemap->get_width(); ++x) {
+ for(size_t y=0; y < tilemap->get_height(); ++y) {
+ const Tile* tile = tilemap->get_tile(x, y);
+ if(tile->getID() == 112) tilemap->change(x, y, 1311);
+ }
+ }
+
+ if (height < 19) tilemap->resize(width, 19);
+ add_object(tilemap);
+ }
+
+ if(reader.get_vector("background-tm", tiles)) {
+ TileMap* tilemap = new TileMap();
+ tilemap->set(width, height, tiles, LAYER_BACKGROUNDTILES, false);
+ if (height < 19) tilemap->resize(width, 19);
+ add_object(tilemap);
+ }
+
+ if(reader.get_vector("foreground-tm", tiles)) {
+ TileMap* tilemap = new TileMap();
+ tilemap->set(width, height, tiles, LAYER_FOREGROUNDTILES, false);
+
+ // fill additional space in foreground with tiles of ID 2035 (lightmap/black)
+ if (height < 19) tilemap->resize(width, 19, 2035);
+
+ add_object(tilemap);
+ }
+
+ // read reset-points (now spawn-points)
+ const lisp::Lisp* resetpoints = reader.get_lisp("reset-points");
+ if(resetpoints) {
+ lisp::ListIterator iter(resetpoints);
+ while(iter.next()) {
+ if(iter.item() == "point") {
+ Vector sp_pos;
+ if(reader.get("x", sp_pos.x) && reader.get("y", sp_pos.y))
+ {
+ SpawnPoint* sp = new SpawnPoint;
+ sp->name = "main";
+ sp->pos = sp_pos;
+ spawnpoints.push_back(sp);
+ }
+ } else {
+ log_warning << "Unknown token '" << iter.item() << "' in reset-points." << std::endl;
+ }
+ }
+ }
+
+ // read objects
+ const lisp::Lisp* objects = reader.get_lisp("objects");
+ if(objects) {
+ lisp::ListIterator iter(objects);
+ while(iter.next()) {
+ GameObject* object = parse_object(iter.item(), *(iter.lisp()));
+ if(object) {
+ add_object(object);
+ } else {
+ log_warning << "Unknown object '" << iter.item() << "' in level." << std::endl;
+ }
+ }
+ }
+
+ // add a camera
+ Camera* camera = new Camera(this, "Camera");
+ add_object(camera);
+
+ update_game_objects();
+
+ if(solid_tilemaps.size() < 1) log_warning << "sector '" << name << "' does not contain a solid tile layer." << std::endl;
+
+ fix_old_tiles();
+ update_game_objects();
+}
+
+void
+Sector::fix_old_tiles()
+{
+ for(std::list<TileMap*>::iterator i = solid_tilemaps.begin(); i != solid_tilemaps.end(); i++) {
+ TileMap* solids = *i;
+ for(size_t x=0; x < solids->get_width(); ++x) {
+ for(size_t y=0; y < solids->get_height(); ++y) {
+ const Tile* tile = solids->get_tile(x, y);
+ Vector pos(solids->get_x_offset() + x*32, solids->get_y_offset() + y*32);
+
+ if(tile->getID() == 112) {
+ add_object(new InvisibleBlock(pos));
+ solids->change(x, y, 0);
+ } else if(tile->getAttributes() & Tile::COIN) {
+ add_object(new Coin(pos));
+ solids->change(x, y, 0);
+ } else if(tile->getAttributes() & Tile::FULLBOX) {
+ add_object(new BonusBlock(pos, tile->getData()));
+ solids->change(x, y, 0);
+ } else if(tile->getAttributes() & Tile::BRICK) {
+ add_object(new Brick(pos, tile->getData()));
+ solids->change(x, y, 0);
+ } else if(tile->getAttributes() & Tile::GOAL) {
+ std::string sequence = tile->getData() == 0 ? "endsequence" : "stoptux";
+ add_object(new SequenceTrigger(pos, sequence));
+ solids->change(x, y, 0);
+ }
+ }
+ }
+ }
+
+ // add lights for special tiles
+ for(GameObjects::iterator i = gameobjects.begin(); i != gameobjects.end(); i++) {
+ TileMap* tm = dynamic_cast<TileMap*>(*i);
+ if (!tm) continue;
+ for(size_t x=0; x < tm->get_width(); ++x) {
+ for(size_t y=0; y < tm->get_height(); ++y) {
+ const Tile* tile = tm->get_tile(x, y);
+ Vector pos(tm->get_x_offset() + x*32, tm->get_y_offset() + y*32);
+ Vector center(pos.x + 16, pos.y + 16);
+
+ // torch
+ if (tile->getID() == 1517) {
+ float pseudo_rnd = (float)((int)pos.x % 10) / 10;
+ add_object(new PulsingLight(center, 1.0f + pseudo_rnd, 0.9f, 1.0f, Color(1.0f, 1.0f, 0.6f, 1.0f)));
+ }
+ // lava or lavaflow
+ if ((tile->getID() == 173) || (tile->getID() == 1700) || (tile->getID() == 1705) || (tile->getID() == 1706)) {
+ // space lights a bit
+ if (((tm->get_tile(x-1, y)->getID() != tm->get_tile(x,y)->getID())
+ && (tm->get_tile(x, y-1)->getID() != tm->get_tile(x,y)->getID()))
+ || ((x % 3 == 0) && (y % 3 == 0))) {
+ float pseudo_rnd = (float)((int)pos.x % 10) / 10;
+ add_object(new PulsingLight(center, 1.0f + pseudo_rnd, 0.8f, 1.0f, Color(1.0f, 0.3f, 0.0f, 1.0f)));
+ }
+ }
+
+ }
+ }
+ }
+
+
+}
+
+void
+Sector::write(lisp::Writer& writer)
+{
+ writer.write_string("name", name);
+ writer.write_float("gravity", gravity);
+ writer.write_string("music", music);
+
+ // write spawnpoints
+ for(SpawnPoints::iterator i = spawnpoints.begin(); i != spawnpoints.end();
+ ++i) {
+ SpawnPoint* spawn = *i;
+ writer.start_list("spawn-points");
+ writer.write_string("name", spawn->name);
+ writer.write_float("x", spawn->pos.x);
+ writer.write_float("y", spawn->pos.y);
+ writer.end_list("spawn-points");
+ }
+
+ // write objects
+ for(GameObjects::iterator i = gameobjects.begin();
+ i != gameobjects.end(); ++i) {
+ Serializable* serializable = dynamic_cast<Serializable*> (*i);
+ if(serializable)
+ serializable->write(writer);
+ }
+}
+
+HSQUIRRELVM
+Sector::run_script(std::istream& in, const std::string& sourcename)
+{
+ using namespace Scripting;
+
+ // garbage collect thread list
+ for(ScriptList::iterator i = scripts.begin();
+ i != scripts.end(); ) {
+ HSQOBJECT& object = *i;
+ HSQUIRRELVM vm = object_to_vm(object);
+
+ if(sq_getvmstate(vm) != SQ_VMSTATE_SUSPENDED) {
+ sq_release(global_vm, &object);
+ i = scripts.erase(i);
+ continue;
+ }
+
+ ++i;
+ }
+
+ HSQOBJECT object = create_thread(global_vm);
+ scripts.push_back(object);
+
+ HSQUIRRELVM vm = object_to_vm(object);
+
+ // set sector_table as roottable for the thread
+ sq_pushobject(vm, sector_table);
+ sq_setroottable(vm);
+
+ compile_and_run(vm, in, sourcename);
+
+ return vm;
+}
+
+void
+Sector::add_object(GameObject* object)
+{
+ // make sure the object isn't already in the list
+#ifdef DEBUG
+ for(GameObjects::iterator i = gameobjects.begin(); i != gameobjects.end();
+ ++i) {
+ if(*i == object) {
+ assert("object already added to sector" == 0);
+ }
+ }
+ for(GameObjects::iterator i = gameobjects_new.begin();
+ i != gameobjects_new.end(); ++i) {
+ if(*i == object) {
+ assert("object already added to sector" == 0);
+ }
+ }
+#endif
+
+ object->ref();
+ gameobjects_new.push_back(object);
+}
+
+void
+Sector::activate(const std::string& spawnpoint)
+{
+ SpawnPoint* sp = 0;
+ for(SpawnPoints::iterator i = spawnpoints.begin(); i != spawnpoints.end();
+ ++i) {
+ if((*i)->name == spawnpoint) {
+ sp = *i;
+ break;
+ }
+ }
+ if(!sp) {
+ log_warning << "Spawnpoint '" << spawnpoint << "' not found." << std::endl;
+ if(spawnpoint != "main") {
+ activate("main");
+ } else {
+ activate(Vector(0, 0));
+ }
+ } else {
+ activate(sp->pos);
+ }
+}
+
+void
+Sector::activate(const Vector& player_pos)
+{
+ if(_current != this) {
+ if(_current != NULL)
+ _current->deactivate();
+ _current = this;
+
+ // register sectortable as sector in scripting
+ HSQUIRRELVM vm = Scripting::global_vm;
+ sq_pushroottable(vm);
+ sq_pushstring(vm, "sector", -1);
+ sq_pushobject(vm, sector_table);
+ if(SQ_FAILED(sq_createslot(vm, -3)))
+ throw Scripting::SquirrelError(vm, "Couldn't set sector in roottable");
+ sq_pop(vm, 1);
+
+ for(GameObjects::iterator i = gameobjects.begin();
+ i != gameobjects.end(); ++i) {
+ GameObject* object = *i;
+
+ try_expose(object);
+ }
+ }
+ try_expose_me();
+
+ // spawn smalltux below spawnpoint
+ if (!player->is_big()) {
+ player->move(player_pos + Vector(0,32));
+ } else {
+ player->move(player_pos);
+ }
+
+ // spawning tux in the ground would kill him
+ if(!is_free_of_tiles(player->get_bbox())) {
+ log_warning << "Tried spawning Tux in solid matter. Compensating." << std::endl;
+ Vector npos = player->get_bbox().p1;
+ npos.y-=32;
+ player->move(npos);
+ }
+
+ camera->reset(player->get_pos());
+ update_game_objects();
+
+ // Run init script
+ if(init_script != "") {
+ std::istringstream in(init_script);
+ run_script(in, std::string("Sector(") + name + ") - init");
+ }
+}
+
+void
+Sector::deactivate()
+{
+ if(_current != this)
+ return;
+
+ // remove sector entry from global vm
+ HSQUIRRELVM vm = Scripting::global_vm;
+ sq_pushroottable(vm);
+ sq_pushstring(vm, "sector", -1);
+ if(SQ_FAILED(sq_deleteslot(vm, -2, SQFalse)))
+ throw Scripting::SquirrelError(vm, "Couldn't unset sector in roottable");
+ sq_pop(vm, 1);
+
+ for(GameObjects::iterator i = gameobjects.begin();
+ i != gameobjects.end(); ++i) {
+ GameObject* object = *i;
+
+ try_unexpose(object);
+ }
+
+ try_unexpose_me();
+ _current = NULL;
+}
+
+Rect
+Sector::get_active_region()
+{
+ return Rect(
+ camera->get_translation() - Vector(1600, 1200),
+ camera->get_translation() + Vector(1600, 1200) + Vector(SCREEN_WIDTH,SCREEN_HEIGHT));
+}
+
+void
+Sector::update(float elapsed_time)
+{
+ player->check_bounds(camera);
+
+ /* update objects */
+ for(GameObjects::iterator i = gameobjects.begin();
+ i != gameobjects.end(); ++i) {
+ GameObject* object = *i;
+ if(!object->is_valid())
+ continue;
+
+ object->update(elapsed_time);
+ }
+
+ /* Handle all possible collisions. */
+ handle_collisions();
+ update_game_objects();
+}
+
+void
+Sector::update_game_objects()
+{
+ /** cleanup marked objects */
+ for(std::vector<GameObject*>::iterator i = gameobjects.begin();
+ i != gameobjects.end(); /* nothing */) {
+ GameObject* object = *i;
+
+ if(object->is_valid()) {
+ ++i;
+ continue;
+ }
+
+ before_object_remove(object);
+
+ object->unref();
+ i = gameobjects.erase(i);
+ }
+
+ /* add newly created objects */
+ for(std::vector<GameObject*>::iterator i = gameobjects_new.begin();
+ i != gameobjects_new.end(); ++i)
+ {
+ GameObject* object = *i;
+
+ before_object_add(object);
+
+ gameobjects.push_back(object);
+ }
+ gameobjects_new.clear();
+
+ /* update solid_tilemaps list */
+ //FIXME: this could be more efficient
+ solid_tilemaps.clear();
+ for(std::vector<GameObject*>::iterator i = gameobjects.begin();
+ i != gameobjects.end(); ++i)
+ {
+ TileMap* tm = dynamic_cast<TileMap*>(*i);
+ if (!tm) continue;
+ if (tm->is_solid()) solid_tilemaps.push_back(tm);
+ }
+
+}
+
+bool
+Sector::before_object_add(GameObject* object)
+{
+ Bullet* bullet = dynamic_cast<Bullet*> (object);
+ if(bullet != NULL) {
+ bullets.push_back(bullet);
+ }
+
+ MovingObject* movingobject = dynamic_cast<MovingObject*> (object);
+ if(movingobject != NULL) {
+ moving_objects.push_back(movingobject);
+ }
+
+ Portable* portable = dynamic_cast<Portable*> (object);
+ if(portable != NULL) {
+ portables.push_back(portable);
+ }
+
+ TileMap* tilemap = dynamic_cast<TileMap*> (object);
+ if(tilemap != NULL && tilemap->is_solid()) {
+ solid_tilemaps.push_back(tilemap);
+ }
+
+ Camera* camera = dynamic_cast<Camera*> (object);
+ if(camera != NULL) {
+ if(this->camera != 0) {
+ log_warning << "Multiple cameras added. Ignoring" << std::endl;
+ return false;
+ }
+ this->camera = camera;
+ }
+
+ Player* player = dynamic_cast<Player*> (object);
+ if(player != NULL) {
+ if(this->player != 0) {
+ log_warning << "Multiple players added. Ignoring" << std::endl;
+ return false;
+ }
+ this->player = player;
+ }
+
+ UsesPhysic *physic_object = dynamic_cast<UsesPhysic *>(object);
+ if(physic_object)
+ {
+ physic_object->physic.set_gravity(gravity);
+ }
+
+
+ if(_current == this) {
+ try_expose(object);
+ }
+
+ return true;
+}
+
+void
+Sector::try_expose(GameObject* object)
+{
+ ScriptInterface* interface = dynamic_cast<ScriptInterface*> (object);
+ if(interface != NULL) {
+ HSQUIRRELVM vm = Scripting::global_vm;
+ sq_pushobject(vm, sector_table);
+ interface->expose(vm, -1);
+ sq_pop(vm, 1);
+ }
+}
+
+void
+Sector::try_expose_me()
+{
+ HSQUIRRELVM vm = Scripting::global_vm;
+ sq_pushobject(vm, sector_table);
+ Scripting::SSector* interface = static_cast<Scripting::SSector*> (this);
+ expose_object(vm, -1, interface, "settings", false);
+ sq_pop(vm, 1);
+}
+
+void
+Sector::before_object_remove(GameObject* object)
+{
+ Portable* portable = dynamic_cast<Portable*> (object);
+ if(portable != NULL) {
+ portables.erase(std::find(portables.begin(), portables.end(), portable));
+ }
+ Bullet* bullet = dynamic_cast<Bullet*> (object);
+ if(bullet != NULL) {
+ bullets.erase(std::find(bullets.begin(), bullets.end(), bullet));
+ }
+ MovingObject* moving_object = dynamic_cast<MovingObject*> (object);
+ if(moving_object != NULL) {
+ moving_objects.erase(
+ std::find(moving_objects.begin(), moving_objects.end(), moving_object));
+ }
+
+ if(_current == this)
+ try_unexpose(object);
+}
+
+void
+Sector::try_unexpose(GameObject* object)
+{
+ ScriptInterface* interface = dynamic_cast<ScriptInterface*> (object);
+ if(interface != NULL) {
+ HSQUIRRELVM vm = Scripting::global_vm;
+ SQInteger oldtop = sq_gettop(vm);
+ sq_pushobject(vm, sector_table);
+ try {
+ interface->unexpose(vm, -1);
+ } catch(std::exception& e) {
+ log_warning << "Couldn't unregister object: " << e.what() << std::endl;
+ }
+ sq_settop(vm, oldtop);
+ }
+}
+
+void
+Sector::try_unexpose_me()
+{
+ HSQUIRRELVM vm = Scripting::global_vm;
+ SQInteger oldtop = sq_gettop(vm);
+ sq_pushobject(vm, sector_table);
+ try {
+ Scripting::unexpose_object(vm, -1, "settings");
+ } catch(std::exception& e) {
+ log_warning << "Couldn't unregister object: " << e.what() << std::endl;
+ }
+ sq_settop(vm, oldtop);
+}
+void
+Sector::draw(DrawingContext& context)
+{
+ context.set_ambient_color( ambient_light );
+ context.push_transform();
+ context.set_translation(camera->get_translation());
+
+ for(GameObjects::iterator i = gameobjects.begin();
+ i != gameobjects.end(); ++i) {
+ GameObject* object = *i;
+ if(!object->is_valid())
+ continue;
+
+ if (draw_solids_only)
+ {
+ TileMap* tm = dynamic_cast<TileMap*>(object);
+ if (tm && !tm->is_solid())
+ continue;
+ }
+
+ object->draw(context);
+ }
+
+ if(show_collrects) {
+ Color col(0.2f, 0.2f, 0.2f, 0.7f);
+ for(MovingObjects::iterator i = moving_objects.begin();
+ i != moving_objects.end(); ++i) {
+ MovingObject* object = *i;
+ const Rect& rect = object->get_bbox();
+
+ context.draw_filled_rect(rect, col, LAYER_FOREGROUND1 + 10);
+ }
+ }
+
+ context.pop_transform();
+}
+
+/*-------------------------------------------------------------------------
+ * Collision Detection
+ *-------------------------------------------------------------------------*/
+
+static const float SHIFT_DELTA = 7.0f;
+
+/** r1 is supposed to be moving, r2 a solid object */
+void check_collisions(collision::Constraints* constraints,
+ const Vector& movement, const Rect& r1, const Rect& r2,
+ GameObject* object = NULL, MovingObject* other = NULL)
+{
+ if(!collision::intersects(r1, r2))
+ return;
+
+ MovingObject *moving_object = dynamic_cast<MovingObject*> (object);
+ CollisionHit dummy;
+ if(other != NULL && !other->collides(*object, dummy))
+ return;
+ if(moving_object != NULL && !moving_object->collides(*other, dummy))
+ return;
+
+ // calculate intersection
+ float itop = r1.get_bottom() - r2.get_top();
+ float ibottom = r2.get_bottom() - r1.get_top();
+ float ileft = r1.get_right() - r2.get_left();
+ float iright = r2.get_right() - r1.get_left();
+
+ if(fabsf(movement.y) > fabsf(movement.x)) {
+ if(ileft < SHIFT_DELTA) {
+ constraints->right = std::min(constraints->right, r2.get_left());
+ return;
+ } else if(iright < SHIFT_DELTA) {
+ constraints->left = std::max(constraints->left, r2.get_right());
+ return;
+ }
+ } else {
+ // shiftout bottom/top
+ if(itop < SHIFT_DELTA) {
+ constraints->bottom = std::min(constraints->bottom, r2.get_top());
+ return;
+ } else if(ibottom < SHIFT_DELTA) {
+ constraints->top = std::max(constraints->top, r2.get_bottom());
+ return;
+ }
+ }
+
+ if(other != NULL) {
+ HitResponse response = other->collision(*object, dummy);
+ if(response == PASSTHROUGH)
+ return;
+
+ if(other->get_movement() != Vector(0, 0)) {
+ // TODO what todo when we collide with 2 moving objects?!?
+ constraints->ground_movement = other->get_movement();
+ }
+ }
+
+ float vert_penetration = std::min(itop, ibottom);
+ float horiz_penetration = std::min(ileft, iright);
+ if(vert_penetration < horiz_penetration) {
+ if(itop < ibottom) {
+ constraints->bottom = std::min(constraints->bottom, r2.get_top());
+ constraints->hit.bottom = true;
+ } else {
+ constraints->top = std::max(constraints->top, r2.get_bottom());
+ constraints->hit.top = true;
+ }
+ } else {
+ if(ileft < iright) {
+ constraints->right = std::min(constraints->right, r2.get_left());
+ constraints->hit.right = true;
+ } else {
+ constraints->left = std::max(constraints->left, r2.get_right());
+ constraints->hit.left = true;
+ }
+ }
+}
+
+static const float DELTA = .001f;
+
+void
+Sector::collision_tilemap(collision::Constraints* constraints,
+ const Vector& movement, const Rect& dest) const
+{
+ // calculate rectangle where the object will move
+ float x1 = dest.get_left();
+ float x2 = dest.get_right();
+ float y1 = dest.get_top();
+ float y2 = dest.get_bottom();
+
+ for(std::list<TileMap*>::const_iterator i = solid_tilemaps.begin(); i != solid_tilemaps.end(); i++) {
+ TileMap* solids = *i;
+
+ // test with all tiles in this rectangle
+ int starttilex = int(x1 - solids->get_x_offset()) / 32;
+ int starttiley = int(y1 - solids->get_y_offset()) / 32;
+ int max_x = int(x2 - solids->get_x_offset());
+ int max_y = int(y2+1 - solids->get_y_offset());
+
+ for(int x = starttilex; x*32 < max_x; ++x) {
+ for(int y = starttiley; y*32 < max_y; ++y) {
+ const Tile* tile = solids->get_tile(x, y);
+ if(!tile)
+ continue;
+ // skip non-solid tiles
+ if((tile->getAttributes() & Tile::SOLID) == 0)
+ continue;
+ // only handle unisolid when the player is falling down and when he was
+ // above the tile before
+ if(tile->getAttributes() & Tile::UNISOLID) {
+ if(movement.y <= 0 || dest.get_bottom() - movement.y - SHIFT_DELTA > y*32)
+ continue;
+ }
+
+ if(tile->getAttributes() & Tile::SLOPE) { // slope tile
+ AATriangle triangle;
+ Vector p1(x*32 + solids->get_x_offset(), y*32 + solids->get_y_offset());
+ Vector p2((x+1)*32 + solids->get_x_offset(), (y+1)*32 + solids->get_y_offset());
+ triangle = AATriangle(p1, p2, tile->getData());
+
+ collision::rectangle_aatriangle(constraints, dest, triangle);
+ } else { // normal rectangular tile
+ Rect rect(x*32 + solids->get_x_offset(), y*32 + solids->get_y_offset(), (x+1)*32 + solids->get_x_offset(), (y+1)*32 + solids->get_y_offset());
+ check_collisions(constraints, movement, dest, rect);
+ }
+ }
+ }
+ }
+}
+
+uint32_t
+Sector::collision_tile_attributes(const Rect& dest) const
+{
+ float x1 = dest.p1.x;
+ float y1 = dest.p1.y;
+ float x2 = dest.p2.x;
+ float y2 = dest.p2.y;
+
+ uint32_t result = 0;
+ for(std::list<TileMap*>::const_iterator i = solid_tilemaps.begin(); i != solid_tilemaps.end(); i++) {
+ TileMap* solids = *i;
+
+ // test with all tiles in this rectangle
+ int starttilex = int(x1 - solids->get_x_offset()) / 32;
+ int starttiley = int(y1 - solids->get_y_offset()) / 32;
+ int max_x = int(x2 - solids->get_x_offset());
+ int max_y = int(y2+1 - solids->get_y_offset());
+
+ for(int x = starttilex; x*32 < max_x; ++x) {
+ for(int y = starttiley; y*32 < max_y; ++y) {
+ const Tile* tile = solids->get_tile(x, y);
+ if(!tile)
+ continue;
+ result |= tile->getAttributes();
+ }
+ }
+ }
+
+ return result;
+}
+
+/** fills in CollisionHit and Normal vector of 2 intersecting rectangle */
+static void get_hit_normal(const Rect& r1, const Rect& r2, CollisionHit& hit,
+ Vector& normal)
+{
+ float itop = r1.get_bottom() - r2.get_top();
+ float ibottom = r2.get_bottom() - r1.get_top();
+ float ileft = r1.get_right() - r2.get_left();
+ float iright = r2.get_right() - r1.get_left();
+
+ float vert_penetration = std::min(itop, ibottom);
+ float horiz_penetration = std::min(ileft, iright);
+ if(vert_penetration < horiz_penetration) {
+ if(itop < ibottom) {
+ hit.bottom = true;
+ normal.y = vert_penetration;
+ } else {
+ hit.top = true;
+ normal.y = -vert_penetration;
+ }
+ } else {
+ if(ileft < iright) {
+ hit.right = true;
+ normal.x = horiz_penetration;
+ } else {
+ hit.left = true;
+ normal.x = -horiz_penetration;
+ }
+ }
+}
+
+void
+Sector::collision_object(MovingObject* object1, MovingObject* object2) const
+{
+ using namespace collision;
+
+ const Rect& r1 = object1->dest;
+ const Rect& r2 = object2->dest;
+
+ CollisionHit hit;
+ if(intersects(object1->dest, object2->dest)) {
+ Vector normal;
+ get_hit_normal(r1, r2, hit, normal);
+
+ if(!object1->collides(*object2, hit))
+ return;
+ std::swap(hit.left, hit.right);
+ std::swap(hit.top, hit.bottom);
+ if(!object2->collides(*object1, hit))
+ return;
+ std::swap(hit.left, hit.right);
+ std::swap(hit.top, hit.bottom);
+
+ HitResponse response1 = object1->collision(*object2, hit);
+ std::swap(hit.left, hit.right);
+ std::swap(hit.top, hit.bottom);
+ HitResponse response2 = object2->collision(*object1, hit);
+ if(response1 == CONTINUE && response2 == CONTINUE) {
+ normal *= (0.5 + DELTA);
+ object1->dest.move(-normal);
+ object2->dest.move(normal);
+ } else if (response1 == CONTINUE && response2 == FORCE_MOVE) {
+ normal *= (1 + DELTA);
+ object1->dest.move(-normal);
+ } else if (response1 == FORCE_MOVE && response2 == CONTINUE) {
+ normal *= (1 + DELTA);
+ object2->dest.move(normal);
+ }
+ }
+}
+
+void
+Sector::collision_static(collision::Constraints* constraints,
+ const Vector& movement, const Rect& dest,
+ GameObject& object)
+{
+ collision_tilemap(constraints, movement, dest);
+
+ // collision with other (static) objects
+ for(MovingObjects::iterator i = moving_objects.begin();
+ i != moving_objects.end(); ++i) {
+ MovingObject* moving_object = *i;
+ if(moving_object->get_group() != COLGROUP_STATIC
+ && moving_object->get_group() != COLGROUP_MOVING_STATIC)
+ continue;
+ if(!moving_object->is_valid())
+ continue;
+
+ if(moving_object != &object)
+ check_collisions(constraints, movement, dest, moving_object->bbox,
+ &object, moving_object);
+ }
+}
+
+void
+Sector::collision_static_constrains(MovingObject& object)
+{
+ using namespace collision;
+ float infinity = (std::numeric_limits<float>::has_infinity ? std::numeric_limits<float>::infinity() : std::numeric_limits<float>::max());
+
+ Constraints constraints;
+ Vector movement = object.get_movement();
+ Rect& dest = object.dest;
+ float owidth = object.get_bbox().get_width();
+ float oheight = object.get_bbox().get_height();
+
+ for(int i = 0; i < 2; ++i) {
+ collision_static(&constraints, Vector(0, movement.y), dest, object);
+ if(!constraints.has_constraints())
+ break;
+
+ // apply calculated horizontal constraints
+ if(constraints.bottom < infinity) {
+ float height = constraints.bottom - constraints.top;
+ if(height < oheight) {
+ // we're crushed, but ignore this for now, we'll get this again
+ // later if we're really crushed or things will solve itself when
+ // looking at the vertical constraints
+ }
+ dest.p2.y = constraints.bottom - DELTA;
+ dest.p1.y = dest.p2.y - oheight;
+ } else if(constraints.top > -infinity) {
+ dest.p1.y = constraints.top + DELTA;
+ dest.p2.y = dest.p1.y + oheight;
+ }
+ }
+ if(constraints.has_constraints()) {
+ if(constraints.hit.bottom) {
+ dest.move(constraints.ground_movement);
+ }
+ if(constraints.hit.top || constraints.hit.bottom) {
+ constraints.hit.left = false;
+ constraints.hit.right = false;
+ object.collision_solid(constraints.hit);
+ }
+ }
+
+ constraints = Constraints();
+ for(int i = 0; i < 2; ++i) {
+ collision_static(&constraints, movement, dest, object);
+ if(!constraints.has_constraints())
+ break;
+
+ // apply calculated vertical constraints
+ if(constraints.right < infinity) {
+ float width = constraints.right - constraints.left;
+ if(width + SHIFT_DELTA < owidth) {
+#if 0
+ printf("Object %p crushed horizontally... L:%f R:%f\n", &object,
+ constraints.left, constraints.right);
+#endif
+ CollisionHit h;
+ h.left = true;
+ h.right = true;
+ h.crush = true;
+ object.collision_solid(h);
+ } else {
+ dest.p2.x = constraints.right - DELTA;
+ dest.p1.x = dest.p2.x - owidth;
+ }
+ } else if(constraints.left > -infinity) {
+ dest.p1.x = constraints.left + DELTA;
+ dest.p2.x = dest.p1.x + owidth;
+ }
+ }
+
+ if(constraints.has_constraints()) {
+ if( constraints.hit.left || constraints.hit.right
+ || constraints.hit.top || constraints.hit.bottom
+ || constraints.hit.crush )
+ object.collision_solid(constraints.hit);
+ }
+
+ // an extra pass to make sure we're not crushed horizontally
+ constraints = Constraints();
+ collision_static(&constraints, movement, dest, object);
+ if(constraints.bottom < infinity) {
+ float height = constraints.bottom - constraints.top;
+ if(height + SHIFT_DELTA < oheight) {
+#if 0
+ printf("Object %p crushed vertically...\n", &object);
+#endif
+ CollisionHit h;
+ h.top = true;
+ h.bottom = true;
+ h.crush = true;
+ object.collision_solid(h);
+ }
+ }
+}
+
+namespace {
+ const float MAX_SPEED = 16.0f;
+}
+
+void
+Sector::handle_collisions()
+{
+ using namespace collision;
+
+ // calculate destination positions of the objects
+ for(MovingObjects::iterator i = moving_objects.begin();
+ i != moving_objects.end(); ++i) {
+ MovingObject* moving_object = *i;
+ Vector mov = moving_object->get_movement();
+
+ // make sure movement is never faster than MAX_SPEED. Norm is pretty fat, so two addl. checks are done before.
+ if (((mov.x > MAX_SPEED * M_SQRT1_2) || (mov.y > MAX_SPEED * M_SQRT1_2)) && (mov.norm() > MAX_SPEED)) {
+ moving_object->movement = mov.unit() * MAX_SPEED;
+ //log_debug << "Temporarily reduced object's speed of " << mov.norm() << " to " << moving_object->movement.norm() << "." << std::endl;
+ }
+
+ moving_object->dest = moving_object->get_bbox();
+ moving_object->dest.move(moving_object->get_movement());
+ }
+
+ // part1: COLGROUP_MOVING vs COLGROUP_STATIC and tilemap
+ for(MovingObjects::iterator i = moving_objects.begin();
+ i != moving_objects.end(); ++i) {
+ MovingObject* moving_object = *i;
+ if((moving_object->get_group() != COLGROUP_MOVING
+ && moving_object->get_group() != COLGROUP_MOVING_STATIC
+ && moving_object->get_group() != COLGROUP_MOVING_ONLY_STATIC)
+ || !moving_object->is_valid())
+ continue;
+
+ collision_static_constrains(*moving_object);
+ }
+
+
+ // part2: COLGROUP_MOVING vs tile attributes
+ for(MovingObjects::iterator i = moving_objects.begin();
+ i != moving_objects.end(); ++i) {
+ MovingObject* moving_object = *i;
+ if((moving_object->get_group() != COLGROUP_MOVING
+ && moving_object->get_group() != COLGROUP_MOVING_STATIC
+ && moving_object->get_group() != COLGROUP_MOVING_ONLY_STATIC)
+ || !moving_object->is_valid())
+ continue;
+
+ uint32_t tile_attributes = collision_tile_attributes(moving_object->dest);
+ if(tile_attributes > Tile::FIRST_INTERESTING_FLAG) {
+ moving_object->collision_tile(tile_attributes);
+ }
+ }
+
+ // part2.5: COLGROUP_MOVING vs COLGROUP_TOUCHABLE
+ for(MovingObjects::iterator i = moving_objects.begin();
+ i != moving_objects.end(); ++i) {
+ MovingObject* moving_object = *i;
+ if((moving_object->get_group() != COLGROUP_MOVING
+ && moving_object->get_group() != COLGROUP_MOVING_STATIC)
+ || !moving_object->is_valid())
+ continue;
+
+ for(MovingObjects::iterator i2 = moving_objects.begin();
+ i2 != moving_objects.end(); ++i2) {
+ MovingObject* moving_object_2 = *i2;
+ if(moving_object_2->get_group() != COLGROUP_TOUCHABLE
+ || !moving_object_2->is_valid())
+ continue;
+
+ if(intersects(moving_object->dest, moving_object_2->dest)) {
+ Vector normal;
+ CollisionHit hit;
+ get_hit_normal(moving_object->dest, moving_object_2->dest,
+ hit, normal);
+ if(!moving_object->collides(*moving_object_2, hit))
+ continue;
+ if(!moving_object_2->collides(*moving_object, hit))
+ continue;
+
+ moving_object->collision(*moving_object_2, hit);
+ moving_object_2->collision(*moving_object, hit);
+ }
+ }
+ }
+
+ // part3: COLGROUP_MOVING vs COLGROUP_MOVING
+ for(MovingObjects::iterator i = moving_objects.begin();
+ i != moving_objects.end(); ++i) {
+ MovingObject* moving_object = *i;
+
+ if((moving_object->get_group() != COLGROUP_MOVING
+ && moving_object->get_group() != COLGROUP_MOVING_STATIC)
+ || !moving_object->is_valid())
+ continue;
+
+ for(MovingObjects::iterator i2 = i+1;
+ i2 != moving_objects.end(); ++i2) {
+ MovingObject* moving_object_2 = *i2;
+ if((moving_object_2->get_group() != COLGROUP_MOVING
+ && moving_object_2->get_group() != COLGROUP_MOVING_STATIC)
+ || !moving_object_2->is_valid())
+ continue;
+
+ collision_object(moving_object, moving_object_2);
+ }
+ }
+
+ // apply object movement
+ for(MovingObjects::iterator i = moving_objects.begin();
+ i != moving_objects.end(); ++i) {
+ MovingObject* moving_object = *i;
+
+ moving_object->bbox = moving_object->dest;
+ moving_object->movement = Vector(0, 0);
+ }
+}
+
+bool
+Sector::is_free_of_tiles(const Rect& rect, const bool ignoreUnisolid) const
+{
+ using namespace collision;
+
+ for(std::list<TileMap*>::const_iterator i = solid_tilemaps.begin(); i != solid_tilemaps.end(); i++) {
+ TileMap* solids = *i;
+
+ // test with all tiles in this rectangle
+ int starttilex = int(rect.p1.x - solids->get_x_offset()) / 32;
+ int starttiley = int(rect.p1.y - solids->get_y_offset()) / 32;
+ int max_x = int(rect.p2.x - solids->get_x_offset());
+ int max_y = int(rect.p2.y - solids->get_y_offset());
+
+ for(int x = starttilex; x*32 <= max_x; ++x) {
+ for(int y = starttiley; y*32 <= max_y; ++y) {
+ const Tile* tile = solids->get_tile(x, y);
+ if(!tile) continue;
+ if(tile->getAttributes() & Tile::SLOPE) {
+ AATriangle triangle;
+ Vector p1(x*32 + solids->get_x_offset(), y*32 + solids->get_y_offset());
+ Vector p2((x+1)*32 + solids->get_x_offset(), (y+1)*32 + solids->get_y_offset());
+ triangle = AATriangle(p1, p2, tile->getData());
+ Constraints constraints;
+ return collision::rectangle_aatriangle(&constraints, rect, triangle);
+ }
+ if((tile->getAttributes() & Tile::SOLID) && !ignoreUnisolid) return false;
+ if((tile->getAttributes() & Tile::SOLID) && !(tile->getAttributes() & Tile::UNISOLID)) return false;
+ }
+ }
+ }
+
+ return true;
+}
+
+bool
+Sector::is_free_of_statics(const Rect& rect, const MovingObject* ignore_object, const bool ignoreUnisolid) const
+{
+ using namespace collision;
+
+ if (!is_free_of_tiles(rect, ignoreUnisolid)) return false;
+
+ for(MovingObjects::const_iterator i = moving_objects.begin();
+ i != moving_objects.end(); ++i) {
+ const MovingObject* moving_object = *i;
+ if (moving_object == ignore_object) continue;
+ if (!moving_object->is_valid()) continue;
+ if (moving_object->get_group() == COLGROUP_STATIC) {
+ if(intersects(rect, moving_object->get_bbox())) return false;
+ }
+ }
+
+ return true;
+}
+
+bool
+Sector::is_free_of_movingstatics(const Rect& rect, const MovingObject* ignore_object) const
+{
+ using namespace collision;
+
+ if (!is_free_of_tiles(rect)) return false;
+
+ for(MovingObjects::const_iterator i = moving_objects.begin();
+ i != moving_objects.end(); ++i) {
+ const MovingObject* moving_object = *i;
+ if (moving_object == ignore_object) continue;
+ if (!moving_object->is_valid()) continue;
+ if ((moving_object->get_group() == COLGROUP_MOVING)
+ || (moving_object->get_group() == COLGROUP_MOVING_STATIC)
+ || (moving_object->get_group() == COLGROUP_STATIC)) {
+ if(intersects(rect, moving_object->get_bbox())) return false;
+ }
+ }
+
+ return true;
+}
+
+bool
+Sector::add_bullet(const Vector& pos, float xm, Direction dir)
+{
+ // TODO remove this function and move these checks elsewhere...
+
+ Bullet* new_bullet = 0;
+ if((player_status->bonus == FIRE_BONUS &&
+ (int)bullets.size() >= player_status->max_fire_bullets) ||
+ (player_status->bonus == ICE_BONUS &&
+ (int)bullets.size() >= player_status->max_ice_bullets))
+ return false;
+ new_bullet = new Bullet(pos, xm, dir, player_status->bonus);
+ add_object(new_bullet);
+
+ sound_manager->play("sounds/shoot.wav");
+
+ return true;
+}
+
+bool
+Sector::add_smoke_cloud(const Vector& pos)
+{
+ add_object(new SmokeCloud(pos));
+ return true;
+}
+
+void
+Sector::play_music(MusicType type)
+{
+ currentmusic = type;
+ switch(currentmusic) {
+ case LEVEL_MUSIC:
+ sound_manager->play_music(music);
+ break;
+ case HERRING_MUSIC:
+ sound_manager->play_music("music/salcon.ogg");
+ break;
+ case HERRING_WARNING_MUSIC:
+ sound_manager->stop_music(TUX_INVINCIBLE_TIME_WARNING);
+ break;
+ default:
+ sound_manager->play_music("");
+ break;
+ }
+}
+
+MusicType
+Sector::get_music_type()
+{
+ return currentmusic;
+}
+
+int
+Sector::get_total_badguys()
+{
+ int total_badguys = 0;
+ for(GameObjects::iterator i = gameobjects.begin();
+ i != gameobjects.end(); ++i) {
+ BadGuy* badguy = dynamic_cast<BadGuy*> (*i);
+ if (badguy && badguy->countMe)
+ total_badguys++;
+ }
+
+ return total_badguys;
+}
+
+bool
+Sector::inside(const Rect& rect) const
+{
+ for(std::list<TileMap*>::const_iterator i = solid_tilemaps.begin(); i != solid_tilemaps.end(); i++) {
+ TileMap* solids = *i;
+ bool horizontally = ((rect.p2.x >= 0 + solids->get_x_offset()) && (rect.p1.x <= solids->get_width() * 32 + solids->get_x_offset()));
+ bool vertically = (rect.p1.y <= solids->get_height() * 32 + solids->get_y_offset());
+
+ if (horizontally && vertically)
+ return true;
+ }
+ return false;
+}
+
+float
+Sector::get_width() const
+{
+ float width = 0;
+ for(std::list<TileMap*>::const_iterator i = solid_tilemaps.begin();
+ i != solid_tilemaps.end(); i++) {
+ TileMap* solids = *i;
+ if ((solids->get_width() * 32 + solids->get_x_offset()) > width) {
+ width = solids->get_width() * 32 + solids->get_x_offset();
+ }
+ }
+
+ return width;
+}
+
+float
+Sector::get_height() const
+{
+ float height = 0;
+ for(std::list<TileMap*>::const_iterator i = solid_tilemaps.begin();
+ i != solid_tilemaps.end(); i++) {
+ TileMap* solids = *i;
+ if ((solids->get_height() * 32 + solids->get_y_offset()) > height) {
+ height = solids->get_height() * 32 + solids->get_y_offset();
+ }
+ }
+
+ return height;
+}
+
+void
+Sector::change_solid_tiles(uint32_t old_tile_id, uint32_t new_tile_id)
+{
+ for(std::list<TileMap*>::const_iterator i = solid_tilemaps.begin(); i != solid_tilemaps.end(); i++) {
+ TileMap* solids = *i;
+ solids->change_all(old_tile_id, new_tile_id);
+ }
+}
+
+
+void
+Sector::set_ambient_light(float red, float green, float blue)
+{
+ ambient_light.red = red;
+ ambient_light.green = green;
+ ambient_light.blue = blue;
+}
+
+float
+Sector::get_ambient_red()
+{
+ return ambient_light.red;
+}
+
+float
+Sector::get_ambient_green()
+{
+ return ambient_light.green;
+}
+
+float
+Sector::get_ambient_blue()
+{
+ return ambient_light.blue;
+}
--- /dev/null
+// $Id$
+//
+// SuperTux - A Jump'n Run
+// Copyright (C) 2006 Matthias Braun <matze@braunis.de>
+//
+// 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_SECTOR_H
+#define SUPERTUX_SECTOR_H
+
+#include <vector>
+#include <list>
+#include <memory>
+#include <squirrel.h>
+
+#include "direction.hpp"
+#include "video/color.hpp"
+#include "scripting/ssector.hpp"
+
+namespace lisp {
+class Lisp;
+class Writer;
+}
+namespace collision {
+class Constraints;
+}
+
+class Vector;
+class Rect;
+class Sprite;
+class GameObject;
+class Player;
+class Camera;
+class TileMap;
+class Bullet;
+class ScriptInterpreter;
+class SpawnPoint;
+class MovingObject;
+class CollisionHit;
+class Level;
+class Portable;
+class DrawingContext;
+
+enum MusicType {
+ LEVEL_MUSIC,
+ HERRING_MUSIC,
+ HERRING_WARNING_MUSIC
+};
+
+/**
+ * This class holds a sector (a part of a level) and all the game objects in
+ * the sector
+ */
+class Sector : public Scripting::SSector
+{
+public:
+ Sector(Level* parent);
+ ~Sector();
+
+ /// get parent level
+ Level* get_level();
+
+ /// read sector from lisp file
+ void parse(const lisp::Lisp& lisp);
+ void parse_old_format(const lisp::Lisp& lisp);
+ /// write sector to lisp file
+ void write(lisp::Writer& writer);
+
+ /// activates this sector (change music, intialize player class, ...)
+ void activate(const std::string& spawnpoint);
+ void activate(const Vector& player_pos);
+ void deactivate();
+
+ void update(float elapsed_time);
+ void update_game_objects();
+
+ void draw(DrawingContext& context);
+
+ /**
+ * runs a script in the context of the sector (sector_table will be the
+ * roottable of this squirrel VM)
+ */
+ HSQUIRRELVM run_script(std::istream& in, const std::string& sourcename);
+
+ /// adds a gameobject
+ void add_object(GameObject* object);
+
+ void set_name(const std::string& name)
+ { this->name = name; }
+ const std::string& get_name() const
+ { return name; }
+
+ /**
+ * tests if a given rectangle is inside the sector
+ * (a rectangle that is on top of the sector is considered inside)
+ */
+ bool inside(const Rect& rectangle) const;
+
+ void play_music(MusicType musictype);
+ MusicType get_music_type();
+
+ bool add_bullet(const Vector& pos, float xm, Direction dir);
+ bool add_smoke_cloud(const Vector& pos);
+
+ /** get currently activated sector. */
+ static Sector* current()
+ { return _current; }
+
+ /** Get total number of badguys */
+ int get_total_badguys();
+
+ /** Get total number of GameObjects of given type */
+ template<class T> int get_total_count()
+ {
+ int total = 0;
+ for(GameObjects::iterator i = gameobjects.begin(); i != gameobjects.end(); ++i) {
+ if (dynamic_cast<T*>(*i)) total++;
+ }
+ return total;
+ }
+
+ void collision_tilemap(collision::Constraints* constraints,
+ const Vector& movement, const Rect& dest) const;
+
+ /**
+ * Checks if the specified rectangle is free of (solid) tiles.
+ * Note that this does not include static objects, e.g. bonus blocks.
+ */
+ bool is_free_of_tiles(const Rect& rect, const bool ignoreUnisolid = false) const;
+ /**
+ * Checks if the specified rectangle is free of both
+ * 1.) solid tiles and
+ * 2.) MovingObjects in COLGROUP_STATIC.
+ * Note that this does not include badguys or players.
+ */
+ bool is_free_of_statics(const Rect& rect, const MovingObject* ignore_object = 0, const bool ignoreUnisolid = false) const;
+ /**
+ * Checks if the specified rectangle is free of both
+ * 1.) solid tiles and
+ * 2.) MovingObjects in COLGROUP_STATIC, COLGROUP_MOVINGSTATIC or COLGROUP_MOVING.
+ * This includes badguys and players.
+ */
+ bool is_free_of_movingstatics(const Rect& rect, const MovingObject* ignore_object = 0) const;
+
+ /**
+ * returns a list of players currently in the sector
+ */
+ std::vector<Player*> get_players() {
+ return std::vector<Player*>(1, this->player);
+ }
+
+ Rect get_active_region();
+
+ /**
+ * returns the width (in px) of a sector)
+ */
+ float get_width() const;
+
+ /**
+ * returns the height (in px) of a sector)
+ */
+ float get_height() const;
+
+ /**
+ * globally changes solid tilemaps' tile ids
+ */
+ void change_solid_tiles(uint32_t old_tile_id, uint32_t new_tile_id);
+
+ typedef std::vector<GameObject*> GameObjects;
+ typedef std::vector<MovingObject*> MovingObjects;
+ typedef std::vector<SpawnPoint*> SpawnPoints;
+ typedef std::vector<Portable*> Portables;
+
+ // --- Scripting ---
+ /**
+ * get/set color of ambient light
+ */
+ void set_ambient_light(float red, float green, float blue);
+ float get_ambient_red();
+ float get_ambient_green();
+ float get_ambient_blue();
+
+private:
+ Level* level; /**< Parent level containing this sector */
+ uint32_t collision_tile_attributes(const Rect& dest) const;
+
+ void before_object_remove(GameObject* object);
+ bool before_object_add(GameObject* object);
+
+ void try_expose(GameObject* object);
+ void try_unexpose(GameObject* object);
+ void try_expose_me();
+ void try_unexpose_me();
+
+ /** Checks for all possible collisions. And calls the
+ collision_handlers, which the collision_objects provide for this
+ case (or not). */
+ void handle_collisions();
+
+ /**
+ * Does collision detection between 2 objects and does instant
+ * collision response handling in case of a collision
+ */
+ void collision_object(MovingObject* object1, MovingObject* object2) const;
+
+ /**
+ * Does collision detection of an object against all other static
+ * objects (and the tilemap) in the level. Collision response is done
+ * for the first hit in time. (other hits get ignored, the function
+ * should be called repeatedly to resolve those)
+ *
+ * returns true if the collision detection should be aborted for this object
+ * (because of ABORT_MOVE in the collision response or no collisions)
+ */
+ void collision_static(collision::Constraints* constraints,
+ const Vector& movement, const Rect& dest, GameObject& object);
+
+ void collision_static_constrains(MovingObject& object);
+
+ GameObject* parse_object(const std::string& name, const lisp::Lisp& lisp);
+
+ void fix_old_tiles();
+
+ static Sector* _current;
+
+ std::string name;
+
+ std::vector<Bullet*> bullets;
+
+ std::string init_script;
+
+ /// container for newly created objects, they'll be added in Sector::update
+ GameObjects gameobjects_new;
+
+ MusicType currentmusic;
+
+ HSQOBJECT sector_table;
+ /// sector scripts
+ typedef std::vector<HSQOBJECT> ScriptList;
+ ScriptList scripts;
+
+ Color ambient_light;
+
+public: // TODO make this private again
+ /// show collision rectangles of moving objects (for debugging)
+ static bool show_collrects;
+ static bool draw_solids_only;
+
+ GameObjects gameobjects;
+ MovingObjects moving_objects;
+ SpawnPoints spawnpoints;
+ Portables portables;
+
+ std::string music;
+ float gravity;
+
+ // some special objects, where we need direct access
+ // (try to avoid accessing them directly)
+ Player* player;
+ std::list<TileMap*> solid_tilemaps;
+ Camera* camera;
+};
+
+#endif
--- /dev/null
+// $Id$
+//
+// SuperTux - A Jump'n Run
+// Copyright (C) 2006 Matthias Braun <matze@braunis.de>
+//
+// 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_SERIALIZABLE_H
+#define SUPERTUX_SERIALIZABLE_H
+
+namespace lisp { class Writer; }
+
+class Serializable
+{
+public:
+ virtual ~Serializable()
+ { }
+
+ virtual void write(lisp::Writer& writer) = 0;
+};
+
+#endif /*SUPERTUX_SERIALIZABLE_H*/
--- /dev/null
+// $Id$
+//
+// SuperTux
+// Copyright (C) 2006 Matthias Braun <matze@braunis.de>
+//
+// 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 <config.h>
+
+#include "shrinkfade.hpp"
+#include "main.hpp"
+#include "video/drawing_context.hpp"
+
+ShrinkFade::ShrinkFade(const Vector& dest, float fade_time)
+ : dest(dest), fade_time(fade_time), accum_time(0)
+{
+ speedleft = dest.x / fade_time;
+ speedright = (SCREEN_WIDTH - dest.x) / fade_time;
+ speedtop = dest.y / fade_time;
+ speedbottom = (SCREEN_HEIGHT - dest.y) / fade_time;
+}
+
+ShrinkFade::~ShrinkFade()
+{
+}
+
+void
+ShrinkFade::update(float elapsed_time)
+{
+ accum_time += elapsed_time;
+ if(accum_time > fade_time)
+ accum_time = fade_time;
+}
+
+void
+ShrinkFade::draw(DrawingContext& context)
+{
+ Color black(0, 0, 0);
+ float left = speedleft * accum_time;
+ float top = speedtop * accum_time;
+ float right = SCREEN_WIDTH - speedright * accum_time;
+ float bottom = SCREEN_HEIGHT - speedbottom * accum_time;
+
+ context.draw_filled_rect(Vector(0, 0),
+ Vector(left, SCREEN_HEIGHT),
+ black, LAYER_GUI+1);
+ context.draw_filled_rect(Vector(0, 0),
+ Vector(SCREEN_WIDTH, top),
+ black, LAYER_GUI+1);
+ context.draw_filled_rect(Vector(right, 0),
+ Vector(SCREEN_WIDTH, SCREEN_HEIGHT),
+ black, LAYER_GUI+1);
+ context.draw_filled_rect(Vector(0, bottom),
+ Vector(SCREEN_WIDTH, SCREEN_HEIGHT),
+ black, LAYER_GUI+1);
+}
+
+bool
+ShrinkFade::done()
+{
+ return accum_time >= fade_time;
+}
--- /dev/null
+// $Id$
+//
+// SuperTux
+// Copyright (C) 2006 Matthias Braun <matze@braunis.de>
+//
+// 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 __SHRINKFADE_HPP__
+#define __SHRINKFADE_HPP__
+
+#include "screen_fade.hpp"
+#include "math/vector.hpp"
+
+/**
+ * Shrinks a rectangle screen towards a specific position
+ */
+class ShrinkFade : public ScreenFade
+{
+public:
+ ShrinkFade(const Vector& point, float fade_time);
+ virtual ~ShrinkFade();
+
+ virtual void update(float elapsed_time);
+ virtual void draw(DrawingContext& context);
+
+ virtual bool done();
+
+private:
+ Vector dest;
+ float fade_time;
+ float accum_time;
+ float speedleft, speedright, speedtop, speedbottom;
+};
+
+#endif
--- /dev/null
+// $Id$
+//
+// SuperTux
+// Copyright (C) 2006 Matthias Braun <matze@braunis.de>
+//
+// 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 <config.h>
+
+#include <stdexcept>
+#include <iostream>
+#include "spawn_point.hpp"
+#include "lisp/lisp.hpp"
+#include "lisp/list_iterator.hpp"
+#include "log.hpp"
+
+SpawnPoint::SpawnPoint()
+{}
+
+SpawnPoint::SpawnPoint(const SpawnPoint& other)
+ : name(other.name), pos(other.pos)
+{}
+
+SpawnPoint::SpawnPoint(const lisp::Lisp* slisp)
+{
+ pos.x = -1;
+ pos.y = -1;
+ lisp::ListIterator iter(slisp);
+ while(iter.next()) {
+ const std::string& token = iter.item();
+ if(token == "name") {
+ iter.value()->get(name);
+ } else if(token == "x") {
+ iter.value()->get(pos.x);
+ } else if(token == "y") {
+ iter.value()->get(pos.y);
+ } else {
+ log_warning << "unknown token '" << token << "' in SpawnPoint" << std::endl;
+ }
+ }
+
+ if(name == "")
+ throw std::runtime_error("No name specified for spawnpoint");
+ if(pos.x < 0 || pos.y < 0)
+ throw std::runtime_error("Invalid coordinates for spawnpoint");
+}
--- /dev/null
+// $Id$
+//
+// SuperTux
+// Copyright (C) 2006 Matthias Braun <matze@braunis.de>
+//
+// 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 __SPAWN_POINT_H__
+#define __SPAWN_POINT_H__
+
+#include <string>
+#include "math/vector.hpp"
+namespace lisp { class Lisp; }
+
+class SpawnPoint
+{
+public:
+ SpawnPoint();
+ SpawnPoint(const SpawnPoint& other);
+ SpawnPoint(const lisp::Lisp* lisp);
+
+ std::string name;
+ Vector pos;
+};
+
+#endif
--- /dev/null
+// $Id$
+//
+// SuperTux
+// Copyright (C) 2006 Matthias Braun <matze@braunis.de>
+//
+// 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 <config.h>
+
+#include <iostream>
+#include <cmath>
+#include <cassert>
+#include <stdexcept>
+
+
+#include "video/surface.hpp"
+#include "sprite.hpp"
+#include "video/drawing_context.hpp"
+#include "log.hpp"
+#include "timer.hpp"
+
+Sprite::Sprite(SpriteData& newdata)
+ : data(newdata),
+ frame(0),
+ animation_loops(-1),
+ angle(0.0f),
+ color(1.0f, 1.0f, 1.0f, 1.0f)
+{
+ action = data.get_action("normal");
+ if(!action)
+ action = data.actions.begin()->second;
+ last_ticks = game_time;
+}
+
+Sprite::Sprite(const Sprite& other)
+ : data(other.data), frame(other.frame),
+ animation_loops(other.animation_loops),
+ angle(0.0f),
+ color(1.0f, 1.0f, 1.0f, 1.0f),
+ action(other.action)
+{
+ last_ticks = game_time;
+}
+
+Sprite::~Sprite()
+{
+}
+
+void
+Sprite::set_action(const std::string& name, int loops)
+{
+ if(action && action->name == name)
+ return;
+
+ SpriteData::Action* newaction = data.get_action(name);
+ if(!newaction) {
+ log_debug << "Action '" << name << "' not found." << std::endl;
+ return;
+ }
+
+ action = newaction;
+ animation_loops = loops;
+ frame = 0;
+}
+
+bool
+Sprite::animation_done()
+{
+ return animation_loops == 0;
+}
+
+void
+Sprite::update()
+{
+ if(animation_done())
+ return;
+
+ float frame_inc = action->fps * (game_time - last_ticks);
+ last_ticks = game_time;
+
+ frame += frame_inc;
+
+ if(frame >= get_frames()) {
+ frame = fmodf(frame, get_frames());
+
+ animation_loops--;
+ if(animation_done())
+ frame = get_frames()-1;
+ }
+}
+
+void
+Sprite::draw(DrawingContext& context, const Vector& pos, int layer)
+{
+ assert(action != 0);
+ update();
+
+ if((int)frame >= get_frames() || (int)frame < 0)
+ log_warning << "frame out of range: " << (int)frame << "/" << get_frames() << " at " << get_name() << "/" << get_action() << std::endl;
+ else
+ context.draw_surface(action->surfaces[(int)frame],
+ pos - Vector(action->x_offset, action->y_offset),
+ angle,
+ color,
+ blend,
+ layer + action->z_order);
+}
+
+void
+Sprite::draw_part(DrawingContext& context, const Vector& source,
+ const Vector& size, const Vector& pos, int layer)
+{
+ assert(action != 0);
+ update();
+
+ int frameidx = (int) frame;
+
+ if(frameidx >= get_frames() || frameidx < 0) {
+#ifndef DEBUG
+ // in optimized mode we get some small rounding errors in floating point
+ // number sometimes...
+ log_warning << "frame out of range: " << frameidx << "/" << get_frames() << " at sprite: " << get_name() << "/" << get_action() << std::endl;
+#endif
+ frameidx = get_frames() - 1;
+ }
+
+ context.draw_surface_part(action->surfaces[frameidx], source, size,
+ pos - Vector(action->x_offset, action->y_offset),
+ layer + action->z_order);
+}
+
+int
+Sprite::get_width() const
+{
+ return (int) action->surfaces[get_frame()]->get_width();
+}
+
+int
+Sprite::get_height() const
+{
+ return (int) action->surfaces[get_frame()]->get_height();
+}
+
+float
+Sprite::get_current_hitbox_x_offset() const
+{
+ return action->x_offset;
+}
+
+float
+Sprite::get_current_hitbox_y_offset() const
+{
+ return action->y_offset;
+}
+
+float
+Sprite::get_current_hitbox_width() const
+{
+ return action->hitbox_w;
+}
+
+float
+Sprite::get_current_hitbox_height() const
+{
+ return action->hitbox_h;
+}
+
+Rect
+Sprite::get_current_hitbox() const
+{
+ return Rect(action->x_offset, action->y_offset, action->x_offset + action->hitbox_w, action->y_offset + action->hitbox_h);
+}
+
+void
+Sprite::set_fps(float new_fps)
+{
+ action->fps = new_fps;
+}
+
+void
+Sprite::set_angle(float a)
+{
+ angle = a;
+}
+
+float
+Sprite::get_angle() const
+{
+ return angle;
+}
+
+void
+Sprite::set_color(const Color& c)
+{
+ color = c;
+}
+
+Color
+Sprite::get_color() const
+{
+ return color;
+}
+
+void
+Sprite::set_blend(const Blend& b)
+{
+ blend = b;
+}
+
+Blend
+Sprite::get_blend() const
+{
+ return blend;
+}
+
+/* EOF */
--- /dev/null
+// $Id$
+//
+// SuperTux
+// Copyright (C) 2006 Matthias Braun <matze@braunis.de>
+//
+// 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 <string>
+#include <assert.h>
+
+#include "math/vector.hpp"
+#include "math/rect.hpp"
+#include "sprite_data.hpp"
+#include "video/color.hpp"
+#include "video/drawing_context.hpp"
+
+class Surface;
+class DrawingContext;
+class Blend;
+
+class Sprite
+{
+public:
+ Sprite(SpriteData& data);
+ Sprite(const Sprite& other);
+ ~Sprite();
+
+ /** Draw sprite, automatically calculates next frame */
+ void draw(DrawingContext& context, const Vector& pos, int layer);
+
+ void draw_part(DrawingContext& context, const Vector& source,
+ const Vector& size, const Vector& pos, int layer);
+
+ /** Set action (or state) */
+ void set_action(const std::string& act, int loops = -1);
+
+ /** Set number of animation cycles until animation stops */
+ void set_animation_loops(int loops = -1)
+ { animation_loops = loops; }
+
+ /** Set framerate */
+ void set_fps(float new_fps);
+
+ /* Stop animation */
+ void stop_animation()
+ { animation_loops = 0; }
+ /** Check if animation is stopped or not */
+ bool animation_done();
+
+ float get_fps() const
+ { return action->fps; }
+ /** Get current action total frames */
+ int get_frames() const
+ { return action->surfaces.size(); }
+ /** Get sprite's name */
+ const std::string& get_name() const
+ { return data.name; }
+ /** Get current action name */
+ const std::string& get_action() const
+ { return action->name; }
+
+ int get_width() const;
+ int get_height() const;
+
+ /** return x-offset of current action's hitbox, relative to start of image */
+ float get_current_hitbox_x_offset() const;
+ /** return y-offset of current action's hitbox, relative to start of image */
+ float get_current_hitbox_y_offset() const;
+ /** return width of current action's hitbox */
+ float get_current_hitbox_width() const;
+ /** return height of current action's hitbox */
+ float get_current_hitbox_height() const;
+ /** return current action's hitbox, relative to 0,0 */
+ Rect get_current_hitbox() const;
+
+ /** Set the angle of the sprite rotation in degree */
+ void set_angle(float angle);
+
+ /** Get the angle of the sprite rotation in degree */
+ float get_angle() const;
+
+ void set_color(const Color& color);
+
+ Color get_color() const;
+
+ void set_blend(const Blend& blend);
+
+ Blend get_blend() const;
+
+ /** Get current frame */
+ int get_frame() const
+ { return (int)frame; }
+ /** Set current frame */
+ void set_frame(int frame)
+ {
+ this->frame = (float) (frame % get_frames());
+ }
+ Surface* get_frame(unsigned int frame)
+ {
+ assert(frame < action->surfaces.size());
+ return action->surfaces[frame];
+ }
+
+private:
+ void update();
+
+ SpriteData& data;
+
+ float frame;
+ int animation_loops;
+ float last_ticks;
+ float angle;
+ Color color;
+ Blend blend;
+
+ SpriteData::Action* action;
+};
+
+#endif
--- /dev/null
+// $Id$
+//
+// SuperTux
+// Copyright (C) 2006 Matthias Braun <matze@braunis.de>
+//
+// 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 <config.h>
+
+#include <iostream>
+#include <cmath>
+#include <sstream>
+#include <stdexcept>
+
+#include "sprite_data.hpp"
+#include "resources.hpp"
+#include "video/drawing_context.hpp"
+#include "lisp/list_iterator.hpp"
+#include "log.hpp"
+
+SpriteData::Action::Action()
+{
+ x_offset = 0;
+ y_offset = 0;
+ hitbox_w = 0;
+ hitbox_h = 0;
+ z_order = 0;
+ fps = 10;
+}
+
+SpriteData::Action::~Action()
+{
+ for(std::vector<Surface*>::iterator i = surfaces.begin();
+ i != surfaces.end(); ++i)
+ delete *i;
+}
+
+SpriteData::SpriteData(const lisp::Lisp* lisp, const std::string& basedir)
+{
+ lisp::ListIterator iter(lisp);
+ while(iter.next()) {
+ if(iter.item() == "name") {
+ iter.value()->get(name);
+ } else if(iter.item() == "action") {
+ parse_action(iter.lisp(), basedir);
+ } else {
+ log_warning << "Unknown sprite field: " << iter.item() << std::endl;
+ }
+ }
+ if(actions.empty())
+ throw std::runtime_error("Error: Sprite wihtout actions.");
+}
+
+SpriteData::~SpriteData()
+{
+ for(Actions::iterator i=actions.begin(); i != actions.end(); ++i)
+ delete i->second;
+}
+
+void
+SpriteData::parse_action(const lisp::Lisp* lisp, const std::string& basedir)
+{
+ Action* action = new Action;
+
+ if(!lisp->get("name", action->name)) {
+ if(!actions.empty())
+ throw std::runtime_error(
+ "If there are more than one action, they need names!");
+ }
+ std::vector<float> hitbox;
+ if (lisp->get_vector("hitbox", hitbox)) {
+ if (hitbox.size() != 4) throw std::runtime_error("hitbox must specify exactly 4 coordinates");
+ action->x_offset = hitbox[0];
+ action->y_offset = hitbox[1];
+ action->hitbox_w = hitbox[2];
+ action->hitbox_h = hitbox[3];
+ }
+ lisp->get("z-order", action->z_order);
+ lisp->get("fps", action->fps);
+
+ std::string mirror_action;
+ lisp->get("mirror-action", mirror_action);
+ if(!mirror_action.empty()) {
+ Action* act_tmp = get_action(mirror_action);
+ if(act_tmp == NULL) {
+ throw std::runtime_error("Could not mirror action. Action not found\n"
+ "Mirror actions must be defined after the real one!");
+ } else {
+ float max_w = 0;
+ float max_h = 0;
+ for(int i = 0; static_cast<unsigned int>(i) < act_tmp->surfaces.size();
+ i++) {
+ Surface* surface = new Surface(*(act_tmp->surfaces[i]));
+ surface->hflip();
+ max_w = std::max(max_w, (float) surface->get_width());
+ max_h = std::max(max_h, (float) surface->get_height());
+ action->surfaces.push_back(surface);
+ }
+ if (action->hitbox_w < 1) action->hitbox_w = max_w;
+ if (action->hitbox_h < 1) action->hitbox_h = max_h;
+ }
+ } else { // Load images
+ std::vector<std::string> images;
+ if(!lisp->get_vector("images", images)) {
+ std::stringstream msg;
+ msg << "Sprite '" << name << "' contains no images in action '"
+ << action->name << "'.";
+ throw std::runtime_error(msg.str());
+ }
+
+ float max_w = 0;
+ float max_h = 0;
+ for(std::vector<std::string>::size_type i = 0; i < images.size(); i++) {
+ Surface* surface = new Surface(basedir + images[i]);
+ max_w = std::max(max_w, (float) surface->get_width());
+ max_h = std::max(max_h, (float) surface->get_height());
+ action->surfaces.push_back(surface);
+ }
+ if (action->hitbox_w < 1) action->hitbox_w = max_w;
+ if (action->hitbox_h < 1) action->hitbox_h = max_h;
+ }
+ actions[action->name] = action;
+}
+
+SpriteData::Action*
+SpriteData::get_action(std::string act)
+{
+ Actions::iterator i = actions.find(act);
+ if(i == actions.end()) {
+ return 0;
+ }
+ return i->second;
+}
--- /dev/null
+// $Id$
+//
+// SuperTux
+// Copyright (C) 2006 Matthias Braun <matze@braunis.de>
+//
+// 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_DATA_H
+#define SUPERTUX_SPRITE_DATA_H
+
+#include <string>
+#include <vector>
+#include <map>
+
+#include "lisp/lisp.hpp"
+#include "video/surface.hpp"
+
+class SpriteData
+{
+public:
+ /** cur has to be a pointer to data in the form of ((hitbox 5 10 0 0) ...) */
+ SpriteData(const lisp::Lisp* cur, const std::string& basedir);
+ ~SpriteData();
+
+ const std::string& get_name() const
+ {
+ return name;
+ }
+
+private:
+ friend class Sprite;
+
+ struct Action
+ {
+ Action();
+ ~Action();
+
+ std::string name;
+
+ /** Position correction */
+ float x_offset;
+ float y_offset;
+
+ /** Hitbox width */
+ float hitbox_w;
+
+ /** Hitbox height */
+ float hitbox_h;
+
+ /** Drawing priority in queue */
+ int z_order;
+
+ /** Frames per second */
+ float fps;
+
+ std::vector<Surface*> surfaces;
+ };
+
+ typedef std::map <std::string, Action*> Actions;
+ Actions actions;
+
+ void parse_action(const lisp::Lisp* lispreader, const std::string& basedir);
+ /** Get an action */
+ Action* get_action(std::string act);
+
+ std::string name;
+};
+
+#endif
--- /dev/null
+// $Id$
+//
+// SuperTux
+// Copyright (C) 2006 Matthias Braun <matze@braunis.de>
+//
+// 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 <config.h>
+
+#include <iostream>
+#include <sstream>
+#include <stdexcept>
+
+#include "sprite_manager.hpp"
+#include "sprite_data.hpp"
+#include "sprite.hpp"
+#include "lisp/lisp.hpp"
+#include "lisp/parser.hpp"
+#include "lisp/list_iterator.hpp"
+#include "file_system.hpp"
+#include "log.hpp"
+
+SpriteManager* sprite_manager = NULL;
+
+SpriteManager::SpriteManager()
+{
+}
+
+SpriteManager::~SpriteManager()
+{
+ for(Sprites::iterator i = sprites.begin(); i != sprites.end(); ++i) {
+ delete i->second;
+ }
+}
+
+Sprite*
+SpriteManager::create(const std::string& name)
+{
+ Sprites::iterator i = sprites.find(name);
+ SpriteData* data;
+ if(i == sprites.end()) {
+ // try loading the spritefile
+ data = load(name);
+ if(data == NULL) {
+ std::stringstream msg;
+ msg << "Sprite '" << name << "' not found.";
+ throw std::runtime_error(msg.str());
+ }
+ } else {
+ data = i->second;
+ }
+
+ return new Sprite(*data);
+}
+
+SpriteData*
+SpriteManager::load(const std::string& filename)
+{
+ lisp::Parser parser;
+ const lisp::Lisp* root;
+
+ try {
+ root = parser.parse(filename);
+ } catch(const std::exception& e) {
+ std::ostringstream msg;
+ msg << "Parse error when trying to load sprite '" << filename
+ << "': " << e.what() << "\n";
+ throw std::runtime_error(msg.str());
+ }
+
+ const lisp::Lisp* sprite = root->get_lisp("supertux-sprite");
+ if(!sprite) {
+ std::ostringstream msg;
+ msg << "'" << filename << "' is not a supertux-sprite file";
+ throw std::runtime_error(msg.str());
+ }
+
+ std::auto_ptr<SpriteData> data (
+ new SpriteData(sprite, FileSystem::dirname(filename)) );
+ sprites[filename] = data.release();
+
+ return sprites[filename];
+}
--- /dev/null
+// $Id$
+//
+// SuperTux
+// Copyright (C) 2006 Matthias Braun <matze@braunis.de>
+//
+// 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 <map>
+
+class SpriteData;
+class Sprite;
+
+class SpriteManager
+{
+private:
+ typedef std::map<std::string, SpriteData*> Sprites;
+ Sprites sprites;
+
+public:
+ SpriteManager();
+ ~SpriteManager();
+
+ /** loads a sprite. */
+ Sprite* create(const std::string& filename);
+
+private:
+ SpriteData* load(const std::string& filename);
+};
+
+extern SpriteManager* sprite_manager;
+
+#endif
--- /dev/null
+#
+# SuperTux - squirrel library build script
+# Copyright (C) 2006 Christoph Sommer <christoph.sommer@2006.expires.deltadevelopment.de>
+#
+# 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
+#
+
+## Add include/ to include directories
+
+INCLUDE_DIRECTORIES(${SUPERTUX_SOURCE_DIR}/src/squirrel/include/)
+
+## build list of source files
+
+FILE(GLOB SQUIRREL_SOURCES squirrel/*.cpp sqstdlib/*.cpp sqstdlib/*.c)
+
+## add additional compiler switches
+
+ADD_DEFINITIONS(-include ${CMAKE_BINARY_DIR}/config.h)
+
+## define a target for building the library
+
+ADD_LIBRARY(squirrel ${SQUIRREL_SOURCES})
--- /dev/null
+Copyright (c) 2003-2007 Alberto Demichelis\r
+\r
+This software is provided 'as-is', without any \r
+express or implied warranty. In no event will the \r
+authors be held liable for any damages arising from \r
+the use of this software.\r
+\r
+Permission is granted to anyone to use this software \r
+for any purpose, including commercial applications, \r
+and to alter it and redistribute it freely, subject \r
+to the following restrictions:\r
+\r
+ 1. The origin of this software must not be \r
+ misrepresented; you must not claim that \r
+ you wrote the original software. If you \r
+ use this software in a product, an \r
+ acknowledgment in the product \r
+ documentation would be appreciated but is \r
+ not required.\r
+\r
+ 2. Altered source versions must be plainly \r
+ marked as such, and must not be \r
+ misrepresented as being the original \r
+ software.\r
+\r
+ 3. This notice may not be removed or \r
+ altered from any source distribution.\r
+-----------------------------------------------------\r
+END OF COPYRIGHT
\ No newline at end of file
--- /dev/null
+***version 2.1.2 stable***\r
+-new behaviour for generators iteration using foreach\r
+now when a generator is iterated by foreach the value returned by a 'return val' statement\r
+will terminate the iteration but will not be returned as foreach iteration\r
+-added sq_setclassudsize()\r
+-added sq_clear()\r
+-added table.clear(), array.clear()\r
+-fixed sq_cmp() (thx jyuill)\r
+-fixed minor bugs\r
+\r
+***2006-08-21 ***\r
+***version 2.1.1 stable***\r
+-vm refactoring\r
+-optimized internal function memory layout\r
+-new global symbol _version_ (is the version string)\r
+-code size optimization for float literals(on 32bits float builts)\r
+-now the raw ref API(sq_addref etc...) is fully reentrant.\r
+-fixed a bug in sq_getdelegate() now pushes null if the object doesn't have a delegate(thx MatzeB)\r
+-improved C reference performances in NO_GARBAGE_COLLECTOR builds\r
+-sq_getlocal() now enumerates also outer values.\r
+-fixed regexp library for GCC users.\r
+\r
+***2006-03-19 ***\r
+***version 2.1 stable***\r
+-added static class fields, new keyword static\r
+-added 64bits architecture support\r
+-added global slot _intsize_ int the base lib to recognize 32bits and 64bits builds\r
+-added functions with fixed environment, closure.bindenv() built-in function\r
+-all types except userdata and null implement the tostring() method\r
+-string concatenation now invokes metamethod _tostring\r
+-new metamethods for class objects _newmember and _inherited\r
+-sq_call() sq_resume() sq_wakeupvm() have a new signature\r
+-new C referencing implementation(scales more with the amount of references)\r
+-refactored hash table\r
+-new api functions sq_newslot(),sq_tobool(),sq_getbase(), sq_instanceof(), sq_bindenv()\r
+-the api func sq_createslot was deprecated but still supported in form of C macro on top of sq_newslot\r
+-sq_setreleasehook() now also works for classes\r
+-stream.readstr() and stream.writestr() have been deprecated(this affects file and blob)\r
+-fixed squirrel.h undeclared api calls\r
+-fixed few minor bugs\r
+-SQChar is now defined as wchar_t\r
+-removed warning when building with -Wall -pedantic for GCC users\r
+-added new std io function writeclosuretofile()\r
+-added new std string functions strip(),rstrip(),lstrip() and split()\r
+-regular expressions operators (+,*) now have more POSIX greedyness behaviour\r
+-class constructors are now invoked as normal functions\r
+\r
+***2005-10-02 ***\r
+***version 2.0.5 stable***\r
+-fixed some 64bits incompatibilities (thx sarge)\r
+-fixed minor bug in the stdlib format() function (thx Rick)\r
+-fixed a bug in dofile() that was preventing to compile empty files\r
+-added new API sq_poptop() & sq_getfreevariable()\r
+-some performance improvements\r
+\r
+***2005-08-14 ***\r
+***version 2.0.4 stable***\r
+-weak references and related API calls\r
+-added sq_objtobool()\r
+-class instances memory policies improved(1 mem allocation for the whole instance)\r
+-typetags are now declared as SQUserPointer instead of unsigned int\r
+-first pass for 64bits compatibility\r
+-fixed minor bug in the stdio stream\r
+-fixed a bug in format()\r
+-fixed bug in string.tointeger() and string.tofloat()\r
+\r
+***2005-06-24 ***\r
+***version 2.0.3 stable***\r
+-dofile() and loadfile() in the iolib now can decode ASCII, UTF8 files UCS2 big-endian and little-endian\r
+-sq_setparamscheck() : now typemesk can check for null\r
+-added string escape sequence \xhhhh\r
+-fixed some C++ standard incompatibilities\r
+\r
+***2005-05-15 ***\r
+***version 2.0.2 stable***\r
+-performances improvements (expecially for GCC users)\r
+-removed all dependencies from C++ exception handling\r
+-various bugfixes\r
+\r
+***2005-04-12 ***\r
+***version 2.0.1 stable***\r
+-various bugfixes\r
+-sq_setparamscheck() now allows spaces in the typemask\r
+\r
+***2005-04-03 ***\r
+***version 2.0 stable***\r
+-added API sq_gettypetag()\r
+-added built-in function to the bool type(tointeger, tostring etc...)\r
+\r
+***2005-02-27 ***\r
+***version 2.0 release candidate 1(RC 1)***\r
+-added API sq_reseterror()\r
+-modified sq_release()\r
+-now class instances can be cloned\r
+-various bufixes\r
+\r
+***2005-01-26 ***\r
+***version 2.0 beta 1***\r
+-added bool type\r
+-class properties can be redefined in a derived class\r
+-added ops *= /= and %=\r
+-new syntax for class attributes declaration </ and /> instead of ( and )\r
+-increased the max number of literals per function from 65535 to 16777215\r
+-now free variables have proper lexical scoping\r
+-added API sq_createinstance(), sq_pushbool(), sq_getbool()\r
+-added built-in function type()\r
+-added built-in function obj.rawin(key) in table,class and instance\r
+-sq_rawget() and sq_rawset() now work also on classes and instances\r
+-the VM no longer uses C++ exception handling (more suitable for embedded devices)\r
+-various bufixes\r
+\r
+***2004-12-21 ***\r
+***version 2.0 alpha 2***\r
+-globals scoping changed, now if :: is omitted the VM automatically falls back on the root table\r
+-various bufixes\r
+-added class level attributes\r
+\r
+***2004-12-12 ***\r
+***version 2.0 alpha 1***\r
+-codebase branch from version 1.x\r
+-added classes\r
+-added functions with variable number of parameters(vargc & vargv and the ...)\r
+-0 and 0.0 are now considered 'false' by all conditional statements(if,while,for,?,do-while)\r
+-added new api functions sq_newclass() sq_setinstanceup() sq_getinstanceup() sq_getattributes() sq_setattributes()\r
+-modified api sq_settypetag()\r
+\r
+***2004-11-01 ***\r
+***version 1.0 stable***\r
+-fixed some minor bug\r
+-improoved operator 'delete' performances\r
+-added scientific notation for float numbers( eg. 2.e16 or 2.e-2)\r
+\r
+***2004-08-30 ***\r
+***version 1.0 release candidate 2(RC 2)***\r
+-fixed bug in the vm(thx Pierre Renaux)\r
+-fixed bug in the optimizer(thx Pierre Renaux)\r
+-fixed some bug in the documentation(thx JD)\r
+-added new api functions for raw object handling\r
+-removed nested multiline comments\r
+-reduced memory footprint in C references\r
+\r
+***2004-08-23 ***\r
+***version 1.0 release candidate 1(RC 1)***\r
+-fixed division by zero\r
+-the 'in' operator and obj.rawget() do not query the default delegate anymore\r
+-added function sq_getprintfunc()\r
+-added new standard library 'auxlib'(implements default error handlers)\r
+\r
+***2004-07-12 ***\r
+***version 1.0 beta 4***\r
+-fixed a bug in the integer.tochar() built-in method\r
+-fixed unary minus operator\r
+-fixed bug in dofile()\r
+-fixed inconsistency between != and == operators(on float/integer comparison)\r
+-added javascript style unsigned right shift operator '>>>'\r
+-added array(size) constructor built-in function\r
+-array.resize(size,[fill]) built-in function accepts an optional 'fill' value\r
+-improved debug API, added sq_getclosureinfo() and sq_setnativeclosurename()\r
+\r
+***2004-05-23 ***\r
+***version 1.0 beta 3***\r
+-minor vm bug fixes\r
+-string allocation is now faster\r
+-tables and array memory usage is now less conservative(they shrink)\r
+-added regular expression routines in the standard library\r
+-The 'c' expression now accepts only 1 character(thx irbrian)\r
+-multiline strings <[ ]> have been substituted with C# style verbatim strings (eg. @"string")\r
+-added new keyword 'parent' for accessing the delegate of tables and unserdata\r
+-The metamethod '_clone' has been renamed '_cloned'\r
+-the _delslot metamethod's behaviour and prototype have been changed\r
+-new default function in the integer and float object 'tochar()'\r
+-the built-in function chcode2string has been removed\r
+-the default method [table].getdelegate() has been removed\r
+-new api sq_rawdeleteslot()\r
+-new table built-in method rawdelete(key)\r
+-the dynamic mudule loading has been removed from the standard distribution\r
+-some optimizations in the VM\r
+\r
+***2004-04-21 ***\r
+***version 1.0 beta 2***\r
+-minor compiler/parser bug fixes\r
+-sq_newclosure has a different prototype, the "paramscheck" of paramter has been moved to the new function sq_setparamscheck()\r
+-sq_setparamscheck allows to add automatic parameters type checking in native closures\r
+-sq_compile() lost the lineinfo parameter\r
+-new api sq_enabledebuginfo() globally sets compiler's debug info generation\r
+-added consistency check on bytecode serialization\r
+-fixed += operator, now works on strings like +\r
+-added global slot in the base lib _charsize_ to recognize unicode builds from ascii builds runtime\r
+-added registry table\r
+-new api call sq_pushregistrytable()\r
+-added type tag to the userdata type sq_settypetag()\r
+-sq_getuserdata now queries the userdata typetag\r
+-the built in function collect_garbage() as been renamed collectgarbage() for consistency reasons\r
+-new standard libraries(sqlibs are now obsolete)\r
+\r
+***2004-02-20 ***\r
+***version 1.0 beta 1***\r
+-fixed a bug in the compiler (thanks Martin Kofler)\r
+-fixed bug in the switch case statement\r
+-fixed the _unm metamethod\r
+-fixed minor bugs in the API\r
+-fixed automatic stack resizing\r
+-first beta version \r
+ first pass code clean up in the VM and base lib\r
+ first pass code coverege test has been done on VM and built-in lib\r
+-new VM creation API sq_open() sq_close() (sq_newvm and sq_releasevm are now obsolete)\r
+-new api allows to specifiy a "print" function to output text(sq_printfunc)\r
+-added some small optimizations\r
+-new cooperative multi-threading capabilities in the base library(coroutines), VMs are now a built in type("thread")\r
+-new built in functions have been added for manipulating the new "thread" type\r
+-friend virtual machines share the same root table, error handler and debug hook by default\r
+-new compile time options\r
+\r
+***2004-01-19 ***\r
+***version 0.9 alpha***\r
+-fixed a garbage collection bug\r
+-fixed some API bugs(thanks to Joshua Jensen)\r
+-fixed tail calls (in the version 0.8 the tail call optimization was erroneously disabled)\r
+-new function parameters semantic, now passing a wrong number of parameters generates an exception\r
+-native closures have now a built in parameter number checking\r
+-sq_rawget and sq_rawset now work also on arrays\r
+-sq_getsize now woks also on userdata\r
+-the userdata release hook prototype is changed(now passes the size of the userdata)\r
+-the lexer reader function now returns an integer instead of a char that allows better error checking on the input(thx Joshua Jensen)\r
+-faster compiler\r
+-try/catch blocks do not cause any runtime memory allocation anymore\r
+\r
+***2003-12-06 ***\r
+***version 0.8 alpha***\r
+-fixed a bug that was preventing to have callable userdata throught the metamethod _call\r
+-fixed a garbage collection bug\r
+-fixed == operator now can compare correctly different types\r
+-new built in method getstackinfos(level)\r
+-improoved line informations precision for the debug hook\r
+-new api call sq_compilebuffer()\r
+-new built-in api function compilestring()\r
+-new syntactic sugar for function declarations inside tables\r
+-the debug API has been finalized\r
+\r
+***2003-11-17 ***\r
+***version 0.7 alpha***\r
+-fixed critical bug SQInteger the tail call system\r
+-fixed bug in the continue statement code generation\r
+-fixed func call param issue(thanks to Rewoonenco Andrew)\r
+-added _delslot metamethod(thanks to Rewoonenco Andrew)\r
+-new multiline string expression ( delimited by <[ and ]> )\r
+-normal strings ("") do not allow embedded new line anymore\r
+-reduced vm memory footprint(C refs are shared between friend VMs)\r
+-new api method sq_deleteslot()\r
+-new debug hook event 'r' is triggered when a function returns\r
+\r
+***2003-11-04 ***\r
+***version 0.6 alpha***\r
+-fixed switch statement(was executing the default case after a break)\r
+-sq_call() doesn't pop the closure (just the params)\r
+-the vm execution can be suspended from the C API anytime (micro-threads)\r
+-new api calls sq_suspendvm() sq_wakeupvm() sq_getvmstate() and sq_reservestack()\r
+\r
+***2003-10-13 ***\r
+***version 0.5 alpha***\r
+-fixed some minor bug\r
+-tested with non ASCII identifiers in unicode mode(I've tried chinese chars)\r
+-added built-in function string.find()\r
+-the built-in function array.sort() optionally accepts a cmp(a,b) function\r
+-the debug hook function now has a new prototype debug_hook(event_type,sourcefile,line,functionname)\r
+-fixed some debug info imprecision\r
+\r
+***2003-10-01 ***\r
+***version 0.4 alpha***\r
+-faster VM\r
+-sq_call will pop arguments and closure also in case of failure\r
+-fixed a bug in sq_remove\r
+-now the VM detects delegation cycles(and throws an exception)\r
+-new operators ++ and --\r
+-new operator ',' comma operator\r
+-fixed some expression precedence issue\r
+-fixed bug in sq_arraypop\r
+\r
+***2003-09-15 ***\r
+***version 0.3 alpha***\r
+-fixed a bug in array::insert()\r
+-optional Unicode core(define SQUNICODE or _UNICODE on Win32)\r
+-sq_compiler uses a new reader function SQLEXREADFUNC\r
+-the debug hook passes 'l' instead of 'line' for line callbacks\r
+ and 'c' instead of 'call' for call callbacks\r
+-new array.extend() bulit-in function\r
+-new API sq_clone()\r
+\r
+***2003-09-10 ***\r
+***version 0.2 pre-alpha***\r
+-new completely reentrant VM (sq_open and sq_close are now obsolete)\r
+-sq_newvm() has a new prototype\r
+-allocators are now global and linked in the VM\r
+-_newslot meta method added\r
+-rawset creates a slot if doesn't exists\r
+-the compiler error callback pass the vm handle(thanks Pierre Renaux)\r
+-sq_setforeignptr() sq_getforeingptr() are now public\r
+-sq_resume() now is possible to resume generators from C\r
+-sq_getlasterror() retrieve the last thrown error\r
+-improved docs\r
+\r
+***2003-09-06 ***\r
+***version 0.1 pre-alpha***\r
+first release\r
--- /dev/null
+SubDir TOP src squirrel ;
+
+SQDBG_SOURCES = [ Wildcard sqdbg : *.cpp *.h *.inl ] ;
+if $(enable_sqdbg) = "yes" {
+ EXTRA_SOURCES = $(SQDBG_SOURCES) ;
+} else {
+ Package $(SQDBG_SOURCES) ;
+}
+
+Library squirrel
+ : [ Wildcard squirrel : *.cpp *.h ]
+ [ Wildcard sqstdlib : *.cpp *.c *.h ]
+ $(EXTRA_SOURCES)
+ : noinstall
+;
+
+for i in $(squirrel_OBJECTS) {
+ CXXFLAGS on $(i) = [ Filter [ on $(i) GetVar CXXFLAGS ] : -Wall -W -Werror ] -include $(top_builddir)/config.h ;
+ CFLAGS on $(i) = [ Filter [ on $(i) GetVar CFLAGS ] : -Wall -W -Werror ] -include $(top_builddir)/config.h ;
+}
+IncludeDir squirrel : include ;
+Package [ Wildcard include : *.h ] ;
--- /dev/null
+The programming language SQUIRREL 2.1.2 stable\r
+\r
+--------------------------------------------------\r
+The project has been compiled and run on Windows(Windows XP/2000 on Intel x86 Windows XP Pro on AMD x64) and\r
+Linux(Slackware 9.0 on Intel x86, Fedora Core 4 on AMD x64).\r
+\r
+Has been tested with the following compilers:\r
+ MS Visual C++ 6.0,7.0,7.1 and 8.0 (32 and 64bits)\r
+ MinGW gcc 3.2 (mingw special 20020817-1)\r
+ Cygnus gcc 3.2\r
+ Linux gcc 3.2.3\r
+ Linux gcc 4.0.0 (x86 64bits)\r
+ \r
+\r
+Feedback and suggestions are appreciated \r
+project page - http://www.squirrel-lang.org\r
+community forums - http://www.squirrel-lang.org/Forums\r
+wiki - http://wiki.squirrel-lang.org\r
+author - alberto@ademichelis.com\r
+\r
+END OF README\r
+\r
--- /dev/null
+/* see copyright notice in squirrel.h */
+#ifndef _SQSTD_AUXLIB_H_
+#define _SQSTD_AUXLIB_H_
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+SQUIRREL_API void sqstd_seterrorhandlers(HSQUIRRELVM v);
+SQUIRREL_API void sqstd_printcallstack(HSQUIRRELVM v);
+
+#ifdef __cplusplus
+} /*extern "C"*/
+#endif
+
+#endif /* _SQSTD_AUXLIB_H_ */
--- /dev/null
+/* see copyright notice in squirrel.h */
+#ifndef _SQSTDBLOB_H_
+#define _SQSTDBLOB_H_
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+SQUIRREL_API SQUserPointer sqstd_createblob(HSQUIRRELVM v, SQInteger size);
+SQUIRREL_API SQRESULT sqstd_getblob(HSQUIRRELVM v,SQInteger idx,SQUserPointer *ptr);
+SQUIRREL_API SQInteger sqstd_getblobsize(HSQUIRRELVM v,SQInteger idx);
+
+SQUIRREL_API SQRESULT sqstd_register_bloblib(HSQUIRRELVM v);
+
+#ifdef __cplusplus
+} /*extern "C"*/
+#endif
+
+#endif /*_SQSTDBLOB_H_*/
+
--- /dev/null
+/* see copyright notice in squirrel.h */
+#ifndef _SQSTDIO_H_
+#define _SQSTDIO_H_
+
+#ifdef __cplusplus
+
+#define SQSTD_STREAM_TYPE_TAG 0x80000000
+
+struct SQStream {
+
+ // [SuperTux] Added virtual destructor to avoid compiler warnings
+ virtual ~SQStream() { };
+
+ virtual SQInteger Read(void *buffer, SQInteger size) = 0;
+ virtual SQInteger Write(void *buffer, SQInteger size) = 0;
+ virtual SQInteger Flush() = 0;
+ virtual SQInteger Tell() = 0;
+ virtual SQInteger Len() = 0;
+ virtual SQInteger Seek(SQInteger offset, SQInteger origin) = 0;
+ virtual bool IsValid() = 0;
+ virtual bool EOS() = 0;
+};
+
+extern "C" {
+#endif
+
+#define SQ_SEEK_CUR 0
+#define SQ_SEEK_END 1
+#define SQ_SEEK_SET 2
+
+typedef void* SQFILE;
+
+SQUIRREL_API SQFILE sqstd_fopen(const SQChar *,const SQChar *);
+SQUIRREL_API SQInteger sqstd_fread(SQUserPointer, SQInteger, SQInteger, SQFILE);
+SQUIRREL_API SQInteger sqstd_fwrite(const SQUserPointer, SQInteger, SQInteger, SQFILE);
+SQUIRREL_API SQInteger sqstd_fseek(SQFILE , SQInteger , SQInteger);
+SQUIRREL_API SQInteger sqstd_ftell(SQFILE);
+SQUIRREL_API SQInteger sqstd_fflush(SQFILE);
+SQUIRREL_API SQInteger sqstd_fclose(SQFILE);
+SQUIRREL_API SQInteger sqstd_feof(SQFILE);
+
+SQUIRREL_API SQRESULT sqstd_createfile(HSQUIRRELVM v, SQFILE file,SQBool own);
+SQUIRREL_API SQRESULT sqstd_getfile(HSQUIRRELVM v, SQInteger idx, SQFILE *file);
+
+//compiler helpers
+SQUIRREL_API SQRESULT sqstd_loadfile(HSQUIRRELVM v,const SQChar *filename,SQBool printerror);
+SQUIRREL_API SQRESULT sqstd_dofile(HSQUIRRELVM v,const SQChar *filename,SQBool retval,SQBool printerror);
+SQUIRREL_API SQRESULT sqstd_writeclosuretofile(HSQUIRRELVM v,const SQChar *filename);
+
+SQUIRREL_API SQRESULT sqstd_register_iolib(HSQUIRRELVM v);
+
+#ifdef __cplusplus
+} /*extern "C"*/
+#endif
+
+#endif /*_SQSTDIO_H_*/
+
--- /dev/null
+/* see copyright notice in squirrel.h */
+#ifndef _SQSTD_MATH_H_
+#define _SQSTD_MATH_H_
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+SQUIRREL_API SQRESULT sqstd_register_mathlib(HSQUIRRELVM v);
+
+#ifdef __cplusplus
+} /*extern "C"*/
+#endif
+
+#endif /*_SQSTD_MATH_H_*/
--- /dev/null
+/* see copyright notice in squirrel.h */
+#ifndef _SQSTD_STRING_H_
+#define _SQSTD_STRING_H_
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+typedef unsigned int SQRexBool;
+typedef struct SQRex SQRex;
+
+typedef struct {
+ const SQChar *begin;
+ SQInteger len;
+} SQRexMatch;
+
+SQUIRREL_API SQRex *sqstd_rex_compile(const SQChar *pattern,const SQChar **error);
+SQUIRREL_API void sqstd_rex_free(SQRex *exp);
+SQUIRREL_API SQBool sqstd_rex_match(SQRex* exp,const SQChar* text);
+SQUIRREL_API SQBool sqstd_rex_search(SQRex* exp,const SQChar* text, const SQChar** out_begin, const SQChar** out_end);
+SQUIRREL_API SQBool sqstd_rex_searchrange(SQRex* exp,const SQChar* text_begin,const SQChar* text_end,const SQChar** out_begin, const SQChar** out_end);
+SQUIRREL_API SQInteger sqstd_rex_getsubexpcount(SQRex* exp);
+SQUIRREL_API SQBool sqstd_rex_getsubexp(SQRex* exp, SQInteger n, SQRexMatch *subexp);
+
+SQUIRREL_API SQRESULT sqstd_register_stringlib(HSQUIRRELVM v);
+
+#ifdef __cplusplus
+} /*extern "C"*/
+#endif
+
+#endif /*_SQSTD_STRING_H_*/
--- /dev/null
+/* see copyright notice in squirrel.h */
+#ifndef _SQSTD_SYSTEMLIB_H_
+#define _SQSTD_SYSTEMLIB_H_
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+SQUIRREL_API SQInteger sqstd_register_systemlib(HSQUIRRELVM v);
+
+#ifdef __cplusplus
+} /*extern "C"*/
+#endif
+
+#endif /* _SQSTD_SYSTEMLIB_H_ */
--- /dev/null
+/*
+Copyright (c) 2003-2007 Alberto Demichelis
+
+This software is provided 'as-is', without any
+express or implied warranty. In no event will the
+authors be held liable for any damages arising from
+the use of this software.
+
+Permission is granted to anyone to use this software
+for any purpose, including commercial applications,
+and to alter it and redistribute it freely, subject
+to the following restrictions:
+
+ 1. The origin of this software must not be
+ misrepresented; you must not claim that
+ you wrote the original software. If you
+ use this software in a product, an
+ acknowledgment in the product
+ documentation would be appreciated but is
+ not required.
+
+ 2. Altered source versions must be plainly
+ marked as such, and must not be
+ misrepresented as being the original
+ software.
+
+ 3. This notice may not be removed or
+ altered from any source distribution.
+
+*/
+#ifndef _SQUIRREL_H_
+#define _SQUIRREL_H_
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#ifndef SQUIRREL_API
+#define SQUIRREL_API extern
+#endif
+
+#ifdef _SQ64
+#ifdef _MSC_VER
+typedef __int64 SQInteger;
+typedef unsigned __int64 SQUnsignedInteger;
+typedef unsigned __int64 SQHash; /*should be the same size of a pointer*/
+#else
+typedef long SQInteger;
+typedef unsigned long SQUnsignedInteger;
+typedef unsigned long SQHash; /*should be the same size of a pointer*/
+#endif
+typedef int SQInt32;
+#else
+typedef int SQInteger;
+typedef int SQInt32; /*must be 32 bits(also on 64bits processors)*/
+typedef unsigned int SQUnsignedInteger;
+typedef unsigned int SQHash; /*should be the same size of a pointer*/
+#endif
+
+typedef float SQFloat;
+typedef void* SQUserPointer;
+typedef SQUnsignedInteger SQBool;
+typedef SQInteger SQRESULT;
+
+#define SQTrue (1)
+#define SQFalse (0)
+
+
+struct SQVM;
+struct SQTable;
+struct SQArray;
+struct SQString;
+struct SQClosure;
+struct SQGenerator;
+struct SQNativeClosure;
+struct SQUserData;
+struct SQFunctionProto;
+struct SQRefCounted;
+struct SQClass;
+struct SQInstance;
+struct SQDelegable;
+
+#ifdef _UNICODE
+#define SQUNICODE
+#endif
+
+#ifdef SQUNICODE
+#if (defined(_MSC_VER) && _MSC_VER >= 1400) // 1400 = VS8
+
+#if defined(wchar_t) //this is if the compiler considers wchar_t as native type
+#define wchar_t unsigned short
+#endif
+
+#else
+typedef unsigned short wchar_t;
+#endif
+
+typedef wchar_t SQChar;
+#define _SC(a) L##a
+#define scstrcmp wcscmp
+#define scsprintf swprintf
+#define scstrlen wcslen
+#define scstrtod wcstod
+#define scstrtol wcstol
+#define scatoi _wtoi
+#define scstrtoul wcstoul
+#define scvsprintf vswprintf
+#define scstrstr wcsstr
+#define scisspace iswspace
+#define scisdigit iswdigit
+#define scisxdigit iswxdigit
+#define scisalpha iswalpha
+#define sciscntrl iswcntrl
+#define scisalnum iswalnum
+#define scprintf wprintf
+#define MAX_CHAR 0xFFFF
+#else
+typedef char SQChar;
+#define _SC(a) a
+#define scstrcmp strcmp
+#define scsprintf sprintf
+#define scstrlen strlen
+#define scstrtod strtod
+#define scstrtol strtol
+#define scatoi atoi
+#define scstrtoul strtoul
+#define scvsprintf vsprintf
+#define scstrstr strstr
+#define scisspace isspace
+#define scisdigit isdigit
+#define scisxdigit isxdigit
+#define sciscntrl iscntrl
+#define scisalpha isalpha
+#define scisalnum isalnum
+#define scprintf printf
+#define MAX_CHAR 0xFF
+#endif
+
+#define SQUIRREL_VERSION _SC("Squirrel 2.1.2 stable")
+#define SQUIRREL_COPYRIGHT _SC("Copyright (C) 2003-2007 Alberto Demichelis")
+#define SQUIRREL_AUTHOR _SC("Alberto Demichelis")
+
+#define SQ_VMSTATE_IDLE 0
+#define SQ_VMSTATE_RUNNING 1
+#define SQ_VMSTATE_SUSPENDED 2
+
+#define SQUIRREL_EOB 0
+#define SQ_BYTECODE_STREAM_TAG 0xFAFA
+
+#define SQOBJECT_REF_COUNTED 0x08000000
+#define SQOBJECT_NUMERIC 0x04000000
+#define SQOBJECT_DELEGABLE 0x02000000
+#define SQOBJECT_CANBEFALSE 0x01000000
+
+#define SQ_MATCHTYPEMASKSTRING (-99999)
+
+#define _RT_MASK 0x00FFFFFF
+#define _RAW_TYPE(type) (type&_RT_MASK)
+
+#define _RT_NULL 0x00000001
+#define _RT_INTEGER 0x00000002
+#define _RT_FLOAT 0x00000004
+#define _RT_BOOL 0x00000008
+#define _RT_STRING 0x00000010
+#define _RT_TABLE 0x00000020
+#define _RT_ARRAY 0x00000040
+#define _RT_USERDATA 0x00000080
+#define _RT_CLOSURE 0x00000100
+#define _RT_NATIVECLOSURE 0x00000200
+#define _RT_GENERATOR 0x00000400
+#define _RT_USERPOINTER 0x00000800
+#define _RT_THREAD 0x00001000
+#define _RT_FUNCPROTO 0x00002000
+#define _RT_CLASS 0x00004000
+#define _RT_INSTANCE 0x00008000
+#define _RT_WEAKREF 0x00010000
+
+typedef enum tagSQObjectType{
+ OT_NULL = (_RT_NULL|SQOBJECT_CANBEFALSE),
+ OT_INTEGER = (_RT_INTEGER|SQOBJECT_NUMERIC|SQOBJECT_CANBEFALSE),
+ OT_FLOAT = (_RT_FLOAT|SQOBJECT_NUMERIC|SQOBJECT_CANBEFALSE),
+ OT_BOOL = (_RT_BOOL|SQOBJECT_CANBEFALSE),
+ OT_STRING = (_RT_STRING|SQOBJECT_REF_COUNTED),
+ OT_TABLE = (_RT_TABLE|SQOBJECT_REF_COUNTED|SQOBJECT_DELEGABLE),
+ OT_ARRAY = (_RT_ARRAY|SQOBJECT_REF_COUNTED),
+ OT_USERDATA = (_RT_USERDATA|SQOBJECT_REF_COUNTED|SQOBJECT_DELEGABLE),
+ OT_CLOSURE = (_RT_CLOSURE|SQOBJECT_REF_COUNTED),
+ OT_NATIVECLOSURE = (_RT_NATIVECLOSURE|SQOBJECT_REF_COUNTED),
+ OT_GENERATOR = (_RT_GENERATOR|SQOBJECT_REF_COUNTED),
+ OT_USERPOINTER = _RT_USERPOINTER,
+ OT_THREAD = (_RT_THREAD|SQOBJECT_REF_COUNTED) ,
+ OT_FUNCPROTO = (_RT_FUNCPROTO|SQOBJECT_REF_COUNTED), //internal usage only
+ OT_CLASS = (_RT_CLASS|SQOBJECT_REF_COUNTED),
+ OT_INSTANCE = (_RT_INSTANCE|SQOBJECT_REF_COUNTED|SQOBJECT_DELEGABLE),
+ OT_WEAKREF = (_RT_WEAKREF|SQOBJECT_REF_COUNTED)
+}SQObjectType;
+
+#define ISREFCOUNTED(t) (t&SQOBJECT_REF_COUNTED)
+
+
+typedef union tagSQObjectValue
+{
+ struct SQTable *pTable;
+ struct SQArray *pArray;
+ struct SQClosure *pClosure;
+ struct SQGenerator *pGenerator;
+ struct SQNativeClosure *pNativeClosure;
+ struct SQString *pString;
+ struct SQUserData *pUserData;
+ SQInteger nInteger;
+ SQFloat fFloat;
+ SQUserPointer pUserPointer;
+ struct SQFunctionProto *pFunctionProto;
+ struct SQRefCounted *pRefCounted;
+ struct SQDelegable *pDelegable;
+ struct SQVM *pThread;
+ struct SQClass *pClass;
+ struct SQInstance *pInstance;
+ struct SQWeakRef *pWeakRef;
+}SQObjectValue;
+
+
+typedef struct tagSQObject
+{
+ SQObjectType _type;
+ SQObjectValue _unVal;
+}SQObject;
+
+typedef struct tagSQStackInfos{
+ const SQChar* funcname;
+ const SQChar* source;
+ SQInteger line;
+}SQStackInfos;
+
+typedef struct SQVM* HSQUIRRELVM;
+typedef SQObject HSQOBJECT;
+typedef SQInteger (*SQFUNCTION)(HSQUIRRELVM);
+typedef SQInteger (*SQRELEASEHOOK)(SQUserPointer,SQInteger size);
+typedef void (*SQCOMPILERERROR)(HSQUIRRELVM,const SQChar * /*desc*/,const SQChar * /*source*/,SQInteger /*line*/,SQInteger /*column*/);
+typedef void (*SQPRINTFUNCTION)(HSQUIRRELVM,const SQChar * ,...);
+
+typedef SQInteger (*SQWRITEFUNC)(SQUserPointer,SQUserPointer,SQInteger);
+typedef SQInteger (*SQREADFUNC)(SQUserPointer,SQUserPointer,SQInteger);
+
+typedef SQInteger (*SQLEXREADFUNC)(SQUserPointer);
+
+typedef struct tagSQRegFunction{
+ const SQChar *name;
+ SQFUNCTION f;
+ SQInteger nparamscheck;
+ const SQChar *typemask;
+}SQRegFunction;
+
+/*vm*/
+SQUIRREL_API HSQUIRRELVM sq_open(SQInteger initialstacksize);
+SQUIRREL_API HSQUIRRELVM sq_newthread(HSQUIRRELVM friendvm, SQInteger initialstacksize);
+SQUIRREL_API void sq_seterrorhandler(HSQUIRRELVM v);
+SQUIRREL_API void sq_close(HSQUIRRELVM v);
+SQUIRREL_API void sq_setforeignptr(HSQUIRRELVM v,SQUserPointer p);
+SQUIRREL_API SQUserPointer sq_getforeignptr(HSQUIRRELVM v);
+SQUIRREL_API void sq_setprintfunc(HSQUIRRELVM v, SQPRINTFUNCTION printfunc);
+SQUIRREL_API SQPRINTFUNCTION sq_getprintfunc(HSQUIRRELVM v);
+SQUIRREL_API SQRESULT sq_suspendvm(HSQUIRRELVM v);
+SQUIRREL_API SQRESULT sq_wakeupvm(HSQUIRRELVM v,SQBool resumedret,SQBool retval,SQBool raiseerror);
+SQUIRREL_API SQInteger sq_getvmstate(HSQUIRRELVM v);
+
+/*compiler*/
+SQUIRREL_API SQRESULT sq_compile(HSQUIRRELVM v,SQLEXREADFUNC read,SQUserPointer p,const SQChar *sourcename,SQBool raiseerror);
+SQUIRREL_API SQRESULT sq_compilebuffer(HSQUIRRELVM v,const SQChar *s,SQInteger size,const SQChar *sourcename,SQBool raiseerror);
+SQUIRREL_API void sq_enabledebuginfo(HSQUIRRELVM v, SQBool enable);
+SQUIRREL_API void sq_notifyallexceptions(HSQUIRRELVM v, SQBool enable);
+SQUIRREL_API void sq_setcompilererrorhandler(HSQUIRRELVM v,SQCOMPILERERROR f);
+
+/*stack operations*/
+SQUIRREL_API void sq_push(HSQUIRRELVM v,SQInteger idx);
+SQUIRREL_API void sq_pop(HSQUIRRELVM v,SQInteger nelemstopop);
+SQUIRREL_API void sq_poptop(HSQUIRRELVM v);
+SQUIRREL_API void sq_remove(HSQUIRRELVM v,SQInteger idx);
+SQUIRREL_API SQInteger sq_gettop(HSQUIRRELVM v);
+SQUIRREL_API void sq_settop(HSQUIRRELVM v,SQInteger newtop);
+SQUIRREL_API void sq_reservestack(HSQUIRRELVM v,SQInteger nsize);
+SQUIRREL_API SQInteger sq_cmp(HSQUIRRELVM v);
+SQUIRREL_API void sq_move(HSQUIRRELVM dest,HSQUIRRELVM src,SQInteger idx);
+
+/*object creation handling*/
+SQUIRREL_API SQUserPointer sq_newuserdata(HSQUIRRELVM v,SQUnsignedInteger size);
+SQUIRREL_API void sq_newtable(HSQUIRRELVM v);
+SQUIRREL_API void sq_newarray(HSQUIRRELVM v,SQInteger size);
+SQUIRREL_API void sq_newclosure(HSQUIRRELVM v,SQFUNCTION func,SQUnsignedInteger nfreevars);
+SQUIRREL_API SQRESULT sq_setparamscheck(HSQUIRRELVM v,SQInteger nparamscheck,const SQChar *typemask);
+SQUIRREL_API SQRESULT sq_bindenv(HSQUIRRELVM v,SQInteger idx);
+SQUIRREL_API void sq_pushstring(HSQUIRRELVM v,const SQChar *s,SQInteger len);
+SQUIRREL_API void sq_pushfloat(HSQUIRRELVM v,SQFloat f);
+SQUIRREL_API void sq_pushinteger(HSQUIRRELVM v,SQInteger n);
+SQUIRREL_API void sq_pushbool(HSQUIRRELVM v,SQBool b);
+SQUIRREL_API void sq_pushuserpointer(HSQUIRRELVM v,SQUserPointer p);
+SQUIRREL_API void sq_pushnull(HSQUIRRELVM v);
+SQUIRREL_API SQObjectType sq_gettype(HSQUIRRELVM v,SQInteger idx);
+SQUIRREL_API SQInteger sq_getsize(HSQUIRRELVM v,SQInteger idx);
+SQUIRREL_API SQRESULT sq_getbase(HSQUIRRELVM v,SQInteger idx);
+SQUIRREL_API SQBool sq_instanceof(HSQUIRRELVM v);
+SQUIRREL_API void sq_tostring(HSQUIRRELVM v,SQInteger idx);
+SQUIRREL_API void sq_tobool(HSQUIRRELVM v, SQInteger idx, SQBool *b);
+SQUIRREL_API SQRESULT sq_getstring(HSQUIRRELVM v,SQInteger idx,const SQChar **c);
+SQUIRREL_API SQRESULT sq_getinteger(HSQUIRRELVM v,SQInteger idx,SQInteger *i);
+SQUIRREL_API SQRESULT sq_getfloat(HSQUIRRELVM v,SQInteger idx,SQFloat *f);
+SQUIRREL_API SQRESULT sq_getbool(HSQUIRRELVM v,SQInteger idx,SQBool *b);
+SQUIRREL_API SQRESULT sq_getthread(HSQUIRRELVM v,SQInteger idx,HSQUIRRELVM *thread);
+SQUIRREL_API SQRESULT sq_getuserpointer(HSQUIRRELVM v,SQInteger idx,SQUserPointer *p);
+SQUIRREL_API SQRESULT sq_getuserdata(HSQUIRRELVM v,SQInteger idx,SQUserPointer *p,SQUserPointer *typetag);
+SQUIRREL_API SQRESULT sq_settypetag(HSQUIRRELVM v,SQInteger idx,SQUserPointer typetag);
+SQUIRREL_API SQRESULT sq_gettypetag(HSQUIRRELVM v,SQInteger idx,SQUserPointer *typetag);
+SQUIRREL_API void sq_setreleasehook(HSQUIRRELVM v,SQInteger idx,SQRELEASEHOOK hook);
+SQUIRREL_API SQChar *sq_getscratchpad(HSQUIRRELVM v,SQInteger minsize);
+SQUIRREL_API SQRESULT sq_getclosureinfo(HSQUIRRELVM v,SQInteger idx,SQUnsignedInteger *nparams,SQUnsignedInteger *nfreevars);
+SQUIRREL_API SQRESULT sq_setnativeclosurename(HSQUIRRELVM v,SQInteger idx,const SQChar *name);
+SQUIRREL_API SQRESULT sq_setinstanceup(HSQUIRRELVM v, SQInteger idx, SQUserPointer p);
+SQUIRREL_API SQRESULT sq_getinstanceup(HSQUIRRELVM v, SQInteger idx, SQUserPointer *p,SQUserPointer typetag);
+SQUIRREL_API SQRESULT sq_newclass(HSQUIRRELVM v,SQBool hasbase);
+SQUIRREL_API SQRESULT sq_createinstance(HSQUIRRELVM v,SQInteger idx);
+SQUIRREL_API SQRESULT sq_setattributes(HSQUIRRELVM v,SQInteger idx);
+SQUIRREL_API SQRESULT sq_getattributes(HSQUIRRELVM v,SQInteger idx);
+SQUIRREL_API SQRESULT sq_getclass(HSQUIRRELVM v,SQInteger idx);
+SQUIRREL_API void sq_weakref(HSQUIRRELVM v,SQInteger idx);
+SQUIRREL_API SQRESULT sq_getdefaultdelegate(HSQUIRRELVM v,SQObjectType t);
+
+/*object manipulation*/
+SQUIRREL_API void sq_pushroottable(HSQUIRRELVM v);
+SQUIRREL_API void sq_pushregistrytable(HSQUIRRELVM v);
+SQUIRREL_API SQRESULT sq_setroottable(HSQUIRRELVM v);
+SQUIRREL_API SQRESULT sq_newslot(HSQUIRRELVM v, SQInteger idx, SQBool bstatic);
+SQUIRREL_API SQRESULT sq_deleteslot(HSQUIRRELVM v,SQInteger idx,SQBool pushval);
+SQUIRREL_API SQRESULT sq_set(HSQUIRRELVM v,SQInteger idx);
+SQUIRREL_API SQRESULT sq_get(HSQUIRRELVM v,SQInteger idx);
+SQUIRREL_API SQRESULT sq_rawget(HSQUIRRELVM v,SQInteger idx);
+SQUIRREL_API SQRESULT sq_rawset(HSQUIRRELVM v,SQInteger idx);
+SQUIRREL_API SQRESULT sq_rawdeleteslot(HSQUIRRELVM v,SQInteger idx,SQBool pushval);
+SQUIRREL_API SQRESULT sq_arrayappend(HSQUIRRELVM v,SQInteger idx);
+SQUIRREL_API SQRESULT sq_arraypop(HSQUIRRELVM v,SQInteger idx,SQBool pushval);
+SQUIRREL_API SQRESULT sq_arrayresize(HSQUIRRELVM v,SQInteger idx,SQInteger newsize);
+SQUIRREL_API SQRESULT sq_arrayreverse(HSQUIRRELVM v,SQInteger idx);
+SQUIRREL_API SQRESULT sq_setdelegate(HSQUIRRELVM v,SQInteger idx);
+SQUIRREL_API SQRESULT sq_getdelegate(HSQUIRRELVM v,SQInteger idx);
+SQUIRREL_API SQRESULT sq_clone(HSQUIRRELVM v,SQInteger idx);
+SQUIRREL_API SQRESULT sq_setfreevariable(HSQUIRRELVM v,SQInteger idx,SQUnsignedInteger nval);
+SQUIRREL_API SQRESULT sq_next(HSQUIRRELVM v,SQInteger idx);
+SQUIRREL_API SQRESULT sq_getweakrefval(HSQUIRRELVM v,SQInteger idx);
+SQUIRREL_API SQRESULT sq_clear(HSQUIRRELVM v,SQInteger idx);
+
+/*calls*/
+SQUIRREL_API SQRESULT sq_call(HSQUIRRELVM v,SQInteger params,SQBool retval,SQBool raiseerror);
+SQUIRREL_API SQRESULT sq_resume(HSQUIRRELVM v,SQBool retval,SQBool raiseerror);
+SQUIRREL_API const SQChar *sq_getlocal(HSQUIRRELVM v,SQUnsignedInteger level,SQUnsignedInteger idx);
+SQUIRREL_API const SQChar *sq_getfreevariable(HSQUIRRELVM v,SQInteger idx,SQUnsignedInteger nval);
+SQUIRREL_API SQRESULT sq_throwerror(HSQUIRRELVM v,const SQChar *err);
+SQUIRREL_API void sq_reseterror(HSQUIRRELVM v);
+SQUIRREL_API void sq_getlasterror(HSQUIRRELVM v);
+
+/*raw object handling*/
+SQUIRREL_API SQRESULT sq_getstackobj(HSQUIRRELVM v,SQInteger idx,HSQOBJECT *po);
+SQUIRREL_API void sq_pushobject(HSQUIRRELVM v,HSQOBJECT obj);
+SQUIRREL_API void sq_addref(HSQUIRRELVM v,HSQOBJECT *po);
+SQUIRREL_API SQBool sq_release(HSQUIRRELVM v,HSQOBJECT *po);
+SQUIRREL_API void sq_resetobject(HSQOBJECT *po);
+SQUIRREL_API const SQChar *sq_objtostring(HSQOBJECT *o);
+SQUIRREL_API SQBool sq_objtobool(HSQOBJECT *o);
+SQUIRREL_API SQInteger sq_objtointeger(HSQOBJECT *o);
+SQUIRREL_API SQFloat sq_objtofloat(HSQOBJECT *o);
+SQUIRREL_API SQRESULT sq_getobjtypetag(HSQOBJECT *o,SQUserPointer * typetag);
+
+/*GC*/
+SQUIRREL_API SQInteger sq_collectgarbage(HSQUIRRELVM v);
+
+/*serialization*/
+SQUIRREL_API SQRESULT sq_writeclosure(HSQUIRRELVM vm,SQWRITEFUNC writef,SQUserPointer up);
+SQUIRREL_API SQRESULT sq_readclosure(HSQUIRRELVM vm,SQREADFUNC readf,SQUserPointer up);
+
+/*mem allocation*/
+SQUIRREL_API void *sq_malloc(SQUnsignedInteger size);
+SQUIRREL_API void *sq_realloc(void* p,SQUnsignedInteger oldsize,SQUnsignedInteger newsize);
+SQUIRREL_API void sq_free(void *p,SQUnsignedInteger size);
+
+/*debug*/
+SQUIRREL_API SQRESULT sq_stackinfos(HSQUIRRELVM v,SQInteger level,SQStackInfos *si);
+SQUIRREL_API void sq_setdebughook(HSQUIRRELVM v);
+
+/*UTILITY MACRO*/
+#define sq_isnumeric(o) ((o)._type&SQOBJECT_NUMERIC)
+#define sq_istable(o) ((o)._type==OT_TABLE)
+#define sq_isarray(o) ((o)._type==OT_ARRAY)
+#define sq_isfunction(o) ((o)._type==OT_FUNCPROTO)
+#define sq_isclosure(o) ((o)._type==OT_CLOSURE)
+#define sq_isgenerator(o) ((o)._type==OT_GENERATOR)
+#define sq_isnativeclosure(o) ((o)._type==OT_NATIVECLOSURE)
+#define sq_isstring(o) ((o)._type==OT_STRING)
+#define sq_isinteger(o) ((o)._type==OT_INTEGER)
+#define sq_isfloat(o) ((o)._type==OT_FLOAT)
+#define sq_isuserpointer(o) ((o)._type==OT_USERPOINTER)
+#define sq_isuserdata(o) ((o)._type==OT_USERDATA)
+#define sq_isthread(o) ((o)._type==OT_THREAD)
+#define sq_isnull(o) ((o)._type==OT_NULL)
+#define sq_isclass(o) ((o)._type==OT_CLASS)
+#define sq_isinstance(o) ((o)._type==OT_INSTANCE)
+#define sq_isbool(o) ((o)._type==OT_BOOL)
+#define sq_isweakref(o) ((o)._type==OT_WEAKREF)
+#define sq_type(o) ((o)._type)
+
+/* deprecated */
+#define sq_createslot(v,n) sq_newslot(v,n,SQFalse)
+
+#define SQ_OK (0)
+#define SQ_ERROR (-1)
+
+#define SQ_FAILED(res) (res<0)
+#define SQ_SUCCEEDED(res) (res>=0)
+
+#ifdef __cplusplus
+} /*extern "C"*/
+#endif
+
+#endif /*_SQUIRREL_H_*/
--- /dev/null
+static const SQChar serialize_state_nut[] = {
+0x74, 0x72, 0x79, 0x20, 0x7b, 0x0d, 0x0a, 0x09, 0x0d, 0x0a, 0x6c, 0x6f,
+0x63, 0x61, 0x6c, 0x20, 0x6f, 0x62, 0x6a, 0x73, 0x5f, 0x72, 0x65, 0x67,
+0x3d, 0x7b, 0x6d, 0x61, 0x78, 0x69, 0x64, 0x3d, 0x30, 0x2c, 0x72, 0x65,
+0x66, 0x73, 0x3d, 0x7b, 0x7d, 0x7d, 0x0d, 0x0a, 0x0d, 0x0a, 0x63, 0x6f,
+0x6d, 0x70, 0x6c, 0x65, 0x78, 0x5f, 0x74, 0x79, 0x70, 0x65, 0x73, 0x20,
+0x3c, 0x2d, 0x20, 0x7b, 0x0d, 0x0a, 0x09, 0x5b, 0x22, 0x74, 0x61, 0x62,
+0x6c, 0x65, 0x22, 0x5d, 0x20, 0x3d, 0x20, 0x6e, 0x75, 0x6c, 0x6c, 0x2c,
+0x0d, 0x0a, 0x09, 0x5b, 0x22, 0x61, 0x72, 0x72, 0x61, 0x79, 0x22, 0x5d,
+0x20, 0x3d, 0x20, 0x6e, 0x75, 0x6c, 0x6c, 0x2c, 0x0d, 0x0a, 0x09, 0x5b,
+0x22, 0x63, 0x6c, 0x61, 0x73, 0x73, 0x22, 0x5d, 0x20, 0x3d, 0x20, 0x6e,
+0x75, 0x6c, 0x6c, 0x2c, 0x0d, 0x0a, 0x09, 0x5b, 0x22, 0x69, 0x6e, 0x73,
+0x74, 0x61, 0x6e, 0x63, 0x65, 0x22, 0x5d, 0x20, 0x3d, 0x20, 0x6e, 0x75,
+0x6c, 0x6c, 0x2c, 0x0d, 0x0a, 0x09, 0x5b, 0x22, 0x77, 0x65, 0x61, 0x6b,
+0x72, 0x65, 0x66, 0x22, 0x5d, 0x20, 0x3d, 0x20, 0x6e, 0x75, 0x6c, 0x6c,
+0x2c, 0x0d, 0x0a, 0x7d, 0x0d, 0x0a, 0x0d, 0x0a, 0x66, 0x75, 0x6e, 0x63,
+0x74, 0x69, 0x6f, 0x6e, 0x20, 0x62, 0x75, 0x69, 0x6c, 0x64, 0x5f, 0x72,
+0x65, 0x66, 0x73, 0x28, 0x74, 0x29, 0x3a, 0x28, 0x6f, 0x62, 0x6a, 0x73,
+0x5f, 0x72, 0x65, 0x67, 0x29, 0x0d, 0x0a, 0x7b, 0x0d, 0x0a, 0x09, 0x69,
+0x66, 0x28, 0x74, 0x20, 0x3d, 0x3d, 0x20, 0x3a, 0x3a, 0x67, 0x65, 0x74,
+0x72, 0x6f, 0x6f, 0x74, 0x74, 0x61, 0x62, 0x6c, 0x65, 0x28, 0x29, 0x29,
+0x0d, 0x0a, 0x09, 0x09, 0x72, 0x65, 0x74, 0x75, 0x72, 0x6e, 0x3b, 0x0d,
+0x0a, 0x09, 0x6c, 0x6f, 0x63, 0x61, 0x6c, 0x20, 0x6f, 0x74, 0x79, 0x70,
+0x65, 0x20, 0x3d, 0x20, 0x3a, 0x3a, 0x74, 0x79, 0x70, 0x65, 0x28, 0x74,
+0x29, 0x3b, 0x0d, 0x0a, 0x09, 0x69, 0x66, 0x28, 0x6f, 0x74, 0x79, 0x70,
+0x65, 0x20, 0x69, 0x6e, 0x20, 0x63, 0x6f, 0x6d, 0x70, 0x6c, 0x65, 0x78,
+0x5f, 0x74, 0x79, 0x70, 0x65, 0x73, 0x29, 0x0d, 0x0a, 0x09, 0x7b, 0x0d,
+0x0a, 0x09, 0x09, 0x69, 0x66, 0x28, 0x21, 0x28, 0x74, 0x20, 0x69, 0x6e,
+0x20, 0x6f, 0x62, 0x6a, 0x73, 0x5f, 0x72, 0x65, 0x67, 0x2e, 0x72, 0x65,
+0x66, 0x73, 0x29, 0x29, 0x20, 0x7b, 0x0d, 0x0a, 0x09, 0x09, 0x09, 0x6f,
+0x62, 0x6a, 0x73, 0x5f, 0x72, 0x65, 0x67, 0x2e, 0x72, 0x65, 0x66, 0x73,
+0x5b, 0x74, 0x5d, 0x20, 0x3c, 0x2d, 0x20, 0x6f, 0x62, 0x6a, 0x73, 0x5f,
+0x72, 0x65, 0x67, 0x2e, 0x6d, 0x61, 0x78, 0x69, 0x64, 0x2b, 0x2b, 0x3b,
+0x0d, 0x0a, 0x09, 0x09, 0x0d, 0x0a, 0x09, 0x09, 0x20, 0x20, 0x20, 0x20,
+0x69, 0x74, 0x65, 0x72, 0x61, 0x74, 0x65, 0x6f, 0x62, 0x6a, 0x65, 0x63,
+0x74, 0x28, 0x74, 0x2c, 0x66, 0x75, 0x6e, 0x63, 0x74, 0x69, 0x6f, 0x6e,
+0x28, 0x6f, 0x2c, 0x69, 0x2c, 0x76, 0x61, 0x6c, 0x29, 0x3a, 0x28, 0x6f,
+0x62, 0x6a, 0x73, 0x5f, 0x72, 0x65, 0x67, 0x29, 0x0d, 0x0a, 0x09, 0x09,
+0x20, 0x20, 0x20, 0x20, 0x7b, 0x0d, 0x0a, 0x09, 0x09, 0x09, 0x20, 0x20,
+0x20, 0x20, 0x62, 0x75, 0x69, 0x6c, 0x64, 0x5f, 0x72, 0x65, 0x66, 0x73,
+0x28, 0x76, 0x61, 0x6c, 0x29, 0x3b, 0x0d, 0x0a, 0x09, 0x09, 0x09, 0x20,
+0x20, 0x20, 0x20, 0x62, 0x75, 0x69, 0x6c, 0x64, 0x5f, 0x72, 0x65, 0x66,
+0x73, 0x28, 0x69, 0x29, 0x3b, 0x0d, 0x0a, 0x09, 0x09, 0x20, 0x20, 0x20,
+0x20, 0x7d, 0x29, 0x0d, 0x0a, 0x09, 0x09, 0x7d, 0x0d, 0x0a, 0x09, 0x09,
+0x0d, 0x0a, 0x09, 0x7d, 0x0d, 0x0a, 0x7d, 0x0d, 0x0a, 0x0d, 0x0a, 0x66,
+0x75, 0x6e, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x20, 0x67, 0x65, 0x74, 0x76,
+0x61, 0x6c, 0x75, 0x65, 0x28, 0x76, 0x29, 0x3a, 0x28, 0x6f, 0x62, 0x6a,
+0x73, 0x5f, 0x72, 0x65, 0x67, 0x29, 0x0d, 0x0a, 0x7b, 0x0d, 0x0a, 0x09,
+0x73, 0x77, 0x69, 0x74, 0x63, 0x68, 0x28, 0x3a, 0x3a, 0x74, 0x79, 0x70,
+0x65, 0x28, 0x76, 0x29, 0x29, 0x0d, 0x0a, 0x09, 0x7b, 0x0d, 0x0a, 0x09,
+0x09, 0x63, 0x61, 0x73, 0x65, 0x20, 0x22, 0x74, 0x61, 0x62, 0x6c, 0x65,
+0x22, 0x3a, 0x0d, 0x0a, 0x09, 0x09, 0x63, 0x61, 0x73, 0x65, 0x20, 0x22,
+0x61, 0x72, 0x72, 0x61, 0x79, 0x22, 0x3a, 0x0d, 0x0a, 0x09, 0x09, 0x63,
+0x61, 0x73, 0x65, 0x20, 0x22, 0x63, 0x6c, 0x61, 0x73, 0x73, 0x22, 0x3a,
+0x0d, 0x0a, 0x09, 0x09, 0x63, 0x61, 0x73, 0x65, 0x20, 0x22, 0x69, 0x6e,
+0x73, 0x74, 0x61, 0x6e, 0x63, 0x65, 0x22, 0x3a, 0x0d, 0x0a, 0x09, 0x09,
+0x09, 0x72, 0x65, 0x74, 0x75, 0x72, 0x6e, 0x20, 0x6f, 0x62, 0x6a, 0x73,
+0x5f, 0x72, 0x65, 0x67, 0x2e, 0x72, 0x65, 0x66, 0x73, 0x5b, 0x76, 0x5d,
+0x2e, 0x74, 0x6f, 0x73, 0x74, 0x72, 0x69, 0x6e, 0x67, 0x28, 0x29, 0x3b,
+0x0d, 0x0a, 0x09, 0x09, 0x63, 0x61, 0x73, 0x65, 0x20, 0x22, 0x69, 0x6e,
+0x74, 0x65, 0x67, 0x65, 0x72, 0x22, 0x3a, 0x0d, 0x0a, 0x09, 0x09, 0x63,
+0x61, 0x73, 0x65, 0x20, 0x22, 0x66, 0x6c, 0x6f, 0x61, 0x74, 0x22, 0x3a,
+0x0d, 0x0a, 0x09, 0x09, 0x20, 0x20, 0x20, 0x20, 0x72, 0x65, 0x74, 0x75,
+0x72, 0x6e, 0x20, 0x76, 0x3b, 0x0d, 0x0a, 0x09, 0x09, 0x63, 0x61, 0x73,
+0x65, 0x20, 0x22, 0x62, 0x6f, 0x6f, 0x6c, 0x22, 0x3a, 0x0d, 0x0a, 0x09,
+0x09, 0x20, 0x20, 0x20, 0x20, 0x72, 0x65, 0x74, 0x75, 0x72, 0x6e, 0x20,
+0x76, 0x2e, 0x74, 0x6f, 0x73, 0x74, 0x72, 0x69, 0x6e, 0x67, 0x28, 0x29,
+0x3b, 0x0d, 0x0a, 0x09, 0x09, 0x63, 0x61, 0x73, 0x65, 0x20, 0x22, 0x73,
+0x74, 0x72, 0x69, 0x6e, 0x67, 0x22, 0x3a, 0x0d, 0x0a, 0x09, 0x09, 0x09,
+0x72, 0x65, 0x74, 0x75, 0x72, 0x6e, 0x20, 0x76, 0x3b, 0x0d, 0x0a, 0x09,
+0x09, 0x63, 0x61, 0x73, 0x65, 0x20, 0x22, 0x6e, 0x75, 0x6c, 0x6c, 0x22,
+0x3a, 0x0d, 0x0a, 0x09, 0x09, 0x20, 0x20, 0x20, 0x20, 0x72, 0x65, 0x74,
+0x75, 0x72, 0x6e, 0x20, 0x22, 0x6e, 0x75, 0x6c, 0x6c, 0x22, 0x3b, 0x0d,
+0x0a, 0x09, 0x09, 0x64, 0x65, 0x66, 0x61, 0x75, 0x6c, 0x74, 0x3a, 0x0d,
+0x0a, 0x09, 0x09, 0x09, 0x0d, 0x0a, 0x09, 0x09, 0x09, 0x72, 0x65, 0x74,
+0x75, 0x72, 0x6e, 0x20, 0x70, 0x61, 0x63, 0x6b, 0x5f, 0x74, 0x79, 0x70,
+0x65, 0x28, 0x3a, 0x3a, 0x74, 0x79, 0x70, 0x65, 0x28, 0x76, 0x29, 0x29,
+0x3b, 0x0d, 0x0a, 0x09, 0x7d, 0x0d, 0x0a, 0x7d, 0x0d, 0x0a, 0x0d, 0x0a,
+0x6c, 0x6f, 0x63, 0x61, 0x6c, 0x20, 0x70, 0x61, 0x63, 0x6b, 0x65, 0x64,
+0x5f, 0x74, 0x79, 0x70, 0x65, 0x73, 0x3d, 0x7b, 0x0d, 0x0a, 0x09, 0x5b,
+0x22, 0x6e, 0x75, 0x6c, 0x6c, 0x22, 0x5d, 0x3d, 0x22, 0x6e, 0x22, 0x2c,
+0x0d, 0x0a, 0x09, 0x5b, 0x22, 0x73, 0x74, 0x72, 0x69, 0x6e, 0x67, 0x22,
+0x5d, 0x3d, 0x22, 0x73, 0x22, 0x2c, 0x0d, 0x0a, 0x09, 0x5b, 0x22, 0x69,
+0x6e, 0x74, 0x65, 0x67, 0x65, 0x72, 0x22, 0x5d, 0x3d, 0x22, 0x69, 0x22,
+0x2c, 0x0d, 0x0a, 0x09, 0x5b, 0x22, 0x66, 0x6c, 0x6f, 0x61, 0x74, 0x22,
+0x5d, 0x3d, 0x22, 0x66, 0x22, 0x2c, 0x0d, 0x0a, 0x09, 0x5b, 0x22, 0x75,
+0x73, 0x65, 0x72, 0x64, 0x61, 0x74, 0x61, 0x22, 0x5d, 0x3d, 0x22, 0x75,
+0x22, 0x2c, 0x0d, 0x0a, 0x09, 0x5b, 0x22, 0x66, 0x75, 0x6e, 0x63, 0x74,
+0x69, 0x6f, 0x6e, 0x22, 0x5d, 0x3d, 0x22, 0x66, 0x6e, 0x22, 0x2c, 0x0d,
+0x0a, 0x09, 0x5b, 0x22, 0x74, 0x61, 0x62, 0x6c, 0x65, 0x22, 0x5d, 0x3d,
+0x22, 0x74, 0x22, 0x2c, 0x0d, 0x0a, 0x09, 0x5b, 0x22, 0x61, 0x72, 0x72,
+0x61, 0x79, 0x22, 0x5d, 0x3d, 0x22, 0x61, 0x22, 0x2c, 0x0d, 0x0a, 0x09,
+0x5b, 0x22, 0x67, 0x65, 0x6e, 0x65, 0x72, 0x61, 0x74, 0x6f, 0x72, 0x22,
+0x5d, 0x3d, 0x22, 0x67, 0x22, 0x2c, 0x0d, 0x0a, 0x09, 0x5b, 0x22, 0x74,
+0x68, 0x72, 0x65, 0x61, 0x64, 0x22, 0x5d, 0x3d, 0x22, 0x68, 0x22, 0x2c,
+0x0d, 0x0a, 0x09, 0x5b, 0x22, 0x69, 0x6e, 0x73, 0x74, 0x61, 0x6e, 0x63,
+0x65, 0x22, 0x5d, 0x3d, 0x22, 0x78, 0x22, 0x2c, 0x20, 0x0d, 0x0a, 0x09,
+0x5b, 0x22, 0x63, 0x6c, 0x61, 0x73, 0x73, 0x22, 0x5d, 0x3d, 0x22, 0x79,
+0x22, 0x2c, 0x20, 0x20, 0x0d, 0x0a, 0x09, 0x5b, 0x22, 0x62, 0x6f, 0x6f,
+0x6c, 0x22, 0x5d, 0x3d, 0x22, 0x62, 0x22, 0x2c, 0x0d, 0x0a, 0x09, 0x5b,
+0x22, 0x77, 0x65, 0x61, 0x6b, 0x72, 0x65, 0x66, 0x22, 0x5d, 0x3d, 0x22,
+0x77, 0x22, 0x20, 0x20, 0x0d, 0x0a, 0x7d, 0x0d, 0x0a, 0x0d, 0x0a, 0x66,
+0x75, 0x6e, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x20, 0x70, 0x61, 0x63, 0x6b,
+0x5f, 0x74, 0x79, 0x70, 0x65, 0x28, 0x74, 0x79, 0x70, 0x65, 0x29, 0x3a,
+0x28, 0x70, 0x61, 0x63, 0x6b, 0x65, 0x64, 0x5f, 0x74, 0x79, 0x70, 0x65,
+0x73, 0x29, 0x0d, 0x0a, 0x7b, 0x0d, 0x0a, 0x09, 0x69, 0x66, 0x28, 0x74,
+0x79, 0x70, 0x65, 0x20, 0x69, 0x6e, 0x20, 0x70, 0x61, 0x63, 0x6b, 0x65,
+0x64, 0x5f, 0x74, 0x79, 0x70, 0x65, 0x73, 0x29, 0x72, 0x65, 0x74, 0x75,
+0x72, 0x6e, 0x20, 0x70, 0x61, 0x63, 0x6b, 0x65, 0x64, 0x5f, 0x74, 0x79,
+0x70, 0x65, 0x73, 0x5b, 0x74, 0x79, 0x70, 0x65, 0x5d, 0x0d, 0x0a, 0x09,
+0x72, 0x65, 0x74, 0x75, 0x72, 0x6e, 0x20, 0x74, 0x79, 0x70, 0x65, 0x0d,
+0x0a, 0x7d, 0x20, 0x0d, 0x0a, 0x0d, 0x0a, 0x66, 0x75, 0x6e, 0x63, 0x74,
+0x69, 0x6f, 0x6e, 0x20, 0x69, 0x74, 0x65, 0x72, 0x61, 0x74, 0x65, 0x6f,
+0x62, 0x6a, 0x65, 0x63, 0x74, 0x28, 0x6f, 0x62, 0x6a, 0x2c, 0x66, 0x75,
+0x6e, 0x63, 0x29, 0x0d, 0x0a, 0x7b, 0x0d, 0x0a, 0x09, 0x6c, 0x6f, 0x63,
+0x61, 0x6c, 0x20, 0x74, 0x79, 0x20, 0x3d, 0x20, 0x3a, 0x3a, 0x74, 0x79,
+0x70, 0x65, 0x28, 0x6f, 0x62, 0x6a, 0x29, 0x3b, 0x0d, 0x0a, 0x09, 0x69,
+0x66, 0x28, 0x74, 0x79, 0x20, 0x3d, 0x3d, 0x20, 0x22, 0x69, 0x6e, 0x73,
+0x74, 0x61, 0x6e, 0x63, 0x65, 0x22, 0x29, 0x20, 0x7b, 0x0d, 0x0a, 0x09,
+0x09, 0x74, 0x72, 0x79, 0x20, 0x7b, 0x20, 0x2f, 0x2f, 0x54, 0x52, 0x59,
+0x20, 0x54, 0x4f, 0x20, 0x55, 0x53, 0x45, 0x20, 0x5f, 0x6e, 0x65, 0x78,
+0x74, 0x69, 0x0d, 0x0a, 0x09, 0x09, 0x20, 0x20, 0x20, 0x20, 0x66, 0x6f,
+0x72, 0x65, 0x61, 0x63, 0x68, 0x28, 0x69, 0x64, 0x78, 0x2c, 0x76, 0x61,
+0x6c, 0x20, 0x69, 0x6e, 0x20, 0x6f, 0x62, 0x6a, 0x29, 0x0d, 0x0a, 0x09,
+0x09, 0x20, 0x20, 0x20, 0x20, 0x7b, 0x0d, 0x0a, 0x09, 0x09, 0x09, 0x09,
+0x66, 0x75, 0x6e, 0x63, 0x28, 0x6f, 0x62, 0x6a, 0x2c, 0x69, 0x64, 0x78,
+0x2c, 0x76, 0x61, 0x6c, 0x29, 0x3b, 0x0d, 0x0a, 0x09, 0x09, 0x20, 0x20,
+0x20, 0x20, 0x7d, 0x0d, 0x0a, 0x09, 0x09, 0x7d, 0x0d, 0x0a, 0x09, 0x09,
+0x63, 0x61, 0x74, 0x63, 0x68, 0x28, 0x65, 0x29, 0x20, 0x7b, 0x0d, 0x0a,
+0x09, 0x09, 0x20, 0x20, 0x20, 0x66, 0x6f, 0x72, 0x65, 0x61, 0x63, 0x68,
+0x28, 0x69, 0x64, 0x78, 0x2c, 0x76, 0x61, 0x6c, 0x20, 0x69, 0x6e, 0x20,
+0x6f, 0x62, 0x6a, 0x2e, 0x67, 0x65, 0x74, 0x63, 0x6c, 0x61, 0x73, 0x73,
+0x28, 0x29, 0x29, 0x0d, 0x0a, 0x09, 0x09, 0x20, 0x20, 0x20, 0x7b, 0x0d,
+0x0a, 0x09, 0x09, 0x09, 0x66, 0x75, 0x6e, 0x63, 0x28, 0x6f, 0x62, 0x6a,
+0x2c, 0x69, 0x64, 0x78, 0x2c, 0x6f, 0x62, 0x6a, 0x5b, 0x69, 0x64, 0x78,
+0x5d, 0x29, 0x3b, 0x0d, 0x0a, 0x09, 0x09, 0x20, 0x20, 0x20, 0x7d, 0x0d,
+0x0a, 0x09, 0x09, 0x7d, 0x0d, 0x0a, 0x09, 0x7d, 0x0d, 0x0a, 0x09, 0x65,
+0x6c, 0x73, 0x65, 0x20, 0x69, 0x66, 0x28, 0x74, 0x79, 0x20, 0x3d, 0x3d,
+0x20, 0x22, 0x77, 0x65, 0x61, 0x6b, 0x72, 0x65, 0x66, 0x22, 0x29, 0x20,
+0x7b, 0x0d, 0x0a, 0x09, 0x09, 0x66, 0x75, 0x6e, 0x63, 0x28, 0x6f, 0x62,
+0x6a, 0x2c, 0x22, 0x40, 0x72, 0x65, 0x66, 0x22, 0x2c, 0x6f, 0x62, 0x6a,
+0x2e, 0x72, 0x65, 0x66, 0x28, 0x29, 0x29, 0x3b, 0x0d, 0x0a, 0x09, 0x7d,
+0x0d, 0x0a, 0x09, 0x65, 0x6c, 0x73, 0x65, 0x20, 0x7b, 0x0d, 0x0a, 0x09,
+0x09, 0x66, 0x6f, 0x72, 0x65, 0x61, 0x63, 0x68, 0x28, 0x69, 0x64, 0x78,
+0x2c, 0x76, 0x61, 0x6c, 0x20, 0x69, 0x6e, 0x20, 0x6f, 0x62, 0x6a, 0x29,
+0x0d, 0x0a, 0x09, 0x09, 0x7b, 0x0d, 0x0a, 0x09, 0x09, 0x20, 0x20, 0x20,
+0x20, 0x66, 0x75, 0x6e, 0x63, 0x28, 0x6f, 0x62, 0x6a, 0x2c, 0x69, 0x64,
+0x78, 0x2c, 0x76, 0x61, 0x6c, 0x29, 0x3b, 0x0d, 0x0a, 0x09, 0x09, 0x7d,
+0x0d, 0x0a, 0x09, 0x7d, 0x0d, 0x0a, 0x09, 0x09, 0x09, 0x0d, 0x0a, 0x7d,
+0x0d, 0x0a, 0x0d, 0x0a, 0x66, 0x75, 0x6e, 0x63, 0x74, 0x69, 0x6f, 0x6e,
+0x20, 0x62, 0x75, 0x69, 0x6c, 0x64, 0x5f, 0x74, 0x72, 0x65, 0x65, 0x28,
+0x29, 0x3a, 0x28, 0x6f, 0x62, 0x6a, 0x73, 0x5f, 0x72, 0x65, 0x67, 0x29,
+0x0d, 0x0a, 0x7b, 0x0d, 0x0a, 0x09, 0x66, 0x6f, 0x72, 0x65, 0x61, 0x63,
+0x68, 0x28, 0x69, 0x2c, 0x6f, 0x20, 0x69, 0x6e, 0x20, 0x6f, 0x62, 0x6a,
+0x73, 0x5f, 0x72, 0x65, 0x67, 0x2e, 0x72, 0x65, 0x66, 0x73, 0x29, 0x0d,
+0x0a, 0x09, 0x7b, 0x0d, 0x0a, 0x09, 0x09, 0x62, 0x65, 0x67, 0x69, 0x6e,
+0x65, 0x6c, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x28, 0x22, 0x6f, 0x22, 0x29,
+0x3b, 0x0d, 0x0a, 0x09, 0x09, 0x61, 0x74, 0x74, 0x72, 0x69, 0x62, 0x75,
+0x74, 0x65, 0x28, 0x22, 0x74, 0x79, 0x70, 0x65, 0x22, 0x2c, 0x28, 0x69,
+0x3d, 0x3d, 0x3a, 0x3a, 0x67, 0x65, 0x74, 0x72, 0x6f, 0x6f, 0x74, 0x74,
+0x61, 0x62, 0x6c, 0x65, 0x28, 0x29, 0x3f, 0x22, 0x72, 0x22, 0x3a, 0x70,
+0x61, 0x63, 0x6b, 0x5f, 0x74, 0x79, 0x70, 0x65, 0x28, 0x3a, 0x3a, 0x74,
+0x79, 0x70, 0x65, 0x28, 0x69, 0x29, 0x29, 0x29, 0x29, 0x3b, 0x0d, 0x0a,
+0x09, 0x09, 0x6c, 0x6f, 0x63, 0x61, 0x6c, 0x20, 0x5f, 0x74, 0x79, 0x70,
+0x65, 0x6f, 0x66, 0x20, 0x3d, 0x20, 0x74, 0x79, 0x70, 0x65, 0x6f, 0x66,
+0x20, 0x69, 0x3b, 0x0d, 0x0a, 0x09, 0x09, 0x69, 0x66, 0x28, 0x5f, 0x74,
+0x79, 0x70, 0x65, 0x6f, 0x66, 0x20, 0x21, 0x3d, 0x20, 0x3a, 0x3a, 0x74,
+0x79, 0x70, 0x65, 0x28, 0x69, 0x29, 0x29, 0x20, 0x7b, 0x0d, 0x0a, 0x09,
+0x09, 0x09, 0x61, 0x74, 0x74, 0x72, 0x69, 0x62, 0x75, 0x74, 0x65, 0x28,
+0x22, 0x74, 0x79, 0x70, 0x65, 0x6f, 0x66, 0x22, 0x2c, 0x5f, 0x74, 0x79,
+0x70, 0x65, 0x6f, 0x66, 0x29, 0x3b, 0x0d, 0x0a, 0x09, 0x09, 0x7d, 0x0d,
+0x0a, 0x09, 0x09, 0x61, 0x74, 0x74, 0x72, 0x69, 0x62, 0x75, 0x74, 0x65,
+0x28, 0x22, 0x72, 0x65, 0x66, 0x22, 0x2c, 0x6f, 0x2e, 0x74, 0x6f, 0x73,
+0x74, 0x72, 0x69, 0x6e, 0x67, 0x28, 0x29, 0x29, 0x3b, 0x0d, 0x0a, 0x09,
+0x09, 0x69, 0x66, 0x28, 0x69, 0x20, 0x21, 0x3d, 0x20, 0x3a, 0x3a, 0x67,
+0x65, 0x74, 0x72, 0x6f, 0x6f, 0x74, 0x74, 0x61, 0x62, 0x6c, 0x65, 0x28,
+0x29, 0x29, 0x7b, 0x0d, 0x0a, 0x09, 0x09, 0x09, 0x69, 0x74, 0x65, 0x72,
+0x61, 0x74, 0x65, 0x6f, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x28, 0x69, 0x2c,
+0x66, 0x75, 0x6e, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x20, 0x28, 0x6f, 0x62,
+0x6a, 0x2c, 0x69, 0x64, 0x78, 0x2c, 0x76, 0x61, 0x6c, 0x29, 0x20, 0x7b,
+0x0d, 0x0a, 0x09, 0x09, 0x09, 0x09, 0x69, 0x66, 0x28, 0x3a, 0x3a, 0x74,
+0x79, 0x70, 0x65, 0x28, 0x76, 0x61, 0x6c, 0x29, 0x20, 0x3d, 0x3d, 0x20,
+0x22, 0x66, 0x75, 0x6e, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x22, 0x29, 0x0d,
+0x0a, 0x09, 0x09, 0x09, 0x09, 0x09, 0x72, 0x65, 0x74, 0x75, 0x72, 0x6e,
+0x3b, 0x0d, 0x0a, 0x09, 0x09, 0x09, 0x09, 0x09, 0x0d, 0x0a, 0x09, 0x09,
+0x09, 0x09, 0x62, 0x65, 0x67, 0x69, 0x6e, 0x65, 0x6c, 0x65, 0x6d, 0x65,
+0x6e, 0x74, 0x28, 0x22, 0x65, 0x22, 0x29, 0x3b, 0x09, 0x0d, 0x0a, 0x09,
+0x09, 0x09, 0x09, 0x09, 0x65, 0x6d, 0x69, 0x74, 0x76, 0x61, 0x6c, 0x75,
+0x65, 0x28, 0x22, 0x6b, 0x74, 0x22, 0x2c, 0x22, 0x6b, 0x76, 0x22, 0x2c,
+0x69, 0x64, 0x78, 0x29, 0x3b, 0x0d, 0x0a, 0x09, 0x09, 0x09, 0x09, 0x09,
+0x65, 0x6d, 0x69, 0x74, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x28, 0x22, 0x76,
+0x74, 0x22, 0x2c, 0x22, 0x76, 0x22, 0x2c, 0x6f, 0x62, 0x6a, 0x5b, 0x69,
+0x64, 0x78, 0x5d, 0x29, 0x3b, 0x0d, 0x0a, 0x09, 0x09, 0x09, 0x09, 0x65,
+0x6e, 0x64, 0x65, 0x6c, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x28, 0x22, 0x65,
+0x22, 0x29, 0x3b, 0x09, 0x0d, 0x0a, 0x0d, 0x0a, 0x09, 0x09, 0x09, 0x7d,
+0x29, 0x0d, 0x0a, 0x09, 0x09, 0x7d, 0x0d, 0x0a, 0x09, 0x09, 0x65, 0x6e,
+0x64, 0x65, 0x6c, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x28, 0x22, 0x6f, 0x22,
+0x29, 0x3b, 0x0d, 0x0a, 0x09, 0x7d, 0x0d, 0x0a, 0x7d, 0x0d, 0x0a, 0x0d,
+0x0a, 0x66, 0x75, 0x6e, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x20, 0x65, 0x76,
+0x61, 0x6c, 0x75, 0x61, 0x74, 0x65, 0x5f, 0x77, 0x61, 0x74, 0x63, 0x68,
+0x28, 0x6c, 0x6f, 0x63, 0x61, 0x6c, 0x73, 0x2c, 0x69, 0x64, 0x2c, 0x65,
+0x78, 0x70, 0x72, 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x29, 0x0d, 0x0a,
+0x7b, 0x0d, 0x0a, 0x09, 0x6c, 0x6f, 0x63, 0x61, 0x6c, 0x20, 0x66, 0x75,
+0x6e, 0x63, 0x5f, 0x73, 0x72, 0x63, 0x3d, 0x22, 0x72, 0x65, 0x74, 0x75,
+0x72, 0x6e, 0x20, 0x66, 0x75, 0x6e, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x20,
+0x28, 0x22, 0x0d, 0x0a, 0x09, 0x6c, 0x6f, 0x63, 0x61, 0x6c, 0x20, 0x70,
+0x61, 0x72, 0x61, 0x6d, 0x73, 0x3d, 0x5b, 0x5d, 0x3b, 0x0d, 0x0a, 0x09,
+0x0d, 0x0a, 0x09, 0x70, 0x61, 0x72, 0x61, 0x6d, 0x73, 0x2e, 0x61, 0x70,
+0x70, 0x65, 0x6e, 0x64, 0x28, 0x6c, 0x6f, 0x63, 0x61, 0x6c, 0x73, 0x5b,
+0x22, 0x74, 0x68, 0x69, 0x73, 0x22, 0x5d, 0x29, 0x0d, 0x0a, 0x09, 0x6c,
+0x6f, 0x63, 0x61, 0x6c, 0x20, 0x66, 0x69, 0x72, 0x73, 0x74, 0x3d, 0x31,
+0x3b, 0x0d, 0x0a, 0x09, 0x66, 0x6f, 0x72, 0x65, 0x61, 0x63, 0x68, 0x28,
+0x69, 0x2c, 0x76, 0x20, 0x69, 0x6e, 0x20, 0x6c, 0x6f, 0x63, 0x61, 0x6c,
+0x73, 0x29, 0x7b, 0x0d, 0x0a, 0x09, 0x09, 0x69, 0x66, 0x28, 0x69, 0x21,
+0x3d, 0x22, 0x74, 0x68, 0x69, 0x73, 0x22, 0x20, 0x26, 0x26, 0x20, 0x69,
+0x5b, 0x30, 0x5d, 0x20, 0x21, 0x3d, 0x20, 0x27, 0x40, 0x27, 0x29, 0x7b,
+0x20, 0x2f, 0x2f, 0x66, 0x6f, 0x72, 0x65, 0x61, 0x63, 0x68, 0x20, 0x69,
+0x74, 0x65, 0x72, 0x61, 0x74, 0x6f, 0x72, 0x73, 0x20, 0x73, 0x74, 0x61,
+0x72, 0x74, 0x20, 0x77, 0x69, 0x74, 0x68, 0x20, 0x40, 0x0d, 0x0a, 0x09,
+0x09, 0x09, 0x69, 0x66, 0x28, 0x21, 0x66, 0x69, 0x72, 0x73, 0x74, 0x29,
+0x7b, 0x0d, 0x0a, 0x09, 0x09, 0x09, 0x09, 0x66, 0x75, 0x6e, 0x63, 0x5f,
+0x73, 0x72, 0x63, 0x3d, 0x66, 0x75, 0x6e, 0x63, 0x5f, 0x73, 0x72, 0x63,
+0x2b, 0x22, 0x2c, 0x22, 0x0d, 0x0a, 0x09, 0x09, 0x09, 0x09, 0x0d, 0x0a,
+0x09, 0x09, 0x09, 0x7d, 0x0d, 0x0a, 0x09, 0x09, 0x09, 0x66, 0x69, 0x72,
+0x73, 0x74, 0x3d, 0x6e, 0x75, 0x6c, 0x6c, 0x0d, 0x0a, 0x09, 0x09, 0x09,
+0x70, 0x61, 0x72, 0x61, 0x6d, 0x73, 0x2e, 0x61, 0x70, 0x70, 0x65, 0x6e,
+0x64, 0x28, 0x76, 0x29, 0x0d, 0x0a, 0x09, 0x09, 0x09, 0x66, 0x75, 0x6e,
+0x63, 0x5f, 0x73, 0x72, 0x63, 0x3d, 0x66, 0x75, 0x6e, 0x63, 0x5f, 0x73,
+0x72, 0x63, 0x2b, 0x69, 0x0d, 0x0a, 0x09, 0x09, 0x7d, 0x0d, 0x0a, 0x09,
+0x7d, 0x0d, 0x0a, 0x09, 0x66, 0x75, 0x6e, 0x63, 0x5f, 0x73, 0x72, 0x63,
+0x3d, 0x66, 0x75, 0x6e, 0x63, 0x5f, 0x73, 0x72, 0x63, 0x2b, 0x22, 0x29,
+0x7b, 0x5c, 0x6e, 0x22, 0x0d, 0x0a, 0x09, 0x66, 0x75, 0x6e, 0x63, 0x5f,
+0x73, 0x72, 0x63, 0x3d, 0x66, 0x75, 0x6e, 0x63, 0x5f, 0x73, 0x72, 0x63,
+0x2b, 0x22, 0x72, 0x65, 0x74, 0x75, 0x72, 0x6e, 0x20, 0x28, 0x22, 0x2b,
+0x65, 0x78, 0x70, 0x72, 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x2b, 0x22,
+0x29, 0x5c, 0x6e, 0x7d, 0x22, 0x0d, 0x0a, 0x09, 0x0d, 0x0a, 0x09, 0x74,
+0x72, 0x79, 0x20, 0x7b, 0x0d, 0x0a, 0x09, 0x09, 0x6c, 0x6f, 0x63, 0x61,
+0x6c, 0x20, 0x66, 0x75, 0x6e, 0x63, 0x3d, 0x3a, 0x3a, 0x63, 0x6f, 0x6d,
+0x70, 0x69, 0x6c, 0x65, 0x73, 0x74, 0x72, 0x69, 0x6e, 0x67, 0x28, 0x66,
+0x75, 0x6e, 0x63, 0x5f, 0x73, 0x72, 0x63, 0x29, 0x3b, 0x0d, 0x0a, 0x09,
+0x09, 0x72, 0x65, 0x74, 0x75, 0x72, 0x6e, 0x20, 0x7b, 0x73, 0x74, 0x61,
+0x74, 0x75, 0x73, 0x3d, 0x22, 0x6f, 0x6b, 0x22, 0x20, 0x2c, 0x20, 0x76,
+0x61, 0x6c, 0x3d, 0x66, 0x75, 0x6e, 0x63, 0x28, 0x29, 0x2e, 0x61, 0x63,
+0x61, 0x6c, 0x6c, 0x28, 0x70, 0x61, 0x72, 0x61, 0x6d, 0x73, 0x29, 0x7d,
+0x3b, 0x0d, 0x0a, 0x09, 0x7d, 0x0d, 0x0a, 0x09, 0x63, 0x61, 0x74, 0x63,
+0x68, 0x28, 0x65, 0x29, 0x0d, 0x0a, 0x09, 0x7b, 0x0d, 0x0a, 0x09, 0x09,
+0x0d, 0x0a, 0x09, 0x09, 0x72, 0x65, 0x74, 0x75, 0x72, 0x6e, 0x20, 0x7b,
+0x73, 0x74, 0x61, 0x74, 0x75, 0x73, 0x3d, 0x22, 0x65, 0x72, 0x72, 0x6f,
+0x72, 0x22, 0x7d, 0x0d, 0x0a, 0x09, 0x7d, 0x0d, 0x0a, 0x7d, 0x0d, 0x0a,
+0x0d, 0x0a, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f,
+0x2f, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f,
+0x2f, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f,
+0x2f, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f,
+0x2f, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f,
+0x2f, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f,
+0x2f, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f, 0x0d, 0x0a, 0x2f, 0x2f, 0x2f,
+0x2f, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f,
+0x2f, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f,
+0x2f, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f,
+0x2f, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f,
+0x2f, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f,
+0x2f, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f,
+0x2f, 0x2f, 0x0d, 0x0a, 0x66, 0x75, 0x6e, 0x63, 0x74, 0x69, 0x6f, 0x6e,
+0x20, 0x65, 0x6d, 0x69, 0x74, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x28, 0x74,
+0x79, 0x70, 0x65, 0x5f, 0x61, 0x74, 0x74, 0x72, 0x69, 0x62, 0x2c, 0x76,
+0x61, 0x6c, 0x75, 0x65, 0x5f, 0x61, 0x74, 0x74, 0x72, 0x69, 0x62, 0x2c,
+0x76, 0x61, 0x6c, 0x29, 0x0d, 0x0a, 0x7b, 0x0d, 0x0a, 0x09, 0x61, 0x74,
+0x74, 0x72, 0x69, 0x62, 0x75, 0x74, 0x65, 0x28, 0x74, 0x79, 0x70, 0x65,
+0x5f, 0x61, 0x74, 0x74, 0x72, 0x69, 0x62, 0x2c, 0x70, 0x61, 0x63, 0x6b,
+0x5f, 0x74, 0x79, 0x70, 0x65, 0x28, 0x3a, 0x3a, 0x74, 0x79, 0x70, 0x65,
+0x28, 0x76, 0x61, 0x6c, 0x29, 0x29, 0x29, 0x3b, 0x0d, 0x0a, 0x09, 0x61,
+0x74, 0x74, 0x72, 0x69, 0x62, 0x75, 0x74, 0x65, 0x28, 0x76, 0x61, 0x6c,
+0x75, 0x65, 0x5f, 0x61, 0x74, 0x74, 0x72, 0x69, 0x62, 0x2c, 0x67, 0x65,
+0x74, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x28, 0x76, 0x61, 0x6c, 0x29, 0x2e,
+0x74, 0x6f, 0x73, 0x74, 0x72, 0x69, 0x6e, 0x67, 0x28, 0x29, 0x29, 0x3b,
+0x0d, 0x0a, 0x7d, 0x0d, 0x0a, 0x0d, 0x0a, 0x6c, 0x6f, 0x63, 0x61, 0x6c,
+0x20, 0x73, 0x74, 0x61, 0x63, 0x6b, 0x3d, 0x5b, 0x5d, 0x0d, 0x0a, 0x6c,
+0x6f, 0x63, 0x61, 0x6c, 0x20, 0x6c, 0x65, 0x76, 0x65, 0x6c, 0x3d, 0x33,
+0x3b, 0x0d, 0x0a, 0x6c, 0x6f, 0x63, 0x61, 0x6c, 0x20, 0x73, 0x69, 0x3b,
+0x0d, 0x0a, 0x0d, 0x0a, 0x2f, 0x2f, 0x74, 0x72, 0x79, 0x20, 0x7b, 0x0d,
+0x0a, 0x09, 0x2f, 0x2f, 0x45, 0x4e, 0x55, 0x4d, 0x45, 0x52, 0x41, 0x54,
+0x45, 0x20, 0x54, 0x48, 0x45, 0x20, 0x53, 0x54, 0x41, 0x43, 0x4b, 0x20,
+0x57, 0x41, 0x54, 0x43, 0x48, 0x45, 0x53, 0x0d, 0x0a, 0x09, 0x77, 0x68,
+0x69, 0x6c, 0x65, 0x28, 0x73, 0x69, 0x3d, 0x3a, 0x3a, 0x67, 0x65, 0x74,
+0x73, 0x74, 0x61, 0x63, 0x6b, 0x69, 0x6e, 0x66, 0x6f, 0x73, 0x28, 0x6c,
+0x65, 0x76, 0x65, 0x6c, 0x29, 0x29, 0x0d, 0x0a, 0x09, 0x7b, 0x0d, 0x0a,
+0x09, 0x09, 0x73, 0x74, 0x61, 0x63, 0x6b, 0x2e, 0x61, 0x70, 0x70, 0x65,
+0x6e, 0x64, 0x28, 0x73, 0x69, 0x29, 0x3b, 0x0d, 0x0a, 0x09, 0x09, 0x6c,
+0x65, 0x76, 0x65, 0x6c, 0x2b, 0x2b, 0x3b, 0x0d, 0x0a, 0x09, 0x7d, 0x0d,
+0x0a, 0x0d, 0x0a, 0x09, 0x2f, 0x2f, 0x45, 0x56, 0x41, 0x4c, 0x55, 0x41,
+0x54, 0x45, 0x20, 0x41, 0x4c, 0x4c, 0x20, 0x57, 0x41, 0x54, 0x43, 0x48,
+0x45, 0x53, 0x0d, 0x0a, 0x09, 0x6f, 0x62, 0x6a, 0x73, 0x5f, 0x72, 0x65,
+0x67, 0x2e, 0x72, 0x65, 0x66, 0x73, 0x5b, 0x3a, 0x3a, 0x67, 0x65, 0x74,
+0x72, 0x6f, 0x6f, 0x74, 0x74, 0x61, 0x62, 0x6c, 0x65, 0x28, 0x29, 0x5d,
+0x20, 0x3c, 0x2d, 0x20, 0x6f, 0x62, 0x6a, 0x73, 0x5f, 0x72, 0x65, 0x67,
+0x2e, 0x6d, 0x61, 0x78, 0x69, 0x64, 0x2b, 0x2b, 0x3b, 0x0d, 0x0a, 0x09,
+0x66, 0x6f, 0x72, 0x65, 0x61, 0x63, 0x68, 0x28, 0x69, 0x2c, 0x76, 0x61,
+0x6c, 0x20, 0x69, 0x6e, 0x20, 0x73, 0x74, 0x61, 0x63, 0x6b, 0x29, 0x0d,
+0x0a, 0x09, 0x7b, 0x0d, 0x0a, 0x09, 0x09, 0x69, 0x66, 0x28, 0x76, 0x61,
+0x6c, 0x2e, 0x73, 0x72, 0x63, 0x21, 0x3d, 0x22, 0x4e, 0x41, 0x54, 0x49,
+0x56, 0x45, 0x22, 0x29, 0x20, 0x7b, 0x0d, 0x0a, 0x09, 0x09, 0x09, 0x69,
+0x66, 0x28, 0x22, 0x77, 0x61, 0x74, 0x63, 0x68, 0x65, 0x73, 0x22, 0x20,
+0x69, 0x6e, 0x20, 0x74, 0x68, 0x69, 0x73, 0x29, 0x20, 0x7b, 0x0d, 0x0a,
+0x09, 0x09, 0x09, 0x09, 0x76, 0x61, 0x6c, 0x2e, 0x77, 0x61, 0x74, 0x63,
+0x68, 0x65, 0x73, 0x20, 0x3c, 0x2d, 0x20, 0x7b, 0x7d, 0x0d, 0x0a, 0x09,
+0x09, 0x09, 0x09, 0x66, 0x6f, 0x72, 0x65, 0x61, 0x63, 0x68, 0x28, 0x69,
+0x2c, 0x77, 0x61, 0x74, 0x63, 0x68, 0x20, 0x69, 0x6e, 0x20, 0x77, 0x61,
+0x74, 0x63, 0x68, 0x65, 0x73, 0x29, 0x0d, 0x0a, 0x09, 0x09, 0x09, 0x09,
+0x7b, 0x0d, 0x0a, 0x09, 0x09, 0x09, 0x09, 0x09, 0x69, 0x66, 0x28, 0x76,
+0x61, 0x6c, 0x2e, 0x73, 0x72, 0x63, 0x21, 0x3d, 0x22, 0x4e, 0x41, 0x54,
+0x49, 0x56, 0x45, 0x22, 0x29, 0x7b, 0x0d, 0x0a, 0x09, 0x09, 0x09, 0x09,
+0x09, 0x09, 0x76, 0x61, 0x6c, 0x2e, 0x77, 0x61, 0x74, 0x63, 0x68, 0x65,
+0x73, 0x5b, 0x69, 0x5d, 0x20, 0x3c, 0x2d, 0x20, 0x65, 0x76, 0x61, 0x6c,
+0x75, 0x61, 0x74, 0x65, 0x5f, 0x77, 0x61, 0x74, 0x63, 0x68, 0x28, 0x76,
+0x61, 0x6c, 0x2e, 0x6c, 0x6f, 0x63, 0x61, 0x6c, 0x73, 0x2c, 0x69, 0x2c,
+0x77, 0x61, 0x74, 0x63, 0x68, 0x29, 0x3b, 0x0d, 0x0a, 0x09, 0x09, 0x09,
+0x09, 0x09, 0x09, 0x69, 0x66, 0x28, 0x76, 0x61, 0x6c, 0x2e, 0x77, 0x61,
+0x74, 0x63, 0x68, 0x65, 0x73, 0x5b, 0x69, 0x5d, 0x2e, 0x73, 0x74, 0x61,
+0x74, 0x75, 0x73, 0x21, 0x3d, 0x22, 0x65, 0x72, 0x72, 0x6f, 0x72, 0x22,
+0x29, 0x0d, 0x0a, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x62, 0x75,
+0x69, 0x6c, 0x64, 0x5f, 0x72, 0x65, 0x66, 0x73, 0x28, 0x76, 0x61, 0x6c,
+0x2e, 0x77, 0x61, 0x74, 0x63, 0x68, 0x65, 0x73, 0x5b, 0x69, 0x5d, 0x2e,
+0x76, 0x61, 0x6c, 0x29, 0x3b, 0x0d, 0x0a, 0x09, 0x09, 0x09, 0x09, 0x09,
+0x7d, 0x0d, 0x0a, 0x09, 0x09, 0x09, 0x09, 0x09, 0x65, 0x6c, 0x73, 0x65,
+0x7b, 0x0d, 0x0a, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x76, 0x61, 0x6c,
+0x2e, 0x77, 0x61, 0x74, 0x63, 0x68, 0x65, 0x73, 0x5b, 0x69, 0x5d, 0x20,
+0x3c, 0x2d, 0x20, 0x7b, 0x73, 0x74, 0x61, 0x74, 0x75, 0x73, 0x3d, 0x22,
+0x65, 0x72, 0x72, 0x6f, 0x72, 0x22, 0x7d, 0x0d, 0x0a, 0x09, 0x09, 0x09,
+0x09, 0x09, 0x7d, 0x0d, 0x0a, 0x09, 0x09, 0x09, 0x09, 0x09, 0x76, 0x61,
+0x6c, 0x2e, 0x77, 0x61, 0x74, 0x63, 0x68, 0x65, 0x73, 0x5b, 0x69, 0x5d,
+0x2e, 0x65, 0x78, 0x70, 0x20, 0x3c, 0x2d, 0x20, 0x77, 0x61, 0x74, 0x63,
+0x68, 0x3b, 0x0d, 0x0a, 0x09, 0x09, 0x09, 0x09, 0x7d, 0x0d, 0x0a, 0x09,
+0x09, 0x09, 0x09, 0x0d, 0x0a, 0x09, 0x09, 0x09, 0x7d, 0x0d, 0x0a, 0x09,
+0x09, 0x7d, 0x0d, 0x0a, 0x09, 0x09, 0x66, 0x6f, 0x72, 0x65, 0x61, 0x63,
+0x68, 0x28, 0x69, 0x2c, 0x6c, 0x20, 0x69, 0x6e, 0x20, 0x76, 0x61, 0x6c,
+0x2e, 0x6c, 0x6f, 0x63, 0x61, 0x6c, 0x73, 0x29, 0x0d, 0x0a, 0x09, 0x09,
+0x09, 0x62, 0x75, 0x69, 0x6c, 0x64, 0x5f, 0x72, 0x65, 0x66, 0x73, 0x28,
+0x6c, 0x29, 0x3b, 0x0d, 0x0a, 0x09, 0x7d, 0x0d, 0x0a, 0x0d, 0x0a, 0x0d,
+0x0a, 0x09, 0x62, 0x65, 0x67, 0x69, 0x6e, 0x65, 0x6c, 0x65, 0x6d, 0x65,
+0x6e, 0x74, 0x28, 0x22, 0x6f, 0x62, 0x6a, 0x73, 0x22, 0x29, 0x3b, 0x0d,
+0x0a, 0x09, 0x62, 0x75, 0x69, 0x6c, 0x64, 0x5f, 0x74, 0x72, 0x65, 0x65,
+0x28, 0x29, 0x3b, 0x0d, 0x0a, 0x09, 0x65, 0x6e, 0x64, 0x65, 0x6c, 0x65,
+0x6d, 0x65, 0x6e, 0x74, 0x28, 0x22, 0x6f, 0x62, 0x6a, 0x73, 0x22, 0x29,
+0x3b, 0x0d, 0x0a, 0x0d, 0x0a, 0x09, 0x62, 0x65, 0x67, 0x69, 0x6e, 0x65,
+0x6c, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x28, 0x22, 0x63, 0x61, 0x6c, 0x6c,
+0x73, 0x22, 0x29, 0x3b, 0x0d, 0x0a, 0x0d, 0x0a, 0x09, 0x66, 0x6f, 0x72,
+0x65, 0x61, 0x63, 0x68, 0x28, 0x69, 0x2c, 0x76, 0x61, 0x6c, 0x20, 0x69,
+0x6e, 0x20, 0x73, 0x74, 0x61, 0x63, 0x6b, 0x29, 0x0d, 0x0a, 0x09, 0x7b,
+0x0d, 0x0a, 0x0d, 0x0a, 0x09, 0x09, 0x62, 0x65, 0x67, 0x69, 0x6e, 0x65,
+0x6c, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x28, 0x22, 0x63, 0x61, 0x6c, 0x6c,
+0x22, 0x29, 0x3b, 0x0d, 0x0a, 0x09, 0x09, 0x61, 0x74, 0x74, 0x72, 0x69,
+0x62, 0x75, 0x74, 0x65, 0x28, 0x22, 0x66, 0x6e, 0x63, 0x22, 0x2c, 0x76,
+0x61, 0x6c, 0x2e, 0x66, 0x75, 0x6e, 0x63, 0x29, 0x3b, 0x0d, 0x0a, 0x09,
+0x09, 0x61, 0x74, 0x74, 0x72, 0x69, 0x62, 0x75, 0x74, 0x65, 0x28, 0x22,
+0x73, 0x72, 0x63, 0x22, 0x2c, 0x76, 0x61, 0x6c, 0x2e, 0x73, 0x72, 0x63,
+0x29, 0x3b, 0x0d, 0x0a, 0x09, 0x09, 0x61, 0x74, 0x74, 0x72, 0x69, 0x62,
+0x75, 0x74, 0x65, 0x28, 0x22, 0x6c, 0x69, 0x6e, 0x65, 0x22, 0x2c, 0x76,
+0x61, 0x6c, 0x2e, 0x6c, 0x69, 0x6e, 0x65, 0x2e, 0x74, 0x6f, 0x73, 0x74,
+0x72, 0x69, 0x6e, 0x67, 0x28, 0x29, 0x29, 0x3b, 0x0d, 0x0a, 0x09, 0x09,
+0x66, 0x6f, 0x72, 0x65, 0x61, 0x63, 0x68, 0x28, 0x69, 0x2c, 0x76, 0x20,
+0x69, 0x6e, 0x20, 0x76, 0x61, 0x6c, 0x2e, 0x6c, 0x6f, 0x63, 0x61, 0x6c,
+0x73, 0x29, 0x0d, 0x0a, 0x09, 0x09, 0x7b, 0x0d, 0x0a, 0x09, 0x09, 0x09,
+0x62, 0x65, 0x67, 0x69, 0x6e, 0x65, 0x6c, 0x65, 0x6d, 0x65, 0x6e, 0x74,
+0x28, 0x22, 0x6c, 0x22, 0x29, 0x3b, 0x0d, 0x0a, 0x09, 0x09, 0x09, 0x09,
+0x61, 0x74, 0x74, 0x72, 0x69, 0x62, 0x75, 0x74, 0x65, 0x28, 0x22, 0x6e,
+0x61, 0x6d, 0x65, 0x22, 0x2c, 0x67, 0x65, 0x74, 0x76, 0x61, 0x6c, 0x75,
+0x65, 0x28, 0x69, 0x29, 0x2e, 0x74, 0x6f, 0x73, 0x74, 0x72, 0x69, 0x6e,
+0x67, 0x28, 0x29, 0x29, 0x3b, 0x0d, 0x0a, 0x09, 0x09, 0x09, 0x09, 0x65,
+0x6d, 0x69, 0x74, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x28, 0x22, 0x74, 0x79,
+0x70, 0x65, 0x22, 0x2c, 0x22, 0x76, 0x61, 0x6c, 0x22, 0x2c, 0x76, 0x29,
+0x3b, 0x0d, 0x0a, 0x09, 0x09, 0x09, 0x65, 0x6e, 0x64, 0x65, 0x6c, 0x65,
+0x6d, 0x65, 0x6e, 0x74, 0x28, 0x22, 0x6c, 0x22, 0x29, 0x3b, 0x0d, 0x0a,
+0x09, 0x09, 0x7d, 0x0d, 0x0a, 0x09, 0x09, 0x69, 0x66, 0x28, 0x22, 0x77,
+0x61, 0x74, 0x63, 0x68, 0x65, 0x73, 0x22, 0x20, 0x69, 0x6e, 0x20, 0x76,
+0x61, 0x6c, 0x29, 0x20, 0x7b, 0x0d, 0x0a, 0x09, 0x09, 0x09, 0x66, 0x6f,
+0x72, 0x65, 0x61, 0x63, 0x68, 0x28, 0x69, 0x2c, 0x76, 0x20, 0x69, 0x6e,
+0x20, 0x76, 0x61, 0x6c, 0x2e, 0x77, 0x61, 0x74, 0x63, 0x68, 0x65, 0x73,
+0x29, 0x0d, 0x0a, 0x09, 0x09, 0x09, 0x7b, 0x0d, 0x0a, 0x09, 0x09, 0x09,
+0x09, 0x62, 0x65, 0x67, 0x69, 0x6e, 0x65, 0x6c, 0x65, 0x6d, 0x65, 0x6e,
+0x74, 0x28, 0x22, 0x77, 0x22, 0x29, 0x3b, 0x0d, 0x0a, 0x09, 0x09, 0x09,
+0x09, 0x09, 0x61, 0x74, 0x74, 0x72, 0x69, 0x62, 0x75, 0x74, 0x65, 0x28,
+0x22, 0x69, 0x64, 0x22, 0x2c, 0x69, 0x2e, 0x74, 0x6f, 0x73, 0x74, 0x72,
+0x69, 0x6e, 0x67, 0x28, 0x29, 0x29, 0x3b, 0x0d, 0x0a, 0x09, 0x09, 0x09,
+0x09, 0x09, 0x61, 0x74, 0x74, 0x72, 0x69, 0x62, 0x75, 0x74, 0x65, 0x28,
+0x22, 0x65, 0x78, 0x70, 0x22, 0x2c, 0x76, 0x2e, 0x65, 0x78, 0x70, 0x29,
+0x3b, 0x0d, 0x0a, 0x09, 0x09, 0x09, 0x09, 0x09, 0x61, 0x74, 0x74, 0x72,
+0x69, 0x62, 0x75, 0x74, 0x65, 0x28, 0x22, 0x73, 0x74, 0x61, 0x74, 0x75,
+0x73, 0x22, 0x2c, 0x76, 0x2e, 0x73, 0x74, 0x61, 0x74, 0x75, 0x73, 0x29,
+0x3b, 0x0d, 0x0a, 0x09, 0x09, 0x09, 0x09, 0x09, 0x69, 0x66, 0x28, 0x76,
+0x2e, 0x73, 0x74, 0x61, 0x74, 0x75, 0x73, 0x21, 0x3d, 0x22, 0x65, 0x72,
+0x72, 0x6f, 0x72, 0x22, 0x29, 0x20, 0x7b, 0x0d, 0x0a, 0x09, 0x09, 0x09,
+0x09, 0x09, 0x09, 0x65, 0x6d, 0x69, 0x74, 0x76, 0x61, 0x6c, 0x75, 0x65,
+0x28, 0x22, 0x74, 0x79, 0x70, 0x65, 0x22, 0x2c, 0x22, 0x76, 0x61, 0x6c,
+0x22, 0x2c, 0x76, 0x2e, 0x76, 0x61, 0x6c, 0x29, 0x3b, 0x0d, 0x0a, 0x09,
+0x09, 0x09, 0x09, 0x09, 0x7d, 0x0d, 0x0a, 0x09, 0x09, 0x09, 0x09, 0x65,
+0x6e, 0x64, 0x65, 0x6c, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x28, 0x22, 0x77,
+0x22, 0x29, 0x3b, 0x0d, 0x0a, 0x09, 0x09, 0x09, 0x7d, 0x0d, 0x0a, 0x09,
+0x09, 0x7d, 0x0d, 0x0a, 0x09, 0x09, 0x65, 0x6e, 0x64, 0x65, 0x6c, 0x65,
+0x6d, 0x65, 0x6e, 0x74, 0x28, 0x22, 0x63, 0x61, 0x6c, 0x6c, 0x22, 0x29,
+0x3b, 0x0d, 0x0a, 0x09, 0x09, 0x20, 0x0d, 0x0a, 0x09, 0x7d, 0x0d, 0x0a,
+0x09, 0x65, 0x6e, 0x64, 0x65, 0x6c, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x28,
+0x22, 0x63, 0x61, 0x6c, 0x6c, 0x73, 0x22, 0x29, 0x3b, 0x0d, 0x0a, 0x0d,
+0x0a, 0x0d, 0x0a, 0x09, 0x6f, 0x62, 0x6a, 0x73, 0x5f, 0x72, 0x65, 0x67,
+0x20, 0x3d, 0x20, 0x6e, 0x75, 0x6c, 0x6c, 0x3b, 0x0d, 0x0a, 0x09, 0x73,
+0x74, 0x61, 0x63, 0x6b, 0x20, 0x3d, 0x20, 0x6e, 0x75, 0x6c, 0x6c, 0x3b,
+0x0d, 0x0a, 0x09, 0x0d, 0x0a, 0x09, 0x69, 0x66, 0x28, 0x22, 0x63, 0x6f,
+0x6c, 0x6c, 0x65, 0x63, 0x74, 0x67, 0x61, 0x72, 0x62, 0x61, 0x67, 0x65,
+0x22, 0x20, 0x69, 0x6e, 0x20, 0x3a, 0x3a, 0x67, 0x65, 0x74, 0x72, 0x6f,
+0x6f, 0x74, 0x74, 0x61, 0x62, 0x6c, 0x65, 0x28, 0x29, 0x29, 0x20, 0x3a,
+0x3a, 0x63, 0x6f, 0x6c, 0x6c, 0x65, 0x63, 0x74, 0x67, 0x61, 0x72, 0x62,
+0x61, 0x67, 0x65, 0x28, 0x29, 0x3b, 0x0d, 0x0a, 0x7d, 0x63, 0x61, 0x74,
+0x63, 0x68, 0x28, 0x65, 0x29, 0x0d, 0x0a, 0x7b, 0x0d, 0x0a, 0x09, 0x3a,
+0x3a, 0x70, 0x72, 0x69, 0x6e, 0x74, 0x28, 0x22, 0x45, 0x52, 0x52, 0x4f,
+0x52, 0x22, 0x2b, 0x65, 0x2b, 0x22, 0x5c, 0x6e, 0x22, 0x29, 0x3b, 0x0d,
+0x0a, 0x7d, 0x0d, 0x0a, 0x0d, 0x0a, 0x00,
+};
--- /dev/null
+#include <squirrel.h>
+#include <assert.h>
+#include <sqstdblob.h>
+#include "sqrdbg.h"
+#include "sqdbgserver.h"
+
+
+#ifndef _UNICODE
+#define scstrcpy strcpy
+#else
+#define scstrcpy wcscpy
+#endif
+struct XMLEscape{
+ const SQChar c;
+ const SQChar *esc;
+};
+
+#define SQDBG_DEBUG_HOOK _SC("_sqdbg_debug_hook_")
+#define SQDBG_ERROR_HANDLER _SC("_sqdbg_error_handler_")
+
+XMLEscape g_escapes[]={
+ {_SC('<'),_SC("<")},{'>',_SC(">")},{_SC('&'),_SC("&")},{_SC('\''),_SC("'")},{_SC('\"'),_SC(""")},{_SC('\n'),_SC(""n")},{_SC('\r'),_SC(""r")},{0, NULL}
+};
+
+const SQChar *IntToString(int n)
+{
+ static SQChar temp[256];
+ scsprintf(temp,_SC("%d"),n);
+ return temp;
+}
+
+SQInteger debug_hook(HSQUIRRELVM v);
+SQInteger error_handler(HSQUIRRELVM v);
+
+SQInteger beginelement(HSQUIRRELVM v)
+{
+ SQUserPointer up;
+ const SQChar *name;
+ sq_getuserpointer(v,-1,&up);
+ SQDbgServer *self = (SQDbgServer*)up;
+ sq_getuserpointer(v,-1,&up);
+ sq_getstring(v,2,&name);
+ self->BeginElement(name);
+ return 0;
+}
+
+SQInteger endelement(HSQUIRRELVM v)
+{
+ SQUserPointer up;
+ const SQChar *name;
+ sq_getuserpointer(v,-1,&up);
+ SQDbgServer *self = (SQDbgServer*)up;
+ sq_getuserpointer(v,-1,&up);
+ sq_getstring(v,2,&name);
+ self->EndElement(name);
+ return 0;
+}
+
+SQInteger attribute(HSQUIRRELVM v)
+{
+ SQUserPointer up;
+ const SQChar *name,*value;
+ sq_getuserpointer(v,-1,&up);
+ SQDbgServer *self = (SQDbgServer*)up;
+ sq_getuserpointer(v,-1,&up);
+ sq_getstring(v,2,&name);
+ sq_getstring(v,3,&value);
+ self->Attribute(name,value);
+ return 0;
+}
+
+const SQChar *EscapeXMLString(HSQUIRRELVM v,const SQChar *s)
+{
+
+ SQChar *temp=sq_getscratchpad(v,((int)scstrlen(s)*6) + sizeof(SQChar));
+ SQChar *dest=temp;
+ while(*s!=_SC('\0')){
+ int i=0;
+ bool escaped=false;
+ while(g_escapes[i].esc!=NULL){
+ if(*s==g_escapes[i].c){
+ scstrcpy(dest,g_escapes[i].esc);
+ dest+=scstrlen(g_escapes[i].esc);
+ escaped=true;
+ break;
+ }
+ i++;
+ }
+ if(!escaped){*dest=*s;*dest++;}
+ *s++;
+ }
+ *dest=_SC('\0');
+ return temp;
+}
+
+SQDbgServer::SQDbgServer(HSQUIRRELVM v)
+{
+ _ready = false;
+ _nestedcalls = 0;
+ _autoupdate = false;
+ _v = v;
+ _state = eDBG_Running;
+ _accept = INVALID_SOCKET;
+ _endpoint = INVALID_SOCKET;
+ _maxrecursion = 10;
+ sq_resetobject(&_debugroot);
+}
+
+SQDbgServer::~SQDbgServer()
+{
+ sq_release(_v,&_debugroot);
+ if(_accept != INVALID_SOCKET)
+ sqdbg_closesocket(_accept);
+ if(_endpoint != INVALID_SOCKET)
+ sqdbg_closesocket(_endpoint);
+}
+
+bool SQDbgServer::Init()
+{
+ //creates an environment table for the debugger
+
+ sq_newtable(_v);
+ sq_getstackobj(_v,-1,&_debugroot);
+ sq_addref(_v,&_debugroot);
+
+ //creates a emptyslot to store the watches
+ sq_pushstring(_v,_SC("watches"),-1);
+ sq_pushnull(_v);
+ sq_createslot(_v,-3);
+
+ sq_pushstring(_v,_SC("beginelement"),-1);
+ sq_pushuserpointer(_v,this);
+ sq_newclosure(_v,beginelement,1);
+ sq_setparamscheck(_v,2,_SC(".s"));
+ sq_createslot(_v,-3);
+
+ sq_pushstring(_v,_SC("endelement"),-1);
+ sq_pushuserpointer(_v,this);
+ sq_newclosure(_v,endelement,1);
+ sq_setparamscheck(_v,2,_SC(".s"));
+ sq_createslot(_v,-3);
+
+ sq_pushstring(_v,_SC("attribute"),-1);
+ sq_pushuserpointer(_v,this);
+ sq_newclosure(_v,attribute,1);
+ sq_setparamscheck(_v,3,_SC(".ss"));
+ sq_createslot(_v,-3);
+
+ sq_pop(_v,1);
+
+ //stores debug hook and error handler in the registry
+ sq_pushregistrytable(_v);
+
+ sq_pushstring(_v,SQDBG_DEBUG_HOOK,-1);
+ sq_pushuserpointer(_v,this);
+ sq_newclosure(_v,debug_hook,1);
+ sq_createslot(_v,-3);
+
+ sq_pushstring(_v,SQDBG_ERROR_HANDLER,-1);
+ sq_pushuserpointer(_v,this);
+ sq_newclosure(_v,error_handler,1);
+ sq_createslot(_v,-3);
+
+
+ sq_pop(_v,1);
+
+ //sets the error handlers
+ SetErrorHandlers();
+ return true;
+}
+
+bool SQDbgServer::ReadMsg()
+{
+ return false;
+}
+
+void SQDbgServer::BusyWait()
+{
+ while( !ReadMsg() )
+ sleep(0);
+}
+
+void SQDbgServer::SendChunk(const SQChar *chunk)
+{
+ char *buf=NULL;
+ int buf_len=0;
+#ifdef _UNICODE
+ buf_len=(int)scstrlen(chunk)+1;
+ buf=(char *)sq_getscratchpad(_v,(buf_len)*3);
+ wcstombs((char *)buf,chunk,buf_len);
+#else
+ buf_len=(int)scstrlen(chunk);
+ buf=(char *)chunk;
+#endif
+ send(_endpoint,(const char*)buf,(int)strlen((const char *)buf),0);
+}
+
+
+void SQDbgServer::Terminated()
+{
+ BeginElement(_SC("terminated"));
+ EndElement(_SC("terminated"));
+ ::usleep(200);
+}
+
+void SQDbgServer::Hook(int type,int line,const SQChar *src,const SQChar *func)
+{
+ switch(_state){
+ case eDBG_Running:
+ if(type==_SC('l') && _breakpoints.size()) {
+ BreakPointSetItor itr = _breakpoints.find(BreakPoint(line,src));
+ if(itr != _breakpoints.end()) {
+ Break(line,src,_SC("breakpoint"));
+ BreakExecution();
+ }
+ }
+ break;
+ case eDBG_Suspended:
+ _nestedcalls=0;
+ case eDBG_StepOver:
+ switch(type){
+ case _SC('l'):
+ if(_nestedcalls==0) {
+ Break(line,src,_SC("step"));
+ BreakExecution();
+ }
+ break;
+ case _SC('c'):
+ _nestedcalls++;
+ break;
+ case _SC('r'):
+ if(_nestedcalls==0){
+ _nestedcalls=0;
+
+ }else{
+ _nestedcalls--;
+ }
+ break;
+ }
+ break;
+ case eDBG_StepInto:
+ switch(type){
+ case _SC('l'):
+ _nestedcalls=0;
+ Break(line,src,_SC("step"));
+ BreakExecution();
+ break;
+
+ }
+ break;
+ case eDBG_StepReturn:
+ switch(type){
+ case _SC('l'):
+ break;
+ case _SC('c'):
+ _nestedcalls++;
+ break;
+ case _SC('r'):
+ if(_nestedcalls==0){
+ _nestedcalls=0;
+ _state=eDBG_StepOver;
+ }else{
+ _nestedcalls--;
+ }
+
+ break;
+ }
+ break;
+ case eDBG_Disabled:
+ break;
+ }
+}
+
+
+#define MSG_ID(x,y) ((y<<8)|x)
+//ab Add Breakpoint
+//rb Remove Breakpoint
+//sp Suspend
+void SQDbgServer::ParseMsg(const char *msg)
+{
+
+ switch(*((unsigned short *)msg)){
+ case MSG_ID('a','b'): {
+ BreakPoint bp;
+ if(ParseBreakpoint(msg+3,bp)){
+ AddBreakpoint(bp);
+ scprintf(_SC("added bp %d %s\n"),bp._line,bp._src.c_str());
+ }
+ else
+ scprintf(_SC("error parsing add breakpoint"));
+ }
+ break;
+ case MSG_ID('r','b'): {
+ BreakPoint bp;
+ if(ParseBreakpoint(msg+3,bp)){
+ RemoveBreakpoint(bp);
+ scprintf(_SC("removed bp %d %s\n"),bp._line,bp._src.c_str());
+ }else
+ scprintf(_SC("error parsing remove breakpoint"));
+ }
+ break;
+ case MSG_ID('g','o'):
+ if(_state!=eDBG_Running){
+ _state=eDBG_Running;
+ BeginDocument();
+ BeginElement(_SC("resumed"));
+ EndElement(_SC("resumed"));
+ EndDocument();
+// Send(_SC("<resumed/>\r\n"));
+ scprintf(_SC("go (execution resumed)\n"));
+ }
+ break;
+ case MSG_ID('s','p'):
+ if(_state!=eDBG_Suspended){
+ _state=eDBG_Suspended;
+ scprintf(_SC("suspend\n"));
+ }
+ break;
+ case MSG_ID('s','o'):
+ if(_state==eDBG_Suspended){
+ _state=eDBG_StepOver;
+ }
+ break;
+ case MSG_ID('s','i'):
+ if(_state==eDBG_Suspended){
+ _state=eDBG_StepInto;
+ scprintf(_SC("step into\n"));
+ }
+ break;
+ case MSG_ID('s','r'):
+ if(_state==eDBG_Suspended){
+ _state=eDBG_StepReturn;
+ scprintf(_SC("step return\n"));
+ }
+ break;
+ case MSG_ID('d','i'):
+ if(_state!=eDBG_Disabled){
+ _state=eDBG_Disabled;
+ scprintf(_SC("disabled\n"));
+ }
+ break;
+ case MSG_ID('a','w'): {
+ Watch w;
+ if(ParseWatch(msg+3,w))
+ {
+ AddWatch(w);
+ scprintf(_SC("added watch %d %s\n"),w._id,w._exp.c_str());
+ }
+ else
+ scprintf(_SC("error parsing add watch"));
+ }
+ break;
+ case MSG_ID('r','w'): {
+ int id;
+ if(ParseRemoveWatch(msg+3,id))
+ {
+ RemoveWatch(id);
+ scprintf(_SC("added watch %d\n"),id);
+ }
+ else
+ scprintf(_SC("error parsing remove watch"));
+ }
+ break;
+ case MSG_ID('t','r'):
+ scprintf(_SC("terminate from user\n"));
+ break;
+ case MSG_ID('r','d'):
+ scprintf(_SC("ready\n"));
+ _ready=true;
+ break;
+ default:
+ scprintf(_SC("unknown packet"));
+
+ }
+}
+
+/*
+ see copyright notice in sqrdbg.h
+*/
+bool SQDbgServer::ParseBreakpoint(const char *msg,BreakPoint &out)
+{
+ static char stemp[MAX_BP_PATH];
+ char *ep=NULL;
+ out._line=strtoul(msg,&ep,16);
+ if(ep==msg || (*ep)!=':')return false;
+
+ char *dest=stemp;
+ ep++;
+ while((*ep)!='\n' && (*ep)!='\0')
+ {
+ *dest=*ep;
+ *dest++;*ep++;
+ }
+ *dest='\0';
+ *dest++;
+ *dest='\0';
+#ifdef _UNICODE
+ int len=(int)strlen(stemp);
+ SQChar *p=sq_getscratchpad(_v,(SQInteger)(mbstowcs(NULL,stemp,len)+2)*sizeof(SQChar));
+ size_t destlen=mbstowcs(p,stemp,len);
+ p[destlen]=_SC('\0');
+ out._src=p;
+#else
+ out._src=stemp;
+#endif
+ return true;
+}
+
+bool SQDbgServer::ParseWatch(const char *msg,Watch &out)
+{
+ char *ep=NULL;
+ out._id=strtoul(msg,&ep,16);
+ if(ep==msg || (*ep)!=':')return false;
+
+ //char *dest=out._src;
+ ep++;
+ while((*ep)!='\n' && (*ep)!='\0')
+ {
+ out._exp.append(1,*ep);
+ *ep++;
+ }
+ return true;
+}
+
+bool SQDbgServer::ParseRemoveWatch(const char *msg,int &id)
+{
+ char *ep=NULL;
+ id=strtoul(msg,&ep,16);
+ if(ep==msg)return false;
+ return true;
+}
+
+
+void SQDbgServer::BreakExecution()
+{
+ _state=eDBG_Suspended;
+ while(_state==eDBG_Suspended){
+ if(SQ_FAILED(sq_rdbg_update(this)))
+ exit(0);
+ usleep(10);
+ }
+}
+
+//COMMANDS
+void SQDbgServer::AddBreakpoint(BreakPoint &bp)
+{
+ _breakpoints.insert(bp);
+ BeginDocument();
+ BeginElement(_SC("addbreakpoint"));
+ Attribute(_SC("line"),IntToString(bp._line));
+ Attribute(_SC("src"),bp._src.c_str());
+ EndElement(_SC("addbreakpoint"));
+ EndDocument();
+}
+
+void SQDbgServer::AddWatch(Watch &w)
+{
+ _watches.insert(w);
+}
+
+void SQDbgServer::RemoveWatch(int id)
+{
+ WatchSetItor itor=_watches.find(Watch(id,_SC("")));
+ if(itor==_watches.end()){
+ BeginDocument();
+ BeginElement(_SC("error"));
+ Attribute(_SC("desc"),_SC("the watch does not exists"));
+ EndElement(_SC("error"));
+ EndDocument();
+ }
+ else{
+ _watches.erase(itor);
+ scprintf(_SC("removed watch %d\n"),id);
+ }
+}
+
+void SQDbgServer::RemoveBreakpoint(BreakPoint &bp)
+{
+ BreakPointSetItor itor=_breakpoints.find(bp);
+ if(itor==_breakpoints.end()){
+ BeginDocument();
+ BeginElement(_SC("break"));
+ Attribute(_SC("desc"),_SC("the breakpoint doesn't exists"));
+ EndElement(_SC("break"));
+ EndDocument();
+ }
+ else{
+ BeginDocument();
+ BeginElement(_SC("removebreakpoint"));
+ Attribute(_SC("line"),IntToString(bp._line));
+ Attribute(_SC("src"),bp._src.c_str());
+ EndElement(_SC("removebreakpoint"));
+ EndDocument();
+ _breakpoints.erase(itor);
+ }
+}
+
+void SQDbgServer::Break(int line,const SQChar *src,const SQChar *type,const SQChar *error)
+{
+ if(!error){
+ BeginDocument();
+ BeginElement(_SC("break"));
+ Attribute(_SC("line"),IntToString(line));
+ Attribute(_SC("src"),src);
+ Attribute(_SC("type"),type);
+ SerializeState();
+ EndElement(_SC("break"));
+ EndDocument();
+ }else{
+ BeginDocument();
+ BeginElement(_SC("break"));
+ Attribute(_SC("line"),IntToString(line));
+ Attribute(_SC("src"),src);
+ Attribute(_SC("type"),type);
+ Attribute(_SC("error"),error);
+ SerializeState();
+ EndElement(_SC("break"));
+ EndDocument();
+ }
+}
+
+void SQDbgServer::SerializeState()
+{
+ sq_pushnull(_v);
+ sq_setdebughook(_v);
+ sq_pushnull(_v);
+ sq_seterrorhandler(_v);
+ const SQChar *sz;
+ sq_pushobject(_v,_serializefunc);
+ sq_pushobject(_v,_debugroot);
+ sq_pushstring(_v,_SC("watches"),-1);
+ sq_newtable(_v);
+ for(WatchSetItor i=_watches.begin(); i!=_watches.end(); ++i)
+ {
+ sq_pushinteger(_v,i->_id);
+ sq_pushstring(_v,i->_exp.c_str(),(int)i->_exp.length());
+ sq_createslot(_v,-3);
+ }
+ sq_rawset(_v,-3);
+ if(SQ_SUCCEEDED(sq_call(_v,1,SQTrue,SQTrue))){
+ if(SQ_SUCCEEDED(sqstd_getblob(_v,-1,(SQUserPointer*)&sz)))
+ SendChunk(sz);
+ }
+ sq_pop(_v,2);
+
+ SetErrorHandlers();
+}
+
+
+void SQDbgServer::SetErrorHandlers()
+{
+ sq_pushregistrytable(_v);
+ sq_pushstring(_v,SQDBG_DEBUG_HOOK,-1);
+ sq_rawget(_v,-2);
+ sq_setdebughook(_v);
+ sq_pushstring(_v,SQDBG_ERROR_HANDLER,-1);
+ sq_rawget(_v,-2);
+ sq_seterrorhandler(_v);
+ sq_pop(_v,1);
+}
+
+void SQDbgServer::BeginElement(const SQChar *name)
+{
+ _xmlcurrentement++;
+ XMLElementState *self = &xmlstate[_xmlcurrentement];
+ scstrcpy(self->name,name);
+ self->haschildren = false;
+ if(_xmlcurrentement > 0) {
+ XMLElementState *parent = &xmlstate[_xmlcurrentement-1];
+ if(!parent->haschildren) {
+ SendChunk(_SC(">")); // closes the parent tag
+ parent->haschildren = true;
+ }
+ }
+ _scratchstring.resize(2+scstrlen(name));
+ scsprintf(&_scratchstring[0],_SC("<%s"),name);
+ SendChunk(&_scratchstring[0]);
+}
+
+void SQDbgServer::Attribute(const SQChar *name,const SQChar *value)
+{
+ XMLElementState *self = &xmlstate[_xmlcurrentement];
+ assert(!self->haschildren); //cannot have attributes if already has children
+ const SQChar *escval = escape_xml(value);
+ _scratchstring.resize(5+scstrlen(name)+scstrlen(escval));
+ scsprintf(&_scratchstring[0],_SC(" %s=\"%s\""),name,escval);
+ SendChunk(&_scratchstring[0]);
+}
+
+void SQDbgServer::EndElement(const SQChar *name)
+{
+ XMLElementState *self = &xmlstate[_xmlcurrentement];
+ assert(scstrcmp(self->name,name) == 0);
+ if(self->haschildren) {
+ _scratchstring.resize(4+scstrlen(name));
+ scsprintf(&_scratchstring[0],_SC("</%s>"),name);
+ SendChunk(&_scratchstring[0]);
+
+ }
+ else {
+ SendChunk(_SC("/>"));
+ }
+ _xmlcurrentement--;
+}
+
+void SQDbgServer::EndDocument()
+{
+ SendChunk(_SC("\r\n"));
+}
+
+//this can be done much better/faster(do we need that?)
+const SQChar *SQDbgServer::escape_xml(const SQChar *s)
+{
+ SQChar *temp=sq_getscratchpad(_v,((int)scstrlen(s)*6) + sizeof(SQChar));
+ SQChar *dest=temp;
+ while(*s!=_SC('\0')){
+ int i=0;
+ bool escaped=false;
+ while(g_escapes[i].esc!=NULL){
+ if(*s==g_escapes[i].c){
+ scstrcpy(dest,g_escapes[i].esc);
+ dest+=scstrlen(g_escapes[i].esc);
+ escaped=true;
+ break;
+ }
+ i++;
+ }
+ if(!escaped){*dest=*s;*dest++;}
+ *s++;
+ }
+ *dest=_SC('\0');
+ return temp;
+
+}
--- /dev/null
+#ifndef _SQ_DBGSERVER_H_
+#define _SQ_DBGSERVER_H_
+
+#define MAX_BP_PATH 512
+#define MAX_MSG_LEN 2049
+
+#include <set>
+#include <string>
+#include <vector>
+
+#ifdef _WIN32
+#include <winsock.h>
+#define sqdbg_closesocket(x) closesocket((x))
+typedef socklen_t int;
+#else
+#include <unistd.h>
+#include <errno.h>
+#include <sys/socket.h>
+#include <sys/types.h>
+#include <netinet/in.h>
+#include <arpa/inet.h>
+#include <netdb.h>
+#include <fcntl.h>
+
+#define sqdbg_closesocket(x) close((x))
+typedef int SOCKET;
+typedef struct timeval TIMEVAL;
+#define SOCKET_ERROR -1
+#define INVALID_SOCKET -1
+#endif
+
+typedef std::basic_string<SQChar> SQDBGString;
+
+inline bool dbg_less(const SQChar *x,const SQChar *y)
+{
+ // [SuperTux] commented out to avoid compiler warning
+ //int n = 0;
+ do {
+ int xl = *x == '\\' ? '/' : tolower(*x);
+ int yl = *y == '\\' ? '/' : tolower(*y);
+ int diff = xl - yl;
+ if(diff != 0)
+ return diff > 0?true:false;
+ x++; y++;
+ }while(*x != 0 && *y != 0);
+ return false;
+}
+
+struct BreakPoint{
+ BreakPoint(){_line=0;}
+ BreakPoint(int line, const SQChar *src){ _line = line; _src = src; }
+ BreakPoint(const BreakPoint& bp){ _line = bp._line; _src=bp._src; }
+ inline bool operator<(const BreakPoint& bp) const
+ {
+ if(_line<bp._line)
+ return true;
+ if(_line==bp._line){
+ return dbg_less(_src.c_str(),bp._src.c_str());
+ }
+ return false;
+ }
+
+ int _line;
+ SQDBGString _src;
+};
+
+struct Watch{
+ Watch() { _id = 0; }
+ Watch(int id,const SQChar *exp) { _id = id; _exp = exp; }
+ Watch(const Watch &w) { _id = w._id; _exp = w._exp; }
+ bool operator<(const Watch& w) const { return _id<w._id; }
+ bool operator==(const Watch& w) const { return _id == w._id; }
+ int _id;
+ SQDBGString _exp;
+};
+
+typedef std::set<BreakPoint> BreakPointSet;
+typedef BreakPointSet::iterator BreakPointSetItor;
+
+typedef std::set<Watch> WatchSet;
+typedef WatchSet::iterator WatchSetItor;
+
+typedef std::vector<SQChar> SQCharVec;
+struct SQDbgServer{
+public:
+ enum eDbgState{
+ eDBG_Running,
+ eDBG_StepOver,
+ eDBG_StepInto,
+ eDBG_StepReturn,
+ eDBG_Suspended,
+ eDBG_Disabled,
+ };
+
+ SQDbgServer(HSQUIRRELVM v);
+ ~SQDbgServer();
+ bool Init();
+ //returns true if a message has been received
+ bool WaitForClient();
+ bool ReadMsg();
+ void BusyWait();
+ void Hook(int type,int line,const SQChar *src,const SQChar *func);
+ void ParseMsg(const char *msg);
+ bool ParseBreakpoint(const char *msg,BreakPoint &out);
+ bool ParseWatch(const char *msg,Watch &out);
+ bool ParseRemoveWatch(const char *msg,int &id);
+ void Terminated();
+ //
+ void BreakExecution();
+ void Send(const SQChar *s,...);
+ void SendChunk(const SQChar *chunk);
+ void Break(int line,const SQChar *src,const SQChar *type,const SQChar *error=NULL);
+
+
+ void SerializeState();
+ //COMMANDS
+ void AddBreakpoint(BreakPoint &bp);
+ void AddWatch(Watch &w);
+ void RemoveWatch(int id);
+ void RemoveBreakpoint(BreakPoint &bp);
+
+ //
+ void SetErrorHandlers();
+
+ //XML RELATED STUFF///////////////////////
+ #define MAX_NESTING 10
+ struct XMLElementState {
+ SQChar name[256];
+ bool haschildren;
+ };
+
+ XMLElementState xmlstate[MAX_NESTING];
+ int _xmlcurrentement;
+
+ void BeginDocument() { _xmlcurrentement = -1; }
+ void BeginElement(const SQChar *name);
+ void Attribute(const SQChar *name, const SQChar *value);
+ void EndElement(const SQChar *name);
+ void EndDocument();
+
+ const SQChar *escape_xml(const SQChar *x);
+ //////////////////////////////////////////////
+ HSQUIRRELVM _v;
+ HSQOBJECT _debugroot;
+ eDbgState _state;
+ SOCKET _accept;
+ SOCKET _endpoint;
+ BreakPointSet _breakpoints;
+ WatchSet _watches;
+ int _recursionlevel;
+ int _maxrecursion;
+ int _nestedcalls;
+ bool _ready;
+ bool _autoupdate;
+ HSQOBJECT _serializefunc;
+ SQCharVec _scratchstring;
+
+};
+
+#endif //_SQ_DBGSERVER_H_
--- /dev/null
+/*
+ see copyright notice in sqrdbg.h
+*/
+#include <squirrel.h>
+#include "sqrdbg.h"
+#include "sqdbgserver.h"
+SQInteger debug_hook(HSQUIRRELVM v);
+SQInteger error_handler(HSQUIRRELVM v);
+
+#include "serialize_state.inl"
+
+HSQREMOTEDBG sq_rdbg_init(HSQUIRRELVM v,unsigned short port,SQBool autoupdate)
+{
+ sockaddr_in bindaddr;
+#ifdef _WIN32
+ WSADATA wsadata;
+ if (WSAStartup (MAKEWORD(1,1), &wsadata) != 0){
+ return NULL;
+ }
+#endif
+
+ SQDbgServer *rdbg = new SQDbgServer(v);
+ rdbg->_autoupdate = autoupdate?true:false;
+ rdbg->_accept = socket(AF_INET,SOCK_STREAM,0);
+ bindaddr.sin_family = AF_INET;
+ bindaddr.sin_port = htons(port);
+ bindaddr.sin_addr.s_addr = htonl (INADDR_ANY);
+ if(bind(rdbg->_accept,(sockaddr*)&bindaddr,sizeof(bindaddr))==SOCKET_ERROR){
+ delete rdbg;
+ sq_throwerror(v,_SC("failed to bind the socket"));
+ return NULL;
+ }
+ if(!rdbg->Init()) {
+ delete rdbg;
+ sq_throwerror(v,_SC("failed to initialize the debugger"));
+ return NULL;
+ }
+
+ return rdbg;
+}
+
+SQRESULT sq_rdbg_waitforconnections(HSQREMOTEDBG rdbg)
+{
+ if(SQ_FAILED(sq_compilebuffer(rdbg->_v,serialize_state_nut,(SQInteger)scstrlen(serialize_state_nut),_SC("SERIALIZE_STATE"),SQFalse))) {
+ sq_throwerror(rdbg->_v,_SC("error compiling the serialization function"));
+ }
+ sq_getstackobj(rdbg->_v,-1,&rdbg->_serializefunc);
+ sq_addref(rdbg->_v,&rdbg->_serializefunc);
+ sq_pop(rdbg->_v,1);
+
+ sockaddr_in cliaddr;
+ socklen_t addrlen=sizeof(cliaddr);
+ if(listen(rdbg->_accept,0)==SOCKET_ERROR)
+ return sq_throwerror(rdbg->_v,_SC("error on listen(socket)"));
+ rdbg->_endpoint = accept(rdbg->_accept,(sockaddr*)&cliaddr,&addrlen);
+ //do not accept any other connection
+ sqdbg_closesocket(rdbg->_accept);
+ rdbg->_accept = INVALID_SOCKET;
+ if(rdbg->_endpoint==INVALID_SOCKET){
+ return sq_throwerror(rdbg->_v,_SC("error accept(socket)"));
+ }
+ while(!rdbg->_ready){
+ sq_rdbg_update(rdbg);
+ }
+ return SQ_OK;
+}
+
+SQRESULT sq_rdbg_update(HSQREMOTEDBG rdbg)
+{
+ TIMEVAL time;
+ time.tv_sec=0;
+ time.tv_usec=0;
+ fd_set read_flags;
+ FD_ZERO(&read_flags);
+ FD_SET(rdbg->_endpoint, &read_flags);
+ select(FD_SETSIZE, &read_flags, NULL, NULL, &time);
+
+ if(FD_ISSET(rdbg->_endpoint,&read_flags)){
+ char temp[1024];
+ int size=0;
+ char c,prev=0;
+ memset(&temp,0,sizeof(temp));
+ int res;
+ FD_CLR(rdbg->_endpoint, &read_flags);
+ while((res = recv(rdbg->_endpoint,&c,1,0))>0){
+
+ if(c=='\n')break;
+ if(c!='\r'){
+ temp[size]=c;
+ prev=c;
+ size++;
+ }
+ }
+ switch(res){
+ case 0:
+ return sq_throwerror(rdbg->_v,_SC("disconnected"));
+ case SOCKET_ERROR:
+ return sq_throwerror(rdbg->_v,_SC("socket error"));
+ }
+
+ temp[size]=0;
+ temp[size+1]=0;
+ rdbg->ParseMsg(temp);
+ }
+ return SQ_OK;
+}
+
+SQInteger debug_hook(HSQUIRRELVM v)
+{
+ SQUserPointer up;
+ SQInteger event_type,line;
+ const SQChar *src,*func;
+ sq_getinteger(v,2,&event_type);
+ sq_getstring(v,3,&src);
+ sq_getinteger(v,4,&line);
+ sq_getstring(v,5,&func);
+ sq_getuserpointer(v,-1,&up);
+ HSQREMOTEDBG rdbg = (HSQREMOTEDBG)up;
+ rdbg->Hook(event_type,line,src,func);
+ if(rdbg->_autoupdate) {
+ if(SQ_FAILED(sq_rdbg_update(rdbg)))
+ return sq_throwerror(v,_SC("socket failed"));
+ }
+ return 0;
+}
+
+SQInteger error_handler(HSQUIRRELVM v)
+{
+ SQUserPointer up;
+ const SQChar *sErr=NULL;
+ const SQChar *fn=_SC("unknown");
+ const SQChar *src=_SC("unknown");
+ int line=-1;
+ SQStackInfos si;
+ sq_getuserpointer(v,-1,&up);
+ HSQREMOTEDBG rdbg=(HSQREMOTEDBG)up;
+ if(SQ_SUCCEEDED(sq_stackinfos(v,1,&si)))
+ {
+ if(si.funcname)fn=si.funcname;
+ if(si.source)src=si.source;
+ line=si.line;
+ scprintf(_SC("*FUNCTION [%s] %s line [%d]\n"),fn,src,si.line);
+ }
+ if(sq_gettop(v)>=1){
+ if(SQ_SUCCEEDED(sq_getstring(v,2,&sErr))) {
+ scprintf(_SC("\nAN ERROR HAS OCCURED [%s]\n"),sErr);
+ rdbg->Break(si.line,src,_SC("error"),sErr);
+ }
+ else{
+ scprintf(_SC("\nAN ERROR HAS OCCURED [unknown]\n"));
+ rdbg->Break(si.line,src,_SC("error"),_SC("unknown"));
+ }
+ }
+ rdbg->BreakExecution();
+ return 0;
+}
+
+
+SQRESULT sq_rdbg_shutdown(HSQREMOTEDBG rdbg)
+{
+ delete rdbg;
+#ifdef _WIN32
+ WSACleanup();
+#endif
+ return SQ_OK;
+}
--- /dev/null
+/*
+Copyright (c) 2003-2005 Alberto Demichelis
+
+This software is provided 'as-is', without any
+express or implied warranty. In no event will the
+authors be held liable for any damages arising from
+the use of this software.
+
+Permission is granted to anyone to use this software
+for any purpose, including commercial applications,
+and to alter it and redistribute it freely, subject
+to the following restrictions:
+
+ 1. The origin of this software must not be
+ misrepresented; you must not claim that
+ you wrote the original software. If you
+ use this software in a product, an
+ acknowledgment in the product
+ documentation would be appreciated but is
+ not required.
+
+ 2. Altered source versions must be plainly
+ marked as such, and must not be
+ misrepresented as being the original
+ software.
+
+ 3. This notice may not be removed or
+ altered from any source distribution.
+
+*/
+#ifndef _SQ_RDBG_H_
+#define _SQ_RDBG_H_
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+struct SQDbgServer;
+typedef SQDbgServer* HSQREMOTEDBG;
+
+HSQREMOTEDBG sq_rdbg_init(HSQUIRRELVM v,unsigned short port,SQBool autoupdate);
+SQRESULT sq_rdbg_waitforconnections(HSQREMOTEDBG rdbg);
+SQRESULT sq_rdbg_shutdown(HSQREMOTEDBG rdbg);
+SQRESULT sq_rdbg_update(HSQREMOTEDBG rdbg);
+
+#ifdef __cplusplus
+} /*extern "C"*/
+#endif
+
+#endif //_SQ_RDBG_H_
--- /dev/null
+/* see copyright notice in squirrel.h */
+#include <squirrel.h>
+#include <sqstdaux.h>
+#include <assert.h>
+
+void sqstd_printcallstack(HSQUIRRELVM v)
+{
+ SQPRINTFUNCTION pf = sq_getprintfunc(v);
+ if(pf) {
+ SQStackInfos si;
+ SQInteger i;
+ SQFloat f;
+ const SQChar *s;
+ SQInteger level=1; //1 is to skip this function that is level 0
+ const SQChar *name=0;
+ SQInteger seq=0;
+ pf(v,_SC("\nCALLSTACK\n"));
+ while(SQ_SUCCEEDED(sq_stackinfos(v,level,&si)))
+ {
+ const SQChar *fn=_SC("unknown");
+ const SQChar *src=_SC("unknown");
+ if(si.funcname)fn=si.funcname;
+ if(si.source)src=si.source;
+ pf(v,_SC("*FUNCTION [%s()] %s line [%d]\n"),fn,src,si.line);
+ level++;
+ }
+ level=0;
+ pf(v,_SC("\nLOCALS\n"));
+
+ for(level=0;level<10;level++){
+ seq=0;
+ while((name = sq_getlocal(v,level,seq)))
+ {
+ seq++;
+ switch(sq_gettype(v,-1))
+ {
+ case OT_NULL:
+ pf(v,_SC("[%s] NULL\n"),name);
+ break;
+ case OT_INTEGER:
+ sq_getinteger(v,-1,&i);
+ pf(v,_SC("[%s] %d\n"),name,i);
+ break;
+ case OT_FLOAT:
+ sq_getfloat(v,-1,&f);
+ pf(v,_SC("[%s] %.14g\n"),name,f);
+ break;
+ case OT_USERPOINTER:
+ pf(v,_SC("[%s] USERPOINTER\n"),name);
+ break;
+ case OT_STRING:
+ sq_getstring(v,-1,&s);
+ pf(v,_SC("[%s] \"%s\"\n"),name,s);
+ break;
+ case OT_TABLE:
+ pf(v,_SC("[%s] TABLE\n"),name);
+ break;
+ case OT_ARRAY:
+ pf(v,_SC("[%s] ARRAY\n"),name);
+ break;
+ case OT_CLOSURE:
+ pf(v,_SC("[%s] CLOSURE\n"),name);
+ break;
+ case OT_NATIVECLOSURE:
+ pf(v,_SC("[%s] NATIVECLOSURE\n"),name);
+ break;
+ case OT_GENERATOR:
+ pf(v,_SC("[%s] NATIVECLOSURE\n"),name);
+ break;
+ case OT_USERDATA:
+ pf(v,_SC("[%s] USERDATA\n"),name);
+ break;
+ case OT_THREAD:
+ pf(v,_SC("[%s] THREAD\n"),name);
+ break;
+ case OT_CLASS:
+ pf(v,_SC("[%s] CLASS\n"),name);
+ break;
+ case OT_INSTANCE:
+ pf(v,_SC("[%s] INSTANCE\n"),name);
+ break;
+ case OT_WEAKREF:
+ pf(v,_SC("[%s] WEAKREF\n"),name);
+ break;
+ case OT_BOOL:{
+ sq_getinteger(v,-1,&i);
+ pf(v,_SC("[%s] %s\n"),name,i?_SC("true"):_SC("false"));
+ }
+ break;
+ default: assert(0); break;
+ }
+ sq_pop(v,1);
+ }
+ }
+ }
+}
+
+static SQInteger _sqstd_aux_printerror(HSQUIRRELVM v)
+{
+ SQPRINTFUNCTION pf = sq_getprintfunc(v);
+ if(pf) {
+ const SQChar *sErr = 0;
+ if(sq_gettop(v)>=1) {
+ if(SQ_SUCCEEDED(sq_getstring(v,2,&sErr))) {
+ pf(v,_SC("\nAN ERROR HAS OCCURED [%s]\n"),sErr);
+ }
+ else{
+ pf(v,_SC("\nAN ERROR HAS OCCURED [unknown]\n"));
+ }
+ sqstd_printcallstack(v);
+ }
+ }
+ return 0;
+}
+
+void _sqstd_compiler_error(HSQUIRRELVM v,const SQChar *sErr,const SQChar *sSource,SQInteger line,SQInteger column)
+{
+ SQPRINTFUNCTION pf = sq_getprintfunc(v);
+ if(pf) {
+ pf(v,_SC("%s line = (%d) column = (%d) : error %s\n"),sSource,line,column,sErr);
+ }
+}
+
+void sqstd_seterrorhandlers(HSQUIRRELVM v)
+{
+ sq_setcompilererrorhandler(v,_sqstd_compiler_error);
+ sq_newclosure(v,_sqstd_aux_printerror,0);
+ sq_seterrorhandler(v);
+}
--- /dev/null
+/* see copyright notice in squirrel.h */
+#include <new>
+#include <squirrel.h>
+#include <sqstdio.h>
+#include <string.h>
+#include <sqstdblob.h>
+#include "sqstdstream.h"
+#include "sqstdblobimpl.h"
+
+#define SQSTD_BLOB_TYPE_TAG (SQSTD_STREAM_TYPE_TAG | 0x00000002)
+
+//Blob
+
+
+#define SETUP_BLOB(v) \
+ SQBlob *self = NULL; \
+ { if(SQ_FAILED(sq_getinstanceup(v,1,(SQUserPointer*)&self,(SQUserPointer)SQSTD_BLOB_TYPE_TAG))) \
+ return SQ_ERROR; }
+
+
+static SQInteger _blob_resize(HSQUIRRELVM v)
+{
+ SETUP_BLOB(v);
+ SQInteger size;
+ sq_getinteger(v,2,&size);
+ if(!self->Resize(size))
+ return sq_throwerror(v,_SC("resize failed"));
+ return 0;
+}
+
+static void __swap_dword(unsigned int *n)
+{
+ *n=(unsigned int)(((*n&0xFF000000)>>24) |
+ ((*n&0x00FF0000)>>8) |
+ ((*n&0x0000FF00)<<8) |
+ ((*n&0x000000FF)<<24));
+}
+
+static void __swap_word(unsigned short *n)
+{
+ *n=(unsigned short)((*n>>8)&0x00FF)| ((*n<<8)&0xFF00);
+}
+
+static SQInteger _blob_swap4(HSQUIRRELVM v)
+{
+ SETUP_BLOB(v);
+ SQInteger num=(self->Len()-(self->Len()%4))>>2;
+ unsigned int *t=(unsigned int *)self->GetBuf();
+ for(SQInteger i = 0; i < num; i++) {
+ __swap_dword(&t[i]);
+ }
+ return 0;
+}
+
+static SQInteger _blob_swap2(HSQUIRRELVM v)
+{
+ SETUP_BLOB(v);
+ SQInteger num=(self->Len()-(self->Len()%2))>>1;
+ unsigned short *t = (unsigned short *)self->GetBuf();
+ for(SQInteger i = 0; i < num; i++) {
+ __swap_word(&t[i]);
+ }
+ return 0;
+}
+
+static SQInteger _blob__set(HSQUIRRELVM v)
+{
+ SETUP_BLOB(v);
+ SQInteger idx,val;
+ sq_getinteger(v,2,&idx);
+ sq_getinteger(v,3,&val);
+ if(idx < 0 || idx >= self->Len())
+ return sq_throwerror(v,_SC("index out of range"));
+ ((unsigned char *)self->GetBuf())[idx] = (unsigned char) val;
+ sq_push(v,3);
+ return 1;
+}
+
+static SQInteger _blob__get(HSQUIRRELVM v)
+{
+ SETUP_BLOB(v);
+ SQInteger idx;
+ sq_getinteger(v,2,&idx);
+ if(idx < 0 || idx >= self->Len())
+ return sq_throwerror(v,_SC("index out of range"));
+ sq_pushinteger(v,((unsigned char *)self->GetBuf())[idx]);
+ return 1;
+}
+
+static SQInteger _blob__nexti(HSQUIRRELVM v)
+{
+ SETUP_BLOB(v);
+ if(sq_gettype(v,2) == OT_NULL) {
+ sq_pushinteger(v, 0);
+ return 1;
+ }
+ SQInteger idx;
+ if(SQ_SUCCEEDED(sq_getinteger(v, 2, &idx))) {
+ if(idx+1 < self->Len()) {
+ sq_pushinteger(v, idx+1);
+ return 1;
+ }
+ sq_pushnull(v);
+ return 1;
+ }
+ return sq_throwerror(v,_SC("internal error (_nexti) wrong argument type"));
+}
+
+static SQInteger _blob__typeof(HSQUIRRELVM v)
+{
+ sq_pushstring(v,_SC("blob"),-1);
+ return 1;
+}
+
+static SQInteger _blob_releasehook(SQUserPointer p, SQInteger size)
+{
+ SQBlob *self = (SQBlob*)p;
+ delete self;
+ return 1;
+}
+
+static SQInteger _blob_constructor(HSQUIRRELVM v)
+{
+ SQInteger nparam = sq_gettop(v);
+ SQInteger size = 0;
+ if(nparam == 2) {
+ sq_getinteger(v, 2, &size);
+ }
+ if(size < 0) return sq_throwerror(v, _SC("cannot create blob with negative size"));
+ SQBlob *b = new SQBlob(size);
+ if(SQ_FAILED(sq_setinstanceup(v,1,b))) {
+ delete b;
+ return sq_throwerror(v, _SC("cannot create blob with negative size"));
+ }
+ sq_setreleasehook(v,1,_blob_releasehook);
+ return 0;
+}
+
+#define _DECL_BLOB_FUNC(name,nparams,typecheck) {_SC(#name),_blob_##name,nparams,typecheck}
+static SQRegFunction _blob_methods[] = {
+ _DECL_BLOB_FUNC(constructor,-1,_SC("xn")),
+ _DECL_BLOB_FUNC(resize,2,_SC("xn")),
+ _DECL_BLOB_FUNC(swap2,1,_SC("x")),
+ _DECL_BLOB_FUNC(swap4,1,_SC("x")),
+ _DECL_BLOB_FUNC(_set,3,_SC("xnn")),
+ _DECL_BLOB_FUNC(_get,2,_SC("xn")),
+ _DECL_BLOB_FUNC(_typeof,1,_SC("x")),
+ _DECL_BLOB_FUNC(_nexti,2,_SC("x")),
+ {0,0,0,0}
+};
+
+
+
+//GLOBAL FUNCTIONS
+
+static SQInteger _g_blob_casti2f(HSQUIRRELVM v)
+{
+ SQInteger i;
+ sq_getinteger(v,2,&i);
+ sq_pushfloat(v,*((SQFloat *)&i));
+ return 1;
+}
+
+static SQInteger _g_blob_castf2i(HSQUIRRELVM v)
+{
+ SQFloat f;
+ sq_getfloat(v,2,&f);
+ sq_pushinteger(v,*((SQInteger *)&f));
+ return 1;
+}
+
+static SQInteger _g_blob_swap2(HSQUIRRELVM v)
+{
+ SQInteger i;
+ sq_getinteger(v,2,&i);
+ short s=(short)i;
+ sq_pushinteger(v,(s<<8)|((s>>8)&0x00FF));
+ return 1;
+}
+
+static SQInteger _g_blob_swap4(HSQUIRRELVM v)
+{
+ SQInteger i;
+ sq_getinteger(v,2,&i);
+ unsigned int t4 = (unsigned int)i;
+ __swap_dword(&t4);
+ sq_pushinteger(v,(SQInteger)t4);
+ return 1;
+}
+
+static SQInteger _g_blob_swapfloat(HSQUIRRELVM v)
+{
+ SQFloat f;
+ sq_getfloat(v,2,&f);
+ __swap_dword((unsigned int *)&f);
+ sq_pushfloat(v,f);
+ return 1;
+}
+
+#define _DECL_GLOBALBLOB_FUNC(name,nparams,typecheck) {_SC(#name),_g_blob_##name,nparams,typecheck}
+static SQRegFunction bloblib_funcs[]={
+ _DECL_GLOBALBLOB_FUNC(casti2f,2,_SC(".n")),
+ _DECL_GLOBALBLOB_FUNC(castf2i,2,_SC(".n")),
+ _DECL_GLOBALBLOB_FUNC(swap2,2,_SC(".n")),
+ _DECL_GLOBALBLOB_FUNC(swap4,2,_SC(".n")),
+ _DECL_GLOBALBLOB_FUNC(swapfloat,2,_SC(".n")),
+ {0,0}
+};
+
+SQRESULT sqstd_getblob(HSQUIRRELVM v,SQInteger idx,SQUserPointer *ptr)
+{
+ SQBlob *blob;
+ if(SQ_FAILED(sq_getinstanceup(v,idx,(SQUserPointer *)&blob,(SQUserPointer)SQSTD_BLOB_TYPE_TAG)))
+ return -1;
+ *ptr = blob->GetBuf();
+ return SQ_OK;
+}
+
+SQInteger sqstd_getblobsize(HSQUIRRELVM v,SQInteger idx)
+{
+ SQBlob *blob;
+ if(SQ_FAILED(sq_getinstanceup(v,idx,(SQUserPointer *)&blob,(SQUserPointer)SQSTD_BLOB_TYPE_TAG)))
+ return -1;
+ return blob->Len();
+}
+
+SQUserPointer sqstd_createblob(HSQUIRRELVM v, SQInteger size)
+{
+ SQInteger top = sq_gettop(v);
+ sq_pushregistrytable(v);
+ sq_pushstring(v,_SC("std_blob"),-1);
+ if(SQ_SUCCEEDED(sq_get(v,-2))) {
+ sq_remove(v,-2); //removes the registry
+ sq_push(v,1); // push the this
+ sq_pushinteger(v,size); //size
+ SQBlob *blob = NULL;
+ if(SQ_SUCCEEDED(sq_call(v,2,SQTrue,SQFalse))
+ && SQ_SUCCEEDED(sq_getinstanceup(v,-1,(SQUserPointer *)&blob,(SQUserPointer)SQSTD_BLOB_TYPE_TAG))) {
+ sq_remove(v,-2);
+ return blob->GetBuf();
+ }
+ }
+ sq_settop(v,top);
+ return NULL;
+}
+
+SQRESULT sqstd_register_bloblib(HSQUIRRELVM v)
+{
+ return declare_stream(v,_SC("blob"),(SQUserPointer)SQSTD_BLOB_TYPE_TAG,_SC("std_blob"),_blob_methods,bloblib_funcs);
+}
+
--- /dev/null
+/* see copyright notice in squirrel.h */
+#ifndef _SQSTD_BLOBIMPL_H_
+#define _SQSTD_BLOBIMPL_H_
+
+struct SQBlob : public SQStream
+{
+ SQBlob(SQInteger size) {
+ _size = size;
+ _allocated = size;
+ _buf = (unsigned char *)sq_malloc(size);
+ memset(_buf, 0, _size);
+ _ptr = 0;
+ _owns = true;
+ }
+ virtual ~SQBlob() {
+ sq_free(_buf, _allocated);
+ }
+ SQInteger Write(void *buffer, SQInteger size) {
+ if(!CanAdvance(size)) {
+ GrowBufOf(_ptr + size - _size);
+ }
+ memcpy(&_buf[_ptr], buffer, size);
+ _ptr += size;
+ return size;
+ }
+ SQInteger Read(void *buffer,SQInteger size) {
+ SQInteger n = size;
+ if(!CanAdvance(size)) {
+ if((_size - _ptr) > 0)
+ n = _size - _ptr;
+ else return 0;
+ }
+ memcpy(buffer, &_buf[_ptr], n);
+ _ptr += n;
+ return n;
+ }
+ bool Resize(SQInteger n) {
+ if(!_owns) return false;
+ if(n != _allocated) {
+ unsigned char *newbuf = (unsigned char *)sq_malloc(n);
+ memset(newbuf,0,n);
+ if(_size > n)
+ memcpy(newbuf,_buf,n);
+ else
+ memcpy(newbuf,_buf,_size);
+ sq_free(_buf,_allocated);
+ _buf=newbuf;
+ _allocated = n;
+ if(_size > _allocated)
+ _size = _allocated;
+ if(_ptr > _allocated)
+ _ptr = _allocated;
+ }
+ return true;
+ }
+ bool GrowBufOf(SQInteger n)
+ {
+ bool ret = true;
+ if(_size + n > _allocated) {
+ if(_size + n > _size * 2)
+ ret = Resize(_size + n);
+ else
+ ret = Resize(_size * 2);
+ }
+ _size = _size + n;
+ return ret;
+ }
+ bool CanAdvance(SQInteger n) {
+ if(_ptr+n>_size)return false;
+ return true;
+ }
+ SQInteger Seek(SQInteger offset, SQInteger origin) {
+ switch(origin) {
+ case SQ_SEEK_SET:
+ if(offset > _size || offset < 0) return -1;
+ _ptr = offset;
+ break;
+ case SQ_SEEK_CUR:
+ if(_ptr + offset > _size || _ptr + offset < 0) return -1;
+ _ptr += offset;
+ break;
+ case SQ_SEEK_END:
+ if(_size + offset > _size || _size + offset < 0) return -1;
+ _ptr = _size + offset;
+ break;
+ default: return -1;
+ }
+ return 0;
+ }
+ bool IsValid() {
+ return _buf?true:false;
+ }
+ bool EOS() {
+ return _ptr == _size;
+ }
+ SQInteger Flush() { return 0; }
+ SQInteger Tell() { return _ptr; }
+ SQInteger Len() { return _size; }
+ SQUserPointer GetBuf(){ return _buf; }
+private:
+ SQInteger _size;
+ SQInteger _allocated;
+ SQInteger _ptr;
+ unsigned char *_buf;
+ bool _owns;
+};
+
+#endif //_SQSTD_BLOBIMPL_H_
--- /dev/null
+/* see copyright notice in squirrel.h */
+#include <new>
+#include <stdio.h>
+#include <squirrel.h>
+#include <sqstdio.h>
+#include "sqstdstream.h"
+
+#define SQSTD_FILE_TYPE_TAG (SQSTD_STREAM_TYPE_TAG | 0x00000001)
+//basic API
+SQFILE sqstd_fopen(const SQChar *filename ,const SQChar *mode)
+{
+#ifndef _UNICODE
+ return (SQFILE)fopen(filename,mode);
+#else
+ return (SQFILE)_wfopen(filename,mode);
+#endif
+}
+
+SQInteger sqstd_fread(void* buffer, SQInteger size, SQInteger count, SQFILE file)
+{
+ return (SQInteger)fread(buffer,size,count,(FILE *)file);
+}
+
+SQInteger sqstd_fwrite(const SQUserPointer buffer, SQInteger size, SQInteger count, SQFILE file)
+{
+ return (SQInteger)fwrite(buffer,size,count,(FILE *)file);
+}
+
+SQInteger sqstd_fseek(SQFILE file, SQInteger offset, SQInteger origin)
+{
+ SQInteger realorigin;
+ switch(origin) {
+ case SQ_SEEK_CUR: realorigin = SEEK_CUR; break;
+ case SQ_SEEK_END: realorigin = SEEK_END; break;
+ case SQ_SEEK_SET: realorigin = SEEK_SET; break;
+ default: return -1; //failed
+ }
+ return fseek((FILE *)file,(long)offset,(int)realorigin);
+}
+
+SQInteger sqstd_ftell(SQFILE file)
+{
+ return ftell((FILE *)file);
+}
+
+SQInteger sqstd_fflush(SQFILE file)
+{
+ return fflush((FILE *)file);
+}
+
+SQInteger sqstd_fclose(SQFILE file)
+{
+ return fclose((FILE *)file);
+}
+
+SQInteger sqstd_feof(SQFILE file)
+{
+ return feof((FILE *)file);
+}
+
+//File
+struct SQFile : public SQStream {
+ SQFile() { _handle = NULL; _owns = false;}
+ SQFile(SQFILE file, bool owns) { _handle = file; _owns = owns;}
+ virtual ~SQFile() { Close(); }
+ bool Open(const SQChar *filename ,const SQChar *mode) {
+ Close();
+ if( (_handle = sqstd_fopen(filename,mode)) ) {
+ _owns = true;
+ return true;
+ }
+ return false;
+ }
+ void Close() {
+ if(_handle && _owns) {
+ sqstd_fclose(_handle);
+ _handle = NULL;
+ _owns = false;
+ }
+ }
+ SQInteger Read(void *buffer,SQInteger size) {
+ return sqstd_fread(buffer,1,size,_handle);
+ }
+ SQInteger Write(void *buffer,SQInteger size) {
+ return sqstd_fwrite(buffer,1,size,_handle);
+ }
+ SQInteger Flush() {
+ return sqstd_fflush(_handle);
+ }
+ SQInteger Tell() {
+ return sqstd_ftell(_handle);
+ }
+ SQInteger Len() {
+ SQInteger prevpos=Tell();
+ Seek(0,SQ_SEEK_END);
+ SQInteger size=Tell();
+ Seek(prevpos,SQ_SEEK_SET);
+ return size;
+ }
+ SQInteger Seek(SQInteger offset, SQInteger origin) {
+ return sqstd_fseek(_handle,offset,origin);
+ }
+ bool IsValid() { return _handle?true:false; }
+ bool EOS() { return Tell()==Len()?true:false;}
+ SQFILE GetHandle() {return _handle;}
+private:
+ SQFILE _handle;
+ bool _owns;
+};
+
+static SQInteger _file__typeof(HSQUIRRELVM v)
+{
+ sq_pushstring(v,_SC("file"),-1);
+ return 1;
+}
+
+static SQInteger _file_releasehook(SQUserPointer p, SQInteger size)
+{
+ SQFile *self = (SQFile*)p;
+ delete self;
+ return 1;
+}
+
+static SQInteger _file_constructor(HSQUIRRELVM v)
+{
+ const SQChar *filename,*mode;
+ bool owns = true;
+ SQFile *f;
+ SQFILE newf;
+ if(sq_gettype(v,2) == OT_STRING && sq_gettype(v,3) == OT_STRING) {
+ sq_getstring(v, 2, &filename);
+ sq_getstring(v, 3, &mode);
+ newf = sqstd_fopen(filename, mode);
+ if(!newf) return sq_throwerror(v, _SC("cannot open file"));
+ } else if(sq_gettype(v,2) == OT_USERPOINTER) {
+ owns = !(sq_gettype(v,3) == OT_NULL);
+ sq_getuserpointer(v,2,&newf);
+ } else {
+ return sq_throwerror(v,_SC("wrong parameter"));
+ }
+ f = new SQFile(newf,owns);
+ if(SQ_FAILED(sq_setinstanceup(v,1,f))) {
+ delete f;
+ return sq_throwerror(v, _SC("cannot create blob with negative size"));
+ }
+ sq_setreleasehook(v,1,_file_releasehook);
+ return 0;
+}
+
+//bindings
+#define _DECL_FILE_FUNC(name,nparams,typecheck) {_SC(#name),_file_##name,nparams,typecheck}
+static SQRegFunction _file_methods[] = {
+ _DECL_FILE_FUNC(constructor,3,_SC("x")),
+ _DECL_FILE_FUNC(_typeof,1,_SC("x")),
+ {0,0,0,0},
+};
+
+
+
+SQRESULT sqstd_createfile(HSQUIRRELVM v, SQFILE file,SQBool own)
+{
+ SQInteger top = sq_gettop(v);
+ sq_pushregistrytable(v);
+ sq_pushstring(v,_SC("std_file"),-1);
+ if(SQ_SUCCEEDED(sq_get(v,-2))) {
+ sq_remove(v,-2); //removes the registry
+ sq_pushroottable(v); // push the this
+ sq_pushuserpointer(v,file); //file
+ if(own){
+ sq_pushinteger(v,1); //true
+ }
+ else{
+ sq_pushnull(v); //false
+ }
+ if(SQ_SUCCEEDED( sq_call(v,3,SQTrue,SQFalse) )) {
+ sq_remove(v,-2);
+ return SQ_OK;
+ }
+ }
+ sq_settop(v,top);
+ return SQ_OK;
+}
+
+SQRESULT sqstd_getfile(HSQUIRRELVM v, SQInteger idx, SQFILE *file)
+{
+ SQFile *fileobj = NULL;
+ if(SQ_SUCCEEDED(sq_getinstanceup(v,idx,(SQUserPointer*)&fileobj,(SQUserPointer)SQSTD_FILE_TYPE_TAG))) {
+ *file = fileobj->GetHandle();
+ return SQ_OK;
+ }
+ return sq_throwerror(v,_SC("not a file"));
+}
+
+
+
+static SQInteger _io_file_lexfeed_ASCII(SQUserPointer file)
+{
+ SQInteger ret;
+ char c;
+ if( ( ret=sqstd_fread(&c,sizeof(c),1,(FILE *)file )>0) )
+ return c;
+ return 0;
+}
+
+static SQInteger _io_file_lexfeed_UTF8(SQUserPointer file)
+{
+#define READ() \
+ if(sqstd_fread(&inchar,sizeof(inchar),1,(FILE *)file) != 1) \
+ return 0;
+
+ static const SQInteger utf8_lengths[16] =
+ {
+ 1,1,1,1,1,1,1,1, /* 0000 to 0111 : 1 byte (plain ASCII) */
+ 0,0,0,0, /* 1000 to 1011 : not valid */
+ 2,2, /* 1100, 1101 : 2 bytes */
+ 3, /* 1110 : 3 bytes */
+ 4 /* 1111 :4 bytes */
+ };
+ static unsigned char byte_masks[5] = {0,0,0x1f,0x0f,0x07};
+ unsigned char inchar;
+ SQInteger c = 0;
+ READ();
+ c = inchar;
+ //
+ if(c >= 0x80) {
+ SQInteger tmp;
+ SQInteger codelen = utf8_lengths[c>>4];
+ if(codelen == 0)
+ return 0;
+ //"invalid UTF-8 stream";
+ tmp = c&byte_masks[codelen];
+ for(SQInteger n = 0; n < codelen-1; n++) {
+ tmp<<=6;
+ READ();
+ tmp |= inchar & 0x3F;
+ }
+ c = tmp;
+ }
+ return c;
+}
+
+static SQInteger _io_file_lexfeed_UCS2_LE(SQUserPointer file)
+{
+ SQInteger ret;
+ wchar_t c;
+ if( ( ret=sqstd_fread(&c,sizeof(c),1,(FILE *)file )>0) )
+ return (SQChar)c;
+ return 0;
+}
+
+static SQInteger _io_file_lexfeed_UCS2_BE(SQUserPointer file)
+{
+ SQInteger ret;
+ unsigned short c;
+ if( ( ret=sqstd_fread(&c,sizeof(c),1,(FILE *)file )>0) ) {
+ c = ((c>>8)&0x00FF)| ((c<<8)&0xFF00);
+ return (SQChar)c;
+ }
+ return 0;
+}
+
+SQInteger file_read(SQUserPointer file,SQUserPointer buf,SQInteger size)
+{
+ SQInteger ret;
+ if( ( ret = sqstd_fread(buf,1,size,(SQFILE)file ))!=0 )return ret;
+ return -1;
+}
+
+SQInteger file_write(SQUserPointer file,SQUserPointer p,SQInteger size)
+{
+ return sqstd_fwrite(p,1,size,(SQFILE)file);
+}
+
+SQRESULT sqstd_loadfile(HSQUIRRELVM v,const SQChar *filename,SQBool printerror)
+{
+ SQFILE file = sqstd_fopen(filename,_SC("rb"));
+ SQInteger ret;
+ unsigned short us;
+ unsigned char uc;
+ SQLEXREADFUNC func = _io_file_lexfeed_ASCII;
+ if(file){
+ ret = sqstd_fread(&us,1,2,file);
+ if(ret != 2) {
+ //probably an empty file
+ us = 0;
+ }
+ if(us == SQ_BYTECODE_STREAM_TAG) { //BYTECODE
+ sqstd_fseek(file,0,SQ_SEEK_SET);
+ if(SQ_SUCCEEDED(sq_readclosure(v,file_read,file))) {
+ sqstd_fclose(file);
+ return SQ_OK;
+ }
+ }
+ else { //SCRIPT
+ switch(us)
+ {
+ //gotta swap the next 2 lines on BIG endian machines
+ case 0xFFFE: func = _io_file_lexfeed_UCS2_BE; break;//UTF-16 little endian;
+ case 0xFEFF: func = _io_file_lexfeed_UCS2_LE; break;//UTF-16 big endian;
+ case 0xBBEF:
+ if(sqstd_fread(&uc,1,sizeof(uc),file) == 0) {
+ sqstd_fclose(file);
+ return sq_throwerror(v,_SC("io error"));
+ }
+ if(uc != 0xBF) {
+ sqstd_fclose(file);
+ return sq_throwerror(v,_SC("Unrecognozed ecoding"));
+ }
+ func = _io_file_lexfeed_UTF8;
+ break;//UTF-8 ;
+ default: sqstd_fseek(file,0,SQ_SEEK_SET); break; // ascii
+ }
+
+ if(SQ_SUCCEEDED(sq_compile(v,func,file,filename,printerror))){
+ sqstd_fclose(file);
+ return SQ_OK;
+ }
+ }
+ sqstd_fclose(file);
+ return SQ_ERROR;
+ }
+ return sq_throwerror(v,_SC("cannot open the file"));
+}
+
+SQRESULT sqstd_dofile(HSQUIRRELVM v,const SQChar *filename,SQBool retval,SQBool printerror)
+{
+ if(SQ_SUCCEEDED(sqstd_loadfile(v,filename,printerror))) {
+ sq_push(v,-2);
+ if(SQ_SUCCEEDED(sq_call(v,1,retval,SQTrue))) {
+ sq_remove(v,retval?-2:-1); //removes the closure
+ return 1;
+ }
+ sq_pop(v,1); //removes the closure
+ }
+ return SQ_ERROR;
+}
+
+SQRESULT sqstd_writeclosuretofile(HSQUIRRELVM v,const SQChar *filename)
+{
+ SQFILE file = sqstd_fopen(filename,_SC("wb+"));
+ if(!file) return sq_throwerror(v,_SC("cannot open the file"));
+ if(SQ_SUCCEEDED(sq_writeclosure(v,file_write,file))) {
+ sqstd_fclose(file);
+ return SQ_OK;
+ }
+ sqstd_fclose(file);
+ return SQ_ERROR; //forward the error
+}
+
+SQInteger _g_io_loadfile(HSQUIRRELVM v)
+{
+ const SQChar *filename;
+ SQBool printerror = SQFalse;
+ sq_getstring(v,2,&filename);
+ if(sq_gettop(v) >= 3) {
+ sq_getbool(v,3,&printerror);
+ }
+ if(SQ_SUCCEEDED(sqstd_loadfile(v,filename,printerror)))
+ return 1;
+ return SQ_ERROR; //propagates the error
+}
+
+SQInteger _g_io_writeclosuretofile(HSQUIRRELVM v)
+{
+ const SQChar *filename;
+ sq_getstring(v,2,&filename);
+ if(SQ_SUCCEEDED(sqstd_writeclosuretofile(v,filename)))
+ return 1;
+ return SQ_ERROR; //propagates the error
+}
+
+SQInteger _g_io_dofile(HSQUIRRELVM v)
+{
+ const SQChar *filename;
+ SQBool printerror = SQFalse;
+ sq_getstring(v,2,&filename);
+ if(sq_gettop(v) >= 3) {
+ sq_getbool(v,3,&printerror);
+ }
+ sq_push(v,1); //repush the this
+ if(SQ_SUCCEEDED(sqstd_dofile(v,filename,SQTrue,printerror)))
+ return 1;
+ return SQ_ERROR; //propagates the error
+}
+
+#define _DECL_GLOBALIO_FUNC(name,nparams,typecheck) {_SC(#name),_g_io_##name,nparams,typecheck}
+static SQRegFunction iolib_funcs[]={
+ _DECL_GLOBALIO_FUNC(loadfile,-2,_SC(".sb")),
+ _DECL_GLOBALIO_FUNC(dofile,-2,_SC(".sb")),
+ _DECL_GLOBALIO_FUNC(writeclosuretofile,3,_SC(".sc")),
+ {0,0}
+};
+
+SQRESULT sqstd_register_iolib(HSQUIRRELVM v)
+{
+ SQInteger top = sq_gettop(v);
+ //create delegate
+ declare_stream(v,_SC("file"),(SQUserPointer)SQSTD_FILE_TYPE_TAG,_SC("std_file"),_file_methods,iolib_funcs);
+ sq_pushstring(v,_SC("stdout"),-1);
+ sqstd_createfile(v,stdout,SQFalse);
+ sq_createslot(v,-3);
+ sq_pushstring(v,_SC("stdin"),-1);
+ sqstd_createfile(v,stdin,SQFalse);
+ sq_createslot(v,-3);
+ sq_pushstring(v,_SC("stderr"),-1);
+ sqstd_createfile(v,stderr,SQFalse);
+ sq_createslot(v,-3);
+ sq_settop(v,top);
+ return SQ_OK;
+}
--- /dev/null
+/* see copyright notice in squirrel.h */
+#include <squirrel.h>
+#include <math.h>
+#include <stdlib.h>
+#include <sqstdmath.h>
+
+#define SINGLE_ARG_FUNC(_funcname) static SQInteger math_##_funcname(HSQUIRRELVM v){ \
+ SQFloat f; \
+ sq_getfloat(v,2,&f); \
+ sq_pushfloat(v,(SQFloat)_funcname(f)); \
+ return 1; \
+}
+
+#define TWO_ARGS_FUNC(_funcname) static SQInteger math_##_funcname(HSQUIRRELVM v){ \
+ SQFloat p1,p2; \
+ sq_getfloat(v,2,&p1); \
+ sq_getfloat(v,3,&p2); \
+ sq_pushfloat(v,(SQFloat)_funcname(p1,p2)); \
+ return 1; \
+}
+
+static SQInteger math_srand(HSQUIRRELVM v)
+{
+ SQInteger i;
+ if(!sq_getinteger(v,2,&i))return sq_throwerror(v,_SC("invalid param"));
+ srand((unsigned int)i);
+ return 0;
+}
+
+static SQInteger math_rand(HSQUIRRELVM v)
+{
+ sq_pushinteger(v,rand());
+ return 1;
+}
+
+static SQInteger math_abs(HSQUIRRELVM v)
+{
+ SQInteger n;
+ sq_getinteger(v,2,&n);
+ sq_pushinteger(v,(SQInteger)abs((int)n));
+ return 1;
+}
+
+SINGLE_ARG_FUNC(sqrt)
+SINGLE_ARG_FUNC(fabs)
+SINGLE_ARG_FUNC(sin)
+SINGLE_ARG_FUNC(cos)
+SINGLE_ARG_FUNC(asin)
+SINGLE_ARG_FUNC(acos)
+SINGLE_ARG_FUNC(log)
+SINGLE_ARG_FUNC(log10)
+SINGLE_ARG_FUNC(tan)
+SINGLE_ARG_FUNC(atan)
+TWO_ARGS_FUNC(atan2)
+TWO_ARGS_FUNC(pow)
+SINGLE_ARG_FUNC(floor)
+SINGLE_ARG_FUNC(ceil)
+SINGLE_ARG_FUNC(exp)
+
+#define _DECL_FUNC(name,nparams,tycheck) {_SC(#name),math_##name,nparams,tycheck}
+static SQRegFunction mathlib_funcs[] = {
+ _DECL_FUNC(sqrt,2,_SC(".n")),
+ _DECL_FUNC(sin,2,_SC(".n")),
+ _DECL_FUNC(cos,2,_SC(".n")),
+ _DECL_FUNC(asin,2,_SC(".n")),
+ _DECL_FUNC(acos,2,_SC(".n")),
+ _DECL_FUNC(log,2,_SC(".n")),
+ _DECL_FUNC(log10,2,_SC(".n")),
+ _DECL_FUNC(tan,2,_SC(".n")),
+ _DECL_FUNC(atan,2,_SC(".n")),
+ _DECL_FUNC(atan2,3,_SC(".nn")),
+ _DECL_FUNC(pow,3,_SC(".nn")),
+ _DECL_FUNC(floor,2,_SC(".n")),
+ _DECL_FUNC(ceil,2,_SC(".n")),
+ _DECL_FUNC(exp,2,_SC(".n")),
+ _DECL_FUNC(srand,2,_SC(".n")),
+ _DECL_FUNC(rand,1,NULL),
+ _DECL_FUNC(fabs,2,_SC(".n")),
+ _DECL_FUNC(abs,2,_SC(".n")),
+ {0,0},
+};
+
+#ifndef M_PI
+#define M_PI (3.14159265358979323846)
+#endif
+
+SQRESULT sqstd_register_mathlib(HSQUIRRELVM v)
+{
+ SQInteger i=0;
+ while(mathlib_funcs[i].name!=0) {
+ sq_pushstring(v,mathlib_funcs[i].name,-1);
+ sq_newclosure(v,mathlib_funcs[i].f,0);
+ sq_setparamscheck(v,mathlib_funcs[i].nparamscheck,mathlib_funcs[i].typemask);
+ sq_setnativeclosurename(v,-1,mathlib_funcs[i].name);
+ sq_createslot(v,-3);
+ i++;
+ }
+ sq_pushstring(v,_SC("RAND_MAX"),-1);
+ sq_pushinteger(v,RAND_MAX);
+ sq_createslot(v,-3);
+ sq_pushstring(v,_SC("PI"),-1);
+ sq_pushfloat(v,(SQFloat)M_PI);
+ sq_createslot(v,-3);
+ return SQ_OK;
+}
--- /dev/null
+/* see copyright notice in squirrel.h */
+#include <squirrel.h>
+#include <string.h>
+#include <ctype.h>
+#include <setjmp.h>
+#include "sqstdstring.h"
+
+#ifdef _UINCODE
+#define scisprint iswprint
+#else
+#define scisprint isprint
+#endif
+
+#ifdef _DEBUG
+#include <stdio.h>
+
+static const SQChar *g_nnames[] =
+{
+ _SC("NONE"),_SC("OP_GREEDY"), _SC("OP_OR"),
+ _SC("OP_EXPR"),_SC("OP_NOCAPEXPR"),_SC("OP_DOT"), _SC("OP_CLASS"),
+ _SC("OP_CCLASS"),_SC("OP_NCLASS"),_SC("OP_RANGE"),_SC("OP_CHAR"),
+ _SC("OP_EOL"),_SC("OP_BOL"),_SC("OP_WB")
+};
+
+#endif
+
+#define OP_GREEDY (MAX_CHAR+1) // * + ? {n}
+#define OP_OR (MAX_CHAR+2)
+#define OP_EXPR (MAX_CHAR+3) //parentesis ()
+#define OP_NOCAPEXPR (MAX_CHAR+4) //parentesis (?:)
+#define OP_DOT (MAX_CHAR+5)
+#define OP_CLASS (MAX_CHAR+6)
+#define OP_CCLASS (MAX_CHAR+7)
+#define OP_NCLASS (MAX_CHAR+8) //negates class the [^
+#define OP_RANGE (MAX_CHAR+9)
+#define OP_CHAR (MAX_CHAR+10)
+#define OP_EOL (MAX_CHAR+11)
+#define OP_BOL (MAX_CHAR+12)
+#define OP_WB (MAX_CHAR+13)
+
+#define SQREX_SYMBOL_ANY_CHAR ('.')
+#define SQREX_SYMBOL_GREEDY_ONE_OR_MORE ('+')
+#define SQREX_SYMBOL_GREEDY_ZERO_OR_MORE ('*')
+#define SQREX_SYMBOL_GREEDY_ZERO_OR_ONE ('?')
+#define SQREX_SYMBOL_BRANCH ('|')
+#define SQREX_SYMBOL_END_OF_STRING ('$')
+#define SQREX_SYMBOL_BEGINNING_OF_STRING ('^')
+#define SQREX_SYMBOL_ESCAPE_CHAR ('\\')
+
+
+typedef int SQRexNodeType;
+
+typedef struct tagSQRexNode{
+ SQRexNodeType type;
+ SQInteger left;
+ SQInteger right;
+ SQInteger next;
+}SQRexNode;
+
+struct SQRex{
+ const SQChar *_eol;
+ const SQChar *_bol;
+ const SQChar *_p;
+ SQInteger _first;
+ SQInteger _op;
+ SQRexNode *_nodes;
+ SQInteger _nallocated;
+ SQInteger _nsize;
+ SQInteger _nsubexpr;
+ SQRexMatch *_matches;
+ SQInteger _currsubexp;
+ void *_jmpbuf;
+ const SQChar **_error;
+};
+
+static SQInteger sqstd_rex_list(SQRex *exp);
+
+static SQInteger sqstd_rex_newnode(SQRex *exp, SQRexNodeType type)
+{
+ SQRexNode n;
+ n.type = type;
+ n.next = n.right = n.left = -1;
+ if(type == OP_EXPR)
+ n.right = exp->_nsubexpr++;
+ if(exp->_nallocated < (exp->_nsize + 1)) {
+ SQInteger oldsize = exp->_nallocated;
+ exp->_nallocated *= 2;
+ exp->_nodes = (SQRexNode *)sq_realloc(exp->_nodes, oldsize * sizeof(SQRexNode) ,exp->_nallocated * sizeof(SQRexNode));
+ }
+ exp->_nodes[exp->_nsize++] = n;
+ SQInteger newid = exp->_nsize - 1;
+ return (SQInteger)newid;
+}
+
+static void sqstd_rex_error(SQRex *exp,const SQChar *error)
+{
+ if(exp->_error) *exp->_error = error;
+ longjmp(*((jmp_buf*)exp->_jmpbuf),-1);
+}
+
+static void sqstd_rex_expect(SQRex *exp, SQInteger n){
+ if((*exp->_p) != n)
+ sqstd_rex_error(exp, _SC("expected paren"));
+ exp->_p++;
+}
+
+static SQChar sqstd_rex_escapechar(SQRex *exp)
+{
+ if(*exp->_p == SQREX_SYMBOL_ESCAPE_CHAR){
+ exp->_p++;
+ switch(*exp->_p) {
+ case 'v': exp->_p++; return '\v';
+ case 'n': exp->_p++; return '\n';
+ case 't': exp->_p++; return '\t';
+ case 'r': exp->_p++; return '\r';
+ case 'f': exp->_p++; return '\f';
+ default: return (*exp->_p++);
+ }
+ } else if(!scisprint(*exp->_p)) sqstd_rex_error(exp,_SC("letter expected"));
+ return (*exp->_p++);
+}
+
+static SQInteger sqstd_rex_charclass(SQRex *exp,SQInteger classid)
+{
+ SQInteger n = sqstd_rex_newnode(exp,OP_CCLASS);
+ exp->_nodes[n].left = classid;
+ return n;
+}
+
+static SQInteger sqstd_rex_charnode(SQRex *exp,SQBool isclass)
+{
+ SQChar t;
+ if(*exp->_p == SQREX_SYMBOL_ESCAPE_CHAR) {
+ exp->_p++;
+ switch(*exp->_p) {
+ case 'n': exp->_p++; return sqstd_rex_newnode(exp,'\n');
+ case 't': exp->_p++; return sqstd_rex_newnode(exp,'\t');
+ case 'r': exp->_p++; return sqstd_rex_newnode(exp,'\r');
+ case 'f': exp->_p++; return sqstd_rex_newnode(exp,'\f');
+ case 'v': exp->_p++; return sqstd_rex_newnode(exp,'\v');
+ case 'a': case 'A': case 'w': case 'W': case 's': case 'S':
+ case 'd': case 'D': case 'x': case 'X': case 'c': case 'C':
+ case 'p': case 'P': case 'l': case 'u':
+ {
+ t = *exp->_p; exp->_p++;
+ return sqstd_rex_charclass(exp,t);
+ }
+ case 'b':
+ case 'B':
+ if(!isclass) {
+ SQInteger node = sqstd_rex_newnode(exp,OP_WB);
+ exp->_nodes[node].left = *exp->_p;
+ exp->_p++;
+ return node;
+ } //else default
+ default:
+ t = *exp->_p; exp->_p++;
+ return sqstd_rex_newnode(exp,t);
+ }
+ }
+ else if(!scisprint(*exp->_p)) {
+
+ sqstd_rex_error(exp,_SC("letter expected"));
+ }
+ t = *exp->_p; exp->_p++;
+ return sqstd_rex_newnode(exp,t);
+}
+static SQInteger sqstd_rex_class(SQRex *exp)
+{
+ SQInteger ret = -1;
+ SQInteger first = -1,chain;
+ if(*exp->_p == SQREX_SYMBOL_BEGINNING_OF_STRING){
+ ret = sqstd_rex_newnode(exp,OP_NCLASS);
+ exp->_p++;
+ }else ret = sqstd_rex_newnode(exp,OP_CLASS);
+
+ if(*exp->_p == ']') sqstd_rex_error(exp,_SC("empty class"));
+ chain = ret;
+ while(*exp->_p != ']' && exp->_p != exp->_eol) {
+ if(*exp->_p == '-' && first != -1){
+ SQInteger r;
+ if(*exp->_p++ == ']') sqstd_rex_error(exp,_SC("unfinished range"));
+ r = sqstd_rex_newnode(exp,OP_RANGE);
+ if(first>*exp->_p) sqstd_rex_error(exp,_SC("invalid range"));
+ if(exp->_nodes[first].type == OP_CCLASS) sqstd_rex_error(exp,_SC("cannot use character classes in ranges"));
+ exp->_nodes[r].left = exp->_nodes[first].type;
+ SQInteger t = sqstd_rex_escapechar(exp);
+ exp->_nodes[r].right = t;
+ exp->_nodes[chain].next = r;
+ chain = r;
+ first = -1;
+ }
+ else{
+ if(first!=-1){
+ SQInteger c = first;
+ exp->_nodes[chain].next = c;
+ chain = c;
+ first = sqstd_rex_charnode(exp,SQTrue);
+ }
+ else{
+ first = sqstd_rex_charnode(exp,SQTrue);
+ }
+ }
+ }
+ if(first!=-1){
+ SQInteger c = first;
+ exp->_nodes[chain].next = c;
+ chain = c;
+ first = -1;
+ }
+ /* hack? */
+ exp->_nodes[ret].left = exp->_nodes[ret].next;
+ exp->_nodes[ret].next = -1;
+ return ret;
+}
+
+static SQInteger sqstd_rex_parsenumber(SQRex *exp)
+{
+ SQInteger ret = *exp->_p-'0';
+ SQInteger positions = 10;
+ exp->_p++;
+ while(isdigit(*exp->_p)) {
+ ret = ret*10+(*exp->_p++-'0');
+ if(positions==1000000000) sqstd_rex_error(exp,_SC("overflow in numeric constant"));
+ positions *= 10;
+ };
+ return ret;
+}
+
+static SQInteger sqstd_rex_element(SQRex *exp)
+{
+ SQInteger ret = -1;
+ switch(*exp->_p)
+ {
+ case '(': {
+ SQInteger expr;
+ exp->_p++;
+
+
+ if(*exp->_p =='?') {
+ exp->_p++;
+ sqstd_rex_expect(exp,':');
+ expr = sqstd_rex_newnode(exp,OP_NOCAPEXPR);
+ }
+ else
+ expr = sqstd_rex_newnode(exp,OP_EXPR);
+ SQInteger newn = sqstd_rex_list(exp);
+ exp->_nodes[expr].left = newn;
+ ret = expr;
+ sqstd_rex_expect(exp,')');
+ }
+ break;
+ case '[':
+ exp->_p++;
+ ret = sqstd_rex_class(exp);
+ sqstd_rex_expect(exp,']');
+ break;
+ case SQREX_SYMBOL_END_OF_STRING: exp->_p++; ret = sqstd_rex_newnode(exp,OP_EOL);break;
+ case SQREX_SYMBOL_ANY_CHAR: exp->_p++; ret = sqstd_rex_newnode(exp,OP_DOT);break;
+ default:
+ ret = sqstd_rex_charnode(exp,SQFalse);
+ break;
+ }
+
+
+ SQInteger op;
+ SQBool isgreedy = SQFalse;
+ unsigned short p0 = 0, p1 = 0;
+ switch(*exp->_p){
+ case SQREX_SYMBOL_GREEDY_ZERO_OR_MORE: p0 = 0; p1 = 0xFFFF; exp->_p++; isgreedy = SQTrue; break;
+ case SQREX_SYMBOL_GREEDY_ONE_OR_MORE: p0 = 1; p1 = 0xFFFF; exp->_p++; isgreedy = SQTrue; break;
+ case SQREX_SYMBOL_GREEDY_ZERO_OR_ONE: p0 = 0; p1 = 1; exp->_p++; isgreedy = SQTrue; break;
+ case '{':
+ exp->_p++;
+ if(!isdigit(*exp->_p)) sqstd_rex_error(exp,_SC("number expected"));
+ p0 = (unsigned short)sqstd_rex_parsenumber(exp);
+ /*******************************/
+ switch(*exp->_p) {
+ case '}':
+ p1 = p0; exp->_p++;
+ break;
+ case ',':
+ exp->_p++;
+ p1 = 0xFFFF;
+ if(isdigit(*exp->_p)){
+ p1 = (unsigned short)sqstd_rex_parsenumber(exp);
+ }
+ sqstd_rex_expect(exp,'}');
+ break;
+ default:
+ sqstd_rex_error(exp,_SC(", or } expected"));
+ }
+ /*******************************/
+ isgreedy = SQTrue;
+ break;
+
+ }
+ if(isgreedy) {
+ SQInteger nnode = sqstd_rex_newnode(exp,OP_GREEDY);
+ op = OP_GREEDY;
+ exp->_nodes[nnode].left = ret;
+ exp->_nodes[nnode].right = ((p0)<<16)|p1;
+ ret = nnode;
+ }
+
+ if((*exp->_p != SQREX_SYMBOL_BRANCH) && (*exp->_p != ')') && (*exp->_p != SQREX_SYMBOL_GREEDY_ZERO_OR_MORE) && (*exp->_p != SQREX_SYMBOL_GREEDY_ONE_OR_MORE) && (*exp->_p != '\0')) {
+ SQInteger nnode = sqstd_rex_element(exp);
+ exp->_nodes[ret].next = nnode;
+ }
+
+ return ret;
+}
+
+static SQInteger sqstd_rex_list(SQRex *exp)
+{
+ SQInteger ret=-1,e;
+ if(*exp->_p == SQREX_SYMBOL_BEGINNING_OF_STRING) {
+ exp->_p++;
+ ret = sqstd_rex_newnode(exp,OP_BOL);
+ }
+ e = sqstd_rex_element(exp);
+ if(ret != -1) {
+ exp->_nodes[ret].next = e;
+ }
+ else ret = e;
+
+ if(*exp->_p == SQREX_SYMBOL_BRANCH) {
+ SQInteger temp,tright;
+ exp->_p++;
+ temp = sqstd_rex_newnode(exp,OP_OR);
+ exp->_nodes[temp].left = ret;
+ tright = sqstd_rex_list(exp);
+ exp->_nodes[temp].right = tright;
+ ret = temp;
+ }
+ return ret;
+}
+
+static SQBool sqstd_rex_matchcclass(SQInteger cclass,SQChar c)
+{
+ switch(cclass) {
+ case 'a': return isalpha(c)?SQTrue:SQFalse;
+ case 'A': return !isalpha(c)?SQTrue:SQFalse;
+ case 'w': return (isalnum(c) || c == '_')?SQTrue:SQFalse;
+ case 'W': return (!isalnum(c) && c != '_')?SQTrue:SQFalse;
+ case 's': return isspace(c)?SQTrue:SQFalse;
+ case 'S': return !isspace(c)?SQTrue:SQFalse;
+ case 'd': return isdigit(c)?SQTrue:SQFalse;
+ case 'D': return !isdigit(c)?SQTrue:SQFalse;
+ case 'x': return isxdigit(c)?SQTrue:SQFalse;
+ case 'X': return !isxdigit(c)?SQTrue:SQFalse;
+ case 'c': return iscntrl(c)?SQTrue:SQFalse;
+ case 'C': return !iscntrl(c)?SQTrue:SQFalse;
+ case 'p': return ispunct(c)?SQTrue:SQFalse;
+ case 'P': return !ispunct(c)?SQTrue:SQFalse;
+ case 'l': return islower(c)?SQTrue:SQFalse;
+ case 'u': return isupper(c)?SQTrue:SQFalse;
+ }
+ return SQFalse; /*cannot happen*/
+}
+
+static SQBool sqstd_rex_matchclass(SQRex* exp,SQRexNode *node,SQChar c)
+{
+ do {
+ switch(node->type) {
+ case OP_RANGE:
+ if(c >= node->left && c <= node->right) return SQTrue;
+ break;
+ case OP_CCLASS:
+ if(sqstd_rex_matchcclass(node->left,c)) return SQTrue;
+ break;
+ default:
+ if(c == node->type)return SQTrue;
+ }
+ } while((node->next != -1) && (node = &exp->_nodes[node->next]));
+ return SQFalse;
+}
+
+static const SQChar *sqstd_rex_matchnode(SQRex* exp,SQRexNode *node,const SQChar *str,SQRexNode *next)
+{
+
+ SQRexNodeType type = node->type;
+ switch(type) {
+ case OP_GREEDY: {
+ //SQRexNode *greedystop = (node->next != -1) ? &exp->_nodes[node->next] : NULL;
+ SQRexNode *greedystop = NULL;
+ SQInteger p0 = (node->right >> 16)&0x0000FFFF, p1 = node->right&0x0000FFFF, nmaches = 0;
+ const SQChar *s=str, *good = str;
+
+ if(node->next != -1) {
+ greedystop = &exp->_nodes[node->next];
+ }
+ else {
+ greedystop = next;
+ }
+
+ while((nmaches == 0xFFFF || nmaches < p1)) {
+
+ const SQChar *stop;
+ if(!(s = sqstd_rex_matchnode(exp,&exp->_nodes[node->left],s,greedystop)))
+ break;
+ nmaches++;
+ good=s;
+ if(greedystop) {
+ //checks that 0 matches satisfy the expression(if so skips)
+ //if not would always stop(for instance if is a '?')
+ if(greedystop->type != OP_GREEDY ||
+ (greedystop->type == OP_GREEDY && ((greedystop->right >> 16)&0x0000FFFF) != 0))
+ {
+ SQRexNode *gnext = NULL;
+ if(greedystop->next != -1) {
+ gnext = &exp->_nodes[greedystop->next];
+ }else if(next && next->next != -1){
+ gnext = &exp->_nodes[next->next];
+ }
+ stop = sqstd_rex_matchnode(exp,greedystop,s,gnext);
+ if(stop) {
+ //if satisfied stop it
+ if(p0 == p1 && p0 == nmaches) break;
+ else if(nmaches >= p0 && p1 == 0xFFFF) break;
+ else if(nmaches >= p0 && nmaches <= p1) break;
+ }
+ }
+ }
+
+ if(s >= exp->_eol)
+ break;
+ }
+ if(p0 == p1 && p0 == nmaches) return good;
+ else if(nmaches >= p0 && p1 == 0xFFFF) return good;
+ else if(nmaches >= p0 && nmaches <= p1) return good;
+ return NULL;
+ }
+ case OP_OR: {
+ const SQChar *asd = str;
+ SQRexNode *temp=&exp->_nodes[node->left];
+ while( (asd = sqstd_rex_matchnode(exp,temp,asd,NULL)) ) {
+ if(temp->next != -1)
+ temp = &exp->_nodes[temp->next];
+ else
+ return asd;
+ }
+ asd = str;
+ temp = &exp->_nodes[node->right];
+ while( (asd = sqstd_rex_matchnode(exp,temp,asd,NULL)) ) {
+ if(temp->next != -1)
+ temp = &exp->_nodes[temp->next];
+ else
+ return asd;
+ }
+ return NULL;
+ break;
+ }
+ case OP_EXPR:
+ case OP_NOCAPEXPR:{
+ SQRexNode *n = &exp->_nodes[node->left];
+ const SQChar *cur = str;
+ SQInteger capture = -1;
+ if(node->type != OP_NOCAPEXPR && node->right == exp->_currsubexp) {
+ capture = exp->_currsubexp;
+ exp->_matches[capture].begin = cur;
+ exp->_currsubexp++;
+ }
+
+ do {
+ SQRexNode *subnext = NULL;
+ if(n->next != -1) {
+ subnext = &exp->_nodes[n->next];
+ }else {
+ subnext = next;
+ }
+ if(!(cur = sqstd_rex_matchnode(exp,n,cur,subnext))) {
+ if(capture != -1){
+ exp->_matches[capture].begin = 0;
+ exp->_matches[capture].len = 0;
+ }
+ return NULL;
+ }
+ } while((n->next != -1) && (n = &exp->_nodes[n->next]));
+
+ if(capture != -1)
+ exp->_matches[capture].len = cur - exp->_matches[capture].begin;
+ return cur;
+ }
+ case OP_WB:
+ if(str == exp->_bol && !isspace(*str)
+ || (str == exp->_eol && !isspace(*(str-1)))
+ || (!isspace(*str) && isspace(*(str+1)))
+ || (isspace(*str) && !isspace(*(str+1))) ) {
+ return (node->left == 'b')?str:NULL;
+ }
+ return (node->left == 'b')?NULL:str;
+ case OP_BOL:
+ if(str == exp->_bol) return str;
+ return NULL;
+ case OP_EOL:
+ if(str == exp->_eol) return str;
+ return NULL;
+ case OP_DOT:{
+ *str++;
+ }
+ return str;
+ case OP_NCLASS:
+ case OP_CLASS:
+ if(sqstd_rex_matchclass(exp,&exp->_nodes[node->left],*str)?(type == OP_CLASS?SQTrue:SQFalse):(type == OP_NCLASS?SQTrue:SQFalse)) {
+ *str++;
+ return str;
+ }
+ return NULL;
+ case OP_CCLASS:
+ if(sqstd_rex_matchcclass(node->left,*str)) {
+ *str++;
+ return str;
+ }
+ return NULL;
+ default: /* char */
+ if(*str != node->type) return NULL;
+ *str++;
+ return str;
+ }
+ return NULL;
+}
+
+/* public api */
+SQRex *sqstd_rex_compile(const SQChar *pattern,const SQChar **error)
+{
+ SQRex *exp = (SQRex *)sq_malloc(sizeof(SQRex));
+ exp->_eol = exp->_bol = NULL;
+ exp->_p = pattern;
+ exp->_nallocated = (SQInteger)scstrlen(pattern) * sizeof(SQChar);
+ exp->_nodes = (SQRexNode *)sq_malloc(exp->_nallocated * sizeof(SQRexNode));
+ exp->_nsize = 0;
+ exp->_matches = 0;
+ exp->_nsubexpr = 0;
+ exp->_first = sqstd_rex_newnode(exp,OP_EXPR);
+ exp->_error = error;
+ exp->_jmpbuf = sq_malloc(sizeof(jmp_buf));
+ if(setjmp(*((jmp_buf*)exp->_jmpbuf)) == 0) {
+ SQInteger res = sqstd_rex_list(exp);
+ exp->_nodes[exp->_first].left = res;
+ if(*exp->_p!='\0')
+ sqstd_rex_error(exp,_SC("unexpected character"));
+#ifdef _DEBUG
+ {
+ SQInteger nsize,i;
+ SQRexNode *t;
+ nsize = exp->_nsize;
+ t = &exp->_nodes[0];
+ scprintf(_SC("\n"));
+ for(i = 0;i < nsize; i++) {
+ if(exp->_nodes[i].type>MAX_CHAR)
+ scprintf(_SC("[%02d] %10s "),i,g_nnames[exp->_nodes[i].type-MAX_CHAR]);
+ else
+ scprintf(_SC("[%02d] %10c "),i,exp->_nodes[i].type);
+ scprintf(_SC("left %02d right %02d next %02d\n"),exp->_nodes[i].left,exp->_nodes[i].right,exp->_nodes[i].next);
+ }
+ scprintf(_SC("\n"));
+ }
+#endif
+ exp->_matches = (SQRexMatch *) sq_malloc(exp->_nsubexpr * sizeof(SQRexMatch));
+ memset(exp->_matches,0,exp->_nsubexpr * sizeof(SQRexMatch));
+ }
+ else{
+ sqstd_rex_free(exp);
+ return NULL;
+ }
+ return exp;
+}
+
+void sqstd_rex_free(SQRex *exp)
+{
+ if(exp) {
+ if(exp->_nodes) sq_free(exp->_nodes,exp->_nallocated * sizeof(SQRexNode));
+ if(exp->_jmpbuf) sq_free(exp->_jmpbuf,sizeof(jmp_buf));
+ if(exp->_matches) sq_free(exp->_matches,exp->_nsubexpr * sizeof(SQRexMatch));
+ sq_free(exp,sizeof(SQRex));
+ }
+}
+
+SQBool sqstd_rex_match(SQRex* exp,const SQChar* text)
+{
+ const SQChar* res = NULL;
+ exp->_bol = text;
+ exp->_eol = text + scstrlen(text);
+ exp->_currsubexp = 0;
+ res = sqstd_rex_matchnode(exp,exp->_nodes,text,NULL);
+ if(res == NULL || res != exp->_eol)
+ return SQFalse;
+ return SQTrue;
+}
+
+SQBool sqstd_rex_searchrange(SQRex* exp,const SQChar* text_begin,const SQChar* text_end,const SQChar** out_begin, const SQChar** out_end)
+{
+ const SQChar *cur = NULL;
+ SQInteger node = exp->_first;
+ if(text_begin >= text_end) return SQFalse;
+ exp->_bol = text_begin;
+ exp->_eol = text_end;
+ do {
+ cur = text_begin;
+ while(node != -1) {
+ exp->_currsubexp = 0;
+ cur = sqstd_rex_matchnode(exp,&exp->_nodes[node],cur,NULL);
+ if(!cur)
+ break;
+ node = exp->_nodes[node].next;
+ }
+ *text_begin++;
+ } while(cur == NULL && text_begin != text_end);
+
+ if(cur == NULL)
+ return SQFalse;
+
+ --text_begin;
+
+ if(out_begin) *out_begin = text_begin;
+ if(out_end) *out_end = cur;
+ return SQTrue;
+}
+
+SQBool sqstd_rex_search(SQRex* exp,const SQChar* text, const SQChar** out_begin, const SQChar** out_end)
+{
+ return sqstd_rex_searchrange(exp,text,text + scstrlen(text),out_begin,out_end);
+}
+
+SQInteger sqstd_rex_getsubexpcount(SQRex* exp)
+{
+ return exp->_nsubexpr;
+}
+
+SQBool sqstd_rex_getsubexp(SQRex* exp, SQInteger n, SQRexMatch *subexp)
+{
+ if( n<0 || n >= exp->_nsubexpr) return SQFalse;
+ *subexp = exp->_matches[n];
+ return SQTrue;
+}
+
--- /dev/null
+/* see copyright notice in squirrel.h */
+#include <new>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <squirrel.h>
+#include <sqstdio.h>
+#include <sqstdblob.h>
+#include "sqstdstream.h"
+#include "sqstdblobimpl.h"
+
+#define SETUP_STREAM(v) \
+ SQStream *self = NULL; \
+ if(SQ_FAILED(sq_getinstanceup(v,1,(SQUserPointer*)&self,(SQUserPointer)SQSTD_STREAM_TYPE_TAG))) \
+ return sq_throwerror(v,_SC("invalid type tag")); \
+ if(!self->IsValid()) \
+ return sq_throwerror(v,_SC("the stream is invalid"));
+
+SQInteger _stream_readblob(HSQUIRRELVM v)
+{
+ SETUP_STREAM(v);
+ SQUserPointer data,blobp;
+ SQInteger size,res;
+ sq_getinteger(v,2,&size);
+ if(size > self->Len()) {
+ size = self->Len();
+ }
+ data = sq_getscratchpad(v,size);
+ res = self->Read(data,size);
+ if(res <= 0)
+ return sq_throwerror(v,_SC("no data left to read"));
+ blobp = sqstd_createblob(v,res);
+ memcpy(blobp,data,res);
+ return 1;
+}
+
+#define SAFE_READN(ptr,len) { \
+ if(self->Read(ptr,len) != len) return sq_throwerror(v,_SC("io error")); \
+ }
+SQInteger _stream_readn(HSQUIRRELVM v)
+{
+ SETUP_STREAM(v);
+ SQInteger format;
+ sq_getinteger(v, 2, &format);
+ switch(format) {
+ case 'l': {
+ SQInteger i;
+ SAFE_READN(&i, sizeof(i));
+ sq_pushinteger(v, i);
+ }
+ break;
+ case 'i': {
+ SQInt32 i;
+ SAFE_READN(&i, sizeof(i));
+ sq_pushinteger(v, i);
+ }
+ break;
+ case 's': {
+ short s;
+ SAFE_READN(&s, sizeof(short));
+ sq_pushinteger(v, s);
+ }
+ break;
+ case 'w': {
+ unsigned short w;
+ SAFE_READN(&w, sizeof(unsigned short));
+ sq_pushinteger(v, w);
+ }
+ break;
+ case 'c': {
+ char c;
+ SAFE_READN(&c, sizeof(char));
+ sq_pushinteger(v, c);
+ }
+ break;
+ case 'b': {
+ unsigned char c;
+ SAFE_READN(&c, sizeof(unsigned char));
+ sq_pushinteger(v, c);
+ }
+ break;
+ case 'f': {
+ float f;
+ SAFE_READN(&f, sizeof(float));
+ sq_pushfloat(v, f);
+ }
+ break;
+ case 'd': {
+ double d;
+ SAFE_READN(&d, sizeof(double));
+ sq_pushfloat(v, (SQFloat)d);
+ }
+ break;
+ default:
+ return sq_throwerror(v, _SC("invalid format"));
+ }
+ return 1;
+}
+
+SQInteger _stream_writeblob(HSQUIRRELVM v)
+{
+ SQUserPointer data;
+ SQInteger size;
+ SETUP_STREAM(v);
+ if(SQ_FAILED(sqstd_getblob(v,2,&data)))
+ return sq_throwerror(v,_SC("invalid parameter"));
+ size = sqstd_getblobsize(v,2);
+ if(self->Write(data,size) != size)
+ return sq_throwerror(v,_SC("io error"));
+ sq_pushinteger(v,size);
+ return 1;
+}
+
+SQInteger _stream_writen(HSQUIRRELVM v)
+{
+ SETUP_STREAM(v);
+ SQInteger format, ti;
+ SQFloat tf;
+ sq_getinteger(v, 3, &format);
+ switch(format) {
+ case 'l': {
+ SQInteger i;
+ sq_getinteger(v, 2, &ti);
+ i = ti;
+ self->Write(&i, sizeof(SQInteger));
+ }
+ break;
+ case 'i': {
+ SQInt32 i;
+ sq_getinteger(v, 2, &ti);
+ i = (SQInt32)ti;
+ self->Write(&i, sizeof(SQInt32));
+ }
+ break;
+ case 's': {
+ short s;
+ sq_getinteger(v, 2, &ti);
+ s = (short)ti;
+ self->Write(&s, sizeof(short));
+ }
+ break;
+ case 'w': {
+ unsigned short w;
+ sq_getinteger(v, 2, &ti);
+ w = (unsigned short)ti;
+ self->Write(&w, sizeof(unsigned short));
+ }
+ break;
+ case 'c': {
+ char c;
+ sq_getinteger(v, 2, &ti);
+ c = (char)ti;
+ self->Write(&c, sizeof(char));
+ }
+ break;
+ case 'b': {
+ unsigned char b;
+ sq_getinteger(v, 2, &ti);
+ b = (unsigned char)ti;
+ self->Write(&b, sizeof(unsigned char));
+ }
+ break;
+ case 'f': {
+ float f;
+ sq_getfloat(v, 2, &tf);
+ f = (float)tf;
+ self->Write(&f, sizeof(float));
+ }
+ break;
+ case 'd': {
+ double d;
+ sq_getfloat(v, 2, &tf);
+ d = tf;
+ self->Write(&d, sizeof(double));
+ }
+ break;
+ default:
+ return sq_throwerror(v, _SC("invalid format"));
+ }
+ return 0;
+}
+
+SQInteger _stream_seek(HSQUIRRELVM v)
+{
+ SETUP_STREAM(v);
+ SQInteger offset, origin = SQ_SEEK_SET;
+ sq_getinteger(v, 2, &offset);
+ if(sq_gettop(v) > 2) {
+ SQInteger t;
+ sq_getinteger(v, 3, &t);
+ switch(t) {
+ case 'b': origin = SQ_SEEK_SET; break;
+ case 'c': origin = SQ_SEEK_CUR; break;
+ case 'e': origin = SQ_SEEK_END; break;
+ default: return sq_throwerror(v,_SC("invalid origin"));
+ }
+ }
+ sq_pushinteger(v, self->Seek(offset, origin));
+ return 1;
+}
+
+SQInteger _stream_tell(HSQUIRRELVM v)
+{
+ SETUP_STREAM(v);
+ sq_pushinteger(v, self->Tell());
+ return 1;
+}
+
+SQInteger _stream_len(HSQUIRRELVM v)
+{
+ SETUP_STREAM(v);
+ sq_pushinteger(v, self->Len());
+ return 1;
+}
+
+SQInteger _stream_flush(HSQUIRRELVM v)
+{
+ SETUP_STREAM(v);
+ if(!self->Flush())
+ sq_pushinteger(v, 1);
+ else
+ sq_pushnull(v);
+ return 1;
+}
+
+SQInteger _stream_eos(HSQUIRRELVM v)
+{
+ SETUP_STREAM(v);
+ if(self->EOS())
+ sq_pushinteger(v, 1);
+ else
+ sq_pushnull(v);
+ return 1;
+}
+
+static SQRegFunction _stream_methods[] = {
+ _DECL_STREAM_FUNC(readblob,2,_SC("xn")),
+ _DECL_STREAM_FUNC(readn,2,_SC("xn")),
+ _DECL_STREAM_FUNC(writeblob,-2,_SC("xx")),
+ _DECL_STREAM_FUNC(writen,3,_SC("xnn")),
+ _DECL_STREAM_FUNC(seek,-2,_SC("xnn")),
+ _DECL_STREAM_FUNC(tell,1,_SC("x")),
+ _DECL_STREAM_FUNC(len,1,_SC("x")),
+ _DECL_STREAM_FUNC(eos,1,_SC("x")),
+ _DECL_STREAM_FUNC(flush,1,_SC("x")),
+ {0,0}
+};
+
+void init_streamclass(HSQUIRRELVM v)
+{
+ sq_pushregistrytable(v);
+ sq_pushstring(v,_SC("std_stream"),-1);
+ if(SQ_FAILED(sq_get(v,-2))) {
+ sq_pushstring(v,_SC("std_stream"),-1);
+ sq_newclass(v,SQFalse);
+ sq_settypetag(v,-1,(SQUserPointer)SQSTD_STREAM_TYPE_TAG);
+ SQInteger i = 0;
+ while(_stream_methods[i].name != 0) {
+ SQRegFunction &f = _stream_methods[i];
+ sq_pushstring(v,f.name,-1);
+ sq_newclosure(v,f.f,0);
+ sq_setparamscheck(v,f.nparamscheck,f.typemask);
+ sq_createslot(v,-3);
+ i++;
+ }
+ sq_createslot(v,-3);
+ sq_pushroottable(v);
+ sq_pushstring(v,_SC("stream"),-1);
+ sq_pushstring(v,_SC("std_stream"),-1);
+ sq_get(v,-4);
+ sq_createslot(v,-3);
+ sq_pop(v,1);
+ }
+ else {
+ sq_pop(v,1); //result
+ }
+ sq_pop(v,1);
+}
+
+SQRESULT declare_stream(HSQUIRRELVM v,SQChar* name,SQUserPointer typetag,SQChar* reg_name,SQRegFunction *methods,SQRegFunction *globals)
+{
+ if(sq_gettype(v,-1) != OT_TABLE)
+ return sq_throwerror(v,_SC("table expected"));
+ SQInteger top = sq_gettop(v);
+ //create delegate
+ init_streamclass(v);
+ sq_pushregistrytable(v);
+ sq_pushstring(v,reg_name,-1);
+ sq_pushstring(v,_SC("std_stream"),-1);
+ if(SQ_SUCCEEDED(sq_get(v,-3))) {
+ sq_newclass(v,SQTrue);
+ sq_settypetag(v,-1,typetag);
+ SQInteger i = 0;
+ while(methods[i].name != 0) {
+ SQRegFunction &f = methods[i];
+ sq_pushstring(v,f.name,-1);
+ sq_newclosure(v,f.f,0);
+ sq_setparamscheck(v,f.nparamscheck,f.typemask);
+ sq_setnativeclosurename(v,-1,f.name);
+ sq_createslot(v,-3);
+ i++;
+ }
+ sq_createslot(v,-3);
+ sq_pop(v,1);
+
+ i = 0;
+ while(globals[i].name!=0)
+ {
+ SQRegFunction &f = globals[i];
+ sq_pushstring(v,f.name,-1);
+ sq_newclosure(v,f.f,0);
+ sq_setparamscheck(v,f.nparamscheck,f.typemask);
+ sq_setnativeclosurename(v,-1,f.name);
+ sq_createslot(v,-3);
+ i++;
+ }
+ //register the class in the target table
+ sq_pushstring(v,name,-1);
+ sq_pushregistrytable(v);
+ sq_pushstring(v,reg_name,-1);
+ sq_get(v,-2);
+ sq_remove(v,-2);
+ sq_createslot(v,-3);
+
+ sq_settop(v,top);
+ return SQ_OK;
+ }
+ sq_settop(v,top);
+ return SQ_ERROR;
+}
--- /dev/null
+/* see copyright notice in squirrel.h */
+#ifndef _SQSTD_STREAM_H_
+#define _SQSTD_STREAM_H_
+
+SQInteger _stream_readblob(HSQUIRRELVM v);
+SQInteger _stream_readline(HSQUIRRELVM v);
+SQInteger _stream_readn(HSQUIRRELVM v);
+SQInteger _stream_writeblob(HSQUIRRELVM v);
+SQInteger _stream_writen(HSQUIRRELVM v);
+SQInteger _stream_seek(HSQUIRRELVM v);
+SQInteger _stream_tell(HSQUIRRELVM v);
+SQInteger _stream_len(HSQUIRRELVM v);
+SQInteger _stream_eos(HSQUIRRELVM v);
+SQInteger _stream_flush(HSQUIRRELVM v);
+
+#define _DECL_STREAM_FUNC(name,nparams,typecheck) {_SC(#name),_stream_##name,nparams,typecheck}
+SQRESULT declare_stream(HSQUIRRELVM v,SQChar* name,SQUserPointer typetag,SQChar* reg_name,SQRegFunction *methods,SQRegFunction *globals);
+#endif /*_SQSTD_STREAM_H_*/
--- /dev/null
+/* see copyright notice in squirrel.h */
+#include <squirrel.h>
+#include <sqstdstring.h>
+#include <string.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <ctype.h>
+#include <assert.h>
+
+#ifdef _UNICODE
+#define scstrchr wcschr
+#define scsnprintf wsnprintf
+#define scatoi _wtoi
+#define scstrtok wcstok
+#else
+#define scstrchr strchr
+#define scsnprintf snprintf
+#define scatoi atoi
+#define scstrtok strtok
+#endif
+#define MAX_FORMAT_LEN 20
+#define MAX_WFORMAT_LEN 3
+#define ADDITIONAL_FORMAT_SPACE (100*sizeof(SQChar))
+
+static SQInteger validate_format(HSQUIRRELVM v, SQChar *fmt, const SQChar *src, SQInteger n,SQInteger &width)
+{
+ SQChar swidth[MAX_WFORMAT_LEN];
+ SQInteger wc = 0;
+ SQInteger start = n;
+ fmt[0] = '%';
+ while (scstrchr(_SC("-+ #0"), src[n])) n++;
+ while (scisdigit(src[n])) {
+ swidth[wc] = src[n];
+ n++;
+ wc++;
+ if(wc>=MAX_WFORMAT_LEN)
+ return sq_throwerror(v,_SC("width format too long"));
+ }
+ swidth[wc] = '\0';
+ if(wc > 0) {
+ width = scatoi(swidth);
+ }
+ else
+ width = 0;
+ if (src[n] == '.') {
+ n++;
+
+ wc = 0;
+ while (scisdigit(src[n])) {
+ swidth[wc] = src[n];
+ n++;
+ wc++;
+ if(wc>=MAX_WFORMAT_LEN)
+ return sq_throwerror(v,_SC("precision format too long"));
+ }
+ swidth[wc] = '\0';
+ if(wc > 0) {
+ width += scatoi(swidth);
+ }
+ }
+ if (n-start > MAX_FORMAT_LEN )
+ return sq_throwerror(v,_SC("format too long"));
+ memcpy(&fmt[1],&src[start],((n-start)+1)*sizeof(SQChar));
+ fmt[(n-start)+2] = '\0';
+ return n;
+}
+
+static SQInteger _string_format(HSQUIRRELVM v)
+{
+ const SQChar *format;
+ SQChar *dest;
+ SQChar fmt[MAX_FORMAT_LEN];
+ sq_getstring(v,2,&format);
+ SQInteger allocated = (sq_getsize(v,2)+1)*sizeof(SQChar);
+ dest = sq_getscratchpad(v,allocated);
+ SQInteger n = 0,i = 0, nparam = 3, w = 0;
+ while(format[n] != '\0') {
+ if(format[n] != '%') {
+ assert(i < allocated);
+ dest[i++] = format[n];
+ n++;
+ }
+ else if(format[n+1] == '%') { //handles %%
+ dest[i++] = '%';
+ n += 2;
+ }
+ else {
+ n++;
+ if( nparam > sq_gettop(v) )
+ return sq_throwerror(v,_SC("not enough paramters for the given format string"));
+ n = validate_format(v,fmt,format,n,w);
+ if(n < 0) return -1;
+ SQInteger addlen = 0;
+ SQInteger valtype = 0;
+ const SQChar *ts;
+ SQInteger ti;
+ SQFloat tf;
+ switch(format[n]) {
+ case 's':
+ if(SQ_FAILED(sq_getstring(v,nparam,&ts)))
+ return sq_throwerror(v,_SC("string expected for the specified format"));
+ addlen = (sq_getsize(v,nparam)*sizeof(SQChar))+((w+1)*sizeof(SQChar));
+ valtype = 's';
+ break;
+ case 'i': case 'd': case 'c':case 'o': case 'u': case 'x': case 'X':
+ if(SQ_FAILED(sq_getinteger(v,nparam,&ti)))
+ return sq_throwerror(v,_SC("integer expected for the specified format"));
+ addlen = (ADDITIONAL_FORMAT_SPACE)+((w+1)*sizeof(SQChar));
+ valtype = 'i';
+ break;
+ case 'f': case 'g': case 'G': case 'e': case 'E':
+ if(SQ_FAILED(sq_getfloat(v,nparam,&tf)))
+ return sq_throwerror(v,_SC("float expected for the specified format"));
+ addlen = (ADDITIONAL_FORMAT_SPACE)+((w+1)*sizeof(SQChar));
+ valtype = 'f';
+ break;
+ default:
+ return sq_throwerror(v,_SC("invalid format"));
+ }
+ n++;
+ allocated += addlen;
+ dest = sq_getscratchpad(v,allocated);
+ switch(valtype) {
+ case 's': i += scsprintf(&dest[i],fmt,ts); break;
+ case 'i': i += scsprintf(&dest[i],fmt,ti); break;
+ case 'f': i += scsprintf(&dest[i],fmt,tf); break;
+ };
+ nparam ++;
+ }
+ }
+ sq_pushstring(v,dest,i);
+ return 1;
+}
+
+static void __strip_l(const SQChar *str,const SQChar **start)
+{
+ const SQChar *t = str;
+ while(((*t) != '\0') && scisspace(*t)){ t++; }
+ *start = t;
+}
+
+static void __strip_r(const SQChar *str,SQInteger len,const SQChar **end)
+{
+ if(len == 0) {
+ *end = str;
+ return;
+ }
+ const SQChar *t = &str[len-1];
+ while(t != str && scisspace(*t)) { t--; }
+ *end = t+1;
+}
+
+static SQInteger _string_strip(HSQUIRRELVM v)
+{
+ const SQChar *str,*start,*end;
+ sq_getstring(v,2,&str);
+ SQInteger len = sq_getsize(v,2);
+ __strip_l(str,&start);
+ __strip_r(str,len,&end);
+ sq_pushstring(v,start,end - start);
+ return 1;
+}
+
+static SQInteger _string_lstrip(HSQUIRRELVM v)
+{
+ const SQChar *str,*start;
+ sq_getstring(v,2,&str);
+ __strip_l(str,&start);
+ sq_pushstring(v,start,-1);
+ return 1;
+}
+
+static SQInteger _string_rstrip(HSQUIRRELVM v)
+{
+ const SQChar *str,*end;
+ sq_getstring(v,2,&str);
+ SQInteger len = sq_getsize(v,2);
+ __strip_r(str,len,&end);
+ sq_pushstring(v,str,end - str);
+ return 1;
+}
+
+static SQInteger _string_split(HSQUIRRELVM v)
+{
+ const SQChar *str,*seps;
+ SQChar *stemp,*tok;
+ sq_getstring(v,2,&str);
+ sq_getstring(v,3,&seps);
+ if(sq_getsize(v,3) == 0) return sq_throwerror(v,_SC("empty separators string"));
+ SQInteger memsize = (sq_getsize(v,2)+1)*sizeof(SQChar);
+ stemp = sq_getscratchpad(v,memsize);
+ memcpy(stemp,str,memsize);
+ tok = scstrtok(stemp,seps);
+ sq_newarray(v,0);
+ while( tok != NULL ) {
+ sq_pushstring(v,tok,-1);
+ sq_arrayappend(v,-2);
+ tok = scstrtok( NULL, seps );
+ }
+ return 1;
+}
+
+#define SETUP_REX(v) \
+ SQRex *self = NULL; \
+ sq_getinstanceup(v,1,(SQUserPointer *)&self,0);
+
+static SQInteger _rexobj_releasehook(SQUserPointer p, SQInteger size)
+{
+ SQRex *self = ((SQRex *)p);
+ sqstd_rex_free(self);
+ return 1;
+}
+
+static SQInteger _regexp_match(HSQUIRRELVM v)
+{
+ SETUP_REX(v);
+ const SQChar *str;
+ sq_getstring(v,2,&str);
+ if(sqstd_rex_match(self,str) == SQTrue)
+ {
+ sq_pushbool(v,SQTrue);
+ return 1;
+ }
+ sq_pushbool(v,SQFalse);
+ return 1;
+}
+
+static void _addrexmatch(HSQUIRRELVM v,const SQChar *str,const SQChar *begin,const SQChar *end)
+{
+ sq_newtable(v);
+ sq_pushstring(v,_SC("begin"),-1);
+ sq_pushinteger(v,begin - str);
+ sq_rawset(v,-3);
+ sq_pushstring(v,_SC("end"),-1);
+ sq_pushinteger(v,end - str);
+ sq_rawset(v,-3);
+}
+
+static SQInteger _regexp_search(HSQUIRRELVM v)
+{
+ SETUP_REX(v);
+ const SQChar *str,*begin,*end;
+ SQInteger start = 0;
+ sq_getstring(v,2,&str);
+ if(sq_gettop(v) > 2) sq_getinteger(v,3,&start);
+ if(sqstd_rex_search(self,str+start,&begin,&end) == SQTrue) {
+ _addrexmatch(v,str,begin,end);
+ return 1;
+ }
+ return 0;
+}
+
+static SQInteger _regexp_capture(HSQUIRRELVM v)
+{
+ SETUP_REX(v);
+ const SQChar *str,*begin,*end;
+ SQInteger start = 0;
+ sq_getstring(v,2,&str);
+ if(sq_gettop(v) > 2) sq_getinteger(v,3,&start);
+ if(sqstd_rex_search(self,str+start,&begin,&end) == SQTrue) {
+ SQInteger n = sqstd_rex_getsubexpcount(self);
+ SQRexMatch match;
+ sq_newarray(v,0);
+ for(SQInteger i = 0;i < n; i++) {
+ sqstd_rex_getsubexp(self,i,&match);
+ if(match.len > 0)
+ _addrexmatch(v,str,match.begin,match.begin+match.len);
+ else
+ _addrexmatch(v,str,str,str); //empty match
+ sq_arrayappend(v,-2);
+ }
+ return 1;
+ }
+ return 0;
+}
+
+static SQInteger _regexp_subexpcount(HSQUIRRELVM v)
+{
+ SETUP_REX(v);
+ sq_pushinteger(v,sqstd_rex_getsubexpcount(self));
+ return 1;
+}
+
+static SQInteger _regexp_constructor(HSQUIRRELVM v)
+{
+ const SQChar *error,*pattern;
+ sq_getstring(v,2,&pattern);
+ SQRex *rex = sqstd_rex_compile(pattern,&error);
+ if(!rex) return sq_throwerror(v,error);
+ sq_setinstanceup(v,1,rex);
+ sq_setreleasehook(v,1,_rexobj_releasehook);
+ return 0;
+}
+
+static SQInteger _regexp__typeof(HSQUIRRELVM v)
+{
+ sq_pushstring(v,_SC("regexp"),-1);
+ return 1;
+}
+
+#define _DECL_REX_FUNC(name,nparams,pmask) {_SC(#name),_regexp_##name,nparams,pmask}
+static SQRegFunction rexobj_funcs[]={
+ _DECL_REX_FUNC(constructor,2,_SC(".s")),
+ _DECL_REX_FUNC(search,-2,_SC("xsn")),
+ _DECL_REX_FUNC(match,2,_SC("xs")),
+ _DECL_REX_FUNC(capture,-2,_SC("xsn")),
+ _DECL_REX_FUNC(subexpcount,1,_SC("x")),
+ _DECL_REX_FUNC(_typeof,1,_SC("x")),
+ {0,0}
+};
+
+#define _DECL_FUNC(name,nparams,pmask) {_SC(#name),_string_##name,nparams,pmask}
+static SQRegFunction stringlib_funcs[]={
+ _DECL_FUNC(format,-2,_SC(".s")),
+ _DECL_FUNC(strip,2,_SC(".s")),
+ _DECL_FUNC(lstrip,2,_SC(".s")),
+ _DECL_FUNC(rstrip,2,_SC(".s")),
+ _DECL_FUNC(split,3,_SC(".ss")),
+ {0,0}
+};
+
+
+SQInteger sqstd_register_stringlib(HSQUIRRELVM v)
+{
+ sq_pushstring(v,_SC("regexp"),-1);
+ sq_newclass(v,SQFalse);
+ SQInteger i = 0;
+ while(rexobj_funcs[i].name != 0) {
+ SQRegFunction &f = rexobj_funcs[i];
+ sq_pushstring(v,f.name,-1);
+ sq_newclosure(v,f.f,0);
+ sq_setparamscheck(v,f.nparamscheck,f.typemask);
+ sq_setnativeclosurename(v,-1,f.name);
+ sq_createslot(v,-3);
+ i++;
+ }
+ sq_createslot(v,-3);
+
+ i = 0;
+ while(stringlib_funcs[i].name!=0)
+ {
+ sq_pushstring(v,stringlib_funcs[i].name,-1);
+ sq_newclosure(v,stringlib_funcs[i].f,0);
+ sq_setparamscheck(v,stringlib_funcs[i].nparamscheck,stringlib_funcs[i].typemask);
+ sq_setnativeclosurename(v,-1,stringlib_funcs[i].name);
+ sq_createslot(v,-3);
+ i++;
+ }
+ return 1;
+}
--- /dev/null
+/* see copyright notice in squirrel.h */
+#include <squirrel.h>
+#include <time.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <sqstdsystem.h>
+
+#ifdef SQUNICODE
+#include <wchar.h>
+#define scgetenv _wgetenv
+#define scsystem _wsystem
+#define scasctime _wasctime
+#define scremove _wremove
+#define screname _wrename
+#else
+#define scgetenv getenv
+#define scsystem system
+#define scasctime asctime
+#define scremove remove
+#define screname rename
+#endif
+
+static SQInteger _system_getenv(HSQUIRRELVM v)
+{
+ const SQChar *s;
+ if(SQ_SUCCEEDED(sq_getstring(v,2,&s))){
+ sq_pushstring(v,scgetenv(s),-1);
+ return 1;
+ }
+ return 0;
+}
+
+
+static SQInteger _system_system(HSQUIRRELVM v)
+{
+ const SQChar *s;
+ if(SQ_SUCCEEDED(sq_getstring(v,2,&s))){
+ sq_pushinteger(v,scsystem(s));
+ return 1;
+ }
+ return sq_throwerror(v,_SC("wrong param"));
+}
+
+
+static SQInteger _system_clock(HSQUIRRELVM v)
+{
+ sq_pushfloat(v,((SQFloat)clock())/(SQFloat)CLOCKS_PER_SEC);
+ return 1;
+}
+
+static SQInteger _system_time(HSQUIRRELVM v)
+{
+ time_t t;
+ time(&t);
+ sq_pushinteger(v,*((SQInteger *)&t));
+ return 1;
+}
+
+static SQInteger _system_remove(HSQUIRRELVM v)
+{
+ const SQChar *s;
+ sq_getstring(v,2,&s);
+ if(scremove(s)==-1)
+ return sq_throwerror(v,_SC("remove() failed"));
+ return 0;
+}
+
+static SQInteger _system_rename(HSQUIRRELVM v)
+{
+ const SQChar *oldn,*newn;
+ sq_getstring(v,2,&oldn);
+ sq_getstring(v,3,&newn);
+ if(screname(oldn,newn)==-1)
+ return sq_throwerror(v,_SC("rename() failed"));
+ return 0;
+}
+
+static void _set_integer_slot(HSQUIRRELVM v,const SQChar *name,SQInteger val)
+{
+ sq_pushstring(v,name,-1);
+ sq_pushinteger(v,val);
+ sq_rawset(v,-3);
+}
+
+static SQInteger _system_date(HSQUIRRELVM v)
+{
+ time_t t;
+ SQInteger it;
+ SQInteger format = 'l';
+ if(sq_gettop(v) > 1) {
+ sq_getinteger(v,2,&it);
+ t = it;
+ if(sq_gettop(v) > 2) {
+ sq_getinteger(v,3,(SQInteger*)&format);
+ }
+ }
+ else {
+ time(&t);
+ }
+ tm *date;
+ if(format == 'u')
+ date = gmtime(&t);
+ else
+ date = localtime(&t);
+ if(!date)
+ return sq_throwerror(v,_SC("crt api failure"));
+ sq_newtable(v);
+ _set_integer_slot(v, _SC("sec"), date->tm_sec);
+ _set_integer_slot(v, _SC("min"), date->tm_min);
+ _set_integer_slot(v, _SC("hour"), date->tm_hour);
+ _set_integer_slot(v, _SC("day"), date->tm_mday);
+ _set_integer_slot(v, _SC("month"), date->tm_mon);
+ _set_integer_slot(v, _SC("year"), date->tm_year+1900);
+ _set_integer_slot(v, _SC("wday"), date->tm_wday);
+ _set_integer_slot(v, _SC("yday"), date->tm_yday);
+ return 1;
+}
+
+
+
+#define _DECL_FUNC(name,nparams,pmask) {_SC(#name),_system_##name,nparams,pmask}
+static SQRegFunction systemlib_funcs[]={
+ _DECL_FUNC(getenv,2,_SC(".s")),
+ _DECL_FUNC(system,2,_SC(".s")),
+ _DECL_FUNC(clock,1,NULL),
+ _DECL_FUNC(time,1,NULL),
+ _DECL_FUNC(date,-1,_SC(".nn")),
+ _DECL_FUNC(remove,2,_SC(".s")),
+ _DECL_FUNC(rename,3,_SC(".ss")),
+ {0,0}
+};
+
+
+SQInteger sqstd_register_systemlib(HSQUIRRELVM v)
+{
+ SQInteger i=0;
+ while(systemlib_funcs[i].name!=0)
+ {
+ sq_pushstring(v,systemlib_funcs[i].name,-1);
+ sq_newclosure(v,systemlib_funcs[i].f,0);
+ sq_setparamscheck(v,systemlib_funcs[i].nparamscheck,systemlib_funcs[i].typemask);
+ sq_setnativeclosurename(v,-1,systemlib_funcs[i].name);
+ sq_createslot(v,-3);
+ i++;
+ }
+ return 1;
+}
--- /dev/null
+/*
+ see copyright notice in squirrel.h
+*/
+#include "sqpcheader.h"
+#include "sqvm.h"
+#include "sqstring.h"
+#include "sqtable.h"
+#include "sqarray.h"
+#include "sqfuncproto.h"
+#include "sqclosure.h"
+#include "squserdata.h"
+#include "sqcompiler.h"
+#include "sqfuncstate.h"
+#include "sqclass.h"
+
+bool sq_aux_gettypedarg(HSQUIRRELVM v,SQInteger idx,SQObjectType type,SQObjectPtr **o)
+{
+ *o = &stack_get(v,idx);
+ if(type(**o) != type){
+ SQObjectPtr oval = v->PrintObjVal(**o);
+ v->Raise_Error(_SC("wrong argument type, expected '%s' got '%.50s'"),IdType2Name(type),_stringval(oval));
+ return false;
+ }
+ return true;
+}
+
+#define _GETSAFE_OBJ(v,idx,type,o) { if(!sq_aux_gettypedarg(v,idx,type,&o)) return SQ_ERROR; }
+
+#define sq_aux_paramscheck(v,count) \
+{ \
+ if(sq_gettop(v) < count){ v->Raise_Error(_SC("not enough params in the stack")); return SQ_ERROR; }\
+}
+
+SQInteger sq_aux_throwobject(HSQUIRRELVM v,SQObjectPtr &e)
+{
+ v->_lasterror = e;
+ return SQ_ERROR;
+}
+
+SQInteger sq_aux_invalidtype(HSQUIRRELVM v,SQObjectType type)
+{
+ scsprintf(_ss(v)->GetScratchPad(100), _SC("unexpected type %s"), IdType2Name(type));
+ return sq_throwerror(v, _ss(v)->GetScratchPad(-1));
+}
+
+HSQUIRRELVM sq_open(SQInteger initialstacksize)
+{
+ SQSharedState *ss;
+ SQVM *v;
+ sq_new(ss, SQSharedState);
+ ss->Init();
+ v = (SQVM *)SQ_MALLOC(sizeof(SQVM));
+ new (v) SQVM(ss);
+ ss->_root_vm = v;
+ if(v->Init(NULL, initialstacksize)) {
+ return v;
+ } else {
+ sq_delete(v, SQVM);
+ return NULL;
+ }
+ return v;
+}
+
+HSQUIRRELVM sq_newthread(HSQUIRRELVM friendvm, SQInteger initialstacksize)
+{
+ SQSharedState *ss;
+ SQVM *v;
+ ss=_ss(friendvm);
+
+ v= (SQVM *)SQ_MALLOC(sizeof(SQVM));
+ new (v) SQVM(ss);
+
+ if(v->Init(friendvm, initialstacksize)) {
+ friendvm->Push(v);
+ return v;
+ } else {
+ sq_delete(v, SQVM);
+ return NULL;
+ }
+}
+
+SQInteger sq_getvmstate(HSQUIRRELVM v)
+{
+ if(v->_suspended)
+ return SQ_VMSTATE_SUSPENDED;
+ else {
+ if(v->_callsstacksize != 0) return SQ_VMSTATE_RUNNING;
+ else return SQ_VMSTATE_IDLE;
+ }
+}
+
+void sq_seterrorhandler(HSQUIRRELVM v)
+{
+ SQObject o = stack_get(v, -1);
+ if(sq_isclosure(o) || sq_isnativeclosure(o) || sq_isnull(o)) {
+ v->_errorhandler = o;
+ v->Pop();
+ }
+}
+
+void sq_setdebughook(HSQUIRRELVM v)
+{
+ SQObject o = stack_get(v,-1);
+ if(sq_isclosure(o) || sq_isnativeclosure(o) || sq_isnull(o)) {
+ v->_debughook = o;
+ v->Pop();
+ }
+}
+
+void sq_close(HSQUIRRELVM v)
+{
+ SQSharedState *ss = _ss(v);
+ _thread(ss->_root_vm)->Finalize();
+ sq_delete(ss, SQSharedState);
+}
+
+SQRESULT sq_compile(HSQUIRRELVM v,SQLEXREADFUNC read,SQUserPointer p,const SQChar *sourcename,SQBool raiseerror)
+{
+ SQObjectPtr o;
+ if(Compile(v, read, p, sourcename, o, raiseerror?true:false, _ss(v)->_debuginfo)) {
+ v->Push(SQClosure::Create(_ss(v), _funcproto(o)));
+ return SQ_OK;
+ }
+ return SQ_ERROR;
+}
+
+void sq_enabledebuginfo(HSQUIRRELVM v, SQBool enable)
+{
+ _ss(v)->_debuginfo = enable?true:false;
+}
+
+void sq_notifyallexceptions(HSQUIRRELVM v, SQBool enable)
+{
+ _ss(v)->_notifyallexceptions = enable?true:false;
+}
+
+void sq_addref(HSQUIRRELVM v,HSQOBJECT *po)
+{
+ if(!ISREFCOUNTED(type(*po))) return;
+#ifdef NO_GARBAGE_COLLECTOR
+ __AddRef(po->_type,po->_unVal);
+#else
+ _ss(v)->_refs_table.AddRef(*po);
+#endif
+}
+
+SQBool sq_release(HSQUIRRELVM v,HSQOBJECT *po)
+{
+ if(!ISREFCOUNTED(type(*po))) return SQTrue;
+#ifdef NO_GARBAGE_COLLECTOR
+ __Release(po->_type,po->_unVal);
+ return SQFalse; //the ret val doesn't work(and cannot be fixed)
+#else
+ return _ss(v)->_refs_table.Release(*po);
+#endif
+}
+
+const SQChar *sq_objtostring(HSQOBJECT *o)
+{
+ if(sq_type(*o) == OT_STRING) {
+ return _stringval(*o);
+ }
+ return NULL;
+}
+
+SQInteger sq_objtointeger(HSQOBJECT *o)
+{
+ if(sq_isnumeric(*o)) {
+ return tointeger(*o);
+ }
+ return 0;
+}
+
+SQFloat sq_objtofloat(HSQOBJECT *o)
+{
+ if(sq_isnumeric(*o)) {
+ return tofloat(*o);
+ }
+ return 0;
+}
+
+SQBool sq_objtobool(HSQOBJECT *o)
+{
+ if(sq_isbool(*o)) {
+ return _integer(*o);
+ }
+ return SQFalse;
+}
+
+void sq_pushnull(HSQUIRRELVM v)
+{
+ v->Push(_null_);
+}
+
+void sq_pushstring(HSQUIRRELVM v,const SQChar *s,SQInteger len)
+{
+ if(s)
+ v->Push(SQObjectPtr(SQString::Create(_ss(v), s, len)));
+ else v->Push(_null_);
+}
+
+void sq_pushinteger(HSQUIRRELVM v,SQInteger n)
+{
+ v->Push(n);
+}
+
+void sq_pushbool(HSQUIRRELVM v,SQBool b)
+{
+ v->Push(b?true:false);
+}
+
+void sq_pushfloat(HSQUIRRELVM v,SQFloat n)
+{
+ v->Push(n);
+}
+
+void sq_pushuserpointer(HSQUIRRELVM v,SQUserPointer p)
+{
+ v->Push(p);
+}
+
+SQUserPointer sq_newuserdata(HSQUIRRELVM v,SQUnsignedInteger size)
+{
+ SQUserData *ud = SQUserData::Create(_ss(v), size);
+ v->Push(ud);
+ return ud->_val;
+}
+
+void sq_newtable(HSQUIRRELVM v)
+{
+ v->Push(SQTable::Create(_ss(v), 0));
+}
+
+void sq_newarray(HSQUIRRELVM v,SQInteger size)
+{
+ v->Push(SQArray::Create(_ss(v), size));
+}
+
+SQRESULT sq_newclass(HSQUIRRELVM v,SQBool hasbase)
+{
+ SQClass *baseclass = NULL;
+ if(hasbase) {
+ SQObjectPtr &base = stack_get(v,-1);
+ if(type(base) != OT_CLASS)
+ return sq_throwerror(v,_SC("invalid base type"));
+ baseclass = _class(base);
+ }
+ SQClass *newclass = SQClass::Create(_ss(v), baseclass);
+ if(baseclass) v->Pop();
+ v->Push(newclass);
+ return SQ_OK;
+}
+
+SQBool sq_instanceof(HSQUIRRELVM v)
+{
+ SQObjectPtr &inst = stack_get(v,-1);
+ SQObjectPtr &cl = stack_get(v,-2);
+ if(type(inst) != OT_INSTANCE || type(cl) != OT_CLASS)
+ return sq_throwerror(v,_SC("invalid param type"));
+ return _instance(inst)->InstanceOf(_class(cl))?SQTrue:SQFalse;
+}
+
+SQRESULT sq_arrayappend(HSQUIRRELVM v,SQInteger idx)
+{
+ sq_aux_paramscheck(v,2);
+ SQObjectPtr *arr;
+ _GETSAFE_OBJ(v, idx, OT_ARRAY,arr);
+ _array(*arr)->Append(v->GetUp(-1));
+ v->Pop(1);
+ return SQ_OK;
+}
+
+SQRESULT sq_arraypop(HSQUIRRELVM v,SQInteger idx,SQBool pushval)
+{
+ sq_aux_paramscheck(v, 1);
+ SQObjectPtr *arr;
+ _GETSAFE_OBJ(v, idx, OT_ARRAY,arr);
+ if(_array(*arr)->Size() > 0) {
+ if(pushval != 0){ v->Push(_array(*arr)->Top()); }
+ _array(*arr)->Pop();
+ return SQ_OK;
+ }
+ return sq_throwerror(v, _SC("empty array"));
+}
+
+SQRESULT sq_arrayresize(HSQUIRRELVM v,SQInteger idx,SQInteger newsize)
+{
+ sq_aux_paramscheck(v,1);
+ SQObjectPtr *arr;
+ _GETSAFE_OBJ(v, idx, OT_ARRAY,arr);
+ if(_array(*arr)->Size() > 0) {
+ _array(*arr)->Resize(newsize);
+ return SQ_OK;
+ }
+ return SQ_OK;
+}
+
+
+SQRESULT sq_arrayreverse(HSQUIRRELVM v,SQInteger idx)
+{
+ sq_aux_paramscheck(v, 1);
+ SQObjectPtr *o;
+ _GETSAFE_OBJ(v, idx, OT_ARRAY,o);
+ SQArray *arr = _array(*o);
+ if(arr->Size() > 0) {
+ SQObjectPtr t;
+ SQInteger size = arr->Size();
+ SQInteger n = size >> 1; size -= 1;
+ for(SQInteger i = 0; i < n; i++) {
+ t = arr->_values[i];
+ arr->_values[i] = arr->_values[size-i];
+ arr->_values[size-i] = t;
+ }
+ return SQ_OK;
+ }
+ return SQ_OK;
+}
+
+void sq_newclosure(HSQUIRRELVM v,SQFUNCTION func,SQUnsignedInteger nfreevars)
+{
+ SQNativeClosure *nc = SQNativeClosure::Create(_ss(v), func);
+ nc->_nparamscheck = 0;
+ for(SQUnsignedInteger i = 0; i < nfreevars; i++) {
+ nc->_outervalues.push_back(v->Top());
+ v->Pop();
+ }
+ v->Push(SQObjectPtr(nc));
+}
+
+SQRESULT sq_getclosureinfo(HSQUIRRELVM v,SQInteger idx,SQUnsignedInteger *nparams,SQUnsignedInteger *nfreevars)
+{
+ SQObject o = stack_get(v, idx);
+ if(sq_isclosure(o)) {
+ SQClosure *c = _closure(o);
+ SQFunctionProto *proto = _funcproto(c->_function);
+ *nparams = (SQUnsignedInteger)proto->_nparameters;
+ *nfreevars = (SQUnsignedInteger)c->_outervalues.size();
+ return SQ_OK;
+ }
+ return sq_throwerror(v,_SC("the object is not a closure"));
+}
+
+SQRESULT sq_setnativeclosurename(HSQUIRRELVM v,SQInteger idx,const SQChar *name)
+{
+ SQObject o = stack_get(v, idx);
+ if(sq_isnativeclosure(o)) {
+ SQNativeClosure *nc = _nativeclosure(o);
+ nc->_name = SQString::Create(_ss(v),name);
+ return SQ_OK;
+ }
+ return sq_throwerror(v,_SC("the object is not a nativeclosure"));
+}
+
+SQRESULT sq_setparamscheck(HSQUIRRELVM v,SQInteger nparamscheck,const SQChar *typemask)
+{
+ SQObject o = stack_get(v, -1);
+ if(!sq_isnativeclosure(o))
+ return sq_throwerror(v, _SC("native closure expected"));
+ SQNativeClosure *nc = _nativeclosure(o);
+ nc->_nparamscheck = nparamscheck;
+ if(typemask) {
+ SQIntVec res;
+ if(!CompileTypemask(res, typemask))
+ return sq_throwerror(v, _SC("invalid typemask"));
+ nc->_typecheck.copy(res);
+ }
+ else {
+ nc->_typecheck.resize(0);
+ }
+ if(nparamscheck == SQ_MATCHTYPEMASKSTRING) {
+ nc->_nparamscheck = nc->_typecheck.size();
+ }
+ return SQ_OK;
+}
+
+SQRESULT sq_bindenv(HSQUIRRELVM v,SQInteger idx)
+{
+ SQObjectPtr &o = stack_get(v,idx);
+ if(!sq_isnativeclosure(o) &&
+ !sq_isclosure(o))
+ return sq_throwerror(v,_SC("the target is not a closure"));
+ SQObjectPtr &env = stack_get(v,-1);
+ if(!sq_istable(env) &&
+ !sq_isclass(env) &&
+ !sq_isinstance(env))
+ return sq_throwerror(v,_SC("invalid environment"));
+ SQObjectPtr w = _refcounted(env)->GetWeakRef(type(env));
+ SQObjectPtr ret;
+ if(sq_isclosure(o)) {
+ SQClosure *c = _closure(o)->Clone();
+ c->_env = w;
+ ret = c;
+ }
+ else { //then must be a native closure
+ SQNativeClosure *c = _nativeclosure(o)->Clone();
+ c->_env = w;
+ ret = c;
+ }
+ v->Pop();
+ v->Push(ret);
+ return SQ_OK;
+}
+
+SQRESULT sq_clear(HSQUIRRELVM v,SQInteger idx)
+{
+ SQObject &o=stack_get(v,idx);
+ switch(type(o)) {
+ case OT_TABLE: _table(o)->Clear(); break;
+ case OT_ARRAY: _array(o)->Resize(0); break;
+ default:
+ return sq_throwerror(v, _SC("clear only works on table and array"));
+ break;
+
+ }
+ return SQ_OK;
+}
+
+void sq_pushroottable(HSQUIRRELVM v)
+{
+ v->Push(v->_roottable);
+}
+
+void sq_pushregistrytable(HSQUIRRELVM v)
+{
+ v->Push(_ss(v)->_registry);
+}
+
+SQRESULT sq_setroottable(HSQUIRRELVM v)
+{
+ SQObject o = stack_get(v, -1);
+ if(sq_istable(o) || sq_isnull(o)) {
+ v->_roottable = o;
+ v->Pop();
+ return SQ_OK;
+ }
+ return sq_throwerror(v, _SC("ivalid type"));
+}
+
+void sq_setforeignptr(HSQUIRRELVM v,SQUserPointer p)
+{
+ v->_foreignptr = p;
+}
+
+SQUserPointer sq_getforeignptr(HSQUIRRELVM v)
+{
+ return v->_foreignptr;
+}
+
+void sq_push(HSQUIRRELVM v,SQInteger idx)
+{
+ v->Push(stack_get(v, idx));
+}
+
+SQObjectType sq_gettype(HSQUIRRELVM v,SQInteger idx)
+{
+ return type(stack_get(v, idx));
+}
+
+
+void sq_tostring(HSQUIRRELVM v,SQInteger idx)
+{
+ SQObjectPtr &o = stack_get(v, idx);
+ SQObjectPtr res;
+ v->ToString(o,res);
+ v->Push(res);
+}
+
+void sq_tobool(HSQUIRRELVM v, SQInteger idx, SQBool *b)
+{
+ SQObjectPtr &o = stack_get(v, idx);
+ *b = v->IsFalse(o)?SQFalse:SQTrue;
+}
+
+SQRESULT sq_getinteger(HSQUIRRELVM v,SQInteger idx,SQInteger *i)
+{
+ SQObjectPtr &o = stack_get(v, idx);
+ if(sq_isnumeric(o)) {
+ *i = tointeger(o);
+ return SQ_OK;
+ }
+ return SQ_ERROR;
+}
+
+SQRESULT sq_getfloat(HSQUIRRELVM v,SQInteger idx,SQFloat *f)
+{
+ SQObjectPtr &o = stack_get(v, idx);
+ if(sq_isnumeric(o)) {
+ *f = tofloat(o);
+ return SQ_OK;
+ }
+ return SQ_ERROR;
+}
+
+SQRESULT sq_getbool(HSQUIRRELVM v,SQInteger idx,SQBool *b)
+{
+ SQObjectPtr &o = stack_get(v, idx);
+ if(sq_isbool(o)) {
+ *b = _integer(o);
+ return SQ_OK;
+ }
+ return SQ_ERROR;
+}
+
+SQRESULT sq_getstring(HSQUIRRELVM v,SQInteger idx,const SQChar **c)
+{
+ SQObjectPtr *o = NULL;
+ _GETSAFE_OBJ(v, idx, OT_STRING,o);
+ *c = _stringval(*o);
+ return SQ_OK;
+}
+
+SQRESULT sq_getthread(HSQUIRRELVM v,SQInteger idx,HSQUIRRELVM *thread)
+{
+ SQObjectPtr *o = NULL;
+ _GETSAFE_OBJ(v, idx, OT_THREAD,o);
+ *thread = _thread(*o);
+ return SQ_OK;
+}
+
+SQRESULT sq_clone(HSQUIRRELVM v,SQInteger idx)
+{
+ SQObjectPtr &o = stack_get(v,idx);
+ v->Push(_null_);
+ if(!v->Clone(o, stack_get(v, -1))){
+ v->Pop();
+ return sq_aux_invalidtype(v, type(o));
+ }
+ return SQ_OK;
+}
+
+SQInteger sq_getsize(HSQUIRRELVM v, SQInteger idx)
+{
+ SQObjectPtr &o = stack_get(v, idx);
+ SQObjectType type = type(o);
+ switch(type) {
+ case OT_STRING: return _string(o)->_len;
+ case OT_TABLE: return _table(o)->CountUsed();
+ case OT_ARRAY: return _array(o)->Size();
+ case OT_USERDATA: return _userdata(o)->_size;
+ default:
+ return sq_aux_invalidtype(v, type);
+ }
+}
+
+SQRESULT sq_getuserdata(HSQUIRRELVM v,SQInteger idx,SQUserPointer *p,SQUserPointer *typetag)
+{
+ SQObjectPtr *o = NULL;
+ _GETSAFE_OBJ(v, idx, OT_USERDATA,o);
+ (*p) = _userdataval(*o);
+ if(typetag) *typetag = _userdata(*o)->_typetag;
+ return SQ_OK;
+}
+
+SQRESULT sq_settypetag(HSQUIRRELVM v,SQInteger idx,SQUserPointer typetag)
+{
+ SQObjectPtr &o = stack_get(v,idx);
+ switch(type(o)) {
+ case OT_USERDATA: _userdata(o)->_typetag = typetag; break;
+ case OT_CLASS: _class(o)->_typetag = typetag; break;
+ default: return sq_throwerror(v,_SC("invalid object type"));
+ }
+ return SQ_OK;
+}
+
+SQRESULT sq_getobjtypetag(HSQOBJECT *o,SQUserPointer * typetag)
+{
+ switch(type(*o)) {
+ case OT_INSTANCE: *typetag = _instance(*o)->_class->_typetag; break;
+ case OT_USERDATA: *typetag = _userdata(*o)->_typetag; break;
+ case OT_CLASS: *typetag = _class(*o)->_typetag; break;
+ default: return SQ_ERROR;
+ }
+ return SQ_OK;
+}
+
+SQRESULT sq_gettypetag(HSQUIRRELVM v,SQInteger idx,SQUserPointer *typetag)
+{
+ SQObjectPtr &o = stack_get(v,idx);
+ if(SQ_FAILED(sq_getobjtypetag(&o,typetag)))
+ return sq_throwerror(v,_SC("invalid object type"));
+ return SQ_OK;
+}
+
+SQRESULT sq_getuserpointer(HSQUIRRELVM v, SQInteger idx, SQUserPointer *p)
+{
+ SQObjectPtr *o = NULL;
+ _GETSAFE_OBJ(v, idx, OT_USERPOINTER,o);
+ (*p) = _userpointer(*o);
+ return SQ_OK;
+}
+
+SQRESULT sq_setinstanceup(HSQUIRRELVM v, SQInteger idx, SQUserPointer p)
+{
+ SQObjectPtr &o = stack_get(v,idx);
+ if(type(o) != OT_INSTANCE) return sq_throwerror(v,_SC("the object is not a class instance"));
+ _instance(o)->_userpointer = p;
+ return SQ_OK;
+}
+
+SQRESULT sq_setclassudsize(HSQUIRRELVM v, SQInteger idx, SQInteger udsize)
+{
+ SQObjectPtr &o = stack_get(v,idx);
+ if(type(o) != OT_CLASS) return sq_throwerror(v,_SC("the object is not a class"));
+ if(_class(o)->_locked) return sq_throwerror(v,_SC("the class is locked"));
+ _class(o)->_udsize = udsize;
+ return SQ_OK;
+}
+
+
+SQRESULT sq_getinstanceup(HSQUIRRELVM v, SQInteger idx, SQUserPointer *p,SQUserPointer typetag)
+{
+ SQObjectPtr &o = stack_get(v,idx);
+ if(type(o) != OT_INSTANCE) return sq_throwerror(v,_SC("the object is not a class instance"));
+ (*p) = _instance(o)->_userpointer;
+ if(typetag != 0) {
+ SQClass *cl = _instance(o)->_class;
+ do{
+ if(cl->_typetag == typetag)
+ return SQ_OK;
+ cl = cl->_base;
+ }while(cl != NULL);
+ return sq_throwerror(v,_SC("invalid type tag"));
+ }
+ return SQ_OK;
+}
+
+SQInteger sq_gettop(HSQUIRRELVM v)
+{
+ return (v->_top) - v->_stackbase;
+}
+
+void sq_settop(HSQUIRRELVM v, SQInteger newtop)
+{
+ SQInteger top = sq_gettop(v);
+ if(top > newtop)
+ sq_pop(v, top - newtop);
+ else
+ while(top < newtop) sq_pushnull(v);
+}
+
+void sq_pop(HSQUIRRELVM v, SQInteger nelemstopop)
+{
+ assert(v->_top >= nelemstopop);
+ v->Pop(nelemstopop);
+}
+
+void sq_poptop(HSQUIRRELVM v)
+{
+ assert(v->_top >= 1);
+ v->Pop();
+}
+
+
+void sq_remove(HSQUIRRELVM v, SQInteger idx)
+{
+ v->Remove(idx);
+}
+
+SQInteger sq_cmp(HSQUIRRELVM v)
+{
+ SQInteger res;
+ v->ObjCmp(stack_get(v, -1), stack_get(v, -2),res);
+ return res;
+}
+
+SQRESULT sq_newslot(HSQUIRRELVM v, SQInteger idx, SQBool bstatic)
+{
+ sq_aux_paramscheck(v, 3);
+ SQObjectPtr &self = stack_get(v, idx);
+ if(type(self) == OT_TABLE || type(self) == OT_CLASS) {
+ SQObjectPtr &key = v->GetUp(-2);
+ if(type(key) == OT_NULL) return sq_throwerror(v, _SC("null is not a valid key"));
+ v->NewSlot(self, key, v->GetUp(-1),bstatic?true:false);
+ v->Pop(2);
+ }
+ return SQ_OK;
+}
+
+SQRESULT sq_deleteslot(HSQUIRRELVM v,SQInteger idx,SQBool pushval)
+{
+ sq_aux_paramscheck(v, 2);
+ SQObjectPtr *self;
+ _GETSAFE_OBJ(v, idx, OT_TABLE,self);
+ SQObjectPtr &key = v->GetUp(-1);
+ if(type(key) == OT_NULL) return sq_throwerror(v, _SC("null is not a valid key"));
+ SQObjectPtr res;
+ if(!v->DeleteSlot(*self, key, res)){
+ return SQ_ERROR;
+ }
+ if(pushval) v->GetUp(-1) = res;
+ else v->Pop(1);
+ return SQ_OK;
+}
+
+SQRESULT sq_set(HSQUIRRELVM v,SQInteger idx)
+{
+ SQObjectPtr &self = stack_get(v, idx);
+ if(v->Set(self, v->GetUp(-2), v->GetUp(-1),false)) {
+ v->Pop(2);
+ return SQ_OK;
+ }
+ v->Raise_IdxError(v->GetUp(-2));return SQ_ERROR;
+}
+
+SQRESULT sq_rawset(HSQUIRRELVM v,SQInteger idx)
+{
+ SQObjectPtr &self = stack_get(v, idx);
+ if(type(v->GetUp(-2)) == OT_NULL) return sq_throwerror(v, _SC("null key"));
+ switch(type(self)) {
+ case OT_TABLE:
+ _table(self)->NewSlot(v->GetUp(-2), v->GetUp(-1));
+ v->Pop(2);
+ return SQ_OK;
+ break;
+ case OT_CLASS:
+ _class(self)->NewSlot(_ss(v), v->GetUp(-2), v->GetUp(-1),false);
+ v->Pop(2);
+ return SQ_OK;
+ break;
+ case OT_INSTANCE:
+ if(_instance(self)->Set(v->GetUp(-2), v->GetUp(-1))) {
+ v->Pop(2);
+ return SQ_OK;
+ }
+ break;
+ case OT_ARRAY:
+ if(v->Set(self, v->GetUp(-2), v->GetUp(-1),false)) {
+ v->Pop(2);
+ return SQ_OK;
+ }
+ break;
+ default:
+ v->Pop(2);
+ return sq_throwerror(v, _SC("rawset works only on array/table/class and instance"));
+ }
+ v->Raise_IdxError(v->GetUp(-2));return SQ_ERROR;
+}
+
+SQRESULT sq_setdelegate(HSQUIRRELVM v,SQInteger idx)
+{
+ SQObjectPtr &self = stack_get(v, idx);
+ SQObjectPtr &mt = v->GetUp(-1);
+ SQObjectType type = type(self);
+ switch(type) {
+ case OT_TABLE:
+ if(type(mt) == OT_TABLE) {
+ if(!_table(self)->SetDelegate(_table(mt))) return sq_throwerror(v, _SC("delagate cycle")); v->Pop();}
+ else if(type(mt)==OT_NULL) {
+ _table(self)->SetDelegate(NULL); v->Pop(); }
+ else return sq_aux_invalidtype(v,type);
+ break;
+ case OT_USERDATA:
+ if(type(mt)==OT_TABLE) {
+ _userdata(self)->SetDelegate(_table(mt)); v->Pop(); }
+ else if(type(mt)==OT_NULL) {
+ _userdata(self)->SetDelegate(NULL); v->Pop(); }
+ else return sq_aux_invalidtype(v, type);
+ break;
+ default:
+ return sq_aux_invalidtype(v, type);
+ break;
+ }
+ return SQ_OK;
+}
+
+SQRESULT sq_rawdeleteslot(HSQUIRRELVM v,SQInteger idx,SQBool pushval)
+{
+ sq_aux_paramscheck(v, 2);
+ SQObjectPtr *self;
+ _GETSAFE_OBJ(v, idx, OT_TABLE,self);
+ SQObjectPtr &key = v->GetUp(-1);
+ SQObjectPtr t;
+ if(_table(*self)->Get(key,t)) {
+ _table(*self)->Remove(key);
+ }
+ if(pushval != 0)
+ if(pushval) v->GetUp(-1) = t;
+ else
+ v->Pop(1);
+ return SQ_OK;
+}
+
+SQRESULT sq_getdelegate(HSQUIRRELVM v,SQInteger idx)
+{
+ SQObjectPtr &self=stack_get(v,idx);
+ switch(type(self)){
+ case OT_TABLE:
+ case OT_USERDATA:
+ if(!_delegable(self)->_delegate){
+ v->Push(_null_);
+ break;
+ }
+ v->Push(SQObjectPtr(_delegable(self)->_delegate));
+ break;
+ default: return sq_throwerror(v,_SC("wrong type")); break;
+ }
+ return SQ_OK;
+
+}
+
+SQRESULT sq_get(HSQUIRRELVM v,SQInteger idx)
+{
+ SQObjectPtr &self=stack_get(v,idx);
+ if(v->Get(self,v->GetUp(-1),v->GetUp(-1),false,false))
+ return SQ_OK;
+ v->Pop(1);
+ return sq_throwerror(v,_SC("the index doesn't exist"));
+}
+
+SQRESULT sq_rawget(HSQUIRRELVM v,SQInteger idx)
+{
+ SQObjectPtr &self=stack_get(v,idx);
+ switch(type(self)) {
+ case OT_TABLE:
+ if(_table(self)->Get(v->GetUp(-1),v->GetUp(-1)))
+ return SQ_OK;
+ break;
+ case OT_CLASS:
+ if(_class(self)->Get(v->GetUp(-1),v->GetUp(-1)))
+ return SQ_OK;
+ break;
+ case OT_INSTANCE:
+ if(_instance(self)->Get(v->GetUp(-1),v->GetUp(-1)))
+ return SQ_OK;
+ break;
+ case OT_ARRAY:
+ if(v->Get(self,v->GetUp(-1),v->GetUp(-1),false,false))
+ return SQ_OK;
+ break;
+ default:
+ v->Pop(1);
+ return sq_throwerror(v,_SC("rawget works only on array/table/instance and class"));
+ }
+ v->Pop(1);
+ return sq_throwerror(v,_SC("the index doesn't exist"));
+}
+
+SQRESULT sq_getstackobj(HSQUIRRELVM v,SQInteger idx,HSQOBJECT *po)
+{
+ *po=stack_get(v,idx);
+ return SQ_OK;
+}
+
+const SQChar *sq_getlocal(HSQUIRRELVM v,SQUnsignedInteger level,SQUnsignedInteger idx)
+{
+ SQUnsignedInteger cstksize=v->_callsstacksize;
+ SQUnsignedInteger lvl=(cstksize-level)-1;
+ SQInteger stackbase=v->_stackbase;
+ if(lvl<cstksize){
+ for(SQUnsignedInteger i=0;i<level;i++){
+ SQVM::CallInfo &ci=v->_callsstack[(cstksize-i)-1];
+ stackbase-=ci._prevstkbase;
+ }
+ SQVM::CallInfo &ci=v->_callsstack[lvl];
+ if(type(ci._closure)!=OT_CLOSURE)
+ return NULL;
+ SQClosure *c=_closure(ci._closure);
+ SQFunctionProto *func=_funcproto(c->_function);
+ if(func->_noutervalues > (SQInteger)idx) {
+ v->Push(c->_outervalues[idx]);
+ return _stringval(func->_outervalues[idx]._name);
+ }
+ idx -= func->_noutervalues;
+ return func->GetLocal(v,stackbase,idx,(SQInteger)(ci._ip-func->_instructions)-1);
+ }
+ return NULL;
+}
+
+void sq_pushobject(HSQUIRRELVM v,HSQOBJECT obj)
+{
+ v->Push(SQObjectPtr(obj));
+}
+
+void sq_resetobject(HSQOBJECT *po)
+{
+ po->_unVal.pUserPointer=NULL;po->_type=OT_NULL;
+}
+
+SQRESULT sq_throwerror(HSQUIRRELVM v,const SQChar *err)
+{
+ v->_lasterror=SQString::Create(_ss(v),err);
+ return -1;
+}
+
+void sq_reseterror(HSQUIRRELVM v)
+{
+ v->_lasterror = _null_;
+}
+
+void sq_getlasterror(HSQUIRRELVM v)
+{
+ v->Push(v->_lasterror);
+}
+
+void sq_reservestack(HSQUIRRELVM v,SQInteger nsize)
+{
+ if (((SQUnsignedInteger)v->_top + nsize) > v->_stack.size()) {
+ v->_stack.resize(v->_stack.size() + ((v->_top + nsize) - v->_stack.size()));
+ }
+}
+
+SQRESULT sq_resume(HSQUIRRELVM v,SQBool retval,SQBool raiseerror)
+{
+ if(type(v->GetUp(-1))==OT_GENERATOR){
+ v->Push(_null_); //retval
+ if(!v->Execute(v->GetUp(-2),v->_top,0,v->_top,v->GetUp(-1),raiseerror,SQVM::ET_RESUME_GENERATOR))
+ {v->Raise_Error(v->_lasterror); return SQ_ERROR;}
+ if(!retval)
+ v->Pop();
+ return SQ_OK;
+ }
+ return sq_throwerror(v,_SC("only generators can be resumed"));
+}
+
+SQRESULT sq_call(HSQUIRRELVM v,SQInteger params,SQBool retval,SQBool raiseerror)
+{
+ SQObjectPtr res;
+ if(v->Call(v->GetUp(-(params+1)),params,v->_top-params,res,raiseerror?true:false)){
+ v->Pop(params);//pop closure and args
+ if(retval){
+ v->Push(res); return SQ_OK;
+ }
+ return SQ_OK;
+ }
+ else {
+ v->Pop(params);
+ return SQ_ERROR;
+ }
+ if(!v->_suspended)
+ v->Pop(params);
+ return sq_throwerror(v,_SC("call failed"));
+}
+
+SQRESULT sq_suspendvm(HSQUIRRELVM v)
+{
+ return v->Suspend();
+}
+
+SQRESULT sq_wakeupvm(HSQUIRRELVM v,SQBool wakeupret,SQBool retval,SQBool raiseerror)
+{
+ SQObjectPtr ret;
+ if(!v->_suspended)
+ return sq_throwerror(v,_SC("cannot resume a vm that is not running any code"));
+ if(wakeupret) {
+ v->GetAt(v->_stackbase+v->_suspended_target)=v->GetUp(-1); //retval
+ v->Pop();
+ } else v->GetAt(v->_stackbase+v->_suspended_target)=_null_;
+ if(!v->Execute(_null_,v->_top,-1,-1,ret,raiseerror,SQVM::ET_RESUME_VM))
+ return SQ_ERROR;
+ if(sq_getvmstate(v) == SQ_VMSTATE_IDLE) {
+ while (v->_top > 1) v->_stack[--v->_top] = _null_;
+ }
+ if(retval)
+ v->Push(ret);
+ return SQ_OK;
+}
+
+void sq_setreleasehook(HSQUIRRELVM v,SQInteger idx,SQRELEASEHOOK hook)
+{
+ if(sq_gettop(v) >= 1){
+ SQObjectPtr &ud=stack_get(v,idx);
+ switch( type(ud) ) {
+ case OT_USERDATA: _userdata(ud)->_hook = hook; break;
+ case OT_INSTANCE: _instance(ud)->_hook = hook; break;
+ case OT_CLASS: _class(ud)->_hook = hook; break;
+ default: break; //shutup compiler
+ }
+ }
+}
+
+void sq_setcompilererrorhandler(HSQUIRRELVM v,SQCOMPILERERROR f)
+{
+ _ss(v)->_compilererrorhandler = f;
+}
+
+SQRESULT sq_writeclosure(HSQUIRRELVM v,SQWRITEFUNC w,SQUserPointer up)
+{
+ SQObjectPtr *o = NULL;
+ _GETSAFE_OBJ(v, -1, OT_CLOSURE,o);
+ unsigned short tag = SQ_BYTECODE_STREAM_TAG;
+ if(w(up,&tag,2) != 2)
+ return sq_throwerror(v,_SC("io error"));
+ if(!_closure(*o)->Save(v,up,w))
+ return SQ_ERROR;
+ return SQ_OK;
+}
+
+SQRESULT sq_readclosure(HSQUIRRELVM v,SQREADFUNC r,SQUserPointer up)
+{
+ SQObjectPtr closure;
+
+ unsigned short tag;
+ if(r(up,&tag,2) != 2)
+ return sq_throwerror(v,_SC("io error"));
+ if(tag != SQ_BYTECODE_STREAM_TAG)
+ return sq_throwerror(v,_SC("invalid stream"));
+ if(!SQClosure::Load(v,up,r,closure))
+ return SQ_ERROR;
+ v->Push(closure);
+ return SQ_OK;
+}
+
+SQChar *sq_getscratchpad(HSQUIRRELVM v,SQInteger minsize)
+{
+ return _ss(v)->GetScratchPad(minsize);
+}
+
+SQInteger sq_collectgarbage(HSQUIRRELVM v)
+{
+#ifndef NO_GARBAGE_COLLECTOR
+ return _ss(v)->CollectGarbage(v);
+#else
+ return -1;
+#endif
+}
+
+const SQChar *sq_getfreevariable(HSQUIRRELVM v,SQInteger idx,SQUnsignedInteger nval)
+{
+ SQObjectPtr &self = stack_get(v,idx);
+ const SQChar *name = NULL;
+ if(type(self) == OT_CLOSURE) {
+ if(_closure(self)->_outervalues.size()>nval) {
+ v->Push(_closure(self)->_outervalues[nval]);
+ SQFunctionProto *fp = _funcproto(_closure(self)->_function);
+ SQOuterVar &ov = fp->_outervalues[nval];
+ name = _stringval(ov._name);
+ }
+ }
+ return name;
+}
+
+SQRESULT sq_setfreevariable(HSQUIRRELVM v,SQInteger idx,SQUnsignedInteger nval)
+{
+ SQObjectPtr &self=stack_get(v,idx);
+ switch(type(self))
+ {
+ case OT_CLOSURE:
+ if(_closure(self)->_outervalues.size()>nval){
+ _closure(self)->_outervalues[nval]=stack_get(v,-1);
+ }
+ else return sq_throwerror(v,_SC("invalid free var index"));
+ break;
+ case OT_NATIVECLOSURE:
+ if(_nativeclosure(self)->_outervalues.size()>nval){
+ _nativeclosure(self)->_outervalues[nval]=stack_get(v,-1);
+ }
+ else return sq_throwerror(v,_SC("invalid free var index"));
+ break;
+ default:
+ return sq_aux_invalidtype(v,type(self));
+ }
+ v->Pop(1);
+ return SQ_OK;
+}
+
+SQRESULT sq_setattributes(HSQUIRRELVM v,SQInteger idx)
+{
+ SQObjectPtr *o = NULL;
+ _GETSAFE_OBJ(v, idx, OT_CLASS,o);
+ SQObjectPtr &key = stack_get(v,-2);
+ SQObjectPtr &val = stack_get(v,-1);
+ SQObjectPtr attrs;
+ if(type(key) == OT_NULL) {
+ attrs = _class(*o)->_attributes;
+ _class(*o)->_attributes = val;
+ v->Pop(2);
+ v->Push(attrs);
+ return SQ_OK;
+ }else if(_class(*o)->GetAttributes(key,attrs)) {
+ _class(*o)->SetAttributes(key,val);
+ v->Pop(2);
+ v->Push(attrs);
+ return SQ_OK;
+ }
+ return sq_throwerror(v,_SC("wrong index"));
+}
+
+SQRESULT sq_getattributes(HSQUIRRELVM v,SQInteger idx)
+{
+ SQObjectPtr *o = NULL;
+ _GETSAFE_OBJ(v, idx, OT_CLASS,o);
+ SQObjectPtr &key = stack_get(v,-1);
+ SQObjectPtr attrs;
+ if(type(key) == OT_NULL) {
+ attrs = _class(*o)->_attributes;
+ v->Pop();
+ v->Push(attrs);
+ return SQ_OK;
+ }
+ else if(_class(*o)->GetAttributes(key,attrs)) {
+ v->Pop();
+ v->Push(attrs);
+ return SQ_OK;
+ }
+ return sq_throwerror(v,_SC("wrong index"));
+}
+
+SQRESULT sq_getbase(HSQUIRRELVM v,SQInteger idx)
+{
+ SQObjectPtr *o = NULL;
+ _GETSAFE_OBJ(v, idx, OT_CLASS,o);
+ if(_class(*o)->_base)
+ v->Push(SQObjectPtr(_class(*o)->_base));
+ else
+ v->Push(_null_);
+ return SQ_OK;
+}
+
+SQRESULT sq_getclass(HSQUIRRELVM v,SQInteger idx)
+{
+ SQObjectPtr *o = NULL;
+ _GETSAFE_OBJ(v, idx, OT_INSTANCE,o);
+ v->Push(SQObjectPtr(_instance(*o)->_class));
+ return SQ_OK;
+}
+
+SQRESULT sq_createinstance(HSQUIRRELVM v,SQInteger idx)
+{
+ SQObjectPtr *o = NULL;
+ _GETSAFE_OBJ(v, idx, OT_CLASS,o);
+ v->Push(_class(*o)->CreateInstance());
+ return SQ_OK;
+}
+
+void sq_weakref(HSQUIRRELVM v,SQInteger idx)
+{
+ SQObject &o=stack_get(v,idx);
+ if(ISREFCOUNTED(type(o))) {
+ v->Push(_refcounted(o)->GetWeakRef(type(o)));
+ return;
+ }
+ v->Push(o);
+}
+
+SQRESULT sq_getweakrefval(HSQUIRRELVM v,SQInteger idx)
+{
+ SQObjectPtr &o = stack_get(v,idx);
+ if(type(o) != OT_WEAKREF) {
+ return sq_throwerror(v,_SC("the object must be a weakref"));
+ }
+ v->Push(_weakref(o)->_obj);
+ return SQ_OK;
+}
+
+SQRESULT sq_getdefaultdelegate(HSQUIRRELVM v,SQObjectType t)
+{
+ SQSharedState *ss = _ss(v);
+ switch(t) {
+ case OT_TABLE: v->Push(ss->_table_default_delegate); break;
+ case OT_ARRAY: v->Push(ss->_array_default_delegate); break;
+ case OT_STRING: v->Push(ss->_string_default_delegate); break;
+ case OT_INTEGER: case OT_FLOAT: v->Push(ss->_number_default_delegate); break;
+ case OT_GENERATOR: v->Push(ss->_generator_default_delegate); break;
+ case OT_CLOSURE: case OT_NATIVECLOSURE: v->Push(ss->_closure_default_delegate); break;
+ case OT_THREAD: v->Push(ss->_thread_default_delegate); break;
+ case OT_CLASS: v->Push(ss->_class_default_delegate); break;
+ case OT_INSTANCE: v->Push(ss->_instance_default_delegate); break;
+ case OT_WEAKREF: v->Push(ss->_weakref_default_delegate); break;
+ default: return sq_throwerror(v,_SC("the type doesn't have a default delegate"));
+ }
+ return SQ_OK;
+}
+
+SQRESULT sq_next(HSQUIRRELVM v,SQInteger idx)
+{
+ SQObjectPtr o=stack_get(v,idx),&refpos = stack_get(v,-1),realkey,val;
+ if(type(o) == OT_GENERATOR) {
+ return sq_throwerror(v,_SC("cannot iterate a generator"));
+ }
+ int faketojump;
+ if(!v->FOREACH_OP(o,realkey,val,refpos,0,666,faketojump))
+ return SQ_ERROR;
+ if(faketojump != 666) {
+ v->Push(realkey);
+ v->Push(val);
+ return SQ_OK;
+ }
+ return SQ_ERROR;
+}
+
+struct BufState{
+ const SQChar *buf;
+ SQInteger ptr;
+ SQInteger size;
+};
+
+SQInteger buf_lexfeed(SQUserPointer file)
+{
+ BufState *buf=(BufState*)file;
+ if(buf->size<(buf->ptr+1))
+ return 0;
+ return buf->buf[buf->ptr++];
+}
+
+SQRESULT sq_compilebuffer(HSQUIRRELVM v,const SQChar *s,SQInteger size,const SQChar *sourcename,SQBool raiseerror) {
+ BufState buf;
+ buf.buf = s;
+ buf.size = size;
+ buf.ptr = 0;
+ return sq_compile(v, buf_lexfeed, &buf, sourcename, raiseerror);
+}
+
+void sq_move(HSQUIRRELVM dest,HSQUIRRELVM src,SQInteger idx)
+{
+ dest->Push(stack_get(src,idx));
+}
+
+void sq_setprintfunc(HSQUIRRELVM v, SQPRINTFUNCTION printfunc)
+{
+ _ss(v)->_printfunc = printfunc;
+}
+
+SQPRINTFUNCTION sq_getprintfunc(HSQUIRRELVM v)
+{
+ return _ss(v)->_printfunc;
+}
+
+void *sq_malloc(SQUnsignedInteger size)
+{
+ return SQ_MALLOC(size);
+}
+
+void *sq_realloc(void* p,SQUnsignedInteger oldsize,SQUnsignedInteger newsize)
+{
+ return SQ_REALLOC(p,oldsize,newsize);
+}
+
+void sq_free(void *p,SQUnsignedInteger size)
+{
+ SQ_FREE(p,size);
+}
--- /dev/null
+/* see copyright notice in squirrel.h */
+#ifndef _SQARRAY_H_
+#define _SQARRAY_H_
+
+struct SQArray : public CHAINABLE_OBJ
+{
+private:
+ SQArray(SQSharedState *ss,SQInteger nsize){_values.resize(nsize); INIT_CHAIN();ADD_TO_CHAIN(&_ss(this)->_gc_chain,this);}
+ ~SQArray()
+ {
+ REMOVE_FROM_CHAIN(&_ss(this)->_gc_chain,this);
+ }
+public:
+ static SQArray* Create(SQSharedState *ss,SQInteger nInitialSize){
+ SQArray *newarray=(SQArray*)SQ_MALLOC(sizeof(SQArray));
+ new (newarray) SQArray(ss,nInitialSize);
+ return newarray;
+ }
+#ifndef NO_GARBAGE_COLLECTOR
+ void Mark(SQCollectable **chain);
+#endif
+ void Finalize(){
+ _values.resize(0);
+ }
+ bool Get(const SQInteger nidx,SQObjectPtr &val)
+ {
+ if(nidx>=0 && nidx<(SQInteger)_values.size()){
+ SQObjectPtr &o = _values[nidx];
+ val = _realval(o);
+ return true;
+ }
+ else return false;
+ }
+ bool Set(const SQInteger nidx,const SQObjectPtr &val)
+ {
+ if(nidx>=0 && nidx<(SQInteger)_values.size()){
+ _values[nidx]=val;
+ return true;
+ }
+ else return false;
+ }
+ SQInteger Next(const SQObjectPtr &refpos,SQObjectPtr &outkey,SQObjectPtr &outval)
+ {
+ SQUnsignedInteger idx=TranslateIndex(refpos);
+ while(idx<_values.size()){
+ //first found
+ outkey=(SQInteger)idx;
+ SQObjectPtr &o = _values[idx];
+ outval = _realval(o);
+ //return idx for the next iteration
+ return ++idx;
+ }
+ //nothing to iterate anymore
+ return -1;
+ }
+ SQArray *Clone(){SQArray *anew=Create(_opt_ss(this),Size()); anew->_values.copy(_values); return anew; }
+ SQInteger Size() const {return _values.size();}
+ void Resize(SQInteger size,SQObjectPtr &fill = _null_) { _values.resize(size,fill); ShrinkIfNeeded(); }
+ void Reserve(SQInteger size) { _values.reserve(size); }
+ void Append(const SQObject &o){_values.push_back(o);}
+ void Extend(const SQArray *a);
+ SQObjectPtr &Top(){return _values.top();}
+ void Pop(){_values.pop_back(); ShrinkIfNeeded(); }
+ void Insert(const SQObject& idx,const SQObject &val){_values.insert((SQUnsignedInteger)tointeger(idx),val);}
+ void ShrinkIfNeeded() {
+ if(_values.size() <= _values.capacity()>>2) //shrink the array
+ _values.shrinktofit();
+ }
+ void Remove(SQUnsignedInteger idx){
+ _values.remove(idx);
+ ShrinkIfNeeded();
+ }
+ void Release()
+ {
+ sq_delete(this,SQArray);
+ }
+ SQObjectPtrVec _values;
+};
+#endif //_SQARRAY_H_
--- /dev/null
+/*
+ see copyright notice in squirrel.h
+*/
+#include "sqpcheader.h"
+#include "sqvm.h"
+#include "sqstring.h"
+#include "sqtable.h"
+#include "sqarray.h"
+#include "sqfuncproto.h"
+#include "sqclosure.h"
+#include "sqclass.h"
+#include <stdlib.h>
+#include <stdarg.h>
+#include <ctype.h>
+
+bool str2num(const SQChar *s,SQObjectPtr &res)
+{
+ SQChar *end;
+ if(scstrstr(s,_SC("."))){
+ SQFloat r = SQFloat(scstrtod(s,&end));
+ if(s == end) return false;
+ res = r;
+ return true;
+ }
+ else{
+ SQInteger r = SQInteger(scstrtol(s,&end,10));
+ if(s == end) return false;
+ res = r;
+ return true;
+ }
+}
+
+static SQInteger base_dummy(HSQUIRRELVM v)
+{
+ return 0;
+}
+
+#ifndef NO_GARBAGE_COLLECTOR
+static SQInteger base_collectgarbage(HSQUIRRELVM v)
+{
+ sq_pushinteger(v, sq_collectgarbage(v));
+ return 1;
+}
+#endif
+
+static SQInteger base_getroottable(HSQUIRRELVM v)
+{
+ v->Push(v->_roottable);
+ return 1;
+}
+
+static SQInteger base_setroottable(HSQUIRRELVM v)
+{
+ SQObjectPtr &o=stack_get(v,2);
+ if(SQ_FAILED(sq_setroottable(v))) return SQ_ERROR;
+ v->Push(o);
+ return 1;
+}
+
+static SQInteger base_seterrorhandler(HSQUIRRELVM v)
+{
+ sq_seterrorhandler(v);
+ return 0;
+}
+
+static SQInteger base_setdebughook(HSQUIRRELVM v)
+{
+ sq_setdebughook(v);
+ return 0;
+}
+
+static SQInteger base_enabledebuginfo(HSQUIRRELVM v)
+{
+ SQObjectPtr &o=stack_get(v,2);
+ sq_enabledebuginfo(v,(type(o) != OT_NULL)?1:0);
+ return 0;
+}
+
+static SQInteger base_getstackinfos(HSQUIRRELVM v)
+{
+ SQInteger level;
+ SQStackInfos si;
+ SQInteger seq = 0;
+ const SQChar *name = NULL;
+ sq_getinteger(v, -1, &level);
+ if (SQ_SUCCEEDED(sq_stackinfos(v, level, &si)))
+ {
+ const SQChar *fn = _SC("unknown");
+ const SQChar *src = _SC("unknown");
+ if(si.funcname)fn = si.funcname;
+ if(si.source)src = si.source;
+ sq_newtable(v);
+ sq_pushstring(v, _SC("func"), -1);
+ sq_pushstring(v, fn, -1);
+ sq_createslot(v, -3);
+ sq_pushstring(v, _SC("src"), -1);
+ sq_pushstring(v, src, -1);
+ sq_createslot(v, -3);
+ sq_pushstring(v, _SC("line"), -1);
+ sq_pushinteger(v, si.line);
+ sq_createslot(v, -3);
+ sq_pushstring(v, _SC("locals"), -1);
+ sq_newtable(v);
+ seq=0;
+ while ((name = sq_getlocal(v, level, seq))) {
+ sq_pushstring(v, name, -1);
+ sq_push(v, -2);
+ sq_createslot(v, -4);
+ sq_pop(v, 1);
+ seq++;
+ }
+ sq_createslot(v, -3);
+ return 1;
+ }
+
+ return 0;
+}
+
+static SQInteger base_assert(HSQUIRRELVM v)
+{
+ if(v->IsFalse(stack_get(v,2))){
+ return sq_throwerror(v,_SC("assertion failed"));
+ }
+ return 0;
+}
+
+static SQInteger get_slice_params(HSQUIRRELVM v,SQInteger &sidx,SQInteger &eidx,SQObjectPtr &o)
+{
+ SQInteger top = sq_gettop(v);
+ sidx=0;
+ eidx=0;
+ o=stack_get(v,1);
+ SQObjectPtr &start=stack_get(v,2);
+ if(type(start)!=OT_NULL && sq_isnumeric(start)){
+ sidx=tointeger(start);
+ }
+ if(top>2){
+ SQObjectPtr &end=stack_get(v,3);
+ if(sq_isnumeric(end)){
+ eidx=tointeger(end);
+ }
+ }
+ else {
+ eidx = sq_getsize(v,1);
+ }
+ return 1;
+}
+
+static SQInteger base_print(HSQUIRRELVM v)
+{
+ const SQChar *str;
+ sq_tostring(v,2);
+ sq_getstring(v,-1,&str);
+ if(_ss(v)->_printfunc) _ss(v)->_printfunc(v,_SC("%s"),str);
+ return 0;
+}
+
+static SQInteger base_compilestring(HSQUIRRELVM v)
+{
+ SQInteger nargs=sq_gettop(v);
+ const SQChar *src=NULL,*name=_SC("unnamedbuffer");
+ SQInteger size;
+ sq_getstring(v,2,&src);
+ size=sq_getsize(v,2);
+ if(nargs>2){
+ sq_getstring(v,3,&name);
+ }
+ if(SQ_SUCCEEDED(sq_compilebuffer(v,src,size,name,SQFalse)))
+ return 1;
+ else
+ return SQ_ERROR;
+}
+
+static SQInteger base_newthread(HSQUIRRELVM v)
+{
+ SQObjectPtr &func = stack_get(v,2);
+ SQInteger stksize = (_funcproto(_closure(func)->_function)->_stacksize << 1) +2;
+ HSQUIRRELVM newv = sq_newthread(v, (stksize < MIN_STACK_OVERHEAD + 2)? MIN_STACK_OVERHEAD + 2 : stksize);
+ sq_move(newv,v,-2);
+ return 1;
+}
+
+static SQInteger base_suspend(HSQUIRRELVM v)
+{
+ return sq_suspendvm(v);
+}
+
+static SQInteger base_array(HSQUIRRELVM v)
+{
+ SQArray *a;
+ SQObject &size = stack_get(v,2);
+ if(sq_gettop(v) > 2) {
+ a = SQArray::Create(_ss(v),0);
+ a->Resize(tointeger(size),stack_get(v,3));
+ }
+ else {
+ a = SQArray::Create(_ss(v),tointeger(size));
+ }
+ v->Push(a);
+ return 1;
+}
+
+static SQInteger base_type(HSQUIRRELVM v)
+{
+ SQObjectPtr &o = stack_get(v,2);
+ v->Push(SQString::Create(_ss(v),GetTypeName(o),-1));
+ return 1;
+}
+
+static SQRegFunction base_funcs[]={
+ //generic
+ {_SC("seterrorhandler"),base_seterrorhandler,2, NULL},
+ {_SC("setdebughook"),base_setdebughook,2, NULL},
+ {_SC("enabledebuginfo"),base_enabledebuginfo,2, NULL},
+ {_SC("getstackinfos"),base_getstackinfos,2, _SC(".n")},
+ {_SC("getroottable"),base_getroottable,1, NULL},
+ {_SC("setroottable"),base_setroottable,2, NULL},
+ {_SC("assert"),base_assert,2, NULL},
+ {_SC("print"),base_print,2, NULL},
+ {_SC("compilestring"),base_compilestring,-2, _SC(".ss")},
+ {_SC("newthread"),base_newthread,2, _SC(".c")},
+ {_SC("suspend"),base_suspend,-1, NULL},
+ {_SC("array"),base_array,-2, _SC(".n")},
+ {_SC("type"),base_type,2, NULL},
+ {_SC("dummy"),base_dummy,0,NULL},
+#ifndef NO_GARBAGE_COLLECTOR
+ {_SC("collectgarbage"),base_collectgarbage,1, _SC("t")},
+#endif
+ {0,0}
+};
+
+void sq_base_register(HSQUIRRELVM v)
+{
+ SQInteger i=0;
+ sq_pushroottable(v);
+ while(base_funcs[i].name!=0) {
+ sq_pushstring(v,base_funcs[i].name,-1);
+ sq_newclosure(v,base_funcs[i].f,0);
+ sq_setnativeclosurename(v,-1,base_funcs[i].name);
+ sq_setparamscheck(v,base_funcs[i].nparamscheck,base_funcs[i].typemask);
+ sq_createslot(v,-3);
+ i++;
+ }
+ sq_pushstring(v,_SC("_version_"),-1);
+ sq_pushstring(v,SQUIRREL_VERSION,-1);
+ sq_createslot(v,-3);
+ sq_pushstring(v,_SC("_charsize_"),-1);
+ sq_pushinteger(v,sizeof(SQChar));
+ sq_createslot(v,-3);
+ sq_pushstring(v,_SC("_intsize_"),-1);
+ sq_pushinteger(v,sizeof(SQInteger));
+ sq_createslot(v,-3);
+ sq_pop(v,1);
+}
+
+static SQInteger default_delegate_len(HSQUIRRELVM v)
+{
+ v->Push(SQInteger(sq_getsize(v,1)));
+ return 1;
+}
+
+static SQInteger default_delegate_tofloat(HSQUIRRELVM v)
+{
+ SQObjectPtr &o=stack_get(v,1);
+ switch(type(o)){
+ case OT_STRING:{
+ SQObjectPtr res;
+ if(str2num(_stringval(o),res)){
+ v->Push(SQObjectPtr(tofloat(res)));
+ break;
+ }}
+ return sq_throwerror(v, _SC("cannot convert the string"));
+ break;
+ case OT_INTEGER:case OT_FLOAT:
+ v->Push(SQObjectPtr(tofloat(o)));
+ break;
+ case OT_BOOL:
+ v->Push(SQObjectPtr((SQFloat)(_integer(o)?1:0)));
+ break;
+ default:
+ v->Push(_null_);
+ break;
+ }
+ return 1;
+}
+
+static SQInteger default_delegate_tointeger(HSQUIRRELVM v)
+{
+ SQObjectPtr &o=stack_get(v,1);
+ switch(type(o)){
+ case OT_STRING:{
+ SQObjectPtr res;
+ if(str2num(_stringval(o),res)){
+ v->Push(SQObjectPtr(tointeger(res)));
+ break;
+ }}
+ return sq_throwerror(v, _SC("cannot convert the string"));
+ break;
+ case OT_INTEGER:case OT_FLOAT:
+ v->Push(SQObjectPtr(tointeger(o)));
+ break;
+ case OT_BOOL:
+ v->Push(SQObjectPtr(_integer(o)?(SQInteger)1:(SQInteger)0));
+ break;
+ default:
+ v->Push(_null_);
+ break;
+ }
+ return 1;
+}
+
+static SQInteger default_delegate_tostring(HSQUIRRELVM v)
+{
+ sq_tostring(v,1);
+ return 1;
+}
+
+static SQInteger obj_delegate_weakref(HSQUIRRELVM v)
+{
+ sq_weakref(v,1);
+ return 1;
+}
+
+static SQInteger obj_clear(HSQUIRRELVM v)
+{
+ return sq_clear(v,-1);
+}
+
+
+static SQInteger number_delegate_tochar(HSQUIRRELVM v)
+{
+ SQObject &o=stack_get(v,1);
+ SQChar c = (SQChar)tointeger(o);
+ v->Push(SQString::Create(_ss(v),(const SQChar *)&c,1));
+ return 1;
+}
+
+
+/////////////////////////////////////////////////////////////////
+//TABLE DEFAULT DELEGATE
+
+static SQInteger table_rawdelete(HSQUIRRELVM v)
+{
+ if(SQ_FAILED(sq_rawdeleteslot(v,1,SQTrue)))
+ return SQ_ERROR;
+ return 1;
+}
+
+
+static SQInteger container_rawexists(HSQUIRRELVM v)
+{
+ if(SQ_SUCCEEDED(sq_rawget(v,-2))) {
+ sq_pushbool(v,SQTrue);
+ return 1;
+ }
+ sq_pushbool(v,SQFalse);
+ return 1;
+}
+
+static SQInteger table_rawset(HSQUIRRELVM v)
+{
+ return sq_rawset(v,-3);
+}
+
+
+static SQInteger table_rawget(HSQUIRRELVM v)
+{
+ return SQ_SUCCEEDED(sq_rawget(v,-2))?1:SQ_ERROR;
+}
+
+
+SQRegFunction SQSharedState::_table_default_delegate_funcz[]={
+ {_SC("len"),default_delegate_len,1, _SC("t")},
+ {_SC("rawget"),table_rawget,2, _SC("t")},
+ {_SC("rawset"),table_rawset,3, _SC("t")},
+ {_SC("rawdelete"),table_rawdelete,2, _SC("t")},
+ {_SC("rawin"),container_rawexists,2, _SC("t")},
+ {_SC("weakref"),obj_delegate_weakref,1, NULL },
+ {_SC("tostring"),default_delegate_tostring,1, _SC(".")},
+ {_SC("clear"),obj_clear,1, _SC(".")},
+ {0,0}
+};
+
+//ARRAY DEFAULT DELEGATE///////////////////////////////////////
+
+static SQInteger array_append(HSQUIRRELVM v)
+{
+ return sq_arrayappend(v,-2);
+}
+
+static SQInteger array_extend(HSQUIRRELVM v)
+{
+ _array(stack_get(v,1))->Extend(_array(stack_get(v,2)));
+ return 0;
+}
+
+static SQInteger array_reverse(HSQUIRRELVM v)
+{
+ return sq_arrayreverse(v,-1);
+}
+
+static SQInteger array_pop(HSQUIRRELVM v)
+{
+ return SQ_SUCCEEDED(sq_arraypop(v,1,SQTrue))?1:SQ_ERROR;
+}
+
+static SQInteger array_top(HSQUIRRELVM v)
+{
+ SQObject &o=stack_get(v,1);
+ if(_array(o)->Size()>0){
+ v->Push(_array(o)->Top());
+ return 1;
+ }
+ else return sq_throwerror(v,_SC("top() on a empty array"));
+}
+
+static SQInteger array_insert(HSQUIRRELVM v)
+{
+ SQObject &o=stack_get(v,1);
+ SQObject &idx=stack_get(v,2);
+ SQObject &val=stack_get(v,3);
+ _array(o)->Insert(idx,val);
+ return 0;
+}
+
+static SQInteger array_remove(HSQUIRRELVM v)
+{
+ SQObject &o = stack_get(v, 1);
+ SQObject &idx = stack_get(v, 2);
+ if(!sq_isnumeric(idx)) return sq_throwerror(v, _SC("wrong type"));
+ SQObjectPtr val;
+ if(_array(o)->Get(tointeger(idx), val)) {
+ _array(o)->Remove(tointeger(idx));
+ v->Push(val);
+ return 1;
+ }
+ return sq_throwerror(v, _SC("idx out of range"));
+}
+
+static SQInteger array_resize(HSQUIRRELVM v)
+{
+ SQObject &o = stack_get(v, 1);
+ SQObject &nsize = stack_get(v, 2);
+ SQObjectPtr fill;
+ if(sq_isnumeric(nsize)) {
+ if(sq_gettop(v) > 2)
+ fill = stack_get(v, 3);
+ _array(o)->Resize(tointeger(nsize),fill);
+ return 0;
+ }
+ return sq_throwerror(v, _SC("size must be a number"));
+}
+
+
+//QSORT ala Sedgewick
+bool _qsort_compare(HSQUIRRELVM v,SQObjectPtr &arr,SQObjectPtr &a,SQObjectPtr &b,SQInteger func,SQInteger &ret)
+{
+ if(func < 0) {
+ if(!v->ObjCmp(a,b,ret)) return false;
+ }
+ else {
+ SQInteger top = sq_gettop(v);
+ sq_push(v, func);
+ sq_pushroottable(v);
+ v->Push(a);
+ v->Push(b);
+ if(SQ_FAILED(sq_call(v, 3, SQTrue, SQFalse))) {
+ v->Raise_Error(_SC("compare func failed"));
+ return false;
+ }
+ sq_getinteger(v, -1, &ret);
+ sq_settop(v, top);
+ return true;
+ }
+ return true;
+}
+//QSORT ala Sedgewick
+bool _qsort(HSQUIRRELVM v,SQObjectPtr &arr, SQInteger l, SQInteger r,SQInteger func)
+{
+ SQInteger i, j;
+ SQArray *a=_array(arr);
+ SQObjectPtr pivot,t;
+ if( l < r ){
+ pivot = a->_values[l];
+ i = l; j = r+1;
+ while(1){
+ SQInteger ret;
+ do {
+ ++i;
+ if(i > r) break;
+ if(!_qsort_compare(v,arr,a->_values[i],pivot,func,ret))
+ return false;
+ } while( ret <= 0);
+ do {
+ --j;
+ if(!_qsort_compare(v,arr,a->_values[j],pivot,func,ret))
+ return false;
+ }
+ while( ret > 0 );
+ if( i >= j ) break;
+ t = a->_values[i]; a->_values[i] = a->_values[j]; a->_values[j] = t;
+ }
+ t = a->_values[l]; a->_values[l] = a->_values[j]; a->_values[j] = t;
+ if(!_qsort( v, arr, l, j-1,func)) return false;
+ if(!_qsort( v, arr, j+1, r,func)) return false;
+ }
+ return true;
+}
+
+static SQInteger array_sort(HSQUIRRELVM v)
+{
+ SQInteger func = -1;
+ SQObjectPtr &o = stack_get(v,1);
+ SQObject &funcobj = stack_get(v,2);
+ if(_array(o)->Size() > 1) {
+ if(type(funcobj) == OT_CLOSURE || type(funcobj) == OT_NATIVECLOSURE) func = 2;
+ if(!_qsort(v, o, 0, _array(o)->Size()-1, func))
+ return SQ_ERROR;
+
+ }
+ return 0;
+}
+static SQInteger array_slice(HSQUIRRELVM v)
+{
+ SQInteger sidx,eidx;
+ SQObjectPtr o;
+ if(get_slice_params(v,sidx,eidx,o)==-1)return -1;
+ if(sidx<0)sidx=_array(o)->Size()+sidx;
+ if(eidx<0)eidx=_array(o)->Size()+eidx;
+ if(eidx < sidx)return sq_throwerror(v,_SC("wrong indexes"));
+ SQArray *arr=SQArray::Create(_ss(v),eidx-sidx);
+ SQObjectPtr t;
+ SQInteger count=0;
+ for(SQInteger i=sidx;i<eidx;i++){
+ _array(o)->Get(i,t);
+ arr->Set(count++,t);
+ }
+ v->Push(arr);
+ return 1;
+
+}
+
+SQRegFunction SQSharedState::_array_default_delegate_funcz[]={
+ {_SC("len"),default_delegate_len,1, _SC("a")},
+ {_SC("append"),array_append,2, _SC("a")},
+ {_SC("extend"),array_extend,2, _SC("aa")},
+ {_SC("push"),array_append,2, _SC("a")},
+ {_SC("pop"),array_pop,1, _SC("a")},
+ {_SC("top"),array_top,1, _SC("a")},
+ {_SC("insert"),array_insert,3, _SC("an")},
+ {_SC("remove"),array_remove,2, _SC("an")},
+ {_SC("resize"),array_resize,-2, _SC("an")},
+ {_SC("reverse"),array_reverse,1, _SC("a")},
+ {_SC("sort"),array_sort,-1, _SC("ac")},
+ {_SC("slice"),array_slice,-1, _SC("ann")},
+ {_SC("weakref"),obj_delegate_weakref,1, NULL },
+ {_SC("tostring"),default_delegate_tostring,1, _SC(".")},
+ {_SC("clear"),obj_clear,1, _SC(".")},
+ {0,0}
+};
+
+//STRING DEFAULT DELEGATE//////////////////////////
+static SQInteger string_slice(HSQUIRRELVM v)
+{
+ SQInteger sidx,eidx;
+ SQObjectPtr o;
+ if(SQ_FAILED(get_slice_params(v,sidx,eidx,o)))return -1;
+ if(sidx<0)sidx=_string(o)->_len+sidx;
+ if(eidx<0)eidx=_string(o)->_len+eidx;
+ if(eidx<sidx)
+ return sq_throwerror(v,_SC("wrong indexes"));
+ v->Push(SQString::Create(_ss(v),&_stringval(o)[sidx],eidx-sidx));
+ return 1;
+}
+
+static SQInteger string_find(HSQUIRRELVM v)
+{
+ SQInteger top,start_idx=0;
+ const SQChar *str,*substr,*ret;
+ if(((top=sq_gettop(v))>1) && SQ_SUCCEEDED(sq_getstring(v,1,&str)) && SQ_SUCCEEDED(sq_getstring(v,2,&substr))){
+ if(top>2)sq_getinteger(v,3,&start_idx);
+ if((sq_getsize(v,1)>start_idx) && (start_idx>=0)){
+ ret=scstrstr(&str[start_idx],substr);
+ if(ret){
+ sq_pushinteger(v,(SQInteger)(ret-str));
+ return 1;
+ }
+ }
+ return 0;
+ }
+ return sq_throwerror(v,_SC("invalid param"));
+}
+
+#define STRING_TOFUNCZ(func) static SQInteger string_##func(HSQUIRRELVM v) \
+{ \
+ SQObject str=stack_get(v,1); \
+ SQInteger len=_string(str)->_len; \
+ const SQChar *sThis=_stringval(str); \
+ SQChar *sNew=(_ss(v)->GetScratchPad(rsl(len))); \
+ for(SQInteger i=0;i<len;i++) sNew[i]=func(sThis[i]); \
+ v->Push(SQString::Create(_ss(v),sNew,len)); \
+ return 1; \
+}
+
+
+STRING_TOFUNCZ(tolower)
+STRING_TOFUNCZ(toupper)
+
+SQRegFunction SQSharedState::_string_default_delegate_funcz[]={
+ {_SC("len"),default_delegate_len,1, _SC("s")},
+ {_SC("tointeger"),default_delegate_tointeger,1, _SC("s")},
+ {_SC("tofloat"),default_delegate_tofloat,1, _SC("s")},
+ {_SC("tostring"),default_delegate_tostring,1, _SC(".")},
+ {_SC("slice"),string_slice,-1, _SC(" s n n")},
+ {_SC("find"),string_find,-2, _SC("s s n ")},
+ {_SC("tolower"),string_tolower,1, _SC("s")},
+ {_SC("toupper"),string_toupper,1, _SC("s")},
+ {_SC("weakref"),obj_delegate_weakref,1, NULL },
+ {0,0}
+};
+
+//INTEGER DEFAULT DELEGATE//////////////////////////
+SQRegFunction SQSharedState::_number_default_delegate_funcz[]={
+ {_SC("tointeger"),default_delegate_tointeger,1, _SC("n|b")},
+ {_SC("tofloat"),default_delegate_tofloat,1, _SC("n|b")},
+ {_SC("tostring"),default_delegate_tostring,1, _SC(".")},
+ {_SC("tochar"),number_delegate_tochar,1, _SC("n|b")},
+ {_SC("weakref"),obj_delegate_weakref,1, NULL },
+ {0,0}
+};
+
+//CLOSURE DEFAULT DELEGATE//////////////////////////
+static SQInteger closure_pcall(HSQUIRRELVM v)
+{
+ return SQ_SUCCEEDED(sq_call(v,sq_gettop(v)-1,SQTrue,SQFalse))?1:SQ_ERROR;
+}
+
+static SQInteger closure_call(HSQUIRRELVM v)
+{
+ return SQ_SUCCEEDED(sq_call(v,sq_gettop(v)-1,SQTrue,SQTrue))?1:SQ_ERROR;
+}
+
+static SQInteger _closure_acall(HSQUIRRELVM v,SQBool raiseerror)
+{
+ SQArray *aparams=_array(stack_get(v,2));
+ SQInteger nparams=aparams->Size();
+ v->Push(stack_get(v,1));
+ for(SQInteger i=0;i<nparams;i++)v->Push(aparams->_values[i]);
+ return SQ_SUCCEEDED(sq_call(v,nparams,SQTrue,raiseerror))?1:SQ_ERROR;
+}
+
+static SQInteger closure_acall(HSQUIRRELVM v)
+{
+ return _closure_acall(v,SQTrue);
+}
+
+static SQInteger closure_pacall(HSQUIRRELVM v)
+{
+ return _closure_acall(v,SQFalse);
+}
+
+static SQInteger closure_bindenv(HSQUIRRELVM v)
+{
+ if(SQ_FAILED(sq_bindenv(v,1)))
+ return SQ_ERROR;
+ return 1;
+}
+
+static SQInteger closure_getinfos(HSQUIRRELVM v) {
+ SQObject o = stack_get(v,1);
+ SQTable *res = SQTable::Create(_ss(v),4);
+ if(type(o) == OT_CLOSURE) {
+ SQFunctionProto *f = _funcproto(_closure(o)->_function);
+ SQInteger nparams = f->_nparameters + (f->_varparams?1:0);
+ SQObjectPtr params = SQArray::Create(_ss(v),nparams);
+ for(SQInteger n = 0; n<f->_nparameters; n++) {
+ _array(params)->Set((SQInteger)n,f->_parameters[n]);
+ }
+ if(f->_varparams) {
+ _array(params)->Set(nparams-1,SQString::Create(_ss(v),_SC("..."),-1));
+ }
+ res->NewSlot(SQString::Create(_ss(v),_SC("native"),-1),false);
+ res->NewSlot(SQString::Create(_ss(v),_SC("name"),-1),f->_name);
+ res->NewSlot(SQString::Create(_ss(v),_SC("src"),-1),f->_sourcename);
+ res->NewSlot(SQString::Create(_ss(v),_SC("parameters"),-1),params);
+ res->NewSlot(SQString::Create(_ss(v),_SC("varargs"),-1),f->_varparams);
+ }
+ else { //OT_NATIVECLOSURE
+ SQNativeClosure *nc = _nativeclosure(o);
+ res->NewSlot(SQString::Create(_ss(v),_SC("native"),-1),true);
+ res->NewSlot(SQString::Create(_ss(v),_SC("name"),-1),nc->_name);
+ res->NewSlot(SQString::Create(_ss(v),_SC("paramscheck"),-1),nc->_nparamscheck);
+ SQObjectPtr typecheck;
+ if(nc->_typecheck.size() > 0) {
+ typecheck =
+ SQArray::Create(_ss(v), nc->_typecheck.size());
+ for(SQUnsignedInteger n = 0; n<nc->_typecheck.size(); n++) {
+ _array(typecheck)->Set((SQInteger)n,nc->_typecheck[n]);
+ }
+ }
+ res->NewSlot(SQString::Create(_ss(v),_SC("typecheck"),-1),typecheck);
+ }
+ v->Push(res);
+ return 1;
+}
+
+
+SQRegFunction SQSharedState::_closure_default_delegate_funcz[]={
+ {_SC("call"),closure_call,-1, _SC("c")},
+ {_SC("pcall"),closure_pcall,-1, _SC("c")},
+ {_SC("acall"),closure_acall,2, _SC("ca")},
+ {_SC("pacall"),closure_pacall,2, _SC("ca")},
+ {_SC("weakref"),obj_delegate_weakref,1, NULL },
+ {_SC("tostring"),default_delegate_tostring,1, _SC(".")},
+ {_SC("bindenv"),closure_bindenv,2, _SC("c x|y|t")},
+ {_SC("getinfos"),closure_getinfos,1, _SC("c")},
+ {0,0}
+};
+
+//GENERATOR DEFAULT DELEGATE
+static SQInteger generator_getstatus(HSQUIRRELVM v)
+{
+ SQObject &o=stack_get(v,1);
+ switch(_generator(o)->_state){
+ case SQGenerator::eSuspended:v->Push(SQString::Create(_ss(v),_SC("suspended")));break;
+ case SQGenerator::eRunning:v->Push(SQString::Create(_ss(v),_SC("running")));break;
+ case SQGenerator::eDead:v->Push(SQString::Create(_ss(v),_SC("dead")));break;
+ }
+ return 1;
+}
+
+SQRegFunction SQSharedState::_generator_default_delegate_funcz[]={
+ {_SC("getstatus"),generator_getstatus,1, _SC("g")},
+ {_SC("weakref"),obj_delegate_weakref,1, NULL },
+ {_SC("tostring"),default_delegate_tostring,1, _SC(".")},
+ {0,0}
+};
+
+//THREAD DEFAULT DELEGATE
+
+static SQInteger thread_call(HSQUIRRELVM v)
+{
+ SQObjectPtr o = stack_get(v,1);
+ if(type(o) == OT_THREAD) {
+ SQInteger nparams = sq_gettop(v);
+ _thread(o)->Push(_thread(o)->_roottable);
+ for(SQInteger i = 2; i<(nparams+1); i++)
+ sq_move(_thread(o),v,i);
+ if(SQ_SUCCEEDED(sq_call(_thread(o),nparams,SQTrue,SQFalse))) {
+ sq_move(v,_thread(o),-1);
+ return 1;
+ }
+ return SQ_ERROR;
+ }
+ return sq_throwerror(v,_SC("wrong parameter"));
+}
+
+static SQInteger thread_wakeup(HSQUIRRELVM v)
+{
+ SQObjectPtr o = stack_get(v,1);
+ if(type(o) == OT_THREAD) {
+ SQVM *thread = _thread(o);
+ SQInteger state = sq_getvmstate(thread);
+ if(state != SQ_VMSTATE_SUSPENDED) {
+ switch(state) {
+ case SQ_VMSTATE_IDLE:
+ return sq_throwerror(v,_SC("cannot wakeup a idle thread"));
+ break;
+ case SQ_VMSTATE_RUNNING:
+ return sq_throwerror(v,_SC("cannot wakeup a running thread"));
+ break;
+ }
+ }
+
+ SQInteger wakeupret = sq_gettop(v)>1?1:0;
+ if(wakeupret) {
+ sq_move(thread,v,2);
+ }
+ if(SQ_SUCCEEDED(sq_wakeupvm(thread,wakeupret,1,SQFalse))) {
+ sq_move(v,thread,-1);
+ sq_pop(thread,1);
+ if(sq_getvmstate(thread) == SQ_VMSTATE_IDLE) {
+ sq_pop(thread,1);
+ }
+ return 1;
+ }
+ return SQ_ERROR;
+ }
+ return sq_throwerror(v,_SC("wrong parameter"));
+}
+
+static SQInteger thread_getstatus(HSQUIRRELVM v)
+{
+ SQObjectPtr &o = stack_get(v,1);
+ switch(sq_getvmstate(_thread(o))) {
+ case SQ_VMSTATE_IDLE:
+ sq_pushstring(v,_SC("idle"),-1);
+ break;
+ case SQ_VMSTATE_RUNNING:
+ sq_pushstring(v,_SC("running"),-1);
+ break;
+ case SQ_VMSTATE_SUSPENDED:
+ sq_pushstring(v,_SC("suspended"),-1);
+ break;
+ default:
+ return sq_throwerror(v,_SC("internal VM error"));
+ }
+ return 1;
+}
+
+SQRegFunction SQSharedState::_thread_default_delegate_funcz[] = {
+ {_SC("call"), thread_call, -1, _SC("v")},
+ {_SC("wakeup"), thread_wakeup, -1, _SC("v")},
+ {_SC("getstatus"), thread_getstatus, 1, _SC("v")},
+ {_SC("weakref"),obj_delegate_weakref,1, NULL },
+ {_SC("tostring"),default_delegate_tostring,1, _SC(".")},
+ {0,0},
+};
+
+static SQInteger class_getattributes(HSQUIRRELVM v)
+{
+ if(SQ_SUCCEEDED(sq_getattributes(v,-2)))
+ return 1;
+ return SQ_ERROR;
+}
+
+static SQInteger class_setattributes(HSQUIRRELVM v)
+{
+ if(SQ_SUCCEEDED(sq_setattributes(v,-3)))
+ return 1;
+ return SQ_ERROR;
+}
+
+static SQInteger class_instance(HSQUIRRELVM v)
+{
+ if(SQ_SUCCEEDED(sq_createinstance(v,-1)))
+ return 1;
+ return SQ_ERROR;
+}
+
+SQRegFunction SQSharedState::_class_default_delegate_funcz[] = {
+ {_SC("getattributes"), class_getattributes, 2, _SC("y.")},
+ {_SC("setattributes"), class_setattributes, 3, _SC("y..")},
+ {_SC("rawin"),container_rawexists,2, _SC("y")},
+ {_SC("weakref"),obj_delegate_weakref,1, NULL },
+ {_SC("tostring"),default_delegate_tostring,1, _SC(".")},
+ {_SC("instance"),class_instance,1, _SC("y")},
+ {0,0}
+};
+
+static SQInteger instance_getclass(HSQUIRRELVM v)
+{
+ if(SQ_SUCCEEDED(sq_getclass(v,1)))
+ return 1;
+ return SQ_ERROR;
+}
+
+SQRegFunction SQSharedState::_instance_default_delegate_funcz[] = {
+ {_SC("getclass"), instance_getclass, 1, _SC("x")},
+ {_SC("rawin"),container_rawexists,2, _SC("x")},
+ {_SC("weakref"),obj_delegate_weakref,1, NULL },
+ {_SC("tostring"),default_delegate_tostring,1, _SC(".")},
+ {0,0}
+};
+
+static SQInteger weakref_ref(HSQUIRRELVM v)
+{
+ if(SQ_FAILED(sq_getweakrefval(v,1)))
+ return SQ_ERROR;
+ return 1;
+}
+
+SQRegFunction SQSharedState::_weakref_default_delegate_funcz[] = {
+ {_SC("ref"),weakref_ref,1, _SC("r")},
+ {_SC("weakref"),obj_delegate_weakref,1, NULL },
+ {_SC("tostring"),default_delegate_tostring,1, _SC(".")},
+ {0,0}
+};
+
+
--- /dev/null
+/*
+ see copyright notice in squirrel.h
+*/
+#include "sqpcheader.h"
+#include "sqvm.h"
+#include "sqtable.h"
+#include "sqclass.h"
+#include "sqclosure.h"
+
+SQClass::SQClass(SQSharedState *ss,SQClass *base)
+{
+ _base = base;
+ _typetag = 0;
+ _hook = NULL;
+ _udsize = 0;
+ _metamethods.resize(MT_LAST); //size it to max size
+ if(_base) {
+ _defaultvalues.copy(base->_defaultvalues);
+ _methods.copy(base->_methods);
+ _metamethods.copy(base->_metamethods);
+ __ObjAddRef(_base);
+ }
+ _members = base?base->_members->Clone() : SQTable::Create(ss,0);
+ __ObjAddRef(_members);
+ _locked = false;
+ INIT_CHAIN();
+ ADD_TO_CHAIN(&_sharedstate->_gc_chain, this);
+}
+
+void SQClass::Finalize() {
+ _attributes = _null_;
+ _defaultvalues.resize(0);
+ _methods.resize(0);
+ _metamethods.resize(0);
+ __ObjRelease(_members);
+ if(_base) {
+ __ObjRelease(_base);
+ }
+}
+
+SQClass::~SQClass()
+{
+ REMOVE_FROM_CHAIN(&_sharedstate->_gc_chain, this);
+ Finalize();
+}
+
+bool SQClass::NewSlot(SQSharedState *ss,const SQObjectPtr &key,const SQObjectPtr &val,bool bstatic)
+{
+ SQObjectPtr temp;
+ if(_locked)
+ return false; //the class already has an instance so cannot be modified
+ if(_members->Get(key,temp) && _isfield(temp)) //overrides the default value
+ {
+ _defaultvalues[_member_idx(temp)].val = val;
+ return true;
+ }
+ if(type(val) == OT_CLOSURE || type(val) == OT_NATIVECLOSURE || bstatic) {
+ SQInteger mmidx;
+ if((type(val) == OT_CLOSURE || type(val) == OT_NATIVECLOSURE) &&
+ (mmidx = ss->GetMetaMethodIdxByName(key)) != -1) {
+ _metamethods[mmidx] = val;
+ }
+ else {
+ if(type(temp) == OT_NULL) {
+ SQClassMember m;
+ m.val = val;
+ _members->NewSlot(key,SQObjectPtr(_make_method_idx(_methods.size())));
+ _methods.push_back(m);
+ }
+ else {
+ _methods[_member_idx(temp)].val = val;
+ }
+ }
+ return true;
+ }
+ SQClassMember m;
+ m.val = val;
+ _members->NewSlot(key,SQObjectPtr(_make_field_idx(_defaultvalues.size())));
+ _defaultvalues.push_back(m);
+ return true;
+}
+
+SQInstance *SQClass::CreateInstance()
+{
+ if(!_locked) Lock();
+ return SQInstance::Create(_opt_ss(this),this);
+}
+
+SQInteger SQClass::Next(const SQObjectPtr &refpos, SQObjectPtr &outkey, SQObjectPtr &outval)
+{
+ SQObjectPtr oval;
+ SQInteger idx = _members->Next(false,refpos,outkey,oval);
+ if(idx != -1) {
+ if(_ismethod(oval)) {
+ outval = _methods[_member_idx(oval)].val;
+ }
+ else {
+ SQObjectPtr &o = _defaultvalues[_member_idx(oval)].val;
+ outval = _realval(o);
+ }
+ }
+ return idx;
+}
+
+bool SQClass::SetAttributes(const SQObjectPtr &key,const SQObjectPtr &val)
+{
+ SQObjectPtr idx;
+ if(_members->Get(key,idx)) {
+ if(_isfield(idx))
+ _defaultvalues[_member_idx(idx)].attrs = val;
+ else
+ _methods[_member_idx(idx)].attrs = val;
+ return true;
+ }
+ return false;
+}
+
+bool SQClass::GetAttributes(const SQObjectPtr &key,SQObjectPtr &outval)
+{
+ SQObjectPtr idx;
+ if(_members->Get(key,idx)) {
+ outval = (_isfield(idx)?_defaultvalues[_member_idx(idx)].attrs:_methods[_member_idx(idx)].attrs);
+ return true;
+ }
+ return false;
+}
+
+///////////////////////////////////////////////////////////////////////
+void SQInstance::Init(SQSharedState *ss)
+{
+ _userpointer = NULL;
+ _hook = NULL;
+ __ObjAddRef(_class);
+ _delegate = _class->_members;
+ INIT_CHAIN();
+ ADD_TO_CHAIN(&_sharedstate->_gc_chain, this);
+}
+
+SQInstance::SQInstance(SQSharedState *ss, SQClass *c, SQInteger memsize)
+{
+ _memsize = memsize;
+ _class = c;
+ SQUnsignedInteger nvalues = _class->_defaultvalues.size();
+ for(SQUnsignedInteger n = 0; n < nvalues; n++) {
+ new (&_values[n]) SQObjectPtr(_class->_defaultvalues[n].val);
+ }
+ Init(ss);
+}
+
+SQInstance::SQInstance(SQSharedState *ss, SQInstance *i, SQInteger memsize)
+{
+ _memsize = memsize;
+ _class = i->_class;
+ SQUnsignedInteger nvalues = _class->_defaultvalues.size();
+ for(SQUnsignedInteger n = 0; n < nvalues; n++) {
+ new (&_values[n]) SQObjectPtr(i->_values[n]);
+ }
+ Init(ss);
+}
+
+void SQInstance::Finalize()
+{
+ SQUnsignedInteger nvalues = _class->_defaultvalues.size();
+ __ObjRelease(_class);
+ for(SQUnsignedInteger i = 0; i < nvalues; i++) {
+ _values[i] = _null_;
+ }
+}
+
+SQInstance::~SQInstance()
+{
+ REMOVE_FROM_CHAIN(&_sharedstate->_gc_chain, this);
+ if(_class){ Finalize(); } //if _class is null it was already finalized by the GC
+}
+
+bool SQInstance::GetMetaMethod(SQVM *v,SQMetaMethod mm,SQObjectPtr &res)
+{
+ if(type(_class->_metamethods[mm]) != OT_NULL) {
+ res = _class->_metamethods[mm];
+ return true;
+ }
+ return false;
+}
+
+bool SQInstance::InstanceOf(SQClass *trg)
+{
+ SQClass *parent = _class;
+ while(parent != NULL) {
+ if(parent == trg)
+ return true;
+ parent = parent->_base;
+ }
+ return false;
+}
--- /dev/null
+/* see copyright notice in squirrel.h */
+#ifndef _SQCLASS_H_
+#define _SQCLASS_H_
+
+struct SQInstance;
+
+struct SQClassMember {
+ SQClassMember(){}
+ SQClassMember(const SQClassMember &o) {
+ val = o.val;
+ attrs = o.attrs;
+ }
+ SQObjectPtr val;
+ SQObjectPtr attrs;
+};
+
+typedef sqvector<SQClassMember> SQClassMemberVec;
+
+#define MEMBER_TYPE_METHOD 0x01000000
+#define MEMBER_TYPE_FIELD 0x02000000
+
+#define _ismethod(o) (_integer(o)&MEMBER_TYPE_METHOD)
+#define _isfield(o) (_integer(o)&MEMBER_TYPE_FIELD)
+#define _make_method_idx(i) ((SQInteger)(MEMBER_TYPE_METHOD|i))
+#define _make_field_idx(i) ((SQInteger)(MEMBER_TYPE_FIELD|i))
+#define _member_type(o) (_integer(o)&0xFF000000)
+#define _member_idx(o) (_integer(o)&0x00FFFFFF)
+
+struct SQClass : public CHAINABLE_OBJ
+{
+ SQClass(SQSharedState *ss,SQClass *base);
+public:
+ static SQClass* Create(SQSharedState *ss,SQClass *base) {
+ SQClass *newclass = (SQClass *)SQ_MALLOC(sizeof(SQClass));
+ new (newclass) SQClass(ss, base);
+ return newclass;
+ }
+ ~SQClass();
+ bool NewSlot(SQSharedState *ss, const SQObjectPtr &key,const SQObjectPtr &val,bool bstatic);
+ bool Get(const SQObjectPtr &key,SQObjectPtr &val) {
+ if(_members->Get(key,val)) {
+ if(_isfield(val)) {
+ SQObjectPtr &o = _defaultvalues[_member_idx(val)].val;
+ val = _realval(o);
+ }
+ else {
+ val = _methods[_member_idx(val)].val;
+ }
+ return true;
+ }
+ return false;
+ }
+ bool SetAttributes(const SQObjectPtr &key,const SQObjectPtr &val);
+ bool GetAttributes(const SQObjectPtr &key,SQObjectPtr &outval);
+ void Lock() { _locked = true; if(_base) _base->Lock(); }
+ void Release() {
+ if (_hook) { _hook(_typetag,0);}
+ sq_delete(this, SQClass);
+ }
+ void Finalize();
+#ifndef NO_GARBAGE_COLLECTOR
+ void Mark(SQCollectable ** );
+#endif
+ SQInteger Next(const SQObjectPtr &refpos, SQObjectPtr &outkey, SQObjectPtr &outval);
+ SQInstance *CreateInstance();
+ SQTable *_members;
+ SQClass *_base;
+ SQClassMemberVec _defaultvalues;
+ SQClassMemberVec _methods;
+ SQObjectPtrVec _metamethods;
+ SQObjectPtr _attributes;
+ SQUserPointer _typetag;
+ SQRELEASEHOOK _hook;
+ bool _locked;
+ SQInteger _udsize;
+};
+
+#define calcinstancesize(_theclass_) \
+ (_theclass_->_udsize + sizeof(SQInstance) + (sizeof(SQObjectPtr)*(_theclass_->_defaultvalues.size()>0?_theclass_->_defaultvalues.size()-1:0)))
+
+struct SQInstance : public SQDelegable
+{
+ void Init(SQSharedState *ss);
+ SQInstance(SQSharedState *ss, SQClass *c, SQInteger memsize);
+ SQInstance(SQSharedState *ss, SQInstance *c, SQInteger memsize);
+public:
+ static SQInstance* Create(SQSharedState *ss,SQClass *theclass) {
+
+ SQInteger size = calcinstancesize(theclass);
+ SQInstance *newinst = (SQInstance *)SQ_MALLOC(size);
+ new (newinst) SQInstance(ss, theclass,size);
+ if(theclass->_udsize) {
+ newinst->_userpointer = ((unsigned char *)newinst) + (size - theclass->_udsize);
+ }
+ return newinst;
+ }
+ SQInstance *Clone(SQSharedState *ss)
+ {
+ SQInteger size = calcinstancesize(_class);
+ SQInstance *newinst = (SQInstance *)SQ_MALLOC(size);
+ new (newinst) SQInstance(ss, this,size);
+ if(_class->_udsize) {
+ newinst->_userpointer = ((unsigned char *)newinst) + (size - _class->_udsize);
+ }
+ return newinst;
+ }
+ ~SQInstance();
+ bool Get(const SQObjectPtr &key,SQObjectPtr &val) {
+ if(_class->_members->Get(key,val)) {
+ if(_isfield(val)) {
+ SQObjectPtr &o = _values[_member_idx(val)];
+ val = _realval(o);
+ }
+ else {
+ val = _class->_methods[_member_idx(val)].val;
+ }
+ return true;
+ }
+ return false;
+ }
+ bool Set(const SQObjectPtr &key,const SQObjectPtr &val) {
+ SQObjectPtr idx;
+ if(_class->_members->Get(key,idx) && _isfield(idx)) {
+ _values[_member_idx(idx)] = val;
+ return true;
+ }
+ return false;
+ }
+ void Release() {
+ _uiRef++;
+ if (_hook) { _hook(_userpointer,0);}
+ _uiRef--;
+ if(_uiRef > 0) return;
+ SQInteger size = _memsize;
+ this->~SQInstance();
+ SQ_FREE(this, size);
+ }
+ void Finalize();
+#ifndef NO_GARBAGE_COLLECTOR
+ void Mark(SQCollectable ** );
+#endif
+ bool InstanceOf(SQClass *trg);
+ bool GetMetaMethod(SQVM *v,SQMetaMethod mm,SQObjectPtr &res);
+
+ SQClass *_class;
+ SQUserPointer _userpointer;
+ SQRELEASEHOOK _hook;
+ SQInteger _memsize;
+ SQObjectPtr _values[1];
+};
+
+#endif //_SQCLASS_H_
--- /dev/null
+/* see copyright notice in squirrel.h */
+#ifndef _SQCLOSURE_H_
+#define _SQCLOSURE_H_
+
+struct SQFunctionProto;
+
+struct SQClosure : public CHAINABLE_OBJ
+{
+private:
+ SQClosure(SQSharedState *ss,SQFunctionProto *func){_function=func; INIT_CHAIN();ADD_TO_CHAIN(&_ss(this)->_gc_chain,this);}
+public:
+ static SQClosure *Create(SQSharedState *ss,SQFunctionProto *func){
+ SQClosure *nc=(SQClosure*)SQ_MALLOC(sizeof(SQClosure));
+ new (nc) SQClosure(ss,func);
+ return nc;
+ }
+ void Release(){
+ sq_delete(this,SQClosure);
+ }
+ SQClosure *Clone()
+ {
+ SQClosure * ret = SQClosure::Create(_opt_ss(this),_funcproto(_function));
+ ret->_env = _env;
+ ret->_outervalues.copy(_outervalues);
+ return ret;
+ }
+ ~SQClosure()
+ {
+ REMOVE_FROM_CHAIN(&_ss(this)->_gc_chain,this);
+ }
+ bool Save(SQVM *v,SQUserPointer up,SQWRITEFUNC write);
+ static bool Load(SQVM *v,SQUserPointer up,SQREADFUNC read,SQObjectPtr &ret);
+#ifndef NO_GARBAGE_COLLECTOR
+ void Mark(SQCollectable **chain);
+ void Finalize(){_outervalues.resize(0); }
+#endif
+ SQObjectPtr _env;
+ SQObjectPtr _function;
+ SQObjectPtrVec _outervalues;
+};
+//////////////////////////////////////////////
+struct SQGenerator : public CHAINABLE_OBJ
+{
+ enum SQGeneratorState{eRunning,eSuspended,eDead};
+private:
+ SQGenerator(SQSharedState *ss,SQClosure *closure){_closure=closure;_state=eRunning;_ci._generator=_null_;INIT_CHAIN();ADD_TO_CHAIN(&_ss(this)->_gc_chain,this);}
+public:
+ static SQGenerator *Create(SQSharedState *ss,SQClosure *closure){
+ SQGenerator *nc=(SQGenerator*)SQ_MALLOC(sizeof(SQGenerator));
+ new (nc) SQGenerator(ss,closure);
+ return nc;
+ }
+ ~SQGenerator()
+ {
+ REMOVE_FROM_CHAIN(&_ss(this)->_gc_chain,this);
+ }
+ void Kill(){
+ _state=eDead;
+ _stack.resize(0);
+ _closure=_null_;}
+ void Release(){
+ sq_delete(this,SQGenerator);
+ }
+ bool Yield(SQVM *v);
+ bool Resume(SQVM *v,SQInteger target);
+#ifndef NO_GARBAGE_COLLECTOR
+ void Mark(SQCollectable **chain);
+ void Finalize(){_stack.resize(0);_closure=_null_;}
+#endif
+ SQObjectPtr _closure;
+ SQObjectPtrVec _stack;
+ SQObjectPtrVec _vargsstack;
+ SQVM::CallInfo _ci;
+ ExceptionsTraps _etraps;
+ SQGeneratorState _state;
+};
+
+struct SQNativeClosure : public CHAINABLE_OBJ
+{
+private:
+ SQNativeClosure(SQSharedState *ss,SQFUNCTION func){_function=func;INIT_CHAIN();ADD_TO_CHAIN(&_ss(this)->_gc_chain,this); }
+public:
+ static SQNativeClosure *Create(SQSharedState *ss,SQFUNCTION func)
+ {
+ SQNativeClosure *nc=(SQNativeClosure*)SQ_MALLOC(sizeof(SQNativeClosure));
+ new (nc) SQNativeClosure(ss,func);
+ return nc;
+ }
+ SQNativeClosure *Clone()
+ {
+ SQNativeClosure * ret = SQNativeClosure::Create(_opt_ss(this),_function);
+ ret->_env = _env;
+ ret->_name = _name;
+ ret->_outervalues.copy(_outervalues);
+ ret->_typecheck.copy(_typecheck);
+ ret->_nparamscheck = _nparamscheck;
+ return ret;
+ }
+ ~SQNativeClosure()
+ {
+ REMOVE_FROM_CHAIN(&_ss(this)->_gc_chain,this);
+ }
+ void Release(){
+ sq_delete(this,SQNativeClosure);
+ }
+#ifndef NO_GARBAGE_COLLECTOR
+ void Mark(SQCollectable **chain);
+ void Finalize(){_outervalues.resize(0);}
+#endif
+ SQInteger _nparamscheck;
+ SQIntVec _typecheck;
+ SQObjectPtrVec _outervalues;
+ SQObjectPtr _env;
+ SQFUNCTION _function;
+ SQObjectPtr _name;
+};
+
+
+
+#endif //_SQCLOSURE_H_
--- /dev/null
+/*
+ see copyright notice in squirrel.h
+*/
+#include "sqpcheader.h"
+#include <stdarg.h>
+#include <setjmp.h>
+#include "sqopcodes.h"
+#include "sqstring.h"
+#include "sqfuncproto.h"
+#include "sqcompiler.h"
+#include "sqfuncstate.h"
+#include "sqlexer.h"
+#include "sqvm.h"
+
+#define DEREF_NO_DEREF -1
+#define DEREF_FIELD -2
+
+struct ExpState
+{
+ ExpState()
+ {
+ _deref = DEREF_NO_DEREF;
+ _freevar = false;
+ _class_or_delete = false;
+ _funcarg = false;
+ }
+ bool _class_or_delete;
+ bool _funcarg;
+ bool _freevar;
+ SQInteger _deref;
+};
+
+typedef sqvector<ExpState> ExpStateVec;
+
+#define _exst (_expstates.top())
+
+#define BEGIN_BREAKBLE_BLOCK() SQInteger __nbreaks__=_fs->_unresolvedbreaks.size(); \
+ SQInteger __ncontinues__=_fs->_unresolvedcontinues.size(); \
+ _fs->_breaktargets.push_back(0);_fs->_continuetargets.push_back(0);
+
+#define END_BREAKBLE_BLOCK(continue_target) {__nbreaks__=_fs->_unresolvedbreaks.size()-__nbreaks__; \
+ __ncontinues__=_fs->_unresolvedcontinues.size()-__ncontinues__; \
+ if(__ncontinues__>0)ResolveContinues(_fs,__ncontinues__,continue_target); \
+ if(__nbreaks__>0)ResolveBreaks(_fs,__nbreaks__); \
+ _fs->_breaktargets.pop_back();_fs->_continuetargets.pop_back();}
+
+class SQCompiler
+{
+public:
+ SQCompiler(SQVM *v, SQLEXREADFUNC rg, SQUserPointer up, const SQChar* sourcename, bool raiseerror, bool lineinfo)
+ {
+ _vm=v;
+ _lex.Init(_ss(v), rg, up,ThrowError,this);
+ _sourcename = SQString::Create(_ss(v), sourcename);
+ _lineinfo = lineinfo;_raiseerror = raiseerror;
+ compilererror = NULL;
+ }
+ static void ThrowError(void *ud, const SQChar *s) {
+ SQCompiler *c = (SQCompiler *)ud;
+ c->Error(s);
+ }
+ void Error(const SQChar *s, ...)
+ {
+ static SQChar temp[256];
+ va_list vl;
+ va_start(vl, s);
+ scvsprintf(temp, s, vl);
+ va_end(vl);
+ compilererror = temp;
+ longjmp(_errorjmp,1);
+ }
+ void Lex(){ _token = _lex.Lex();}
+ void PushExpState(){ _expstates.push_back(ExpState()); }
+ bool IsDerefToken(SQInteger tok)
+ {
+ switch(tok){
+ case _SC('='): case _SC('('): case TK_NEWSLOT:
+ case TK_MODEQ: case TK_MULEQ: case TK_DIVEQ: case TK_MINUSEQ: case TK_PLUSEQ: case TK_PLUSPLUS: case TK_MINUSMINUS: return true;
+ }
+ return false;
+ }
+ ExpState PopExpState()
+ {
+ ExpState ret = _expstates.top();
+ _expstates.pop_back();
+ return ret;
+ }
+ SQObject Expect(SQInteger tok)
+ {
+
+ if(_token != tok) {
+ if(_token == TK_CONSTRUCTOR && tok == TK_IDENTIFIER) {
+ //ret = SQString::Create(_ss(_vm),_SC("constructor"));
+ //do nothing
+ }
+ else {
+ const SQChar *etypename;
+ if(tok > 255) {
+ switch(tok)
+ {
+ case TK_IDENTIFIER:
+ etypename = _SC("IDENTIFIER");
+ break;
+ case TK_STRING_LITERAL:
+ etypename = _SC("STRING_LITERAL");
+ break;
+ case TK_INTEGER:
+ etypename = _SC("INTEGER");
+ break;
+ case TK_FLOAT:
+ etypename = _SC("FLOAT");
+ break;
+ default:
+ etypename = _lex.Tok2Str(tok);
+ }
+ Error(_SC("expected '%s'"), etypename);
+ }
+ Error(_SC("expected '%c'"), tok);
+ }
+ }
+ SQObjectPtr ret;
+ switch(tok)
+ {
+ case TK_IDENTIFIER:
+ ret = _fs->CreateString(_lex._svalue);
+ break;
+ case TK_STRING_LITERAL:
+ ret = _fs->CreateString(_lex._svalue,_lex._longstr.size()-1);
+ break;
+ case TK_INTEGER:
+ ret = SQObjectPtr(_lex._nvalue);
+ break;
+ case TK_FLOAT:
+ ret = SQObjectPtr(_lex._fvalue);
+ break;
+ }
+ Lex();
+ return ret;
+ }
+ bool IsEndOfStatement() { return ((_lex._prevtoken == _SC('\n')) || (_token == SQUIRREL_EOB) || (_token == _SC('}')) || (_token == _SC(';'))); }
+ void OptionalSemicolon()
+ {
+ if(_token == _SC(';')) { Lex(); return; }
+ if(!IsEndOfStatement()) {
+ Error(_SC("end of statement expected (; or lf)"));
+ }
+ }
+ void MoveIfCurrentTargetIsLocal() {
+ SQInteger trg = _fs->TopTarget();
+ if(_fs->IsLocal(trg)) {
+ trg = _fs->PopTarget(); //no pops the target and move it
+ _fs->AddInstruction(_OP_MOVE, _fs->PushTarget(), trg);
+ }
+ }
+ bool Compile(SQObjectPtr &o)
+ {
+ _debugline = 1;
+ _debugop = 0;
+
+ SQFuncState funcstate(_ss(_vm), NULL,ThrowError,this);
+ funcstate._name = SQString::Create(_ss(_vm), _SC("main"));
+ _fs = &funcstate;
+ _fs->AddParameter(_fs->CreateString(_SC("this")));
+ _fs->_sourcename = _sourcename;
+ SQInteger stacksize = _fs->GetStackSize();
+ if(setjmp(_errorjmp) == 0) {
+ Lex();
+ while(_token > 0){
+ Statement();
+ if(_lex._prevtoken != _SC('}')) OptionalSemicolon();
+ }
+ CleanStack(stacksize);
+ _fs->AddLineInfos(_lex._currentline, _lineinfo, true);
+ _fs->AddInstruction(_OP_RETURN, 0xFF);
+ _fs->SetStackSize(0);
+ o =_fs->BuildProto();
+#ifdef _DEBUG_DUMP
+ _fs->Dump(_funcproto(o));
+#endif
+ }
+ else {
+ if(_raiseerror && _ss(_vm)->_compilererrorhandler) {
+ _ss(_vm)->_compilererrorhandler(_vm, compilererror, type(_sourcename) == OT_STRING?_stringval(_sourcename):_SC("unknown"),
+ _lex._currentline, _lex._currentcolumn);
+ }
+ _vm->_lasterror = SQString::Create(_ss(_vm), compilererror, -1);
+ return false;
+ }
+ return true;
+ }
+ void Statements()
+ {
+ while(_token != _SC('}') && _token != TK_DEFAULT && _token != TK_CASE) {
+ Statement();
+ if(_lex._prevtoken != _SC('}') && _lex._prevtoken != _SC(';')) OptionalSemicolon();
+ }
+ }
+ void Statement()
+ {
+ _fs->AddLineInfos(_lex._currentline, _lineinfo);
+ switch(_token){
+ case _SC(';'): Lex(); break;
+ case TK_IF: IfStatement(); break;
+ case TK_WHILE: WhileStatement(); break;
+ case TK_DO: DoWhileStatement(); break;
+ case TK_FOR: ForStatement(); break;
+ case TK_FOREACH: ForEachStatement(); break;
+ case TK_SWITCH: SwitchStatement(); break;
+ case TK_LOCAL: LocalDeclStatement(); break;
+ case TK_RETURN:
+ case TK_YIELD: {
+ SQOpcode op;
+ if(_token == TK_RETURN) {
+ op = _OP_RETURN;
+
+ }
+ else {
+ op = _OP_YIELD;
+ _fs->_bgenerator = true;
+ }
+ Lex();
+ if(!IsEndOfStatement()) {
+ SQInteger retexp = _fs->GetCurrentPos()+1;
+ CommaExpr();
+ if(op == _OP_RETURN && _fs->_traps > 0)
+ _fs->AddInstruction(_OP_POPTRAP, _fs->_traps, 0);
+ _fs->_returnexp = retexp;
+ _fs->AddInstruction(op, 1, _fs->PopTarget());
+ }
+ else{
+ if(op == _OP_RETURN && _fs->_traps > 0)
+ _fs->AddInstruction(_OP_POPTRAP, _fs->_traps ,0);
+ _fs->_returnexp = -1;
+ _fs->AddInstruction(op, 0xFF);
+ }
+ break;}
+ case TK_BREAK:
+ if(_fs->_breaktargets.size() <= 0)Error(_SC("'break' has to be in a loop block"));
+ if(_fs->_breaktargets.top() > 0){
+ _fs->AddInstruction(_OP_POPTRAP, _fs->_breaktargets.top(), 0);
+ }
+ _fs->AddInstruction(_OP_JMP, 0, -1234);
+ _fs->_unresolvedbreaks.push_back(_fs->GetCurrentPos());
+ Lex();
+ break;
+ case TK_CONTINUE:
+ if(_fs->_continuetargets.size() <= 0)Error(_SC("'continue' has to be in a loop block"));
+ if(_fs->_continuetargets.top() > 0) {
+ _fs->AddInstruction(_OP_POPTRAP, _fs->_continuetargets.top(), 0);
+ }
+ _fs->AddInstruction(_OP_JMP, 0, -1234);
+ _fs->_unresolvedcontinues.push_back(_fs->GetCurrentPos());
+ Lex();
+ break;
+ case TK_FUNCTION:
+ FunctionStatement();
+ break;
+ case TK_CLASS:
+ ClassStatement();
+ break;
+ case _SC('{'):{
+ SQInteger stacksize = _fs->GetStackSize();
+ Lex();
+ Statements();
+ Expect(_SC('}'));
+ _fs->SetStackSize(stacksize);
+ }
+ break;
+ case TK_TRY:
+ TryCatchStatement();
+ break;
+ case TK_THROW:
+ Lex();
+ CommaExpr();
+ _fs->AddInstruction(_OP_THROW, _fs->PopTarget());
+ break;
+ default:
+ CommaExpr();
+ _fs->PopTarget();
+ break;
+ }
+ _fs->SnoozeOpt();
+ }
+ void EmitDerefOp(SQOpcode op)
+ {
+ SQInteger val = _fs->PopTarget();
+ SQInteger key = _fs->PopTarget();
+ SQInteger src = _fs->PopTarget();
+ _fs->AddInstruction(op,_fs->PushTarget(),src,key,val);
+ }
+ void Emit2ArgsOP(SQOpcode op, SQInteger p3 = 0)
+ {
+ SQInteger p2 = _fs->PopTarget(); //src in OP_GET
+ SQInteger p1 = _fs->PopTarget(); //key in OP_GET
+ _fs->AddInstruction(op,_fs->PushTarget(), p1, p2, p3);
+ }
+ void EmitCompoundArith(SQInteger tok,bool deref)
+ {
+ SQInteger oper;
+ switch(tok){
+ case TK_MINUSEQ: oper = '-'; break;
+ case TK_PLUSEQ: oper = '+'; break;
+ case TK_MULEQ: oper = '*'; break;
+ case TK_DIVEQ: oper = '/'; break;
+ case TK_MODEQ: oper = '%'; break;
+ default: oper = 0; //shut up compiler
+ assert(0); break;
+ };
+ if(deref) {
+ SQInteger val = _fs->PopTarget();
+ SQInteger key = _fs->PopTarget();
+ SQInteger src = _fs->PopTarget();
+ //mixes dest obj and source val in the arg1(hack?)
+ _fs->AddInstruction(_OP_COMPARITH,_fs->PushTarget(),(src<<16)|val,key,oper);
+ }
+ else {
+ Emit2ArgsOP(_OP_COMPARITHL, oper);
+ }
+ }
+ void CommaExpr()
+ {
+ for(Expression();_token == ',';_fs->PopTarget(), Lex(), CommaExpr());
+ }
+ ExpState Expression(bool funcarg = false)
+ {
+ PushExpState();
+ _exst._class_or_delete = false;
+ _exst._funcarg = funcarg;
+ LogicalOrExp();
+ switch(_token) {
+ case _SC('='):
+ case TK_NEWSLOT:
+ case TK_MINUSEQ:
+ case TK_PLUSEQ:
+ case TK_MULEQ:
+ case TK_DIVEQ:
+ case TK_MODEQ:
+ {
+ SQInteger op = _token;
+ SQInteger ds = _exst._deref;
+ bool freevar = _exst._freevar;
+ if(ds == DEREF_NO_DEREF) Error(_SC("can't assign expression"));
+ Lex(); Expression();
+
+ switch(op){
+ case TK_NEWSLOT:
+ if(freevar) Error(_SC("free variables cannot be modified"));
+ if(ds == DEREF_FIELD)
+ EmitDerefOp(_OP_NEWSLOT);
+ else //if _derefstate != DEREF_NO_DEREF && DEREF_FIELD so is the index of a local
+ Error(_SC("can't 'create' a local slot"));
+ break;
+ case _SC('='): //ASSIGN
+ if(freevar) Error(_SC("free variables cannot be modified"));
+ if(ds == DEREF_FIELD)
+ EmitDerefOp(_OP_SET);
+ else {//if _derefstate != DEREF_NO_DEREF && DEREF_FIELD so is the index of a local
+ SQInteger p2 = _fs->PopTarget(); //src in OP_GET
+ SQInteger p1 = _fs->TopTarget(); //key in OP_GET
+ _fs->AddInstruction(_OP_MOVE, p1, p2);
+ }
+ break;
+ case TK_MINUSEQ:
+ case TK_PLUSEQ:
+ case TK_MULEQ:
+ case TK_DIVEQ:
+ case TK_MODEQ:
+ EmitCompoundArith(op,ds == DEREF_FIELD);
+ break;
+ }
+ }
+ break;
+ case _SC('?'): {
+ Lex();
+ _fs->AddInstruction(_OP_JZ, _fs->PopTarget());
+ SQInteger jzpos = _fs->GetCurrentPos();
+ SQInteger trg = _fs->PushTarget();
+ Expression();
+ SQInteger first_exp = _fs->PopTarget();
+ if(trg != first_exp) _fs->AddInstruction(_OP_MOVE, trg, first_exp);
+ SQInteger endfirstexp = _fs->GetCurrentPos();
+ _fs->AddInstruction(_OP_JMP, 0, 0);
+ Expect(_SC(':'));
+ SQInteger jmppos = _fs->GetCurrentPos();
+ Expression();
+ SQInteger second_exp = _fs->PopTarget();
+ if(trg != second_exp) _fs->AddInstruction(_OP_MOVE, trg, second_exp);
+ _fs->SetIntructionParam(jmppos, 1, _fs->GetCurrentPos() - jmppos);
+ _fs->SetIntructionParam(jzpos, 1, endfirstexp - jzpos + 1);
+ _fs->SnoozeOpt();
+ }
+ break;
+ }
+ return PopExpState();
+ }
+ void BIN_EXP(SQOpcode op, void (SQCompiler::*f)(void),SQInteger op3 = 0)
+ {
+ Lex(); (this->*f)();
+ SQInteger op1 = _fs->PopTarget();SQInteger op2 = _fs->PopTarget();
+ _fs->AddInstruction(op, _fs->PushTarget(), op1, op2, op3);
+ }
+ void LogicalOrExp()
+ {
+ LogicalAndExp();
+ for(;;) if(_token == TK_OR) {
+ SQInteger first_exp = _fs->PopTarget();
+ SQInteger trg = _fs->PushTarget();
+ _fs->AddInstruction(_OP_OR, trg, 0, first_exp, 0);
+ SQInteger jpos = _fs->GetCurrentPos();
+ if(trg != first_exp) _fs->AddInstruction(_OP_MOVE, trg, first_exp);
+ Lex(); LogicalOrExp();
+ _fs->SnoozeOpt();
+ SQInteger second_exp = _fs->PopTarget();
+ if(trg != second_exp) _fs->AddInstruction(_OP_MOVE, trg, second_exp);
+ _fs->SnoozeOpt();
+ _fs->SetIntructionParam(jpos, 1, (_fs->GetCurrentPos() - jpos));
+ break;
+ }else return;
+ }
+ void LogicalAndExp()
+ {
+ BitwiseOrExp();
+ for(;;) switch(_token) {
+ case TK_AND: {
+ SQInteger first_exp = _fs->PopTarget();
+ SQInteger trg = _fs->PushTarget();
+ _fs->AddInstruction(_OP_AND, trg, 0, first_exp, 0);
+ SQInteger jpos = _fs->GetCurrentPos();
+ if(trg != first_exp) _fs->AddInstruction(_OP_MOVE, trg, first_exp);
+ Lex(); LogicalAndExp();
+ _fs->SnoozeOpt();
+ SQInteger second_exp = _fs->PopTarget();
+ if(trg != second_exp) _fs->AddInstruction(_OP_MOVE, trg, second_exp);
+ _fs->SnoozeOpt();
+ _fs->SetIntructionParam(jpos, 1, (_fs->GetCurrentPos() - jpos));
+ break;
+ }
+ case TK_IN: BIN_EXP(_OP_EXISTS, &SQCompiler::BitwiseOrExp); break;
+ case TK_INSTANCEOF: BIN_EXP(_OP_INSTANCEOF, &SQCompiler::BitwiseOrExp); break;
+ default:
+ return;
+ }
+ }
+ void BitwiseOrExp()
+ {
+ BitwiseXorExp();
+ for(;;) if(_token == _SC('|'))
+ {BIN_EXP(_OP_BITW, &SQCompiler::BitwiseXorExp,BW_OR);
+ }else return;
+ }
+ void BitwiseXorExp()
+ {
+ BitwiseAndExp();
+ for(;;) if(_token == _SC('^'))
+ {BIN_EXP(_OP_BITW, &SQCompiler::BitwiseAndExp,BW_XOR);
+ }else return;
+ }
+ void BitwiseAndExp()
+ {
+ CompExp();
+ for(;;) if(_token == _SC('&'))
+ {BIN_EXP(_OP_BITW, &SQCompiler::CompExp,BW_AND);
+ }else return;
+ }
+ void CompExp()
+ {
+ ShiftExp();
+ for(;;) switch(_token) {
+ case TK_EQ: BIN_EXP(_OP_EQ, &SQCompiler::ShiftExp); break;
+ case _SC('>'): BIN_EXP(_OP_CMP, &SQCompiler::ShiftExp,CMP_G); break;
+ case _SC('<'): BIN_EXP(_OP_CMP, &SQCompiler::ShiftExp,CMP_L); break;
+ case TK_GE: BIN_EXP(_OP_CMP, &SQCompiler::ShiftExp,CMP_GE); break;
+ case TK_LE: BIN_EXP(_OP_CMP, &SQCompiler::ShiftExp,CMP_LE); break;
+ case TK_NE: BIN_EXP(_OP_NE, &SQCompiler::ShiftExp); break;
+ default: return;
+ }
+ }
+ void ShiftExp()
+ {
+ PlusExp();
+ for(;;) switch(_token) {
+ case TK_USHIFTR: BIN_EXP(_OP_BITW, &SQCompiler::PlusExp,BW_USHIFTR); break;
+ case TK_SHIFTL: BIN_EXP(_OP_BITW, &SQCompiler::PlusExp,BW_SHIFTL); break;
+ case TK_SHIFTR: BIN_EXP(_OP_BITW, &SQCompiler::PlusExp,BW_SHIFTR); break;
+ default: return;
+ }
+ }
+ void PlusExp()
+ {
+ MultExp();
+ for(;;) switch(_token) {
+ case _SC('+'): case _SC('-'):
+ BIN_EXP(_OP_ARITH, &SQCompiler::MultExp,_token); break;
+ default: return;
+ }
+ }
+
+ void MultExp()
+ {
+ PrefixedExpr();
+ for(;;) switch(_token) {
+ case _SC('*'): case _SC('/'): case _SC('%'):
+ BIN_EXP(_OP_ARITH, &SQCompiler::PrefixedExpr,_token); break;
+ default: return;
+ }
+ }
+ //if 'pos' != -1 the previous variable is a local variable
+ void PrefixedExpr()
+ {
+ SQInteger pos = Factor();
+ for(;;) {
+ switch(_token) {
+ case _SC('.'): {
+ pos = -1;
+ Lex();
+ if(_token == TK_PARENT) {
+ Lex();
+ if(!NeedGet())
+ Error(_SC("parent cannot be set"));
+ SQInteger src = _fs->PopTarget();
+ _fs->AddInstruction(_OP_GETPARENT, _fs->PushTarget(), src);
+ }
+ else {
+ _fs->AddInstruction(_OP_LOAD, _fs->PushTarget(), _fs->GetConstant(Expect(TK_IDENTIFIER)));
+ if(NeedGet()) Emit2ArgsOP(_OP_GET);
+ }
+ _exst._deref = DEREF_FIELD;
+ _exst._freevar = false;
+ }
+ break;
+ case _SC('['):
+ if(_lex._prevtoken == _SC('\n')) Error(_SC("cannot brake deref/or comma needed after [exp]=exp slot declaration"));
+ Lex(); Expression(); Expect(_SC(']'));
+ pos = -1;
+ if(NeedGet()) Emit2ArgsOP(_OP_GET);
+ _exst._deref = DEREF_FIELD;
+ _exst._freevar = false;
+ break;
+ case TK_MINUSMINUS:
+ case TK_PLUSPLUS:
+ if(_exst._deref != DEREF_NO_DEREF && !IsEndOfStatement()) {
+ SQInteger tok = _token; Lex();
+ if(pos < 0)
+ Emit2ArgsOP(_OP_PINC,tok == TK_MINUSMINUS?-1:1);
+ else {//if _derefstate != DEREF_NO_DEREF && DEREF_FIELD so is the index of a local
+ SQInteger src = _fs->PopTarget();
+ _fs->AddInstruction(_OP_PINCL, _fs->PushTarget(), src, 0, tok == TK_MINUSMINUS?-1:1);
+ }
+
+ }
+ return;
+ break;
+ case _SC('('):
+ {
+ if(_exst._deref != DEREF_NO_DEREF) {
+ if(pos<0) {
+ SQInteger key = _fs->PopTarget(); //key
+ SQInteger table = _fs->PopTarget(); //table etc...
+ SQInteger closure = _fs->PushTarget();
+ SQInteger ttarget = _fs->PushTarget();
+ _fs->AddInstruction(_OP_PREPCALL, closure, key, table, ttarget);
+ }
+ else{
+ _fs->AddInstruction(_OP_MOVE, _fs->PushTarget(), 0);
+ }
+ }
+ else
+ _fs->AddInstruction(_OP_MOVE, _fs->PushTarget(), 0);
+ _exst._deref = DEREF_NO_DEREF;
+ Lex();
+ FunctionCallArgs();
+ }
+ break;
+ default: return;
+ }
+ }
+ }
+ SQInteger Factor()
+ {
+ switch(_token)
+ {
+ case TK_STRING_LITERAL: {
+ //SQObjectPtr id(SQString::Create(_ss(_vm), _lex._svalue,_lex._longstr.size()-1));
+ _fs->AddInstruction(_OP_LOAD, _fs->PushTarget(), _fs->GetConstant(_fs->CreateString(_lex._svalue,_lex._longstr.size()-1)));
+ Lex();
+ }
+ break;
+ case TK_VARGC: Lex(); _fs->AddInstruction(_OP_VARGC, _fs->PushTarget()); break;
+ case TK_VARGV: { Lex();
+ Expect(_SC('['));
+ Expression();
+ Expect(_SC(']'));
+ SQInteger src = _fs->PopTarget();
+ _fs->AddInstruction(_OP_GETVARGV, _fs->PushTarget(), src);
+ }
+ break;
+ case TK_IDENTIFIER:
+ case TK_CONSTRUCTOR:
+ case TK_THIS:{
+ _exst._freevar = false;
+ SQObject id;
+ switch(_token) {
+ case TK_IDENTIFIER: id = _fs->CreateString(_lex._svalue); break;
+ case TK_THIS: id = _fs->CreateString(_SC("this")); break;
+ case TK_CONSTRUCTOR: id = _fs->CreateString(_SC("constructor")); break;
+ }
+ SQInteger pos = -1;
+ Lex();
+ if((pos = _fs->GetLocalVariable(id)) == -1) {
+ //checks if is a free variable
+ if((pos = _fs->GetOuterVariable(id)) != -1) {
+ _exst._deref = _fs->PushTarget();
+ _fs->AddInstruction(_OP_LOADFREEVAR, _exst._deref ,pos);
+ _exst._freevar = true;
+ } else {
+ _fs->PushTarget(0);
+ _fs->AddInstruction(_OP_LOAD, _fs->PushTarget(), _fs->GetConstant(id));
+ if(NeedGet()) Emit2ArgsOP(_OP_GET);
+ _exst._deref = DEREF_FIELD;
+ }
+ }
+ else{
+ _fs->PushTarget(pos);
+ _exst._deref = pos;
+ }
+ return _exst._deref;
+ }
+ break;
+ case TK_PARENT: Lex();_fs->AddInstruction(_OP_GETPARENT, _fs->PushTarget(), 0); break;
+ case TK_DOUBLE_COLON: // "::"
+ _fs->AddInstruction(_OP_LOADROOTTABLE, _fs->PushTarget());
+ _exst._deref = DEREF_FIELD;
+ _token = _SC('.'); //hack
+ return -1;
+ break;
+ case TK_NULL:
+ _fs->AddInstruction(_OP_LOADNULLS, _fs->PushTarget(),1);
+ Lex();
+ break;
+ case TK_INTEGER: {
+ if((_lex._nvalue & (~0x7FFFFFFF)) == 0) { //does it fit in 32 bits?
+ _fs->AddInstruction(_OP_LOADINT, _fs->PushTarget(),_lex._nvalue);
+ }
+ else {
+ _fs->AddInstruction(_OP_LOAD, _fs->PushTarget(), _fs->GetNumericConstant(_lex._nvalue));
+ }
+ Lex();
+ }
+ break;
+ case TK_FLOAT:
+ if(sizeof(SQFloat) == sizeof(SQInt32)) {
+ _fs->AddInstruction(_OP_LOADFLOAT, _fs->PushTarget(),*((SQInt32 *)&_lex._fvalue));
+ }
+ else {
+ _fs->AddInstruction(_OP_LOAD, _fs->PushTarget(), _fs->GetNumericConstant(_lex._fvalue));
+ }
+ Lex();
+ break;
+ case TK_TRUE: case TK_FALSE:
+ _fs->AddInstruction(_OP_LOADBOOL, _fs->PushTarget(),_token == TK_TRUE?1:0);
+ Lex();
+ break;
+ case _SC('['): {
+ _fs->AddInstruction(_OP_NEWARRAY, _fs->PushTarget());
+ SQInteger apos = _fs->GetCurrentPos(),key = 0;
+ Lex();
+ while(_token != _SC(']')) {
+ Expression();
+ if(_token == _SC(',')) Lex();
+ SQInteger val = _fs->PopTarget();
+ SQInteger array = _fs->TopTarget();
+ _fs->AddInstruction(_OP_APPENDARRAY, array, val);
+ key++;
+ }
+ _fs->SetIntructionParam(apos, 1, key);
+ Lex();
+ }
+ break;
+ case _SC('{'):{
+ _fs->AddInstruction(_OP_NEWTABLE, _fs->PushTarget());
+ Lex();ParseTableOrClass(_SC(','));
+ }
+ break;
+ case TK_FUNCTION: FunctionExp(_token);break;
+ case TK_CLASS: Lex(); ClassExp();break;
+ case _SC('-'): UnaryOP(_OP_NEG); break;
+ case _SC('!'): UnaryOP(_OP_NOT); break;
+ case _SC('~'): UnaryOP(_OP_BWNOT); break;
+ case TK_TYPEOF : UnaryOP(_OP_TYPEOF); break;
+ case TK_RESUME : UnaryOP(_OP_RESUME); break;
+ case TK_CLONE : UnaryOP(_OP_CLONE); break;
+ case TK_MINUSMINUS :
+ case TK_PLUSPLUS :PrefixIncDec(_token); break;
+ case TK_DELETE : DeleteExpr(); break;
+ case TK_DELEGATE : DelegateExpr(); break;
+ case _SC('('): Lex(); CommaExpr(); Expect(_SC(')'));
+ break;
+ default: Error(_SC("expression expected"));
+ }
+ return -1;
+ }
+ void UnaryOP(SQOpcode op)
+ {
+ Lex(); PrefixedExpr();
+ SQInteger src = _fs->PopTarget();
+ _fs->AddInstruction(op, _fs->PushTarget(), src);
+ }
+ bool NeedGet()
+ {
+ switch(_token) {
+ case _SC('='): case _SC('('): case TK_NEWSLOT: case TK_PLUSPLUS: case TK_MINUSMINUS:
+ case TK_PLUSEQ: case TK_MINUSEQ: case TK_MULEQ: case TK_DIVEQ: case TK_MODEQ:
+ return false;
+ }
+ return (!_exst._class_or_delete) || (_exst._class_or_delete && (_token == _SC('.') || _token == _SC('[')));
+ }
+
+ void FunctionCallArgs()
+ {
+ SQInteger nargs = 1;//this
+ while(_token != _SC(')')) {
+ Expression(true);
+ MoveIfCurrentTargetIsLocal();
+ nargs++;
+ if(_token == _SC(',')){
+ Lex();
+ if(_token == ')') Error(_SC("expression expected, found ')'"));
+ }
+ }
+ Lex();
+ for(SQInteger i = 0; i < (nargs - 1); i++) _fs->PopTarget();
+ SQInteger stackbase = _fs->PopTarget();
+ SQInteger closure = _fs->PopTarget();
+ _fs->AddInstruction(_OP_CALL, _fs->PushTarget(), closure, stackbase, nargs);
+ }
+ void ParseTableOrClass(SQInteger separator,SQInteger terminator = '}')
+ {
+ SQInteger tpos = _fs->GetCurrentPos(),nkeys = 0;
+
+ while(_token != terminator) {
+ bool hasattrs = false;
+ bool isstatic = false;
+ //check if is an attribute
+ if(separator == ';') {
+ if(_token == TK_ATTR_OPEN) {
+ _fs->AddInstruction(_OP_NEWTABLE, _fs->PushTarget()); Lex();
+ ParseTableOrClass(',',TK_ATTR_CLOSE);
+ hasattrs = true;
+ }
+ if(_token == TK_STATIC) {
+ isstatic = true;
+ Lex();
+ }
+ }
+ switch(_token) {
+ case TK_FUNCTION:
+ case TK_CONSTRUCTOR:{
+ SQInteger tk = _token;
+ Lex();
+ SQObject id = tk == TK_FUNCTION ? Expect(TK_IDENTIFIER) : _fs->CreateString(_SC("constructor"));
+ Expect(_SC('('));
+ _fs->AddInstruction(_OP_LOAD, _fs->PushTarget(), _fs->GetConstant(id));
+ CreateFunction(id);
+ _fs->AddInstruction(_OP_CLOSURE, _fs->PushTarget(), _fs->_functions.size() - 1, 0);
+ }
+ break;
+ case _SC('['):
+ Lex(); CommaExpr(); Expect(_SC(']'));
+ Expect(_SC('=')); Expression();
+ break;
+ default :
+ _fs->AddInstruction(_OP_LOAD, _fs->PushTarget(), _fs->GetConstant(Expect(TK_IDENTIFIER)));
+ Expect(_SC('=')); Expression();
+ }
+
+ if(_token == separator) Lex();//optional comma/semicolon
+ nkeys++;
+ SQInteger val = _fs->PopTarget();
+ SQInteger key = _fs->PopTarget();
+ SQInteger attrs = hasattrs ? _fs->PopTarget():-1;
+ assert(hasattrs && attrs == key-1 || !hasattrs);
+ unsigned char flags = (hasattrs?NEW_SLOT_ATTRIBUTES_FLAG:0)|(isstatic?NEW_SLOT_STATIC_FLAG:0);
+ SQInteger table = _fs->TopTarget(); //<<BECAUSE OF THIS NO COMMON EMIT FUNC IS POSSIBLE
+ _fs->AddInstruction(_OP_NEWSLOTA, flags, table, key, val);
+ //_fs->PopTarget();
+ }
+ if(separator == _SC(',')) //hack recognizes a table from the separator
+ _fs->SetIntructionParam(tpos, 1, nkeys);
+ Lex();
+ }
+ void LocalDeclStatement()
+ {
+ SQObject varname;
+ do {
+ Lex(); varname = Expect(TK_IDENTIFIER);
+ if(_token == _SC('=')) {
+ Lex(); Expression();
+ SQInteger src = _fs->PopTarget();
+ SQInteger dest = _fs->PushTarget();
+ if(dest != src) _fs->AddInstruction(_OP_MOVE, dest, src);
+ }
+ else{
+ _fs->AddInstruction(_OP_LOADNULLS, _fs->PushTarget(),1);
+ }
+ _fs->PopTarget();
+ _fs->PushLocalVariable(varname);
+
+ } while(_token == _SC(','));
+ }
+ void IfStatement()
+ {
+ SQInteger jmppos;
+ bool haselse = false;
+ Lex(); Expect(_SC('(')); CommaExpr(); Expect(_SC(')'));
+ _fs->AddInstruction(_OP_JZ, _fs->PopTarget());
+ SQInteger jnepos = _fs->GetCurrentPos();
+ SQInteger stacksize = _fs->GetStackSize();
+
+ Statement();
+ //
+ if(_token != _SC('}') && _token != TK_ELSE) OptionalSemicolon();
+
+ CleanStack(stacksize);
+ SQInteger endifblock = _fs->GetCurrentPos();
+ if(_token == TK_ELSE){
+ haselse = true;
+ stacksize = _fs->GetStackSize();
+ _fs->AddInstruction(_OP_JMP);
+ jmppos = _fs->GetCurrentPos();
+ Lex();
+ Statement(); OptionalSemicolon();
+ CleanStack(stacksize);
+ _fs->SetIntructionParam(jmppos, 1, _fs->GetCurrentPos() - jmppos);
+ }
+ _fs->SetIntructionParam(jnepos, 1, endifblock - jnepos + (haselse?1:0));
+ }
+ void WhileStatement()
+ {
+ SQInteger jzpos, jmppos;
+ SQInteger stacksize = _fs->GetStackSize();
+ jmppos = _fs->GetCurrentPos();
+ Lex(); Expect(_SC('(')); CommaExpr(); Expect(_SC(')'));
+
+ BEGIN_BREAKBLE_BLOCK();
+ _fs->AddInstruction(_OP_JZ, _fs->PopTarget());
+ jzpos = _fs->GetCurrentPos();
+ stacksize = _fs->GetStackSize();
+
+ Statement();
+
+ CleanStack(stacksize);
+ _fs->AddInstruction(_OP_JMP, 0, jmppos - _fs->GetCurrentPos() - 1);
+ _fs->SetIntructionParam(jzpos, 1, _fs->GetCurrentPos() - jzpos);
+
+ END_BREAKBLE_BLOCK(jmppos);
+ }
+ void DoWhileStatement()
+ {
+ Lex();
+ SQInteger jzpos = _fs->GetCurrentPos();
+ SQInteger stacksize = _fs->GetStackSize();
+ BEGIN_BREAKBLE_BLOCK()
+ Statement();
+ CleanStack(stacksize);
+ Expect(TK_WHILE);
+ SQInteger continuetrg = _fs->GetCurrentPos();
+ Expect(_SC('(')); CommaExpr(); Expect(_SC(')'));
+ _fs->AddInstruction(_OP_JNZ, _fs->PopTarget(), jzpos - _fs->GetCurrentPos() - 1);
+ END_BREAKBLE_BLOCK(continuetrg);
+ }
+ void ForStatement()
+ {
+ Lex();
+ SQInteger stacksize = _fs->GetStackSize();
+ Expect(_SC('('));
+ if(_token == TK_LOCAL) LocalDeclStatement();
+ else if(_token != _SC(';')){
+ CommaExpr();
+ _fs->PopTarget();
+ }
+ Expect(_SC(';'));
+ _fs->SnoozeOpt();
+ SQInteger jmppos = _fs->GetCurrentPos();
+ SQInteger jzpos = -1;
+ if(_token != _SC(';')) { CommaExpr(); _fs->AddInstruction(_OP_JZ, _fs->PopTarget()); jzpos = _fs->GetCurrentPos(); }
+ Expect(_SC(';'));
+ _fs->SnoozeOpt();
+ SQInteger expstart = _fs->GetCurrentPos() + 1;
+ if(_token != _SC(')')) {
+ CommaExpr();
+ _fs->PopTarget();
+ }
+ Expect(_SC(')'));
+ _fs->SnoozeOpt();
+ SQInteger expend = _fs->GetCurrentPos();
+ SQInteger expsize = (expend - expstart) + 1;
+ SQInstructionVec exp;
+ if(expsize > 0) {
+ for(SQInteger i = 0; i < expsize; i++)
+ exp.push_back(_fs->GetInstruction(expstart + i));
+ _fs->PopInstructions(expsize);
+ }
+ BEGIN_BREAKBLE_BLOCK()
+ Statement();
+ SQInteger continuetrg = _fs->GetCurrentPos();
+ if(expsize > 0) {
+ for(SQInteger i = 0; i < expsize; i++)
+ _fs->AddInstruction(exp[i]);
+ }
+ _fs->AddInstruction(_OP_JMP, 0, jmppos - _fs->GetCurrentPos() - 1, 0);
+ if(jzpos> 0) _fs->SetIntructionParam(jzpos, 1, _fs->GetCurrentPos() - jzpos);
+ CleanStack(stacksize);
+
+ END_BREAKBLE_BLOCK(continuetrg);
+ }
+ void ForEachStatement()
+ {
+ SQObject idxname, valname;
+ Lex(); Expect(_SC('(')); valname = Expect(TK_IDENTIFIER);
+ if(_token == _SC(',')) {
+ idxname = valname;
+ Lex(); valname = Expect(TK_IDENTIFIER);
+ }
+ else{
+ idxname = _fs->CreateString(_SC("@INDEX@"));
+ }
+ Expect(TK_IN);
+
+ //save the stack size
+ SQInteger stacksize = _fs->GetStackSize();
+ //put the table in the stack(evaluate the table expression)
+ Expression(); Expect(_SC(')'));
+ SQInteger container = _fs->TopTarget();
+ //push the index local var
+ SQInteger indexpos = _fs->PushLocalVariable(idxname);
+ _fs->AddInstruction(_OP_LOADNULLS, indexpos,1);
+ //push the value local var
+ SQInteger valuepos = _fs->PushLocalVariable(valname);
+ _fs->AddInstruction(_OP_LOADNULLS, valuepos,1);
+ //push reference index
+ SQInteger itrpos = _fs->PushLocalVariable(_fs->CreateString(_SC("@ITERATOR@"))); //use invalid id to make it inaccessible
+ _fs->AddInstruction(_OP_LOADNULLS, itrpos,1);
+ SQInteger jmppos = _fs->GetCurrentPos();
+ _fs->AddInstruction(_OP_FOREACH, container, 0, indexpos);
+ SQInteger foreachpos = _fs->GetCurrentPos();
+ _fs->AddInstruction(_OP_POSTFOREACH, container, 0, indexpos);
+ //generate the statement code
+ BEGIN_BREAKBLE_BLOCK()
+ Statement();
+ _fs->AddInstruction(_OP_JMP, 0, jmppos - _fs->GetCurrentPos() - 1);
+ _fs->SetIntructionParam(foreachpos, 1, _fs->GetCurrentPos() - foreachpos);
+ _fs->SetIntructionParam(foreachpos + 1, 1, _fs->GetCurrentPos() - foreachpos);
+ //restore the local variable stack(remove index,val and ref idx)
+ CleanStack(stacksize);
+ END_BREAKBLE_BLOCK(foreachpos - 1);
+ }
+ void SwitchStatement()
+ {
+ Lex(); Expect(_SC('(')); CommaExpr(); Expect(_SC(')'));
+ Expect(_SC('{'));
+ SQInteger expr = _fs->TopTarget();
+ bool bfirst = true;
+ SQInteger tonextcondjmp = -1;
+ SQInteger skipcondjmp = -1;
+ SQInteger __nbreaks__ = _fs->_unresolvedbreaks.size();
+ _fs->_breaktargets.push_back(0);
+ while(_token == TK_CASE) {
+ //_fs->AddLineInfos(_lex._currentline, _lineinfo); think about this one
+ if(!bfirst) {
+ _fs->AddInstruction(_OP_JMP, 0, 0);
+ skipcondjmp = _fs->GetCurrentPos();
+ _fs->SetIntructionParam(tonextcondjmp, 1, _fs->GetCurrentPos() - tonextcondjmp);
+ }
+ //condition
+ Lex(); Expression(); Expect(_SC(':'));
+ SQInteger trg = _fs->PopTarget();
+ _fs->AddInstruction(_OP_EQ, trg, trg, expr);
+ _fs->AddInstruction(_OP_JZ, trg, 0);
+ //end condition
+ if(skipcondjmp != -1) {
+ _fs->SetIntructionParam(skipcondjmp, 1, (_fs->GetCurrentPos() - skipcondjmp));
+ }
+ tonextcondjmp = _fs->GetCurrentPos();
+ SQInteger stacksize = _fs->GetStackSize();
+ Statements();
+ _fs->SetStackSize(stacksize);
+ bfirst = false;
+ }
+ if(tonextcondjmp != -1)
+ _fs->SetIntructionParam(tonextcondjmp, 1, _fs->GetCurrentPos() - tonextcondjmp);
+ if(_token == TK_DEFAULT) {
+ // _fs->AddLineInfos(_lex._currentline, _lineinfo);
+ Lex(); Expect(_SC(':'));
+ SQInteger stacksize = _fs->GetStackSize();
+ Statements();
+ _fs->SetStackSize(stacksize);
+ }
+ Expect(_SC('}'));
+ _fs->PopTarget();
+ __nbreaks__ = _fs->_unresolvedbreaks.size() - __nbreaks__;
+ if(__nbreaks__ > 0)ResolveBreaks(_fs, __nbreaks__);
+ _fs->_breaktargets.pop_back();
+
+ }
+ void FunctionStatement()
+ {
+ SQObject id;
+ Lex(); id = Expect(TK_IDENTIFIER);
+ _fs->PushTarget(0);
+ _fs->AddInstruction(_OP_LOAD, _fs->PushTarget(), _fs->GetConstant(id));
+ if(_token == TK_DOUBLE_COLON) Emit2ArgsOP(_OP_GET);
+
+ while(_token == TK_DOUBLE_COLON) {
+ Lex();
+ id = Expect(TK_IDENTIFIER);
+ _fs->AddInstruction(_OP_LOAD, _fs->PushTarget(), _fs->GetConstant(id));
+ if(_token == TK_DOUBLE_COLON) Emit2ArgsOP(_OP_GET);
+ }
+ Expect(_SC('('));
+ CreateFunction(id);
+ _fs->AddInstruction(_OP_CLOSURE, _fs->PushTarget(), _fs->_functions.size() - 1, 0);
+ EmitDerefOp(_OP_NEWSLOT);
+ _fs->PopTarget();
+ }
+ void ClassStatement()
+ {
+ ExpState es;
+ Lex(); PushExpState();
+ _exst._class_or_delete = true;
+ _exst._funcarg = false;
+ PrefixedExpr();
+ es = PopExpState();
+ if(es._deref == DEREF_NO_DEREF) Error(_SC("invalid class name"));
+ if(es._deref == DEREF_FIELD) {
+ ClassExp();
+ EmitDerefOp(_OP_NEWSLOT);
+ _fs->PopTarget();
+ }
+ else Error(_SC("cannot create a class in a local with the syntax(class <local>)"));
+ }
+ void TryCatchStatement()
+ {
+ SQObject exid;
+ Lex();
+ _fs->AddInstruction(_OP_PUSHTRAP,0,0);
+ _fs->_traps++;
+ if(_fs->_breaktargets.size()) _fs->_breaktargets.top()++;
+ if(_fs->_continuetargets.size()) _fs->_continuetargets.top()++;
+ SQInteger trappos = _fs->GetCurrentPos();
+ Statement();
+ _fs->_traps--;
+ _fs->AddInstruction(_OP_POPTRAP, 1, 0);
+ if(_fs->_breaktargets.size()) _fs->_breaktargets.top()--;
+ if(_fs->_continuetargets.size()) _fs->_continuetargets.top()--;
+ _fs->AddInstruction(_OP_JMP, 0, 0);
+ SQInteger jmppos = _fs->GetCurrentPos();
+ _fs->SetIntructionParam(trappos, 1, (_fs->GetCurrentPos() - trappos));
+ Expect(TK_CATCH); Expect(_SC('(')); exid = Expect(TK_IDENTIFIER); Expect(_SC(')'));
+ SQInteger stacksize = _fs->GetStackSize();
+ SQInteger ex_target = _fs->PushLocalVariable(exid);
+ _fs->SetIntructionParam(trappos, 0, ex_target);
+ Statement();
+ _fs->SetIntructionParams(jmppos, 0, (_fs->GetCurrentPos() - jmppos), 0);
+ CleanStack(stacksize);
+ }
+ void FunctionExp(SQInteger ftype)
+ {
+ Lex(); Expect(_SC('('));
+ CreateFunction(_null_);
+ _fs->AddInstruction(_OP_CLOSURE, _fs->PushTarget(), _fs->_functions.size() - 1, ftype == TK_FUNCTION?0:1);
+ }
+ void ClassExp()
+ {
+ SQInteger base = -1;
+ SQInteger attrs = -1;
+ if(_token == TK_EXTENDS) {
+ Lex(); Expression();
+ base = _fs->TopTarget();
+ }
+ if(_token == TK_ATTR_OPEN) {
+ Lex();
+ _fs->AddInstruction(_OP_NEWTABLE, _fs->PushTarget());
+ ParseTableOrClass(_SC(','),TK_ATTR_CLOSE);
+ attrs = _fs->TopTarget();
+ }
+ Expect(_SC('{'));
+ if(attrs != -1) _fs->PopTarget();
+ if(base != -1) _fs->PopTarget();
+ _fs->AddInstruction(_OP_CLASS, _fs->PushTarget(), base, attrs);
+ ParseTableOrClass(_SC(';'));
+ }
+ void DelegateExpr()
+ {
+ Lex(); CommaExpr();
+ Expect(_SC(':'));
+ CommaExpr();
+ SQInteger table = _fs->PopTarget(), delegate = _fs->PopTarget();
+ _fs->AddInstruction(_OP_DELEGATE, _fs->PushTarget(), table, delegate);
+ }
+ void DeleteExpr()
+ {
+ ExpState es;
+ Lex(); PushExpState();
+ _exst._class_or_delete = true;
+ _exst._funcarg = false;
+ PrefixedExpr();
+ es = PopExpState();
+ if(es._deref == DEREF_NO_DEREF) Error(_SC("can't delete an expression"));
+ if(es._deref == DEREF_FIELD) Emit2ArgsOP(_OP_DELETE);
+ else Error(_SC("cannot delete a local"));
+ }
+ void PrefixIncDec(SQInteger token)
+ {
+ ExpState es;
+ Lex(); PushExpState();
+ _exst._class_or_delete = true;
+ _exst._funcarg = false;
+ PrefixedExpr();
+ es = PopExpState();
+ if(es._deref == DEREF_FIELD) Emit2ArgsOP(_OP_INC,token == TK_MINUSMINUS?-1:1);
+ else {
+ SQInteger src = _fs->PopTarget();
+ _fs->AddInstruction(_OP_INCL, _fs->PushTarget(), src, 0, token == TK_MINUSMINUS?-1:1);
+ }
+ }
+ void CreateFunction(SQObject &name)
+ {
+
+ SQFuncState *funcstate = _fs->PushChildState(_ss(_vm));
+ funcstate->_name = name;
+ SQObject paramname;
+ funcstate->AddParameter(_fs->CreateString(_SC("this")));
+ funcstate->_sourcename = _sourcename;
+ while(_token!=_SC(')')) {
+ if(_token == TK_VARPARAMS) {
+ funcstate->_varparams = true;
+ Lex();
+ if(_token != _SC(')')) Error(_SC("expected ')'"));
+ break;
+ }
+ else {
+ paramname = Expect(TK_IDENTIFIER);
+ funcstate->AddParameter(paramname);
+ if(_token == _SC(',')) Lex();
+ else if(_token != _SC(')')) Error(_SC("expected ')' or ','"));
+ }
+ }
+ Expect(_SC(')'));
+ //outer values
+ if(_token == _SC(':')) {
+ Lex(); Expect(_SC('('));
+ while(_token != _SC(')')) {
+ paramname = Expect(TK_IDENTIFIER);
+ //outers are treated as implicit local variables
+ funcstate->AddOuterValue(paramname);
+ if(_token == _SC(',')) Lex();
+ else if(_token != _SC(')')) Error(_SC("expected ')' or ','"));
+ }
+ Lex();
+ }
+
+ SQFuncState *currchunk = _fs;
+ _fs = funcstate;
+ Statement();
+ funcstate->AddLineInfos(_lex._prevtoken == _SC('\n')?_lex._lasttokenline:_lex._currentline, _lineinfo, true);
+ funcstate->AddInstruction(_OP_RETURN, -1);
+ funcstate->SetStackSize(0);
+ //_fs->->_stacksize = _fs->_stacksize;
+ SQFunctionProto *func = funcstate->BuildProto();
+#ifdef _DEBUG_DUMP
+ funcstate->Dump(func);
+#endif
+ _fs = currchunk;
+ _fs->_functions.push_back(func);
+ _fs->PopChildState();
+ }
+ void CleanStack(SQInteger stacksize)
+ {
+ if(_fs->GetStackSize() != stacksize)
+ _fs->SetStackSize(stacksize);
+ }
+ void ResolveBreaks(SQFuncState *funcstate, SQInteger ntoresolve)
+ {
+ while(ntoresolve > 0) {
+ SQInteger pos = funcstate->_unresolvedbreaks.back();
+ funcstate->_unresolvedbreaks.pop_back();
+ //set the jmp instruction
+ funcstate->SetIntructionParams(pos, 0, funcstate->GetCurrentPos() - pos, 0);
+ ntoresolve--;
+ }
+ }
+ void ResolveContinues(SQFuncState *funcstate, SQInteger ntoresolve, SQInteger targetpos)
+ {
+ while(ntoresolve > 0) {
+ SQInteger pos = funcstate->_unresolvedcontinues.back();
+ funcstate->_unresolvedcontinues.pop_back();
+ //set the jmp instruction
+ funcstate->SetIntructionParams(pos, 0, targetpos - pos, 0);
+ ntoresolve--;
+ }
+ }
+private:
+ SQInteger _token;
+ SQFuncState *_fs;
+ SQObjectPtr _sourcename;
+ SQLexer _lex;
+ bool _lineinfo;
+ bool _raiseerror;
+ SQInteger _debugline;
+ SQInteger _debugop;
+ ExpStateVec _expstates;
+ SQChar *compilererror;
+ jmp_buf _errorjmp;
+ SQVM *_vm;
+};
+
+bool Compile(SQVM *vm,SQLEXREADFUNC rg, SQUserPointer up, const SQChar *sourcename, SQObjectPtr &out, bool raiseerror, bool lineinfo)
+{
+ SQCompiler p(vm, rg, up, sourcename, raiseerror, lineinfo);
+ return p.Compile(out);
+}
--- /dev/null
+/* see copyright notice in squirrel.h */
+#ifndef _SQCOMPILER_H_
+#define _SQCOMPILER_H_
+
+struct SQVM;
+
+#define TK_IDENTIFIER 258
+#define TK_STRING_LITERAL 259
+#define TK_INTEGER 260
+#define TK_FLOAT 261
+#define TK_DELEGATE 262
+#define TK_DELETE 263
+#define TK_EQ 264
+#define TK_NE 265
+#define TK_LE 266
+#define TK_GE 267
+#define TK_SWITCH 268
+#define TK_ARROW 269
+#define TK_AND 270
+#define TK_OR 271
+#define TK_IF 272
+#define TK_ELSE 273
+#define TK_WHILE 274
+#define TK_BREAK 275
+#define TK_FOR 276
+#define TK_DO 277
+#define TK_NULL 278
+#define TK_FOREACH 279
+#define TK_IN 280
+#define TK_NEWSLOT 281
+#define TK_MODULO 282
+#define TK_LOCAL 283
+#define TK_CLONE 284
+#define TK_FUNCTION 285
+#define TK_RETURN 286
+#define TK_TYPEOF 287
+#define TK_UMINUS 288
+#define TK_PLUSEQ 289
+#define TK_MINUSEQ 290
+#define TK_CONTINUE 291
+#define TK_YIELD 292
+#define TK_TRY 293
+#define TK_CATCH 294
+#define TK_THROW 295
+#define TK_SHIFTL 296
+#define TK_SHIFTR 297
+#define TK_RESUME 298
+#define TK_DOUBLE_COLON 299
+#define TK_CASE 300
+#define TK_DEFAULT 301
+#define TK_THIS 302
+#define TK_PLUSPLUS 303
+#define TK_MINUSMINUS 304
+#define TK_PARENT 305
+#define TK_USHIFTR 306
+#define TK_CLASS 307
+#define TK_EXTENDS 308
+#define TK_CONSTRUCTOR 310
+#define TK_INSTANCEOF 311
+#define TK_VARPARAMS 312
+#define TK_VARGC 313
+#define TK_VARGV 314
+#define TK_TRUE 315
+#define TK_FALSE 316
+#define TK_MULEQ 317
+#define TK_DIVEQ 318
+#define TK_MODEQ 319
+#define TK_ATTR_OPEN 320
+#define TK_ATTR_CLOSE 321
+#define TK_STATIC 322
+
+
+typedef void(*CompilerErrorFunc)(void *ud, const SQChar *s);
+bool Compile(SQVM *vm, SQLEXREADFUNC rg, SQUserPointer up, const SQChar *sourcename, SQObjectPtr &out, bool raiseerror, bool lineinfo);
+#endif //_SQCOMPILER_H_
--- /dev/null
+/*
+ see copyright notice in squirrel.h
+*/
+#include "sqpcheader.h"
+#include <stdarg.h>
+#include "sqvm.h"
+#include "sqfuncproto.h"
+#include "sqclosure.h"
+#include "sqstring.h"
+
+SQRESULT sq_stackinfos(HSQUIRRELVM v, SQInteger level, SQStackInfos *si)
+{
+ SQInteger cssize = v->_callsstacksize;
+ if (cssize > level) {
+ memset(si, 0, sizeof(SQStackInfos));
+ SQVM::CallInfo &ci = v->_callsstack[cssize-level-1];
+ switch (type(ci._closure)) {
+ case OT_CLOSURE:{
+ SQFunctionProto *func = _funcproto(_closure(ci._closure)->_function);
+ if (type(func->_name) == OT_STRING)
+ si->funcname = _stringval(func->_name);
+ if (type(func->_sourcename) == OT_STRING)
+ si->source = _stringval(func->_sourcename);
+ si->line = func->GetLine(ci._ip);
+ }
+ break;
+ case OT_NATIVECLOSURE:
+ si->source = _SC("NATIVE");
+ si->funcname = _SC("unknown");
+ if(type(_nativeclosure(ci._closure)->_name) == OT_STRING)
+ si->funcname = _stringval(_nativeclosure(ci._closure)->_name);
+ si->line = -1;
+ break;
+ default: break; //shutup compiler
+ }
+ return SQ_OK;
+ }
+ return SQ_ERROR;
+}
+
+void SQVM::Raise_Error(const SQChar *s, ...)
+{
+ va_list vl;
+ va_start(vl, s);
+ scvsprintf(_sp(rsl((SQInteger)scstrlen(s)+(NUMBER_MAX_CHAR*2))), s, vl);
+ va_end(vl);
+ _lasterror = SQString::Create(_ss(this),_spval,-1);
+}
+
+void SQVM::Raise_Error(SQObjectPtr &desc)
+{
+ _lasterror = desc;
+}
+
+SQString *SQVM::PrintObjVal(const SQObject &o)
+{
+ switch(type(o)) {
+ case OT_STRING: return _string(o);
+ case OT_INTEGER:
+ scsprintf(_sp(rsl(NUMBER_MAX_CHAR+1)), _SC("%d"), _integer(o));
+ return SQString::Create(_ss(this), _spval);
+ break;
+ case OT_FLOAT:
+ scsprintf(_sp(rsl(NUMBER_MAX_CHAR+1)), _SC("%.14g"), _float(o));
+ return SQString::Create(_ss(this), _spval);
+ break;
+ default:
+ return SQString::Create(_ss(this), GetTypeName(o));
+ }
+}
+
+void SQVM::Raise_IdxError(SQObject &o)
+{
+ SQObjectPtr oval = PrintObjVal(o);
+ Raise_Error(_SC("the index '%.50s' does not exist"), _stringval(oval));
+}
+
+void SQVM::Raise_CompareError(const SQObject &o1, const SQObject &o2)
+{
+ SQObjectPtr oval1 = PrintObjVal(o1), oval2 = PrintObjVal(o2);
+ Raise_Error(_SC("comparsion between '%.50s' and '%.50s'"), _stringval(oval1), _stringval(oval2));
+}
+
+
+void SQVM::Raise_ParamTypeError(SQInteger nparam,SQInteger typemask,SQInteger type)
+{
+ SQObjectPtr exptypes = SQString::Create(_ss(this), _SC(""), -1);
+ SQInteger found = 0;
+ for(SQInteger i=0; i<16; i++)
+ {
+ SQInteger mask = 0x00000001 << i;
+ if(typemask & (mask)) {
+ if(found>0) StringCat(exptypes,SQString::Create(_ss(this), _SC("|"), -1), exptypes);
+ found ++;
+ StringCat(exptypes,SQString::Create(_ss(this), IdType2Name((SQObjectType)mask), -1), exptypes);
+ }
+ }
+ Raise_Error(_SC("parameter %d has an invalid type '%s' ; expected: '%s'"), nparam, IdType2Name((SQObjectType)type), _stringval(exptypes));
+}
--- /dev/null
+/* see copyright notice in squirrel.h */
+#ifndef _SQFUNCTION_H_
+#define _SQFUNCTION_H_
+
+#include "sqopcodes.h"
+
+enum SQOuterType {
+ otLOCAL = 0,
+ otSYMBOL = 1,
+ otOUTER = 2
+};
+
+struct SQOuterVar
+{
+
+ SQOuterVar(){}
+ SQOuterVar(const SQObjectPtr &name,const SQObjectPtr &src,SQOuterType t)
+ {
+ _name = name;
+ _src=src;
+ _type=t;
+ }
+ SQOuterVar(const SQOuterVar &ov)
+ {
+ _type=ov._type;
+ _src=ov._src;
+ _name=ov._name;
+ }
+ SQOuterType _type;
+ SQObjectPtr _name;
+ SQObjectPtr _src;
+};
+
+struct SQLocalVarInfo
+{
+ SQLocalVarInfo():_start_op(0),_end_op(0){}
+ SQLocalVarInfo(const SQLocalVarInfo &lvi)
+ {
+ _name=lvi._name;
+ _start_op=lvi._start_op;
+ _end_op=lvi._end_op;
+ _pos=lvi._pos;
+ }
+ SQObjectPtr _name;
+ SQUnsignedInteger _start_op;
+ SQUnsignedInteger _end_op;
+ SQUnsignedInteger _pos;
+};
+
+struct SQLineInfo { SQInteger _line;SQInteger _op; };
+
+typedef sqvector<SQOuterVar> SQOuterVarVec;
+typedef sqvector<SQLocalVarInfo> SQLocalVarInfoVec;
+typedef sqvector<SQLineInfo> SQLineInfoVec;
+
+#define _FUNC_SIZE(ni,nl,nparams,nfuncs,nouters,nlineinf,localinf) (sizeof(SQFunctionProto) \
+ +((ni-1)*sizeof(SQInstruction))+(nl*sizeof(SQObjectPtr)) \
+ +(nparams*sizeof(SQObjectPtr))+(nfuncs*sizeof(SQObjectPtr)) \
+ +(nouters*sizeof(SQOuterVar))+(nlineinf*sizeof(SQLineInfo)) \
+ +(localinf*sizeof(SQLocalVarInfo)))
+
+#define _CONSTRUCT_VECTOR(type,size,ptr) { \
+ for(SQInteger n = 0; n < size; n++) { \
+ new (&ptr[n]) type(); \
+ } \
+}
+
+#define _DESTRUCT_VECTOR(type,size,ptr) { \
+ for(SQInteger nl = 0; nl < size; nl++) { \
+ ptr[nl].~type(); \
+ } \
+}
+struct SQFunctionProto : public SQRefCounted
+{
+private:
+ SQFunctionProto(){
+ _stacksize=0;
+ _bgenerator=false;}
+public:
+ static SQFunctionProto *Create(SQInteger ninstructions,
+ SQInteger nliterals,SQInteger nparameters,
+ SQInteger nfunctions,SQInteger noutervalues,
+ SQInteger nlineinfos,SQInteger nlocalvarinfos)
+ {
+ SQFunctionProto *f;
+ //I compact the whole class and members in a single memory allocation
+ f = (SQFunctionProto *)sq_vm_malloc(_FUNC_SIZE(ninstructions,nliterals,nparameters,nfunctions,noutervalues,nlineinfos,nlocalvarinfos));
+ new (f) SQFunctionProto;
+ f->_ninstructions = ninstructions;
+ f->_literals = (SQObjectPtr*)&f->_instructions[ninstructions];
+ f->_nliterals = nliterals;
+ f->_parameters = (SQObjectPtr*)&f->_literals[nliterals];
+ f->_nparameters = nparameters;
+ f->_functions = (SQObjectPtr*)&f->_parameters[nparameters];
+ f->_nfunctions = nfunctions;
+ f->_outervalues = (SQOuterVar*)&f->_functions[nfunctions];
+ f->_noutervalues = noutervalues;
+ f->_lineinfos = (SQLineInfo *)&f->_outervalues[noutervalues];
+ f->_nlineinfos = nlineinfos;
+ f->_localvarinfos = (SQLocalVarInfo *)&f->_lineinfos[nlineinfos];
+ f->_nlocalvarinfos = nlocalvarinfos;
+
+ _CONSTRUCT_VECTOR(SQObjectPtr,f->_nliterals,f->_literals);
+ _CONSTRUCT_VECTOR(SQObjectPtr,f->_nparameters,f->_parameters);
+ _CONSTRUCT_VECTOR(SQObjectPtr,f->_nfunctions,f->_functions);
+ _CONSTRUCT_VECTOR(SQOuterVar,f->_noutervalues,f->_outervalues);
+ //_CONSTRUCT_VECTOR(SQLineInfo,f->_nlineinfos,f->_lineinfos); //not required are 2 integers
+ _CONSTRUCT_VECTOR(SQLocalVarInfo,f->_nlocalvarinfos,f->_localvarinfos);
+ return f;
+ }
+ void Release(){
+ _DESTRUCT_VECTOR(SQObjectPtr,_nliterals,_literals);
+ _DESTRUCT_VECTOR(SQObjectPtr,_nparameters,_parameters);
+ _DESTRUCT_VECTOR(SQObjectPtr,_nfunctions,_functions);
+ _DESTRUCT_VECTOR(SQOuterVar,_noutervalues,_outervalues);
+ //_DESTRUCT_VECTOR(SQLineInfo,_nlineinfos,_lineinfos); //not required are 2 integers
+ _DESTRUCT_VECTOR(SQLocalVarInfo,_nlocalvarinfos,_localvarinfos);
+ SQInteger size = _FUNC_SIZE(_ninstructions,_nliterals,_nparameters,_nfunctions,_noutervalues,_nlineinfos,_nlocalvarinfos);
+ this->~SQFunctionProto();
+ sq_vm_free(this,size);
+ }
+ const SQChar* GetLocal(SQVM *v,SQUnsignedInteger stackbase,SQUnsignedInteger nseq,SQUnsignedInteger nop);
+ SQInteger GetLine(SQInstruction *curr);
+ bool Save(SQVM *v,SQUserPointer up,SQWRITEFUNC write);
+ static bool Load(SQVM *v,SQUserPointer up,SQREADFUNC read,SQObjectPtr &ret);
+
+ SQObjectPtr _sourcename;
+ SQObjectPtr _name;
+ SQInteger _stacksize;
+ bool _bgenerator;
+ bool _varparams;
+
+ SQInteger _nlocalvarinfos;
+ SQLocalVarInfo *_localvarinfos;
+
+ SQInteger _nlineinfos;
+ SQLineInfo *_lineinfos;
+
+ SQInteger _nliterals;
+ SQObjectPtr *_literals;
+
+ SQInteger _nparameters;
+ SQObjectPtr *_parameters;
+
+ SQInteger _nfunctions;
+ SQObjectPtr *_functions;
+
+ SQInteger _noutervalues;
+ SQOuterVar *_outervalues;
+
+ SQInteger _ninstructions;
+ SQInstruction _instructions[1];
+};
+
+#endif //_SQFUNCTION_H_
--- /dev/null
+/*
+ see copyright notice in squirrel.h
+*/
+#include "sqpcheader.h"
+#include "sqcompiler.h"
+#include "sqfuncproto.h"
+#include "sqstring.h"
+#include "sqtable.h"
+#include "sqopcodes.h"
+#include "sqfuncstate.h"
+
+#ifdef _DEBUG_DUMP
+SQInstructionDesc g_InstrDesc[]={
+ {_SC("_OP_LINE")},
+ {_SC("_OP_LOAD")},
+ {_SC("_OP_LOADINT")},
+ {_SC("_OP_LOADFLOAT")},
+ {_SC("_OP_DLOAD")},
+ {_SC("_OP_TAILCALL")},
+ {_SC("_OP_CALL")},
+ {_SC("_OP_PREPCALL")},
+ {_SC("_OP_PREPCALLK")},
+ {_SC("_OP_GETK")},
+ {_SC("_OP_MOVE")},
+ {_SC("_OP_NEWSLOT")},
+ {_SC("_OP_DELETE")},
+ {_SC("_OP_SET")},
+ {_SC("_OP_GET")},
+ {_SC("_OP_EQ")},
+ {_SC("_OP_NE")},
+ {_SC("_OP_ARITH")},
+ {_SC("_OP_BITW")},
+ {_SC("_OP_RETURN")},
+ {_SC("_OP_LOADNULLS")},
+ {_SC("_OP_LOADROOTTABLE")},
+ {_SC("_OP_LOADBOOL")},
+ {_SC("_OP_DMOVE")},
+ {_SC("_OP_JMP")},
+ {_SC("_OP_JNZ")},
+ {_SC("_OP_JZ")},
+ {_SC("_OP_LOADFREEVAR")},
+ {_SC("_OP_VARGC")},
+ {_SC("_OP_GETVARGV")},
+ {_SC("_OP_NEWTABLE")},
+ {_SC("_OP_NEWARRAY")},
+ {_SC("_OP_APPENDARRAY")},
+ {_SC("_OP_GETPARENT")},
+ {_SC("_OP_COMPARITH")},
+ {_SC("_OP_COMPARITHL")},
+ {_SC("_OP_INC")},
+ {_SC("_OP_INCL")},
+ {_SC("_OP_PINC")},
+ {_SC("_OP_PINCL")},
+ {_SC("_OP_CMP")},
+ {_SC("_OP_EXISTS")},
+ {_SC("_OP_INSTANCEOF")},
+ {_SC("_OP_AND")},
+ {_SC("_OP_OR")},
+ {_SC("_OP_NEG")},
+ {_SC("_OP_NOT")},
+ {_SC("_OP_BWNOT")},
+ {_SC("_OP_CLOSURE")},
+ {_SC("_OP_YIELD")},
+ {_SC("_OP_RESUME")},
+ {_SC("_OP_FOREACH")},
+ {_SC("_OP_POSTFOREACH")},
+ {_SC("_OP_DELEGATE")},
+ {_SC("_OP_CLONE")},
+ {_SC("_OP_TYPEOF")},
+ {_SC("_OP_PUSHTRAP")},
+ {_SC("_OP_POPTRAP")},
+ {_SC("_OP_THROW")},
+ {_SC("_OP_CLASS")},
+ {_SC("_OP_NEWSLOTA")}
+};
+#endif
+void DumpLiteral(SQObjectPtr &o)
+{
+ switch(type(o)){
+ case OT_STRING: scprintf(_SC("\"%s\""),_stringval(o));break;
+ case OT_FLOAT: scprintf(_SC("{%f}"),_float(o));break;
+ case OT_INTEGER: scprintf(_SC("{%d}"),_integer(o));break;
+ default: assert(0); break; //shut up compiler
+ }
+}
+
+SQFuncState::SQFuncState(SQSharedState *ss,SQFuncState *parent,CompilerErrorFunc efunc,void *ed)
+{
+ _nliterals = 0;
+ _literals = SQTable::Create(ss,0);
+ _strings = SQTable::Create(ss,0);
+ _sharedstate = ss;
+ _lastline = 0;
+ _optimization = true;
+ _parent = parent;
+ _stacksize = 0;
+ _traps = 0;
+ _returnexp = 0;
+ _varparams = false;
+ _errfunc = efunc;
+ _errtarget = ed;
+ _bgenerator = false;
+
+}
+
+void SQFuncState::Error(const SQChar *err)
+{
+ _errfunc(_errtarget,err);
+}
+
+#ifdef _DEBUG_DUMP
+void SQFuncState::Dump(SQFunctionProto *func)
+{
+ SQUnsignedInteger n=0,i;
+ SQInteger si;
+ scprintf(_SC("SQInstruction sizeof %d\n"),sizeof(SQInstruction));
+ scprintf(_SC("SQObject sizeof %d\n"),sizeof(SQObject));
+ scprintf(_SC("--------------------------------------------------------------------\n"));
+ scprintf(_SC("*****FUNCTION [%s]\n"),type(func->_name)==OT_STRING?_stringval(func->_name):_SC("unknown"));
+ scprintf(_SC("-----LITERALS\n"));
+ SQObjectPtr refidx,key,val;
+ SQInteger idx;
+ SQObjectPtrVec templiterals;
+ templiterals.resize(_nliterals);
+ while((idx=_table(_literals)->Next(false,refidx,key,val))!=-1) {
+ refidx=idx;
+ templiterals[_integer(val)]=key;
+ }
+ for(i=0;i<templiterals.size();i++){
+ scprintf(_SC("[%d] "),n);
+ DumpLiteral(templiterals[i]);
+ scprintf(_SC("\n"));
+ n++;
+ }
+ scprintf(_SC("-----PARAMS\n"));
+ if(_varparams)
+ scprintf(_SC("<<VARPARAMS>>\n"));
+ n=0;
+ for(i=0;i<_parameters.size();i++){
+ scprintf(_SC("[%d] "),n);
+ DumpLiteral(_parameters[i]);
+ scprintf(_SC("\n"));
+ n++;
+ }
+ scprintf(_SC("-----LOCALS\n"));
+ for(si=0;si<func->_nlocalvarinfos;si++){
+ SQLocalVarInfo lvi=func->_localvarinfos[si];
+ scprintf(_SC("[%d] %s \t%d %d\n"),lvi._pos,_stringval(lvi._name),lvi._start_op,lvi._end_op);
+ n++;
+ }
+ scprintf(_SC("-----LINE INFO\n"));
+ for(i=0;i<_lineinfos.size();i++){
+ SQLineInfo li=_lineinfos[i];
+ scprintf(_SC("op [%d] line [%d] \n"),li._op,li._line);
+ n++;
+ }
+ scprintf(_SC("-----dump\n"));
+ n=0;
+ for(i=0;i<_instructions.size();i++){
+ SQInstruction &inst=_instructions[i];
+ if(inst.op==_OP_LOAD || inst.op==_OP_DLOAD || inst.op==_OP_PREPCALLK || inst.op==_OP_GETK ){
+
+ SQInteger lidx = inst._arg1;
+ scprintf(_SC("[%03d] %15s %d "),n,g_InstrDesc[inst.op].name,inst._arg0);
+ if(lidx >= 0xFFFFFFFF)
+ scprintf(_SC("null"));
+ else {
+ SQInteger refidx;
+ SQObjectPtr val,key,refo;
+ while(((refidx=_table(_literals)->Next(false,refo,key,val))!= -1) && (_integer(val) != lidx)) {
+ refo = refidx;
+ }
+ DumpLiteral(key);
+ }
+ if(inst.op != _OP_DLOAD) {
+ scprintf(_SC(" %d %d \n"),inst._arg2,inst._arg3);
+ }
+ else {
+ scprintf(_SC(" %d "),inst._arg2);
+ lidx = inst._arg3;
+ if(lidx >= 0xFFFFFFFF)
+ scprintf(_SC("null"));
+ else {
+ SQInteger refidx;
+ SQObjectPtr val,key,refo;
+ while(((refidx=_table(_literals)->Next(false,refo,key,val))!= -1) && (_integer(val) != lidx)) {
+ refo = refidx;
+ }
+ DumpLiteral(key);
+ scprintf(_SC("\n"));
+ }
+ }
+ }
+ else if(inst.op==_OP_LOADFLOAT) {
+ scprintf(_SC("[%03d] %15s %d %f %d %d\n"),n,g_InstrDesc[inst.op].name,inst._arg0,*((SQFloat*)&inst._arg1),inst._arg2,inst._arg3);
+ }
+ else if(inst.op==_OP_ARITH){
+ scprintf(_SC("[%03d] %15s %d %d %d %c\n"),n,g_InstrDesc[inst.op].name,inst._arg0,inst._arg1,inst._arg2,inst._arg3);
+ }
+ else
+ scprintf(_SC("[%03d] %15s %d %d %d %d\n"),n,g_InstrDesc[inst.op].name,inst._arg0,inst._arg1,inst._arg2,inst._arg3);
+ n++;
+ }
+ scprintf(_SC("-----\n"));
+ scprintf(_SC("stack size[%d]\n"),func->_stacksize);
+ scprintf(_SC("--------------------------------------------------------------------\n\n"));
+}
+#endif
+
+SQInteger SQFuncState::GetNumericConstant(const SQInteger cons)
+{
+ return GetConstant(SQObjectPtr(cons));
+}
+
+SQInteger SQFuncState::GetNumericConstant(const SQFloat cons)
+{
+ return GetConstant(SQObjectPtr(cons));
+}
+
+SQInteger SQFuncState::GetConstant(const SQObject &cons)
+{
+ SQObjectPtr val;
+ if(!_table(_literals)->Get(cons,val))
+ {
+ val = _nliterals;
+ _table(_literals)->NewSlot(cons,val);
+ _nliterals++;
+ if(_nliterals > MAX_LITERALS) {
+ val.Null();
+ Error(_SC("internal compiler error: too many literals"));
+ }
+ }
+ return _integer(val);
+}
+
+void SQFuncState::SetIntructionParams(SQInteger pos,SQInteger arg0,SQInteger arg1,SQInteger arg2,SQInteger arg3)
+{
+ _instructions[pos]._arg0=(unsigned char)*((SQUnsignedInteger *)&arg0);
+ _instructions[pos]._arg1=(SQInt32)*((SQUnsignedInteger *)&arg1);
+ _instructions[pos]._arg2=(unsigned char)*((SQUnsignedInteger *)&arg2);
+ _instructions[pos]._arg3=(unsigned char)*((SQUnsignedInteger *)&arg3);
+}
+
+void SQFuncState::SetIntructionParam(SQInteger pos,SQInteger arg,SQInteger val)
+{
+ switch(arg){
+ case 0:_instructions[pos]._arg0=(unsigned char)*((SQUnsignedInteger *)&val);break;
+ case 1:case 4:_instructions[pos]._arg1=(SQInt32)*((SQUnsignedInteger *)&val);break;
+ case 2:_instructions[pos]._arg2=(unsigned char)*((SQUnsignedInteger *)&val);break;
+ case 3:_instructions[pos]._arg3=(unsigned char)*((SQUnsignedInteger *)&val);break;
+ };
+}
+
+SQInteger SQFuncState::AllocStackPos()
+{
+ SQInteger npos=_vlocals.size();
+ _vlocals.push_back(SQLocalVarInfo());
+ if(_vlocals.size()>((SQUnsignedInteger)_stacksize)) {
+ if(_stacksize>MAX_FUNC_STACKSIZE) Error(_SC("internal compiler error: too many locals"));
+ _stacksize=_vlocals.size();
+ }
+ return npos;
+}
+
+SQInteger SQFuncState::PushTarget(SQInteger n)
+{
+ if(n!=-1){
+ _targetstack.push_back(n);
+ return n;
+ }
+ n=AllocStackPos();
+ _targetstack.push_back(n);
+ return n;
+}
+
+SQInteger SQFuncState::GetUpTarget(SQInteger n){
+ return _targetstack[((_targetstack.size()-1)-n)];
+}
+
+SQInteger SQFuncState::TopTarget(){
+ return _targetstack.back();
+}
+SQInteger SQFuncState::PopTarget()
+{
+ SQInteger npos=_targetstack.back();
+ SQLocalVarInfo t=_vlocals[_targetstack.back()];
+ if(type(t._name)==OT_NULL){
+ _vlocals.pop_back();
+ }
+ _targetstack.pop_back();
+ return npos;
+}
+
+SQInteger SQFuncState::GetStackSize()
+{
+ return _vlocals.size();
+}
+
+void SQFuncState::SetStackSize(SQInteger n)
+{
+ SQInteger size=_vlocals.size();
+ while(size>n){
+ size--;
+ SQLocalVarInfo lvi=_vlocals.back();
+ if(type(lvi._name)!=OT_NULL){
+ lvi._end_op=GetCurrentPos();
+ _localvarinfos.push_back(lvi);
+ }
+ _vlocals.pop_back();
+ }
+}
+
+bool SQFuncState::IsLocal(SQUnsignedInteger stkpos)
+{
+ if(stkpos>=_vlocals.size())return false;
+ else if(type(_vlocals[stkpos]._name)!=OT_NULL)return true;
+ return false;
+}
+
+SQInteger SQFuncState::PushLocalVariable(const SQObject &name)
+{
+ SQInteger pos=_vlocals.size();
+ SQLocalVarInfo lvi;
+ lvi._name=name;
+ lvi._start_op=GetCurrentPos()+1;
+ lvi._pos=_vlocals.size();
+ _vlocals.push_back(lvi);
+ if(_vlocals.size()>((SQUnsignedInteger)_stacksize))_stacksize=_vlocals.size();
+
+ return pos;
+}
+
+SQInteger SQFuncState::GetLocalVariable(const SQObject &name)
+{
+ SQInteger locals=_vlocals.size();
+ while(locals>=1){
+ if(type(_vlocals[locals-1]._name)==OT_STRING && _string(_vlocals[locals-1]._name)==_string(name)){
+ return locals-1;
+ }
+ locals--;
+ }
+ return -1;
+}
+
+SQInteger SQFuncState::GetOuterVariable(const SQObject &name)
+{
+ SQInteger outers = _outervalues.size();
+ for(SQInteger i = 0; i<outers; i++) {
+ if(_string(_outervalues[i]._name) == _string(name))
+ return i;
+ }
+ return -1;
+}
+
+void SQFuncState::AddOuterValue(const SQObject &name)
+{
+ SQInteger pos=-1;
+ if(_parent) {
+ pos = _parent->GetLocalVariable(name);
+ if(pos == -1) {
+ pos = _parent->GetOuterVariable(name);
+ if(pos != -1) {
+ _outervalues.push_back(SQOuterVar(name,SQObjectPtr(SQInteger(pos)),otOUTER)); //local
+ return;
+ }
+ }
+ else {
+ _outervalues.push_back(SQOuterVar(name,SQObjectPtr(SQInteger(pos)),otLOCAL)); //local
+ return;
+ }
+ }
+ _outervalues.push_back(SQOuterVar(name,name,otSYMBOL)); //global
+}
+
+void SQFuncState::AddParameter(const SQObject &name)
+{
+ PushLocalVariable(name);
+ _parameters.push_back(name);
+}
+
+void SQFuncState::AddLineInfos(SQInteger line,bool lineop,bool force)
+{
+ if(_lastline!=line || force){
+ SQLineInfo li;
+ li._line=line;li._op=(GetCurrentPos()+1);
+ if(lineop)AddInstruction(_OP_LINE,0,line);
+ _lineinfos.push_back(li);
+ _lastline=line;
+ }
+}
+
+void SQFuncState::AddInstruction(SQInstruction &i)
+{
+ SQInteger size = _instructions.size();
+ if(size > 0 && _optimization){ //simple optimizer
+ SQInstruction &pi = _instructions[size-1];//previous instruction
+ switch(i.op) {
+ case _OP_RETURN:
+ if( _parent && i._arg0 != MAX_FUNC_STACKSIZE && pi.op == _OP_CALL && _returnexp < size-1) {
+ pi.op = _OP_TAILCALL;
+ }
+ break;
+ case _OP_GET:
+ if( pi.op == _OP_LOAD && pi._arg0 == i._arg2 && (!IsLocal(pi._arg0))){
+ pi._arg1 = pi._arg1;
+ pi._arg2 = (unsigned char)i._arg1;
+ pi.op = _OP_GETK;
+ pi._arg0 = i._arg0;
+
+ return;
+ }
+ break;
+ case _OP_PREPCALL:
+ if( pi.op == _OP_LOAD && pi._arg0 == i._arg1 && (!IsLocal(pi._arg0))){
+ pi.op = _OP_PREPCALLK;
+ pi._arg0 = i._arg0;
+ pi._arg1 = pi._arg1;
+ pi._arg2 = i._arg2;
+ pi._arg3 = i._arg3;
+ return;
+ }
+ break;
+ case _OP_APPENDARRAY:
+ if(pi.op == _OP_LOAD && pi._arg0 == i._arg1 && (!IsLocal(pi._arg0))){
+ pi.op = _OP_APPENDARRAY;
+ pi._arg0 = i._arg0;
+ pi._arg1 = pi._arg1;
+ pi._arg2 = MAX_FUNC_STACKSIZE;
+ pi._arg3 = MAX_FUNC_STACKSIZE;
+ return;
+ }
+ break;
+ case _OP_MOVE:
+ if((pi.op == _OP_GET || pi.op == _OP_ARITH || pi.op == _OP_BITW) && (pi._arg0 == i._arg1))
+ {
+ pi._arg0 = i._arg0;
+ _optimization = false;
+ return;
+ }
+
+ if(pi.op == _OP_MOVE)
+ {
+ pi.op = _OP_DMOVE;
+ pi._arg2 = i._arg0;
+ pi._arg3 = (unsigned char)i._arg1;
+ return;
+ }
+ break;
+ case _OP_LOAD:
+ if(pi.op == _OP_LOAD && i._arg1 < 256) {
+ pi.op = _OP_DLOAD;
+ pi._arg2 = i._arg0;
+ pi._arg3 = (unsigned char)i._arg1;
+ return;
+ }
+ break;
+ case _OP_EQ:case _OP_NE:
+ if(pi.op == _OP_LOAD && pi._arg0 == i._arg1 && (!IsLocal(pi._arg0) ))
+ {
+ pi.op = i.op;
+ pi._arg0 = i._arg0;
+ pi._arg1 = pi._arg1;
+ pi._arg2 = i._arg2;
+ pi._arg3 = MAX_FUNC_STACKSIZE;
+ return;
+ }
+ break;
+ case _OP_LOADNULLS:
+ if((pi.op == _OP_LOADNULLS && pi._arg0+pi._arg1 == i._arg0)) {
+
+ pi._arg1 = pi._arg1 + 1;
+ pi.op = _OP_LOADNULLS;
+ return;
+ }
+ break;
+ case _OP_LINE:
+ if(pi.op == _OP_LINE) {
+ _instructions.pop_back();
+ _lineinfos.pop_back();
+ }
+ break;
+ }
+ }
+ _optimization = true;
+ _instructions.push_back(i);
+}
+
+SQObject SQFuncState::CreateString(const SQChar *s,SQInteger len)
+{
+ SQObjectPtr ns(SQString::Create(_sharedstate,s,len));
+ _table(_strings)->NewSlot(ns,(SQInteger)1);
+ return ns;
+}
+
+SQFunctionProto *SQFuncState::BuildProto()
+{
+ SQFunctionProto *f=SQFunctionProto::Create(_instructions.size(),
+ _nliterals,_parameters.size(),_functions.size(),_outervalues.size(),
+ _lineinfos.size(),_localvarinfos.size());
+
+ SQObjectPtr refidx,key,val;
+ SQInteger idx;
+
+ f->_stacksize = _stacksize;
+ f->_sourcename = _sourcename;
+ f->_bgenerator = _bgenerator;
+ f->_name = _name;
+
+ while((idx=_table(_literals)->Next(false,refidx,key,val))!=-1) {
+ f->_literals[_integer(val)]=key;
+ refidx=idx;
+ }
+
+ for(SQUnsignedInteger nf = 0; nf < _functions.size(); nf++) f->_functions[nf] = _functions[nf];
+ for(SQUnsignedInteger np = 0; np < _parameters.size(); np++) f->_parameters[np] = _parameters[np];
+ for(SQUnsignedInteger no = 0; no < _outervalues.size(); no++) f->_outervalues[no] = _outervalues[no];
+ for(SQUnsignedInteger no = 0; no < _localvarinfos.size(); no++) f->_localvarinfos[no] = _localvarinfos[no];
+ for(SQUnsignedInteger no = 0; no < _lineinfos.size(); no++) f->_lineinfos[no] = _lineinfos[no];
+
+ memcpy(f->_instructions,&_instructions[0],_instructions.size()*sizeof(SQInstruction));
+
+ f->_varparams = _varparams;
+
+ return f;
+}
+
+SQFuncState *SQFuncState::PushChildState(SQSharedState *ss)
+{
+ SQFuncState *child = (SQFuncState *)sq_malloc(sizeof(SQFuncState));
+ new (child) SQFuncState(ss,this,_errfunc,_errtarget);
+ _childstates.push_back(child);
+ return child;
+}
+
+void SQFuncState::PopChildState()
+{
+ SQFuncState *child = _childstates.back();
+ sq_delete(child,SQFuncState);
+ _childstates.pop_back();
+}
+
+SQFuncState::~SQFuncState()
+{
+ while(_childstates.size() > 0)
+ {
+ PopChildState();
+ }
+}
--- /dev/null
+/* see copyright notice in squirrel.h */
+#ifndef _SQFUNCSTATE_H_
+#define _SQFUNCSTATE_H_
+///////////////////////////////////
+#include "squtils.h"
+
+struct SQFuncState
+{
+ SQFuncState(SQSharedState *ss,SQFuncState *parent,CompilerErrorFunc efunc,void *ed);
+ ~SQFuncState();
+#ifdef _DEBUG_DUMP
+ void Dump(SQFunctionProto *func);
+#endif
+ void Error(const SQChar *err);
+ SQFuncState *PushChildState(SQSharedState *ss);
+ void PopChildState();
+ void AddInstruction(SQOpcode _op,SQInteger arg0=0,SQInteger arg1=0,SQInteger arg2=0,SQInteger arg3=0){SQInstruction i(_op,arg0,arg1,arg2,arg3);AddInstruction(i);}
+ void AddInstruction(SQInstruction &i);
+ void SetIntructionParams(SQInteger pos,SQInteger arg0,SQInteger arg1,SQInteger arg2=0,SQInteger arg3=0);
+ void SetIntructionParam(SQInteger pos,SQInteger arg,SQInteger val);
+ SQInstruction &GetInstruction(SQInteger pos){return _instructions[pos];}
+ void PopInstructions(SQInteger size){for(SQInteger i=0;i<size;i++)_instructions.pop_back();}
+ void SetStackSize(SQInteger n);
+ void SnoozeOpt(){_optimization=false;}
+ SQInteger GetCurrentPos(){return _instructions.size()-1;}
+ SQInteger GetNumericConstant(const SQInteger cons);
+ SQInteger GetNumericConstant(const SQFloat cons);
+ SQInteger PushLocalVariable(const SQObject &name);
+ void AddParameter(const SQObject &name);
+ void AddOuterValue(const SQObject &name);
+ SQInteger GetLocalVariable(const SQObject &name);
+ SQInteger GetOuterVariable(const SQObject &name);
+ SQInteger GenerateCode();
+ SQInteger GetStackSize();
+ SQInteger CalcStackFrameSize();
+ void AddLineInfos(SQInteger line,bool lineop,bool force=false);
+ SQFunctionProto *BuildProto();
+ SQInteger AllocStackPos();
+ SQInteger PushTarget(SQInteger n=-1);
+ SQInteger PopTarget();
+ SQInteger TopTarget();
+ SQInteger GetUpTarget(SQInteger n);
+ bool IsLocal(SQUnsignedInteger stkpos);
+ SQObject CreateString(const SQChar *s,SQInteger len = -1);
+ SQInteger _returnexp;
+ SQLocalVarInfoVec _vlocals;
+ SQIntVec _targetstack;
+ SQInteger _stacksize;
+ bool _varparams;
+ bool _bgenerator;
+ SQIntVec _unresolvedbreaks;
+ SQIntVec _unresolvedcontinues;
+ SQObjectPtrVec _functions;
+ SQObjectPtrVec _parameters;
+ SQOuterVarVec _outervalues;
+ SQInstructionVec _instructions;
+ SQLocalVarInfoVec _localvarinfos;
+ SQObjectPtr _literals;
+ SQObjectPtr _strings;
+ SQObjectPtr _name;
+ SQObjectPtr _sourcename;
+ SQInteger _nliterals;
+ SQLineInfoVec _lineinfos;
+ SQFuncState *_parent;
+ SQIntVec _breaktargets;
+ SQIntVec _continuetargets;
+ SQInteger _lastline;
+ SQInteger _traps; //contains number of nested exception traps
+ bool _optimization;
+ SQSharedState *_sharedstate;
+ sqvector<SQFuncState*> _childstates;
+ SQInteger GetConstant(const SQObject &cons);
+private:
+ CompilerErrorFunc _errfunc;
+ void *_errtarget;
+};
+
+
+#endif //_SQFUNCSTATE_H_
+
--- /dev/null
+/*
+ see copyright notice in squirrel.h
+*/
+#include "sqpcheader.h"
+#include <ctype.h>
+#include <stdlib.h>
+#include "sqtable.h"
+#include "sqstring.h"
+#include "sqcompiler.h"
+#include "sqlexer.h"
+
+#define CUR_CHAR (_currdata)
+#define RETURN_TOKEN(t) { _prevtoken = _curtoken; _curtoken = t; return t;}
+#define IS_EOB() (CUR_CHAR <= SQUIRREL_EOB)
+#define NEXT() {Next();_currentcolumn++;}
+#define INIT_TEMP_STRING() { _longstr.resize(0);}
+#define APPEND_CHAR(c) { _longstr.push_back(c);}
+#define TERMINATE_BUFFER() {_longstr.push_back(_SC('\0'));}
+#define ADD_KEYWORD(key,id) _keywords->NewSlot( SQString::Create(ss, _SC(#key)) ,SQInteger(id))
+
+SQLexer::SQLexer(){}
+SQLexer::~SQLexer()
+{
+ _keywords->Release();
+}
+
+void SQLexer::Init(SQSharedState *ss, SQLEXREADFUNC rg, SQUserPointer up,CompilerErrorFunc efunc,void *ed)
+{
+ _errfunc = efunc;
+ _errtarget = ed;
+ _sharedstate = ss;
+ _keywords = SQTable::Create(ss, 26);
+ ADD_KEYWORD(while, TK_WHILE);
+ ADD_KEYWORD(do, TK_DO);
+ ADD_KEYWORD(if, TK_IF);
+ ADD_KEYWORD(else, TK_ELSE);
+ ADD_KEYWORD(break, TK_BREAK);
+ ADD_KEYWORD(continue, TK_CONTINUE);
+ ADD_KEYWORD(return, TK_RETURN);
+ ADD_KEYWORD(null, TK_NULL);
+ ADD_KEYWORD(function, TK_FUNCTION);
+ ADD_KEYWORD(local, TK_LOCAL);
+ ADD_KEYWORD(for, TK_FOR);
+ ADD_KEYWORD(foreach, TK_FOREACH);
+ ADD_KEYWORD(in, TK_IN);
+ ADD_KEYWORD(typeof, TK_TYPEOF);
+ ADD_KEYWORD(delegate, TK_DELEGATE);
+ ADD_KEYWORD(delete, TK_DELETE);
+ ADD_KEYWORD(try, TK_TRY);
+ ADD_KEYWORD(catch, TK_CATCH);
+ ADD_KEYWORD(throw, TK_THROW);
+ ADD_KEYWORD(clone, TK_CLONE);
+ ADD_KEYWORD(yield, TK_YIELD);
+ ADD_KEYWORD(resume, TK_RESUME);
+ ADD_KEYWORD(switch, TK_SWITCH);
+ ADD_KEYWORD(case, TK_CASE);
+ ADD_KEYWORD(default, TK_DEFAULT);
+ ADD_KEYWORD(this, TK_THIS);
+ ADD_KEYWORD(parent,TK_PARENT);
+ ADD_KEYWORD(class,TK_CLASS);
+ ADD_KEYWORD(extends,TK_EXTENDS);
+ ADD_KEYWORD(constructor,TK_CONSTRUCTOR);
+ ADD_KEYWORD(instanceof,TK_INSTANCEOF);
+ ADD_KEYWORD(vargc,TK_VARGC);
+ ADD_KEYWORD(vargv,TK_VARGV);
+ ADD_KEYWORD(true,TK_TRUE);
+ ADD_KEYWORD(false,TK_FALSE);
+ ADD_KEYWORD(static,TK_STATIC);
+
+ _readf = rg;
+ _up = up;
+ _lasttokenline = _currentline = 1;
+ _currentcolumn = 0;
+ _prevtoken = -1;
+ Next();
+}
+
+void SQLexer::Error(const SQChar *err)
+{
+ _errfunc(_errtarget,err);
+}
+
+void SQLexer::Next()
+{
+ SQInteger t = _readf(_up);
+ if(t > MAX_CHAR) Error(_SC("Invalid character"));
+ if(t != 0) {
+ _currdata = (LexChar)t;
+ return;
+ }
+ _currdata = SQUIRREL_EOB;
+}
+
+const SQChar *SQLexer::Tok2Str(SQInteger tok)
+{
+ SQObjectPtr itr, key, val;
+ SQInteger nitr;
+ while((nitr = _keywords->Next(false,itr, key, val)) != -1) {
+ itr = (SQInteger)nitr;
+ if(((SQInteger)_integer(val)) == tok)
+ return _stringval(key);
+ }
+ return NULL;
+}
+
+void SQLexer::LexBlockComment()
+{
+ bool done = false;
+ while(!done) {
+ switch(CUR_CHAR) {
+ case _SC('*'): { NEXT(); if(CUR_CHAR == _SC('/')) { done = true; NEXT(); }}; continue;
+ case _SC('\n'): _currentline++; NEXT(); continue;
+ case SQUIRREL_EOB: Error(_SC("missing \"*/\" in comment"));
+ default: NEXT();
+ }
+ }
+}
+
+SQInteger SQLexer::Lex()
+{
+ _lasttokenline = _currentline;
+ while(CUR_CHAR != SQUIRREL_EOB) {
+ switch(CUR_CHAR){
+ case _SC('\t'): case _SC('\r'): case _SC(' '): NEXT(); continue;
+ case _SC('\n'):
+ _currentline++;
+ _prevtoken=_curtoken;
+ _curtoken=_SC('\n');
+ NEXT();
+ _currentcolumn=1;
+ continue;
+ case _SC('/'):
+ NEXT();
+ switch(CUR_CHAR){
+ case _SC('*'):
+ NEXT();
+ LexBlockComment();
+ continue;
+ case _SC('/'):
+ do { NEXT(); } while (CUR_CHAR != _SC('\n') && (!IS_EOB()));
+ continue;
+ case _SC('='):
+ NEXT();
+ RETURN_TOKEN(TK_DIVEQ);
+ continue;
+ case _SC('>'):
+ NEXT();
+ RETURN_TOKEN(TK_ATTR_CLOSE);
+ continue;
+ default:
+ RETURN_TOKEN('/');
+ }
+ case _SC('='):
+ NEXT();
+ if (CUR_CHAR != _SC('=')){ RETURN_TOKEN('=') }
+ else { NEXT(); RETURN_TOKEN(TK_EQ); }
+ case _SC('<'):
+ NEXT();
+ if ( CUR_CHAR == _SC('=') ) { NEXT(); RETURN_TOKEN(TK_LE) }
+ else if ( CUR_CHAR == _SC('-') ) { NEXT(); RETURN_TOKEN(TK_NEWSLOT); }
+ else if ( CUR_CHAR == _SC('<') ) { NEXT(); RETURN_TOKEN(TK_SHIFTL); }
+ else if ( CUR_CHAR == _SC('/') ) { NEXT(); RETURN_TOKEN(TK_ATTR_OPEN); }
+ //else if ( CUR_CHAR == _SC('[') ) { NEXT(); ReadMultilineString(); RETURN_TOKEN(TK_STRING_LITERAL); }
+ else { RETURN_TOKEN('<') }
+ case _SC('>'):
+ NEXT();
+ if (CUR_CHAR == _SC('=')){ NEXT(); RETURN_TOKEN(TK_GE);}
+ else if(CUR_CHAR == _SC('>')){
+ NEXT();
+ if(CUR_CHAR == _SC('>')){
+ NEXT();
+ RETURN_TOKEN(TK_USHIFTR);
+ }
+ RETURN_TOKEN(TK_SHIFTR);
+ }
+ else { RETURN_TOKEN('>') }
+ case _SC('!'):
+ NEXT();
+ if (CUR_CHAR != _SC('=')){ RETURN_TOKEN('!')}
+ else { NEXT(); RETURN_TOKEN(TK_NE); }
+ case _SC('@'): {
+ SQInteger stype;
+ NEXT();
+ if(CUR_CHAR != _SC('"'))
+ Error(_SC("string expected"));
+ if((stype=ReadString('"',true))!=-1) {
+ RETURN_TOKEN(stype);
+ }
+ Error(_SC("error parsing the string"));
+ }
+ case _SC('"'):
+ case _SC('\''): {
+ SQInteger stype;
+ if((stype=ReadString(CUR_CHAR,false))!=-1){
+ RETURN_TOKEN(stype);
+ }
+ Error(_SC("error parsing the string"));
+ }
+ case _SC('{'): case _SC('}'): case _SC('('): case _SC(')'): case _SC('['): case _SC(']'):
+ case _SC(';'): case _SC(','): case _SC('?'): case _SC('^'): case _SC('~'):
+ {SQInteger ret = CUR_CHAR;
+ NEXT(); RETURN_TOKEN(ret); }
+ case _SC('.'):
+ NEXT();
+ if (CUR_CHAR != _SC('.')){ RETURN_TOKEN('.') }
+ NEXT();
+ if (CUR_CHAR != _SC('.')){ Error(_SC("invalid token '..'")); }
+ NEXT();
+ RETURN_TOKEN(TK_VARPARAMS);
+ case _SC('&'):
+ NEXT();
+ if (CUR_CHAR != _SC('&')){ RETURN_TOKEN('&') }
+ else { NEXT(); RETURN_TOKEN(TK_AND); }
+ case _SC('|'):
+ NEXT();
+ if (CUR_CHAR != _SC('|')){ RETURN_TOKEN('|') }
+ else { NEXT(); RETURN_TOKEN(TK_OR); }
+ case _SC(':'):
+ NEXT();
+ if (CUR_CHAR != _SC(':')){ RETURN_TOKEN(':') }
+ else { NEXT(); RETURN_TOKEN(TK_DOUBLE_COLON); }
+ case _SC('*'):
+ NEXT();
+ if (CUR_CHAR == _SC('=')){ NEXT(); RETURN_TOKEN(TK_MULEQ);}
+ else RETURN_TOKEN('*');
+ case _SC('%'):
+ NEXT();
+ if (CUR_CHAR == _SC('=')){ NEXT(); RETURN_TOKEN(TK_MODEQ);}
+ else RETURN_TOKEN('%');
+ case _SC('-'):
+ NEXT();
+ if (CUR_CHAR == _SC('=')){ NEXT(); RETURN_TOKEN(TK_MINUSEQ);}
+ else if (CUR_CHAR == _SC('-')){ NEXT(); RETURN_TOKEN(TK_MINUSMINUS);}
+ else RETURN_TOKEN('-');
+ case _SC('+'):
+ NEXT();
+ if (CUR_CHAR == _SC('=')){ NEXT(); RETURN_TOKEN(TK_PLUSEQ);}
+ else if (CUR_CHAR == _SC('+')){ NEXT(); RETURN_TOKEN(TK_PLUSPLUS);}
+ else RETURN_TOKEN('+');
+ case SQUIRREL_EOB:
+ return 0;
+ default:{
+ if (scisdigit(CUR_CHAR)) {
+ SQInteger ret = ReadNumber();
+ RETURN_TOKEN(ret);
+ }
+ else if (scisalpha(CUR_CHAR) || CUR_CHAR == _SC('_')) {
+ SQInteger t = ReadID();
+ RETURN_TOKEN(t);
+ }
+ else {
+ SQInteger c = CUR_CHAR;
+ if (sciscntrl((int)c)) Error(_SC("unexpected character(control)"));
+ NEXT();
+ RETURN_TOKEN(c);
+ }
+ RETURN_TOKEN(0);
+ }
+ }
+ }
+ return 0;
+}
+
+SQInteger SQLexer::GetIDType(SQChar *s)
+{
+ SQObjectPtr t;
+ if(_keywords->Get(SQString::Create(_sharedstate, s), t)) {
+ return SQInteger(_integer(t));
+ }
+ return TK_IDENTIFIER;
+}
+
+
+SQInteger SQLexer::ReadString(SQInteger ndelim,bool verbatim)
+{
+ INIT_TEMP_STRING();
+ NEXT();
+ if(IS_EOB()) return -1;
+ for(;;) {
+ while(CUR_CHAR != ndelim) {
+ switch(CUR_CHAR) {
+ case SQUIRREL_EOB:
+ Error(_SC("unfinished string"));
+ return -1;
+ case _SC('\n'):
+ if(!verbatim) Error(_SC("newline in a constant"));
+ APPEND_CHAR(CUR_CHAR); NEXT();
+ _currentline++;
+ break;
+ case _SC('\\'):
+ if(verbatim) {
+ APPEND_CHAR('\\'); NEXT();
+ }
+ else {
+ NEXT();
+ switch(CUR_CHAR) {
+ case _SC('x'): NEXT(); {
+ if(!isxdigit(CUR_CHAR)) Error(_SC("hexadecimal number expected"));
+ const SQInteger maxdigits = 4;
+ SQChar temp[maxdigits+1];
+ SQInteger n = 0;
+ while(isxdigit(CUR_CHAR) && n < maxdigits) {
+ temp[n] = CUR_CHAR;
+ n++;
+ NEXT();
+ }
+ temp[n] = 0;
+ SQChar *sTemp;
+ APPEND_CHAR((SQChar)scstrtoul(temp,&sTemp,16));
+ }
+ break;
+ case _SC('t'): APPEND_CHAR(_SC('\t')); NEXT(); break;
+ case _SC('a'): APPEND_CHAR(_SC('\a')); NEXT(); break;
+ case _SC('b'): APPEND_CHAR(_SC('\b')); NEXT(); break;
+ case _SC('n'): APPEND_CHAR(_SC('\n')); NEXT(); break;
+ case _SC('r'): APPEND_CHAR(_SC('\r')); NEXT(); break;
+ case _SC('v'): APPEND_CHAR(_SC('\v')); NEXT(); break;
+ case _SC('f'): APPEND_CHAR(_SC('\f')); NEXT(); break;
+ case _SC('0'): APPEND_CHAR(_SC('\0')); NEXT(); break;
+ case _SC('\\'): APPEND_CHAR(_SC('\\')); NEXT(); break;
+ case _SC('"'): APPEND_CHAR(_SC('"')); NEXT(); break;
+ case _SC('\''): APPEND_CHAR(_SC('\'')); NEXT(); break;
+ default:
+ Error(_SC("unrecognised escaper char"));
+ break;
+ }
+ }
+ break;
+ default:
+ APPEND_CHAR(CUR_CHAR);
+ NEXT();
+ }
+ }
+ NEXT();
+ if(verbatim && CUR_CHAR == '"') { //double quotation
+ APPEND_CHAR(CUR_CHAR);
+ NEXT();
+ }
+ else {
+ break;
+ }
+ }
+ TERMINATE_BUFFER();
+ SQInteger len = _longstr.size()-1;
+ if(ndelim == _SC('\'')) {
+ if(len == 0) Error(_SC("empty constant"));
+ if(len > 1) Error(_SC("constant too long"));
+ _nvalue = _longstr[0];
+ return TK_INTEGER;
+ }
+ _svalue = &_longstr[0];
+ return TK_STRING_LITERAL;
+}
+
+void LexHexadecimal(const SQChar *s,SQUnsignedInteger *res)
+{
+ *res = 0;
+ while(*s != 0)
+ {
+ if(scisdigit(*s)) *res = (*res)*16+((*s++)-'0');
+ else if(scisxdigit(*s)) *res = (*res)*16+(toupper(*s++)-'A'+10);
+ else { assert(0); }
+ }
+}
+
+void LexInteger(const SQChar *s,SQUnsignedInteger *res)
+{
+ *res = 0;
+ while(*s != 0)
+ {
+ *res = (*res)*10+((*s++)-'0');
+ }
+}
+
+SQInteger isexponent(SQInteger c) { return c == 'e' || c=='E'; }
+#define MAX_HEX_DIGITS (sizeof(SQInteger)*2)
+SQInteger SQLexer::ReadNumber()
+{
+#define TINT 1
+#define TFLOAT 2
+#define THEX 3
+#define TSCIENTIFIC 4
+ SQInteger type = TINT, firstchar = CUR_CHAR;
+ SQChar *sTemp;
+ INIT_TEMP_STRING();
+ NEXT();
+ if(firstchar == _SC('0') && toupper(CUR_CHAR) == _SC('X')) {
+ NEXT();
+ type = THEX;
+ while(isxdigit(CUR_CHAR)) {
+ APPEND_CHAR(CUR_CHAR);
+ NEXT();
+ }
+ if(_longstr.size() > MAX_HEX_DIGITS) Error(_SC("too many digits for an Hex number"));
+ }
+ else {
+ APPEND_CHAR((int)firstchar);
+ while (CUR_CHAR == _SC('.') || scisdigit(CUR_CHAR) || isexponent(CUR_CHAR)) {
+ if(CUR_CHAR == _SC('.')) type = TFLOAT;
+ if(isexponent(CUR_CHAR)) {
+ if(type != TFLOAT) Error(_SC("invalid numeric format"));
+ type = TSCIENTIFIC;
+ APPEND_CHAR(CUR_CHAR);
+ NEXT();
+ if(CUR_CHAR == '+' || CUR_CHAR == '-'){
+ APPEND_CHAR(CUR_CHAR);
+ NEXT();
+ }
+ if(!scisdigit(CUR_CHAR)) Error(_SC("exponent expected"));
+ }
+
+ APPEND_CHAR(CUR_CHAR);
+ NEXT();
+ }
+ }
+ TERMINATE_BUFFER();
+ switch(type) {
+ case TSCIENTIFIC:
+ case TFLOAT:
+ _fvalue = (SQFloat)scstrtod(&_longstr[0],&sTemp);
+ return TK_FLOAT;
+ case TINT:
+ LexInteger(&_longstr[0],(SQUnsignedInteger *)&_nvalue);
+ return TK_INTEGER;
+ case THEX:
+ LexHexadecimal(&_longstr[0],(SQUnsignedInteger *)&_nvalue);
+ return TK_INTEGER;
+ }
+ return 0;
+}
+
+SQInteger SQLexer::ReadID()
+{
+ SQInteger res;
+ INIT_TEMP_STRING();
+ do {
+ APPEND_CHAR(CUR_CHAR);
+ NEXT();
+ } while(scisalnum(CUR_CHAR) || CUR_CHAR == _SC('_'));
+ TERMINATE_BUFFER();
+ res = GetIDType(&_longstr[0]);
+ if(res == TK_IDENTIFIER || res == TK_CONSTRUCTOR) {
+ _svalue = &_longstr[0];
+ }
+ return res;
+}
--- /dev/null
+/* see copyright notice in squirrel.h */
+#ifndef _SQLEXER_H_
+#define _SQLEXER_H_
+
+#ifdef _UNICODE
+typedef SQChar LexChar;
+#else
+typedef unsigned char LexChar;
+#endif
+
+struct SQLexer
+{
+ SQLexer();
+ ~SQLexer();
+ void Init(SQSharedState *ss,SQLEXREADFUNC rg,SQUserPointer up,CompilerErrorFunc efunc,void *ed);
+ void Error(const SQChar *err);
+ SQInteger Lex();
+ const SQChar *Tok2Str(SQInteger tok);
+private:
+ SQInteger GetIDType(SQChar *s);
+ SQInteger ReadString(SQInteger ndelim,bool verbatim);
+ SQInteger ReadNumber();
+ void LexBlockComment();
+ SQInteger ReadID();
+ void Next();
+ SQInteger _curtoken;
+ SQTable *_keywords;
+public:
+ SQInteger _prevtoken;
+ SQInteger _currentline;
+ SQInteger _lasttokenline;
+ SQInteger _currentcolumn;
+ const SQChar *_svalue;
+ SQInteger _nvalue;
+ SQFloat _fvalue;
+ SQLEXREADFUNC _readf;
+ SQUserPointer _up;
+ LexChar _currdata;
+ SQSharedState *_sharedstate;
+ sqvector<SQChar> _longstr;
+ CompilerErrorFunc _errfunc;
+ void *_errtarget;
+};
+
+#endif
--- /dev/null
+/*
+ see copyright notice in squirrel.h
+*/
+#include "sqpcheader.h"
+void *sq_vm_malloc(SQUnsignedInteger size){ return malloc(size); }
+
+void *sq_vm_realloc(void *p, SQUnsignedInteger oldsize, SQUnsignedInteger size){ return realloc(p, size); }
+
+void sq_vm_free(void *p, SQUnsignedInteger size){ free(p); }
--- /dev/null
+/*
+ see copyright notice in squirrel.h
+*/
+#include "sqpcheader.h"
+#include "sqvm.h"
+#include "sqstring.h"
+#include "sqarray.h"
+#include "sqtable.h"
+#include "squserdata.h"
+#include "sqfuncproto.h"
+#include "sqclass.h"
+#include "sqclosure.h"
+
+SQString *SQString::Create(SQSharedState *ss,const SQChar *s,SQInteger len)
+{
+ SQString *str=ADD_STRING(ss,s,len);
+ str->_sharedstate=ss;
+ return str;
+}
+
+void SQString::Release()
+{
+ REMOVE_STRING(_sharedstate,this);
+}
+
+SQInteger SQString::Next(const SQObjectPtr &refpos, SQObjectPtr &outkey, SQObjectPtr &outval)
+{
+ SQInteger idx = (SQInteger)TranslateIndex(refpos);
+ while(idx < _len){
+ outkey = (SQInteger)idx;
+ outval = SQInteger(_val[idx]);
+ //return idx for the next iteration
+ return ++idx;
+ }
+ //nothing to iterate anymore
+ return -1;
+}
+
+SQUnsignedInteger TranslateIndex(const SQObjectPtr &idx)
+{
+ switch(type(idx)){
+ case OT_NULL:
+ return 0;
+ case OT_INTEGER:
+ return (SQUnsignedInteger)_integer(idx);
+ default: assert(0); break;
+ }
+ return 0;
+}
+
+SQWeakRef *SQRefCounted::GetWeakRef(SQObjectType type)
+{
+ if(!_weakref) {
+ sq_new(_weakref,SQWeakRef);
+ _weakref->_obj._type = type;
+ _weakref->_obj._unVal.pRefCounted = this;
+ }
+ return _weakref;
+}
+
+SQRefCounted::~SQRefCounted()
+{
+ if(_weakref) {
+ _weakref->_obj._type = OT_NULL;
+ _weakref->_obj._unVal.pRefCounted = NULL;
+ }
+}
+
+void SQWeakRef::Release() {
+ if(ISREFCOUNTED(_obj._type)) {
+ _obj._unVal.pRefCounted->_weakref = NULL;
+ }
+ sq_delete(this,SQWeakRef);
+}
+
+bool SQDelegable::GetMetaMethod(SQVM *v,SQMetaMethod mm,SQObjectPtr &res) {
+ if(_delegate) {
+ return _delegate->Get((*_ss(v)->_metamethods)[mm],res);
+ }
+ return false;
+}
+
+bool SQDelegable::SetDelegate(SQTable *mt)
+{
+ SQTable *temp = mt;
+ while (temp) {
+ if (temp->_delegate == this) return false; //cycle detected
+ temp = temp->_delegate;
+ }
+ if (mt) __ObjAddRef(mt);
+ __ObjRelease(_delegate);
+ _delegate = mt;
+ return true;
+}
+
+bool SQGenerator::Yield(SQVM *v)
+{
+ if(_state==eSuspended) { v->Raise_Error(_SC("internal vm error, yielding dead generator")); return false;}
+ if(_state==eDead) { v->Raise_Error(_SC("internal vm error, yielding a dead generator")); return false; }
+ SQInteger size = v->_top-v->_stackbase;
+ _ci=*v->ci;
+ _stack.resize(size);
+ for(SQInteger n =0; n<size; n++) {
+ _stack._vals[n] = v->_stack[v->_stackbase+n];
+ v->_stack[v->_stackbase+n] = _null_;
+ }
+ SQInteger nvargs = v->ci->_vargs.size;
+ SQInteger vargsbase = v->ci->_vargs.base;
+ for(SQInteger j = nvargs - 1; j >= 0; j--) {
+ _vargsstack.push_back(v->_vargsstack[vargsbase+j]);
+ }
+ _ci._generator=_null_;
+ for(SQInteger i=0;i<_ci._etraps;i++) {
+ _etraps.push_back(v->_etraps.top());
+ v->_etraps.pop_back();
+ }
+ _state=eSuspended;
+ return true;
+}
+
+bool SQGenerator::Resume(SQVM *v,SQInteger target)
+{
+ SQInteger size=_stack.size();
+ if(_state==eDead){ v->Raise_Error(_SC("resuming dead generator")); return false; }
+ if(_state==eRunning){ v->Raise_Error(_SC("resuming active generator")); return false; }
+ SQInteger prevtop=v->_top-v->_stackbase;
+ PUSH_CALLINFO(v,_ci);
+ SQInteger oldstackbase=v->_stackbase;
+ v->_stackbase = v->_top;
+ v->ci->_target = (SQInt32)target;
+ v->ci->_generator = SQObjectPtr(this);
+ v->ci->_vargs.size = (unsigned short)_vargsstack.size();
+
+ for(SQInteger i=0;i<_ci._etraps;i++) {
+ v->_etraps.push_back(_etraps.top());
+ _etraps.pop_back();
+ }
+ for(SQInteger n =0; n<size; n++) {
+ v->_stack[v->_stackbase+n] = _stack._vals[n];
+ _stack._vals[0] = _null_;
+ }
+ while(_vargsstack.size()) {
+ v->_vargsstack.push_back(_vargsstack.back());
+ _vargsstack.pop_back();
+ }
+ v->ci->_vargs.base = (unsigned short)(v->_vargsstack.size() - v->ci->_vargs.size);
+ v->_top=v->_stackbase+size;
+ v->ci->_prevtop = (SQInt32)prevtop;
+ v->ci->_prevstkbase = (SQInt32)(v->_stackbase - oldstackbase);
+ _state=eRunning;
+ return true;
+}
+
+void SQArray::Extend(const SQArray *a){
+ SQInteger xlen;
+ if((xlen=a->Size()))
+ for(SQInteger i=0;i<xlen;i++)
+ Append(a->_values[i]);
+}
+
+const SQChar* SQFunctionProto::GetLocal(SQVM *vm,SQUnsignedInteger stackbase,SQUnsignedInteger nseq,SQUnsignedInteger nop)
+{
+ SQUnsignedInteger nvars=_nlocalvarinfos;
+ const SQChar *res=NULL;
+ if(nvars>=nseq){
+ for(SQUnsignedInteger i=0;i<nvars;i++){
+ if(_localvarinfos[i]._start_op<=nop && _localvarinfos[i]._end_op>=nop)
+ {
+ if(nseq==0){
+ vm->Push(vm->_stack[stackbase+_localvarinfos[i]._pos]);
+ res=_stringval(_localvarinfos[i]._name);
+ break;
+ }
+ nseq--;
+ }
+ }
+ }
+ return res;
+}
+
+SQInteger SQFunctionProto::GetLine(SQInstruction *curr)
+{
+ SQInteger op = (SQInteger)(curr-_instructions);
+ SQInteger line=_lineinfos[0]._line;
+ for(SQInteger i=1;i<_nlineinfos;i++){
+ if(_lineinfos[i]._op>=op)
+ return line;
+ line=_lineinfos[i]._line;
+ }
+ return line;
+}
+
+#define _CHECK_IO(exp) { if(!exp)return false; }
+bool SafeWrite(HSQUIRRELVM v,SQWRITEFUNC write,SQUserPointer up,SQUserPointer dest,SQInteger size)
+{
+ if(write(up,dest,size) != size) {
+ v->Raise_Error(_SC("io error (write function failure)"));
+ return false;
+ }
+ return true;
+}
+
+bool SafeRead(HSQUIRRELVM v,SQWRITEFUNC read,SQUserPointer up,SQUserPointer dest,SQInteger size)
+{
+ if(size && read(up,dest,size) != size) {
+ v->Raise_Error(_SC("io error, read function failure, the origin stream could be corrupted/trucated"));
+ return false;
+ }
+ return true;
+}
+
+bool WriteTag(HSQUIRRELVM v,SQWRITEFUNC write,SQUserPointer up,SQInteger tag)
+{
+ return SafeWrite(v,write,up,&tag,sizeof(tag));
+}
+
+bool CheckTag(HSQUIRRELVM v,SQWRITEFUNC read,SQUserPointer up,SQInteger tag)
+{
+ SQInteger t;
+ _CHECK_IO(SafeRead(v,read,up,&t,sizeof(t)));
+ if(t != tag){
+ v->Raise_Error(_SC("invalid or corrupted closure stream"));
+ return false;
+ }
+ return true;
+}
+
+bool WriteObject(HSQUIRRELVM v,SQUserPointer up,SQWRITEFUNC write,SQObjectPtr &o)
+{
+ _CHECK_IO(SafeWrite(v,write,up,&type(o),sizeof(SQObjectType)));
+ switch(type(o)){
+ case OT_STRING:
+ _CHECK_IO(SafeWrite(v,write,up,&_string(o)->_len,sizeof(SQInteger)));
+ _CHECK_IO(SafeWrite(v,write,up,_stringval(o),rsl(_string(o)->_len)));
+ break;
+ case OT_INTEGER:
+ _CHECK_IO(SafeWrite(v,write,up,&_integer(o),sizeof(SQInteger)));break;
+ case OT_FLOAT:
+ _CHECK_IO(SafeWrite(v,write,up,&_float(o),sizeof(SQFloat)));break;
+ case OT_NULL:
+ break;
+ default:
+ v->Raise_Error(_SC("cannot serialize a %s"),GetTypeName(o));
+ return false;
+ }
+ return true;
+}
+
+bool ReadObject(HSQUIRRELVM v,SQUserPointer up,SQREADFUNC read,SQObjectPtr &o)
+{
+ SQObjectType t;
+ _CHECK_IO(SafeRead(v,read,up,&t,sizeof(SQObjectType)));
+ switch(t){
+ case OT_STRING:{
+ SQInteger len;
+ _CHECK_IO(SafeRead(v,read,up,&len,sizeof(SQInteger)));
+ _CHECK_IO(SafeRead(v,read,up,_ss(v)->GetScratchPad(rsl(len)),rsl(len)));
+ o=SQString::Create(_ss(v),_ss(v)->GetScratchPad(-1),len);
+ }
+ break;
+ case OT_INTEGER:{
+ SQInteger i;
+ _CHECK_IO(SafeRead(v,read,up,&i,sizeof(SQInteger))); o = i; break;
+ }
+ case OT_FLOAT:{
+ SQFloat f;
+ _CHECK_IO(SafeRead(v,read,up,&f,sizeof(SQFloat))); o = f; break;
+ }
+ case OT_NULL:
+ o=_null_;
+ break;
+ default:
+ v->Raise_Error(_SC("cannot serialize a %s"),IdType2Name(t));
+ return false;
+ }
+ return true;
+}
+
+bool SQClosure::Save(SQVM *v,SQUserPointer up,SQWRITEFUNC write)
+{
+ _CHECK_IO(WriteTag(v,write,up,SQ_CLOSURESTREAM_HEAD));
+ _CHECK_IO(WriteTag(v,write,up,sizeof(SQChar)));
+ _CHECK_IO(_funcproto(_function)->Save(v,up,write));
+ _CHECK_IO(WriteTag(v,write,up,SQ_CLOSURESTREAM_TAIL));
+ return true;
+}
+
+bool SQClosure::Load(SQVM *v,SQUserPointer up,SQREADFUNC read,SQObjectPtr &ret)
+{
+ _CHECK_IO(CheckTag(v,read,up,SQ_CLOSURESTREAM_HEAD));
+ _CHECK_IO(CheckTag(v,read,up,sizeof(SQChar)));
+ SQObjectPtr func;
+ _CHECK_IO(SQFunctionProto::Load(v,up,read,func));
+ _CHECK_IO(CheckTag(v,read,up,SQ_CLOSURESTREAM_TAIL));
+ ret = SQClosure::Create(_ss(v),_funcproto(func));
+ return true;
+}
+
+bool SQFunctionProto::Save(SQVM *v,SQUserPointer up,SQWRITEFUNC write)
+{
+ SQInteger i,nliterals = _nliterals,nparameters = _nparameters;
+ SQInteger noutervalues = _noutervalues,nlocalvarinfos = _nlocalvarinfos;
+ SQInteger nlineinfos=_nlineinfos,ninstructions = _ninstructions,nfunctions=_nfunctions;
+ _CHECK_IO(WriteTag(v,write,up,SQ_CLOSURESTREAM_PART));
+ _CHECK_IO(WriteObject(v,up,write,_sourcename));
+ _CHECK_IO(WriteObject(v,up,write,_name));
+ _CHECK_IO(WriteTag(v,write,up,SQ_CLOSURESTREAM_PART));
+ _CHECK_IO(SafeWrite(v,write,up,&nliterals,sizeof(nliterals)));
+ _CHECK_IO(SafeWrite(v,write,up,&nparameters,sizeof(nparameters)));
+ _CHECK_IO(SafeWrite(v,write,up,&noutervalues,sizeof(noutervalues)));
+ _CHECK_IO(SafeWrite(v,write,up,&nlocalvarinfos,sizeof(nlocalvarinfos)));
+ _CHECK_IO(SafeWrite(v,write,up,&nlineinfos,sizeof(nlineinfos)));
+ _CHECK_IO(SafeWrite(v,write,up,&ninstructions,sizeof(ninstructions)));
+ _CHECK_IO(SafeWrite(v,write,up,&nfunctions,sizeof(nfunctions)));
+ _CHECK_IO(WriteTag(v,write,up,SQ_CLOSURESTREAM_PART));
+ for(i=0;i<nliterals;i++){
+ _CHECK_IO(WriteObject(v,up,write,_literals[i]));
+ }
+
+ _CHECK_IO(WriteTag(v,write,up,SQ_CLOSURESTREAM_PART));
+ for(i=0;i<nparameters;i++){
+ _CHECK_IO(WriteObject(v,up,write,_parameters[i]));
+ }
+
+ _CHECK_IO(WriteTag(v,write,up,SQ_CLOSURESTREAM_PART));
+ for(i=0;i<noutervalues;i++){
+ _CHECK_IO(SafeWrite(v,write,up,&_outervalues[i]._type,sizeof(SQUnsignedInteger)));
+ _CHECK_IO(WriteObject(v,up,write,_outervalues[i]._src));
+ _CHECK_IO(WriteObject(v,up,write,_outervalues[i]._name));
+ }
+
+ _CHECK_IO(WriteTag(v,write,up,SQ_CLOSURESTREAM_PART));
+ for(i=0;i<nlocalvarinfos;i++){
+ SQLocalVarInfo &lvi=_localvarinfos[i];
+ _CHECK_IO(WriteObject(v,up,write,lvi._name));
+ _CHECK_IO(SafeWrite(v,write,up,&lvi._pos,sizeof(SQUnsignedInteger)));
+ _CHECK_IO(SafeWrite(v,write,up,&lvi._start_op,sizeof(SQUnsignedInteger)));
+ _CHECK_IO(SafeWrite(v,write,up,&lvi._end_op,sizeof(SQUnsignedInteger)));
+ }
+
+ _CHECK_IO(WriteTag(v,write,up,SQ_CLOSURESTREAM_PART));
+ _CHECK_IO(SafeWrite(v,write,up,_lineinfos,sizeof(SQLineInfo)*nlineinfos));
+
+ _CHECK_IO(WriteTag(v,write,up,SQ_CLOSURESTREAM_PART));
+ _CHECK_IO(SafeWrite(v,write,up,_instructions,sizeof(SQInstruction)*ninstructions));
+
+ _CHECK_IO(WriteTag(v,write,up,SQ_CLOSURESTREAM_PART));
+ for(i=0;i<nfunctions;i++){
+ _CHECK_IO(_funcproto(_functions[i])->Save(v,up,write));
+ }
+ _CHECK_IO(SafeWrite(v,write,up,&_stacksize,sizeof(_stacksize)));
+ _CHECK_IO(SafeWrite(v,write,up,&_bgenerator,sizeof(_bgenerator)));
+ _CHECK_IO(SafeWrite(v,write,up,&_varparams,sizeof(_varparams)));
+ return true;
+}
+
+bool SQFunctionProto::Load(SQVM *v,SQUserPointer up,SQREADFUNC read,SQObjectPtr &ret)
+{
+ SQInteger i, nliterals,nparameters;
+ SQInteger noutervalues ,nlocalvarinfos ;
+ SQInteger nlineinfos,ninstructions ,nfunctions ;
+ SQObjectPtr sourcename, name;
+ SQObjectPtr o;
+ _CHECK_IO(CheckTag(v,read,up,SQ_CLOSURESTREAM_PART));
+ _CHECK_IO(ReadObject(v, up, read, sourcename));
+ _CHECK_IO(ReadObject(v, up, read, name));
+
+ _CHECK_IO(CheckTag(v,read,up,SQ_CLOSURESTREAM_PART));
+ _CHECK_IO(SafeRead(v,read,up, &nliterals, sizeof(nliterals)));
+ _CHECK_IO(SafeRead(v,read,up, &nparameters, sizeof(nparameters)));
+ _CHECK_IO(SafeRead(v,read,up, &noutervalues, sizeof(noutervalues)));
+ _CHECK_IO(SafeRead(v,read,up, &nlocalvarinfos, sizeof(nlocalvarinfos)));
+ _CHECK_IO(SafeRead(v,read,up, &nlineinfos, sizeof(nlineinfos)));
+ _CHECK_IO(SafeRead(v,read,up, &ninstructions, sizeof(ninstructions)));
+ _CHECK_IO(SafeRead(v,read,up, &nfunctions, sizeof(nfunctions)));
+
+ SQFunctionProto *f = SQFunctionProto::Create(ninstructions,nliterals,nparameters,nfunctions,noutervalues,nlineinfos,nlocalvarinfos);
+ SQObjectPtr proto = f; //gets a ref in case of failure
+ f->_sourcename = sourcename;
+ f->_name = name;
+
+ _CHECK_IO(CheckTag(v,read,up,SQ_CLOSURESTREAM_PART));
+
+ for(i = 0;i < nliterals; i++){
+ _CHECK_IO(ReadObject(v, up, read, o));
+ f->_literals[i] = o;
+ }
+ _CHECK_IO(CheckTag(v,read,up,SQ_CLOSURESTREAM_PART));
+
+ for(i = 0; i < nparameters; i++){
+ _CHECK_IO(ReadObject(v, up, read, o));
+ f->_parameters[i] = o;
+ }
+ _CHECK_IO(CheckTag(v,read,up,SQ_CLOSURESTREAM_PART));
+
+ for(i = 0; i < noutervalues; i++){
+ SQUnsignedInteger type;
+ SQObjectPtr name;
+ _CHECK_IO(SafeRead(v,read,up, &type, sizeof(SQUnsignedInteger)));
+ _CHECK_IO(ReadObject(v, up, read, o));
+ _CHECK_IO(ReadObject(v, up, read, name));
+ f->_outervalues[i] = SQOuterVar(name,o, (SQOuterType)type);
+ }
+ _CHECK_IO(CheckTag(v,read,up,SQ_CLOSURESTREAM_PART));
+
+ for(i = 0; i < nlocalvarinfos; i++){
+ SQLocalVarInfo lvi;
+ _CHECK_IO(ReadObject(v, up, read, lvi._name));
+ _CHECK_IO(SafeRead(v,read,up, &lvi._pos, sizeof(SQUnsignedInteger)));
+ _CHECK_IO(SafeRead(v,read,up, &lvi._start_op, sizeof(SQUnsignedInteger)));
+ _CHECK_IO(SafeRead(v,read,up, &lvi._end_op, sizeof(SQUnsignedInteger)));
+ f->_localvarinfos[i] = lvi;
+ }
+ _CHECK_IO(CheckTag(v,read,up,SQ_CLOSURESTREAM_PART));
+ _CHECK_IO(SafeRead(v,read,up, f->_lineinfos, sizeof(SQLineInfo)*nlineinfos));
+
+ _CHECK_IO(CheckTag(v,read,up,SQ_CLOSURESTREAM_PART));
+ _CHECK_IO(SafeRead(v,read,up, f->_instructions, sizeof(SQInstruction)*ninstructions));
+
+ _CHECK_IO(CheckTag(v,read,up,SQ_CLOSURESTREAM_PART));
+ for(i = 0; i < nfunctions; i++){
+ _CHECK_IO(_funcproto(o)->Load(v, up, read, o));
+ f->_functions[i] = o;
+ }
+ _CHECK_IO(SafeRead(v,read,up, &f->_stacksize, sizeof(f->_stacksize)));
+ _CHECK_IO(SafeRead(v,read,up, &f->_bgenerator, sizeof(f->_bgenerator)));
+ _CHECK_IO(SafeRead(v,read,up, &f->_varparams, sizeof(f->_varparams)));
+ ret = f;
+ return true;
+}
+
+#ifndef NO_GARBAGE_COLLECTOR
+
+#define START_MARK() if(!(_uiRef&MARK_FLAG)){ \
+ _uiRef|=MARK_FLAG;
+
+#define END_MARK() RemoveFromChain(&_sharedstate->_gc_chain, this); \
+ AddToChain(chain, this); }
+
+void SQVM::Mark(SQCollectable **chain)
+{
+ START_MARK()
+ SQSharedState::MarkObject(_lasterror,chain);
+ SQSharedState::MarkObject(_errorhandler,chain);
+ SQSharedState::MarkObject(_debughook,chain);
+ SQSharedState::MarkObject(_roottable, chain);
+ SQSharedState::MarkObject(temp_reg, chain);
+ for(SQUnsignedInteger i = 0; i < _stack.size(); i++) SQSharedState::MarkObject(_stack[i], chain);
+ for(SQUnsignedInteger j = 0; j < _vargsstack.size(); j++) SQSharedState::MarkObject(_vargsstack[j], chain);
+ END_MARK()
+}
+
+void SQArray::Mark(SQCollectable **chain)
+{
+ START_MARK()
+ SQInteger len = _values.size();
+ for(SQInteger i = 0;i < len; i++) SQSharedState::MarkObject(_values[i], chain);
+ END_MARK()
+}
+void SQTable::Mark(SQCollectable **chain)
+{
+ START_MARK()
+ if(_delegate) _delegate->Mark(chain);
+ SQInteger len = _numofnodes;
+ for(SQInteger i = 0; i < len; i++){
+ SQSharedState::MarkObject(_nodes[i].key, chain);
+ SQSharedState::MarkObject(_nodes[i].val, chain);
+ }
+ END_MARK()
+}
+
+void SQClass::Mark(SQCollectable **chain)
+{
+ START_MARK()
+ _members->Mark(chain);
+ if(_base) _base->Mark(chain);
+ SQSharedState::MarkObject(_attributes, chain);
+ for(SQUnsignedInteger i =0; i< _defaultvalues.size(); i++) {
+ SQSharedState::MarkObject(_defaultvalues[i].val, chain);
+ SQSharedState::MarkObject(_defaultvalues[i].attrs, chain);
+ }
+ for(SQUnsignedInteger j =0; j< _methods.size(); j++) {
+ SQSharedState::MarkObject(_methods[j].val, chain);
+ SQSharedState::MarkObject(_methods[j].attrs, chain);
+ }
+ for(SQUnsignedInteger k =0; k< _metamethods.size(); k++) {
+ SQSharedState::MarkObject(_metamethods[k], chain);
+ }
+ END_MARK()
+}
+
+void SQInstance::Mark(SQCollectable **chain)
+{
+ START_MARK()
+ _class->Mark(chain);
+ SQUnsignedInteger nvalues = _class->_defaultvalues.size();
+ for(SQUnsignedInteger i =0; i< nvalues; i++) {
+ SQSharedState::MarkObject(_values[i], chain);
+ }
+ END_MARK()
+}
+
+void SQGenerator::Mark(SQCollectable **chain)
+{
+ START_MARK()
+ for(SQUnsignedInteger i = 0; i < _stack.size(); i++) SQSharedState::MarkObject(_stack[i], chain);
+ for(SQUnsignedInteger j = 0; j < _vargsstack.size(); j++) SQSharedState::MarkObject(_vargsstack[j], chain);
+ SQSharedState::MarkObject(_closure, chain);
+ END_MARK()
+}
+
+void SQClosure::Mark(SQCollectable **chain)
+{
+ START_MARK()
+ for(SQUnsignedInteger i = 0; i < _outervalues.size(); i++) SQSharedState::MarkObject(_outervalues[i], chain);
+ END_MARK()
+}
+
+void SQNativeClosure::Mark(SQCollectable **chain)
+{
+ START_MARK()
+ for(SQUnsignedInteger i = 0; i < _outervalues.size(); i++) SQSharedState::MarkObject(_outervalues[i], chain);
+ END_MARK()
+}
+
+void SQUserData::Mark(SQCollectable **chain){
+ START_MARK()
+ if(_delegate) _delegate->Mark(chain);
+ END_MARK()
+}
+
+void SQCollectable::UnMark() { _uiRef&=~MARK_FLAG; }
+
+#endif
+
--- /dev/null
+/* see copyright notice in squirrel.h */
+#ifndef _SQOBJECT_H_
+#define _SQOBJECT_H_
+
+#include "squtils.h"
+
+#define SQ_CLOSURESTREAM_HEAD (('S'<<24)|('Q'<<16)|('I'<<8)|('R'))
+#define SQ_CLOSURESTREAM_PART (('P'<<24)|('A'<<16)|('R'<<8)|('T'))
+#define SQ_CLOSURESTREAM_TAIL (('T'<<24)|('A'<<16)|('I'<<8)|('L'))
+
+struct SQSharedState;
+
+enum SQMetaMethod{
+ MT_ADD=0,
+ MT_SUB=1,
+ MT_MUL=2,
+ MT_DIV=3,
+ MT_UNM=4,
+ MT_MODULO=5,
+ MT_SET=6,
+ MT_GET=7,
+ MT_TYPEOF=8,
+ MT_NEXTI=9,
+ MT_CMP=10,
+ MT_CALL=11,
+ MT_CLONED=12,
+ MT_NEWSLOT=13,
+ MT_DELSLOT=14,
+ MT_TOSTRING=15,
+ MT_NEWMEMBER=16,
+ MT_INHERITED=17,
+ MT_LAST = 18
+};
+
+#define MM_ADD _SC("_add")
+#define MM_SUB _SC("_sub")
+#define MM_MUL _SC("_mul")
+#define MM_DIV _SC("_div")
+#define MM_UNM _SC("_unm")
+#define MM_MODULO _SC("_modulo")
+#define MM_SET _SC("_set")
+#define MM_GET _SC("_get")
+#define MM_TYPEOF _SC("_typeof")
+#define MM_NEXTI _SC("_nexti")
+#define MM_CMP _SC("_cmp")
+#define MM_CALL _SC("_call")
+#define MM_CLONED _SC("_cloned")
+#define MM_NEWSLOT _SC("_newslot")
+#define MM_DELSLOT _SC("_delslot")
+#define MM_TOSTRING _SC("_tostring")
+#define MM_NEWMEMBER _SC("_newmember")
+#define MM_INHERITED _SC("_inherited")
+
+#define MINPOWER2 4
+
+struct SQRefCounted
+{
+ SQRefCounted() { _uiRef = 0; _weakref = NULL; }
+ virtual ~SQRefCounted();
+ SQWeakRef *GetWeakRef(SQObjectType type);
+ SQUnsignedInteger _uiRef;
+ struct SQWeakRef *_weakref;
+ virtual void Release()=0;
+};
+
+struct SQWeakRef : SQRefCounted
+{
+ void Release();
+ SQObject _obj;
+};
+
+#define _realval(o) (type((o)) != OT_WEAKREF?(SQObject)o:_weakref(o)->_obj)
+
+struct SQObjectPtr;
+
+#define __AddRef(type,unval) if(ISREFCOUNTED(type)) \
+ { \
+ unval.pRefCounted->_uiRef++; \
+ }
+
+#define __Release(type,unval) if(ISREFCOUNTED(type) && ((--unval.pRefCounted->_uiRef)<=0)) \
+ { \
+ unval.pRefCounted->Release(); \
+ }
+
+#define __ObjRelease(obj) { \
+ if((obj)) { \
+ (obj)->_uiRef--; \
+ if((obj)->_uiRef == 0) \
+ (obj)->Release(); \
+ (obj) = NULL; \
+ } \
+}
+
+#define __ObjAddRef(obj) { \
+ (obj)->_uiRef++; \
+}
+
+#define type(obj) ((obj)._type)
+#define is_delegable(t) (type(t)&SQOBJECT_DELEGABLE)
+#define raw_type(obj) _RAW_TYPE((obj)._type)
+
+#define _integer(obj) ((obj)._unVal.nInteger)
+#define _float(obj) ((obj)._unVal.fFloat)
+#define _string(obj) ((obj)._unVal.pString)
+#define _table(obj) ((obj)._unVal.pTable)
+#define _array(obj) ((obj)._unVal.pArray)
+#define _closure(obj) ((obj)._unVal.pClosure)
+#define _generator(obj) ((obj)._unVal.pGenerator)
+#define _nativeclosure(obj) ((obj)._unVal.pNativeClosure)
+#define _userdata(obj) ((obj)._unVal.pUserData)
+#define _userpointer(obj) ((obj)._unVal.pUserPointer)
+#define _thread(obj) ((obj)._unVal.pThread)
+#define _funcproto(obj) ((obj)._unVal.pFunctionProto)
+#define _class(obj) ((obj)._unVal.pClass)
+#define _instance(obj) ((obj)._unVal.pInstance)
+#define _delegable(obj) ((SQDelegable *)(obj)._unVal.pDelegable)
+#define _weakref(obj) ((obj)._unVal.pWeakRef)
+#define _refcounted(obj) ((obj)._unVal.pRefCounted)
+#define _rawval(obj) ((obj)._unVal.pRefCounted)
+
+#define _stringval(obj) (obj)._unVal.pString->_val
+#define _userdataval(obj) (obj)._unVal.pUserData->_val
+
+#define tofloat(num) ((type(num)==OT_INTEGER)?(SQFloat)_integer(num):_float(num))
+#define tointeger(num) ((type(num)==OT_FLOAT)?(SQInteger)_float(num):_integer(num))
+/////////////////////////////////////////////////////////////////////////////////////
+/////////////////////////////////////////////////////////////////////////////////////
+struct SQObjectPtr : public SQObject
+{
+ SQObjectPtr()
+ {
+ _type=OT_NULL;
+ _unVal.pUserPointer=NULL;
+ }
+ SQObjectPtr(const SQObjectPtr &o)
+ {
+ _type=o._type;
+ _unVal=o._unVal;
+ __AddRef(_type,_unVal);
+ }
+ SQObjectPtr(const SQObject &o)
+ {
+ _type=o._type;
+ _unVal=o._unVal;
+ __AddRef(_type,_unVal);
+ }
+ SQObjectPtr(SQTable *pTable)
+ {
+ _type=OT_TABLE;
+ _unVal.pTable=pTable;
+ assert(_unVal.pTable);
+ __AddRef(_type,_unVal);
+ }
+ SQObjectPtr(SQClass *pClass)
+ {
+ _type=OT_CLASS;
+ _unVal.pClass=pClass;
+ assert(_unVal.pClass);
+ __AddRef(_type,_unVal);
+ }
+ SQObjectPtr(SQInstance *pInstance)
+ {
+ _type=OT_INSTANCE;
+ _unVal.pInstance=pInstance;
+ assert(_unVal.pInstance);
+ __AddRef(_type,_unVal);
+ }
+ SQObjectPtr(SQArray *pArray)
+ {
+ _type=OT_ARRAY;
+ _unVal.pArray=pArray;
+ assert(_unVal.pArray);
+ __AddRef(_type,_unVal);
+ }
+ SQObjectPtr(SQClosure *pClosure)
+ {
+ _type=OT_CLOSURE;
+ _unVal.pClosure=pClosure;
+ assert(_unVal.pClosure);
+ __AddRef(_type,_unVal);
+ }
+ SQObjectPtr(SQGenerator *pGenerator)
+ {
+ _type=OT_GENERATOR;
+ _unVal.pGenerator=pGenerator;
+ assert(_unVal.pGenerator);
+ __AddRef(_type,_unVal);
+ }
+ SQObjectPtr(SQNativeClosure *pNativeClosure)
+ {
+ _type=OT_NATIVECLOSURE;
+ _unVal.pNativeClosure=pNativeClosure;
+ assert(_unVal.pNativeClosure);
+ __AddRef(_type,_unVal);
+ }
+ SQObjectPtr(SQString *pString)
+ {
+ _type=OT_STRING;
+ _unVal.pString=pString;
+ assert(_unVal.pString);
+ __AddRef(_type,_unVal);
+ }
+ SQObjectPtr(SQUserData *pUserData)
+ {
+ _type=OT_USERDATA;
+ _unVal.pUserData=pUserData;
+ assert(_unVal.pUserData);
+ __AddRef(_type,_unVal);
+ }
+ SQObjectPtr(SQVM *pThread)
+ {
+ _type=OT_THREAD;
+ _unVal.pThread=pThread;
+ assert(_unVal.pThread);
+ __AddRef(_type,_unVal);
+ }
+ SQObjectPtr(SQWeakRef *pWeakRef)
+ {
+ _type=OT_WEAKREF;
+ _unVal.pWeakRef=pWeakRef;
+ assert(_unVal.pWeakRef);
+ __AddRef(_type,_unVal);
+ }
+ SQObjectPtr(SQFunctionProto *pFunctionProto)
+ {
+ _type=OT_FUNCPROTO;
+ _unVal.pFunctionProto=pFunctionProto;
+ assert(_unVal.pFunctionProto);
+ __AddRef(_type,_unVal);
+ }
+ SQObjectPtr(SQInteger nInteger)
+ {
+ _unVal.pUserPointer=NULL;
+ _type=OT_INTEGER;
+ _unVal.nInteger=nInteger;
+ }
+ SQObjectPtr(SQFloat fFloat)
+ {
+ _unVal.pUserPointer=NULL;
+ _type=OT_FLOAT;
+ _unVal.fFloat=fFloat;
+ }
+ SQObjectPtr(bool bBool)
+ {
+ _unVal.pUserPointer=NULL;
+ _type = OT_BOOL;
+ _unVal.nInteger = bBool?1:0;
+ }
+ SQObjectPtr(SQUserPointer pUserPointer)
+ {
+ _type=OT_USERPOINTER;
+ _unVal.pUserPointer=pUserPointer;
+ }
+ ~SQObjectPtr()
+ {
+ __Release(_type,_unVal);
+ }
+ inline void Null()
+ {
+ SQObjectType tOldType;
+ SQObjectValue unOldVal;
+ tOldType = _type;
+ unOldVal = _unVal;
+ _type = OT_NULL;
+ _unVal.pUserPointer = NULL;
+ __Release(tOldType,unOldVal);
+ }
+ inline SQObjectPtr& operator=(SQInteger i)
+ {
+ __Release(_type,_unVal);
+ _unVal.nInteger = i;
+ _type = OT_INTEGER;
+ return *this;
+ }
+ inline SQObjectPtr& operator=(SQFloat f)
+ {
+ __Release(_type,_unVal);
+ _unVal.fFloat = f;
+ _type = OT_FLOAT;
+ return *this;
+ }
+ inline SQObjectPtr& operator=(const SQObjectPtr& obj)
+ {
+ SQObjectType tOldType;
+ SQObjectValue unOldVal;
+ tOldType=_type;
+ unOldVal=_unVal;
+ _unVal = obj._unVal;
+ _type = obj._type;
+ __AddRef(_type,_unVal);
+ __Release(tOldType,unOldVal);
+ return *this;
+ }
+ inline SQObjectPtr& operator=(const SQObject& obj)
+ {
+ SQObjectType tOldType;
+ SQObjectValue unOldVal;
+ tOldType=_type;
+ unOldVal=_unVal;
+ _unVal = obj._unVal;
+ _type = obj._type;
+ __AddRef(_type,_unVal);
+ __Release(tOldType,unOldVal);
+ return *this;
+ }
+ private:
+ SQObjectPtr(const SQChar *){} //safety
+};
+/////////////////////////////////////////////////////////////////////////////////////
+#ifndef NO_GARBAGE_COLLECTOR
+#define MARK_FLAG 0x80000000
+struct SQCollectable : public SQRefCounted {
+ SQCollectable *_next;
+ SQCollectable *_prev;
+ SQSharedState *_sharedstate;
+ virtual void Release()=0;
+ virtual void Mark(SQCollectable **chain)=0;
+ void UnMark();
+ virtual void Finalize()=0;
+ static void AddToChain(SQCollectable **chain,SQCollectable *c);
+ static void RemoveFromChain(SQCollectable **chain,SQCollectable *c);
+};
+
+
+#define ADD_TO_CHAIN(chain,obj) AddToChain(chain,obj)
+#define REMOVE_FROM_CHAIN(chain,obj) {if(!(_uiRef&MARK_FLAG))RemoveFromChain(chain,obj);}
+#define CHAINABLE_OBJ SQCollectable
+#define INIT_CHAIN() {_next=NULL;_prev=NULL;_sharedstate=ss;}
+#else
+
+#define ADD_TO_CHAIN(chain,obj) ((void)0)
+#define REMOVE_FROM_CHAIN(chain,obj) ((void)0)
+#define CHAINABLE_OBJ SQRefCounted
+#define INIT_CHAIN() ((void)0)
+#endif
+
+struct SQDelegable : public CHAINABLE_OBJ {
+ bool SetDelegate(SQTable *m);
+ virtual bool GetMetaMethod(SQVM *v,SQMetaMethod mm,SQObjectPtr &res);
+ SQTable *_delegate;
+};
+
+SQUnsignedInteger TranslateIndex(const SQObjectPtr &idx);
+typedef sqvector<SQObjectPtr> SQObjectPtrVec;
+typedef sqvector<SQInteger> SQIntVec;
+
+
+#endif //_SQOBJECT_H_
--- /dev/null
+/* see copyright notice in squirrel.h */
+#ifndef _SQOPCODES_H_
+#define _SQOPCODES_H_
+
+#define MAX_FUNC_STACKSIZE 0xFF
+#define MAX_LITERALS ((SQInteger)0x7FFFFFFF)
+
+enum BitWiseOP {
+ BW_AND = 0,
+ BW_OR = 2,
+ BW_XOR = 3,
+ BW_SHIFTL = 4,
+ BW_SHIFTR = 5,
+ BW_USHIFTR = 6
+};
+
+enum CmpOP {
+ CMP_G = 0,
+ CMP_GE = 2,
+ CMP_L = 3,
+ CMP_LE = 4
+};
+enum SQOpcode
+{
+ _OP_LINE= 0x00,
+ _OP_LOAD= 0x01,
+ _OP_LOADINT= 0x02,
+ _OP_LOADFLOAT= 0x03,
+ _OP_DLOAD= 0x04,
+ _OP_TAILCALL= 0x05,
+ _OP_CALL= 0x06,
+ _OP_PREPCALL= 0x07,
+ _OP_PREPCALLK= 0x08,
+ _OP_GETK= 0x09,
+ _OP_MOVE= 0x0A,
+ _OP_NEWSLOT= 0x0B,
+ _OP_DELETE= 0x0C,
+ _OP_SET= 0x0D,
+ _OP_GET= 0x0E,
+ _OP_EQ= 0x0F,
+ _OP_NE= 0x10,
+ _OP_ARITH= 0x11,
+ _OP_BITW= 0x12,
+ _OP_RETURN= 0x13,
+ _OP_LOADNULLS= 0x14,
+ _OP_LOADROOTTABLE= 0x15,
+ _OP_LOADBOOL= 0x16,
+ _OP_DMOVE= 0x17,
+ _OP_JMP= 0x18,
+ _OP_JNZ= 0x19,
+ _OP_JZ= 0x1A,
+ _OP_LOADFREEVAR= 0x1B,
+ _OP_VARGC= 0x1C,
+ _OP_GETVARGV= 0x1D,
+ _OP_NEWTABLE= 0x1E,
+ _OP_NEWARRAY= 0x1F,
+ _OP_APPENDARRAY= 0x20,
+ _OP_GETPARENT= 0x21,
+ _OP_COMPARITH= 0x22,
+ _OP_COMPARITHL= 0x23,
+ _OP_INC= 0x24,
+ _OP_INCL= 0x25,
+ _OP_PINC= 0x26,
+ _OP_PINCL= 0x27,
+ _OP_CMP= 0x28,
+ _OP_EXISTS= 0x29,
+ _OP_INSTANCEOF= 0x2A,
+ _OP_AND= 0x2B,
+ _OP_OR= 0x2C,
+ _OP_NEG= 0x2D,
+ _OP_NOT= 0x2E,
+ _OP_BWNOT= 0x2F,
+ _OP_CLOSURE= 0x30,
+ _OP_YIELD= 0x31,
+ _OP_RESUME= 0x32,
+ _OP_FOREACH= 0x33,
+ _OP_POSTFOREACH= 0x34,
+ _OP_DELEGATE= 0x35,
+ _OP_CLONE= 0x36,
+ _OP_TYPEOF= 0x37,
+ _OP_PUSHTRAP= 0x38,
+ _OP_POPTRAP= 0x39,
+ _OP_THROW= 0x3A,
+ _OP_CLASS= 0x3B,
+ _OP_NEWSLOTA= 0x3C,
+};
+
+struct SQInstructionDesc {
+ const SQChar *name;
+};
+
+struct SQInstruction
+{
+ SQInstruction(){};
+ SQInstruction(SQOpcode _op,SQInteger a0=0,SQInteger a1=0,SQInteger a2=0,SQInteger a3=0)
+ { op = _op;
+ _arg0 = (unsigned char)a0;_arg1 = (SQInt32)a1;
+ _arg2 = (unsigned char)a2;_arg3 = (unsigned char)a3;
+ }
+
+
+ SQInt32 _arg1;
+ unsigned char op;
+ unsigned char _arg0;
+ unsigned char _arg2;
+ unsigned char _arg3;
+};
+
+#include "squtils.h"
+typedef sqvector<SQInstruction> SQInstructionVec;
+
+#define NEW_SLOT_ATTRIBUTES_FLAG 0x01
+#define NEW_SLOT_STATIC_FLAG 0x02
+
+#endif // _SQOPCODES_H_
--- /dev/null
+/* see copyright notice in squirrel.h */
+#ifndef _SQPCHEADER_H_
+#define _SQPCHEADER_H_
+
+#if defined(_MSC_VER) && defined(_DEBUG)
+#include <crtdbg.h>
+#endif
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <assert.h>
+#include <new>
+//squirrel stuff
+#include <squirrel.h>
+#include "sqobject.h"
+#include "sqstate.h"
+
+#endif //_SQPCHEADER_H_
--- /dev/null
+/*
+ see copyright notice in squirrel.h
+*/
+#include "sqpcheader.h"
+#include "sqopcodes.h"
+#include "sqvm.h"
+#include "sqfuncproto.h"
+#include "sqclosure.h"
+#include "sqstring.h"
+#include "sqtable.h"
+#include "sqarray.h"
+#include "squserdata.h"
+#include "sqclass.h"
+
+SQObjectPtr _null_;
+SQObjectPtr _true_(true);
+SQObjectPtr _false_(false);
+SQObjectPtr _one_((SQInteger)1);
+SQObjectPtr _minusone_((SQInteger)-1);
+
+SQSharedState::SQSharedState()
+{
+ _compilererrorhandler = NULL;
+ _printfunc = NULL;
+ _debuginfo = false;
+ _notifyallexceptions = false;
+}
+
+#define newsysstring(s) { \
+ _systemstrings->push_back(SQString::Create(this,s)); \
+ }
+
+#define newmetamethod(s) { \
+ _metamethods->push_back(SQString::Create(this,s)); \
+ _table(_metamethodsmap)->NewSlot(_metamethods->back(),(SQInteger)(_metamethods->size()-1)); \
+ }
+
+bool CompileTypemask(SQIntVec &res,const SQChar *typemask)
+{
+ SQInteger i = 0;
+
+ SQInteger mask = 0;
+ while(typemask[i] != 0) {
+
+ switch(typemask[i]){
+ case 'o': mask |= _RT_NULL; break;
+ case 'i': mask |= _RT_INTEGER; break;
+ case 'f': mask |= _RT_FLOAT; break;
+ case 'n': mask |= (_RT_FLOAT | _RT_INTEGER); break;
+ case 's': mask |= _RT_STRING; break;
+ case 't': mask |= _RT_TABLE; break;
+ case 'a': mask |= _RT_ARRAY; break;
+ case 'u': mask |= _RT_USERDATA; break;
+ case 'c': mask |= (_RT_CLOSURE | _RT_NATIVECLOSURE); break;
+ case 'b': mask |= _RT_BOOL; break;
+ case 'g': mask |= _RT_GENERATOR; break;
+ case 'p': mask |= _RT_USERPOINTER; break;
+ case 'v': mask |= _RT_THREAD; break;
+ case 'x': mask |= _RT_INSTANCE; break;
+ case 'y': mask |= _RT_CLASS; break;
+ case 'r': mask |= _RT_WEAKREF; break;
+ case '.': mask = -1; res.push_back(mask); i++; mask = 0; continue;
+ case ' ': i++; continue; //ignores spaces
+ default:
+ return false;
+ }
+ i++;
+ if(typemask[i] == '|') {
+ i++;
+ if(typemask[i] == 0)
+ return false;
+ continue;
+ }
+ res.push_back(mask);
+ mask = 0;
+
+ }
+ return true;
+}
+
+SQTable *CreateDefaultDelegate(SQSharedState *ss,SQRegFunction *funcz)
+{
+ SQInteger i=0;
+ SQTable *t=SQTable::Create(ss,0);
+ while(funcz[i].name!=0){
+ SQNativeClosure *nc = SQNativeClosure::Create(ss,funcz[i].f);
+ nc->_nparamscheck = funcz[i].nparamscheck;
+ nc->_name = SQString::Create(ss,funcz[i].name);
+ if(funcz[i].typemask && !CompileTypemask(nc->_typecheck,funcz[i].typemask))
+ return NULL;
+ t->NewSlot(SQString::Create(ss,funcz[i].name),nc);
+ i++;
+ }
+ return t;
+}
+
+void SQSharedState::Init()
+{
+ _scratchpad=NULL;
+ _scratchpadsize=0;
+#ifndef NO_GARBAGE_COLLECTOR
+ _gc_chain=NULL;
+#endif
+ sq_new(_stringtable,StringTable);
+ sq_new(_metamethods,SQObjectPtrVec);
+ sq_new(_systemstrings,SQObjectPtrVec);
+ sq_new(_types,SQObjectPtrVec);
+ _metamethodsmap = SQTable::Create(this,MT_LAST-1);
+ //adding type strings to avoid memory trashing
+ //types names
+ newsysstring(_SC("null"));
+ newsysstring(_SC("table"));
+ newsysstring(_SC("array"));
+ newsysstring(_SC("closure"));
+ newsysstring(_SC("string"));
+ newsysstring(_SC("userdata"));
+ newsysstring(_SC("integer"));
+ newsysstring(_SC("float"));
+ newsysstring(_SC("userpointer"));
+ newsysstring(_SC("function"));
+ newsysstring(_SC("generator"));
+ newsysstring(_SC("thread"));
+ newsysstring(_SC("class"));
+ newsysstring(_SC("instance"));
+ newsysstring(_SC("bool"));
+ //meta methods
+ newmetamethod(MM_ADD);
+ newmetamethod(MM_SUB);
+ newmetamethod(MM_MUL);
+ newmetamethod(MM_DIV);
+ newmetamethod(MM_UNM);
+ newmetamethod(MM_MODULO);
+ newmetamethod(MM_SET);
+ newmetamethod(MM_GET);
+ newmetamethod(MM_TYPEOF);
+ newmetamethod(MM_NEXTI);
+ newmetamethod(MM_CMP);
+ newmetamethod(MM_CALL);
+ newmetamethod(MM_CLONED);
+ newmetamethod(MM_NEWSLOT);
+ newmetamethod(MM_DELSLOT);
+ newmetamethod(MM_TOSTRING);
+ newmetamethod(MM_NEWMEMBER);
+ newmetamethod(MM_INHERITED);
+
+ _constructoridx = SQString::Create(this,_SC("constructor"));
+ _registry = SQTable::Create(this,0);
+ _table_default_delegate=CreateDefaultDelegate(this,_table_default_delegate_funcz);
+ _array_default_delegate=CreateDefaultDelegate(this,_array_default_delegate_funcz);
+ _string_default_delegate=CreateDefaultDelegate(this,_string_default_delegate_funcz);
+ _number_default_delegate=CreateDefaultDelegate(this,_number_default_delegate_funcz);
+ _closure_default_delegate=CreateDefaultDelegate(this,_closure_default_delegate_funcz);
+ _generator_default_delegate=CreateDefaultDelegate(this,_generator_default_delegate_funcz);
+ _thread_default_delegate=CreateDefaultDelegate(this,_thread_default_delegate_funcz);
+ _class_default_delegate=CreateDefaultDelegate(this,_class_default_delegate_funcz);
+ _instance_default_delegate=CreateDefaultDelegate(this,_instance_default_delegate_funcz);
+ _weakref_default_delegate=CreateDefaultDelegate(this,_weakref_default_delegate_funcz);
+
+}
+
+SQSharedState::~SQSharedState()
+{
+ _constructoridx = _null_;
+ _refs_table.Finalize();
+ _table(_registry)->Finalize();
+ _table(_metamethodsmap)->Finalize();
+ _registry = _null_;
+ _metamethodsmap = _null_;
+ while(!_systemstrings->empty()) {
+ _systemstrings->back()=_null_;
+ _systemstrings->pop_back();
+ }
+ _thread(_root_vm)->Finalize();
+ _root_vm = _null_;
+ _table_default_delegate=_null_;
+ _array_default_delegate=_null_;
+ _string_default_delegate=_null_;
+ _number_default_delegate=_null_;
+ _closure_default_delegate=_null_;
+ _generator_default_delegate=_null_;
+ _thread_default_delegate=_null_;
+ _class_default_delegate=_null_;
+ _instance_default_delegate=_null_;
+ _weakref_default_delegate=_null_;
+
+#ifndef NO_GARBAGE_COLLECTOR
+ SQCollectable *t=_gc_chain;
+ SQCollectable *nx=NULL;
+ while(t) {
+ t->_uiRef++;
+ t->Finalize();
+ nx=t->_next;
+ if(--t->_uiRef == 0)
+ t->Release();
+ t=nx;
+ }
+ assert(_gc_chain==NULL); //just to proove a theory
+ while(_gc_chain){
+ _gc_chain->_uiRef++;
+ _gc_chain->Release();
+ }
+#endif
+
+ sq_delete(_types,SQObjectPtrVec);
+ sq_delete(_systemstrings,SQObjectPtrVec);
+ sq_delete(_metamethods,SQObjectPtrVec);
+ sq_delete(_stringtable,StringTable);
+ if(_scratchpad)SQ_FREE(_scratchpad,_scratchpadsize);
+}
+
+
+SQInteger SQSharedState::GetMetaMethodIdxByName(const SQObjectPtr &name)
+{
+ if(type(name) != OT_STRING)
+ return -1;
+ SQObjectPtr ret;
+ if(_table(_metamethodsmap)->Get(name,ret)) {
+ return _integer(ret);
+ }
+ return -1;
+}
+
+#ifndef NO_GARBAGE_COLLECTOR
+
+void SQSharedState::MarkObject(SQObjectPtr &o,SQCollectable **chain)
+{
+ switch(type(o)){
+ case OT_TABLE:_table(o)->Mark(chain);break;
+ case OT_ARRAY:_array(o)->Mark(chain);break;
+ case OT_USERDATA:_userdata(o)->Mark(chain);break;
+ case OT_CLOSURE:_closure(o)->Mark(chain);break;
+ case OT_NATIVECLOSURE:_nativeclosure(o)->Mark(chain);break;
+ case OT_GENERATOR:_generator(o)->Mark(chain);break;
+ case OT_THREAD:_thread(o)->Mark(chain);break;
+ case OT_CLASS:_class(o)->Mark(chain);break;
+ case OT_INSTANCE:_instance(o)->Mark(chain);break;
+ default: break; //shutup compiler
+ }
+}
+
+
+SQInteger SQSharedState::CollectGarbage(SQVM *vm)
+{
+ SQInteger n=0;
+ SQCollectable *tchain=NULL;
+ SQVM *vms = _thread(_root_vm);
+
+ vms->Mark(&tchain);
+ SQInteger x = _table(_thread(_root_vm)->_roottable)->CountUsed();
+ _refs_table.Mark(&tchain);
+ MarkObject(_registry,&tchain);
+ MarkObject(_metamethodsmap,&tchain);
+ MarkObject(_table_default_delegate,&tchain);
+ MarkObject(_array_default_delegate,&tchain);
+ MarkObject(_string_default_delegate,&tchain);
+ MarkObject(_number_default_delegate,&tchain);
+ MarkObject(_generator_default_delegate,&tchain);
+ MarkObject(_thread_default_delegate,&tchain);
+ MarkObject(_closure_default_delegate,&tchain);
+ MarkObject(_class_default_delegate,&tchain);
+ MarkObject(_instance_default_delegate,&tchain);
+ MarkObject(_weakref_default_delegate,&tchain);
+
+ SQCollectable *t = _gc_chain;
+ SQCollectable *nx = NULL;
+ while(t) {
+ t->_uiRef++;
+ t->Finalize();
+ nx = t->_next;
+ if(--t->_uiRef == 0)
+ t->Release();
+ t = nx;
+ n++;
+ }
+
+ t = tchain;
+ while(t) {
+ t->UnMark();
+ t = t->_next;
+ }
+ _gc_chain = tchain;
+ SQInteger z = _table(_thread(_root_vm)->_roottable)->CountUsed();
+ assert(z == x);
+ return n;
+}
+#endif
+
+#ifndef NO_GARBAGE_COLLECTOR
+void SQCollectable::AddToChain(SQCollectable **chain,SQCollectable *c)
+{
+ c->_prev = NULL;
+ c->_next = *chain;
+ if(*chain) (*chain)->_prev = c;
+ *chain = c;
+}
+
+void SQCollectable::RemoveFromChain(SQCollectable **chain,SQCollectable *c)
+{
+ if(c->_prev) c->_prev->_next = c->_next;
+ else *chain = c->_next;
+ if(c->_next)
+ c->_next->_prev = c->_prev;
+ c->_next = NULL;
+ c->_prev = NULL;
+}
+#endif
+
+SQChar* SQSharedState::GetScratchPad(SQInteger size)
+{
+ SQInteger newsize;
+ if(size>0) {
+ if(_scratchpadsize < size) {
+ newsize = size + (size>>1);
+ _scratchpad = (SQChar *)SQ_REALLOC(_scratchpad,_scratchpadsize,newsize);
+ _scratchpadsize = newsize;
+
+ }else if(_scratchpadsize >= (size<<5)) {
+ newsize = _scratchpadsize >> 1;
+ _scratchpad = (SQChar *)SQ_REALLOC(_scratchpad,_scratchpadsize,newsize);
+ _scratchpadsize = newsize;
+ }
+ }
+ return _scratchpad;
+}
+
+RefTable::RefTable()
+{
+ AllocNodes(4);
+}
+
+void RefTable::Finalize()
+{
+ RefNode *nodes = _nodes;
+ for(SQUnsignedInteger n = 0; n < _numofslots; n++) {
+ nodes->obj = _null_;
+ nodes++;
+ }
+}
+
+RefTable::~RefTable()
+{
+ SQ_FREE(_buckets,(_numofslots * sizeof(RefNode *)) + (_numofslots * sizeof(RefNode)));
+}
+
+#ifndef NO_GARBAGE_COLLECTOR
+void RefTable::Mark(SQCollectable **chain)
+{
+ RefNode *nodes = (RefNode *)_nodes;
+ for(SQUnsignedInteger n = 0; n < _numofslots; n++) {
+ if(type(nodes->obj) != OT_NULL) {
+ SQSharedState::MarkObject(nodes->obj,chain);
+ }
+ nodes++;
+ }
+}
+#endif
+
+void RefTable::AddRef(SQObject &obj)
+{
+ SQHash mainpos;
+ RefNode *prev;
+ RefNode *ref = Get(obj,mainpos,&prev,true);
+ ref->refs++;
+}
+
+SQBool RefTable::Release(SQObject &obj)
+{
+ SQHash mainpos;
+ RefNode *prev;
+ RefNode *ref = Get(obj,mainpos,&prev,false);
+ if(ref) {
+ if(--ref->refs == 0) {
+ SQObjectPtr o = ref->obj;
+ if(prev) {
+ prev->next = ref->next;
+ }
+ else {
+ _buckets[mainpos] = ref->next;
+ }
+ ref->next = _freelist;
+ _freelist = ref;
+ _slotused--;
+ ref->obj = _null_;
+ //<<FIXME>>test for shrink?
+ return SQTrue;
+ }
+ }
+ else {
+ assert(0);
+ }
+ return SQFalse;
+}
+
+void RefTable::Resize(SQUnsignedInteger size)
+{
+ RefNode **oldbucks = _buckets;
+ RefNode *t = _nodes;
+ SQUnsignedInteger oldnumofslots = _numofslots;
+ AllocNodes(size);
+ //rehash
+ SQUnsignedInteger nfound = 0;
+ for(SQUnsignedInteger n = 0; n < oldnumofslots; n++) {
+ if(type(t->obj) != OT_NULL) {
+ //add back;
+ assert(t->refs != 0);
+ RefNode *nn = Add(::HashObj(t->obj)&(_numofslots-1),t->obj);
+ nn->refs = t->refs;
+ t->obj = _null_;
+ nfound++;
+ }
+ t++;
+ }
+ assert(nfound == oldnumofslots);
+ SQ_FREE(oldbucks,(oldnumofslots * sizeof(RefNode *)) + (oldnumofslots * sizeof(RefNode)));
+}
+
+RefTable::RefNode *RefTable::Add(SQHash mainpos,SQObject &obj)
+{
+ RefNode *t = _buckets[mainpos];
+ RefNode *newnode = _freelist;
+ newnode->obj = obj;
+ _buckets[mainpos] = newnode;
+ _freelist = _freelist->next;
+ newnode->next = t;
+ assert(newnode->refs == 0);
+ _slotused++;
+ return newnode;
+}
+
+RefTable::RefNode *RefTable::Get(SQObject &obj,SQHash &mainpos,RefNode **prev,bool add)
+{
+ RefNode *ref;
+ mainpos = ::HashObj(obj)&(_numofslots-1);
+ *prev = NULL;
+ for (ref = _buckets[mainpos]; ref; ) {
+ if(_rawval(ref->obj) == _rawval(obj) && type(ref->obj) == type(obj))
+ break;
+ *prev = ref;
+ ref = ref->next;
+ }
+ if(ref == NULL && add) {
+ if(_numofslots == _slotused) {
+ assert(_freelist == 0);
+ Resize(_numofslots*2);
+ mainpos = ::HashObj(obj)&(_numofslots-1);
+ }
+ ref = Add(mainpos,obj);
+ }
+ return ref;
+}
+
+void RefTable::AllocNodes(SQUnsignedInteger size)
+{
+ RefNode **bucks;
+ RefNode *nodes;
+ bucks = (RefNode **)SQ_MALLOC((size * sizeof(RefNode *)) + (size * sizeof(RefNode)));
+ nodes = (RefNode *)&bucks[size];
+ RefNode *temp = nodes;
+ SQUnsignedInteger n;
+ for(n = 0; n < size - 1; n++) {
+ bucks[n] = NULL;
+ temp->refs = 0;
+ new (&temp->obj) SQObjectPtr;
+ temp->next = temp+1;
+ temp++;
+ }
+ bucks[n] = NULL;
+ temp->refs = 0;
+ new (&temp->obj) SQObjectPtr;
+ temp->next = NULL;
+ _freelist = nodes;
+ _nodes = nodes;
+ _buckets = bucks;
+ _slotused = 0;
+ _numofslots = size;
+}
+//////////////////////////////////////////////////////////////////////////
+//StringTable
+/*
+* The following code is based on Lua 4.0 (Copyright 1994-2002 Tecgraf, PUC-Rio.)
+* http://www.lua.org/copyright.html#4
+* http://www.lua.org/source/4.0.1/src_lstring.c.html
+*/
+
+StringTable::StringTable()
+{
+ AllocNodes(4);
+ _slotused = 0;
+}
+
+StringTable::~StringTable()
+{
+ SQ_FREE(_strings,sizeof(SQString*)*_numofslots);
+ _strings = NULL;
+}
+
+void StringTable::AllocNodes(SQInteger size)
+{
+ _numofslots = size;
+ _strings = (SQString**)SQ_MALLOC(sizeof(SQString*)*_numofslots);
+ memset(_strings,0,sizeof(SQString*)*_numofslots);
+}
+
+SQString *StringTable::Add(const SQChar *news,SQInteger len)
+{
+ if(len<0)
+ len = (SQInteger)scstrlen(news);
+ SQHash h = ::_hashstr(news,len)&(_numofslots-1);
+ SQString *s;
+ for (s = _strings[h]; s; s = s->_next){
+ if(s->_len == len && (!memcmp(news,s->_val,rsl(len))))
+ return s; //found
+ }
+
+ SQString *t=(SQString *)SQ_MALLOC(rsl(len)+sizeof(SQString));
+ new (t) SQString;
+ memcpy(t->_val,news,rsl(len));
+ t->_val[len] = _SC('\0');
+ t->_len = len;
+ t->_hash = ::_hashstr(news,len);
+ t->_next = _strings[h];
+ _strings[h] = t;
+ _slotused++;
+ if (_slotused > _numofslots) /* too crowded? */
+ Resize(_numofslots*2);
+ return t;
+}
+
+void StringTable::Resize(SQInteger size)
+{
+ SQInteger oldsize=_numofslots;
+ SQString **oldtable=_strings;
+ AllocNodes(size);
+ for (SQInteger i=0; i<oldsize; i++){
+ SQString *p = oldtable[i];
+ while(p){
+ SQString *next = p->_next;
+ SQHash h = p->_hash&(_numofslots-1);
+ p->_next = _strings[h];
+ _strings[h] = p;
+ p = next;
+ }
+ }
+ SQ_FREE(oldtable,oldsize*sizeof(SQString*));
+}
+
+void StringTable::Remove(SQString *bs)
+{
+ SQString *s;
+ SQString *prev=NULL;
+ SQHash h = bs->_hash&(_numofslots - 1);
+
+ for (s = _strings[h]; s; ){
+ if(s == bs){
+ if(prev)
+ prev->_next = s->_next;
+ else
+ _strings[h] = s->_next;
+ _slotused--;
+ SQInteger slen = s->_len;
+ s->~SQString();
+ SQ_FREE(s,sizeof(SQString) + rsl(slen));
+ return;
+ }
+ prev = s;
+ s = s->_next;
+ }
+ assert(0);//if this fail something is wrong
+}
--- /dev/null
+/* see copyright notice in squirrel.h */
+#ifndef _SQSTATE_H_
+#define _SQSTATE_H_
+
+#include "squtils.h"
+#include "sqobject.h"
+struct SQString;
+struct SQTable;
+//max number of character for a printed number
+#define NUMBER_MAX_CHAR 50
+
+struct StringTable
+{
+ StringTable();
+ ~StringTable();
+ SQString *Add(const SQChar *,SQInteger len);
+ void Remove(SQString *);
+private:
+ void Resize(SQInteger size);
+ void AllocNodes(SQInteger size);
+ SQString **_strings;
+ SQUnsignedInteger _numofslots;
+ SQUnsignedInteger _slotused;
+};
+
+struct RefTable {
+ struct RefNode {
+ SQObjectPtr obj;
+ SQUnsignedInteger refs;
+ struct RefNode *next;
+ };
+ RefTable();
+ ~RefTable();
+ void AddRef(SQObject &obj);
+ SQBool Release(SQObject &obj);
+#ifndef NO_GARBAGE_COLLECTOR
+ void Mark(SQCollectable **chain);
+#endif
+ void Finalize();
+private:
+ RefNode *Get(SQObject &obj,SQHash &mainpos,RefNode **prev,bool add);
+ RefNode *Add(SQHash mainpos,SQObject &obj);
+ void Resize(SQUnsignedInteger size);
+ void AllocNodes(SQUnsignedInteger size);
+ SQUnsignedInteger _numofslots;
+ SQUnsignedInteger _slotused;
+ RefNode *_nodes;
+ RefNode *_freelist;
+ RefNode **_buckets;
+};
+
+#define ADD_STRING(ss,str,len) ss->_stringtable->Add(str,len)
+#define REMOVE_STRING(ss,bstr) ss->_stringtable->Remove(bstr)
+
+struct SQObjectPtr;
+
+struct SQSharedState
+{
+ SQSharedState();
+ ~SQSharedState();
+ void Init();
+public:
+ SQChar* GetScratchPad(SQInteger size);
+ SQInteger GetMetaMethodIdxByName(const SQObjectPtr &name);
+#ifndef NO_GARBAGE_COLLECTOR
+ SQInteger CollectGarbage(SQVM *vm);
+ static void MarkObject(SQObjectPtr &o,SQCollectable **chain);
+#endif
+ SQObjectPtrVec *_metamethods;
+ SQObjectPtr _metamethodsmap;
+ SQObjectPtrVec *_systemstrings;
+ SQObjectPtrVec *_types;
+ StringTable *_stringtable;
+ RefTable _refs_table;
+ SQObjectPtr _registry;
+ SQObjectPtr _constructoridx;
+#ifndef NO_GARBAGE_COLLECTOR
+ SQCollectable *_gc_chain;
+#endif
+ SQObjectPtr _root_vm;
+ SQObjectPtr _table_default_delegate;
+ static SQRegFunction _table_default_delegate_funcz[];
+ SQObjectPtr _array_default_delegate;
+ static SQRegFunction _array_default_delegate_funcz[];
+ SQObjectPtr _string_default_delegate;
+ static SQRegFunction _string_default_delegate_funcz[];
+ SQObjectPtr _number_default_delegate;
+ static SQRegFunction _number_default_delegate_funcz[];
+ SQObjectPtr _generator_default_delegate;
+ static SQRegFunction _generator_default_delegate_funcz[];
+ SQObjectPtr _closure_default_delegate;
+ static SQRegFunction _closure_default_delegate_funcz[];
+ SQObjectPtr _thread_default_delegate;
+ static SQRegFunction _thread_default_delegate_funcz[];
+ SQObjectPtr _class_default_delegate;
+ static SQRegFunction _class_default_delegate_funcz[];
+ SQObjectPtr _instance_default_delegate;
+ static SQRegFunction _instance_default_delegate_funcz[];
+ SQObjectPtr _weakref_default_delegate;
+ static SQRegFunction _weakref_default_delegate_funcz[];
+
+ SQCOMPILERERROR _compilererrorhandler;
+ SQPRINTFUNCTION _printfunc;
+ bool _debuginfo;
+ bool _notifyallexceptions;
+private:
+ SQChar *_scratchpad;
+ SQInteger _scratchpadsize;
+};
+
+#define _sp(s) (_sharedstate->GetScratchPad(s))
+#define _spval (_sharedstate->GetScratchPad(-1))
+
+#define _table_ddel _table(_sharedstate->_table_default_delegate)
+#define _array_ddel _table(_sharedstate->_array_default_delegate)
+#define _string_ddel _table(_sharedstate->_string_default_delegate)
+#define _number_ddel _table(_sharedstate->_number_default_delegate)
+#define _generator_ddel _table(_sharedstate->_generator_default_delegate)
+#define _closure_ddel _table(_sharedstate->_closure_default_delegate)
+#define _thread_ddel _table(_sharedstate->_thread_default_delegate)
+#define _class_ddel _table(_sharedstate->_class_default_delegate)
+#define _instance_ddel _table(_sharedstate->_instance_default_delegate)
+#define _weakref_ddel _table(_sharedstate->_weakref_default_delegate)
+
+#ifdef SQUNICODE //rsl REAL STRING LEN
+#define rsl(l) ((l)<<1)
+#else
+#define rsl(l) (l)
+#endif
+
+extern SQObjectPtr _null_;
+extern SQObjectPtr _true_;
+extern SQObjectPtr _false_;
+extern SQObjectPtr _one_;
+extern SQObjectPtr _minusone_;
+
+bool CompileTypemask(SQIntVec &res,const SQChar *typemask);
+
+void *sq_vm_malloc(SQUnsignedInteger size);
+void *sq_vm_realloc(void *p,SQUnsignedInteger oldsize,SQUnsignedInteger size);
+void sq_vm_free(void *p,SQUnsignedInteger size);
+#endif //_SQSTATE_H_
--- /dev/null
+/* see copyright notice in squirrel.h */
+#ifndef _SQSTRING_H_
+#define _SQSTRING_H_
+
+inline SQHash _hashstr (const SQChar *s, size_t l)
+{
+ SQHash h = (SQHash)l; /* seed */
+ size_t step = (l>>5)|1; /* if string is too long, don't hash all its chars */
+ for (; l>=step; l-=step)
+ h = h ^ ((h<<5)+(h>>2)+(unsigned short)*(s++));
+ return h;
+}
+
+struct SQString : public SQRefCounted
+{
+ SQString(){}
+ ~SQString(){}
+public:
+ static SQString *Create(SQSharedState *ss, const SQChar *, SQInteger len = -1 );
+ SQInteger Next(const SQObjectPtr &refpos, SQObjectPtr &outkey, SQObjectPtr &outval);
+ void Release();
+ SQSharedState *_sharedstate;
+ SQString *_next; //chain for the string table
+ SQInteger _len;
+ SQHash _hash;
+ SQChar _val[1];
+};
+
+
+
+#endif //_SQSTRING_H_
--- /dev/null
+/*
+see copyright notice in squirrel.h
+*/
+#include "sqpcheader.h"
+#include "sqvm.h"
+#include "sqtable.h"
+#include "sqfuncproto.h"
+#include "sqclosure.h"
+
+SQTable::SQTable(SQSharedState *ss,SQInteger nInitialSize)
+{
+ SQInteger pow2size=MINPOWER2;
+ while(nInitialSize>pow2size)pow2size=pow2size<<1;
+ AllocNodes(pow2size);
+ _usednodes = 0;
+ _delegate = NULL;
+ INIT_CHAIN();
+ ADD_TO_CHAIN(&_sharedstate->_gc_chain,this);
+}
+
+void SQTable::Remove(const SQObjectPtr &key)
+{
+
+ _HashNode *n = _Get(key, HashObj(key) & (_numofnodes - 1));
+ if (n) {
+ n->val = n->key = _null_;
+ _usednodes--;
+ Rehash(false);
+ }
+}
+
+void SQTable::AllocNodes(SQInteger nSize)
+{
+ _HashNode *nodes=(_HashNode *)SQ_MALLOC(sizeof(_HashNode)*nSize);
+ for(SQInteger i=0;i<nSize;i++){
+ new (&nodes[i]) _HashNode;
+ nodes[i].next=NULL;
+ }
+ _numofnodes=nSize;
+ _nodes=nodes;
+ _firstfree=&_nodes[_numofnodes-1];
+}
+
+void SQTable::Rehash(bool force)
+{
+ SQInteger oldsize=_numofnodes;
+ //prevent problems with the integer division
+ if(oldsize<4)oldsize=4;
+ _HashNode *nold=_nodes;
+ SQInteger nelems=CountUsed();
+ if (nelems >= oldsize-oldsize/4) /* using more than 3/4? */
+ AllocNodes(oldsize*2);
+ else if (nelems <= oldsize/4 && /* less than 1/4? */
+ oldsize > MINPOWER2)
+ AllocNodes(oldsize/2);
+ else if(force)
+ AllocNodes(oldsize);
+ else
+ return;
+ _usednodes = 0;
+ for (SQInteger i=0; i<oldsize; i++) {
+ _HashNode *old = nold+i;
+ if (type(old->key) != OT_NULL)
+ NewSlot(old->key,old->val);
+ }
+ for(SQInteger k=0;k<oldsize;k++)
+ nold[k].~_HashNode();
+ SQ_FREE(nold,oldsize*sizeof(_HashNode));
+}
+
+SQTable *SQTable::Clone()
+{
+ SQTable *nt=Create(_opt_ss(this),_numofnodes);
+ SQInteger ridx=0;
+ SQObjectPtr key,val;
+ while((ridx=Next(true,ridx,key,val))!=-1){
+ nt->NewSlot(key,val);
+ }
+ nt->SetDelegate(_delegate);
+ return nt;
+}
+
+bool SQTable::Get(const SQObjectPtr &key,SQObjectPtr &val)
+{
+ if(type(key) == OT_NULL)
+ return false;
+ _HashNode *n = _Get(key, HashObj(key) & (_numofnodes - 1));
+ if (n) {
+ val = _realval(n->val);
+ return true;
+ }
+ return false;
+}
+bool SQTable::NewSlot(const SQObjectPtr &key,const SQObjectPtr &val)
+{
+ assert(type(key) != OT_NULL);
+ SQHash h = HashObj(key) & (_numofnodes - 1);
+ _HashNode *n = _Get(key, h);
+ if (n) {
+ n->val = val;
+ return false;
+ }
+ _HashNode *mp = &_nodes[h];
+ n = mp;
+
+
+ //key not found I'll insert it
+ //main pos is not free
+
+ if(type(mp->key) != OT_NULL) {
+ n = _firstfree; /* get a free place */
+ SQHash mph = HashObj(mp->key) & (_numofnodes - 1);
+ _HashNode *othern; /* main position of colliding node */
+
+ if (mp > n && (othern = &_nodes[mph]) != mp){
+ /* yes; move colliding node into free position */
+ while (othern->next != mp){
+ assert(othern->next != NULL);
+ othern = othern->next; /* find previous */
+ }
+ othern->next = n; /* redo the chain with `n' in place of `mp' */
+ n->key = mp->key;
+ n->val = mp->val;/* copy colliding node into free pos. (mp->next also goes) */
+ n->next = mp->next;
+ mp->key = _null_;
+ mp->val = _null_;
+ mp->next = NULL; /* now `mp' is free */
+ }
+ else{
+ /* new node will go into free position */
+ n->next = mp->next; /* chain new position */
+ mp->next = n;
+ mp = n;
+ }
+ }
+ mp->key = key;
+
+ for (;;) { /* correct `firstfree' */
+ if (type(_firstfree->key) == OT_NULL && _firstfree->next == NULL) {
+ mp->val = val;
+ _usednodes++;
+ return true; /* OK; table still has a free place */
+ }
+ else if (_firstfree == _nodes) break; /* cannot decrement from here */
+ else (_firstfree)--;
+ }
+ Rehash(true);
+ return NewSlot(key, val);
+}
+
+SQInteger SQTable::Next(bool getweakrefs,const SQObjectPtr &refpos, SQObjectPtr &outkey, SQObjectPtr &outval)
+{
+ SQInteger idx = (SQInteger)TranslateIndex(refpos);
+ while (idx < _numofnodes) {
+ if(type(_nodes[idx].key) != OT_NULL) {
+ //first found
+ _HashNode &n = _nodes[idx];
+ outkey = n.key;
+ outval = getweakrefs?(SQObject)n.val:_realval(n.val);
+ //return idx for the next iteration
+ return ++idx;
+ }
+ ++idx;
+ }
+ //nothing to iterate anymore
+ return -1;
+}
+
+
+bool SQTable::Set(const SQObjectPtr &key, const SQObjectPtr &val)
+{
+ _HashNode *n = _Get(key, HashObj(key) & (_numofnodes - 1));
+ if (n) {
+ n->val = val;
+ return true;
+ }
+ return false;
+}
+
+void SQTable::_ClearNodes()
+{
+ for(SQInteger i = 0;i < _numofnodes; i++) { _nodes[i].key = _null_; _nodes[i].val = _null_; }
+}
+
+void SQTable::Finalize()
+{
+ _ClearNodes();
+ SetDelegate(NULL);
+}
+
+void SQTable::Clear()
+{
+ _ClearNodes();
+ _usednodes = 0;
+ Rehash(true);
+}
--- /dev/null
+/* see copyright notice in squirrel.h */
+#ifndef _SQTABLE_H_
+#define _SQTABLE_H_
+/*
+* The following code is based on Lua 4.0 (Copyright 1994-2002 Tecgraf, PUC-Rio.)
+* http://www.lua.org/copyright.html#4
+* http://www.lua.org/source/4.0.1/src_ltable.c.html
+*/
+
+#include "sqstring.h"
+
+
+#define hashptr(p) ((SQHash)(((SQInteger)p) >> 3))
+
+inline SQHash HashObj(const SQObjectPtr &key)
+{
+ switch(type(key)) {
+ case OT_STRING: return _string(key)->_hash;
+ case OT_FLOAT: return (SQHash)((SQInteger)_float(key));
+ case OT_BOOL: case OT_INTEGER: return (SQHash)((SQInteger)_integer(key));
+ default: return hashptr(key._unVal.pRefCounted);
+ }
+}
+
+struct SQTable : public SQDelegable
+{
+private:
+ struct _HashNode
+ {
+ _HashNode() { next = NULL; }
+ SQObjectPtr val;
+ SQObjectPtr key;
+ _HashNode *next;
+ };
+ _HashNode *_firstfree;
+ _HashNode *_nodes;
+ SQInteger _numofnodes;
+ SQInteger _usednodes;
+
+///////////////////////////
+ void AllocNodes(SQInteger nSize);
+ void Rehash(bool force);
+ SQTable(SQSharedState *ss, SQInteger nInitialSize);
+ void _ClearNodes();
+public:
+ static SQTable* Create(SQSharedState *ss,SQInteger nInitialSize)
+ {
+ SQTable *newtable = (SQTable*)SQ_MALLOC(sizeof(SQTable));
+ new (newtable) SQTable(ss, nInitialSize);
+ newtable->_delegate = NULL;
+ return newtable;
+ }
+ void Finalize();
+ SQTable *Clone();
+ ~SQTable()
+ {
+ SetDelegate(NULL);
+ REMOVE_FROM_CHAIN(&_sharedstate->_gc_chain, this);
+ for (SQInteger i = 0; i < _numofnodes; i++) _nodes[i].~_HashNode();
+ SQ_FREE(_nodes, _numofnodes * sizeof(_HashNode));
+ }
+#ifndef NO_GARBAGE_COLLECTOR
+ void Mark(SQCollectable **chain);
+#endif
+ inline _HashNode *_Get(const SQObjectPtr &key,SQHash hash)
+ {
+ _HashNode *n = &_nodes[hash];
+ do{
+ if(_rawval(n->key) == _rawval(key) && type(n->key) == type(key)){
+ return n;
+ }
+ }while((n = n->next));
+ return NULL;
+ }
+ bool Get(const SQObjectPtr &key,SQObjectPtr &val);
+ void Remove(const SQObjectPtr &key);
+ bool Set(const SQObjectPtr &key, const SQObjectPtr &val);
+ //returns true if a new slot has been created false if it was already present
+ bool NewSlot(const SQObjectPtr &key,const SQObjectPtr &val);
+ SQInteger Next(bool getweakrefs,const SQObjectPtr &refpos, SQObjectPtr &outkey, SQObjectPtr &outval);
+
+ SQInteger CountUsed(){ return _usednodes;}
+ void Clear();
+ void Release()
+ {
+ sq_delete(this, SQTable);
+ }
+
+};
+
+#endif //_SQTABLE_H_
--- /dev/null
+/* see copyright notice in squirrel.h */
+#ifndef _SQUSERDATA_H_
+#define _SQUSERDATA_H_
+
+struct SQUserData : SQDelegable
+{
+ SQUserData(SQSharedState *ss){ _delegate = 0; _hook = NULL; INIT_CHAIN(); ADD_TO_CHAIN(&_ss(this)->_gc_chain, this); }
+ ~SQUserData()
+ {
+ REMOVE_FROM_CHAIN(&_ss(this)->_gc_chain, this);
+ SetDelegate(NULL);
+ }
+ static SQUserData* Create(SQSharedState *ss, SQInteger size)
+ {
+ SQUserData* ud = (SQUserData*)SQ_MALLOC(sizeof(SQUserData)+(size-1));
+ new (ud) SQUserData(ss);
+ ud->_size = size;
+ ud->_typetag = 0;
+ return ud;
+ }
+#ifndef NO_GARBAGE_COLLECTOR
+ void Mark(SQCollectable **chain);
+ void Finalize(){SetDelegate(NULL);}
+#endif
+ void Release() {
+ if (_hook) _hook(_val,_size);
+ SQInteger tsize = _size - 1;
+ this->~SQUserData();
+ SQ_FREE(this, sizeof(SQUserData) + tsize);
+ }
+
+ SQInteger _size;
+ SQRELEASEHOOK _hook;
+ SQUserPointer _typetag;
+ SQChar _val[1];
+};
+
+#endif //_SQUSERDATA_H_
--- /dev/null
+/* see copyright notice in squirrel.h */
+#ifndef _SQUTILS_H_
+#define _SQUTILS_H_
+
+#define sq_new(__ptr,__type) {__ptr=(__type *)sq_vm_malloc(sizeof(__type));new (__ptr) __type;}
+#define sq_delete(__ptr,__type) {__ptr->~__type();sq_vm_free(__ptr,sizeof(__type));}
+#define SQ_MALLOC(__size) sq_vm_malloc((__size));
+#define SQ_FREE(__ptr,__size) sq_vm_free((__ptr),(__size));
+#define SQ_REALLOC(__ptr,__oldsize,__size) sq_vm_realloc((__ptr),(__oldsize),(__size));
+
+//sqvector mini vector class, supports objects by value
+template<typename T> class sqvector
+{
+public:
+ sqvector()
+ {
+ _vals = NULL;
+ _size = 0;
+ _allocated = 0;
+ }
+ sqvector(const sqvector<T>& v)
+ {
+ copy(v);
+ }
+ void copy(const sqvector<T>& v)
+ {
+ resize(v._size);
+ for(SQUnsignedInteger i = 0; i < v._size; i++) {
+ new ((void *)&_vals[i]) T(v._vals[i]);
+ }
+ _size = v._size;
+ }
+ ~sqvector()
+ {
+ if(_allocated) {
+ for(SQUnsignedInteger i = 0; i < _size; i++)
+ _vals[i].~T();
+ SQ_FREE(_vals, (_allocated * sizeof(T)));
+ }
+ }
+ void reserve(SQUnsignedInteger newsize) { _realloc(newsize); }
+ void resize(SQUnsignedInteger newsize, const T& fill = T())
+ {
+ if(newsize > _allocated)
+ _realloc(newsize);
+ if(newsize > _size) {
+ while(_size < newsize) {
+ new ((void *)&_vals[_size]) T(fill);
+ _size++;
+ }
+ }
+ else{
+ for(SQUnsignedInteger i = newsize; i < _size; i++) {
+ _vals[i].~T();
+ }
+ _size = newsize;
+ }
+ }
+ void shrinktofit() { if(_size > 4) { _realloc(_size); } }
+ T& top() const { return _vals[_size - 1]; }
+ inline SQUnsignedInteger size() const { return _size; }
+ bool empty() const { return (_size <= 0); }
+ inline T &push_back(const T& val = T())
+ {
+ if(_allocated <= _size)
+ _realloc(_size * 2);
+ return *(new ((void *)&_vals[_size++]) T(val));
+ }
+ inline void pop_back()
+ {
+ _size--; _vals[_size].~T();
+ }
+ void insert(SQUnsignedInteger idx, const T& val)
+ {
+ resize(_size + 1);
+ for(SQUnsignedInteger i = _size - 1; i > idx; i--) {
+ _vals[i] = _vals[i - 1];
+ }
+ _vals[idx] = val;
+ }
+ void remove(SQUnsignedInteger idx)
+ {
+ _vals[idx].~T();
+ if(idx < (_size - 1)) {
+ memcpy(&_vals[idx], &_vals[idx+1], sizeof(T) * (_size - idx - 1));
+ }
+ _size--;
+ }
+ SQUnsignedInteger capacity() { return _allocated; }
+ inline T &back() const { return _vals[_size - 1]; }
+ inline T& operator[](SQUnsignedInteger pos) const{ return _vals[pos]; }
+ T* _vals;
+private:
+ void _realloc(SQUnsignedInteger newsize)
+ {
+ newsize = (newsize > 0)?newsize:4;
+ _vals = (T*)SQ_REALLOC(_vals, _allocated * sizeof(T), newsize * sizeof(T));
+ _allocated = newsize;
+ }
+ SQUnsignedInteger _size;
+ SQUnsignedInteger _allocated;
+};
+
+#endif //_SQUTILS_H_
--- /dev/null
+/*
+ see copyright notice in squirrel.h
+*/
+#include "sqpcheader.h"
+#include <math.h>
+#include <stdlib.h>
+#include "sqopcodes.h"
+#include "sqfuncproto.h"
+#include "sqvm.h"
+#include "sqclosure.h"
+#include "sqstring.h"
+#include "sqtable.h"
+#include "squserdata.h"
+#include "sqarray.h"
+#include "sqclass.h"
+
+#define TOP() (_stack._vals[_top-1])
+
+bool SQVM::BW_OP(SQUnsignedInteger op,SQObjectPtr &trg,const SQObjectPtr &o1,const SQObjectPtr &o2)
+{
+ SQInteger res;
+ SQInteger i1 = _integer(o1), i2 = _integer(o2);
+ if((type(o1)==OT_INTEGER) && (type(o2)==OT_INTEGER))
+ {
+ switch(op) {
+ case BW_AND: res = i1 & i2; break;
+ case BW_OR: res = i1 | i2; break;
+ case BW_XOR: res = i1 ^ i2; break;
+ case BW_SHIFTL: res = i1 << i2; break;
+ case BW_SHIFTR: res = i1 >> i2; break;
+ case BW_USHIFTR:res = (SQInteger)(*((SQUnsignedInteger*)&i1) >> i2); break;
+ default: { Raise_Error(_SC("internal vm error bitwise op failed")); return false; }
+ }
+ }
+ else { Raise_Error(_SC("bitwise op between '%s' and '%s'"),GetTypeName(o1),GetTypeName(o2)); return false;}
+ trg = res;
+ return true;
+}
+
+bool SQVM::ARITH_OP(SQUnsignedInteger op,SQObjectPtr &trg,const SQObjectPtr &o1,const SQObjectPtr &o2)
+{
+ if(sq_isnumeric(o1) && sq_isnumeric(o2)) {
+ if((type(o1)==OT_INTEGER) && (type(o2)==OT_INTEGER)) {
+ SQInteger res, i1 = _integer(o1), i2 = _integer(o2);
+ switch(op) {
+ case '+': res = i1 + i2; break;
+ case '-': res = i1 - i2; break;
+ case '/': if(i2 == 0) { Raise_Error(_SC("division by zero")); return false; }
+ res = i1 / i2;
+ break;
+ case '*': res = i1 * i2; break;
+ case '%': res = i1 % i2; break;
+ default: res = 0xDEADBEEF;
+ }
+ trg = res;
+ }else{
+ SQFloat res, f1 = tofloat(o1), f2 = tofloat(o2);
+ switch(op) {
+ case '+': res = f1 + f2; break;
+ case '-': res = f1 - f2; break;
+ case '/': res = f1 / f2; break;
+ case '*': res = f1 * f2; break;
+ case '%': res = SQFloat(fmod((double)f1,(double)f2)); break;
+ default: res = 0x0f;
+ }
+ trg = res;
+ }
+ } else {
+ if(op == '+' && (type(o1) == OT_STRING || type(o2) == OT_STRING)){
+ if(!StringCat(o1, o2, trg)) return false;
+ }
+ else if(!ArithMetaMethod(op,o1,o2,trg)) {
+ Raise_Error(_SC("arith op %c on between '%s' and '%s'"),op,GetTypeName(o1),GetTypeName(o2)); return false;
+ }
+ }
+ return true;
+}
+
+SQVM::SQVM(SQSharedState *ss)
+{
+ _sharedstate=ss;
+ _suspended = SQFalse;
+ _suspended_target=-1;
+ _suspended_root = SQFalse;
+ _suspended_traps=-1;
+ _foreignptr=NULL;
+ _nnativecalls=0;
+ _lasterror = _null_;
+ _errorhandler = _null_;
+ _debughook = _null_;
+ ci = NULL;
+ INIT_CHAIN();ADD_TO_CHAIN(&_ss(this)->_gc_chain,this);
+}
+
+void SQVM::Finalize()
+{
+ _roottable = _null_;
+ _lasterror = _null_;
+ _errorhandler = _null_;
+ _debughook = _null_;
+ temp_reg = _null_;
+ SQInteger size=_stack.size();
+ for(SQInteger i=0;i<size;i++)
+ _stack[i]=_null_;
+}
+
+SQVM::~SQVM()
+{
+ Finalize();
+ sq_free(_callsstack,_alloccallsstacksize*sizeof(CallInfo));
+ REMOVE_FROM_CHAIN(&_ss(this)->_gc_chain,this);
+}
+
+bool SQVM::ArithMetaMethod(SQInteger op,const SQObjectPtr &o1,const SQObjectPtr &o2,SQObjectPtr &dest)
+{
+ SQMetaMethod mm;
+ switch(op){
+ case _SC('+'): mm=MT_ADD; break;
+ case _SC('-'): mm=MT_SUB; break;
+ case _SC('/'): mm=MT_DIV; break;
+ case _SC('*'): mm=MT_MUL; break;
+ case _SC('%'): mm=MT_MODULO; break;
+ default: mm = MT_ADD; assert(0); break; //shutup compiler
+ }
+ if(is_delegable(o1) && _delegable(o1)->_delegate) {
+ Push(o1);Push(o2);
+ return CallMetaMethod(_delegable(o1),mm,2,dest);
+ }
+ return false;
+}
+
+bool SQVM::NEG_OP(SQObjectPtr &trg,const SQObjectPtr &o)
+{
+
+ switch(type(o)) {
+ case OT_INTEGER:
+ trg = -_integer(o);
+ return true;
+ case OT_FLOAT:
+ trg = -_float(o);
+ return true;
+ case OT_TABLE:
+ case OT_USERDATA:
+ case OT_INSTANCE:
+ if(_delegable(o)->_delegate) {
+ Push(o);
+ if(CallMetaMethod(_delegable(o), MT_UNM, 1, temp_reg)) {
+ trg = temp_reg;
+ return true;
+ }
+ }
+ default:break; //shutup compiler
+ }
+ Raise_Error(_SC("attempt to negate a %s"), GetTypeName(o));
+ return false;
+}
+
+#define _RET_SUCCEED(exp) { result = (exp); return true; }
+bool SQVM::ObjCmp(const SQObjectPtr &o1,const SQObjectPtr &o2,SQInteger &result)
+{
+ if(type(o1)==type(o2)){
+ if(_userpointer(o1)==_userpointer(o2))_RET_SUCCEED(0);
+ SQObjectPtr res;
+ switch(type(o1)){
+ case OT_STRING:
+ _RET_SUCCEED(scstrcmp(_stringval(o1),_stringval(o2)));
+ case OT_INTEGER:
+ _RET_SUCCEED(_integer(o1)-_integer(o2));
+ case OT_FLOAT:
+ _RET_SUCCEED((_float(o1)<_float(o2))?-1:1);
+ case OT_TABLE:
+ case OT_USERDATA:
+ case OT_INSTANCE:
+ if(_delegable(o1)->_delegate) {
+ Push(o1);Push(o2);
+ if(CallMetaMethod(_delegable(o1),MT_CMP,2,res)) break;
+ }
+ //continues through (no break needed)
+ default:
+ _RET_SUCCEED( _userpointer(o1) < _userpointer(o2)?-1:1 );
+ }
+ if(type(res)!=OT_INTEGER) { Raise_CompareError(o1,o2); return false; }
+ _RET_SUCCEED(_integer(res));
+
+ }
+ else{
+ if(sq_isnumeric(o1) && sq_isnumeric(o2)){
+ if((type(o1)==OT_INTEGER) && (type(o2)==OT_FLOAT)) {
+ if( _integer(o1)==_float(o2) ) { _RET_SUCCEED(0); }
+ else if( _integer(o1)<_float(o2) ) { _RET_SUCCEED(-1); }
+ _RET_SUCCEED(1);
+ }
+ else{
+ if( _float(o1)==_integer(o2) ) { _RET_SUCCEED(0); }
+ else if( _float(o1)<_integer(o2) ) { _RET_SUCCEED(-1); }
+ _RET_SUCCEED(1);
+ }
+ }
+ else if(type(o1)==OT_NULL) {_RET_SUCCEED(-1);}
+ else if(type(o2)==OT_NULL) {_RET_SUCCEED(1);}
+ else { Raise_CompareError(o1,o2); return false; }
+
+ }
+ assert(0);
+ _RET_SUCCEED(0); //cannot happen
+}
+
+bool SQVM::CMP_OP(CmpOP op, const SQObjectPtr &o1,const SQObjectPtr &o2,SQObjectPtr &res)
+{
+ SQInteger r;
+ if(ObjCmp(o1,o2,r)) {
+ switch(op) {
+ case CMP_G: res = (r > 0)?_true_:_false_; return true;
+ case CMP_GE: res = (r >= 0)?_true_:_false_; return true;
+ case CMP_L: res = (r < 0)?_true_:_false_; return true;
+ case CMP_LE: res = (r <= 0)?_true_:_false_; return true;
+
+ }
+ assert(0);
+ }
+ return false;
+}
+
+void SQVM::ToString(const SQObjectPtr &o,SQObjectPtr &res)
+{
+ switch(type(o)) {
+ case OT_STRING:
+ res = o;
+ return;
+ case OT_FLOAT:
+ scsprintf(_sp(rsl(NUMBER_MAX_CHAR+1)),_SC("%g"),_float(o));
+ break;
+ case OT_INTEGER:
+ scsprintf(_sp(rsl(NUMBER_MAX_CHAR+1)),_SC("%d"),_integer(o));
+ break;
+ case OT_BOOL:
+ scsprintf(_sp(rsl(6)),_integer(o)?_SC("true"):_SC("false"));
+ break;
+ case OT_TABLE:
+ case OT_USERDATA:
+ case OT_INSTANCE:
+ if(_delegable(o)->_delegate) {
+ Push(o);
+ if(CallMetaMethod(_delegable(o),MT_TOSTRING,1,res)) {
+ if(type(res) == OT_STRING)
+ return;
+ //else keeps going to the default
+ }
+ }
+ default:
+ scsprintf(_sp(rsl(sizeof(void*)+20)),_SC("(%s : 0x%p)"),GetTypeName(o),(void*)_rawval(o));
+ }
+ res = SQString::Create(_ss(this),_spval);
+}
+
+
+bool SQVM::StringCat(const SQObjectPtr &str,const SQObjectPtr &obj,SQObjectPtr &dest)
+{
+ SQObjectPtr a, b;
+ ToString(str, a);
+ ToString(obj, b);
+ SQInteger l = _string(a)->_len , ol = _string(b)->_len;
+ SQChar *s = _sp(rsl(l + ol + 1));
+ memcpy(s, _stringval(a), rsl(l));
+ memcpy(s + l, _stringval(b), rsl(ol));
+ dest = SQString::Create(_ss(this), _spval, l + ol);
+ return true;
+}
+
+const SQChar *IdType2Name(SQObjectType type)
+{
+ switch(_RAW_TYPE(type))
+ {
+ case _RT_NULL:return _SC("null");
+ case _RT_INTEGER:return _SC("integer");
+ case _RT_FLOAT:return _SC("float");
+ case _RT_BOOL:return _SC("bool");
+ case _RT_STRING:return _SC("string");
+ case _RT_TABLE:return _SC("table");
+ case _RT_ARRAY:return _SC("array");
+ case _RT_GENERATOR:return _SC("generator");
+ case _RT_CLOSURE:
+ case _RT_NATIVECLOSURE:
+ return _SC("function");
+ case _RT_USERDATA:
+ case _RT_USERPOINTER:
+ return _SC("userdata");
+ case _RT_THREAD: return _SC("thread");
+ case _RT_FUNCPROTO: return _SC("function");
+ case _RT_CLASS: return _SC("class");
+ case _RT_INSTANCE: return _SC("instance");
+ case _RT_WEAKREF: return _SC("weakref");
+ default:
+ return NULL;
+ }
+}
+
+const SQChar *GetTypeName(const SQObjectPtr &obj1)
+{
+ return IdType2Name(type(obj1));
+}
+
+void SQVM::TypeOf(const SQObjectPtr &obj1,SQObjectPtr &dest)
+{
+ if(is_delegable(obj1) && _delegable(obj1)->_delegate) {
+ Push(obj1);
+ if(CallMetaMethod(_delegable(obj1),MT_TYPEOF,1,dest))
+ return;
+ }
+ dest = SQString::Create(_ss(this),GetTypeName(obj1));
+}
+
+bool SQVM::Init(SQVM *friendvm, SQInteger stacksize)
+{
+ _stack.resize(stacksize);
+ //_callsstack.reserve(4);
+ _alloccallsstacksize = 4;
+ _callsstacksize = 0;
+ _callsstack = (CallInfo*)sq_malloc(_alloccallsstacksize*sizeof(CallInfo));
+ _stackbase = 0;
+ _top = 0;
+ if(!friendvm)
+ _roottable = SQTable::Create(_ss(this), 0);
+ else {
+ _roottable = friendvm->_roottable;
+ _errorhandler = friendvm->_errorhandler;
+ _debughook = friendvm->_debughook;
+ }
+
+ sq_base_register(this);
+ return true;
+}
+
+extern SQInstructionDesc g_InstrDesc[];
+
+bool SQVM::StartCall(SQClosure *closure,SQInteger target,SQInteger nargs,SQInteger stackbase,bool tailcall)
+{
+ SQFunctionProto *func = _funcproto(closure->_function);
+
+ const SQInteger paramssize = func->_nparameters;
+ const SQInteger newtop = stackbase + func->_stacksize;
+
+
+ if (paramssize != nargs) {
+ if(func->_varparams)
+ {
+ if (nargs < paramssize) {
+ Raise_Error(_SC("wrong number of parameters"));
+ return false;
+ }
+ for(SQInteger n = 0; n < nargs - paramssize; n++) {
+ _vargsstack.push_back(_stack._vals[stackbase+paramssize+n]);
+ _stack._vals[stackbase+paramssize+n] = _null_;
+ }
+ }
+ else {
+ Raise_Error(_SC("wrong number of parameters"));
+ return false;
+ }
+ }
+
+ if(type(closure->_env) == OT_WEAKREF) {
+ _stack._vals[stackbase] = _weakref(closure->_env)->_obj;
+ }
+
+ if (!tailcall) {
+ CallInfo lc;
+ lc._etraps = 0;
+ lc._prevstkbase = (SQInt32) ( stackbase - _stackbase );
+ lc._target = (SQInt32) target;
+ lc._prevtop = (SQInt32) (_top - _stackbase);
+ lc._ncalls = 1;
+ lc._root = SQFalse;
+ PUSH_CALLINFO(this, lc);
+ }
+ else {
+ ci->_ncalls++;
+ }
+ ci->_vargs.size = (SQInt32)(nargs - paramssize);
+ ci->_vargs.base = (SQInt32) (_vargsstack.size()-(ci->_vargs.size));
+ ci->_closure._unVal.pClosure = closure;
+ ci->_closure._type = OT_CLOSURE;
+ ci->_literals = func->_literals;
+ ci->_ip = func->_instructions;
+ //grows the stack if needed
+ if (((SQUnsignedInteger)newtop + (func->_stacksize<<1)) > _stack.size()) {
+ _stack.resize(_stack.size() + (func->_stacksize<<1));
+ }
+
+ _top = newtop;
+ _stackbase = stackbase;
+ return true;
+}
+
+bool SQVM::Return(SQInteger _arg0, SQInteger _arg1, SQObjectPtr &retval)
+{
+ if (type(_debughook) != OT_NULL && _rawval(_debughook) != _rawval(ci->_closure))
+ for(SQInteger i=0;i<ci->_ncalls;i++)
+ CallDebugHook(_SC('r'));
+
+ SQBool broot = ci->_root;
+ SQInteger last_top = _top;
+ SQInteger target = ci->_target;
+ SQInteger oldstackbase = _stackbase;
+ _stackbase -= ci->_prevstkbase;
+ _top = _stackbase + ci->_prevtop;
+ if(ci->_vargs.size) PopVarArgs(ci->_vargs);
+ POP_CALLINFO(this);
+ if (broot) {
+ if (_arg0 != MAX_FUNC_STACKSIZE) retval = _stack._vals[oldstackbase+_arg1];
+ else retval = _null_;
+ }
+ else {
+ if(target != -1) { //-1 is when a class contructor ret value has to be ignored
+ if (_arg0 != MAX_FUNC_STACKSIZE)
+ STK(target) = _stack._vals[oldstackbase+_arg1];
+ else
+ STK(target) = _null_;
+ }
+ }
+
+ while (last_top >= _top) _stack._vals[last_top--].Null();
+ assert(oldstackbase >= _stackbase);
+ return broot?true:false;
+}
+
+#define _RET_ON_FAIL(exp) { if(!exp) return false; }
+
+bool SQVM::LOCAL_INC(SQInteger op,SQObjectPtr &target, SQObjectPtr &a, SQObjectPtr &incr)
+{
+ _RET_ON_FAIL(ARITH_OP( op , target, a, incr));
+ a = target;
+ return true;
+}
+
+bool SQVM::PLOCAL_INC(SQInteger op,SQObjectPtr &target, SQObjectPtr &a, SQObjectPtr &incr)
+{
+ SQObjectPtr trg;
+ _RET_ON_FAIL(ARITH_OP( op , trg, a, incr));
+ target = a;
+ a = trg;
+ return true;
+}
+
+bool SQVM::DerefInc(SQInteger op,SQObjectPtr &target, SQObjectPtr &self, SQObjectPtr &key, SQObjectPtr &incr, bool postfix)
+{
+ SQObjectPtr tmp, tself = self, tkey = key;
+ if (!Get(tself, tkey, tmp, false, true)) { Raise_IdxError(tkey); return false; }
+ _RET_ON_FAIL(ARITH_OP( op , target, tmp, incr))
+ Set(tself, tkey, target,true);
+ if (postfix) target = tmp;
+ return true;
+}
+
+#define arg0 (_i_._arg0)
+#define arg1 (_i_._arg1)
+#define sarg1 (*((SQInt32 *)&_i_._arg1))
+#define arg2 (_i_._arg2)
+#define arg3 (_i_._arg3)
+#define sarg3 ((SQInteger)*((signed char *)&_i_._arg3))
+
+SQRESULT SQVM::Suspend()
+{
+ if (_suspended)
+ return sq_throwerror(this, _SC("cannot suspend an already suspended vm"));
+ if (_nnativecalls!=2)
+ return sq_throwerror(this, _SC("cannot suspend through native calls/metamethods"));
+ return SQ_SUSPEND_FLAG;
+}
+
+void SQVM::PopVarArgs(VarArgs &vargs)
+{
+ for(SQInteger n = 0; n< vargs.size; n++)
+ _vargsstack.pop_back();
+}
+
+#define _FINISH(howmuchtojump) {jump = howmuchtojump; return true; }
+bool SQVM::FOREACH_OP(SQObjectPtr &o1,SQObjectPtr &o2,SQObjectPtr
+&o3,SQObjectPtr &o4,SQInteger arg_2,int exitpos,int &jump)
+{
+ SQInteger nrefidx;
+ switch(type(o1)) {
+ case OT_TABLE:
+ if((nrefidx = _table(o1)->Next(false,o4, o2, o3)) == -1) _FINISH(exitpos);
+ o4 = (SQInteger)nrefidx; _FINISH(1);
+ case OT_ARRAY:
+ if((nrefidx = _array(o1)->Next(o4, o2, o3)) == -1) _FINISH(exitpos);
+ o4 = (SQInteger) nrefidx; _FINISH(1);
+ case OT_STRING:
+ if((nrefidx = _string(o1)->Next(o4, o2, o3)) == -1)_FINISH(exitpos);
+ o4 = (SQInteger)nrefidx; _FINISH(1);
+ case OT_CLASS:
+ if((nrefidx = _class(o1)->Next(o4, o2, o3)) == -1)_FINISH(exitpos);
+ o4 = (SQInteger)nrefidx; _FINISH(1);
+ case OT_USERDATA:
+ case OT_INSTANCE:
+ if(_delegable(o1)->_delegate) {
+ SQObjectPtr itr;
+ Push(o1);
+ Push(o4);
+ if(CallMetaMethod(_delegable(o1), MT_NEXTI, 2, itr)){
+ o4 = o2 = itr;
+ if(type(itr) == OT_NULL) _FINISH(exitpos);
+ if(!Get(o1, itr, o3, false,false)) {
+ Raise_Error(_SC("_nexti returned an invalid idx"));
+ return false;
+ }
+ _FINISH(1);
+ }
+ Raise_Error(_SC("_nexti failed"));
+ return false;
+ }
+ break;
+ case OT_GENERATOR:
+ if(_generator(o1)->_state == SQGenerator::eDead) _FINISH(exitpos);
+ if(_generator(o1)->_state == SQGenerator::eSuspended) {
+ SQInteger idx = 0;
+ if(type(o4) == OT_INTEGER) {
+ idx = _integer(o4) + 1;
+ }
+ o2 = idx;
+ o4 = idx;
+ _generator(o1)->Resume(this, arg_2+1);
+ _FINISH(0);
+ }
+ default:
+ Raise_Error(_SC("cannot iterate %s"), GetTypeName(o1));
+ }
+ return false; //cannot be hit(just to avoid warnings)
+}
+
+bool SQVM::DELEGATE_OP(SQObjectPtr &trg,SQObjectPtr &o1,SQObjectPtr &o2)
+{
+ if(type(o1) != OT_TABLE) { Raise_Error(_SC("delegating a '%s'"), GetTypeName(o1)); return false; }
+ switch(type(o2)) {
+ case OT_TABLE:
+ if(!_table(o1)->SetDelegate(_table(o2))){
+ Raise_Error(_SC("delegate cycle detected"));
+ return false;
+ }
+ break;
+ case OT_NULL:
+ _table(o1)->SetDelegate(NULL);
+ break;
+ default:
+ Raise_Error(_SC("using '%s' as delegate"), GetTypeName(o2));
+ return false;
+ break;
+ }
+ trg = o1;
+ return true;
+}
+#define COND_LITERAL (arg3!=0?ci->_literals[arg1]:STK(arg1))
+
+#define _GUARD(exp) { if(!exp) { Raise_Error(_lasterror); SQ_THROW();} }
+
+#define SQ_THROW() { goto exception_trap; }
+
+bool SQVM::CLOSURE_OP(SQObjectPtr &target, SQFunctionProto *func)
+{
+ SQInteger nouters;
+ SQClosure *closure = SQClosure::Create(_ss(this), func);
+ if((nouters = func->_noutervalues)) {
+ closure->_outervalues.reserve(nouters);
+ for(SQInteger i = 0; i<nouters; i++) {
+ SQOuterVar &v = func->_outervalues[i];
+ switch(v._type){
+ case otSYMBOL:
+ closure->_outervalues.push_back(_null_);
+ if(!Get(_stack._vals[_stackbase]/*STK(0)*/, v._src, closure->_outervalues.top(), false,true))
+ {Raise_IdxError(v._src); return false; }
+ break;
+ case otLOCAL:
+ closure->_outervalues.push_back(_stack._vals[_stackbase+_integer(v._src)]);
+ break;
+ case otOUTER:
+ closure->_outervalues.push_back(_closure(ci->_closure)->_outervalues[_integer(v._src)]);
+ break;
+ }
+ }
+ }
+ target = closure;
+ return true;
+
+}
+
+bool SQVM::GETVARGV_OP(SQObjectPtr &target,SQObjectPtr &index,CallInfo *ci)
+{
+ if(ci->_vargs.size == 0) {
+ Raise_Error(_SC("the function doesn't have var args"));
+ return false;
+ }
+ if(!sq_isnumeric(index)){
+ Raise_Error(_SC("indexing 'vargv' with %s"),GetTypeName(index));
+ return false;
+ }
+ SQInteger idx = tointeger(index);
+ if(idx < 0 || idx >= ci->_vargs.size){ Raise_Error(_SC("vargv index out of range")); return false; }
+ target = _vargsstack[ci->_vargs.base+idx];
+ return true;
+}
+
+bool SQVM::CLASS_OP(SQObjectPtr &target,SQInteger baseclass,SQInteger attributes)
+{
+ SQClass *base = NULL;
+ SQObjectPtr attrs;
+ if(baseclass != -1) {
+ if(type(_stack._vals[_stackbase+baseclass]) != OT_CLASS) { Raise_Error(_SC("trying to inherit from a %s"),GetTypeName(_stack._vals[_stackbase+baseclass])); return false; }
+ base = _class(_stack._vals[_stackbase + baseclass]);
+ }
+ if(attributes != MAX_FUNC_STACKSIZE) {
+ attrs = _stack._vals[_stackbase+attributes];
+ }
+ target = SQClass::Create(_ss(this),base);
+ if(type(_class(target)->_metamethods[MT_INHERITED]) != OT_NULL) {
+ int nparams = 2;
+ SQObjectPtr ret;
+ Push(target); Push(attrs);
+ Call(_class(target)->_metamethods[MT_INHERITED],nparams,_top - nparams, ret, false);
+ Pop(nparams);
+ }
+ _class(target)->_attributes = attrs;
+ return true;
+}
+
+
+
+bool SQVM::IsEqual(SQObjectPtr &o1,SQObjectPtr &o2,bool &res)
+{
+ if(type(o1) == type(o2)) {
+ res = ((_userpointer(o1) == _userpointer(o2)?true:false));
+ }
+ else {
+ if(sq_isnumeric(o1) && sq_isnumeric(o2)) {
+ SQInteger cmpres;
+ if(!ObjCmp(o1, o2,cmpres)) return false;
+ res = (cmpres == 0);
+ }
+ else {
+ res = false;
+ }
+ }
+ return true;
+}
+
+bool SQVM::IsFalse(SQObjectPtr &o)
+{
+ if((type(o) & SQOBJECT_CANBEFALSE) && ( (type(o) == OT_FLOAT) && (_float(o) == SQFloat(0.0)) )
+ || (_integer(o) == 0) ) { //OT_NULL|OT_INTEGER|OT_BOOL
+ return true;
+ }
+ return false;
+}
+
+bool SQVM::GETPARENT_OP(SQObjectPtr &o,SQObjectPtr &target)
+{
+ switch(type(o)) {
+ case OT_TABLE: target = _table(o)->_delegate?SQObjectPtr(_table(o)->_delegate):_null_;
+ break;
+ case OT_CLASS: target = _class(o)->_base?_class(o)->_base:_null_;
+ break;
+ default:
+ Raise_Error(_SC("the %s type doesn't have a parent slot"), GetTypeName(o));
+ return false;
+ }
+ return true;
+}
+
+bool SQVM::Execute(SQObjectPtr &closure, SQInteger target, SQInteger nargs, SQInteger stackbase,SQObjectPtr &outres, SQBool raiseerror,ExecutionType et)
+{
+ if ((_nnativecalls + 1) > MAX_NATIVE_CALLS) { Raise_Error(_SC("Native stack overflow")); return false; }
+ _nnativecalls++;
+ AutoDec ad(&_nnativecalls);
+ SQInteger traps = 0;
+ //temp_reg vars for OP_CALL
+ SQInteger ct_target;
+ SQInteger ct_stackbase;
+ bool ct_tailcall;
+
+ switch(et) {
+ case ET_CALL:
+ if(!StartCall(_closure(closure), _top - nargs, nargs, stackbase, false)) {
+ //call the handler if there are no calls in the stack, if not relies on the previous node
+ if(ci == NULL) CallErrorHandler(_lasterror);
+ return false;
+ }
+ ci->_root = SQTrue;
+ break;
+ case ET_RESUME_GENERATOR: _generator(closure)->Resume(this, target); ci->_root = SQTrue; traps += ci->_etraps; break;
+ case ET_RESUME_VM:
+ traps = _suspended_traps;
+ ci->_root = _suspended_root;
+ ci->_vargs = _suspend_varargs;
+ _suspended = SQFalse;
+ break;
+ }
+
+exception_restore:
+ //
+ {
+ for(;;)
+ {
+ const SQInstruction &_i_ = *ci->_ip++;
+ //dumpstack(_stackbase);
+ //scprintf("\n[%d] %s %d %d %d %d\n",ci->_ip-ci->_iv->_vals,g_InstrDesc[_i_.op].name,arg0,arg1,arg2,arg3);
+ switch(_i_.op)
+ {
+ case _OP_LINE:
+ if(type(_debughook) != OT_NULL && _rawval(_debughook) != _rawval(ci->_closure))
+ CallDebugHook(_SC('l'),arg1);
+ continue;
+ case _OP_LOAD: TARGET = ci->_literals[arg1]; continue;
+ case _OP_LOADINT: TARGET = (SQInteger)arg1; continue;
+ case _OP_LOADFLOAT: TARGET = *((SQFloat *)&arg1); continue;
+ case _OP_DLOAD: TARGET = ci->_literals[arg1]; STK(arg2) = ci->_literals[arg3];continue;
+ case _OP_TAILCALL:
+ temp_reg = STK(arg1);
+ if (type(temp_reg) == OT_CLOSURE){
+ ct_tailcall = true;
+ if(ci->_vargs.size) PopVarArgs(ci->_vargs);
+ for (SQInteger i = 0; i < arg3; i++) STK(i) = STK(arg2 + i);
+ ct_target = ci->_target;
+ ct_stackbase = _stackbase;
+ goto common_call;
+ }
+ case _OP_CALL: {
+ ct_tailcall = false;
+ ct_target = arg0;
+ temp_reg = STK(arg1);
+ ct_stackbase = _stackbase+arg2;
+
+common_call:
+ SQInteger last_top = _top;
+ switch (type(temp_reg)) {
+ case OT_CLOSURE:{
+ _GUARD(StartCall(_closure(temp_reg), ct_target, arg3, ct_stackbase, ct_tailcall));
+ if (_funcproto(_closure(temp_reg)->_function)->_bgenerator) {
+ SQGenerator *gen = SQGenerator::Create(_ss(this), _closure(temp_reg));
+ _GUARD(gen->Yield(this));
+ Return(1, ct_target, temp_reg);
+
+
+
+
+ STK(ct_target) = gen;
+ while (last_top >= _top) _stack._vals[last_top--].Null();
+ continue;
+ }
+ if (type(_debughook) != OT_NULL && _rawval(_debughook) != _rawval(ci->_closure))
+ CallDebugHook(_SC('c'));
+ }
+ continue;
+ case OT_NATIVECLOSURE: {
+ bool suspend;
+ _GUARD(CallNative(_nativeclosure(temp_reg), arg3, ct_stackbase, temp_reg,suspend));
+ if(suspend){
+ _suspended = SQTrue;
+ _suspended_target = ct_target;
+ _suspended_root = ci->_root;
+ _suspended_traps = traps;
+ _suspend_varargs = ci->_vargs;
+ outres = temp_reg;
+ return true;
+ }
+ if(ct_target != -1) { //skip return value for constructors
+ STK(ct_target) = temp_reg;
+ }
+ }
+ continue;
+ case OT_CLASS:{
+ SQObjectPtr inst;
+ _GUARD(CreateClassInstance(_class(temp_reg),inst,temp_reg));
+ STK(ct_target) = inst;
+ ct_target = -1; //fakes return value target so that is not overwritten by the constructor
+ if(type(temp_reg) != OT_NULL) {
+ _stack._vals[ct_stackbase] = inst;
+ goto common_call; //hard core spaghetti code(reissues the OP_CALL to invoke the constructor)
+ }
+ }
+ break;
+ case OT_TABLE:
+ case OT_USERDATA:
+ case OT_INSTANCE:
+ {
+ Push(temp_reg);
+ for (SQInteger i = 0; i < arg3; i++) Push(STK(arg2 + i));
+ if (_delegable(temp_reg) && CallMetaMethod(_delegable(temp_reg), MT_CALL, arg3+1, temp_reg)){
+ STK(ct_target) = temp_reg;
+ break;
+ }
+ Raise_Error(_SC("attempt to call '%s'"), GetTypeName(temp_reg));
+ SQ_THROW();
+ }
+ default:
+ Raise_Error(_SC("attempt to call '%s'"), GetTypeName(temp_reg));
+ SQ_THROW();
+ }
+ }
+ continue;
+ case _OP_PREPCALL:
+ case _OP_PREPCALLK:
+ {
+ SQObjectPtr &key = _i_.op == _OP_PREPCALLK?(ci->_literals)[arg1]:STK(arg1);
+ SQObjectPtr &o = STK(arg2);
+ if (!Get(o, key, temp_reg,false,true)) {
+ if(type(o) == OT_CLASS) { //hack?
+ if(_class_ddel->Get(key,temp_reg)) {
+ STK(arg3) = o;
+ TARGET = temp_reg;
+ continue;
+ }
+ }
+ { Raise_IdxError(key); SQ_THROW();}
+ }
+
+ STK(arg3) = type(o) == OT_CLASS?STK(0):o;
+ TARGET = temp_reg;
+ }
+ continue;
+ case _OP_GETK:
+ if (!Get(STK(arg2), ci->_literals[arg1], temp_reg, false,true)) { Raise_IdxError(ci->_literals[arg1]); SQ_THROW();}
+ TARGET = temp_reg;
+ continue;
+ case _OP_MOVE: TARGET = STK(arg1); continue;
+ case _OP_NEWSLOT:
+ _GUARD(NewSlot(STK(arg1), STK(arg2), STK(arg3),false));
+ if(arg0 != arg3) TARGET = STK(arg3);
+ continue;
+ case _OP_DELETE: _GUARD(DeleteSlot(STK(arg1), STK(arg2), TARGET)); continue;
+ case _OP_SET:
+ if (!Set(STK(arg1), STK(arg2), STK(arg3),true)) { Raise_IdxError(STK(arg2)); SQ_THROW(); }
+ if (arg0 != arg3) TARGET = STK(arg3);
+ continue;
+ case _OP_GET:
+ if (!Get(STK(arg1), STK(arg2), temp_reg, false,true)) { Raise_IdxError(STK(arg2)); SQ_THROW(); }
+ TARGET = temp_reg;
+ continue;
+ case _OP_EQ:{
+ bool res;
+ if(!IsEqual(STK(arg2),COND_LITERAL,res)) { SQ_THROW(); }
+ TARGET = res?_true_:_false_;
+ }continue;
+ case _OP_NE:{
+ bool res;
+ if(!IsEqual(STK(arg2),COND_LITERAL,res)) { SQ_THROW(); }
+ TARGET = (!res)?_true_:_false_;
+ } continue;
+ case _OP_ARITH: _GUARD(ARITH_OP( arg3 , temp_reg, STK(arg2), STK(arg1))); TARGET = temp_reg; continue;
+ case _OP_BITW: _GUARD(BW_OP( arg3,TARGET,STK(arg2),STK(arg1))); continue;
+ case _OP_RETURN:
+ if(type((ci)->_generator) == OT_GENERATOR) {
+ _generator((ci)->_generator)->Kill();
+ }
+ if(Return(arg0, arg1, temp_reg)){
+ assert(traps==0);
+ outres = temp_reg;
+ return true;
+ }
+ continue;
+ case _OP_LOADNULLS:{ for(SQInt32 n=0; n < arg1; n++) STK(arg0+n) = _null_; }continue;
+ case _OP_LOADROOTTABLE: TARGET = _roottable; continue;
+ case _OP_LOADBOOL: TARGET = arg1?_true_:_false_; continue;
+ case _OP_DMOVE: STK(arg0) = STK(arg1); STK(arg2) = STK(arg3); continue;
+ case _OP_JMP: ci->_ip += (sarg1); continue;
+ case _OP_JNZ: if(!IsFalse(STK(arg0))) ci->_ip+=(sarg1); continue;
+ case _OP_JZ: if(IsFalse(STK(arg0))) ci->_ip+=(sarg1); continue;
+ case _OP_LOADFREEVAR: TARGET = _closure(ci->_closure)->_outervalues[arg1]; continue;
+ case _OP_VARGC: TARGET = SQInteger(ci->_vargs.size); continue;
+ case _OP_GETVARGV:
+ if(!GETVARGV_OP(TARGET,STK(arg1),ci)) { SQ_THROW(); }
+ continue;
+ case _OP_NEWTABLE: TARGET = SQTable::Create(_ss(this), arg1); continue;
+ case _OP_NEWARRAY: TARGET = SQArray::Create(_ss(this), 0); _array(TARGET)->Reserve(arg1); continue;
+ case _OP_APPENDARRAY: _array(STK(arg0))->Append(COND_LITERAL); continue;
+ case _OP_GETPARENT: _GUARD(GETPARENT_OP(STK(arg1),TARGET)); continue;
+ case _OP_COMPARITH: _GUARD(DerefInc(arg3, TARGET, STK((((SQUnsignedInteger)arg1&0xFFFF0000)>>16)), STK(arg2), STK(arg1&0x0000FFFF), false)); continue;
+ case _OP_COMPARITHL: _GUARD(LOCAL_INC(arg3, TARGET, STK(arg1), STK(arg2))); continue;
+ case _OP_INC: {SQObjectPtr o(sarg3); _GUARD(DerefInc('+',TARGET, STK(arg1), STK(arg2), o, false));} continue;
+ case _OP_INCL: {SQObjectPtr o(sarg3); _GUARD(LOCAL_INC('+',TARGET, STK(arg1), o));} continue;
+ case _OP_PINC: {SQObjectPtr o(sarg3); _GUARD(DerefInc('+',TARGET, STK(arg1), STK(arg2), o, true));} continue;
+ case _OP_PINCL: {SQObjectPtr o(sarg3); _GUARD(PLOCAL_INC('+',TARGET, STK(arg1), o));} continue;
+ case _OP_CMP: _GUARD(CMP_OP((CmpOP)arg3,STK(arg2),STK(arg1),TARGET)) continue;
+ case _OP_EXISTS: TARGET = Get(STK(arg1), STK(arg2), temp_reg, true,false)?_true_:_false_;continue;
+ case _OP_INSTANCEOF:
+ if(type(STK(arg1)) != OT_CLASS || type(STK(arg2)) != OT_INSTANCE)
+ {Raise_Error(_SC("cannot apply instanceof between a %s and a %s"),GetTypeName(STK(arg1)),GetTypeName(STK(arg2))); SQ_THROW();}
+ TARGET = _instance(STK(arg2))->InstanceOf(_class(STK(arg1)))?_true_:_false_;
+ continue;
+ case _OP_AND:
+ if(IsFalse(STK(arg2))) {
+ TARGET = STK(arg2);
+ ci->_ip += (sarg1);
+ }
+ continue;
+ case _OP_OR:
+ if(!IsFalse(STK(arg2))) {
+ TARGET = STK(arg2);
+ ci->_ip += (sarg1);
+ }
+ continue;
+ case _OP_NEG: _GUARD(NEG_OP(TARGET,STK(arg1))); continue;
+ case _OP_NOT: TARGET = (IsFalse(STK(arg1))?_true_:_false_); continue;
+ case _OP_BWNOT:
+ if(type(STK(arg1)) == OT_INTEGER) {
+ SQInteger t = _integer(STK(arg1));
+ TARGET = SQInteger(~t);
+ continue;
+ }
+ Raise_Error(_SC("attempt to perform a bitwise op on a %s"), GetTypeName(STK(arg1)));
+ SQ_THROW();
+ case _OP_CLOSURE: {
+ SQClosure *c = ci->_closure._unVal.pClosure;
+ SQFunctionProto *fp = c->_function._unVal.pFunctionProto;
+ if(!CLOSURE_OP(TARGET,fp->_functions[arg1]._unVal.pFunctionProto)) { SQ_THROW(); }
+ continue;
+ }
+ case _OP_YIELD:{
+ if(type(ci->_generator) == OT_GENERATOR) {
+ if(sarg1 != MAX_FUNC_STACKSIZE) temp_reg = STK(arg1);
+ _GUARD(_generator(ci->_generator)->Yield(this));
+ traps -= ci->_etraps;
+ if(sarg1 != MAX_FUNC_STACKSIZE) STK(arg1) = temp_reg;
+ }
+ else { Raise_Error(_SC("trying to yield a '%s',only genenerator can be yielded"), GetTypeName(ci->_generator)); SQ_THROW();}
+ if(Return(arg0, arg1, temp_reg)){
+ assert(traps == 0);
+ outres = temp_reg;
+ return true;
+ }
+
+ }
+ continue;
+ case _OP_RESUME:
+ if(type(STK(arg1)) != OT_GENERATOR){ Raise_Error(_SC("trying to resume a '%s',only genenerator can be resumed"), GetTypeName(STK(arg1))); SQ_THROW();}
+ _GUARD(_generator(STK(arg1))->Resume(this, arg0));
+ traps += ci->_etraps;
+ continue;
+ case _OP_FOREACH:{ int tojump;
+ _GUARD(FOREACH_OP(STK(arg0),STK(arg2),STK(arg2+1),STK(arg2+2),arg2,sarg1,tojump));
+ ci->_ip += tojump; }
+ continue;
+ case _OP_POSTFOREACH:
+ assert(type(STK(arg0)) == OT_GENERATOR);
+ if(_generator(STK(arg0))->_state == SQGenerator::eDead)
+ ci->_ip += (sarg1 - 1);
+ continue;
+ case _OP_DELEGATE: _GUARD(DELEGATE_OP(TARGET,STK(arg1),STK(arg2))); continue;
+ case _OP_CLONE:
+ if(!Clone(STK(arg1), TARGET))
+ { Raise_Error(_SC("cloning a %s"), GetTypeName(STK(arg1))); SQ_THROW();}
+ continue;
+ case _OP_TYPEOF: TypeOf(STK(arg1), TARGET); continue;
+ case _OP_PUSHTRAP:{
+ SQInstruction *_iv = _funcproto(_closure(ci->_closure)->_function)->_instructions;
+ _etraps.push_back(SQExceptionTrap(_top,_stackbase, &_iv[(ci->_ip-_iv)+arg1], arg0)); traps++;
+ ci->_etraps++;
+ }
+ continue;
+ case _OP_POPTRAP: {
+ for(SQInteger i = 0; i < arg0; i++) {
+ _etraps.pop_back(); traps--;
+ ci->_etraps--;
+ }
+ }
+ continue;
+ case _OP_THROW: Raise_Error(TARGET); SQ_THROW(); continue;
+ case _OP_CLASS: _GUARD(CLASS_OP(TARGET,arg1,arg2)); continue;
+ case _OP_NEWSLOTA:
+ bool bstatic = (arg0&NEW_SLOT_STATIC_FLAG)?true:false;
+ if(type(STK(arg1)) == OT_CLASS) {
+ if(type(_class(STK(arg1))->_metamethods[MT_NEWMEMBER]) != OT_NULL ) {
+ Push(STK(arg1)); Push(STK(arg2)); Push(STK(arg3));
+ Push((arg0&NEW_SLOT_ATTRIBUTES_FLAG) ? STK(arg2-1) : _null_);
+ int nparams = 4;
+ if(Call(_class(STK(arg1))->_metamethods[MT_NEWMEMBER], nparams, _top - nparams, temp_reg,SQFalse)) {
+ Pop(nparams);
+ continue;
+ }
+ }
+ }
+ _GUARD(NewSlot(STK(arg1), STK(arg2), STK(arg3),bstatic));
+ if((arg0&NEW_SLOT_ATTRIBUTES_FLAG)) {
+ _class(STK(arg1))->SetAttributes(STK(arg2),STK(arg2-1));
+ }
+ continue;
+ }
+
+ }
+ }
+exception_trap:
+ {
+ SQObjectPtr currerror = _lasterror;
+// dumpstack(_stackbase);
+ SQInteger n = 0;
+ SQInteger last_top = _top;
+ if(ci) {
+ if(_ss(this)->_notifyallexceptions) CallErrorHandler(currerror);
+
+ if(traps) {
+ do {
+ if(ci->_etraps > 0) {
+ SQExceptionTrap &et = _etraps.top();
+ ci->_ip = et._ip;
+ _top = et._stacksize;
+ _stackbase = et._stackbase;
+ _stack._vals[_stackbase+et._extarget] = currerror;
+ _etraps.pop_back(); traps--; ci->_etraps--;
+ while(last_top >= _top) _stack._vals[last_top--].Null();
+ goto exception_restore;
+ }
+ //if is a native closure
+ if(type(ci->_closure) != OT_CLOSURE && n)
+ break;
+ if(type(ci->_generator) == OT_GENERATOR) _generator(ci->_generator)->Kill();
+ PopVarArgs(ci->_vargs);
+ POP_CALLINFO(this);
+ n++;
+ } while(_callsstacksize);
+ }
+ else {
+ //call the hook
+ if(raiseerror && !_ss(this)->_notifyallexceptions)
+ CallErrorHandler(currerror);
+ }
+ //remove call stack until a C function is found or the cstack is empty
+ if(ci) do {
+ SQBool exitafterthisone = ci->_root;
+ if(type(ci->_generator) == OT_GENERATOR) _generator(ci->_generator)->Kill();
+ _stackbase -= ci->_prevstkbase;
+ _top = _stackbase + ci->_prevtop;
+ PopVarArgs(ci->_vargs);
+ POP_CALLINFO(this);
+ if( (ci && type(ci->_closure) != OT_CLOSURE) || exitafterthisone) break;
+ } while(_callsstacksize);
+
+ while(last_top >= _top) _stack._vals[last_top--].Null();
+ }
+ _lasterror = currerror;
+ return false;
+ }
+ assert(0);
+}
+
+bool SQVM::CreateClassInstance(SQClass *theclass, SQObjectPtr &inst, SQObjectPtr &constructor)
+{
+ inst = theclass->CreateInstance();
+ if(!theclass->Get(_ss(this)->_constructoridx,constructor)) {
+ //if(!Call(constr,nargs,stackbase,constr,false))
+ // return false;
+ constructor = _null_;
+ }
+ return true;
+}
+
+void SQVM::CallErrorHandler(SQObjectPtr &error)
+{
+ if(type(_errorhandler) != OT_NULL) {
+ SQObjectPtr out;
+ Push(_roottable); Push(error);
+ Call(_errorhandler, 2, _top-2, out,SQFalse);
+ Pop(2);
+ }
+}
+
+void SQVM::CallDebugHook(SQInteger type,SQInteger forcedline)
+{
+ SQObjectPtr temp_reg;
+ SQInteger nparams=5;
+ SQFunctionProto *func=_funcproto(_closure(ci->_closure)->_function);
+ Push(_roottable); Push(type); Push(func->_sourcename); Push(forcedline?forcedline:func->GetLine(ci->_ip)); Push(func->_name);
+ Call(_debughook,nparams,_top-nparams,temp_reg,SQFalse);
+ Pop(nparams);
+}
+
+bool SQVM::CallNative(SQNativeClosure *nclosure,SQInteger nargs,SQInteger stackbase,SQObjectPtr &retval,bool &suspend)
+{
+ if (_nnativecalls + 1 > MAX_NATIVE_CALLS) { Raise_Error(_SC("Native stack overflow")); return false; }
+ SQInteger nparamscheck = nclosure->_nparamscheck;
+ if(((nparamscheck > 0) && (nparamscheck != nargs))
+ || ((nparamscheck < 0) && (nargs < (-nparamscheck)))) {
+ Raise_Error(_SC("wrong number of parameters"));
+ return false;
+ }
+
+ SQInteger tcs;
+ if((tcs = nclosure->_typecheck.size())) {
+ for(SQInteger i = 0; i < nargs && i < tcs; i++)
+ if((nclosure->_typecheck._vals[i] != -1) && !(type(_stack._vals[stackbase+i]) & nclosure->_typecheck[i])) {
+ Raise_ParamTypeError(i,nclosure->_typecheck._vals[i],type(_stack._vals[stackbase+i]));
+ return false;
+ }
+ }
+ _nnativecalls++;
+ if ((_top + MIN_STACK_OVERHEAD) > (SQInteger)_stack.size()) {
+ _stack.resize(_stack.size() + (MIN_STACK_OVERHEAD<<1));
+ }
+ SQInteger oldtop = _top;
+ SQInteger oldstackbase = _stackbase;
+ _top = stackbase + nargs;
+ CallInfo lci;
+ lci._etraps = 0;
+ lci._closure._unVal.pNativeClosure = nclosure;
+ lci._closure._type = OT_NATIVECLOSURE;
+ lci._prevstkbase = (SQInt32) (stackbase - _stackbase);
+ lci._ncalls = 1;
+ lci._prevtop = (SQInt32) (oldtop - oldstackbase);
+ PUSH_CALLINFO(this, lci);
+ _stackbase = stackbase;
+ //push free variables
+ SQInteger outers = nclosure->_outervalues.size();
+ for (SQInteger i = 0; i < outers; i++) {
+ Push(nclosure->_outervalues[i]);
+ }
+
+ if(type(nclosure->_env) == OT_WEAKREF) {
+ _stack[stackbase] = _weakref(nclosure->_env)->_obj;
+ }
+
+
+ SQInteger ret = (nclosure->_function)(this);
+ _nnativecalls--;
+ suspend = false;
+ if( ret == SQ_SUSPEND_FLAG) suspend = true;
+ else if (ret < 0) {
+ _stackbase = oldstackbase;
+ _top = oldtop;
+ POP_CALLINFO(this);
+ Raise_Error(_lasterror);
+ return false;
+ }
+
+ if (ret != 0){ retval = TOP(); }
+ else { retval = _null_; }
+ _stackbase = oldstackbase;
+ _top = oldtop;
+ POP_CALLINFO(this);
+ return true;
+}
+
+bool SQVM::Get(const SQObjectPtr &self,const SQObjectPtr &key,SQObjectPtr &dest,bool raw, bool fetchroot)
+{
+ switch(type(self)){
+ case OT_TABLE:
+ if(_table(self)->Get(key,dest))return true;
+ break;
+ case OT_ARRAY:
+ if(sq_isnumeric(key)){
+ return _array(self)->Get(tointeger(key),dest);
+ }
+ break;
+ case OT_INSTANCE:
+ if(_instance(self)->Get(key,dest)) return true;
+ break;
+ default:break; //shut up compiler
+ }
+ if(FallBackGet(self,key,dest,raw)) return true;
+
+ if(fetchroot) {
+ if(_rawval(STK(0)) == _rawval(self) &&
+ type(STK(0)) == type(self)) {
+ return _table(_roottable)->Get(key,dest);
+ }
+ }
+ return false;
+}
+
+bool SQVM::FallBackGet(const SQObjectPtr &self,const SQObjectPtr &key,SQObjectPtr &dest,bool raw)
+{
+ switch(type(self)){
+ case OT_CLASS:
+ return _class(self)->Get(key,dest);
+ break;
+ case OT_TABLE:
+ case OT_USERDATA:
+ //delegation
+ if(_delegable(self)->_delegate) {
+ if(Get(SQObjectPtr(_delegable(self)->_delegate),key,dest,raw,false))
+ return true;
+ if(raw)return false;
+ Push(self);Push(key);
+ if(CallMetaMethod(_delegable(self),MT_GET,2,dest))
+ return true;
+ }
+ if(type(self) == OT_TABLE) {
+ if(raw) return false;
+ return _table_ddel->Get(key,dest);
+ }
+ return false;
+ break;
+ case OT_ARRAY:
+ if(raw)return false;
+ return _array_ddel->Get(key,dest);
+ case OT_STRING:
+ if(sq_isnumeric(key)){
+ SQInteger n=tointeger(key);
+ if(abs((int)n)<_string(self)->_len){
+ if(n<0)n=_string(self)->_len-n;
+ dest=SQInteger(_stringval(self)[n]);
+ return true;
+ }
+ return false;
+ }
+ else {
+ if(raw)return false;
+ return _string_ddel->Get(key,dest);
+ }
+ break;
+ case OT_INSTANCE:
+ if(raw)return false;
+ Push(self);Push(key);
+ if(!CallMetaMethod(_delegable(self),MT_GET,2,dest)) {
+ return _instance_ddel->Get(key,dest);
+ }
+ return true;
+ case OT_INTEGER:case OT_FLOAT:case OT_BOOL:
+ if(raw)return false;
+ return _number_ddel->Get(key,dest);
+ case OT_GENERATOR:
+ if(raw)return false;
+ return _generator_ddel->Get(key,dest);
+ case OT_CLOSURE: case OT_NATIVECLOSURE:
+ if(raw)return false;
+ return _closure_ddel->Get(key,dest);
+ case OT_THREAD:
+ if(raw)return false;
+ return _thread_ddel->Get(key,dest);
+ case OT_WEAKREF:
+ if(raw)return false;
+ return _weakref_ddel->Get(key,dest);
+ default:return false;
+ }
+ return false;
+}
+
+bool SQVM::Set(const SQObjectPtr &self,const SQObjectPtr &key,const SQObjectPtr &val,bool fetchroot)
+{
+ switch(type(self)){
+ case OT_TABLE:
+ if(_table(self)->Set(key,val))
+ return true;
+ if(_table(self)->_delegate) {
+ if(Set(_table(self)->_delegate,key,val,false)) {
+ return true;
+ }
+ }
+ //keeps going
+ case OT_USERDATA:
+ if(_delegable(self)->_delegate) {
+ SQObjectPtr t;
+ Push(self);Push(key);Push(val);
+ if(CallMetaMethod(_delegable(self),MT_SET,3,t)) return true;
+ }
+ break;
+ case OT_INSTANCE:{
+ if(_instance(self)->Set(key,val))
+ return true;
+ SQObjectPtr t;
+ Push(self);Push(key);Push(val);
+ if(CallMetaMethod(_delegable(self),MT_SET,3,t)) return true;
+ }
+ break;
+ case OT_ARRAY:
+ if(!sq_isnumeric(key)) {Raise_Error(_SC("indexing %s with %s"),GetTypeName(self),GetTypeName(key)); return false; }
+ return _array(self)->Set(tointeger(key),val);
+ default:
+ Raise_Error(_SC("trying to set '%s'"),GetTypeName(self));
+ return false;
+ }
+ if(fetchroot) {
+ if(_rawval(STK(0)) == _rawval(self) &&
+ type(STK(0)) == type(self)) {
+ return _table(_roottable)->Set(key,val);
+ }
+ }
+ return false;
+}
+
+bool SQVM::Clone(const SQObjectPtr &self,SQObjectPtr &target)
+{
+ SQObjectPtr temp_reg;
+ SQObjectPtr newobj;
+ switch(type(self)){
+ case OT_TABLE:
+ newobj = _table(self)->Clone();
+ goto cloned_mt;
+ case OT_INSTANCE:
+ newobj = _instance(self)->Clone(_ss(this));
+cloned_mt:
+ if(_delegable(newobj)->_delegate){
+ Push(newobj);
+ Push(self);
+ CallMetaMethod(_delegable(newobj),MT_CLONED,2,temp_reg);
+ }
+ target = newobj;
+ return true;
+ case OT_ARRAY:
+ target = _array(self)->Clone();
+ return true;
+ default: return false;
+ }
+}
+
+bool SQVM::NewSlot(const SQObjectPtr &self,const SQObjectPtr &key,const SQObjectPtr &val,bool bstatic)
+{
+ if(type(key) == OT_NULL) { Raise_Error(_SC("null cannot be used as index")); return false; }
+ switch(type(self)) {
+ case OT_TABLE: {
+ bool rawcall = true;
+ if(_table(self)->_delegate) {
+ SQObjectPtr res;
+ if(!_table(self)->Get(key,res)) {
+ Push(self);Push(key);Push(val);
+ rawcall = !CallMetaMethod(_table(self),MT_NEWSLOT,3,res);
+ }
+ }
+ if(rawcall) _table(self)->NewSlot(key,val); //cannot fail
+
+ break;}
+ case OT_CLASS:
+ if(!_class(self)->NewSlot(_ss(this),key,val,bstatic)) {
+ if(_class(self)->_locked) {
+ Raise_Error(_SC("trying to modify a class that has already been instantiated"));
+ return false;
+ }
+ else {
+ SQObjectPtr oval = PrintObjVal(key);
+ Raise_Error(_SC("the property '%s' already exists"),_stringval(oval));
+ return false;
+ }
+ }
+ break;
+ default:
+ Raise_Error(_SC("indexing %s with %s"),GetTypeName(self),GetTypeName(key));
+ return false;
+ break;
+ }
+ return true;
+}
+
+bool SQVM::DeleteSlot(const SQObjectPtr &self,const SQObjectPtr &key,SQObjectPtr &res)
+{
+ switch(type(self)) {
+ case OT_TABLE:
+ case OT_INSTANCE:
+ case OT_USERDATA: {
+ SQObjectPtr t;
+ bool handled = false;
+ if(_delegable(self)->_delegate) {
+ Push(self);Push(key);
+ handled = CallMetaMethod(_delegable(self),MT_DELSLOT,2,t);
+ }
+
+ if(!handled) {
+ if(type(self) == OT_TABLE) {
+ if(_table(self)->Get(key,t)) {
+ _table(self)->Remove(key);
+ }
+ else {
+ Raise_IdxError((SQObject &)key);
+ return false;
+ }
+ }
+ else {
+ Raise_Error(_SC("cannot delete a slot from %s"),GetTypeName(self));
+ return false;
+ }
+ }
+ res = t;
+ }
+ break;
+ default:
+ Raise_Error(_SC("attempt to delete a slot from a %s"),GetTypeName(self));
+ return false;
+ }
+ return true;
+}
+
+bool SQVM::Call(SQObjectPtr &closure,SQInteger nparams,SQInteger stackbase,SQObjectPtr &outres,SQBool raiseerror)
+{
+#ifdef _DEBUG
+SQInteger prevstackbase = _stackbase;
+#endif
+ switch(type(closure)) {
+ case OT_CLOSURE:
+ return Execute(closure, _top - nparams, nparams, stackbase,outres,raiseerror);
+ break;
+ case OT_NATIVECLOSURE:{
+ bool suspend;
+ return CallNative(_nativeclosure(closure), nparams, stackbase, outres,suspend);
+
+ }
+ break;
+ case OT_CLASS: {
+ SQObjectPtr constr;
+ SQObjectPtr temp;
+ CreateClassInstance(_class(closure),outres,constr);
+ if(type(constr) != OT_NULL) {
+ _stack[stackbase] = outres;
+ return Call(constr,nparams,stackbase,temp,raiseerror);
+ }
+ return true;
+ }
+ break;
+ default:
+ return false;
+ }
+#ifdef _DEBUG
+ if(!_suspended) {
+ assert(_stackbase == prevstackbase);
+ }
+#endif
+ return true;
+}
+
+bool SQVM::CallMetaMethod(SQDelegable *del,SQMetaMethod mm,SQInteger nparams,SQObjectPtr &outres)
+{
+ SQObjectPtr closure;
+ if(del->GetMetaMethod(this, mm, closure)) {
+ if(Call(closure, nparams, _top - nparams, outres, SQFalse)) {
+ Pop(nparams);
+ return true;
+ }
+ }
+ Pop(nparams);
+ return false;
+}
+
+void SQVM::Remove(SQInteger n) {
+ n = (n >= 0)?n + _stackbase - 1:_top + n;
+ for(SQInteger i = n; i < _top; i++){
+ _stack[i] = _stack[i+1];
+ }
+ _stack[_top] = _null_;
+ _top--;
+}
+
+void SQVM::Pop() {
+ _stack[--_top] = _null_;
+}
+
+void SQVM::Pop(SQInteger n) {
+ for(SQInteger i = 0; i < n; i++){
+ _stack[--_top] = _null_;
+ }
+}
+
+void SQVM::Push(const SQObjectPtr &o) { _stack[_top++] = o; }
+SQObjectPtr &SQVM::Top() { return _stack[_top-1]; }
+SQObjectPtr &SQVM::PopGet() { return _stack[--_top]; }
+SQObjectPtr &SQVM::GetUp(SQInteger n) { return _stack[_top+n]; }
+SQObjectPtr &SQVM::GetAt(SQInteger n) { return _stack[n]; }
+
+#ifdef _DEBUG_DUMP
+void SQVM::dumpstack(SQInteger stackbase,bool dumpall)
+{
+ SQInteger size=dumpall?_stack.size():_top;
+ SQInteger n=0;
+ scprintf(_SC("\n>>>>stack dump<<<<\n"));
+ CallInfo &ci=_callsstack[_callsstacksize-1];
+ scprintf(_SC("IP: %p\n"),ci._ip);
+ scprintf(_SC("prev stack base: %d\n"),ci._prevstkbase);
+ scprintf(_SC("prev top: %d\n"),ci._prevtop);
+ for(SQInteger i=0;i<size;i++){
+ SQObjectPtr &obj=_stack[i];
+ if(stackbase==i)scprintf(_SC(">"));else scprintf(_SC(" "));
+ scprintf(_SC("[%d]:"),n);
+ switch(type(obj)){
+ case OT_FLOAT: scprintf(_SC("FLOAT %.3f"),_float(obj));break;
+ case OT_INTEGER: scprintf(_SC("INTEGER %d"),_integer(obj));break;
+ case OT_BOOL: scprintf(_SC("BOOL %s"),_integer(obj)?"true":"false");break;
+ case OT_STRING: scprintf(_SC("STRING %s"),_stringval(obj));break;
+ case OT_NULL: scprintf(_SC("NULL")); break;
+ case OT_TABLE: scprintf(_SC("TABLE %p[%p]"),_table(obj),_table(obj)->_delegate);break;
+ case OT_ARRAY: scprintf(_SC("ARRAY %p"),_array(obj));break;
+ case OT_CLOSURE: scprintf(_SC("CLOSURE [%p]"),_closure(obj));break;
+ case OT_NATIVECLOSURE: scprintf(_SC("NATIVECLOSURE"));break;
+ case OT_USERDATA: scprintf(_SC("USERDATA %p[%p]"),_userdataval(obj),_userdata(obj)->_delegate);break;
+ case OT_GENERATOR: scprintf(_SC("GENERATOR"));break;
+ case OT_THREAD: scprintf(_SC("THREAD [%p]"),_thread(obj));break;
+ case OT_USERPOINTER: scprintf(_SC("USERPOINTER %p"),_userpointer(obj));break;
+ case OT_CLASS: scprintf(_SC("CLASS %p"),_class(obj));break;
+ case OT_INSTANCE: scprintf(_SC("INSTANCE %p"),_instance(obj));break;
+ case OT_WEAKREF: scprintf(_SC("WEAKERF %p"),_weakref(obj));break;
+ default:
+ assert(0);
+ break;
+ };
+ scprintf(_SC("\n"));
+ ++n;
+ }
+}
+
+
+
+#endif
--- /dev/null
+/* see copyright notice in squirrel.h */
+#ifndef _SQVM_H_
+#define _SQVM_H_
+
+#include "sqopcodes.h"
+#include "sqobject.h"
+#define MAX_NATIVE_CALLS 100
+#define MIN_STACK_OVERHEAD 10
+
+#define SQ_SUSPEND_FLAG -666
+//base lib
+void sq_base_register(HSQUIRRELVM v);
+
+struct SQExceptionTrap{
+ SQExceptionTrap() {}
+ SQExceptionTrap(SQInteger ss, SQInteger stackbase,SQInstruction *ip, SQInteger ex_target){ _stacksize = ss; _stackbase = stackbase; _ip = ip; _extarget = ex_target;}
+ SQExceptionTrap(const SQExceptionTrap &et) { (*this) = et; }
+ SQInteger _stackbase;
+ SQInteger _stacksize;
+ SQInstruction *_ip;
+ SQInteger _extarget;
+};
+
+#define _INLINE
+
+#define STK(a) _stack._vals[_stackbase+(a)]
+#define TARGET _stack._vals[_stackbase+arg0]
+
+typedef sqvector<SQExceptionTrap> ExceptionsTraps;
+
+struct SQVM : public CHAINABLE_OBJ
+{
+ struct VarArgs {
+ VarArgs() { size = 0; base = 0; }
+ unsigned short size;
+ unsigned short base;
+ };
+
+ struct CallInfo{
+ CallInfo() { _generator._type = OT_NULL;}
+ SQInstruction *_ip;
+ SQObjectPtr *_literals;
+ SQObject _closure;
+ SQObject _generator;
+ SQInt32 _etraps;
+ SQInt32 _prevstkbase;
+ SQInt32 _prevtop;
+ SQInt32 _target;
+ SQInt32 _ncalls;
+ SQBool _root;
+ VarArgs _vargs;
+ };
+
+typedef sqvector<CallInfo> CallInfoVec;
+public:
+ enum ExecutionType { ET_CALL, ET_RESUME_GENERATOR, ET_RESUME_VM };
+ SQVM(SQSharedState *ss);
+ ~SQVM();
+ bool Init(SQVM *friendvm, SQInteger stacksize);
+ bool Execute(SQObjectPtr &func, SQInteger target, SQInteger nargs, SQInteger stackbase, SQObjectPtr &outres, SQBool raiseerror, ExecutionType et = ET_CALL);
+ //starts a native call return when the NATIVE closure returns
+ bool CallNative(SQNativeClosure *nclosure, SQInteger nargs, SQInteger stackbase, SQObjectPtr &retval,bool &suspend);
+ //starts a SQUIRREL call in the same "Execution loop"
+ bool StartCall(SQClosure *closure, SQInteger target, SQInteger nargs, SQInteger stackbase, bool tailcall);
+ bool CreateClassInstance(SQClass *theclass, SQObjectPtr &inst, SQObjectPtr &constructor);
+ //call a generic closure pure SQUIRREL or NATIVE
+ bool Call(SQObjectPtr &closure, SQInteger nparams, SQInteger stackbase, SQObjectPtr &outres,SQBool raiseerror);
+ SQRESULT Suspend();
+
+ void CallDebugHook(SQInteger type,SQInteger forcedline=0);
+ void CallErrorHandler(SQObjectPtr &e);
+ bool Get(const SQObjectPtr &self, const SQObjectPtr &key, SQObjectPtr &dest, bool raw, bool fetchroot);
+ bool FallBackGet(const SQObjectPtr &self,const SQObjectPtr &key,SQObjectPtr &dest,bool raw);
+ bool Set(const SQObjectPtr &self, const SQObjectPtr &key, const SQObjectPtr &val, bool fetchroot);
+ bool NewSlot(const SQObjectPtr &self, const SQObjectPtr &key, const SQObjectPtr &val,bool bstatic);
+ bool DeleteSlot(const SQObjectPtr &self, const SQObjectPtr &key, SQObjectPtr &res);
+ bool Clone(const SQObjectPtr &self, SQObjectPtr &target);
+ bool ObjCmp(const SQObjectPtr &o1, const SQObjectPtr &o2,SQInteger &res);
+ bool StringCat(const SQObjectPtr &str, const SQObjectPtr &obj, SQObjectPtr &dest);
+ bool IsEqual(SQObjectPtr &o1,SQObjectPtr &o2,bool &res);
+ void ToString(const SQObjectPtr &o,SQObjectPtr &res);
+ SQString *PrintObjVal(const SQObject &o);
+
+
+ void Raise_Error(const SQChar *s, ...);
+ void Raise_Error(SQObjectPtr &desc);
+ void Raise_IdxError(SQObject &o);
+ void Raise_CompareError(const SQObject &o1, const SQObject &o2);
+ void Raise_ParamTypeError(SQInteger nparam,SQInteger typemask,SQInteger type);
+
+ void TypeOf(const SQObjectPtr &obj1, SQObjectPtr &dest);
+ bool CallMetaMethod(SQDelegable *del, SQMetaMethod mm, SQInteger nparams, SQObjectPtr &outres);
+ bool ArithMetaMethod(SQInteger op, const SQObjectPtr &o1, const SQObjectPtr &o2, SQObjectPtr &dest);
+ bool Return(SQInteger _arg0, SQInteger _arg1, SQObjectPtr &retval);
+ //new stuff
+ _INLINE bool ARITH_OP(SQUnsignedInteger op,SQObjectPtr &trg,const SQObjectPtr &o1,const SQObjectPtr &o2);
+ _INLINE bool BW_OP(SQUnsignedInteger op,SQObjectPtr &trg,const SQObjectPtr &o1,const SQObjectPtr &o2);
+ _INLINE bool NEG_OP(SQObjectPtr &trg,const SQObjectPtr &o1);
+ _INLINE bool CMP_OP(CmpOP op, const SQObjectPtr &o1,const SQObjectPtr &o2,SQObjectPtr &res);
+ bool CLOSURE_OP(SQObjectPtr &target, SQFunctionProto *func);
+ bool GETVARGV_OP(SQObjectPtr &target,SQObjectPtr &idx,CallInfo *ci);
+ bool CLASS_OP(SQObjectPtr &target,SQInteger base,SQInteger attrs);
+ bool GETPARENT_OP(SQObjectPtr &o,SQObjectPtr &target);
+ //return true if the loop is finished
+ bool FOREACH_OP(SQObjectPtr &o1,SQObjectPtr &o2,SQObjectPtr &o3,SQObjectPtr &o4,SQInteger arg_2,int exitpos,int &jump);
+ bool DELEGATE_OP(SQObjectPtr &trg,SQObjectPtr &o1,SQObjectPtr &o2);
+ _INLINE bool LOCAL_INC(SQInteger op,SQObjectPtr &target, SQObjectPtr &a, SQObjectPtr &incr);
+ _INLINE bool PLOCAL_INC(SQInteger op,SQObjectPtr &target, SQObjectPtr &a, SQObjectPtr &incr);
+ _INLINE bool DerefInc(SQInteger op,SQObjectPtr &target, SQObjectPtr &self, SQObjectPtr &key, SQObjectPtr &incr, bool postfix);
+ void PopVarArgs(VarArgs &vargs);
+#ifdef _DEBUG_DUMP
+ void dumpstack(SQInteger stackbase=-1, bool dumpall = false);
+#endif
+
+#ifndef NO_GARBAGE_COLLECTOR
+ void Mark(SQCollectable **chain);
+#endif
+ void Finalize();
+ void GrowCallStack() {
+ SQInteger newsize = _alloccallsstacksize*2;
+ _callsstack = (CallInfo*)sq_realloc(_callsstack,_alloccallsstacksize*sizeof(CallInfo),newsize*sizeof(CallInfo));
+ _alloccallsstacksize = newsize;
+ }
+ void Release(){ sq_delete(this,SQVM); } //does nothing
+////////////////////////////////////////////////////////////////////////////
+ //stack functions for the api
+ void Remove(SQInteger n);
+
+ bool IsFalse(SQObjectPtr &o);
+
+ void Pop();
+ void Pop(SQInteger n);
+ void Push(const SQObjectPtr &o);
+ SQObjectPtr &Top();
+ SQObjectPtr &PopGet();
+ SQObjectPtr &GetUp(SQInteger n);
+ SQObjectPtr &GetAt(SQInteger n);
+
+ SQObjectPtrVec _stack;
+ SQObjectPtrVec _vargsstack;
+ SQInteger _top;
+ SQInteger _stackbase;
+ SQObjectPtr _roottable;
+ SQObjectPtr _lasterror;
+ SQObjectPtr _errorhandler;
+ SQObjectPtr _debughook;
+
+ SQObjectPtr temp_reg;
+
+
+ CallInfo* _callsstack;
+ SQInteger _callsstacksize;
+ SQInteger _alloccallsstacksize;
+
+ ExceptionsTraps _etraps;
+ CallInfo *ci;
+ void *_foreignptr;
+ //VMs sharing the same state
+ SQSharedState *_sharedstate;
+ SQInteger _nnativecalls;
+ //suspend infos
+ SQBool _suspended;
+ SQBool _suspended_root;
+ SQInteger _suspended_target;
+ SQInteger _suspended_traps;
+ VarArgs _suspend_varargs;
+};
+
+struct AutoDec{
+ AutoDec(SQInteger *n) { _n = n; }
+ ~AutoDec() { (*_n)--; }
+ SQInteger *_n;
+};
+
+inline SQObjectPtr &stack_get(HSQUIRRELVM v,SQInteger idx){return ((idx>=0)?(v->GetAt(idx+v->_stackbase-1)):(v->GetUp(idx)));}
+const SQChar *GetTypeName(const SQObjectPtr &obj1);
+const SQChar *IdType2Name(SQObjectType type);
+
+#define _ss(_vm_) (_vm_)->_sharedstate
+
+#ifndef NO_GARBAGE_COLLECTOR
+#define _opt_ss(_vm_) (_vm_)->_sharedstate
+#else
+#define _opt_ss(_vm_) NULL
+#endif
+
+#define PUSH_CALLINFO(v,nci){ \
+ if(v->_callsstacksize == v->_alloccallsstacksize) { \
+ v->GrowCallStack(); \
+ } \
+ v->ci = &v->_callsstack[v->_callsstacksize]; \
+ *(v->ci) = nci; \
+ v->_callsstacksize++; \
+}
+
+#define POP_CALLINFO(v){ \
+ v->_callsstacksize--; \
+ if(v->_callsstacksize) \
+ v->ci = &v->_callsstack[v->_callsstacksize-1] ; \
+ else \
+ v->ci = NULL; \
+}
+#endif //_SQVM_H_
--- /dev/null
+// $Id$
+//
+// SuperTux (Statistics module)
+// Copyright (C) 2004 Ricardo Cruz <rick2@aeiou.pt>
+// Copyright (C) 2006 Ondrej Hosek <ondra.hosek@gmail.com>
+// Copyright (C) 2006 Christoph Sommer <christoph.sommer@2006.expires.deltadevelopment.de>
+//
+// 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 <config.h>
+
+#include <assert.h>
+#include <math.h>
+#include <sstream>
+#include <limits>
+#include "video/drawing_context.hpp"
+#include "gettext.hpp"
+#include "lisp/writer.hpp"
+#include "lisp/lisp.hpp"
+#include "resources.hpp"
+#include "main.hpp"
+#include "statistics.hpp"
+#include "log.hpp"
+#include "scripting/squirrel_util.hpp"
+
+namespace {
+ const int nv_coins = std::numeric_limits<int>::min();
+ const int nv_badguys = std::numeric_limits<int>::min();
+ const float nv_time = std::numeric_limits<float>::max();
+ const int nv_secrets = std::numeric_limits<int>::min();
+}
+
+float WMAP_INFO_LEFT_X;
+float WMAP_INFO_RIGHT_X;
+float WMAP_INFO_TOP_Y1;
+float WMAP_INFO_TOP_Y2;
+
+Statistics::Statistics() : coins(nv_coins), total_coins(nv_coins), badguys(nv_badguys), total_badguys(nv_badguys), time(nv_time), secrets(nv_secrets), total_secrets(nv_secrets), valid(true), display_stat(0)
+{
+ WMAP_INFO_LEFT_X = (SCREEN_WIDTH/2 + 80) + 32;
+ WMAP_INFO_RIGHT_X = SCREEN_WIDTH/2 + 368;
+ WMAP_INFO_TOP_Y1 = SCREEN_HEIGHT/2 + 172 - 16;
+ WMAP_INFO_TOP_Y2 = SCREEN_HEIGHT/2 + 172;
+}
+
+Statistics::~Statistics()
+{
+}
+
+/*
+void
+Statistics::parse(const lisp::Lisp& reader)
+{
+ reader.get("coins-collected", coins);
+ reader.get("coins-collected-total", total_coins);
+ reader.get("badguys-killed", badguys);
+ reader.get("badguys-killed-total", total_badguys);
+ reader.get("time-needed", time);
+ reader.get("secrets-found", secrets);
+ reader.get("secrets-found-total", total_secrets);
+}
+
+void
+Statistics::write(lisp::Writer& writer)
+{
+ writer.write_int("coins-collected", coins);
+ writer.write_int("coins-collected-total", total_coins);
+ writer.write_int("badguys-killed", badguys);
+ writer.write_int("badguys-killed-total", total_badguys);
+ writer.write_float("time-needed", time);
+ writer.write_int("secrets-found", secrets);
+ writer.write_int("secrets-found-total", total_secrets);
+}
+*/
+
+void
+Statistics::serialize_to_squirrel(HSQUIRRELVM vm)
+{
+ // TODO: there's some bug in the unserialization routines that breaks stuff when an empty statistics table is written, so -- as a workaround -- let's make sure we will actually write something first
+ if (!((coins != nv_coins) || (total_coins != nv_coins) || (badguys != nv_badguys) || (total_badguys != nv_badguys) || (time != nv_time) || (secrets != nv_secrets) || (total_secrets != nv_secrets))) return;
+
+ sq_pushstring(vm, "statistics", -1);
+ sq_newtable(vm);
+ if (coins != nv_coins) Scripting::store_int(vm, "coins-collected", coins);
+ if (total_coins != nv_coins) Scripting::store_int(vm, "coins-collected-total", total_coins);
+ if (badguys != nv_badguys) Scripting::store_int(vm, "badguys-killed", badguys);
+ if (total_badguys != nv_badguys) Scripting::store_int(vm, "badguys-killed-total", total_badguys);
+ if (time != nv_time) Scripting::store_float(vm, "time-needed", time);
+ if (secrets != nv_secrets) Scripting::store_int(vm, "secrets-found", secrets);
+ if (total_secrets != nv_secrets) Scripting::store_int(vm, "secrets-found-total", total_secrets);
+ sq_createslot(vm, -3);
+}
+
+void
+Statistics::unserialize_from_squirrel(HSQUIRRELVM vm)
+{
+ sq_pushstring(vm, "statistics", -1);
+ if(SQ_FAILED(sq_get(vm, -2))) {
+ return;
+ }
+ Scripting::get_int(vm, "coins-collected", coins);
+ Scripting::get_int(vm, "coins-collected-total", total_coins);
+ Scripting::get_int(vm, "badguys-killed", badguys);
+ Scripting::get_int(vm, "badguys-killed-total", total_badguys);
+ Scripting::get_float(vm, "time-needed", time);
+ Scripting::get_int(vm, "secrets-found", secrets);
+ Scripting::get_int(vm, "secrets-found-total", total_secrets);
+ sq_pop(vm, 1);
+}
+
+//define TOTAL_DISPLAY_TIME 3400
+//define FADING_TIME 600
+
+#define TOTAL_DISPLAY_TIME 5
+#define FADING_TIME 1
+
+void
+Statistics::draw_worldmap_info(DrawingContext& context)
+{
+ // skip draw if level was never played
+ if (coins == nv_coins) return;
+
+ // skip draw if stats were declared invalid
+ if (!valid) return;
+
+ context.draw_text(white_small_text, std::string("- ") + _("Best Level Statistics") + " -", Vector((WMAP_INFO_LEFT_X + WMAP_INFO_RIGHT_X) / 2, WMAP_INFO_TOP_Y1), ALIGN_CENTER, LAYER_GUI);
+
+ float alpha;
+ if(timer.get_timegone() < FADING_TIME)
+ alpha = (timer.get_timegone() * 1.0f / FADING_TIME);
+ else if(timer.get_timeleft() < FADING_TIME)
+ alpha = (timer.get_timeleft() * 1.0f / FADING_TIME);
+ else
+ alpha = 1.0f;
+
+ context.push_transform();
+ context.set_alpha(alpha);
+
+ char caption_buf[128];
+ char stat_buf[128];
+ switch (display_stat)
+ {
+ case 0:
+ snprintf(caption_buf, sizeof(caption_buf), _("Max coins collected:"));
+ snprintf(stat_buf, sizeof(stat_buf), "%d/%d", coins, total_coins);
+ break;
+ case 1:
+ snprintf(caption_buf, sizeof(caption_buf), _("Max fragging:"));
+ snprintf(stat_buf, sizeof(stat_buf), "%d/%d", badguys, total_badguys);
+ break;
+ case 2:
+ snprintf(caption_buf, sizeof(caption_buf), _("Min time needed:"));
+ {
+ int csecs = (int)(time * 100);
+ int mins = (int)(csecs / 6000);
+ int secs = (csecs % 6000) / 100;
+ snprintf(stat_buf, sizeof(stat_buf), "%02d:%02d", mins,secs);
+ }
+ break;
+ case 3:
+ snprintf(caption_buf, sizeof(caption_buf), _("Max secrets found:"));
+ snprintf(stat_buf, sizeof(stat_buf), "%d/%d", secrets, total_secrets);
+ break;
+ default:
+ log_debug << "Invalid stat requested to be drawn" << std::endl;
+ break;
+ }
+
+ if (!timer.started())
+ {
+ timer.start(TOTAL_DISPLAY_TIME);
+ display_stat++;
+ if (display_stat > 3) display_stat = 0;
+ }
+
+ context.draw_text(white_small_text, caption_buf, Vector(WMAP_INFO_LEFT_X, WMAP_INFO_TOP_Y2), ALIGN_LEFT, LAYER_GUI);
+ context.draw_text(white_small_text, stat_buf, Vector(WMAP_INFO_RIGHT_X, WMAP_INFO_TOP_Y2), ALIGN_RIGHT, LAYER_GUI);
+ context.pop_transform();
+}
+
+void
+Statistics::draw_message_info(DrawingContext& context, std::string title)
+{
+ // skip draw if level was never played
+ // TODO: do we need this?
+ if (coins == nv_coins) return;
+
+ // skip draw if stats were declared invalid
+ if (!valid) return;
+
+ const float width = white_small_text->get_text_width("Max coins collected: 1111 / 1111");
+ const float left = (SCREEN_WIDTH - width) / 2;
+ const float right = (SCREEN_WIDTH + width) / 2;
+
+ context.draw_text(gold_text, title, Vector(SCREEN_WIDTH/2, 410), ALIGN_CENTER, LAYER_GUI);
+
+ char stat_buf[128];
+ int py = 450 + 18;
+
+ snprintf(stat_buf, sizeof(stat_buf), "%d/%d", coins, total_coins);
+ context.draw_text(white_small_text, _("Max coins collected:"), Vector(left, py), ALIGN_LEFT, LAYER_GUI);
+ context.draw_text(white_small_text, "%d / %d", Vector(right, py), ALIGN_RIGHT, LAYER_GUI);
+ py+=18;
+
+ snprintf(stat_buf, sizeof(stat_buf), "%d/%d", badguys, total_badguys);
+ context.draw_text(white_small_text, _("Max fragging:"), Vector(left, py), ALIGN_LEFT, LAYER_GUI);
+ context.draw_text(white_small_text, "%d / %d", Vector(right, py), ALIGN_RIGHT, LAYER_GUI);
+ py+=18;
+
+ int csecs = (int)(time * 100);
+ int mins = (int)(csecs / 6000);
+ int secs = (csecs % 6000) / 100;
+ snprintf(stat_buf, sizeof(stat_buf), "%02d:%02d", mins,secs);
+ context.draw_text(white_small_text, _("Min time needed:"), Vector(left, py), ALIGN_LEFT, LAYER_GUI);
+ context.draw_text(white_small_text, "%02d:%02d", Vector(right, py), ALIGN_RIGHT, LAYER_GUI);
+ py+=18;
+
+ snprintf(stat_buf, sizeof(stat_buf), "%d/%d", secrets, total_secrets);
+ context.draw_text(white_small_text, _("Max secrets found:"), Vector(left, py), ALIGN_LEFT, LAYER_GUI);
+ context.draw_text(white_small_text, "%d / %d", Vector(right, py), ALIGN_RIGHT, LAYER_GUI);
+ py+=18;
+}
+
+void
+Statistics::draw_endseq_panel(DrawingContext& context, Statistics* best_stats, Surface* backdrop)
+{
+ // skip draw if level was never played
+ // TODO: do we need this?
+ if (coins == nv_coins) return;
+
+ // skip draw if stats were declared invalid
+ if (!valid) return;
+
+ // abort if we have no backdrop
+ if (!backdrop) return;
+
+ int box_w = 220+110+110;
+ int box_h = 30+20+20+20;
+ int box_x = (int)((SCREEN_WIDTH - box_w) / 2);
+ int box_y = (int)(SCREEN_HEIGHT / 2) - box_h;
+
+ int bd_w = (int)backdrop->get_width();
+ int bd_h = (int)backdrop->get_height();
+ int bd_x = (int)((SCREEN_WIDTH - bd_w) / 2);
+ int bd_y = box_y + (box_h / 2) - (bd_h / 2);
+
+ int col1_x = box_x;
+ int col2_x = col1_x+200;
+ int col3_x = col2_x+130;
+
+ int row1_y = box_y;
+ int row2_y = row1_y+30;
+ int row3_y = row2_y+20;
+ int row4_y = row3_y+20;
+
+ context.push_transform();
+ context.set_alpha(0.5);
+ context.draw_surface(backdrop, Vector(bd_x, bd_y), LAYER_GUI);
+ context.pop_transform();
+
+ char buf[129];
+ context.draw_text(white_text, _("You"), Vector(col2_x, row1_y), ALIGN_LEFT, LAYER_GUI);
+ context.draw_text(white_text, _("Best"), Vector(col3_x, row1_y), ALIGN_LEFT, LAYER_GUI);
+
+ context.draw_text(white_text, _("Coins"), Vector(col2_x-16, row2_y), ALIGN_RIGHT, LAYER_GUI);
+ snprintf(buf, sizeof(buf), "%d/%d", std::min(coins, 999), std::min(total_coins, 999));
+ context.draw_text(gold_text, buf, Vector(col2_x, row2_y), ALIGN_LEFT, LAYER_GUI);
+ if (best_stats && (best_stats->coins > coins)) {
+ snprintf(buf, sizeof(buf), "%d/%d", std::min(best_stats->coins, 999), std::min(best_stats->total_coins, 999));
+ }
+ context.draw_text(gold_text, buf, Vector(col3_x, row2_y), ALIGN_LEFT, LAYER_GUI);
+
+ context.draw_text(white_text, _("Secrets"), Vector(col2_x-16, row4_y), ALIGN_RIGHT, LAYER_GUI);
+ snprintf(buf, sizeof(buf), "%d/%d", secrets, total_secrets);
+ context.draw_text(gold_text, buf, Vector(col2_x, row4_y), ALIGN_LEFT, LAYER_GUI);
+ if (best_stats && (best_stats->secrets > secrets)) {
+ snprintf(buf, sizeof(buf), "%d/%d", best_stats->secrets, best_stats->total_secrets);
+ }
+ context.draw_text(gold_text, buf, Vector(col3_x, row4_y), ALIGN_LEFT, LAYER_GUI);
+
+ context.draw_text(white_text, _("Time"), Vector(col2_x-16, row3_y), ALIGN_RIGHT, LAYER_GUI);
+ int csecs = (int)(time * 100);
+ int mins = (int)(csecs / 6000);
+ int secs = (csecs % 6000) / 100;
+ snprintf(buf, sizeof(buf), "%02d:%02d", mins,secs);
+ context.draw_text(gold_text, buf, Vector(col2_x, row3_y), ALIGN_LEFT, LAYER_GUI);
+ if (best_stats && (best_stats->time < time)) {
+ int csecs = (int)(best_stats->time * 100);
+ int mins = (int)(csecs / 6000);
+ int secs = (csecs % 6000) / 100;
+ snprintf(buf, sizeof(buf), "%02d:%02d", mins,secs);
+ }
+ context.draw_text(gold_text, buf, Vector(col3_x, row3_y), ALIGN_LEFT, LAYER_GUI);
+}
+
+void
+Statistics::zero()
+{
+ reset();
+ total_coins = 0;
+ total_badguys = 0;
+ total_secrets = 0;
+}
+
+void
+Statistics::reset()
+{
+ coins = 0;
+ badguys = 0;
+ time = 0;
+ secrets = 0;
+}
+
+void
+Statistics::merge(Statistics& s2)
+{
+ if (!s2.valid) return;
+ coins = std::max(coins, s2.coins);
+ total_coins = s2.total_coins;
+ badguys = std::max(badguys, s2.badguys);
+ total_badguys = s2.total_badguys;
+ time = std::min(time, s2.time);
+ secrets = std::max(secrets, s2.secrets);
+ total_secrets = s2.total_secrets;
+}
+
+void
+Statistics::operator+=(const Statistics& s2)
+{
+ if (!s2.valid) return;
+ if (s2.coins != nv_coins) coins += s2.coins;
+ if (s2.total_coins != nv_coins) total_coins += s2.total_coins;
+ if (s2.badguys != nv_badguys) badguys += s2.badguys;
+ if (s2.total_badguys != nv_badguys) total_badguys += s2.total_badguys;
+ if (s2.time != nv_time) time += s2.time;
+ if (s2.secrets != nv_secrets) secrets += s2.secrets;
+ if (s2.total_secrets != nv_secrets) total_secrets += s2.total_secrets;
+}
+
+void
+Statistics::declare_invalid()
+{
+ valid = false;
+}
--- /dev/null
+// $Id$
+//
+// SuperTux (Statistics module)
+// Copyright (C) 2004 Ricardo Cruz <rick2@aeiou.pt>
+// Copyright (C) 2006 Ondrej Hosek <ondra.hosek@gmail.com>
+// Copyright (C) 2006 Christoph Sommer <christoph.sommer@2006.expires.deltadevelopment.de>
+//
+// 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_STATISTICS_H
+#define SUPERTUX_STATISTICS_H
+
+#include <squirrel.h>
+#include "timer.hpp"
+
+namespace lisp { class Writer; }
+namespace lisp { class Lisp; }
+class Surface;
+class DrawingContext;
+
+/** This class is a layer between level and worldmap to keep
+ * track of stuff like scores, and minor, but funny things, like
+ * number of jumps and stuff */
+class Statistics
+{
+public:
+ int coins; /**< coins collected */
+ int total_coins; /**< coins in level */
+ int badguys; /**< badguys actively killed */
+ int total_badguys; /**< (vincible) badguys in level */
+ float time; /**< seconds needed */
+ int secrets; /**< secret areas found */
+ int total_secrets; /**< secret areas in level */
+
+public:
+ Statistics(); /**< Creates new statistics, call reset() before counting */
+ ~Statistics();
+
+ /// read statistics from lisp file
+ //void parse(const lisp::Lisp& lisp);
+ /// write statistics to lisp file
+ //void write(lisp::Writer& writer);
+
+ /**
+ * serialize statistics object as squirrel table "statistics"
+ */
+ void serialize_to_squirrel(HSQUIRRELVM vm);
+
+ /**
+ * unserialize statistics object from squirrel table "statistics"
+ */
+ void unserialize_from_squirrel(HSQUIRRELVM vm);
+
+ void draw_worldmap_info(DrawingContext& context); /**< draw worldmap stat HUD */
+ void draw_message_info(DrawingContext& context, std::string title); /**< draw stats at level start */
+ void draw_endseq_panel(DrawingContext& context, Statistics* best_stats, Surface* backdrop); /**< draw panel shown during level's end sequence */
+
+ void zero(); /**< Set stats to zero */
+ void reset(); /**< Set stats (but not totals) to zero */
+ void merge(Statistics& stats); /**< Given another Statistics object finds the best of each one */
+ void operator+=(const Statistics& o); /**< Add two Statistics objects */
+
+ void declare_invalid(); /**< marks statistics as invalid for their entire lifetime (e.g. after cheating). Invalid statistics will not be merged or drawn. */
+
+private:
+ bool valid; /**< stores whether this statistics can be trusted */
+ Timer timer; /**< for draw_worldmap_info: time until switching to next stat */
+ int display_stat; /**< for draw_worldmap_info: which stat is currently displayed */
+};
+
+#endif /*SUPERTUX_STATISTICS_H*/
--- /dev/null
+// $Id$
+//
+// SuperTux
+// Copyright (C) 2006 Matthias Braun <matze@braunis.de>
+//
+// 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 <config.h>
+
+#include "textscroller.hpp"
+
+#include <stdexcept>
+#include "log.hpp"
+#include "mainloop.hpp"
+#include "resources.hpp"
+#include "video/font.hpp"
+#include "video/drawing_context.hpp"
+#include "video/surface.hpp"
+#include "gui/menu.hpp"
+#include "lisp/parser.hpp"
+#include "lisp/lisp.hpp"
+#include "audio/sound_manager.hpp"
+#include "main.hpp"
+#include "fadeout.hpp"
+#include "control/joystickkeyboardcontroller.hpp"
+
+static const float DEFAULT_SPEED = 20;
+static const float LEFT_BORDER = 50;
+static const float SCROLL = 60;
+static const float ITEMS_SPACE = 4;
+
+TextScroller::TextScroller(const std::string& filename)
+{
+ defaultspeed = DEFAULT_SPEED;
+ speed = defaultspeed;
+
+ std::string text;
+ std::string background_file;
+
+ lisp::Parser parser;
+ try {
+ const lisp::Lisp* root = parser.parse(filename);
+
+ const lisp::Lisp* text_lisp = root->get_lisp("supertux-text");
+ if(!text_lisp)
+ throw std::runtime_error("File isn't a supertux-text file");
+
+ if(!text_lisp->get("text", text))
+ throw std::runtime_error("file doesn't contain a text field");
+ if(!text_lisp->get("background", background_file))
+ throw std::runtime_error("file doesn't contain a background file");
+ text_lisp->get("speed", defaultspeed);
+ text_lisp->get("music", music);
+ } catch(std::exception& e) {
+ std::ostringstream msg;
+ msg << "Couldn't load file '" << filename << "': " << e.what() << std::endl;
+ throw std::runtime_error(msg.str());
+ }
+
+ // Split text string lines into a vector
+ lines = InfoBoxLine::split(text, SCREEN_WIDTH - 2*LEFT_BORDER);
+
+ // load background image
+ background.reset(new Surface("images/background/" + background_file));
+
+ scroll = 0;
+ fading = false;
+}
+
+TextScroller::~TextScroller()
+{
+ for(std::vector<InfoBoxLine*>::iterator i = lines.begin(); i != lines.end(); i++) delete *i;
+}
+
+void
+TextScroller::setup()
+{
+ sound_manager->play_music(music);
+ Menu::set_current(NULL);
+}
+
+void
+TextScroller::update(float elapsed_time)
+{
+ if(main_controller->hold(Controller::UP)) {
+ speed = -defaultspeed*5;
+ } else if(main_controller->hold(Controller::DOWN)) {
+ speed = defaultspeed*5;
+ } else {
+ speed = defaultspeed;
+ }
+ if(main_controller->pressed(Controller::JUMP)
+ || main_controller->pressed(Controller::ACTION)
+ || main_controller->pressed(Controller::MENU_SELECT))
+ scroll += SCROLL;
+ if(main_controller->pressed(Controller::PAUSE_MENU)) {
+ main_loop->exit_screen(new FadeOut(0.5));
+ }
+
+ scroll += speed * elapsed_time;
+
+ if(scroll < 0)
+ scroll = 0;
+}
+
+void
+TextScroller::draw(DrawingContext& context)
+{
+ context.draw_filled_rect(Vector(0, 0), Vector(SCREEN_WIDTH, SCREEN_HEIGHT),
+ Color(0.6f, 0.7f, 0.8f, 0.5f), 0);
+ context.draw_surface(background.get(), Vector(SCREEN_WIDTH/2 - background->get_width()/2 , SCREEN_HEIGHT/2 - background->get_height()/2), 0);
+
+ float y = SCREEN_HEIGHT - scroll;
+ for(size_t i = 0; i < lines.size(); i++) {
+ lines[i]->draw(context, Rect(LEFT_BORDER, y, SCREEN_WIDTH - 2*LEFT_BORDER, y), LAYER_GUI);
+ y += lines[i]->get_height();
+ }
+
+ if(y < 0 && !fading ) {
+ fading = true;
+ main_loop->exit_screen(new FadeOut(0.5));
+ }
+}
+
+InfoBox::InfoBox(const std::string& text)
+ : firstline(0)
+{
+ // Split text string lines into a vector
+ lines = InfoBoxLine::split(text, 400);
+
+ try
+ {
+ // get the arrow sprites
+ arrow_scrollup = new Surface("images/engine/menu/scroll-up.png");
+ arrow_scrolldown = new Surface("images/engine/menu/scroll-down.png");
+ }
+ catch (std::exception& e)
+ {
+ log_warning << "Could not load scrolling images: " << e.what() << std::endl;
+ arrow_scrollup = 0;
+ arrow_scrolldown = 0;
+ }
+}
+
+InfoBox::~InfoBox()
+{
+ for(std::vector<InfoBoxLine*>::iterator i = lines.begin();
+ i != lines.end(); i++)
+ delete *i;
+ delete arrow_scrollup;
+ delete arrow_scrolldown;
+}
+
+void
+InfoBox::draw(DrawingContext& context)
+{
+ float x1 = SCREEN_WIDTH/2-200;
+ float y1 = SCREEN_HEIGHT/2-200;
+ float width = 400;
+ float height = 200;
+
+ context.draw_filled_rect(Vector(x1, y1), Vector(width, height),
+ Color(0.6f, 0.7f, 0.8f, 0.5f), LAYER_GUI-1);
+
+ float y = y1;
+ bool linesLeft = false;
+ for(size_t i = firstline; i < lines.size(); ++i) {
+ if(y >= y1 + height) {
+ linesLeft = true;
+ break;
+ }
+
+ lines[i]->draw(context, Rect(x1, y, x1+width, y), LAYER_GUI);
+ y += lines[i]->get_height();
+ }
+
+ {
+ // draw the scrolling arrows
+ if (arrow_scrollup && firstline > 0)
+ context.draw_surface(arrow_scrollup,
+ Vector( x1 + width - arrow_scrollup->get_width(), // top-right corner of box
+ y1), LAYER_GUI);
+
+ if (arrow_scrolldown && linesLeft && firstline < lines.size()-1)
+ context.draw_surface(arrow_scrolldown,
+ Vector( x1 + width - arrow_scrolldown->get_width(), // bottom-light corner of box
+ y1 + height - arrow_scrolldown->get_height()),
+ LAYER_GUI);
+ }
+}
+
+void
+InfoBox::scrollup()
+{
+ if(firstline > 0)
+ firstline--;
+}
+
+void
+InfoBox::scrolldown()
+{
+ if(firstline < lines.size()-1)
+ firstline++;
+}
+
+void
+InfoBox::pageup()
+{
+}
+
+void
+InfoBox::pagedown()
+{
+}
+
+namespace {
+Font* get_font_by_format_char(char format_char) {
+ switch(format_char)
+ {
+ case ' ':
+ return white_small_text;
+ break;
+ case '\t':
+ return white_text;
+ break;
+ case '-':
+ return white_big_text;
+ break;
+ case '*':
+ return blue_text;
+ break;
+ case '#':
+ return white_text;
+ break;
+ case '!':
+ return 0;
+ break;
+ default:
+ return 0;
+ log_warning << "Unknown format_char: '" << format_char << "'" << std::endl;
+ break;
+ }
+}
+
+InfoBoxLine::LineType get_linetype_by_format_char(char format_char) {
+ switch(format_char)
+ {
+ case ' ':
+ return InfoBoxLine::SMALL;
+ break;
+ case '\t':
+ return InfoBoxLine::NORMAL;
+ break;
+ case '-':
+ return InfoBoxLine::HEADING;
+ break;
+ case '*':
+ return InfoBoxLine::REFERENCE;
+ break;
+ case '#':
+ return InfoBoxLine::NORMAL_LEFT;
+ break;
+ case '!':
+ return InfoBoxLine::IMAGE;
+ break;
+ default:
+ return InfoBoxLine::SMALL;
+ log_warning << "Unknown format_char: '" << format_char << "'" << std::endl;
+ break;
+ }
+}
+}
+
+InfoBoxLine::InfoBoxLine(char format_char, const std::string& text) : lineType(NORMAL), font(white_text), text(text), image(0)
+{
+ font = get_font_by_format_char(format_char);
+ lineType = get_linetype_by_format_char(format_char);
+ if (lineType == IMAGE) image = new Surface(text);
+}
+
+InfoBoxLine::~InfoBoxLine()
+{
+ delete image;
+}
+
+const std::vector<InfoBoxLine*>
+InfoBoxLine::split(const std::string& text, float width)
+{
+ std::vector<InfoBoxLine*> lines;
+
+ std::string::size_type i = 0;
+ std::string::size_type l;
+ char format_char = '#';
+ while(i < text.size()) {
+ // take care of empty lines - represent them as blank lines of normal text
+ if (text[i] == '\n') {
+ lines.push_back(new InfoBoxLine('\t', ""));
+ i++;
+ continue;
+ }
+
+ // extract the format_char
+ format_char = text[i];
+ i++;
+ if (i >= text.size()) break;
+
+ // extract one line
+ l = text.find("\n", i);
+ if (l == std::string::npos) l=text.size();
+ std::string s = text.substr(i, l-i);
+ i = l+1;
+
+ // if we are dealing with an image, just store the line
+ if (format_char == '!') {
+ lines.push_back(new InfoBoxLine(format_char, s));
+ continue;
+ }
+
+ // append wrapped parts of line into list
+ std::string overflow;
+ do {
+ Font* font = get_font_by_format_char(format_char);
+ std::string s2 = s;
+ if (font) s2 = font->wrap_to_width(s2, width, &overflow);
+ lines.push_back(new InfoBoxLine(format_char, s2));
+ s = overflow;
+ } while (s.length() > 0);
+
+ }
+
+ return lines;
+}
+
+void
+InfoBoxLine::draw(DrawingContext& context, const Rect& bbox, int layer)
+{
+ Vector position = bbox.p1;
+ switch (lineType) {
+ case IMAGE:
+ context.draw_surface(image, Vector( (bbox.p1.x + bbox.p2.x - image->get_width()) / 2, position.y), layer);
+ break;
+ case NORMAL_LEFT:
+ context.draw_text(font, text, Vector(position.x, position.y), ALIGN_LEFT, layer);
+ break;
+ default:
+ context.draw_text(font, text, Vector((bbox.p1.x + bbox.p2.x) / 2, position.y), ALIGN_CENTER, layer);
+ break;
+ }
+}
+
+float
+InfoBoxLine::get_height()
+{
+ switch (lineType) {
+ case IMAGE:
+ return image->get_height() + ITEMS_SPACE;
+ case NORMAL_LEFT:
+ return font->get_height() + ITEMS_SPACE;
+ default:
+ return font->get_height() + ITEMS_SPACE;
+ }
+}
--- /dev/null
+// $Id$
+//
+// SuperTux
+// Copyright (C) 2006 Matthias Braun <matze@braunis.de>
+//
+// 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 __TEXTSCROLLER_H__
+#define __TEXTSCROLLER_H__
+
+#include <vector>
+#include <string>
+#include <map>
+
+#include "screen.hpp"
+#include "math/vector.hpp"
+#include "math/rect.hpp"
+
+class DrawingContext;
+class Surface;
+class Font;
+
+/**
+ * Helper class for InfoBox: Represents a line of text
+ */
+class InfoBoxLine
+{
+public:
+ enum LineType { NORMAL, NORMAL_LEFT, SMALL, HEADING, REFERENCE, IMAGE};
+
+ InfoBoxLine(char format_char, const std::string& text);
+ ~InfoBoxLine();
+
+ void draw(DrawingContext& context, const Rect& bbox, int layer);
+ float get_height();
+
+ static const std::vector<InfoBoxLine*> split(const std::string& text, float width);
+
+private:
+ InfoBoxLine::LineType lineType;
+ Font* font;
+ std::string text;
+ Surface* image;
+};
+
+/** This class is displaying a box with information text inside the game
+ */
+class InfoBox
+{
+public:
+ InfoBox(const std::string& text);
+ ~InfoBox();
+
+ void draw(DrawingContext& context);
+ void scrolldown();
+ void scrollup();
+ void pagedown();
+ void pageup();
+
+private:
+ size_t firstline;
+ std::vector<InfoBoxLine*> lines;
+ std::map<std::string, Surface*> images;
+ Surface* arrow_scrollup;
+ Surface* arrow_scrolldown;
+};
+
+class TextScroller : public Screen
+{
+public:
+ TextScroller(const std::string& file);
+ virtual ~TextScroller();
+
+ void setup();
+ void draw(DrawingContext& context);
+ void update(float elapsed_time);
+
+private:
+ float defaultspeed;
+ float speed;
+ std::string music;
+ std::auto_ptr<Surface> background;
+ std::vector<InfoBoxLine*> lines;
+ float scroll;
+ bool fading;
+};
+
+#endif
--- /dev/null
+// $Id$
+//
+// SuperTux
+// Copyright (C) 2004 Tobias Glaesser <tobi.web@gmx.de>
+// Copyright (C) 2006 Matthias Braun <matze@braunis.de>
+//
+// 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 <config.h>
+
+#include <math.h>
+#include <assert.h>
+#include <iostream>
+#include <stdexcept>
+
+#include "lisp/lisp.hpp"
+#include "tile.hpp"
+#include "resources.hpp"
+#include "timer.hpp"
+#include "math/vector.hpp"
+#include "video/drawing_context.hpp"
+#include "log.hpp"
+
+
+Tile::Tile()
+ : id(0), attributes(0), data(0), anim_fps(1)
+{
+}
+
+Tile::Tile(unsigned int id, Uint32 attributes, const ImageSpec& imagespec)
+ : id(id), attributes(attributes), data(0), anim_fps(1)
+{
+ imagespecs.push_back(imagespec);
+}
+
+Tile::~Tile()
+{
+ for(std::vector<Surface*>::iterator i = images.begin(); i != images.end();
+ ++i) {
+ delete *i;
+ }
+}
+
+void
+Tile::parse(const lisp::Lisp& reader)
+{
+ if(!reader.get("id", id)) {
+ throw std::runtime_error("Missing tile-id.");
+ }
+
+ bool value = false;
+ if(reader.get("solid", value) && value)
+ attributes |= SOLID;
+ if(reader.get("unisolid", value) && value)
+ attributes |= UNISOLID | SOLID;
+ if(reader.get("brick", value) && value)
+ attributes |= BRICK;
+ if(reader.get("ice", value) && value)
+ attributes |= ICE;
+ if(reader.get("water", value) && value)
+ attributes |= WATER;
+ if(reader.get("hurts", value) && value)
+ attributes |= HURTS;
+ if(reader.get("fire", value) && value)
+ attributes |= FIRE;
+ if(reader.get("fullbox", value) && value)
+ attributes |= FULLBOX;
+ if(reader.get("coin", value) && value)
+ attributes |= COIN;
+ if(reader.get("goal", value) && value)
+ attributes |= GOAL;
+
+ if(reader.get("north", value) && value)
+ data |= WORLDMAP_NORTH;
+ if(reader.get("south", value) && value)
+ data |= WORLDMAP_SOUTH;
+ if(reader.get("west", value) && value)
+ data |= WORLDMAP_WEST;
+ if(reader.get("east", value) && value)
+ data |= WORLDMAP_EAST;
+ if(reader.get("stop", value) && value)
+ data |= WORLDMAP_STOP;
+
+ reader.get("data", data);
+ reader.get("anim-fps", anim_fps);
+
+ if(reader.get("slope-type", data)) {
+ attributes |= SOLID | SLOPE;
+ }
+
+ const lisp::Lisp* images = reader.get_lisp("images");
+ if(images)
+ parse_images(*images);
+}
+
+void
+Tile::parse_images(const lisp::Lisp& images_lisp)
+{
+ const lisp::Lisp* list = &images_lisp;
+ while(list) {
+ const lisp::Lisp* cur = list->get_car();
+ if(cur->get_type() == lisp::Lisp::TYPE_STRING) {
+ std::string file;
+ cur->get(file);
+ imagespecs.push_back(ImageSpec(file, Rect(0, 0, 0, 0)));
+ } else if(cur->get_type() == lisp::Lisp::TYPE_CONS &&
+ cur->get_car()->get_type() == lisp::Lisp::TYPE_SYMBOL &&
+ cur->get_car()->get_symbol() == "region") {
+ const lisp::Lisp* ptr = cur->get_cdr();
+
+ std::string file;
+ float x = 0, y = 0, w = 0, h = 0;
+ ptr->get_car()->get(file); ptr = ptr->get_cdr();
+ ptr->get_car()->get(x); ptr = ptr->get_cdr();
+ ptr->get_car()->get(y); ptr = ptr->get_cdr();
+ ptr->get_car()->get(w); ptr = ptr->get_cdr();
+ ptr->get_car()->get(h);
+ imagespecs.push_back(ImageSpec(file, Rect(x, y, x+w, y+h)));
+ } else {
+ log_warning << "Expected string or list in images tag" << std::endl;
+ continue;
+ }
+
+ list = list->get_cdr();
+ }
+}
+
+void
+Tile::load_images(const std::string& tilesetpath)
+{
+ assert(images.size() == 0);
+ for(std::vector<ImageSpec>::iterator i = imagespecs.begin(); i !=
+ imagespecs.end(); ++i) {
+ const ImageSpec& spec = *i;
+ Surface* surface;
+ std::string file = tilesetpath + spec.file;
+ if(spec.rect.get_width() <= 0) {
+ surface = new Surface(file);
+ } else {
+ surface = new Surface(file,
+ (int) spec.rect.p1.x,
+ (int) spec.rect.p1.y,
+ (int) spec.rect.get_width(),
+ (int) spec.rect.get_height());
+ }
+ images.push_back(surface);
+ }
+}
+
+void
+Tile::draw(DrawingContext& context, const Vector& pos, int z_pos) const
+{
+ if(images.size() > 1) {
+ size_t frame = size_t(game_time * anim_fps) % images.size();
+ context.draw_surface(images[frame], pos, z_pos);
+ } else if (images.size() == 1) {
+ context.draw_surface(images[0], pos, z_pos);
+ }
+}
--- /dev/null
+// $Id$
+//
+// SuperTux
+// Copyright (C) 2004 Tobias Glaesser <tobi.web@gmx.de>
+// Copyright (C) 2006 Matthias Braun <matze@braunis.de>
+//
+// 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 TILE_H
+#define TILE_H
+
+#include <vector>
+#include <SDL.h>
+#include <stdint.h>
+#include "video/surface.hpp"
+#include "math/rect.hpp"
+
+namespace lisp { class Lisp; }
+
+class DrawingContext;
+
+/**
+Tile Class
+*/
+class Tile
+{
+public:
+ /// bitset for tile attributes
+ enum {
+ /** solid tile that is indestructable by Tux */
+ SOLID = 0x0001,
+ /** uni-directional solid tile */
+ UNISOLID = 0x0002,
+ /** a brick that can be destroyed by jumping under it */
+ BRICK = 0x0004,
+ /** the level should be finished when touching a goaltile.
+ * if data is 0 then the endsequence should be triggered, if data is 1
+ * then we can finish the level instantly.
+ */
+ GOAL = 0x0008,
+ /** slope tile */
+ SLOPE = 0x0010,
+ /** Bonusbox, content is stored in \a data */
+ FULLBOX = 0x0020,
+ /** Tile is a coin */
+ COIN = 0x0040,
+
+ /* interesting flags (the following are passed to gameobjects) */
+ FIRST_INTERESTING_FLAG = 0x0100,
+
+ /** an ice brick that makes tux sliding more than usual */
+ ICE = 0x0100,
+ /** a water tile in which tux starts to swim */
+ WATER = 0x0200,
+ /** a tile that hurts the player if he touches it */
+ HURTS = 0x0400,
+ /** for lava: WATER, HURTS, FIRE */
+ FIRE = 0x0800
+ };
+
+ /// worldmap flags
+ enum {
+ WORLDMAP_NORTH = 0x0001,
+ WORLDMAP_SOUTH = 0x0002,
+ WORLDMAP_EAST = 0x0004,
+ WORLDMAP_WEST = 0x0008,
+ WORLDMAP_DIR_MASK = 0x000f,
+
+ WORLDMAP_STOP = 0x0010,
+
+ // convenience values ("C" stands for crossroads)
+ WORLDMAP_CNSE = WORLDMAP_NORTH | WORLDMAP_SOUTH | WORLDMAP_EAST,
+ WORLDMAP_CNSW = WORLDMAP_NORTH | WORLDMAP_SOUTH | WORLDMAP_WEST,
+ WORLDMAP_CNEW = WORLDMAP_NORTH | WORLDMAP_EAST | WORLDMAP_WEST,
+ WORLDMAP_CSEW = WORLDMAP_SOUTH | WORLDMAP_EAST | WORLDMAP_WEST,
+ WORLDMAP_CNSEW = WORLDMAP_NORTH | WORLDMAP_SOUTH | WORLDMAP_EAST | WORLDMAP_WEST
+ };
+
+ struct ImageSpec {
+ ImageSpec(const std::string& newfile, const Rect& newrect)
+ : file(newfile), rect(newrect)
+ { }
+
+ std::string file;
+ Rect rect;
+ };
+
+private:
+ unsigned int id;
+
+ std::vector<ImageSpec> imagespecs;
+ std::vector<Surface*> images;
+
+ /// tile attributes
+ uint32_t attributes;
+
+ /** General purpose data attached to a tile (content of a box, type of coin)*/
+ int data;
+
+ float anim_fps;
+
+public:
+ ~Tile();
+
+ /** Draw a tile on the screen */
+ void draw(DrawingContext& context, const Vector& pos, int z_pos) const;
+
+ unsigned int getID() const
+ { return id; }
+
+ uint32_t getAttributes() const
+ { return attributes; }
+
+ int getData() const
+ { return data; }
+
+ /// returns the width of the tile in pixels
+ int getWidth() const
+ {
+ if(!images.size())
+ return 0;
+ return (int) images[0]->get_width();
+ }
+
+ /// returns the height of the tiles in pixels
+ int getHeight() const
+ {
+ if(!images.size())
+ return 0;
+ return (int) images[0]->get_height();
+ }
+
+protected:
+ friend class TileManager;
+ Tile();
+ Tile(unsigned int id, Uint32 attributes, const ImageSpec& imagespec);
+
+ void load_images(const std::string& tilesetpath);
+
+ /// parses the tile and returns it's id number
+ void parse(const lisp::Lisp& reader);
+ void parse_images(const lisp::Lisp& cur);
+};
+
+#endif
--- /dev/null
+// $Id$
+//
+// SuperTux
+// Copyright (C) 2004 Tobias Glaesser <tobi.web@gmx.de>
+// Copyright (C) 2006 Matthias Braun <matze@braunis.de>
+//
+// 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 <config.h>
+
+#include <memory>
+#include <stdexcept>
+#include <sstream>
+#include <iostream>
+#include <assert.h>
+#include <SDL.h>
+#include "video/drawing_context.hpp"
+#include "log.hpp"
+#include "lisp/lisp.hpp"
+#include "lisp/parser.hpp"
+#include "lisp/list_iterator.hpp"
+#include "tile.hpp"
+#include "tile_manager.hpp"
+#include "resources.hpp"
+
+TileManager* tile_manager = NULL;
+
+TileManager::TileManager(const std::string& filename)
+{
+#ifdef DEBUG
+ Uint32 ticks = SDL_GetTicks();
+#endif
+ load_tileset(filename);
+#ifdef DEBUG
+ log_debug << "Tiles loaded in " << (SDL_GetTicks() - ticks) / 1000.0 << " seconds" << std::endl;
+#endif
+}
+
+TileManager::~TileManager()
+{
+ for(Tiles::iterator i = tiles.begin(); i != tiles.end(); ++i)
+ delete *i;
+}
+
+void TileManager::load_tileset(std::string filename)
+{
+ // free old tiles
+ for(Tiles::iterator i = tiles.begin(); i != tiles.end(); ++i)
+ delete *i;
+ tiles.clear();
+
+ std::string::size_type t = filename.rfind('/');
+ if(t == std::string::npos) {
+ tiles_path = "";
+ } else {
+ tiles_path = filename.substr(0, t+1);
+ }
+
+ lisp::Parser parser;
+ const lisp::Lisp* root = parser.parse(filename);
+
+ const lisp::Lisp* tiles_lisp = root->get_lisp("supertux-tiles");
+ if(!tiles_lisp)
+ throw std::runtime_error("file is not a supertux tiles file.");
+
+ lisp::ListIterator iter(tiles_lisp);
+ while(iter.next()) {
+ if(iter.item() == "tile") {
+ Tile* tile = new Tile();
+ tile->parse(*(iter.lisp()));
+
+ if(tile->id >= tiles.size())
+ tiles.resize(tile->id+1, 0);
+
+ if(tiles[tile->id] != 0) {
+ log_warning << "Tile with ID " << tile->id << " redefined" << std::endl;
+ delete tile;
+ } else {
+ tiles[tile->id] = tile;
+ }
+ } else if(iter.item() == "tilegroup") {
+ TileGroup tilegroup;
+ const lisp::Lisp* tilegroup_lisp = iter.lisp();
+ tilegroup_lisp->get("name", tilegroup.name);
+ tilegroup_lisp->get_vector("tiles", tilegroup.tiles);
+ tilegroups.insert(tilegroup);
+ } else if (iter.item() == "tiles") {
+ // List of ids (use 0 if the tile should be ignored)
+ std::vector<unsigned int> ids;
+ // List of attributes of the tile
+ std::vector<unsigned int> attributes;
+ std::string image;
+
+ // width and height of the image in tile units, this is used for two
+ // purposes:
+ // a) so we don't have to load the image here to know its dimensions
+ // b) so that the resulting 'tiles' entry is more robust,
+ // ie. enlarging the image won't break the tile id mapping
+ // FIXME: height is actually not used, since width might be enough for
+ // all purposes, still feels somewhat more natural this way
+ unsigned int width = 0;
+ unsigned int height = 0;
+
+ iter.lisp()->get_vector("ids", ids);
+ iter.lisp()->get_vector("attributes", attributes);
+ iter.lisp()->get("image", image);
+ iter.lisp()->get("width", width);
+ iter.lisp()->get("height", height);
+
+ if (ids.size() != attributes.size())
+ {
+ std::ostringstream err;
+ err << "Number of ids (" << ids.size() << ") and attributes (" << attributes.size()
+ << ") missmatch for image '" << image << "', but must be equal";
+ throw std::runtime_error(err.str());
+ }
+
+ for(std::vector<unsigned int>::size_type i = 0; i < ids.size() && i < width*height; ++i)
+ {
+ if (ids[i])
+ {
+ if(ids[i] >= tiles.size())
+ tiles.resize(ids[i]+1, 0);
+
+ int x = 32*(i % width);
+ int y = 32*(i / width);
+ Tile* tile = new Tile(ids[i], attributes[i], Tile::ImageSpec(image, Rect(x, y, x + 32, y + 32)));
+ if (tiles[ids[i]] == 0) {
+ tiles[ids[i]] = tile;
+ } else {
+ log_warning << "Tile with ID " << ids[i] << " redefined" << std::endl;
+ delete tile;
+ }
+ }
+ }
+
+ } else if(iter.item() == "properties") {
+ // deprecated
+ } else {
+ log_warning << "Unknown symbol '" << iter.item() << "' tile defintion file" << std::endl;
+ }
+ }
+
+ if (0)
+ { // enable this if you want to see a list of free tiles
+ log_info << "Last Tile ID is " << tiles.size()-1 << std::endl;
+ int last = -1;
+ for(int i = 0; i < int(tiles.size()); ++i)
+ {
+ if (tiles[i] == 0 && last == -1)
+ {
+ last = i;
+ }
+ else if (tiles[i] && last != -1)
+ {
+ log_info << "Free Tile IDs (" << i - last << "): " << last << " - " << i-1 << std::endl;
+ last = -1;
+ }
+ }
+ }
+}
--- /dev/null
+// $Id$
+//
+// SuperTux
+// Copyright (C) 2004 Tobias Glaesser <tobi.web@gmx.de>
+// Copyright (C) 2006 Matthias Braun <matze@braunis.de>
+//
+// 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 HEADER_TILE_MANAGER_HXX
+#define HEADER_TILE_MANAGER_HXX
+
+#include <set>
+#include <vector>
+#include <string>
+#include <map>
+#include <iostream>
+#include <stdint.h>
+#include <assert.h>
+#include "log.hpp"
+#include "tile.hpp"
+
+struct TileGroup
+{
+ friend bool operator<(const TileGroup& lhs, const TileGroup& rhs)
+ { return lhs.name < rhs.name; };
+ friend bool operator>(const TileGroup& lhs, const TileGroup& rhs)
+ { return lhs.name > rhs.name; };
+
+ std::string name;
+ std::vector<int> tiles;
+};
+
+class TileManager
+{
+private:
+ typedef std::vector<Tile*> Tiles;
+ Tiles tiles;
+
+ static TileManager* instance_ ;
+ std::set<TileGroup> tilegroups;
+
+ std::string tiles_path;
+
+ void load_tileset(std::string filename);
+
+public:
+ TileManager(const std::string& filename);
+ ~TileManager();
+
+ const std::set<TileGroup>& get_tilegroups() const
+ {
+ return tilegroups;
+ }
+
+ const Tile* get(uint32_t id) const
+ {
+ //FIXME: Commenting out tiles in sprites.strf makes tiles.size() fail - it's being set to the first tile commented out.
+ assert(id < tiles.size());
+ Tile* tile = tiles[id];
+ if(!tile) {
+ log_warning << "Invalid tile: " << id << std::endl;
+ return tiles[0];
+ }
+
+ if(tile->images.size() == 0 && tile->imagespecs.size() != 0)
+ tile->load_images(tiles_path);
+
+ return tile;
+ }
+
+ uint32_t get_max_tileid() const
+ {
+ return tiles.size();
+ }
+
+ int get_default_width() const
+ {
+ return 32;
+ }
+
+ int get_default_height() const
+ {
+ return 32;
+ }
+};
+
+extern TileManager* tile_manager;
+
+#endif
--- /dev/null
+// $Id$
+//
+// SuperTux
+// Copyright (C) 2006 Matthias Braun <matze@braunis.de>
+//
+// 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 <config.h>
+
+#include <math.h>
+#include "timer.hpp"
+
+float game_time = 0;
+float real_time = 0;
+
+Timer::Timer()
+ : period(0), cycle_start(0), cyclic(false)
+{
+}
+
+Timer::~Timer()
+{
+}
+
+void
+Timer::start(float period, bool cyclic)
+{
+ this->period = period;
+ this->cyclic = cyclic;
+ cycle_start = game_time;
+}
+
+bool
+Timer::check()
+{
+ if(period == 0)
+ return false;
+
+ if(game_time - cycle_start >= period) {
+ if(cyclic) {
+ cycle_start = game_time - fmodf(game_time - cycle_start, period);
+ } else {
+ period = 0;
+ }
+ return true;
+ }
+
+ return false;
+}
--- /dev/null
+// $Id$
+//
+// SuperTux
+// Copyright (C) 2006 Matthias Braun <matze@braunis.de>
+//
+// 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 float game_time;
+extern float real_time;
+
+/**
+ * Simple timer designed to be used in the update functions of objects
+ */
+class Timer
+{
+public:
+ Timer();
+ ~Timer();
+
+ /** start the timer with the given period (in seconds).
+ * If cyclic=true then the timer willl be reset after each period.
+ * Set period to zero if you want to disable the timer.
+ */
+ void start(float period, bool cyclic = false);
+ /** returns true if a period (or more) passed since start call or last
+ * successfull check
+ */
+ bool check();
+ /** stop the timer */
+ void stop()
+ { start(0); }
+
+ /** returns the period of the timer or 0 if it isn't started */
+ float get_period() const
+ { return period; }
+ float get_timeleft() const
+ { return period - (game_time - cycle_start); }
+ float get_timegone() const
+ { return game_time - cycle_start; }
+ bool started() const
+ { return period != 0 && get_timeleft() > 0; }
+
+private:
+ float period;
+ float cycle_start;
+ bool cyclic;
+};
+
+#endif
--- /dev/null
+/*
+ findlocale-0.46.tar.gz from http://icculus.org/~aspirin/findlocale/
+
+Copyright (C) 2004 Adam D. Moss (the "Author"). All Rights Reserved.
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is fur-
+nished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in
+all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FIT-
+NESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHOR BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
+IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CON-
+NECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+
+Except as contained in this notice, the name of the Author of the
+Software shall not be used in advertising or otherwise to promote the sale,
+use or other dealings in this Software without prior written authorization
+from the Author.
+
+*/
+#include <config.h>
+
+#include <stdlib.h>
+#include <string.h>
+#include <ctype.h>
+
+#ifdef WIN32
+#include <windows.h>
+#include <winnt.h>
+#endif
+
+#ifdef MACOSX
+#include <CoreFoundation/CoreFoundation.h>
+#endif
+
+#include "findlocale.hpp"
+
+static int
+is_lcchar(const int c) {
+ return isalnum(c);
+}
+
+static void
+lang_country_variant_from_envstring(const char *str,
+ char **lang,
+ char **country,
+ char **variant) {
+ int end = 0;
+ int start;
+
+ /* get lang, if any */
+ start = end;
+ while (is_lcchar(str[end])) {
+ ++end;
+ }
+ if (start != end) {
+ int i;
+ int len = end - start;
+ char *s = (char*) malloc(len + 1);
+ for (i=0; i<len; ++i) {
+ s[i] = tolower(str[start + i]);
+ }
+ s[i] = '\0';
+ *lang = s;
+ } else {
+ *lang = NULL;
+ }
+
+ if (str[end] && str[end]!=':') { /* not at end of str */
+ ++end;
+ }
+
+ /* get country, if any */
+ start = end;
+ while (is_lcchar(str[end])) {
+ ++end;
+ }
+ if (start != end) {
+ int i;
+ int len = end - start;
+ char *s = (char*) malloc(len + 1);
+ for (i=0; i<len; ++i) {
+ s[i] = toupper(str[start + i]);
+ }
+ s[i] = '\0';
+ *country = s;
+ } else {
+ *country = NULL;
+ }
+
+ if (str[end] && str[end]!=':') { /* not at end of str */
+ ++end;
+ }
+
+ /* get variant, if any */
+ start = end;
+ while (str[end] && str[end]!=':') {
+ ++end;
+ }
+ if (start != end) {
+ int i;
+ int len = end - start;
+ char *s = (char*) malloc(len + 1);
+ for (i=0; i<len; ++i) {
+ s[i] = str[start + i];
+ }
+ s[i] = '\0';
+ *variant = s;
+ } else {
+ *variant = NULL;
+ }
+}
+
+
+static int
+accumulate_locstring(const char *str, FL_Locale *l) {
+ char *lang = NULL;
+ char *country = NULL;
+ char *variant = NULL;
+ if (str) {
+ lang_country_variant_from_envstring(str, &lang, &country, &variant);
+ if (lang) {
+ l->lang = lang;
+ l->country = country;
+ l->variant = variant;
+ return 1;
+ }
+ }
+ free(lang); free(country); free(variant);
+ return 0;
+}
+
+
+#ifndef WIN32
+static int
+accumulate_env(const char *name, FL_Locale *l) {
+ char *env;
+ char *lang = NULL;
+ char *country = NULL;
+ char *variant = NULL;
+ env = getenv(name);
+ if (env) {
+ return accumulate_locstring(env, l);
+ }
+ free(lang); free(country); free(variant);
+ return 0;
+}
+#endif
+
+static void
+canonise_fl(FL_Locale *l) {
+ /* this function fixes some common locale-specifying mistakes */
+ /* en_UK -> en_GB */
+ if (l->lang && 0 == strcmp(l->lang, "en")) {
+ if (l->country && 0 == strcmp(l->country, "UK")) {
+ free((void*)l->country);
+ l->country = strdup("GB");
+ }
+ }
+ /* ja_JA -> ja_JP */
+ if (l->lang && 0 == strcmp(l->lang, "ja")) {
+ if (l->country && 0 == strcmp(l->country, "JA")) {
+ free((void*)l->country);
+ l->country = strdup("JP");
+ }
+ }
+}
+
+
+#ifdef WIN32
+#include <stdio.h>
+#define ML(pn,sn) MAKELANGID(LANG_##pn, SUBLANG_##pn##_##sn)
+#define MLN(pn) MAKELANGID(LANG_##pn, SUBLANG_DEFAULT)
+#define RML(pn,sn) MAKELANGID(LANG_##pn, SUBLANG_##sn)
+typedef struct {
+ LANGID id;
+ char* code;
+} IDToCode;
+static const IDToCode both_to_code[] = {
+ {ML(ENGLISH,US), "en_US.ISO_8859-1"},
+ {ML(ENGLISH,CAN), "en_CA"}, /* english / canadian */
+ {ML(ENGLISH,UK), "en_GB"},
+ {ML(ENGLISH,EIRE), "en_IE"},
+ {ML(ENGLISH,AUS), "en_AU"},
+ {MLN(GERMAN), "de_DE"},
+ {MLN(SPANISH), "es_ES"},
+ {ML(SPANISH,MEXICAN), "es_MX"},
+ {MLN(FRENCH), "fr_FR"},
+ {ML(FRENCH,CANADIAN), "fr_CA"},
+ {ML(FRENCH,BELGIAN), "fr_BE"}, /* ? */
+ {ML(DUTCH,BELGIAN), "nl_BE"}, /* ? */
+ {ML(PORTUGUESE,BRAZILIAN), "pt_BR"},
+ {MLN(PORTUGUESE), "pt_PT"},
+ {MLN(SWEDISH), "sv_SE"},
+ {ML(CHINESE,HONGKONG), "zh_HK"},
+ /* these are machine-generated and not yet verified */
+ {RML(AFRIKAANS,DEFAULT), "af_ZA"},
+ {RML(ALBANIAN,DEFAULT), "sq_AL"},
+ {RML(ARABIC,ARABIC_ALGERIA), "ar_DZ"},
+ {RML(ARABIC,ARABIC_BAHRAIN), "ar_BH"},
+ {RML(ARABIC,ARABIC_EGYPT), "ar_EG"},
+ {RML(ARABIC,ARABIC_IRAQ), "ar_IQ"},
+ {RML(ARABIC,ARABIC_JORDAN), "ar_JO"},
+ {RML(ARABIC,ARABIC_KUWAIT), "ar_KW"},
+ {RML(ARABIC,ARABIC_LEBANON), "ar_LB"},
+ {RML(ARABIC,ARABIC_LIBYA), "ar_LY"},
+ {RML(ARABIC,ARABIC_MOROCCO), "ar_MA"},
+ {RML(ARABIC,ARABIC_OMAN), "ar_OM"},
+ {RML(ARABIC,ARABIC_QATAR), "ar_QA"},
+ {RML(ARABIC,ARABIC_SAUDI_ARABIA), "ar_SA"},
+ {RML(ARABIC,ARABIC_SYRIA), "ar_SY"},
+ {RML(ARABIC,ARABIC_TUNISIA), "ar_TN"},
+ {RML(ARABIC,ARABIC_UAE), "ar_AE"},
+ {RML(ARABIC,ARABIC_YEMEN), "ar_YE"},
+ {RML(ARMENIAN,DEFAULT), "hy_AM"},
+ {RML(AZERI,AZERI_CYRILLIC), "az_AZ"},
+ {RML(AZERI,AZERI_LATIN), "az_AZ"},
+ {RML(BASQUE,DEFAULT), "eu_ES"},
+ {RML(BELARUSIAN,DEFAULT), "be_BY"},
+/*{RML(BRETON,DEFAULT), "br_FR"},*/
+ {RML(BULGARIAN,DEFAULT), "bg_BG"},
+ {RML(CATALAN,DEFAULT), "ca_ES"},
+ {RML(CHINESE,CHINESE_HONGKONG), "zh_HK"},
+ {RML(CHINESE,CHINESE_MACAU), "zh_MO"},
+ {RML(CHINESE,CHINESE_SIMPLIFIED), "zh_CN"},
+ {RML(CHINESE,CHINESE_SINGAPORE), "zh_SG"},
+ {RML(CHINESE,CHINESE_TRADITIONAL), "zh_TW"},
+/*{RML(CORNISH,DEFAULT), "kw_GB"},*/
+ {RML(CZECH,DEFAULT), "cs_CZ"},
+ {RML(DANISH,DEFAULT), "da_DK"},
+ {RML(DUTCH,DUTCH), "nl_NL"},
+ {RML(DUTCH,DUTCH_BELGIAN), "nl_BE"},
+/*{RML(DUTCH,DUTCH_SURINAM), "nl_SR"},*/
+ {RML(ENGLISH,ENGLISH_AUS), "en_AU"},
+ {RML(ENGLISH,ENGLISH_BELIZE), "en_BZ"},
+ {RML(ENGLISH,ENGLISH_CAN), "en_CA"},
+ {RML(ENGLISH,ENGLISH_CARIBBEAN), "en_CB"},
+ {RML(ENGLISH,ENGLISH_EIRE), "en_IE"},
+ {RML(ENGLISH,ENGLISH_JAMAICA), "en_JM"},
+ {RML(ENGLISH,ENGLISH_NZ), "en_NZ"},
+ {RML(ENGLISH,ENGLISH_PHILIPPINES), "en_PH"},
+ {RML(ENGLISH,ENGLISH_SOUTH_AFRICA), "en_ZA"},
+ {RML(ENGLISH,ENGLISH_TRINIDAD), "en_TT"},
+ {RML(ENGLISH,ENGLISH_UK), "en_GB"},
+ {RML(ENGLISH,ENGLISH_US), "en_US"},
+ {RML(ENGLISH,ENGLISH_ZIMBABWE), "en_ZW"},
+/*{RML(ESPERANTO,DEFAULT), "eo_"},*/
+ {RML(ESTONIAN,DEFAULT), "et_EE"},
+ {RML(FAEROESE,DEFAULT), "fo_FO"},
+ {RML(FARSI,DEFAULT), "fa_IR"},
+ {RML(FINNISH,DEFAULT), "fi_FI"},
+ {RML(FRENCH,FRENCH), "fr_FR"},
+ {RML(FRENCH,FRENCH_BELGIAN), "fr_BE"},
+ {RML(FRENCH,FRENCH_CANADIAN), "fr_CA"},
+ {RML(FRENCH,FRENCH_LUXEMBOURG), "fr_LU"},
+ {RML(FRENCH,FRENCH_MONACO), "fr_MC"},
+ {RML(FRENCH,FRENCH_SWISS), "fr_CH"},
+/*{RML(GAELIC,GAELIC), "ga_IE"},*/
+/*{RML(GAELIC,GAELIC_MANX), "gv_GB"},*/
+/*{RML(GAELIC,GAELIC_SCOTTISH), "gd_GB"},*/
+/*{RML(GALICIAN,DEFAULT), "gl_ES"},*/
+ {RML(GEORGIAN,DEFAULT), "ka_GE"},
+ {RML(GERMAN,GERMAN), "de_DE"},
+ {RML(GERMAN,GERMAN_AUSTRIAN), "de_AT"},
+ {RML(GERMAN,GERMAN_LIECHTENSTEIN), "de_LI"},
+ {RML(GERMAN,GERMAN_LUXEMBOURG), "de_LU"},
+ {RML(GERMAN,GERMAN_SWISS), "de_CH"},
+ {RML(GREEK,DEFAULT), "el_GR"},
+ {RML(GUJARATI,DEFAULT), "gu_IN"},
+ {RML(HEBREW,DEFAULT), "he_IL"},
+ {RML(HINDI,DEFAULT), "hi_IN"},
+ {RML(HUNGARIAN,DEFAULT), "hu_HU"},
+ {RML(ICELANDIC,DEFAULT), "is_IS"},
+ {RML(INDONESIAN,DEFAULT), "id_ID"},
+ {RML(ITALIAN,ITALIAN), "it_IT"},
+ {RML(ITALIAN,ITALIAN_SWISS), "it_CH"},
+ {RML(JAPANESE,DEFAULT), "ja_JP"},
+ {RML(KANNADA,DEFAULT), "kn_IN"},
+ {RML(KAZAK,DEFAULT), "kk_KZ"},
+ {RML(KONKANI,DEFAULT), "kok_IN"},
+ {RML(KOREAN,KOREAN), "ko_KR"},
+/*{RML(KYRGYZ,DEFAULT), "ky_KG"},*/
+ {RML(LATVIAN,DEFAULT), "lv_LV"},
+ {RML(LITHUANIAN,LITHUANIAN), "lt_LT"},
+ {RML(MACEDONIAN,DEFAULT), "mk_MK"},
+ {RML(MALAY,MALAY_BRUNEI_DARUSSALAM), "ms_BN"},
+ {RML(MALAY,MALAY_MALAYSIA), "ms_MY"},
+ {RML(MARATHI,DEFAULT), "mr_IN"},
+/*{RML(MONGOLIAN,DEFAULT), "mn_MN"},*/
+ {RML(NORWEGIAN,NORWEGIAN_BOKMAL), "nb_NO"},
+ {RML(NORWEGIAN,NORWEGIAN_NYNORSK), "nn_NO"},
+ {RML(POLISH,DEFAULT), "pl_PL"},
+ {RML(PORTUGUESE,PORTUGUESE), "pt_PT"},
+ {RML(PORTUGUESE,PORTUGUESE_BRAZILIAN), "pt_BR"},
+ {RML(PUNJABI,DEFAULT), "pa_IN"},
+ {RML(ROMANIAN,DEFAULT), "ro_RO"},
+ {RML(RUSSIAN,DEFAULT), "ru_RU"},
+ {RML(SANSKRIT,DEFAULT), "sa_IN"},
+ {RML(SERBIAN,DEFAULT), "hr_HR"},
+ {RML(SERBIAN,SERBIAN_CYRILLIC), "sr_SP"},
+ {RML(SERBIAN,SERBIAN_LATIN), "sr_SP"},
+ {RML(SLOVAK,DEFAULT), "sk_SK"},
+ {RML(SLOVENIAN,DEFAULT), "sl_SI"},
+ {RML(SPANISH,SPANISH), "es_ES"},
+ {RML(SPANISH,SPANISH_ARGENTINA), "es_AR"},
+ {RML(SPANISH,SPANISH_BOLIVIA), "es_BO"},
+ {RML(SPANISH,SPANISH_CHILE), "es_CL"},
+ {RML(SPANISH,SPANISH_COLOMBIA), "es_CO"},
+ {RML(SPANISH,SPANISH_COSTA_RICA), "es_CR"},
+ {RML(SPANISH,SPANISH_DOMINICAN_REPUBLIC), "es_DO"},
+ {RML(SPANISH,SPANISH_ECUADOR), "es_EC"},
+ {RML(SPANISH,SPANISH_EL_SALVADOR), "es_SV"},
+ {RML(SPANISH,SPANISH_GUATEMALA), "es_GT"},
+ {RML(SPANISH,SPANISH_HONDURAS), "es_HN"},
+ {RML(SPANISH,SPANISH_MEXICAN), "es_MX"},
+ {RML(SPANISH,SPANISH_MODERN), "es_ES"},
+ {RML(SPANISH,SPANISH_NICARAGUA), "es_NI"},
+ {RML(SPANISH,SPANISH_PANAMA), "es_PA"},
+ {RML(SPANISH,SPANISH_PARAGUAY), "es_PY"},
+ {RML(SPANISH,SPANISH_PERU), "es_PE"},
+ {RML(SPANISH,SPANISH_PUERTO_RICO), "es_PR"},
+ {RML(SPANISH,SPANISH_URUGUAY), "es_UY"},
+ {RML(SPANISH,SPANISH_VENEZUELA), "es_VE"},
+ {RML(SWAHILI,DEFAULT), "sw_KE"},
+ {RML(SWEDISH,SWEDISH), "sv_SE"},
+ {RML(SWEDISH,SWEDISH_FINLAND), "sv_FI"},
+/*{RML(SYRIAC,DEFAULT), "syr_SY"},*/
+ {RML(TAMIL,DEFAULT), "ta_IN"},
+ {RML(TATAR,DEFAULT), "tt_TA"},
+ {RML(TELUGU,DEFAULT), "te_IN"},
+ {RML(THAI,DEFAULT), "th_TH"},
+ {RML(TURKISH,DEFAULT), "tr_TR"},
+ {RML(UKRAINIAN,DEFAULT), "uk_UA"},
+ {RML(URDU,URDU_PAKISTAN), "ur_PK"},
+ {RML(UZBEK,UZBEK_CYRILLIC), "uz_UZ"},
+ {RML(UZBEK,UZBEK_LATIN), "uz_UZ"},
+ {RML(VIETNAMESE,DEFAULT), "vi_VN"},
+/*{RML(WALON,DEFAULT), "wa_BE"},*/
+/*{RML(WELSH,DEFAULT), "cy_GB"},*/
+};
+static const IDToCode primary_to_code[] = {
+ {LANG_AFRIKAANS, "af"},
+ {LANG_ARABIC, "ar"},
+ {LANG_AZERI, "az"},
+ {LANG_BULGARIAN, "bg"},
+/*{LANG_BRETON, "br"},*/
+ {LANG_BELARUSIAN, "by"},
+ {LANG_CATALAN, "ca"},
+ {LANG_CZECH, "cs"},
+/*{LANG_WELSH, "cy"},*/
+ {LANG_DANISH, "da"},
+ {LANG_GERMAN, "de"},
+ {LANG_GREEK, "el"},
+ {LANG_ENGLISH, "en"},
+/*{LANG_ESPERANTO, "eo"},*/
+ {LANG_SPANISH, "es"},
+ {LANG_ESTONIAN, "et"},
+ {LANG_BASQUE, "eu"},
+ {LANG_FARSI, "fa"},
+ {LANG_FINNISH, "fi"},
+ {LANG_FAEROESE, "fo"},
+ {LANG_FRENCH, "fr"},
+/*{LANG_GAELIC, "ga"},*/
+/*{LANG_GALICIAN, "gl"},*/
+ {LANG_GUJARATI, "gu"},
+ {LANG_HEBREW, "he"},
+ {LANG_HINDI, "hi"},
+ {LANG_SERBIAN, "hr"},
+ {LANG_HUNGARIAN, "hu"},
+ {LANG_ARMENIAN, "hy"},
+ {LANG_INDONESIAN, "id"},
+ {LANG_ITALIAN, "it"},
+ {LANG_JAPANESE, "ja"},
+ {LANG_GEORGIAN, "ka"},
+ {LANG_KAZAK, "kk"},
+ {LANG_KANNADA, "kn"},
+ {LANG_KOREAN, "ko"},
+/*{LANG_KYRGYZ, "ky"},*/
+ {LANG_LITHUANIAN, "lt"},
+ {LANG_LATVIAN, "lv"},
+ {LANG_MACEDONIAN, "mk"},
+/*{LANG_MONGOLIAN, "mn"},*/
+ {LANG_MARATHI, "mr"},
+ {LANG_MALAY, "ms"},
+ {LANG_NORWEGIAN, "nb"},
+ {LANG_DUTCH, "nl"},
+ {LANG_NORWEGIAN, "nn"},
+ {LANG_NORWEGIAN, "no"},/* unofficial? */
+ {LANG_PUNJABI, "pa"},
+ {LANG_POLISH, "pl"},
+ {LANG_PORTUGUESE, "pt"},
+ {LANG_ROMANIAN, "ro"},
+ {LANG_RUSSIAN, "ru"},
+ {LANG_SLOVAK, "sk"},
+ {LANG_SLOVENIAN, "sl"},
+ {LANG_ALBANIAN, "sq"},
+ {LANG_SERBIAN, "sr"},
+ {LANG_SWEDISH, "sv"},
+ {LANG_SWAHILI, "sw"},
+ {LANG_TAMIL, "ta"},
+ {LANG_THAI, "th"},
+ {LANG_TURKISH, "tr"},
+ {LANG_TATAR, "tt"},
+ {LANG_UKRAINIAN, "uk"},
+ {LANG_URDU, "ur"},
+ {LANG_UZBEK, "uz"},
+ {LANG_VIETNAMESE, "vi"},
+/*{LANG_WALON, "wa"},*/
+ {LANG_CHINESE, "zh"},
+};
+static int num_primary_to_code =
+ sizeof(primary_to_code) / sizeof(*primary_to_code);
+static int num_both_to_code =
+ sizeof(both_to_code) / sizeof(*both_to_code);
+
+static const int
+lcid_to_fl(LCID lcid,
+ FL_Locale *rtn) {
+ LANGID langid = LANGIDFROMLCID(lcid);
+ LANGID primary_lang = PRIMARYLANGID(langid);
+#if 0
+ LANGID sub_lang = SUBLANGID(langid);
+#endif
+ int i;
+ /* try to find an exact primary/sublanguage combo that we know about */
+ for (i=0; i<num_both_to_code; ++i) {
+ if (both_to_code[i].id == langid) {
+ accumulate_locstring(both_to_code[i].code, rtn);
+ return 1;
+ }
+ }
+ /* fallback to just checking the primary language id */
+ for (i=0; i<num_primary_to_code; ++i) {
+ if (primary_to_code[i].id == primary_lang) {
+ accumulate_locstring(primary_to_code[i].code, rtn);
+ return 1;
+ }
+ }
+ return 0;
+}
+#endif
+
+
+FL_Success
+FL_FindLocale(FL_Locale **locale, FL_Domain /*domain*/) {
+ FL_Success success = FL_FAILED;
+ FL_Locale *rtn = (FL_Locale*) malloc(sizeof(FL_Locale));
+ rtn->lang = NULL;
+ rtn->country = NULL;
+ rtn->variant = NULL;
+
+#ifdef WIN32
+ /* win32 >= mswindows95 */
+ {
+ LCID lcid = GetThreadLocale();
+ if (lcid_to_fl(lcid, rtn)) {
+ success = FL_CONFIDENT;
+ }
+ if (success == FL_FAILED) {
+ /* assume US English on mswindows systems unless we know otherwise */
+ if (accumulate_locstring("en_US.ISO_8859-1", rtn)) {
+ success = FL_DEFAULT_GUESS;
+ }
+ }
+ }
+#else
+ /* assume unixoid */
+ {
+#ifdef MACOSX
+ CFIndex sz;
+ CFArrayRef languages;
+ CFStringRef uxstylelangs;
+ char *uxsl;
+
+ /* get the languages from the user's presets */
+ languages = (CFArrayRef)CFPreferencesCopyValue(CFSTR("AppleLanguages"),
+ kCFPreferencesAnyApplication, kCFPreferencesCurrentUser,
+ kCFPreferencesAnyHost);
+
+ /* join the returned string array into a string separated by colons */
+ uxstylelangs = CFStringCreateByCombiningStrings(kCFAllocatorDefault,
+ languages, CFSTR(":"));
+
+ /* convert this string into a C string */
+ sz = CFStringGetLength(uxstylelangs) + 1;
+ uxsl = (char*)malloc(sz);
+ CFStringGetCString(uxstylelangs, uxsl, sz, kCFStringEncodingISOLatin1);
+
+ /* add it to the list */
+ if (accumulate_locstring(uxsl, rtn)) {
+ success = FL_CONFIDENT;
+ }
+ /* continue the UNIX method */
+#endif
+ /* examples: */
+ /* sv_SE.ISO_8859-1 */
+ /* fr_FR.ISO8859-1 */
+ /* no_NO_NB */
+ /* no_NO_NY */
+ /* no_NO */
+ /* de_DE */
+ /* try the various vars in decreasing order of authority */
+ if (accumulate_env("LC_ALL", rtn) ||
+ accumulate_env("LC_MESSAGES", rtn) ||
+ accumulate_env("LANG", rtn) ||
+ accumulate_env("LANGUAGE", rtn)) {
+ success = FL_CONFIDENT;
+ }
+ if (success == FL_FAILED) {
+ /* assume US English on unixoid systems unless we know otherwise */
+ if (accumulate_locstring("en_US.ISO_8859-1", rtn)) {
+ success = FL_DEFAULT_GUESS;
+ }
+ }
+ }
+#endif
+
+ if (success != FL_FAILED) {
+ canonise_fl(rtn);
+ }
+
+ *locale = rtn;
+ return success;
+}
+
+
+void
+FL_FreeLocale(FL_Locale **locale) {
+ if (locale) {
+ FL_Locale *l = *locale;
+ if (l) {
+ if (l->lang) {
+ free((void*)l->lang);
+ }
+ if (l->country) {
+ free((void*)l->country);
+ }
+ if (l->variant) {
+ free((void*)l->variant);
+ }
+ free(l);
+ *locale = NULL;
+ }
+ }
+}
--- /dev/null
+/*
+ findlocale-0.46.tar.gz from http://icculus.org/~aspirin/findlocale/
+
+Copyright (C) 2004 Adam D. Moss (the "Author"). All Rights Reserved.
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is fur-
+nished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in
+all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FIT-
+NESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHOR BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
+IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CON-
+NECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+
+Except as contained in this notice, the name of the Author of the
+Software shall not be used in advertising or otherwise to promote the sale,
+use or other dealings in this Software without prior written authorization
+from the Author.
+
+*/
+
+#ifndef __findlocale_h_
+#define __findlocale_h_
+
+typedef const char* FL_Lang;
+typedef const char* FL_Country;
+typedef const char* FL_Variant;
+
+typedef struct {
+ FL_Lang lang;
+ FL_Country country;
+ FL_Variant variant;
+} FL_Locale;
+
+typedef enum {
+ /* for some reason we failed to even guess: this should never happen */
+ FL_FAILED = 0,
+ /* couldn't query locale -- returning a guess (almost always English) */
+ FL_DEFAULT_GUESS = 1,
+ /* the returned locale type was found by successfully asking the system */
+ FL_CONFIDENT = 2
+} FL_Success;
+
+typedef enum {
+ FL_MESSAGES = 0
+} FL_Domain;
+
+/* This allocates/fills in a FL_Locale structure with pointers to
+ strings (which should be treated as static), or NULL for inappropriate /
+ undetected fields. */
+FL_Success FL_FindLocale(FL_Locale **locale, FL_Domain domain);
+/* This should be used to free the struct written by FL_FindLocale */
+void FL_FreeLocale(FL_Locale **locale);
+
+#endif /*__findlocale_h_*/
--- /dev/null
+// $Id$
+//
+// TinyGetText
+// Copyright (C) 2006 Ingo Ruhnke <grumbel@gmx.de>
+//
+// 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 <config.h>
+
+#include <sys/types.h>
+#include <fstream>
+#include <iostream>
+#include <algorithm>
+#include <ctype.h>
+#include <errno.h>
+
+#include "SDL.h"
+
+#include "tinygettext.hpp"
+#include "log.hpp"
+#include "physfs/physfs_stream.hpp"
+#include "log.hpp"
+#include "findlocale.hpp"
+
+//#define TRANSLATION_DEBUG
+
+namespace TinyGetText {
+
+/** Convert \a which is in \a from_charset to \a to_charset and return it */
+std::string convert(const std::string& text,
+ const std::string& from_charset,
+ const std::string& to_charset)
+{
+ if (from_charset == to_charset)
+ return text;
+
+ char *in = new char[text.length() + 1];
+ strcpy(in, text.c_str());
+ char *out = SDL_iconv_string(to_charset.c_str(), from_charset.c_str(), in, text.length() + 1);
+ delete[] in;
+ if(out == 0)
+ {
+ log_warning << "Error: conversion from " << from_charset << " to " << to_charset << " failed" << std::endl;
+ return "";
+ }
+ std::string ret(out);
+ SDL_free(out);
+ return ret;
+#if 0
+ iconv_t cd = SDL_iconv_open(to_charset.c_str(), from_charset.c_str());
+
+ size_t in_len = text.length();
+ size_t out_len = text.length()*3; // FIXME: cross fingers that this is enough
+
+ char* out_orig = new char[out_len];
+ char* in_orig = new char[in_len+1];
+ strcpy(in_orig, text.c_str());
+
+ char* out = out_orig;
+ ICONV_CONST char* in = in_orig;
+ size_t out_len_temp = out_len; // iconv is counting down the bytes it has
+ // written from this...
+
+ size_t retval = SDL_iconv(cd, &in, &in_len, &out, &out_len_temp);
+ out_len -= out_len_temp; // see above
+ if (retval == (size_t) -1)
+ {
+ log_warning << strerror(errno) << std::endl;
+ log_warning << "Error: conversion from " << from_charset << " to " << to_charset << " went wrong: " << retval << std::endl;
+ return "";
+ }
+ SDL_iconv_close(cd);
+
+ std::string ret(out_orig, out_len);
+ delete[] out_orig;
+ delete[] in_orig;
+ return ret;
+#endif
+}
+
+bool has_suffix(const std::string& lhs, const std::string rhs)
+{
+ if (lhs.length() < rhs.length())
+ return false;
+ else
+ return lhs.compare(lhs.length() - rhs.length(), rhs.length(), rhs) == 0;
+}
+
+bool has_prefix(const std::string& lhs, const std::string rhs)
+{
+ if (lhs.length() < rhs.length())
+ return false;
+ else
+ return lhs.compare(0, rhs.length(), rhs) == 0;
+}
+
+int plural1(int ) { return 0; }
+int plural2_1(int n) { return (n != 1); }
+int plural2_2(int n) { return (n > 1); }
+int plural3_lv(int n) { return (n%10==1 && n%100!=11 ? 0 : n != 0 ? 1 : 2); }
+int plural3_ga(int n) { return n==1 ? 0 : n==2 ? 1 : 2; }
+int plural3_lt(int n) { return (n%10==1 && n%100!=11 ? 0 : n%10>=2 && (n%100<10 || n%100>=20) ? 1 : 2); }
+int plural3_1(int n) { return (n%10==1 && n%100!=11 ? 0 : n%10>=2 && n%10<=4 && (n%100<10 || n%100>=20) ? 1 : 2); }
+int plural3_sk(int n) { return (n==1) ? 0 : (n>=2 && n<=4) ? 1 : 2; }
+int plural3_pl(int n) { return (n==1 ? 0 : n%10>=2 && n%10<=4 && (n%100<10 || n%100>=20) ? 1 : 2); }
+int plural3_sl(int n) { return (n%100==1 ? 0 : n%100==2 ? 1 : n%100==3 || n%100==4 ? 2 : 3); }
+
+/** Language Definitions */
+//*{
+LanguageDef lang_hu("hu", "Hungarian", 1, plural1); // "nplurals=1; plural=0;"
+LanguageDef lang_ja("ja", "Japanese", 1, plural1); // "nplurals=1; plural=0;"
+LanguageDef lang_ko("ko", "Korean", 1, plural1); // "nplurals=1; plural=0;"
+LanguageDef lang_tr("tr", "Turkish", 1, plural1); // "nplurals=1; plural=0;"
+LanguageDef lang_da("da", "Danish", 2, plural2_1); // "nplurals=2; plural=(n != 1);"
+LanguageDef lang_nl("nl", "Dutch", 2, plural2_1); // "nplurals=2; plural=(n != 1);"
+LanguageDef lang_en("en", "English", 2, plural2_1); // "nplurals=2; plural=(n != 1);"
+LanguageDef lang_fo("fo", "Faroese", 2, plural2_1); // "nplurals=2; plural=(n != 1);"
+LanguageDef lang_de("de", "German", 2, plural2_1); // "nplurals=2; plural=(n != 1);"
+LanguageDef lang_nb("nb", "Norwegian Bokmal", 2, plural2_1); // "nplurals=2; plural=(n != 1);"
+LanguageDef lang_no("no", "Norwegian", 2, plural2_1); // "nplurals=2; plural=(n != 1);"
+LanguageDef lang_nn("nn", "Norwegian Nynorsk", 2, plural2_1); // "nplurals=2; plural=(n != 1);"
+LanguageDef lang_sv("sv", "Swedish", 2, plural2_1); // "nplurals=2; plural=(n != 1);"
+LanguageDef lang_et("et", "Estonian", 2, plural2_1); // "nplurals=2; plural=(n != 1);"
+LanguageDef lang_fi("fi", "Finnish", 2, plural2_1); // "nplurals=2; plural=(n != 1);"
+LanguageDef lang_el("el", "Greek", 2, plural2_1); // "nplurals=2; plural=(n != 1);"
+LanguageDef lang_he("he", "Hebrew", 2, plural2_1); // "nplurals=2; plural=(n != 1);"
+LanguageDef lang_it("it", "Italian", 2, plural2_1); // "nplurals=2; plural=(n != 1);"
+LanguageDef lang_pt("pt", "Portuguese", 2, plural2_1); // "nplurals=2; plural=(n != 1);"
+LanguageDef lang_es("es", "Spanish", 2, plural2_1); // "nplurals=2; plural=(n != 1);"
+LanguageDef lang_eo("eo", "Esperanto", 2, plural2_1); // "nplurals=2; plural=(n != 1);"
+LanguageDef lang_fr("fr", "French", 2, plural2_2); // "nplurals=2; plural=(n > 1);"
+LanguageDef lang_pt_BR("pt_BR", "Brazilian", 2, plural2_2); // "nplurals=2; plural=(n > 1);"
+LanguageDef lang_lv("lv", "Latvian", 3, plural3_lv); // "nplurals=3; plural=(n%10==1 && n%100!=11 ? 0 : n != 0 ? 1 : 2);"
+LanguageDef lang_ga("ga", "Irish", 3, plural3_ga); // "nplurals=3; plural=n==1 ? 0 : n==2 ? 1 : 2;"
+LanguageDef lang_lt("lt", "Lithuanian", 3, plural3_lt); // "nplurals=3; plural=(n%10==1 && n%100!=11 ? 0 : n%10>=2 && (n%100<10 || n%100>=20) ? 1 : 2);"
+LanguageDef lang_hr("hr", "Croatian", 3, plural3_1); // "nplurals=3; plural=(n%10==1 && n%100!=11 ? 0 : n%10>=2 && n%10<=4 && (n%100<10 || n%100>=20) ? 1 : 2);"
+LanguageDef lang_cs("cs", "Czech", 3, plural3_1); // "nplurals=3; plural=(n%10==1 && n%100!=11 ? 0 : n%10>=2 && n%10<=4 && (n%100<10 || n%100>=20) ? 1 : 2);"
+LanguageDef lang_ru("ru", "Russian", 3, plural3_1); // "nplurals=3; plural=(n%10==1 && n%100!=11 ? 0 : n%10>=2 && n%10<=4 && (n%100<10 || n%100>=20) ? 1 : 2);"
+LanguageDef lang_uk("uk", "Ukrainian", 3, plural3_1); // "nplurals=3; plural=(n%10==1 && n%100!=11 ? 0 : n%10>=2 && n%10<=4 && (n%100<10 || n%100>=20) ? 1 : 2);"
+LanguageDef lang_sk("sk", "Slovak", 3, plural3_sk); // "nplurals=3; plural=(n==1) ? 0 : (n>=2 && n<=4) ? 1 : 2;"
+LanguageDef lang_pl("pl", "Polish", 3, plural3_pl); // "nplurals=3; plural=(n==1 ? 0 : n%10>=2 && n%10<=4 && (n%100<10 || n%100>=20) ? 1 : 2);
+LanguageDef lang_sl("sl", "Slovenian", 3, plural3_sl); // "nplurals=4; plural=(n%100==1 ? 0 : n%100==2 ? 1 : n%100==3 || n%100==4 ? 2 : 3);"
+//*}
+
+LanguageDef&
+get_language_def(const std::string& name)
+{
+ if (name == "hu") return lang_hu;
+ else if (name == "ja") return lang_ja;
+ else if (name == "ko") return lang_ko;
+ else if (name == "tr") return lang_tr;
+ else if (name == "da") return lang_da;
+ else if (name == "nl") return lang_nl;
+ else if (name == "en") return lang_en;
+ else if (name == "fo") return lang_fo;
+ else if (name == "de") return lang_de;
+ else if (name == "nb") return lang_nb;
+ else if (name == "no") return lang_no;
+ else if (name == "nn") return lang_nn;
+ else if (name == "sv") return lang_sv;
+ else if (name == "et") return lang_et;
+ else if (name == "fi") return lang_fi;
+ else if (name == "el") return lang_el;
+ else if (name == "he") return lang_he;
+ else if (name == "it") return lang_it;
+ else if (name == "pt") return lang_pt;
+ else if (name == "es") return lang_es;
+ else if (name == "eo") return lang_eo;
+ else if (name == "fr") return lang_fr;
+ else if (name == "pt_BR") return lang_pt_BR;
+ else if (name == "lv") return lang_lv;
+ else if (name == "ga") return lang_ga;
+ else if (name == "lt") return lang_lt;
+ else if (name == "hr") return lang_hr;
+ else if (name == "cs") return lang_cs;
+ else if (name == "ru") return lang_ru;
+ else if (name == "uk") return lang_uk;
+ else if (name == "sk") return lang_sk;
+ else if (name == "pl") return lang_pl;
+ else if (name == "sl") return lang_sl;
+ else return lang_en;
+}
+
+DictionaryManager::DictionaryManager()
+ : current_dict(&empty_dict)
+{
+ parseLocaleAliases();
+ // Environment variable SUPERTUX_LANG overrides language settings.
+ const char* lang = getenv( "SUPERTUX_LANG" );
+ if( lang ){
+ set_language( lang );
+ return;
+ }
+ // use findlocale to setup language
+ FL_Locale *locale;
+ FL_FindLocale( &locale, FL_MESSAGES );
+ if(locale->lang) {
+ if (locale->country) {
+ set_language( std::string(locale->lang)+"_"+std::string(locale->country) );
+ } else {
+ set_language( std::string(locale->lang) );
+ }
+ }
+ FL_FreeLocale( &locale );
+}
+
+void
+DictionaryManager::parseLocaleAliases()
+{
+ // try to parse language alias list
+ std::ifstream in("/usr/share/locale/locale.alias");
+
+ char c = ' ';
+ while(in.good() && !in.eof()) {
+ while(isspace(c) && !in.eof())
+ in.get(c);
+
+ if(c == '#') { // skip comments
+ while(c != '\n' && !in.eof())
+ in.get(c);
+ continue;
+ }
+
+ std::string alias;
+ while(!isspace(c) && !in.eof()) {
+ alias += c;
+ in.get(c);
+ }
+ while(isspace(c) && !in.eof())
+ in.get(c);
+ std::string language;
+ while(!isspace(c) && !in.eof()) {
+ language += c;
+ in.get(c);
+ }
+
+ if(in.eof())
+ break;
+ set_language_alias(alias, language);
+ }
+}
+
+Dictionary&
+DictionaryManager::get_dictionary(const std::string& spec)
+{
+
+ //log_debug << "Dictionary for language \"" << spec << "\" requested" << std::endl;
+
+ std::string lang = get_language_from_spec(spec);
+
+ //log_debug << "...normalized as \"" << lang << "\"" << std::endl;
+
+ Dictionaries::iterator i = dictionaries.find(get_language_from_spec(lang));
+ if (i != dictionaries.end())
+ {
+ return i->second;
+ }
+ else // Dictionary for languages lang isn't loaded, so we load it
+ {
+ //log_debug << "get_dictionary: " << lang << std::endl;
+ Dictionary& dict = dictionaries[lang];
+
+ dict.set_language(get_language_def(lang));
+ if(charset != "")
+ dict.set_charset(charset);
+
+ for (SearchPath::iterator p = search_path.begin(); p != search_path.end(); ++p)
+ {
+ char** files = PHYSFS_enumerateFiles(p->c_str());
+ if(!files)
+ {
+ log_warning << "Error: enumerateFiles() failed on " << *p << std::endl;
+ }
+ else
+ {
+ for(const char* const* filename = files;
+ *filename != 0; filename++) {
+
+ // check if filename matches requested language
+ std::string fname = std::string(*filename);
+ std::string load_from_file = "";
+ if(fname == lang + ".po") {
+ load_from_file = fname;
+ } else {
+ std::string::size_type s = lang.find("_");
+ if(s != std::string::npos) {
+ std::string lang_short = std::string(lang, 0, s);
+ if (fname == lang_short + ".po") {
+ load_from_file = lang_short;
+ }
+ }
+ }
+
+ // if it matched, load dictionary
+ if (load_from_file != "") {
+ //log_debug << "Loading dictionary for language \"" << lang << "\" from \"" << filename << "\"" << std::endl;
+ std::string pofile = *p + "/" + *filename;
+ try {
+ IFileStream in(pofile);
+ read_po_file(dict, in);
+ } catch(std::exception& e) {
+ log_warning << "Error: Failure file opening: " << pofile << std::endl;
+ log_warning << e.what() << "" << std::endl;
+ }
+ }
+
+ }
+ PHYSFS_freeList(files);
+ }
+ }
+
+ return dict;
+ }
+}
+
+std::set<std::string>
+DictionaryManager::get_languages()
+{
+ std::set<std::string> languages;
+
+ for (SearchPath::iterator p = search_path.begin(); p != search_path.end(); ++p)
+ {
+ char** files = PHYSFS_enumerateFiles(p->c_str());
+ if (!files)
+ {
+ log_warning << "Error: opendir() failed on " << *p << std::endl;
+ }
+ else
+ {
+ for(const char* const* file = files; *file != 0; file++) {
+ if(has_suffix(*file, ".po")) {
+ std::string filename = *file;
+ languages.insert(filename.substr(0, filename.length()-3));
+ }
+ }
+ PHYSFS_freeList(files);
+ }
+ }
+ return languages;
+}
+
+void
+DictionaryManager::set_language(const std::string& lang)
+{
+ //log_debug << "set_language \"" << lang << "\"" << std::endl;
+ language = get_language_from_spec(lang);
+ //log_debug << "==> \"" << language << "\"" << std::endl;
+ current_dict = & (get_dictionary(language));
+}
+
+const std::string&
+DictionaryManager::get_language() const
+{
+ return language;
+}
+
+void
+DictionaryManager::set_charset(const std::string& charset)
+{
+ dictionaries.clear(); // changing charset invalidates cache
+ this->charset = charset;
+ set_language(language);
+}
+
+void
+DictionaryManager::set_language_alias(const std::string& alias,
+ const std::string& language)
+{
+ language_aliases.insert(std::make_pair(alias, language));
+}
+
+std::string
+DictionaryManager::get_language_from_spec(const std::string& spec)
+{
+ std::string lang = spec;
+ Aliases::iterator i = language_aliases.find(lang);
+ if(i != language_aliases.end()) {
+ lang = i->second;
+ }
+
+ std::string::size_type s = lang.find(".");
+ if(s != std::string::npos) {
+ lang = std::string(lang, 0, s);
+ }
+
+ s = lang.find("_");
+ if(s == std::string::npos) {
+ std::string lang_big = lang;
+ std::transform (lang_big.begin(), lang_big.end(), lang_big.begin(), toupper);
+ lang += "_" + lang_big;
+ }
+
+ return lang;
+
+}
+
+void
+DictionaryManager::add_directory(const std::string& pathname)
+{
+ dictionaries.clear(); // adding directories invalidates cache
+ search_path.push_back(pathname);
+ set_language(language);
+}
+
+//---------------------------------------------------------------------------
+
+Dictionary::Dictionary(const LanguageDef& language_, const std::string& charset_)
+ : language(language_), charset(charset_)
+{
+}
+
+Dictionary::Dictionary()
+ : language(lang_en)
+{
+}
+
+std::string
+Dictionary::get_charset() const
+{
+ return charset;
+}
+
+void
+Dictionary::set_charset(const std::string& charset_)
+{
+ charset = charset_;
+}
+
+void
+Dictionary::set_language(const LanguageDef& lang)
+{
+ language = lang;
+}
+
+std::string
+Dictionary::translate(const std::string& msgid, const std::string& msgid2, int num)
+{
+ PluralEntries::iterator i = plural_entries.find(msgid);
+ std::map<int, std::string>& msgstrs = i->second;
+
+ if (i != plural_entries.end() && !msgstrs.empty())
+ {
+ int g = language.plural(num);
+ std::map<int, std::string>::iterator j = msgstrs.find(g);
+ if (j != msgstrs.end())
+ {
+ return j->second;
+ }
+ else
+ {
+ // Return the first translation, in case we can't translate the specific number
+ return msgstrs.begin()->second;
+ }
+ }
+ else
+ {
+#ifdef TRANSLATION_DEBUG
+ log_warning << "Couldn't translate: " << msgid << std::endl;
+ log_warning << "Candidates: " << std::endl;
+ for (PluralEntries::iterator i = plural_entries.begin(); i != plural_entries.end(); ++i)
+ log_debug << "'" << i->first << "'" << std::endl;
+#endif
+
+ if (plural2_1(num)) // default to english rules
+ return msgid2;
+ else
+ return msgid;
+ }
+}
+
+const char*
+Dictionary::translate(const char* msgid)
+{
+ Entries::iterator i = entries.find(msgid);
+ if (i != entries.end() && !i->second.empty())
+ {
+ return i->second.c_str();
+ }
+ else
+ {
+#ifdef TRANSLATION_DBEUG
+ log_warning << "Couldn't translate: " << msgid << std::endl;
+#endif
+ return msgid;
+ }
+}
+
+std::string
+Dictionary::translate(const std::string& msgid)
+{
+ Entries::iterator i = entries.find(msgid);
+ if (i != entries.end() && !i->second.empty())
+ {
+ return i->second;
+ }
+ else
+ {
+#ifdef TRANSLATION_DBEUG
+ log_warning << "Couldn't translate: " << msgid << std::endl;
+#endif
+ return msgid;
+ }
+}
+
+void
+Dictionary::add_translation(const std::string& msgid, const std::string& ,
+ const std::map<int, std::string>& msgstrs)
+{
+ // Do we need msgid2 for anything? its after all supplied to the
+ // translate call, so we just throw it away
+ plural_entries[msgid] = msgstrs;
+}
+
+void
+Dictionary::add_translation(const std::string& msgid, const std::string& msgstr)
+{
+ entries[msgid] = msgstr;
+}
+
+class POFileReader
+{
+private:
+ struct Token
+ {
+ std::string keyword;
+ std::string content;
+ };
+
+ Dictionary& dict;
+
+ std::string from_charset;
+ std::string to_charset;
+
+ std::string current_msgid;
+ std::string current_msgid_plural;
+ std::map<int, std::string> msgstr_plural;
+
+ int line_num;
+
+ enum { WANT_MSGID, WANT_MSGSTR, WANT_MSGSTR_PLURAL, WANT_MSGID_PLURAL } state;
+
+public:
+ POFileReader(std::istream& in, Dictionary& dict_)
+ : dict(dict_)
+ {
+ state = WANT_MSGID;
+ line_num = 0;
+ char c = in.get();
+ if(c == (char) 0xef) { // skip UTF-8 intro that some texteditors produce
+ in.get();
+ in.get();
+ } else {
+ in.unget();
+ }
+ tokenize_po(in);
+ }
+
+ void parse_header(const std::string& header)
+ {
+ // Seperate the header in lines
+ typedef std::vector<std::string> Lines;
+ Lines lines;
+
+ std::string::size_type start = 0;
+ for(std::string::size_type i = 0; i < header.length(); ++i)
+ {
+ if (header[i] == '\n')
+ {
+ lines.push_back(header.substr(start, i - start));
+ start = i+1;
+ }
+ }
+
+ for(Lines::iterator i = lines.begin(); i != lines.end(); ++i)
+ {
+ if (has_prefix(*i, "Content-Type: text/plain; charset=")) {
+ from_charset = i->substr(strlen("Content-Type: text/plain; charset="));
+ }
+ }
+
+ if (from_charset.empty() || from_charset == "CHARSET")
+ {
+ log_warning << "Error: Charset not specified for .po, fallback to ISO-8859-1" << std::endl;
+ from_charset = "ISO-8859-1";
+ }
+
+ to_charset = dict.get_charset();
+ if (to_charset.empty())
+ { // No charset requested from the dict, use utf-8
+ to_charset = "utf-8";
+ dict.set_charset(from_charset);
+ }
+ }
+
+ void add_token(const Token& token)
+ {
+ switch(state)
+ {
+ case WANT_MSGID:
+ if (token.keyword == "msgid")
+ {
+ current_msgid = token.content;
+ state = WANT_MSGID_PLURAL;
+ }
+ else if (token.keyword.empty())
+ {
+ //log_warning << "Got EOF, everything looks ok." << std::endl;
+ }
+ else
+ {
+ log_warning << "tinygettext: expected 'msgid' keyword, got " << token.keyword << " at line " << line_num << std::endl;
+ }
+ break;
+
+ case WANT_MSGID_PLURAL:
+ if (token.keyword == "msgid_plural")
+ {
+ current_msgid_plural = token.content;
+ state = WANT_MSGSTR_PLURAL;
+ }
+ else
+ {
+ state = WANT_MSGSTR;
+ add_token(token);
+ }
+ break;
+
+ case WANT_MSGSTR:
+ if (token.keyword == "msgstr")
+ {
+ if (current_msgid == "")
+ { // .po Header is hidden in the msgid with the empty string
+ parse_header(token.content);
+ }
+ else
+ {
+ dict.add_translation(current_msgid, convert(token.content, from_charset, to_charset));
+ }
+ state = WANT_MSGID;
+ }
+ else
+ {
+ log_warning << "tinygettext: expected 'msgstr' keyword, got " << token.keyword << " at line " << line_num << std::endl;
+ }
+ break;
+
+ case WANT_MSGSTR_PLURAL:
+ if (has_prefix(token.keyword, "msgstr["))
+ {
+ int num;
+ if (sscanf(token.keyword.c_str(), "msgstr[%d]", &num) != 1)
+ {
+ log_warning << "Error: Couldn't parse: " << token.keyword << std::endl;
+ }
+ else
+ {
+ msgstr_plural[num] = convert(token.content, from_charset, to_charset);
+ }
+ }
+ else
+ {
+ dict.add_translation(current_msgid, current_msgid_plural, msgstr_plural);
+
+ state = WANT_MSGID;
+ add_token(token);
+ }
+ break;
+ }
+ }
+
+ inline int getchar(std::istream& in)
+ {
+ int c = in.get();
+ if (c == '\n')
+ line_num += 1;
+ return c;
+ }
+
+ void tokenize_po(std::istream& in)
+ {
+ enum State { READ_KEYWORD,
+ READ_CONTENT,
+ READ_CONTENT_IN_STRING,
+ SKIP_COMMENT };
+
+ State state = READ_KEYWORD;
+ int c;
+ Token token;
+
+ while((c = getchar(in)) != EOF)
+ {
+ //log_debug << "Lexing char: " << char(c) << " " << state << std::endl;
+ switch(state)
+ {
+ case READ_KEYWORD:
+ if (c == '#')
+ {
+ state = SKIP_COMMENT;
+ }
+ else
+ {
+ // Read a new token
+ token = Token();
+
+ do { // Read keyword
+ token.keyword += c;
+ } while((c = getchar(in)) != EOF && !isspace(c));
+ in.unget();
+
+ state = READ_CONTENT;
+ }
+ break;
+
+ case READ_CONTENT:
+ while((c = getchar(in)) != EOF)
+ {
+ if (c == '"') {
+ // Found start of content
+ state = READ_CONTENT_IN_STRING;
+ break;
+ } else if (isspace(c)) {
+ // skip
+ } else { // Read something that may be a keyword
+ in.unget();
+ state = READ_KEYWORD;
+ add_token(token);
+ break;
+ }
+ }
+ break;
+
+ case READ_CONTENT_IN_STRING:
+ if (c == '\\') {
+ c = getchar(in);
+ if (c != EOF)
+ {
+ if (c == 'n') token.content += '\n';
+ else if (c == 't') token.content += '\t';
+ else if (c == 'r') token.content += '\r';
+ else if (c == '"') token.content += '"';
+ else if (c == '\\') token.content += '\\';
+ else
+ {
+ log_warning << "Unhandled escape character: " << char(c) << std::endl;
+ }
+ }
+ else
+ {
+ log_warning << "Unterminated string" << std::endl;
+ }
+ } else if (c == '"') { // Content string is terminated
+ state = READ_CONTENT;
+ } else {
+ token.content += c;
+ }
+ break;
+
+ case SKIP_COMMENT:
+ if (c == '\n')
+ state = READ_KEYWORD;
+ break;
+ }
+ }
+ add_token(token);
+ }
+};
+
+void read_po_file(Dictionary& dict_, std::istream& in)
+{
+ POFileReader reader(in, dict_);
+}
+
+} // namespace TinyGetText
+
+/* EOF */
--- /dev/null
+// $Id$
+//
+// SuperTux
+// Copyright (C) 2006 Matthias Braun <matze@braunis.de>
+//
+// 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 HEADER_TINYGETTEXT_H
+#define HEADER_TINYGETTEXT_H
+
+#include <map>
+#include <vector>
+#include <set>
+#include <string>
+
+namespace TinyGetText {
+
+typedef int (*PluralFunc)(int n);
+
+struct LanguageDef {
+ const char* code;
+ const char* name;
+ int nplural;
+ PluralFunc plural;
+
+ LanguageDef(const char* code_, const char* name_, int nplural_, PluralFunc plural_)
+ : code(code_), name(name_), nplural(nplural_), plural(plural_)
+ {}
+};
+
+/** A simple dictionary class that mimics gettext() behaviour. Each
+ Dictionary only works for a single language, for managing multiple
+ languages and .po files at once use the DictionaryManager. */
+class Dictionary
+{
+private:
+ typedef std::map<std::string, std::string> Entries;
+ Entries entries;
+
+ typedef std::map<std::string, std::map<int, std::string> > PluralEntries;
+ PluralEntries plural_entries;
+
+ LanguageDef language;
+ std::string charset;
+public:
+ /** */
+ Dictionary(const LanguageDef& language_, const std::string& charset = "");
+
+ Dictionary();
+
+ /** Return the charset used for this dictionary */
+ std::string get_charset() const;
+
+ /** Set a charset for this dictionary, this will NOT convert stuff,
+ it is for information only, you have to convert stuff yourself
+ when you add it with \a add_translation() */
+ void set_charset(const std::string& charset);
+
+ /** Set the language that is used for this dictionary, this is
+ mainly needed to evaluate plural forms */
+ void set_language(const LanguageDef& lang);
+
+ /** Translate the string \a msgid to its correct plural form, based
+ on the number of items given by \a num. \a msgid2 is \a msgid in
+ plural form. */
+ std::string translate(const std::string& msgid, const std::string& msgid2, int num);
+
+ /** Translate the string \a msgid. */
+ std::string translate(const std::string& msgid);
+ /** Translate the string \a msgid. */
+ const char* translate(const char* msgid);
+
+ /** Add a translation from \a msgid to \a msgstr to the dictionary,
+ where \a msgid is the singular form of the message, msgid2 the
+ plural form and msgstrs a table of translations. The right
+ translation will be calculated based on the \a num argument to
+ translate(). */
+ void add_translation(const std::string& msgid, const std::string& msgid2,
+ const std::map<int, std::string>& msgstrs);
+
+ /** Add a translation from \a msgid to \a msgstr to the
+ dictionary */
+ void add_translation(const std::string& msgid, const std::string& msgstr);
+};
+
+/** Manager class for dictionaries, you give it a bunch of directories
+ with .po files and it will then automatically load the right file
+ on demand depending on which language was set. */
+class DictionaryManager
+{
+private:
+ typedef std::map<std::string, Dictionary> Dictionaries;
+ Dictionaries dictionaries;
+ typedef std::vector<std::string> SearchPath;
+ SearchPath search_path;
+ typedef std::map<std::string, std::string> Aliases;
+ Aliases language_aliases;
+ std::string charset;
+ std::string language;
+ Dictionary* current_dict;
+ Dictionary empty_dict;
+
+public:
+ DictionaryManager();
+
+ /** Return the currently active dictionary, if none is set, an empty
+ dictionary is returned. */
+ Dictionary& get_dictionary()
+ { return *current_dict; }
+
+ /** Get dictionary for lang */
+ Dictionary& get_dictionary(const std::string& langspec);
+
+ /** Set a language based on a four? letter country code */
+ void set_language(const std::string& langspec);
+
+ /** returns the (normalized) country code of the currently used language */
+ const std::string& get_language() const;
+
+ /** Set a charset that will be set on the returned dictionaries */
+ void set_charset(const std::string& charset);
+
+ /** Define an alias for a language */
+ void set_language_alias(const std::string& alias, const std::string& lang);
+
+ /** Add a directory to the search path for dictionaries */
+ void add_directory(const std::string& pathname);
+
+ /** Return a set of the available languages in their country code */
+ std::set<std::string> get_languages();
+
+private:
+ void parseLocaleAliases();
+ /// returns the language part in a language spec (like de_DE.UTF-8 -> de)
+ std::string get_language_from_spec(const std::string& spec);
+};
+
+/** Read the content of the .po file given as \a in into the
+ dictionary given as \a dict */
+void read_po_file(Dictionary& dict, std::istream& in);
+LanguageDef& get_language_def(const std::string& name);
+
+} // namespace TinyGetText
+
+#endif
+
+/* EOF */
--- /dev/null
+// $Id$
+//
+// SuperTux
+// Copyright (C) 2004 Tobias Glaesser <tobi.web@gmx.de>
+// Copyright (C) 2006 Matthias Braun <matze@braunis.de>
+//
+// 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 <config.h>
+
+#include <iostream>
+#include <sstream>
+#include <stdexcept>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <math.h>
+#include <errno.h>
+#include <unistd.h>
+#include <SDL.h>
+#include <SDL_image.h>
+#include <physfs.h>
+
+#include "title.hpp"
+#include "mainloop.hpp"
+#include "video/drawing_context.hpp"
+#include "video/surface.hpp"
+#include "audio/sound_manager.hpp"
+#include "gui/menu.hpp"
+#include "timer.hpp"
+#include "lisp/lisp.hpp"
+#include "lisp/parser.hpp"
+#include "level.hpp"
+#include "world.hpp"
+#include "game_session.hpp"
+#include "worldmap/worldmap.hpp"
+#include "player_status.hpp"
+#include "tile.hpp"
+#include "sector.hpp"
+#include "object/tilemap.hpp"
+#include "object/camera.hpp"
+#include "object/player.hpp"
+#include "resources.hpp"
+#include "gettext.hpp"
+#include "textscroller.hpp"
+#include "fadeout.hpp"
+#include "file_system.hpp"
+#include "control/joystickkeyboardcontroller.hpp"
+#include "control/codecontroller.hpp"
+#include "main.hpp"
+#include "log.hpp"
+#include "options_menu.hpp"
+#include "console.hpp"
+#include "random_generator.hpp"
+#include "addon_manager.hpp"
+
+enum MainMenuIDs {
+ MNID_STARTGAME,
+ MNID_LEVELS_CONTRIB,
+ MNID_ADDONS,
+ MNID_OPTIONMENU,
+ MNID_LEVELEDITOR,
+ MNID_CREDITS,
+ MNID_QUITMAINMENU
+};
+
+void
+TitleScreen::update_load_game_menu()
+{
+ load_game_menu.reset(new Menu());
+
+ load_game_menu->add_label(_("Start Game"));
+ load_game_menu->add_hl();
+ for(int i = 1; i <= 5; ++i) {
+ load_game_menu->add_entry(i, get_slotinfo(i));
+ }
+ load_game_menu->add_hl();
+ load_game_menu->add_back(_("Back"));
+}
+
+void
+TitleScreen::free_contrib_menu()
+{
+ for(std::vector<World*>::iterator i = contrib_worlds.begin();
+ i != contrib_worlds.end(); ++i)
+ delete *i;
+
+ contrib_worlds.clear();
+}
+
+void
+TitleScreen::generate_contrib_menu()
+{
+ /** Generating contrib levels list by making use of Level Subset */
+ std::vector<std::string> level_worlds;
+ char** files = PHYSFS_enumerateFiles("levels/");
+ for(const char* const* filename = files; *filename != 0; ++filename) {
+ std::string filepath = std::string("levels/") + *filename;
+ if(PHYSFS_isDirectory(filepath.c_str()))
+ level_worlds.push_back(filepath);
+ }
+ PHYSFS_freeList(files);
+
+ free_contrib_menu();
+ contrib_menu.reset(new Menu());
+
+ contrib_menu->add_label(_("Contrib Levels"));
+ contrib_menu->add_hl();
+
+ int i = 0;
+ for (std::vector<std::string>::iterator it = level_worlds.begin();
+ it != level_worlds.end(); ++it) {
+ try {
+ std::auto_ptr<World> world (new World());
+ world->load(*it + "/info");
+ if(world->hide_from_contribs) {
+ continue;
+ }
+ contrib_menu->add_entry(i++, world->title);
+ contrib_worlds.push_back(world.release());
+ } catch(std::exception& e) {
+#ifdef DEBUG
+ log_warning << "Couldn't parse levelset info for '" << *it << "': " << e.what() << std::endl;
+#endif
+ }
+ }
+
+ contrib_menu->add_hl();
+ contrib_menu->add_back(_("Back"));
+}
+
+std::string
+TitleScreen::get_level_name(const std::string& filename)
+{
+ try {
+ lisp::Parser parser;
+ const lisp::Lisp* root = parser.parse(filename);
+
+ const lisp::Lisp* level = root->get_lisp("supertux-level");
+ if(!level)
+ return "";
+
+ std::string name;
+ level->get("name", name);
+ return name;
+ } catch(std::exception& e) {
+ log_warning << "Problem getting name of '" << filename << "': "
+ << e.what() << std::endl;
+ return "";
+ }
+}
+
+void
+TitleScreen::check_levels_contrib_menu()
+{
+ int index = contrib_menu->check();
+ if (index == -1)
+ return;
+
+ current_world = contrib_worlds[index];
+
+ if(!current_world->is_levelset) {
+ update_load_game_menu();
+ Menu::push_current(load_game_menu.get());
+ } else {
+ contrib_world_menu.reset(new Menu());
+
+ contrib_world_menu->add_label(current_world->title);
+ contrib_world_menu->add_hl();
+
+ for (unsigned int i = 0; i < current_world->get_num_levels(); ++i)
+ {
+ /** get level's title */
+ std::string filename = current_world->get_level_filename(i);
+ std::string title = get_level_name(filename);
+ contrib_world_menu->add_entry(i, title);
+ }
+
+ contrib_world_menu->add_hl();
+ contrib_world_menu->add_back(_("Back"));
+
+ Menu::push_current(contrib_world_menu.get());
+ }
+}
+
+void
+TitleScreen::check_contrib_world_menu()
+{
+ int index = contrib_world_menu->check();
+ if (index != -1) {
+ if (contrib_world_menu->get_item_by_id(index).kind == MN_ACTION) {
+ sound_manager->stop_music();
+ GameSession* session =
+ new GameSession(current_world->get_level_filename(index));
+ main_loop->push_screen(session);
+ }
+ }
+}
+
+namespace {
+ bool generate_addons_menu_sorter(const Addon& a1, const Addon& a2)
+ {
+ return a1.title < a2.title;
+ }
+
+ const int ADDON_LIST_START_ID = 10;
+}
+
+void
+TitleScreen::generate_addons_menu()
+{
+ AddonManager& adm = AddonManager::get_instance();
+
+ // refresh list of installed addons
+ installed_addons = adm.get_installed_addons();
+
+ // build new Add-on list
+ addons.clear();
+
+ // add installed addons to list
+ addons.insert(addons.end(), installed_addons.begin(), installed_addons.end());
+
+ // add available addons to list
+ addons.insert(addons.end(), available_addons.begin(), available_addons.end());
+
+ // sort list
+ std::sort(addons.begin(), addons.end(), generate_addons_menu_sorter);
+
+ // remove available addons that are already installed
+ std::vector<Addon>::iterator it2 = addons.begin();
+ while (it2 != addons.end()) {
+ Addon addon = *it2;
+ if (addon.isInstalled) {
+ bool restart = false;
+ for (std::vector<Addon>::iterator it = addons.begin(); it != addons.end(); ++it) {
+ Addon addon2 = *it;
+ if ((addon2.equals(addon)) && (!addon2.isInstalled)) {
+ addons.erase(it);
+ restart = true;
+ break;
+ }
+ }
+ if (restart) {
+ it2 = addons.begin();
+ continue;
+ }
+ }
+ it2++;
+ }
+
+ // (re)generate menu
+ free_addons_menu();
+ addons_menu.reset(new Menu());
+
+ addons_menu->add_label(_("Add-ons"));
+ addons_menu->add_hl();
+
+#ifdef HAVE_LIBCURL
+ addons_menu->add_entry(0, std::string(_("Check Online")));
+#else
+ addons_menu->add_deactive(0, std::string(_("Check Online (disabled)")));
+#endif
+
+ //addons_menu->add_hl();
+
+ for (unsigned int i = 0; i < addons.size(); i++) {
+ Addon addon = addons[i];
+ std::string text = "";
+ if (addon.kind != "") text += addon.kind + " ";
+ text += std::string("\"") + addon.title + "\"";
+ if (addon.author != "") text += " by \"" + addon.author + "\"";
+ addons_menu->add_toggle(ADDON_LIST_START_ID + i, text, addon.isInstalled);
+ }
+
+ addons_menu->add_hl();
+ addons_menu->add_back(_("Back"));
+}
+
+void
+TitleScreen::check_addons_menu()
+{
+ int index = addons_menu->check();
+ if (index == -1) return;
+
+ // check if "Check Online" was chosen
+ if (index == 0) {
+ try {
+ available_addons = AddonManager::get_instance().get_available_addons();
+ generate_addons_menu();
+ Menu::set_current(addons_menu.get());
+ addons_menu->set_active_item(index);
+ }
+ catch (std::runtime_error e) {
+ log_warning << "Check for available Add-ons failed: " << e.what() << std::endl;
+ }
+ return;
+ }
+
+ // if one of the Addons listed was chosen, take appropriate action
+ if ((index >= ADDON_LIST_START_ID) && (index < ADDON_LIST_START_ID) + addons.size()) {
+ Addon addon = addons[index - ADDON_LIST_START_ID];
+ if (!addon.isInstalled) {
+ try {
+ addon.install();
+ //generate_addons_menu();
+ //Menu::set_current(addons_menu.get());
+ //addons_menu->set_active_item(index);
+ Menu::set_current(0);
+ }
+ catch (std::runtime_error e) {
+ log_warning << "Installation of Add-on failed: " << e.what() << std::endl;
+ }
+ } else {
+ try {
+ addon.remove();
+ //generate_addons_menu();
+ //Menu::set_current(addons_menu.get());
+ //addons_menu->set_active_item(index);
+ Menu::set_current(0);
+ }
+ catch (std::runtime_error e) {
+ log_warning << "Removal of Add-on failed: " << e.what() << std::endl;
+ }
+ }
+ }
+
+}
+
+void
+TitleScreen::free_addons_menu()
+{
+}
+
+void
+TitleScreen::make_tux_jump()
+{
+ static bool jumpWasReleased = true;
+ Sector* sector = titlesession->get_current_sector();
+ Player* tux = sector->player;
+
+ controller->update();
+ controller->press(Controller::RIGHT);
+
+ // Check if we should press the jump button
+ Rect lookahead = tux->get_bbox();
+ lookahead.p2.x += 96;
+ bool pathBlocked = !sector->is_free_of_statics(lookahead);
+ if ((pathBlocked && jumpWasReleased) || !tux->on_ground()) {
+ controller->press(Controller::JUMP);
+ jumpWasReleased = false;
+ } else {
+ jumpWasReleased = true;
+ }
+
+ // Wrap around at the end of the level back to the beginnig
+ if(sector->get_width() - 320 < tux->get_pos().x) {
+ sector->activate("main");
+ sector->camera->reset(tux->get_pos());
+ }
+}
+
+TitleScreen::TitleScreen()
+{
+ controller.reset(new CodeController());
+ titlesession.reset(new GameSession("levels/misc/menu.stl"));
+
+ Player* player = titlesession->get_current_sector()->player;
+ player->set_controller(controller.get());
+ player->set_speedlimit(230); //MAX_WALK_XM
+
+ generate_main_menu();
+}
+
+void
+TitleScreen::generate_main_menu()
+{
+ main_menu.reset(new Menu());
+ main_menu->set_pos(SCREEN_WIDTH/2, SCREEN_HEIGHT/2 + 35);
+ main_menu->add_entry(MNID_STARTGAME, _("Start Game"));
+ main_menu->add_entry(MNID_LEVELS_CONTRIB, _("Contrib Levels"));
+ main_menu->add_entry(MNID_ADDONS, _("Add-ons"));
+ main_menu->add_submenu(_("Options"), get_options_menu());
+ main_menu->add_entry(MNID_CREDITS, _("Credits"));
+ main_menu->add_entry(MNID_QUITMAINMENU, _("Quit"));
+}
+
+TitleScreen::~TitleScreen()
+{
+}
+
+void
+TitleScreen::setup()
+{
+ player_status->reset();
+
+ Sector* sector = titlesession->get_current_sector();
+ if(Sector::current() != sector) {
+ sector->play_music(LEVEL_MUSIC);
+ sector->activate(sector->player->get_pos());
+ }
+
+ Menu::set_current(main_menu.get());
+}
+
+void
+TitleScreen::leave()
+{
+ Sector* sector = titlesession->get_current_sector();
+ sector->deactivate();
+ Menu::set_current(NULL);
+}
+
+void
+TitleScreen::draw(DrawingContext& context)
+{
+ Sector* sector = titlesession->get_current_sector();
+ sector->draw(context);
+
+ context.draw_text(white_small_text, "SuperTux " PACKAGE_VERSION "\n",
+ Vector(5, SCREEN_HEIGHT - 50), ALIGN_LEFT, LAYER_FOREGROUND1);
+ context.draw_text(white_small_text,
+ _(
+"Copyright (c) 2007 SuperTux Devel Team\n"
+"This game comes with ABSOLUTELY NO WARRANTY. This is free software, and you are welcome to\n"
+"redistribute it under certain conditions; see the file COPYING for details.\n"
+),
+ Vector(5, SCREEN_HEIGHT - 50 + white_small_text->get_height() + 5),
+ ALIGN_LEFT, LAYER_FOREGROUND1);
+}
+
+void
+TitleScreen::update(float elapsed_time)
+{
+ main_loop->set_speed(0.6f);
+ Sector* sector = titlesession->get_current_sector();
+ sector->update(elapsed_time);
+
+ make_tux_jump();
+
+ Menu* menu = Menu::current();
+ if(menu) {
+ menu->update();
+
+ if(menu == main_menu.get()) {
+ switch (main_menu->check()) {
+ case MNID_STARTGAME:
+ // Start Game, ie. goto the slots menu
+ if(main_world.get() == NULL) {
+ main_world.reset(new World());
+ main_world->load("levels/world1/info");
+ }
+ current_world = main_world.get();
+ update_load_game_menu();
+ Menu::push_current(load_game_menu.get());
+ break;
+ case MNID_LEVELS_CONTRIB:
+ // Contrib Menu
+ generate_contrib_menu();
+ Menu::push_current(contrib_menu.get());
+ break;
+ case MNID_ADDONS:
+ // Add-ons Menu
+ generate_addons_menu();
+ Menu::push_current(addons_menu.get());
+ break;
+ case MNID_CREDITS:
+ main_loop->push_screen(new TextScroller("credits.txt"),
+ new FadeOut(0.5));
+ break;
+ case MNID_QUITMAINMENU:
+ main_loop->quit(new FadeOut(0.25));
+ sound_manager->stop_music(0.25);
+ break;
+ }
+ } else if(menu == load_game_menu.get()) {
+ /*
+ if(event.key.keysym.sym == SDLK_DELETE) {
+ int slot = menu->get_active_item_id();
+ std::stringstream stream;
+ stream << slot;
+ std::string str = _("Are you sure you want to delete slot") + stream.str() + "?";
+
+ if(confirm_dialog(bkg_title, str.c_str())) {
+ str = "save/slot" + stream.str() + ".stsg";
+ log_debug << "Removing: " << str << std::endl;
+ PHYSFS_delete(str.c_str());
+ }
+
+ update_load_save_game_menu(load_game_menu);
+ Menu::set_current(main_menu.get());
+ }*/
+ process_load_game_menu();
+ } else if(menu == contrib_menu.get()) {
+ check_levels_contrib_menu();
+ } else if(menu == addons_menu.get()) {
+ check_addons_menu();
+ } else if (menu == contrib_world_menu.get()) {
+ check_contrib_world_menu();
+ }
+ }
+
+ // reopen menu of user closed it (so that the app doesn't close when user
+ // accidently hit ESC)
+ if(Menu::current() == 0) {
+ generate_main_menu();
+ Menu::set_current(main_menu.get());
+ }
+}
+
+std::string
+TitleScreen::get_slotinfo(int slot)
+{
+ std::string tmp;
+ std::string title;
+
+ std::string basename = current_world->get_basedir();
+ basename = basename.substr(0, basename.length()-1);
+ std::string worlddirname = FileSystem::basename(basename);
+ std::ostringstream stream;
+ stream << "save/" << worlddirname << "_" << slot << ".stsg";
+ std::string slotfile = stream.str();
+
+ try {
+ lisp::Parser parser;
+ const lisp::Lisp* root = parser.parse(slotfile);
+
+ const lisp::Lisp* savegame = root->get_lisp("supertux-savegame");
+ if(!savegame)
+ throw std::runtime_error("file is not a supertux-savegame.");
+
+ savegame->get("title", title);
+ } catch(std::exception& ) {
+ std::ostringstream slottitle;
+ slottitle << _("Slot") << " " << slot << " - " << _("Free");
+ return slottitle.str();
+ }
+
+ std::ostringstream slottitle;
+ slottitle << _("Slot") << " " << slot << " - " << title;
+ return slottitle.str();
+}
+
+bool
+TitleScreen::process_load_game_menu()
+{
+ int slot = load_game_menu->check();
+
+ if(slot == -1)
+ return false;
+
+ if(load_game_menu->get_item_by_id(slot).kind != MN_ACTION)
+ return false;
+
+ std::string basename = current_world->get_basedir();
+ basename = basename.substr(0, basename.length()-1);
+ std::string worlddirname = FileSystem::basename(basename);
+ std::stringstream stream;
+ stream << "save/" << worlddirname << "_" << slot << ".stsg";
+ std::string slotfile = stream.str();
+
+ try {
+ current_world->set_savegame_filename(slotfile);
+ current_world->run();
+ } catch(std::exception& e) {
+ log_fatal << "Couldn't start world: " << e.what() << std::endl;
+ }
+
+ return true;
+}
--- /dev/null
+// $Id$
+//
+// SuperTux
+// Copyright (C) 2004 Tobias Glaesser <tobi.web@gmx.de>
+// Copyright (C) 2006 Matthias Braun <matze@braunis.de>
+//
+// 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_TITLE_H
+#define SUPERTUX_TITLE_H
+
+#include <memory>
+#include <vector>
+#include "screen.hpp"
+#include "game_session.hpp"
+#include "addon.hpp"
+
+class Menu;
+class World;
+class CodeController;
+
+class TitleScreen : public Screen
+{
+public:
+ TitleScreen();
+ virtual ~TitleScreen();
+
+ virtual void setup();
+ virtual void leave();
+
+ virtual void draw(DrawingContext& context);
+
+ virtual void update(float elapsed_time);
+
+private:
+ std::string get_slotinfo(int slot);
+ std::string get_level_name(const std::string& levelfile);
+ bool process_load_game_menu();
+ void make_tux_jump();
+ void update_load_game_menu();
+ void generate_main_menu();
+ void generate_contrib_menu();
+ void check_levels_contrib_menu();
+ void check_contrib_world_menu();
+ void free_contrib_menu();
+ void generate_addons_menu();
+ void check_addons_menu();
+ void free_addons_menu();
+
+ std::auto_ptr<Menu> main_menu;
+ std::auto_ptr<Menu> load_game_menu;
+ std::auto_ptr<Menu> contrib_menu;
+ std::auto_ptr<Menu> contrib_world_menu;
+ std::auto_ptr<World> main_world;
+ std::vector<World*> contrib_worlds;
+ std::auto_ptr<Menu> addons_menu;
+ std::vector<Addon> addons; /**< shown list of Add-ons */
+ std::vector<Addon> available_addons; /**< list of downloadable Add-ons */
+ std::vector<Addon> installed_addons; /**< list of currently installed Add-ons */
+ World* current_world;
+
+ std::auto_ptr<CodeController> controller;
+ std::auto_ptr<GameSession> titlesession;
+};
+
+#endif
--- /dev/null
+// $Id$
+//
+// SuperTux - Climbable area
+// Copyright (C) 2007 Christoph Sommer <christoph.sommer@2007.expires.deltadevelopment.de>
+//
+// 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 <config.h>
+
+#include "climbable.hpp"
+#include "game_session.hpp"
+#include "lisp/lisp.hpp"
+#include "lisp/writer.hpp"
+#include "object_factory.hpp"
+#include "main.hpp"
+#include "sector.hpp"
+#include "level.hpp"
+#include "gettext.hpp"
+#include "object/tilemap.hpp"
+
+namespace {
+ const float GRACE_DX = 8; // how far off may the player's bounding-box be x-wise
+ const float GRACE_DY = 8; // how far off may the player's bounding-box be y-wise
+ const float ACTIVATE_TRY_FOR = 1; // how long to try correcting mis-alignment of player and climbable before giving up
+ const float POSITION_FIX_AX = 50; // x-wise acceleration applied to player when trying to align player and Climbable
+ const float POSITION_FIX_AY = 50; // y-wise acceleration applied to player when trying to align player and Climbable
+}
+
+Climbable::Climbable(const lisp::Lisp& reader)
+ : climbed_by(0)
+{
+ reader.get("x", bbox.p1.x);
+ reader.get("y", bbox.p1.y);
+ float w = 32, h = 32;
+ reader.get("width", w);
+ reader.get("height", h);
+ bbox.set_size(w, h);
+}
+
+Climbable::Climbable(const Rect& area)
+ : climbed_by(0)
+{
+ bbox = area;
+}
+
+Climbable::~Climbable()
+{
+ if (climbed_by) {
+ climbed_by->stop_climbing(*this);
+ climbed_by = 0;
+ }
+}
+
+void
+Climbable::write(lisp::Writer& writer)
+{
+ writer.start_list("climbable");
+
+ writer.write_float("x", bbox.p1.x);
+ writer.write_float("y", bbox.p1.y);
+ writer.write_float("width", bbox.get_width());
+ writer.write_float("height", bbox.get_height());
+
+ writer.end_list("climbable");
+}
+
+void
+Climbable::update(float /*elapsed_time*/)
+{
+ if (!climbed_by) return;
+
+ if (!may_climb(*climbed_by)) {
+ climbed_by->stop_climbing(*this);
+ climbed_by = 0;
+ }
+}
+
+void
+Climbable::draw(DrawingContext& context)
+{
+ if (climbed_by) {
+ context.push_transform();
+ context.set_translation(Vector(0, 0));
+ Vector pos = Vector(0, SCREEN_HEIGHT/2 - gold_text->get_height()/2);
+ context.draw_center_text(gold_text, "Up we go...", pos, LAYER_GUI);
+ context.pop_transform();
+ }
+}
+
+void
+Climbable::event(Player& player, EventType type)
+{
+ if ((type == EVENT_ACTIVATE) || (activate_try_timer.started())) {
+ if (may_climb(player)) {
+ climbed_by = &player;
+ player.start_climbing(*this);
+ activate_try_timer.stop();
+ } else {
+ if (type == EVENT_ACTIVATE) activate_try_timer.start(ACTIVATE_TRY_FOR);
+ if (player.get_bbox().p1.x < get_bbox().p1.x - GRACE_DX) player.add_velocity(Vector(POSITION_FIX_AX,0));
+ if (player.get_bbox().p2.x > get_bbox().p2.x + GRACE_DX) player.add_velocity(Vector(-POSITION_FIX_AX,0));
+ if (player.get_bbox().p1.y < get_bbox().p1.y - GRACE_DY) player.add_velocity(Vector(0,POSITION_FIX_AY));
+ if (player.get_bbox().p2.y > get_bbox().p2.y + GRACE_DY) player.add_velocity(Vector(0,-POSITION_FIX_AY));
+ }
+ }
+ if(type == EVENT_LOSETOUCH) {
+ player.stop_climbing(*this);
+ climbed_by = 0;
+ }
+}
+
+bool
+Climbable::may_climb(Player& player)
+{
+ if (player.get_bbox().p1.x < get_bbox().p1.x - GRACE_DX) return false;
+ if (player.get_bbox().p2.x > get_bbox().p2.x + GRACE_DX) return false;
+ if (player.get_bbox().p1.y < get_bbox().p1.y - GRACE_DY) return false;
+ if (player.get_bbox().p2.y > get_bbox().p2.y + GRACE_DY) return false;
+ return true;
+}
+
+IMPLEMENT_FACTORY(Climbable, "climbable");
+
--- /dev/null
+// $Id$
+//
+// SuperTux - Climbable area
+// Copyright (C) 2007 Christoph Sommer <christoph.sommer@2007.expires.deltadevelopment.de>
+//
+// 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 __CLIMBABLE_H__
+#define __CLIMBABLE_H__
+
+#include "trigger_base.hpp"
+#include "serializable.hpp"
+#include "resources.hpp"
+#include "video/drawing_context.hpp"
+#include "timer.hpp"
+#include "object/player.hpp"
+
+class Climbable : public TriggerBase, public Serializable
+{
+public:
+ Climbable(const lisp::Lisp& reader);
+ Climbable(const Rect& area);
+ ~Climbable();
+
+ void write(lisp::Writer& writer);
+ void event(Player& player, EventType type);
+ void update(float elapsed_time);
+ void draw(DrawingContext& context);
+
+ /**
+ * returns true if the player is within bounds of the Climbable
+ */
+ bool may_climb(Player& player);
+
+protected:
+ Player* climbed_by; /**< set to player who's currently climbing us, null if nobody is */
+ Timer activate_try_timer; /**< try to correct mis-alignment while this timer runs */
+};
+
+#endif
--- /dev/null
+// $Id$
+//
+// SuperTux
+// Copyright (C) 2006 Matthias Braun <matze@braunis.de>
+//
+// 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 <config.h>
+
+#include "door.hpp"
+#include "game_session.hpp"
+#include "resources.hpp"
+#include "object_factory.hpp"
+#include "sprite/sprite.hpp"
+#include "sprite/sprite_manager.hpp"
+#include "video/drawing_context.hpp"
+#include "lisp/lisp.hpp"
+#include "lisp/writer.hpp"
+#include "audio/sound_manager.hpp"
+
+Door::Door(const lisp::Lisp& reader)
+ : state(CLOSED)
+{
+ reader.get("x", bbox.p1.x);
+ reader.get("y", bbox.p1.y);
+ reader.get("sector", target_sector);
+ reader.get("spawnpoint", target_spawnpoint);
+
+ sprite = sprite_manager->create("images/objects/door/door.sprite");
+ sprite->set_action("closed");
+ bbox.set_size(sprite->get_current_hitbox_width(), sprite->get_current_hitbox_height());
+}
+
+Door::Door(int x, int y, std::string sector, std::string spawnpoint)
+ : state(CLOSED)
+{
+ bbox.set_pos(Vector(x, y));
+ target_sector = sector;
+ target_spawnpoint = spawnpoint;
+
+ sprite = sprite_manager->create("images/objects/door/door.sprite");
+ sprite->set_action("closed");
+ bbox.set_size(sprite->get_current_hitbox_width(), sprite->get_current_hitbox_height());
+}
+
+Door::~Door()
+{
+ delete sprite;
+}
+
+void
+Door::write(lisp::Writer& writer)
+{
+ writer.start_list("door");
+
+ writer.write_float("x", bbox.p1.x);
+ writer.write_float("y", bbox.p1.y);
+ writer.write_float("width", bbox.get_width());
+ writer.write_float("height", bbox.get_height());
+
+ writer.write_string("sector", target_sector);
+ writer.write_string("spawnpoint", target_spawnpoint);
+ sound_manager->preload("sounds/door.wav");
+ writer.end_list("door");
+}
+
+void
+Door::update(float )
+{
+ switch (state) {
+ case CLOSED:
+ break;
+ case OPENING:
+ // if door has finished opening, start timer and keep door open
+ if(sprite->animation_done()) {
+ state = OPEN;
+ sprite->set_action("open");
+ stay_open_timer.start(1.0);
+ }
+ break;
+ case OPEN:
+ // if door was open long enough, start closing it
+ if (stay_open_timer.check()) {
+ state = CLOSING;
+ sprite->set_action("closing", 1);
+ }
+ break;
+ case CLOSING:
+ // if door has finished closing, keep it shut
+ if(sprite->animation_done()) {
+ state = CLOSED;
+ sprite->set_action("closed");
+ }
+ break;
+ }
+}
+
+void
+Door::draw(DrawingContext& context)
+{
+ sprite->draw(context, bbox.p1, LAYER_BACKGROUNDTILES+1);
+}
+
+void
+Door::event(Player& , EventType type)
+{
+ switch (state) {
+ case CLOSED:
+ // if door was activated, start opening it
+ if (type == EVENT_ACTIVATE) {
+ state = OPENING;
+ sound_manager->play("sounds/door.wav");
+ sprite->set_action("opening", 1);
+ }
+ break;
+ case OPENING:
+ break;
+ case OPEN:
+ break;
+ case CLOSING:
+ break;
+ }
+}
+
+HitResponse
+Door::collision(GameObject& other, const CollisionHit& hit)
+{
+ switch (state) {
+ case CLOSED:
+ break;
+ case OPENING:
+ break;
+ case OPEN:
+ {
+ // if door is open and was touched by a player, teleport the player
+ Player* player = dynamic_cast<Player*> (&other);
+ if (player) {
+ state = CLOSING;
+ sprite->set_action("closing", 1);
+ GameSession::current()->respawn(target_sector, target_spawnpoint);
+ }
+ }
+ break;
+ case CLOSING:
+ break;
+ }
+
+ return TriggerBase::collision(other, hit);
+}
+
+IMPLEMENT_FACTORY(Door, "door");
--- /dev/null
+// $Id$
+//
+// SuperTux
+// Copyright (C) 2006 Matthias Braun <matze@braunis.de>
+//
+// 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_DOOR_H
+#define SUPERTUX_DOOR_H
+
+#include <string>
+
+#include "video/surface.hpp"
+#include "sprite/sprite.hpp"
+#include "trigger_base.hpp"
+#include "serializable.hpp"
+#include "timer.hpp"
+#include "object/player.hpp"
+
+class Door : public TriggerBase, public Serializable
+{
+public:
+ Door(const lisp::Lisp& reader);
+ Door(int x, int y, std::string sector, std::string spawnpoint);
+ virtual ~Door();
+
+ virtual void write(lisp::Writer& writer);
+
+ virtual void update(float elapsed_time);
+ virtual void draw(DrawingContext& context);
+ virtual void event(Player& player, EventType type);
+ virtual HitResponse collision(GameObject& other, const CollisionHit& hit);
+
+private:
+ enum DoorState {
+ CLOSED,
+ OPENING,
+ OPEN,
+ CLOSING
+ };
+
+ DoorState state; /**< current state of the door */
+ std::string target_sector; /**< target sector to teleport to */
+ std::string target_spawnpoint; /**< target spawnpoint to teleport to */
+ Sprite* sprite; /**< "door" sprite to render */
+ Timer stay_open_timer; /**< time until door will close again */
+};
+
+#endif
--- /dev/null
+// $Id$
+//
+// SuperTux
+// Copyright (C) 2006 Matthias Braun <matze@braunis.de>
+//
+// 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 <config.h>
+
+#include <sstream>
+#include <stdexcept>
+#include <memory>
+
+#include "scripttrigger.hpp"
+#include "game_session.hpp"
+#include "lisp/lisp.hpp"
+#include "lisp/writer.hpp"
+#include "object_factory.hpp"
+#include "sector.hpp"
+
+ScriptTrigger::ScriptTrigger(const lisp::Lisp& reader)
+{
+ bool must_activate = false;
+
+ reader.get("x", bbox.p1.x);
+ reader.get("y", bbox.p1.y);
+ float w = 0, h = 0;
+ reader.get("width", w);
+ reader.get("height", h);
+ bbox.set_size(w, h);
+ reader.get("script", script);
+ reader.get("button", must_activate);
+ if(script == "") {
+ throw std::runtime_error("Need to specify a script for trigger object");
+ }
+
+ if (must_activate)
+ triggerevent = EVENT_ACTIVATE;
+ else
+ triggerevent = EVENT_TOUCH;
+}
+
+ScriptTrigger::ScriptTrigger(const Vector& pos, const std::string& script)
+{
+ bbox.set_pos(pos);
+ bbox.set_size(32, 32);
+ this->script = script;
+ triggerevent = EVENT_TOUCH;
+}
+
+ScriptTrigger::~ScriptTrigger()
+{
+}
+
+void
+ScriptTrigger::write(lisp::Writer& writer)
+{
+ writer.start_list("scripttrigger");
+
+ writer.write_float("x", bbox.p1.x);
+ writer.write_float("y", bbox.p1.y);
+ writer.write_float("width", bbox.get_width());
+ writer.write_float("height", bbox.get_height());
+ writer.write_string("script", script);
+ writer.write_bool("button", triggerevent == EVENT_ACTIVATE);
+
+ writer.end_list("scripttrigger");
+}
+
+void
+ScriptTrigger::event(Player& , EventType type)
+{
+ if(type != triggerevent)
+ return;
+
+ std::istringstream stream(script);
+ Sector::current()->run_script(stream, "ScriptTrigger");
+}
+
+IMPLEMENT_FACTORY(ScriptTrigger, "scripttrigger");
--- /dev/null
+// $Id$
+//
+// SuperTux
+// Copyright (C) 2006 Matthias Braun <matze@braunis.de>
+//
+// 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 __SCRIPTTRIGGER_H__
+#define __SCRIPTTRIGGER_H__
+
+#include "trigger_base.hpp"
+#include "serializable.hpp"
+
+class ScriptTrigger : public TriggerBase, public Serializable
+{
+public:
+ ScriptTrigger(const lisp::Lisp& reader);
+ ScriptTrigger(const Vector& pos, const std::string& script);
+ ~ScriptTrigger();
+
+ void write(lisp::Writer& writer);
+ void event(Player& player, EventType type);
+
+private:
+ EventType triggerevent;
+ std::string script;
+};
+
+#endif
--- /dev/null
+// $Id$
+//
+// SuperTux
+// Copyright (C) 2006 Matthias Braun <matze@braunis.de>
+//
+// 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 <config.h>
+
+#include "secretarea_trigger.hpp"
+#include "game_session.hpp"
+#include "lisp/lisp.hpp"
+#include "lisp/writer.hpp"
+#include "object_factory.hpp"
+#include "main.hpp"
+#include "sector.hpp"
+#include "level.hpp"
+#include "gettext.hpp"
+#include "object/tilemap.hpp"
+
+static const float MESSAGE_TIME=3.5;
+
+SecretAreaTrigger::SecretAreaTrigger(const lisp::Lisp& reader)
+ : fade_tilemap("")
+{
+ reader.get("x", bbox.p1.x);
+ reader.get("y", bbox.p1.y);
+ float w = 32, h = 32;
+ reader.get("width", w);
+ reader.get("height", h);
+ bbox.set_size(w, h);
+ reader.get("fade-tilemap", fade_tilemap);
+
+ message_displayed = false;
+}
+
+SecretAreaTrigger::SecretAreaTrigger(const Rect& area, std::string fade_tilemap)
+ : fade_tilemap(fade_tilemap)
+{
+ bbox = area;
+ message_displayed = false;
+}
+
+SecretAreaTrigger::~SecretAreaTrigger()
+{
+}
+
+void
+SecretAreaTrigger::write(lisp::Writer& writer)
+{
+ writer.start_list("secretarea");
+
+ writer.write_float("x", bbox.p1.x);
+ writer.write_float("y", bbox.p1.y);
+ writer.write_float("width", bbox.get_width());
+ writer.write_float("height", bbox.get_height());
+ writer.write_string("fade-tilemap", fade_tilemap);
+
+ writer.end_list("secretarea");
+}
+
+void
+SecretAreaTrigger::draw(DrawingContext& context)
+{
+ if (message_timer.started()) {
+ context.push_transform();
+ context.set_translation(Vector(0, 0));
+ Vector pos = Vector(0, SCREEN_HEIGHT/2 - gold_text->get_height()/2);
+ context.draw_center_text(gold_text, _("You found a secret area!"), pos, LAYER_GUI);
+ context.pop_transform();
+ }
+ if (message_timer.check()) {
+ remove_me();
+ }
+}
+
+void
+SecretAreaTrigger::event(Player& , EventType type)
+{
+ if(type == EVENT_TOUCH) {
+ if (!message_displayed) {
+ message_timer.start(MESSAGE_TIME);
+ message_displayed = true;
+ Sector::current()->get_level()->stats.secrets++;
+
+ if (fade_tilemap != "") {
+ // fade away tilemaps
+ Sector& sector = *Sector::current();
+ for(Sector::GameObjects::iterator i = sector.gameobjects.begin(); i != sector.gameobjects.end(); ++i) {
+ TileMap* tm = dynamic_cast<TileMap*>(*i);
+ if (!tm) continue;
+ if (tm->get_name() != fade_tilemap) continue;
+ tm->fade(0.0, 1.0);
+ }
+ }
+
+ }
+ }
+}
+
+IMPLEMENT_FACTORY(SecretAreaTrigger, "secretarea");
--- /dev/null
+// $Id$
+//
+// SuperTux
+// Copyright (C) 2006 Matthias Braun <matze@braunis.de>
+//
+// 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 __SECRETAREA_TRIGGER_H__
+#define __SECRETAREA_TRIGGER_H__
+
+#include "trigger_base.hpp"
+#include "serializable.hpp"
+#include "resources.hpp"
+#include "video/drawing_context.hpp"
+#include "timer.hpp"
+
+class SecretAreaTrigger : public TriggerBase, public Serializable
+{
+public:
+ SecretAreaTrigger(const lisp::Lisp& reader);
+ SecretAreaTrigger(const Rect& area, std::string fade_tilemap = "");
+ ~SecretAreaTrigger();
+
+ void write(lisp::Writer& writer);
+ void event(Player& player, EventType type);
+ void draw(DrawingContext& context);
+
+private:
+ Timer message_timer;
+ bool message_displayed;
+ std::string fade_tilemap; /**< tilemap to fade away when trigger is activated, or empty if you don't care */
+};
+
+#endif
--- /dev/null
+// $Id$
+//
+// SuperTux
+// Copyright (C) 2006 Matthias Braun <matze@braunis.de>
+//
+// 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 <config.h>
+
+#include "sequence_trigger.hpp"
+#include "game_session.hpp"
+#include "lisp/lisp.hpp"
+#include "lisp/writer.hpp"
+#include "object_factory.hpp"
+
+SequenceTrigger::SequenceTrigger(const lisp::Lisp& reader)
+{
+ reader.get("x", bbox.p1.x);
+ reader.get("y", bbox.p1.y);
+ float w = 0, h = 0;
+ reader.get("width", w);
+ reader.get("height", h);
+ bbox.set_size(w, h);
+ reader.get("sequence", sequence_name);
+ triggerevent = EVENT_TOUCH;
+}
+
+SequenceTrigger::SequenceTrigger(const Vector& pos, const std::string& sequence)
+{
+ bbox.set_pos(pos);
+ bbox.set_size(32, 32);
+ sequence_name = sequence;
+ triggerevent = EVENT_TOUCH;
+}
+
+SequenceTrigger::~SequenceTrigger()
+{
+}
+
+void
+SequenceTrigger::write(lisp::Writer& writer)
+{
+ writer.start_list("sequencetrigger");
+
+ writer.write_float("x", bbox.p1.x);
+ writer.write_float("y", bbox.p1.y);
+ writer.write_float("width", bbox.get_width());
+ writer.write_float("height", bbox.get_height());
+ writer.write_string("sequence", sequence_name);
+
+ writer.end_list("sequencetrigger");
+}
+
+void
+SequenceTrigger::event(Player& player, EventType type)
+{
+ if(type == triggerevent) {
+ player.trigger_sequence(sequence_name);
+ }
+}
+
+IMPLEMENT_FACTORY(SequenceTrigger, "sequencetrigger")
--- /dev/null
+// $Id$
+//
+// SuperTux
+// Copyright (C) 2006 Matthias Braun <matze@braunis.de>
+//
+// 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 __SEQUENCE_TRIGGER_H__
+#define __SEQUENCE_TRIGGER_H__
+
+#include "trigger_base.hpp"
+#include "serializable.hpp"
+#include "object/player.hpp"
+
+class SequenceTrigger : public TriggerBase, public Serializable
+{
+public:
+ SequenceTrigger(const lisp::Lisp& reader);
+ SequenceTrigger(const Vector& pos, const std::string& sequence);
+ ~SequenceTrigger();
+
+ void write(lisp::Writer& writer);
+ void event(Player& player, EventType type);
+
+ std::string get_sequence_name() const { return sequence_name; }
+
+private:
+ EventType triggerevent;
+ std::string sequence_name;
+};
+
+#endif
--- /dev/null
+// $Id$
+//
+// SuperTux - Switch Trigger
+// Copyright (C) 2006 Christoph Sommer <christoph.sommer@2006.expires.deltadevelopment.de>
+//
+// 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 <config.h>
+#include <stdexcept>
+
+#include "switch.hpp"
+#include "object_factory.hpp"
+#include "sprite/sprite.hpp"
+#include "sprite/sprite_manager.hpp"
+#include "sector.hpp"
+#include "audio/sound_manager.hpp"
+
+namespace {
+ const std::string SWITCH_SOUND = "sounds/switch.ogg";
+}
+
+Switch::Switch(const lisp::Lisp& reader)
+ : state(OFF)
+{
+ if (!reader.get("x", bbox.p1.x)) throw std::runtime_error("no x position set");
+ if (!reader.get("y", bbox.p1.y)) throw std::runtime_error("no y position set");
+ if (!reader.get("sprite", sprite_name)) throw std::runtime_error("no sprite name set");
+ sprite = sprite_manager->create(sprite_name);
+ bbox.set_size(sprite->get_current_hitbox_width(), sprite->get_current_hitbox_height());
+
+ if (!reader.get("script", script)) throw std::runtime_error("no script set");
+ sound_manager->preload( SWITCH_SOUND );
+}
+
+Switch::~Switch()
+{
+ delete sprite;
+}
+
+void
+Switch::write(lisp::Writer& writer)
+{
+ writer.start_list("switch");
+ writer.write_float("x", bbox.p1.x);
+ writer.write_float("y", bbox.p1.y);
+ writer.write_string("sprite", sprite_name);
+ writer.write_string("script", script);
+ writer.end_list("switch");
+}
+
+void
+Switch::update(float )
+{
+ switch (state) {
+ case OFF:
+ break;
+ case TURN_ON:
+ if(sprite->animation_done()) {
+ std::istringstream stream(script);
+ Sector::current()->run_script(stream, "Switch");
+
+ sprite->set_action("on", 1);
+ state = ON;
+ }
+ break;
+ case ON:
+ if(sprite->animation_done()) {
+ sprite->set_action("turnoff", 1);
+ state = TURN_OFF;
+ }
+ break;
+ case TURN_OFF:
+ if(sprite->animation_done()) {
+ sprite->set_action("off");
+ state = OFF;
+ }
+ break;
+ }
+}
+
+void
+Switch::draw(DrawingContext& context)
+{
+ sprite->draw(context, bbox.p1, LAYER_TILES);
+}
+
+void
+Switch::event(Player& , EventType type)
+{
+ if(type != EVENT_ACTIVATE) return;
+
+ switch (state) {
+ case OFF:
+ sprite->set_action("turnon", 1);
+ sound_manager->play( SWITCH_SOUND );
+ state = TURN_ON;
+ break;
+ case TURN_ON:
+ break;
+ case ON:
+ break;
+ case TURN_OFF:
+ break;
+ }
+
+}
+
+IMPLEMENT_FACTORY(Switch, "switch");
--- /dev/null
+// $Id$
+//
+// SuperTux - Switch Trigger
+// Copyright (C) 2006 Christoph Sommer <christoph.sommer@2006.expires.deltadevelopment.de>
+//
+// 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_SWITCH_H
+#define SUPERTUX_SWITCH_H
+
+#include <string>
+
+#include "trigger_base.hpp"
+#include "serializable.hpp"
+#include "lisp/lisp.hpp"
+#include "lisp/writer.hpp"
+#include "video/drawing_context.hpp"
+#include "sprite/sprite.hpp"
+
+class Switch : public TriggerBase, public Serializable
+{
+public:
+ Switch(const lisp::Lisp& reader);
+ virtual ~Switch();
+
+ virtual void write(lisp::Writer& writer);
+
+ virtual void update(float elapsed_time);
+ virtual void draw(DrawingContext& context);
+ virtual void event(Player& player, EventType type);
+
+private:
+ enum SwitchState {
+ OFF,
+ TURN_ON,
+ ON,
+ TURN_OFF
+ };
+
+ std::string sprite_name;
+ Sprite* sprite;
+ std::string script;
+
+ SwitchState state;
+
+};
+
+#endif
--- /dev/null
+// $Id$
+//
+// SuperTux
+// Copyright (C) 2006 Matthias Braun <matze@braunis.de>
+//
+// 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 <config.h>
+
+#include "trigger_base.hpp"
+#include "video/drawing_context.hpp"
+#include "object/player.hpp"
+#include "log.hpp"
+
+TriggerBase::TriggerBase()
+ : sprite(0), lasthit(false), hit(false)
+{
+ set_group(COLGROUP_TOUCHABLE);
+}
+
+TriggerBase::~TriggerBase()
+{
+ // unregister remove_listener hooks, so nobody will try to call us after we've been destroyed
+ for (std::list<Player*>::iterator i = losetouch_listeners.begin(); i != losetouch_listeners.end(); i++) {
+ Player* p = *i;
+ p->del_remove_listener(this);
+ }
+ losetouch_listeners.clear();
+}
+
+void
+TriggerBase::update(float )
+{
+ if (lasthit && !hit) {
+ for (std::list<Player*>::iterator i = losetouch_listeners.begin(); i != losetouch_listeners.end(); i++) {
+ Player* p = *i;
+ event(*p, EVENT_LOSETOUCH);
+ p->del_remove_listener(this);
+ }
+ losetouch_listeners.clear();
+ }
+ lasthit = hit;
+ hit = false;
+}
+
+void
+TriggerBase::draw(DrawingContext& context)
+{
+ if(!sprite)
+ return;
+
+ sprite->draw(context, get_pos(), LAYER_TILES+1);
+}
+
+HitResponse
+TriggerBase::collision(GameObject& other, const CollisionHit& )
+{
+ Player* player = dynamic_cast<Player*> (&other);
+ if(player) {
+ hit = true;
+ if(!lasthit) {
+ losetouch_listeners.push_back(player);
+ player->add_remove_listener(this);
+ event(*player, EVENT_TOUCH);
+ }
+ }
+
+ return ABORT_MOVE;
+}
+
+void
+TriggerBase::object_removed(GameObject* object)
+{
+ for (std::list<Player*>::iterator i = losetouch_listeners.begin(); i != losetouch_listeners.end(); i++) {
+ Player* p = *i;
+ if (p == object) {
+ losetouch_listeners.erase(i);
+ break;
+ }
+ }
+}
+
--- /dev/null
+// $Id$
+//
+// SuperTux
+// Copyright (C) 2006 Matthias Braun <matze@braunis.de>
+//
+// 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_TRIGGER_BASE_H
+#define SUPERTUX_TRIGGER_BASE_H
+
+#include <list>
+#include "moving_object.hpp"
+#include "math/rect.hpp"
+#include "sprite/sprite.hpp"
+#include "object_remove_listener.hpp"
+
+class Player;
+
+/** This class is the base class for all objects you can interact with in some
+ * way. There are several interaction types defined like touch and activate
+ */
+class TriggerBase : public MovingObject, public ObjectRemoveListener
+{
+public:
+ enum EventType {
+ EVENT_TOUCH, /**< Object came into contact */
+ EVENT_LOSETOUCH, /**< Lost contact with object */
+ EVENT_ACTIVATE /**< Action button pressed */
+ };
+
+ TriggerBase();
+ ~TriggerBase();
+
+ void update(float elapsed_time);
+ void draw(DrawingContext& context);
+ HitResponse collision(GameObject& other, const CollisionHit& hit);
+
+ /**
+ * Receive trigger events
+ */
+ virtual void event(Player& player, EventType type) = 0;
+
+ /**
+ * Called by GameObject destructor of an object in losetouch_listeners
+ */
+ virtual void object_removed(GameObject* object);
+
+private:
+ Sprite* sprite;
+ bool lasthit;
+ bool hit;
+
+ std::list<Player*> losetouch_listeners; /**< Players that will be informed when we lose touch with them */
+};
+
+#endif /*SUPERTUX_INTERACTIVE_OBJECT_H*/
--- /dev/null
+// $Id: color.cpp 5063 2007-05-27 11:32:00Z matzeb $
+//
+// SuperTux
+// Copyright (C) 2006 Matthias Braun <matze@braunis.de>
+//
+// 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 <config.h>
+
+#include "color.hpp"
+
+const Color Color::BLACK(0.0, 0.0, 0.0);
+const Color Color::RED(1.0, 0.0, 0.0);
+const Color Color::GREEN(0.0, 1.0, 0.0);
+const Color Color::BLUE(0.0, 0.0, 1.0);
+const Color Color::CYAN(0.0, 1.0, 1.0);
+const Color Color::MAGENTA(1.0, 0.0, 1.0);
+const Color Color::YELLOW(1.0, 1.0, 0.0);
+const Color Color::WHITE(1.0, 1.0, 1.0);
--- /dev/null
+// $Id$
+//
+// SuperTux
+// Copyright (C) 2006 Matthias Braun <matze@braunis.de>
+//
+// 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 __COLOR_HPP__
+#define __COLOR_HPP__
+
+#include <vector>
+#include <assert.h>
+#include "log.hpp"
+
+class Color
+{
+public:
+ Color()
+ : red(0), green(0), blue(0), alpha(1.0)
+ { }
+ Color(float red, float green, float blue, float alpha = 1.0)
+ : red(red), green(green), blue(blue), alpha(alpha)
+ {
+#ifdef DEBUG
+ check_color_ranges();
+#endif
+ }
+ Color(const std::vector<float>& vals)
+ {
+ assert(vals.size() >= 3);
+ red = vals[0];
+ green = vals[1];
+ blue = vals[2];
+ if(vals.size() > 3)
+ alpha = vals[3];
+ else
+ alpha = 1.0;
+#ifdef DEBUG
+ check_color_ranges();
+#endif
+ }
+
+ bool operator==(const Color& other) const
+ {
+ return red == other.red && green == other.green && blue == other.blue
+ && alpha == other.alpha;
+ }
+
+ void check_color_ranges()
+ {
+ if(red < 0 || red > 1.0 || green < 0 || green > 1.0
+ || blue < 0 || blue > 1.0
+ || alpha < 0 || alpha > 1.0)
+ log_warning << "color value out of range: " << red << ", " << green << ", " << blue << ", " << alpha << std::endl;
+ }
+
+ float greyscale() const
+ {
+ return red * 0.30 + green * 0.59 + blue * 0.11;
+ }
+
+ bool operator < (const Color& other) const
+ {
+ return greyscale() < other.greyscale();
+ }
+
+ float red, green, blue, alpha;
+
+ static const Color BLACK;
+ static const Color RED;
+ static const Color GREEN;
+ static const Color BLUE;
+ static const Color CYAN;
+ static const Color MAGENTA;
+ static const Color YELLOW;
+ static const Color WHITE;
+};
+
+#endif
--- /dev/null
+// $Id$
+//
+// SuperTux
+// Copyright (C) 2006 Matthias Braun <matze@braunis.de>
+//
+// 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 <config.h>
+
+#include <functional>
+#include <algorithm>
+#include <cassert>
+#include <iostream>
+#include <SDL_image.h>
+#include <sstream>
+#include <iomanip>
+#include <physfs.h>
+
+#include "drawing_context.hpp"
+#include "drawing_request.hpp"
+#include "video_systems.hpp"
+#include "renderer.hpp"
+#include "lightmap.hpp"
+#include "surface.hpp"
+#include "main.hpp"
+#include "gameconfig.hpp"
+#include "texture.hpp"
+#include "texture_manager.hpp"
+#include "obstack/obstackpp.hpp"
+
+static inline int next_po2(int val)
+{
+ int result = 1;
+ while(result < val)
+ result *= 2;
+
+ return result;
+}
+
+DrawingContext::DrawingContext() :
+ renderer(0), lightmap(0), ambient_color(1.0f, 1.0f, 1.0f, 1.0f), target(NORMAL), screenshot_requested(false)
+{
+ requests = &drawing_requests;
+ obstack_init(&obst);
+}
+
+DrawingContext::~DrawingContext()
+{
+ delete renderer;
+ delete lightmap;
+
+ obstack_free(&obst, NULL);
+}
+
+void
+DrawingContext::init_renderer()
+{
+ delete renderer;
+ delete lightmap;
+
+ renderer = new_renderer();
+ lightmap = new_lightmap();
+}
+
+void
+DrawingContext::draw_surface(const Surface* surface, const Vector& position,
+ float angle, const Color& color, const Blend& blend,
+ int layer)
+{
+ assert(surface != 0);
+
+ DrawingRequest* request = new(obst) DrawingRequest();
+
+ request->target = target;
+ request->type = SURFACE;
+ request->pos = transform.apply(position);
+
+ if(request->pos.x >= SCREEN_WIDTH || request->pos.y >= SCREEN_HEIGHT
+ || request->pos.x + surface->get_width() < 0
+ || request->pos.y + surface->get_height() < 0)
+ return;
+
+ request->layer = layer;
+ request->drawing_effect = transform.drawing_effect;
+ request->alpha = transform.alpha;
+ request->angle = angle;
+ request->color = color;
+ request->blend = blend;
+
+ request->request_data = const_cast<Surface*> (surface);
+
+ requests->push_back(request);
+}
+
+void
+DrawingContext::draw_surface(const Surface* surface, const Vector& position,
+ int layer)
+{
+ draw_surface(surface, position, 0.0f, Color(1.0f, 1.0f, 1.0f), Blend(), layer);
+}
+
+void
+DrawingContext::draw_surface_part(const Surface* surface, const Vector& source,
+ const Vector& size, const Vector& dest, int layer)
+{
+ assert(surface != 0);
+
+ DrawingRequest* request = new(obst) DrawingRequest();
+
+ request->target = target;
+ request->type = SURFACE_PART;
+ request->pos = transform.apply(dest);
+ request->layer = layer;
+ request->drawing_effect = transform.drawing_effect;
+ request->alpha = transform.alpha;
+
+ SurfacePartRequest* surfacepartrequest = new(obst) SurfacePartRequest();
+ surfacepartrequest->size = size;
+ surfacepartrequest->source = source;
+ surfacepartrequest->surface = surface;
+
+ // clip on screen borders
+ if(request->pos.x < 0) {
+ surfacepartrequest->size.x += request->pos.x;
+ if(surfacepartrequest->size.x <= 0)
+ return;
+ surfacepartrequest->source.x -= request->pos.x;
+ request->pos.x = 0;
+ }
+ if(request->pos.y < 0) {
+ surfacepartrequest->size.y += request->pos.y;
+ if(surfacepartrequest->size.y <= 0)
+ return;
+ surfacepartrequest->source.y -= request->pos.y;
+ request->pos.y = 0;
+ }
+ request->request_data = surfacepartrequest;
+
+ requests->push_back(request);
+}
+
+void
+DrawingContext::draw_text(const Font* font, const std::string& text,
+ const Vector& position, FontAlignment alignment, int layer)
+{
+ DrawingRequest* request = new(obst) DrawingRequest();
+
+ request->target = target;
+ request->type = TEXT;
+ request->pos = transform.apply(position);
+ request->layer = layer;
+ request->drawing_effect = transform.drawing_effect;
+ request->alpha = transform.alpha;
+
+ TextRequest* textrequest = new(obst) TextRequest();
+ textrequest->font = font;
+ textrequest->text = text;
+ textrequest->alignment = alignment;
+ request->request_data = textrequest;
+
+ requests->push_back(request);
+}
+
+void
+DrawingContext::draw_center_text(const Font* font, const std::string& text,
+ const Vector& position, int layer)
+{
+ draw_text(font, text, Vector(position.x + SCREEN_WIDTH/2, position.y),
+ ALIGN_CENTER, layer);
+}
+
+void
+DrawingContext::draw_gradient(const Color& top, const Color& bottom, int layer)
+{
+ DrawingRequest* request = new(obst) DrawingRequest();
+
+ request->target = target;
+ request->type = GRADIENT;
+ request->pos = Vector(0,0);
+ request->layer = layer;
+
+ request->drawing_effect = transform.drawing_effect;
+ request->alpha = transform.alpha;
+
+ GradientRequest* gradientrequest = new(obst) GradientRequest();
+ gradientrequest->top = top;
+ gradientrequest->bottom = bottom;
+ request->request_data = gradientrequest;
+
+ requests->push_back(request);
+}
+
+void
+DrawingContext::draw_filled_rect(const Vector& topleft, const Vector& size,
+ const Color& color, int layer)
+{
+ DrawingRequest* request = new(obst) DrawingRequest();
+
+ request->target = target;
+ request->type = FILLRECT;
+ request->pos = transform.apply(topleft);
+ request->layer = layer;
+
+ request->drawing_effect = transform.drawing_effect;
+ request->alpha = transform.alpha;
+
+ FillRectRequest* fillrectrequest = new(obst) FillRectRequest();
+ fillrectrequest->size = size;
+ fillrectrequest->color = color;
+ fillrectrequest->color.alpha = color.alpha * transform.alpha;
+ request->request_data = fillrectrequest;
+
+ requests->push_back(request);
+}
+
+void
+DrawingContext::draw_filled_rect(const Rect& rect, const Color& color,
+ int layer)
+{
+ DrawingRequest* request = new(obst) DrawingRequest();
+
+ request->target = target;
+ request->type = FILLRECT;
+ request->pos = transform.apply(rect.p1);
+ request->layer = layer;
+
+ request->drawing_effect = transform.drawing_effect;
+ request->alpha = transform.alpha;
+
+ FillRectRequest* fillrectrequest = new(obst) FillRectRequest;
+ fillrectrequest->size = Vector(rect.get_width(), rect.get_height());
+ fillrectrequest->color = color;
+ fillrectrequest->color.alpha = color.alpha * transform.alpha;
+ request->request_data = fillrectrequest;
+
+ requests->push_back(request);
+}
+
+void
+DrawingContext::get_light(const Vector& position, Color* color)
+{
+ if( ambient_color.red == 1.0f && ambient_color.green == 1.0f
+ && ambient_color.blue == 1.0f ) {
+ *color = Color( 1.0f, 1.0f, 1.0f);
+ return;
+ }
+
+ DrawingRequest* request = new(obst) DrawingRequest();
+ request->target = target;
+ request->type = GETLIGHT;
+ request->pos = transform.apply(position);
+
+ //There is no light offscreen.
+ if(request->pos.x >= SCREEN_WIDTH || request->pos.y >= SCREEN_HEIGHT
+ || request->pos.x < 0 || request->pos.y < 0){
+ *color = Color( 0, 0, 0);
+ return;
+ }
+
+ request->layer = LAYER_GUI; //make sure all get_light requests are handled last.
+ GetLightRequest* getlightrequest = new(obst) GetLightRequest();
+ getlightrequest->color_ptr = color;
+ request->request_data = getlightrequest;
+ lightmap_requests.push_back(request);
+}
+
+void
+DrawingContext::do_drawing()
+{
+#ifdef DEBUG
+ assert(transformstack.empty());
+ assert(target_stack.empty());
+#endif
+ transformstack.clear();
+ target_stack.clear();
+
+ //Use Lightmap if ambient color is not white.
+ bool use_lightmap = ( ambient_color.red != 1.0f || ambient_color.green != 1.0f ||
+ ambient_color.blue != 1.0f );
+
+ // PART1: create lightmap
+ if(use_lightmap) {
+ lightmap->start_draw(ambient_color);
+ handle_drawing_requests(lightmap_requests);
+ lightmap->end_draw();
+ }
+
+ handle_drawing_requests(drawing_requests);
+ if(use_lightmap) {
+ lightmap->do_draw();
+ }
+ obstack_free(&obst, NULL);
+ obstack_init(&obst);
+
+ // if a screenshot was requested, take one
+ if (screenshot_requested) {
+ renderer->do_take_screenshot();
+ screenshot_requested = false;
+ }
+
+ renderer->flip();
+}
+
+class RequestPtrCompare
+ : public std::binary_function<const DrawingRequest*,
+ const DrawingRequest*,
+ bool>
+{
+public:
+ bool operator()(const DrawingRequest* r1, const DrawingRequest* r2) const
+ {
+ return *r1 < *r2;
+ }
+};
+
+void
+DrawingContext::handle_drawing_requests(DrawingRequests& requests)
+{
+ std::stable_sort(requests.begin(), requests.end(), RequestPtrCompare());
+
+ DrawingRequests::const_iterator i;
+ for(i = requests.begin(); i != requests.end(); ++i) {
+ const DrawingRequest& request = **i;
+
+ switch(request.target) {
+ case NORMAL:
+ switch(request.type) {
+ case SURFACE:
+ renderer->draw_surface(request);
+ break;
+ case SURFACE_PART:
+ renderer->draw_surface_part(request);
+ break;
+ case GRADIENT:
+ renderer->draw_gradient(request);
+ break;
+ case TEXT:
+ {
+ const TextRequest* textrequest = (TextRequest*) request.request_data;
+ textrequest->font->draw(renderer, textrequest->text, request.pos,
+ textrequest->alignment, request.drawing_effect, request.alpha);
+ }
+ break;
+ case FILLRECT:
+ renderer->draw_filled_rect(request);
+ break;
+ case GETLIGHT:
+ lightmap->get_light(request);
+ break;
+ }
+ break;
+ case LIGHTMAP:
+ switch(request.type) {
+ case SURFACE:
+ lightmap->draw_surface(request);
+ break;
+ case SURFACE_PART:
+ lightmap->draw_surface_part(request);
+ break;
+ case GRADIENT:
+ lightmap->draw_gradient(request);
+ break;
+ case TEXT:
+ {
+ const TextRequest* textrequest = (TextRequest*) request.request_data;
+ textrequest->font->draw(renderer, textrequest->text, request.pos,
+ textrequest->alignment, request.drawing_effect, request.alpha);
+ }
+ break;
+ case FILLRECT:
+ lightmap->draw_filled_rect(request);
+ break;
+ case GETLIGHT:
+ lightmap->get_light(request);
+ break;
+ }
+ break;
+ }
+ }
+ requests.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(DrawingEffect effect)
+{
+ transform.drawing_effect = effect;
+}
+
+DrawingEffect
+DrawingContext::get_drawing_effect() const
+{
+ return transform.drawing_effect;
+}
+
+void
+DrawingContext::set_alpha(float alpha)
+{
+ transform.alpha = alpha;
+}
+
+float
+DrawingContext::get_alpha() const
+{
+ return transform.alpha;
+}
+
+void
+DrawingContext::push_target()
+{
+ target_stack.push_back(target);
+}
+
+void
+DrawingContext::pop_target()
+{
+ set_target(target_stack.back());
+ target_stack.pop_back();
+}
+
+void
+DrawingContext::set_target(Target target)
+{
+ this->target = target;
+ if(target == LIGHTMAP) {
+ requests = &lightmap_requests;
+ } else {
+ assert(target == NORMAL);
+ requests = &drawing_requests;
+ }
+}
+
+void
+DrawingContext::set_ambient_color( Color new_color )
+{
+ ambient_color = new_color;
+}
+
+void
+DrawingContext::take_screenshot()
+{
+ screenshot_requested = true;
+}
+
--- /dev/null
+// $Id$
+//
+// SuperTux
+// Copyright (C) 2006 Matthias Braun <matze@braunis.de>
+//
+// 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_DRAWINGCONTEXT_H
+#define SUPERTUX_DRAWINGCONTEXT_H
+
+#include <vector>
+#include <string>
+#include <memory>
+
+#include <stdint.h>
+
+#include <SDL_video.h>
+
+#include "glutil.hpp"
+#include "obstack/obstack.h"
+#include "math/vector.hpp"
+#include "math/rect.hpp"
+#include "drawing_request.hpp"
+#include "font.hpp"
+#include "color.hpp"
+
+class Surface;
+class Texture;
+struct DrawingRequest;
+class Renderer;
+class Lightmap;
+
+/**
+ * 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();
+
+ void init_renderer();
+
+ /// Adds a drawing request for a surface into the request list.
+ void draw_surface(const Surface* surface, const Vector& position,
+ int layer);
+ /// Adds a drawing request for a surface into the request list.
+ void draw_surface(const Surface* surface, const Vector& position,
+ float angle, const Color& color, const Blend& blend,
+ int layer);
+ /// 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);
+ /// Draws a text.
+ void draw_text(const Font* font, const std::string& text,
+ const Vector& position, FontAlignment alignment, int layer);
+
+ /// Draws text on screen center (feed Vector.x with a 0).
+ /// This is the same as draw_text() with a SCREEN_WIDTH/2 position and
+ /// alignment set to LEFT_ALLIGN
+ void draw_center_text(const Font* font, const std::string& text,
+ const Vector& position, int layer);
+ /// Draws a color gradient onto the whole screen */
+ void draw_gradient(const Color& from, const Color& to, int layer);
+ /// Fills a rectangle.
+ void draw_filled_rect(const Vector& topleft, const Vector& size,
+ const Color& color, int layer);
+ void draw_filled_rect(const Rect& rect, const 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(DrawingEffect effect);
+ /// return currently applied drawing effect
+ DrawingEffect get_drawing_effect() const;
+ /// apply that alpha in the next draws (1.0 means fully opaque) */
+ void set_alpha(float alpha);
+ /// return currently set alpha
+ float get_alpha() const;
+
+ /// on next update, set color to lightmap's color at position
+ void get_light(const Vector& position, Color* color );
+
+ typedef ::Target Target;
+ static const Target NORMAL = ::NORMAL;
+ static const Target LIGHTMAP = ::LIGHTMAP;
+ void push_target();
+ void pop_target();
+ void set_target(Target target);
+
+ void set_ambient_color( Color new_color );
+
+ /**
+ * requests that a screenshot be taken after the next frame has been rendered
+ */
+ void take_screenshot();
+
+private:
+ class Transform
+ {
+ public:
+ Vector translation;
+ DrawingEffect drawing_effect;
+ float alpha;
+
+ Transform()
+ : drawing_effect(NO_EFFECT), alpha(1.0f)
+ { }
+
+ Vector apply(const Vector& v) const
+ {
+ return v - translation;
+ }
+ };
+
+ Renderer *renderer;
+ Lightmap *lightmap;
+
+ /// the transform stack
+ std::vector<Transform> transformstack;
+ /// the currently active transform
+ Transform transform;
+
+ std::vector<Blend> blend_stack;
+ Blend blend_mode;
+
+ typedef std::vector<DrawingRequest*> DrawingRequests;
+
+ void handle_drawing_requests(DrawingRequests& requests);
+
+ DrawingRequests drawing_requests;
+ DrawingRequests lightmap_requests;
+
+ DrawingRequests* requests;
+ Color ambient_color;
+
+ Target target;
+ std::vector<Target> target_stack;
+
+ /* obstack holding the memory of the drawing requests */
+ struct obstack obst;
+
+ bool screenshot_requested; /**< true if a screenshot should be taken after the next frame has been rendered */
+};
+
+#endif
+
--- /dev/null
+// $Id: drawing_request.hpp 4986 2007-04-16 17:48:28Z matzeb $
+//
+// SuperTux
+// Copyright (C) 2006 Matthias Braun <matze@braunis.de>
+//
+// 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_DRAWINGREQUEST_H
+#define SUPERTUX_DRAWINGREQUEST_H
+
+#include <vector>
+#include <string>
+#include <memory>
+
+#include <stdint.h>
+
+#include <SDL_video.h>
+
+#include "glutil.hpp"
+#include "math/vector.hpp"
+#include "color.hpp"
+#include "font.hpp"
+
+class Surface;
+
+// some constants for predefined layer values
+enum {
+ LAYER_BACKGROUND0 = -300,
+ LAYER_BACKGROUND1 = -200,
+ LAYER_BACKGROUNDTILES = -100,
+ LAYER_TILES = 0,
+ LAYER_OBJECTS = 50,
+ LAYER_FLOATINGOBJECTS = 150,
+ LAYER_FOREGROUNDTILES = 200,
+ LAYER_FOREGROUND0 = 300,
+ LAYER_FOREGROUND1 = 400,
+ LAYER_HUD = 500,
+ LAYER_GUI = 600
+};
+
+class Blend
+{
+public:
+ GLenum sfactor;
+ GLenum dfactor;
+
+ Blend()
+ : sfactor(GL_SRC_ALPHA), dfactor(GL_ONE_MINUS_SRC_ALPHA)
+ {}
+
+ Blend(GLenum s, GLenum d)
+ : sfactor(s), dfactor(d)
+ {}
+};
+
+enum Target {
+ NORMAL, LIGHTMAP
+};
+
+enum RequestType
+{
+ SURFACE, SURFACE_PART, TEXT, GRADIENT, FILLRECT, GETLIGHT
+};
+
+struct SurfacePartRequest
+{
+ const Surface* surface;
+ Vector source, size;
+};
+
+struct TextRequest
+{
+ const Font* font;
+ std::string text;
+ FontAlignment alignment;
+};
+
+struct GradientRequest
+{
+ Color top, bottom;
+ Vector size;
+};
+
+struct FillRectRequest
+{
+ Color color;
+ Vector size;
+};
+
+struct DrawingRequest
+{
+ Target target;
+ RequestType type;
+ Vector pos;
+
+ int layer;
+ DrawingEffect drawing_effect;
+ float alpha;
+ Blend blend;
+ float angle;
+ Color color;
+
+ void* request_data;
+
+ DrawingRequest()
+ : angle(0.0f),
+ color(1.0f, 1.0f, 1.0f, 1.0f)
+ {}
+
+ bool operator<(const DrawingRequest& other) const
+ {
+ return layer < other.layer;
+ }
+};
+
+struct GetLightRequest
+{
+ Color* color_ptr;
+};
+
+#endif
+
--- /dev/null
+// $Id$
+//
+// SuperTux
+// Copyright (C) 2006 Matthias Braun <matze@braunis.de>
+// Ingo Ruhnke <grumbel@gmx.de>
+//
+// 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 <config.h>
+
+#include <cstdlib>
+#include <cstring>
+#include <stdexcept>
+
+#include <SDL_image.h>
+#include "physfs/physfs_sdl.hpp"
+
+#include "lisp/parser.hpp"
+#include "lisp/lisp.hpp"
+#include "screen.hpp"
+#include "font.hpp"
+#include "renderer.hpp"
+#include "drawing_context.hpp"
+#include "log.hpp"
+
+namespace {
+bool has_multibyte_mark(unsigned char c);
+uint32_t decode_utf8(const std::string& text, size_t& p);
+
+struct UTF8Iterator
+{
+ const std::string& text;
+ std::string::size_type pos;
+ uint32_t chr;
+
+ UTF8Iterator(const std::string& text_)
+ : text(text_),
+ pos(0)
+ {
+ chr = decode_utf8(text, pos);
+ }
+
+ bool done() const
+ {
+ return pos > text.size();
+ }
+
+ UTF8Iterator& operator++() {
+ try {
+ chr = decode_utf8(text, pos);
+ } catch (std::runtime_error) {
+ log_debug << "Malformed utf-8 sequence beginning with " << *((uint32_t*)(text.c_str() + pos)) << " found " << std::endl;
+ chr = 0;
+ ++pos;
+ }
+
+ return *this;
+ }
+
+ uint32_t operator*() const {
+ return chr;
+ }
+};
+
+bool vline_empty(SDL_Surface* surface, int x, int start_y, int end_y, Uint8 threshold)
+{
+ Uint8* pixels = (Uint8*)surface->pixels;
+
+ for(int y = start_y; y < end_y; ++y)
+ {
+ const Uint8& p = pixels[surface->pitch*y + x*surface->format->BytesPerPixel + 3];
+ if (p > threshold)
+ {
+ return false;
+ }
+ }
+ return true;
+}
+} // namespace
+
+Font::Font(GlyphWidth glyph_width_,
+ const std::string& filename,
+ const std::string& shadowfile,
+ int char_width, int char_height_,
+ int shadowsize_)
+ : glyph_width(glyph_width_),
+ glyph_surface(0), shadow_glyph_surface(0),
+ char_height(char_height_),
+ shadowsize(shadowsize_)
+{
+ glyph_surface = new Surface(filename);
+ shadow_glyph_surface = new Surface(shadowfile);
+
+ first_char = 32;
+ char_count = ((int) glyph_surface->get_height() / char_height) * 16;
+
+ if (glyph_width == FIXED)
+ {
+ for(uint32_t i = 0; i < char_count; ++i)
+ {
+ float x = (i % 16) * char_width;
+ float y = (i / 16) * char_height;
+
+ Glyph glyph;
+ glyph.advance = char_width;
+ glyph.offset = Vector(0, 0);
+ glyph.rect = Rect(x, y, x + char_width, y + char_height);
+
+ glyphs.push_back(glyph);
+ shadow_glyphs.push_back(glyph);
+ }
+ }
+ else // glyph_width == VARIABLE
+ {
+ // Load the surface into RAM and scan the pixel data for characters
+ SDL_Surface* surface = IMG_Load_RW(get_physfs_SDLRWops(filename), 1);
+ if(surface == NULL) {
+ std::ostringstream msg;
+ msg << "Couldn't load image '" << filename << "' :" << SDL_GetError();
+ throw std::runtime_error(msg.str());
+ }
+
+ SDL_LockSurface(surface);
+
+ for(uint32_t i = 0; i < char_count; ++i)
+ {
+ int x = (i % 16) * char_width;
+ int y = (i / 16) * char_height;
+
+ int left = x;
+ while (left < x + char_width &&
+ vline_empty(surface, left, y, y + char_height, 64))
+ left += 1;
+
+ int right = x + char_width - 1;
+ while (right > left &&
+ vline_empty(surface, right, y, y + char_height, 64))
+ right -= 1;
+
+ Glyph glyph;
+ glyph.offset = Vector(0, 0);
+
+ if (left <= right)
+ glyph.rect = Rect(left, y, right+1, y + char_height);
+ else // glyph is completly transparent
+ glyph.rect = Rect(x, y, x + char_width, y + char_height);
+
+ glyph.advance = glyph.rect.get_width() + 1; // FIXME: might be usefull to make spacing configurable
+
+ glyphs.push_back(glyph);
+ shadow_glyphs.push_back(glyph);
+ }
+
+ SDL_UnlockSurface(surface);
+
+ SDL_FreeSurface(surface);
+ }
+}
+
+Font::~Font()
+{
+ delete glyph_surface;
+ delete shadow_glyph_surface;
+}
+
+float
+Font::get_text_width(const std::string& text) const
+{
+ float curr_width = 0;
+ float last_width = 0;
+
+ for(UTF8Iterator it(text); !it.done(); ++it)
+ {
+ if (*it == '\n')
+ {
+ last_width = std::max(last_width, curr_width);
+ curr_width = 0;
+ }
+ else
+ {
+ int idx = chr2glyph(*it);
+ curr_width += glyphs[idx].advance;
+ }
+ }
+
+ return std::max(curr_width, last_width);
+}
+
+float
+Font::get_text_height(const std::string& text) const
+{
+ std::string::size_type text_height = char_height;
+
+ for(std::string::const_iterator it = text.begin(); it != text.end(); ++it)
+ { // since UTF8 multibyte characters are decoded with values
+ // outside the ASCII range there is no risk of overlapping and
+ // thus we don't need to decode the utf-8 string
+ if (*it == '\n')
+ text_height += char_height + 2;
+ }
+
+ return text_height;
+}
+
+float
+Font::get_height() const
+{
+ return char_height;
+}
+
+std::string
+Font::wrap_to_chars(const std::string& s, int line_length, std::string* overflow)
+{
+ // if text is already smaller, return full text
+ if ((int)s.length() <= line_length) {
+ if (overflow) *overflow = "";
+ return s;
+ }
+
+ // if we can find a whitespace character to break at, return text up to this character
+ int i = line_length;
+ while ((i > 0) && (s[i] != ' ')) i--;
+ if (i > 0) {
+ if (overflow) *overflow = s.substr(i+1);
+ return s.substr(0, i);
+ }
+
+ // FIXME: wrap at line_length, taking care of multibyte characters
+ if (overflow) *overflow = "";
+ return s;
+}
+
+std::string
+Font::wrap_to_width(const std::string& s, float width, std::string* overflow)
+{
+ // if text is already smaller, return full text
+ if (get_text_width(s) <= width) {
+ if (overflow) *overflow = "";
+ return s;
+ }
+
+ // if we can find a whitespace character to break at, return text up to this character
+ for (int i = s.length()-1; i >= 0; i--) {
+ std::string s2 = s.substr(0,i);
+ if (s[i] != ' ') continue;
+ if (get_text_width(s2) <= width) {
+ if (overflow) *overflow = s.substr(i+1);
+ return s.substr(0, i);
+ }
+ }
+
+ // FIXME: hard-wrap at width, taking care of multibyte characters
+ if (overflow) *overflow = "";
+ return s;
+}
+
+void
+Font::draw(Renderer *renderer, const std::string& text, const Vector& pos_,
+ FontAlignment alignment, DrawingEffect drawing_effect,
+ float alpha) const
+{
+ float x = pos_.x;
+ float y = pos_.y;
+
+ std::string::size_type last = 0;
+ for(std::string::size_type i = 0;; ++i)
+ {
+ if (text[i] == '\n' || i == text.size())
+ {
+ std::string temp = text.substr(last, i - last);
+
+ // calculate X positions based on the alignment type
+ Vector pos = Vector(x, y);
+
+ if(alignment == ALIGN_CENTER)
+ pos.x -= get_text_width(temp) / 2;
+ else if(alignment == ALIGN_RIGHT)
+ pos.x -= get_text_width(temp);
+
+ // Cast font position to integer to get a clean drawing result and
+ // no bluring as we would get with subpixel positions
+ pos.x = static_cast<int>(pos.x);
+
+ draw_text(renderer, temp, pos, drawing_effect, alpha);
+
+ if (i == text.size())
+ break;
+
+ y += char_height + 2;
+ last = i + 1;
+ }
+ }
+}
+
+void
+Font::draw_text(Renderer *renderer, const std::string& text, const Vector& pos,
+ DrawingEffect drawing_effect, float alpha) const
+{
+ if(shadowsize > 0)
+ {
+ // FIXME: shadow_glyph_surface and glyph_surface do currently
+ // share the same glyph array, this is incorrect and should be
+ // fixed, it is however hardly noticable
+ draw_chars(renderer, shadow_glyph_surface, text,
+ pos + Vector(shadowsize, shadowsize), drawing_effect, alpha);
+ }
+
+ draw_chars(renderer, glyph_surface, text, pos, drawing_effect, alpha);
+}
+
+int
+Font::chr2glyph(uint32_t chr) const
+{
+ int glyph_index = chr - first_char;
+
+ // we don't have the control chars 0x80-0xa0 in the font
+ if (chr >= 0x80) { // non-ascii character
+ glyph_index -= 32;
+ if(chr <= 0xa0) {
+ log_debug << "Unsupported utf-8 character '" << chr << "' found" << std::endl;
+ glyph_index = 0;
+ }
+ }
+
+ if(glyph_index < 0 || glyph_index >= (int) char_count) {
+ log_debug << "Unsupported utf-8 character found" << std::endl;
+ glyph_index = 0;
+ }
+
+ return glyph_index;
+}
+
+void
+Font::draw_chars(Renderer *renderer, Surface* pchars, const std::string& text,
+ const Vector& pos, DrawingEffect drawing_effect,
+ float alpha) const
+{
+ Vector p = pos;
+
+ for(UTF8Iterator it(text); !it.done(); ++it)
+ {
+ int font_index = chr2glyph(*it);
+
+ if(*it == '\n')
+ {
+ p.x = pos.x;
+ p.y += char_height + 2;
+ }
+ else if(*it == ' ')
+ {
+ p.x += glyphs[font_index].advance;
+ }
+ else
+ {
+ const Glyph& glyph = glyphs[font_index];
+ DrawingRequest request;
+
+ request.pos = p + glyph.offset;
+ request.drawing_effect = drawing_effect;
+ request.alpha = alpha;
+
+ SurfacePartRequest surfacepartrequest;
+ surfacepartrequest.size = glyph.rect.p2 - glyph.rect.p1;
+ surfacepartrequest.source = glyph.rect.p1;
+ surfacepartrequest.surface = pchars;
+
+ request.request_data = &surfacepartrequest;
+ renderer->draw_surface_part(request);
+
+ p.x += glyphs[font_index].advance;
+ }
+ }
+}
+
+
+namespace {
+
+/**
+ * returns true if this byte matches a bitmask of 10xx.xxxx, i.e. it is the 2nd, 3rd or 4th byte of a multibyte utf8 string
+ */
+bool has_multibyte_mark(unsigned char c) {
+ return ((c & 0300) == 0200);
+}
+
+/**
+ * gets unicode character at byte position @a p of UTF-8 encoded @a
+ * text, then advances @a p to the next character.
+ *
+ * @throws std::runtime_error if decoding fails.
+ * See unicode standard section 3.10 table 3-5 and 3-6 for details.
+ */
+uint32_t decode_utf8(const std::string& text, size_t& p)
+{
+ uint32_t c1 = (unsigned char) text[p+0];
+
+ if (has_multibyte_mark(c1)) std::runtime_error("Malformed utf-8 sequence");
+
+ if ((c1 & 0200) == 0000) {
+ // 0xxx.xxxx: 1 byte sequence
+ p+=1;
+ return c1;
+ }
+ else if ((c1 & 0340) == 0300) {
+ // 110x.xxxx: 2 byte sequence
+ if(p+1 >= text.size()) throw std::range_error("Malformed utf-8 sequence");
+ uint32_t c2 = (unsigned char) text[p+1];
+ if (!has_multibyte_mark(c2)) throw std::runtime_error("Malformed utf-8 sequence");
+ p+=2;
+ return (c1 & 0037) << 6 | (c2 & 0077);
+ }
+ else if ((c1 & 0360) == 0340) {
+ // 1110.xxxx: 3 byte sequence
+ if(p+2 >= text.size()) throw std::range_error("Malformed utf-8 sequence");
+ uint32_t c2 = (unsigned char) text[p+1];
+ uint32_t c3 = (unsigned char) text[p+2];
+ if (!has_multibyte_mark(c2)) throw std::runtime_error("Malformed utf-8 sequence");
+ if (!has_multibyte_mark(c3)) throw std::runtime_error("Malformed utf-8 sequence");
+ p+=3;
+ return (c1 & 0017) << 12 | (c2 & 0077) << 6 | (c3 & 0077);
+ }
+ else if ((c1 & 0370) == 0360) {
+ // 1111.0xxx: 4 byte sequence
+ if(p+3 >= text.size()) throw std::range_error("Malformed utf-8 sequence");
+ uint32_t c2 = (unsigned char) text[p+1];
+ uint32_t c3 = (unsigned char) text[p+2];
+ uint32_t c4 = (unsigned char) text[p+4];
+ if (!has_multibyte_mark(c2)) throw std::runtime_error("Malformed utf-8 sequence");
+ if (!has_multibyte_mark(c3)) throw std::runtime_error("Malformed utf-8 sequence");
+ if (!has_multibyte_mark(c4)) throw std::runtime_error("Malformed utf-8 sequence");
+ p+=4;
+ return (c1 & 0007) << 18 | (c2 & 0077) << 12 | (c3 & 0077) << 6 | (c4 & 0077);
+ }
+ throw std::runtime_error("Malformed utf-8 sequence");
+}
+
+} // namespace
--- /dev/null
+// $Id$
+//
+// SuperTux
+// Copyright (C) 2006 Matthias Braun <matze@braunis.de>,
+// Ingo Ruhnke <grumbel@gmx.de>
+//
+// 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 <string>
+#include <stdint.h>
+
+#include "video/surface.hpp"
+#include "math/vector.hpp"
+#include "math/rect.hpp"
+
+class Renderer;
+
+enum FontAlignment {
+ ALIGN_LEFT,
+ ALIGN_CENTER,
+ ALIGN_RIGHT
+};
+
+class Font
+{
+public:
+ enum GlyphWidth {
+ FIXED,
+ VARIABLE
+ };
+
+ /** Construct a fixed-width font
+ *
+ * @param glyph_width VARIABLE for proportional fonts, VARIABLE for monospace ones
+ * @param filename image file containing the characters
+ * @param shadowfile image file containing the characters shadows
+ * @param char_width width of a character
+ * @param char_height height of a character
+ */
+ Font(GlyphWidth glyph_width,
+ const std::string& filename, const std::string& shadowfile,
+ int char_width, int char_height, int shadowsize = 2);
+ ~Font();
+
+ /** 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.)
+ * Supports breaklines.
+ */
+ float get_text_width(const std::string& text) const;
+
+ /** returns the height of a given text. This function supports breaklines.
+ * In case, you are positive that your text doesn't use break lines, you can
+ * just use get_height().
+ */
+ float get_text_height(const std::string& text) const;
+
+ /**
+ * returns the height of the font.
+ */
+ float get_height() const;
+
+ /**
+ * returns the given string, truncated (preferrably at whitespace) to be at most max_chars characters long
+ */
+ static std::string wrap_to_chars(const std::string& text, int max_chars, std::string* overflow);
+
+ /**
+ * returns the given string, truncated (preferrably at whitespace) to be at most "width" pixels wide
+ */
+ std::string wrap_to_width(const std::string& text, float width, std::string* overflow);
+
+ /** Draws the given text to the screen. Also needs the position.
+ * Type of alignment, drawing effect and alpha are optional. */
+ void draw(Renderer *renderer, const std::string& text, const Vector& pos,
+ FontAlignment allignment = ALIGN_LEFT,
+ DrawingEffect drawing_effect = NO_EFFECT,
+ float alpha = 1.0f) const;
+
+private:
+ friend class DrawingContext;
+
+ void draw_text(Renderer *renderer, const std::string& text, const Vector& pos,
+ DrawingEffect drawing_effect = NO_EFFECT,
+ float alpha = 1.0f) const;
+
+ void draw_chars(Renderer *renderer, Surface* pchars, const std::string& text,
+ const Vector& position, DrawingEffect drawing_effect,
+ float alpha) const;
+
+ /** Convert a Unicode character code to the index of its glyph */
+ int chr2glyph(uint32_t chr) const;
+
+ GlyphWidth glyph_width;
+ Surface* glyph_surface;
+ Surface* shadow_glyph_surface;
+ int char_height;
+ int shadowsize;
+
+ /// the number of the first character that is represented in the font
+ uint32_t first_char;
+ /// the number of the last character that is represented in the font
+ uint32_t char_count;
+
+ struct Glyph {
+ /** How many pixels should the cursor advance after printing the
+ glyph */
+ float advance;
+
+ /** Offset that is used when drawing the glyph */
+ Vector offset;
+
+ /** Position of the glyph inside the surface */
+ Rect rect;
+ };
+
+ /** Location of the characters inside the surface */
+ std::vector<Glyph> glyphs;
+ std::vector<Glyph> shadow_glyphs;
+};
+
+#endif
--- /dev/null
+// $Id: gl_lightmap.cpp 5063 2007-05-27 11:32:00Z matzeb $
+//
+// SuperTux
+// Copyright (C) 2006 Matthias Braun <matze@braunis.de>
+//
+// 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 <config.h>
+
+#ifdef HAVE_OPENGL
+
+#include <functional>
+#include <algorithm>
+#include <cassert>
+#include <math.h>
+#include <iostream>
+#include <SDL_image.h>
+#include <sstream>
+#include <iomanip>
+#include <physfs.h>
+
+#include "glutil.hpp"
+#include "gl_lightmap.hpp"
+#include "gl_surface_data.hpp"
+#include "drawing_context.hpp"
+#include "drawing_request.hpp"
+#include "renderer.hpp"
+#include "surface.hpp"
+#include "font.hpp"
+#include "main.hpp"
+#include "gameconfig.hpp"
+#include "gl_texture.hpp"
+#include "texture_manager.hpp"
+#include "obstack/obstackpp.hpp"
+
+namespace
+{
+ inline void intern_draw(float left, float top, float right, float bottom,
+ float uv_left, float uv_top,
+ float uv_right, float uv_bottom,
+ float angle, float alpha,
+ const Color& color,
+ const Blend& blend,
+ DrawingEffect effect)
+ {
+ if(effect & HORIZONTAL_FLIP)
+ std::swap(uv_left, uv_right);
+ if(effect & VERTICAL_FLIP) {
+ std::swap(uv_top, uv_bottom);
+ }
+
+ float center_x = (left + right) / 2;
+ float center_y = (top + bottom) / 2;
+
+ float sa = sinf(angle/180.0f*M_PI);
+ float ca = cosf(angle/180.0f*M_PI);
+
+ left -= center_x;
+ right -= center_x;
+
+ top -= center_y;
+ bottom -= center_y;
+
+ glBlendFunc(blend.sfactor, blend.dfactor);
+ glColor4f(color.red, color.green, color.blue, color.alpha * alpha);
+ glBegin(GL_QUADS);
+ glTexCoord2f(uv_left, uv_top);
+ glVertex2f(left*ca - top*sa + center_x,
+ left*sa + top*ca + center_y);
+
+ glTexCoord2f(uv_right, uv_top);
+ glVertex2f(right*ca - top*sa + center_x,
+ right*sa + top*ca + center_y);
+
+ glTexCoord2f(uv_right, uv_bottom);
+ glVertex2f(right*ca - bottom*sa + center_x,
+ right*sa + bottom*ca + center_y);
+
+ glTexCoord2f(uv_left, uv_bottom);
+ glVertex2f(left*ca - bottom*sa + center_x,
+ left*sa + bottom*ca + center_y);
+ glEnd();
+
+ // FIXME: find a better way to restore the blend mode
+ glColor4f(1.0f, 1.0f, 1.0f, 1.0f);
+ glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
+ }
+}
+
+namespace GL
+{
+ static inline int next_po2(int val)
+ {
+ int result = 1;
+ while(result < val)
+ result *= 2;
+
+ return result;
+ }
+
+ Lightmap::Lightmap()
+ {
+ screen = SDL_GetVideoSurface();
+
+ lightmap_width = screen->w / LIGHTMAP_DIV;
+ lightmap_height = screen->h / LIGHTMAP_DIV;
+ unsigned int width = next_po2(lightmap_width);
+ unsigned int height = next_po2(lightmap_height);
+
+ lightmap = new Texture(width, height);
+
+ lightmap_uv_right = static_cast<float>(lightmap_width) / static_cast<float>(width);
+ lightmap_uv_bottom = static_cast<float>(lightmap_height) / static_cast<float>(height);
+ texture_manager->register_texture(lightmap);
+ }
+
+ Lightmap::~Lightmap()
+ {
+ texture_manager->remove_texture(lightmap);
+ delete lightmap;
+ }
+
+ void
+ Lightmap::start_draw(const Color &ambient_color)
+ {
+ glViewport(0, screen->h - lightmap_height, lightmap_width, lightmap_height);
+ glMatrixMode(GL_PROJECTION);
+ glLoadIdentity();
+ glOrtho(0, SCREEN_WIDTH, SCREEN_HEIGHT, 0, -1.0, 1.0);
+ glMatrixMode(GL_MODELVIEW);
+ glLoadIdentity();
+
+ glClearColor( ambient_color.red, ambient_color.green, ambient_color.blue, 1 );
+ glClear(GL_COLOR_BUFFER_BIT);
+ }
+
+ void
+ Lightmap::end_draw()
+ {
+ glDisable(GL_BLEND);
+ glBindTexture(GL_TEXTURE_2D, lightmap->get_handle());
+ glCopyTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, 0, screen->h - lightmap_height, lightmap_width, lightmap_height);
+
+ glViewport(0, 0, screen->w, screen->h);
+ glMatrixMode(GL_PROJECTION);
+ glLoadIdentity();
+ glOrtho(0, SCREEN_WIDTH, SCREEN_HEIGHT, 0, -1.0, 1.0);
+ glMatrixMode(GL_MODELVIEW);
+ glLoadIdentity();
+ glEnable(GL_BLEND);
+ //glClear(GL_COLOR_BUFFER_BIT);
+ }
+
+ void
+ Lightmap::do_draw()
+ {
+ const Texture* texture = lightmap;
+
+ // multiple the lightmap with the framebuffer
+ glBlendFunc(GL_DST_COLOR, GL_ZERO);
+
+ glBindTexture(GL_TEXTURE_2D, texture->get_handle());
+ glBegin(GL_QUADS);
+
+ glTexCoord2f(0, lightmap_uv_bottom);
+ glVertex2f(0, 0);
+
+ glTexCoord2f(lightmap_uv_right, lightmap_uv_bottom);
+ glVertex2f(SCREEN_WIDTH, 0);
+
+ glTexCoord2f(lightmap_uv_right, 0);
+ glVertex2f(SCREEN_WIDTH, SCREEN_HEIGHT);
+
+ glTexCoord2f(0, 0);
+ glVertex2f(0, SCREEN_HEIGHT);
+
+ glEnd();
+
+ glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
+ }
+
+ void
+ Lightmap::draw_surface(const DrawingRequest& request)
+ {
+ const Surface* surface = (const Surface*) request.request_data;
+ GL::Texture *gltexture = dynamic_cast<GL::Texture *>(surface->get_texture());
+ GL::SurfaceData *surface_data = reinterpret_cast<GL::SurfaceData *>(surface->get_surface_data());
+
+ glBindTexture(GL_TEXTURE_2D, gltexture->get_handle());
+ intern_draw(request.pos.x, request.pos.y,
+ request.pos.x + surface->get_width(),
+ request.pos.y + surface->get_height(),
+ surface_data->get_uv_left(),
+ surface_data->get_uv_top(),
+ surface_data->get_uv_right(),
+ surface_data->get_uv_bottom(),
+ request.angle,
+ request.alpha,
+ request.color,
+ request.blend,
+ request.drawing_effect);
+ }
+
+ void
+ Lightmap::draw_surface_part(const DrawingRequest& request)
+ {
+ const SurfacePartRequest* surfacepartrequest
+ = (SurfacePartRequest*) request.request_data;
+ const Surface *surface = surfacepartrequest->surface;
+ GL::Texture *gltexture = dynamic_cast<GL::Texture *>(surface->get_texture());
+ GL::SurfaceData *surface_data = reinterpret_cast<GL::SurfaceData *>(surface->get_surface_data());
+
+ float uv_width = surface_data->get_uv_right() - surface_data->get_uv_left();
+ float uv_height = surface_data->get_uv_bottom() - surface_data->get_uv_top();
+
+ float uv_left = surface_data->get_uv_left() + (uv_width * surfacepartrequest->source.x) / surface->get_width();
+ float uv_top = surface_data->get_uv_top() + (uv_height * surfacepartrequest->source.y) / surface->get_height();
+ float uv_right = surface_data->get_uv_left() + (uv_width * (surfacepartrequest->source.x + surfacepartrequest->size.x)) / surface->get_width();
+ float uv_bottom = surface_data->get_uv_top() + (uv_height * (surfacepartrequest->source.y + surfacepartrequest->size.y)) / surface->get_height();
+
+ glBindTexture(GL_TEXTURE_2D, gltexture->get_handle());
+ intern_draw(request.pos.x, request.pos.y,
+ request.pos.x + surfacepartrequest->size.x,
+ request.pos.y + surfacepartrequest->size.y,
+ uv_left,
+ uv_top,
+ uv_right,
+ uv_bottom,
+ 0.0,
+ request.alpha,
+ Color(1.0, 1.0, 1.0),
+ Blend(),
+ request.drawing_effect);
+ }
+
+ void
+ Lightmap::draw_gradient(const DrawingRequest& request)
+ {
+ const GradientRequest* gradientrequest
+ = (GradientRequest*) request.request_data;
+ const Color& top = gradientrequest->top;
+ const Color& bottom = gradientrequest->bottom;
+
+ glDisable(GL_TEXTURE_2D);
+ glBegin(GL_QUADS);
+ glColor4f(top.red, top.green, top.blue, top.alpha);
+ glVertex2f(0, 0);
+ glVertex2f(SCREEN_WIDTH, 0);
+ glColor4f(bottom.red, bottom.green, bottom.blue, bottom.alpha);
+ glVertex2f(SCREEN_WIDTH, SCREEN_HEIGHT);
+ glVertex2f(0, SCREEN_HEIGHT);
+ glEnd();
+ glEnable(GL_TEXTURE_2D);
+ glColor4f(1, 1, 1, 1);
+ }
+
+ void
+ Lightmap::draw_filled_rect(const DrawingRequest& request)
+ {
+ const 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;
+
+ glDisable(GL_TEXTURE_2D);
+ glColor4f(fillrectrequest->color.red, fillrectrequest->color.green,
+ fillrectrequest->color.blue, fillrectrequest->color.alpha);
+
+ glBegin(GL_QUADS);
+ glVertex2f(x, y);
+ glVertex2f(x+w, y);
+ glVertex2f(x+w, y+h);
+ glVertex2f(x, y+h);
+ glEnd();
+ glEnable(GL_TEXTURE_2D);
+ glColor4f(1, 1, 1, 1);
+ }
+
+ void
+ Lightmap::get_light(const DrawingRequest& request) const
+ {
+ const GetLightRequest* getlightrequest
+ = (GetLightRequest*) request.request_data;
+
+ float pixels[3];
+ for( int i = 0; i<3; i++)
+ pixels[i] = 0.0f; //set to black
+
+ float posX = request.pos.x * lightmap_width / SCREEN_WIDTH;
+ float posY = screen->h - request.pos.y * lightmap_height / SCREEN_HEIGHT;
+ glReadPixels((GLint) posX, (GLint) posY , 1, 1, GL_RGB, GL_FLOAT, pixels);
+ *(getlightrequest->color_ptr) = Color( pixels[0], pixels[1], pixels[2]);
+ //printf("get_light %f/%f =>%f/%f r%f g%f b%f\n", request.pos.x, request.pos.y, posX, posY, pixels[0], pixels[1], pixels[2]);
+ }
+}
+
+#endif
--- /dev/null
+// $Id: gl_lightmap.hpp 4986 2007-04-16 17:48:28Z matzeb $
+//
+// SuperTux
+// Copyright (C) 2006 Matthias Braun <matze@braunis.de>
+//
+// 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 <config.h>
+
+#ifdef HAVE_OPENGL
+
+#ifndef SUPERTUX_GL_LIGHTMAP_H
+#define SUPERTUX_GL_LIGHTMAP_H
+
+#include <SDL_video.h>
+
+#include "lightmap.hpp"
+
+struct DrawingRequest;
+
+namespace GL
+{
+ class Texture;
+ class Lightmap : public ::Lightmap
+ {
+ public:
+ Lightmap();
+ ~Lightmap();
+
+ void start_draw(const Color &ambient_color);
+ void end_draw();
+ void do_draw();
+ void draw_surface(const DrawingRequest& request);
+ void draw_surface_part(const DrawingRequest& request);
+ void draw_text(const DrawingRequest& request);
+ void draw_gradient(const DrawingRequest& request);
+ void draw_filled_rect(const DrawingRequest& request);
+ void get_light(const DrawingRequest& request) const;
+
+ private:
+ static const int LIGHTMAP_DIV = 5;
+
+ SDL_Surface* screen;
+ Texture* lightmap;
+ int lightmap_width, lightmap_height;
+ float lightmap_uv_right, lightmap_uv_bottom;
+ };
+}
+
+#endif
+
+#endif
--- /dev/null
+// $Id: gl_renderer.cpp 5063 2007-05-27 11:32:00Z matzeb $
+//
+// SuperTux
+// Copyright (C) 2006 Matthias Braun <matze@braunis.de>
+//
+// 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 <config.h>
+
+#ifdef HAVE_OPENGL
+
+#include <functional>
+#include <algorithm>
+#include <cassert>
+#include <math.h>
+#include <iostream>
+#include <SDL_image.h>
+#include <sstream>
+#include <iomanip>
+#include <physfs.h>
+
+#include "glutil.hpp"
+#include "gl_renderer.hpp"
+#include "gl_texture.hpp"
+#include "gl_surface_data.hpp"
+#include "drawing_context.hpp"
+#include "drawing_request.hpp"
+#include "surface.hpp"
+#include "font.hpp"
+#include "main.hpp"
+#include "gameconfig.hpp"
+#include "texture.hpp"
+#include "texture_manager.hpp"
+#include "obstack/obstackpp.hpp"
+#define LIGHTMAP_DIV 5
+
+namespace
+{
+ inline void intern_draw(float left, float top, float right, float bottom,
+ float uv_left, float uv_top,
+ float uv_right, float uv_bottom,
+ float angle, float alpha,
+ const Color& color,
+ const Blend& blend,
+ DrawingEffect effect)
+ {
+ if(effect & HORIZONTAL_FLIP)
+ std::swap(uv_left, uv_right);
+ if(effect & VERTICAL_FLIP) {
+ std::swap(uv_top, uv_bottom);
+ }
+
+ float center_x = (left + right) / 2;
+ float center_y = (top + bottom) / 2;
+
+ float sa = sinf(angle/180.0f*M_PI);
+ float ca = cosf(angle/180.0f*M_PI);
+
+ left -= center_x;
+ right -= center_x;
+
+ top -= center_y;
+ bottom -= center_y;
+
+ glBlendFunc(blend.sfactor, blend.dfactor);
+ glColor4f(color.red, color.green, color.blue, color.alpha * alpha);
+ glBegin(GL_QUADS);
+ glTexCoord2f(uv_left, uv_top);
+ glVertex2f(left*ca - top*sa + center_x,
+ left*sa + top*ca + center_y);
+
+ glTexCoord2f(uv_right, uv_top);
+ glVertex2f(right*ca - top*sa + center_x,
+ right*sa + top*ca + center_y);
+
+ glTexCoord2f(uv_right, uv_bottom);
+ glVertex2f(right*ca - bottom*sa + center_x,
+ right*sa + bottom*ca + center_y);
+
+ glTexCoord2f(uv_left, uv_bottom);
+ glVertex2f(left*ca - bottom*sa + center_x,
+ left*sa + bottom*ca + center_y);
+ glEnd();
+
+ // FIXME: find a better way to restore the blend mode
+ glColor4f(1.0f, 1.0f, 1.0f, 1.0f);
+ glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
+ }
+}
+
+namespace GL
+{
+ Renderer::Renderer()
+ {
+ if(texture_manager != 0)
+ texture_manager->save_textures();
+
+ if(config->try_vsync) {
+ /* we want vsync for smooth scrolling */
+ SDL_GL_SetAttribute(SDL_GL_SWAP_CONTROL, 1);
+ }
+
+ SDL_GL_SetAttribute(SDL_GL_DOUBLEBUFFER, 1);
+ SDL_GL_SetAttribute(SDL_GL_RED_SIZE, 5);
+ SDL_GL_SetAttribute(SDL_GL_GREEN_SIZE, 5);
+ SDL_GL_SetAttribute(SDL_GL_BLUE_SIZE, 5);
+
+ int flags = SDL_OPENGL;
+ if(config->use_fullscreen)
+ flags |= SDL_FULLSCREEN;
+ int width = config->screenwidth;
+ int height = config->screenheight;
+ int bpp = 0;
+
+ SDL_Surface *screen = SDL_SetVideoMode(width, height, bpp, flags);
+ if(screen == 0) {
+ std::stringstream msg;
+ msg << "Couldn't set video mode (" << width << "x" << height
+ << "-" << bpp << "bpp): " << SDL_GetError();
+ throw std::runtime_error(msg.str());
+ }
+
+ // setup opengl state and transform
+ glDisable(GL_DEPTH_TEST);
+ glDisable(GL_CULL_FACE);
+ glEnable(GL_TEXTURE_2D);
+ glEnable(GL_BLEND);
+ glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
+
+ glViewport(0, 0, screen->w, screen->h);
+ glMatrixMode(GL_PROJECTION);
+ glLoadIdentity();
+ // logical resolution here not real monitor resolution
+ glOrtho(0, SCREEN_WIDTH, SCREEN_HEIGHT, 0, -1.0, 1.0);
+ glMatrixMode(GL_MODELVIEW);
+ glLoadIdentity();
+ glTranslatef(0, 0, 0);
+
+ check_gl_error("Setting up view matrices");
+
+
+ if(texture_manager == 0)
+ texture_manager = new TextureManager();
+ else
+ texture_manager->reload_textures();
+ }
+
+ Renderer::~Renderer()
+ {
+ }
+
+ void
+ Renderer::draw_surface(const DrawingRequest& request)
+ {
+ const Surface* surface = (const Surface*) request.request_data;
+ GL::Texture *gltexture = dynamic_cast<GL::Texture *>(surface->get_texture());
+ GL::SurfaceData *surface_data = reinterpret_cast<GL::SurfaceData *>(surface->get_surface_data());
+
+ glBindTexture(GL_TEXTURE_2D, gltexture->get_handle());
+ intern_draw(request.pos.x, request.pos.y,
+ request.pos.x + surface->get_width(),
+ request.pos.y + surface->get_height(),
+ surface_data->get_uv_left(),
+ surface_data->get_uv_top(),
+ surface_data->get_uv_right(),
+ surface_data->get_uv_bottom(),
+ request.angle,
+ request.alpha,
+ request.color,
+ request.blend,
+ request.drawing_effect);
+ }
+
+ void
+ Renderer::draw_surface_part(const DrawingRequest& request)
+ {
+ const SurfacePartRequest* surfacepartrequest
+ = (SurfacePartRequest*) request.request_data;
+ const Surface *surface = surfacepartrequest->surface;
+ GL::Texture *gltexture = dynamic_cast<GL::Texture *>(surface->get_texture());
+ GL::SurfaceData *surface_data = reinterpret_cast<GL::SurfaceData *>(surface->get_surface_data());
+
+ float uv_width = surface_data->get_uv_right() - surface_data->get_uv_left();
+ float uv_height = surface_data->get_uv_bottom() - surface_data->get_uv_top();
+
+ float uv_left = surface_data->get_uv_left() + (uv_width * surfacepartrequest->source.x) / surface->get_width();
+ float uv_top = surface_data->get_uv_top() + (uv_height * surfacepartrequest->source.y) / surface->get_height();
+ float uv_right = surface_data->get_uv_left() + (uv_width * (surfacepartrequest->source.x + surfacepartrequest->size.x)) / surface->get_width();
+ float uv_bottom = surface_data->get_uv_top() + (uv_height * (surfacepartrequest->source.y + surfacepartrequest->size.y)) / surface->get_height();
+
+ glBindTexture(GL_TEXTURE_2D, gltexture->get_handle());
+ intern_draw(request.pos.x, request.pos.y,
+ request.pos.x + surfacepartrequest->size.x,
+ request.pos.y + surfacepartrequest->size.y,
+ uv_left,
+ uv_top,
+ uv_right,
+ uv_bottom,
+ 0.0,
+ request.alpha,
+ Color(1.0, 1.0, 1.0),
+ Blend(),
+ request.drawing_effect);
+ }
+
+ void
+ Renderer::draw_gradient(const DrawingRequest& request)
+ {
+ const GradientRequest* gradientrequest
+ = (GradientRequest*) request.request_data;
+ const Color& top = gradientrequest->top;
+ const Color& bottom = gradientrequest->bottom;
+
+ glDisable(GL_TEXTURE_2D);
+ glBegin(GL_QUADS);
+ glColor4f(top.red, top.green, top.blue, top.alpha);
+ glVertex2f(0, 0);
+ glVertex2f(SCREEN_WIDTH, 0);
+ glColor4f(bottom.red, bottom.green, bottom.blue, bottom.alpha);
+ glVertex2f(SCREEN_WIDTH, SCREEN_HEIGHT);
+ glVertex2f(0, SCREEN_HEIGHT);
+ glEnd();
+ glEnable(GL_TEXTURE_2D);
+ glColor4f(1, 1, 1, 1);
+ }
+
+ void
+ Renderer::draw_filled_rect(const DrawingRequest& request)
+ {
+ const 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;
+
+ glDisable(GL_TEXTURE_2D);
+ glColor4f(fillrectrequest->color.red, fillrectrequest->color.green,
+ fillrectrequest->color.blue, fillrectrequest->color.alpha);
+
+ glBegin(GL_QUADS);
+ glVertex2f(x, y);
+ glVertex2f(x+w, y);
+ glVertex2f(x+w, y+h);
+ glVertex2f(x, y+h);
+ glEnd();
+ glEnable(GL_TEXTURE_2D);
+ glColor4f(1, 1, 1, 1);
+ }
+
+ void
+ Renderer::do_take_screenshot()
+ {
+ // [Christoph] TODO: Yes, this method also takes care of the actual disk I/O. Split it?
+
+ SDL_Surface *shot_surf;
+ // create surface to hold screenshot
+ #if SDL_BYTEORDER == SDL_BIG_ENDIAN
+ shot_surf = SDL_CreateRGBSurface(SDL_SWSURFACE, SCREEN_WIDTH, SCREEN_HEIGHT, 24, 0x00FF0000, 0x0000FF00, 0x000000FF, 0);
+ #else
+ shot_surf = SDL_CreateRGBSurface(SDL_SWSURFACE, SCREEN_WIDTH, SCREEN_HEIGHT, 24, 0x000000FF, 0x0000FF00, 0x00FF0000, 0);
+ #endif
+ if (!shot_surf) {
+ log_warning << "Could not create RGB Surface to contain screenshot" << std::endl;
+ return;
+ }
+
+ // read pixels into array
+ char* pixels = new char[3 * SCREEN_WIDTH * SCREEN_HEIGHT];
+ if (!pixels) {
+ log_warning << "Could not allocate memory to store screenshot" << std::endl;
+ SDL_FreeSurface(shot_surf);
+ return;
+ }
+ glReadPixels(0, 0, SCREEN_WIDTH, SCREEN_HEIGHT, GL_RGB, GL_UNSIGNED_BYTE, pixels);
+
+ // copy array line-by-line
+ for (int i = 0; i < SCREEN_HEIGHT; i++) {
+ char* src = pixels + (3 * SCREEN_WIDTH * (SCREEN_HEIGHT - i - 1));
+ if(SDL_MUSTLOCK(shot_surf))
+ {
+ SDL_LockSurface(shot_surf);
+ }
+ char* dst = ((char*)shot_surf->pixels) + i * shot_surf->pitch;
+ memcpy(dst, src, 3 * SCREEN_WIDTH);
+ if(SDL_MUSTLOCK(shot_surf))
+ {
+ SDL_UnlockSurface(shot_surf);
+ }
+ }
+
+ // free array
+ delete[](pixels);
+
+ // save screenshot
+ static const std::string writeDir = PHYSFS_getWriteDir();
+ static const std::string dirSep = PHYSFS_getDirSeparator();
+ static const std::string baseName = "screenshot";
+ static const std::string fileExt = ".bmp";
+ std::string fullFilename;
+ for (int num = 0; num < 1000; num++) {
+ std::ostringstream oss;
+ oss << baseName;
+ oss << std::setw(3) << std::setfill('0') << num;
+ oss << fileExt;
+ std::string fileName = oss.str();
+ fullFilename = writeDir + dirSep + fileName;
+ if (!PHYSFS_exists(fileName.c_str())) {
+ SDL_SaveBMP(shot_surf, fullFilename.c_str());
+ log_debug << "Wrote screenshot to \"" << fullFilename << "\"" << std::endl;
+ SDL_FreeSurface(shot_surf);
+ return;
+ }
+ }
+ log_warning << "Did not save screenshot, because all files up to \"" << fullFilename << "\" already existed" << std::endl;
+ SDL_FreeSurface(shot_surf);
+ }
+
+ void
+ Renderer::flip()
+ {
+ assert_gl("drawing");
+ SDL_GL_SwapBuffers();
+ }
+}
+
+#endif
--- /dev/null
+// $Id: gl_renderer.hpp 4986 2007-04-16 17:48:28Z matzeb $
+//
+// SuperTux
+// Copyright (C) 2006 Matthias Braun <matze@braunis.de>
+//
+// 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 <config.h>
+
+#ifdef HAVE_OPENGL
+
+#ifndef SUPERTUX_GL_RENDERER_H
+#define SUPERTUX_GL_RENDERER_H
+
+#include "renderer.hpp"
+
+namespace GL
+{
+ class Renderer : public ::Renderer
+ {
+ public:
+ Renderer();
+ ~Renderer();
+
+ void draw_surface(const DrawingRequest& request);
+ void draw_surface_part(const DrawingRequest& request);
+ void draw_text(const DrawingRequest& request);
+ void draw_gradient(const DrawingRequest& request);
+ void draw_filled_rect(const DrawingRequest& request);
+ void do_take_screenshot();
+ void flip();
+ };
+}
+
+#endif
+
+#endif
--- /dev/null
+// $Id: gl_surface_data.hpp 4063 2006-07-21 21:05:23Z anmaster $
+//
+// SuperTux
+// Copyright (C) 2006 Matthias Braun <matze@braunis.de>
+//
+// 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 <config.h>
+
+#ifdef HAVE_OPENGL
+
+#ifndef __GL_SURFACE_DATA_HPP__
+#define __GL_SURFACE_DATA_HPP__
+
+#include "surface.hpp"
+
+namespace GL
+{
+ class SurfaceData
+ {
+ private:
+ const Surface &surface;
+ float uv_left;
+ float uv_top;
+ float uv_right;
+ float uv_bottom;
+
+ public:
+ SurfaceData(const Surface &surface) :
+ surface(surface)
+ {
+ uv_left = (float) surface.get_x() / surface.get_texture()->get_texture_width();
+ uv_top = (float) surface.get_y() / surface.get_texture()->get_texture_height();
+ uv_right = (float) (surface.get_x() + surface.get_width()) / surface.get_texture()->get_texture_width();
+ uv_bottom = (float) (surface.get_y() + surface.get_height()) / surface.get_texture()->get_texture_height();
+ }
+
+ float get_uv_left() const
+ {
+ return surface.get_flipx() ? uv_right : uv_left;
+ }
+
+ float get_uv_top() const
+ {
+ return uv_top;
+ }
+
+ float get_uv_right() const
+ {
+ return surface.get_flipx() ? uv_left : uv_right;
+ }
+
+ float get_uv_bottom() const
+ {
+ return uv_bottom;
+ }
+ };
+}
+
+#endif
+
+#endif
--- /dev/null
+// $Id: gl_texture.cpp 4063 2006-07-21 21:05:23Z anmaster $
+//
+// SuperTux
+// Copyright (C) 2006 Matthias Braun <matze@braunis.de>
+//
+// 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 <config.h>
+
+#ifdef HAVE_OPENGL
+
+#include "gl_texture.hpp"
+#include "gameconfig.hpp"
+#include "glutil.hpp"
+#include "log.hpp"
+
+#include <assert.h>
+#include <stdexcept>
+
+namespace
+{
+ inline bool is_power_of_2(int v)
+ {
+ return (v & (v-1)) == 0;
+ }
+
+ inline int next_power_of_two(int val)
+ {
+ int result = 1;
+ while(result < val)
+ result *= 2;
+ return result;
+ }
+}
+
+namespace GL
+{
+ Texture::Texture(unsigned int width, unsigned int height)
+ {
+ assert(is_power_of_2(width));
+ assert(is_power_of_2(height));
+ texture_width = width;
+ texture_height = height;
+ image_width = width;
+ image_height = height;
+
+ assert_gl("before creating texture");
+ glGenTextures(1, &handle);
+
+ try {
+ glBindTexture(GL_TEXTURE_2D, handle);
+
+ glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, texture_width,
+ texture_height, 0, GL_RGBA, GL_UNSIGNED_BYTE, 0);
+
+ set_texture_params();
+ } catch(...) {
+ glDeleteTextures(1, &handle);
+ throw;
+ }
+ }
+
+ Texture::Texture(SDL_Surface* image)
+ {
+ texture_width = next_power_of_two(image->w);
+ texture_height = next_power_of_two(image->h);
+ image_width = image->w;
+ image_height = image->h;
+
+#if SDL_BYTEORDER == SDL_BIG_ENDIAN
+ SDL_Surface* convert = SDL_CreateRGBSurface(SDL_SWSURFACE,
+ texture_width, texture_height, 32,
+ 0xff000000, 0x00ff0000, 0x0000ff00, 0x000000ff);
+#else
+ SDL_Surface* convert = SDL_CreateRGBSurface(SDL_SWSURFACE,
+ texture_width, texture_height, 32,
+ 0x000000ff, 0x0000ff00, 0x00ff0000, 0xff000000);
+#endif
+
+ if(convert == 0) {
+ throw std::runtime_error("Couldn't create texture: out of memory");
+ }
+
+ SDL_SetAlpha(image, 0, 0);
+ SDL_BlitSurface(image, 0, convert, 0);
+
+ assert_gl("before creating texture");
+ glGenTextures(1, &handle);
+
+ try {
+ GLenum sdl_format;
+ if(convert->format->BytesPerPixel == 3)
+ sdl_format = GL_RGB;
+ else if(convert->format->BytesPerPixel == 4)
+ sdl_format = GL_RGBA;
+ else
+ assert(false);
+
+ glBindTexture(GL_TEXTURE_2D, handle);
+ glPixelStorei(GL_UNPACK_ALIGNMENT, 1);
+ glPixelStorei(GL_UNPACK_ROW_LENGTH, convert->pitch/convert->format->BytesPerPixel);
+ if(SDL_MUSTLOCK(convert))
+ {
+ SDL_LockSurface(convert);
+ }
+ glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, texture_width,
+ texture_height, 0, sdl_format,
+ GL_UNSIGNED_BYTE, convert->pixels);
+ if(SDL_MUSTLOCK(convert))
+ {
+ SDL_UnlockSurface(convert);
+ }
+
+ assert_gl("creating texture");
+
+ set_texture_params();
+ } catch(...) {
+ glDeleteTextures(1, &handle);
+ SDL_FreeSurface(convert);
+ throw;
+ }
+ SDL_FreeSurface(convert);
+ }
+
+ Texture::~Texture()
+ {
+ glDeleteTextures(1, &handle);
+ }
+
+ void
+ Texture::set_texture_params()
+ {
+ 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);
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP);
+
+ assert_gl("set texture params");
+ }
+}
+
+#endif
--- /dev/null
+// $Id: gl_texture.hpp 4063 2006-07-21 21:05:23Z anmaster $
+//
+// SuperTux
+// Copyright (C) 2006 Matthias Braun <matze@braunis.de>
+//
+// 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 <config.h>
+
+#ifdef HAVE_OPENGL
+
+#ifndef __GL_TEXTURE_HPP__
+#define __GL_TEXTURE_HPP__
+
+#include <SDL.h>
+
+#include "texture.hpp"
+#include "glutil.hpp"
+
+/**
+ * This class is a wrapper around a texture handle. It stores the texture width
+ * and height and provides convenience functions for uploading SDL_Surfaces
+ * into the texture
+ */
+namespace GL
+{
+ class Texture : public ::Texture
+ {
+ protected:
+ GLuint handle;
+ unsigned int texture_width;
+ unsigned int texture_height;
+ unsigned int image_width;
+ unsigned int image_height;
+
+ public:
+ Texture(unsigned int width, unsigned int height);
+ Texture(SDL_Surface* image);
+ ~Texture();
+
+ const GLuint &get_handle() const {
+ return handle;
+ }
+
+ void set_handle(GLuint handle) {
+ this->handle = handle;
+ }
+
+ unsigned int get_texture_width() const
+ {
+ return texture_width;
+ }
+
+ unsigned int get_texture_height() const
+ {
+ return texture_height;
+ }
+
+ unsigned int get_image_width() const
+ {
+ return image_width;
+ }
+
+ unsigned int get_image_height() const
+ {
+ return image_height;
+ }
+
+ void set_image_width(unsigned int width)
+ {
+ image_width = width;
+ }
+
+ void set_image_height(unsigned int height)
+ {
+ image_height = height;
+ }
+
+ private:
+ void set_texture_params();
+ };
+}
+
+#endif
+
+#endif
--- /dev/null
+// $Id$
+//
+// SuperTux
+// Copyright (C) 2006 Matthias Braun <matze@braunis.de>
+//
+// 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 __GLUTIL_HPP__
+#define __GLUTIL_HPP__
+
+#include <config.h>
+
+#ifdef HAVE_OPENGL
+
+#include <sstream>
+#include <stdexcept>
+
+#ifndef MACOSX
+#include <GL/gl.h>
+#include <GL/glext.h>
+#else
+#include <OpenGL/gl.h>
+#include <OpenGL/glext.h>
+#endif
+
+static inline void check_gl_error(const char* message)
+{
+#ifdef DEBUG
+ GLenum error = glGetError();
+ if(error != GL_NO_ERROR) {
+ std::ostringstream msg;
+ msg << "OpenGLError while '" << message << "': ";
+ switch(error) {
+ case GL_INVALID_ENUM:
+ msg << "INVALID_ENUM: An unacceptable value is specified for an "
+ "enumerated argument.";
+ break;
+ case GL_INVALID_VALUE:
+ msg << "INVALID_VALUE: A numeric argument is out of range.";
+ break;
+ case GL_INVALID_OPERATION:
+ msg << "INVALID_OPERATION: The specified operation is not allowed "
+ "in the current state.";
+ break;
+ case GL_STACK_OVERFLOW:
+ msg << "STACK_OVERFLOW: This command would cause a stack overflow.";
+ break;
+ case GL_STACK_UNDERFLOW:
+ msg << "STACK_UNDERFLOW: This command would cause a stack underflow.";
+ break;
+ case GL_OUT_OF_MEMORY:
+ msg << "OUT_OF_MEMORY: There is not enough memory left to execute the "
+ "command.";
+ break;
+#ifdef GL_TABLE_TOO_LARGE
+ case GL_TABLE_TOO_LARGE:
+ msg << "TABLE_TOO_LARGE: table is too large";
+ break;
+#endif
+ default:
+ msg << "Unknown error (code " << error << ")";
+ }
+
+ throw std::runtime_error(msg.str());
+ }
+#else
+ (void) message;
+#endif
+}
+
+static inline void assert_gl(const char* message)
+{
+#ifdef DEBUG
+ check_gl_error(message);
+#else
+ (void) message;
+#endif
+}
+
+#else
+
+#define GLenum int
+#define GLint int
+#define GL_SRC_ALPHA 0
+#define GL_ONE_MINUS_SRC_ALPHA 1
+#define GL_RGBA 2
+#define GL_ONE 3
+
+#endif
+
+#endif
--- /dev/null
+// $Id: lightmap.hpp 4986 2007-04-16 17:48:28Z matzeb $
+//
+// SuperTux
+// Copyright (C) 2006 Matthias Braun <matze@braunis.de>
+//
+// 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_LIGHTMAP_H
+#define SUPERTUX_LIGHTMAP_H
+
+#include <vector>
+#include <string>
+#include <memory>
+
+#include <stdint.h>
+
+#include <SDL_video.h>
+
+#include "glutil.hpp"
+#include "obstack/obstack.h"
+#include "math/vector.hpp"
+#include "math/rect.hpp"
+#include "drawing_request.hpp"
+#include "surface.hpp"
+#include "font.hpp"
+#include "color.hpp"
+
+class Texture;
+struct DrawingRequest;
+
+class Lightmap
+{
+public:
+ virtual ~Lightmap() {}
+
+ virtual void start_draw(const Color &ambient_color) = 0;
+ virtual void end_draw() = 0;
+ virtual void do_draw() = 0;
+ virtual void draw_surface(const DrawingRequest& request) = 0;
+ virtual void draw_surface_part(const DrawingRequest& request) = 0;
+ virtual void draw_gradient(const DrawingRequest& request) = 0;
+ virtual void draw_filled_rect(const DrawingRequest& request) = 0;
+ virtual void get_light(const DrawingRequest& request) const = 0;
+};
+
+#endif
+
--- /dev/null
+// $Id: drawing_context.hpp 4986 2007-04-16 17:48:28Z matzeb $
+//
+// SuperTux
+// Copyright (C) 2006 Matthias Braun <matze@braunis.de>
+//
+// 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_RENDERER_H
+#define SUPERTUX_RENDERER_H
+
+#include <vector>
+#include <string>
+#include <memory>
+
+#include <stdint.h>
+
+#include <SDL_video.h>
+
+#include "glutil.hpp"
+#include "obstack/obstack.h"
+#include "math/vector.hpp"
+#include "math/rect.hpp"
+#include "surface.hpp"
+#include "font.hpp"
+#include "color.hpp"
+
+class Surface;
+class Texture;
+struct DrawingRequest;
+
+class Renderer
+{
+public:
+ virtual ~Renderer() {}
+
+ virtual void draw_surface(const DrawingRequest& request) = 0;
+ virtual void draw_surface_part(const DrawingRequest& request) = 0;
+ virtual void draw_gradient(const DrawingRequest& request) = 0;
+ virtual void draw_filled_rect(const DrawingRequest& request)= 0;
+ virtual void do_take_screenshot() = 0;
+ virtual void flip() = 0;
+};
+
+#endif
+
--- /dev/null
+// $Id: sdl_lightmap.cpp 5063 2007-05-27 11:32:00Z matzeb $
+//
+// SuperTux
+// Copyright (C) 2006 Matthias Braun <matze@braunis.de>
+//
+// 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 <config.h>
+
+#include <functional>
+#include <algorithm>
+#include <cassert>
+#include <iostream>
+#include <SDL_image.h>
+#include <sstream>
+#include <iomanip>
+#include <physfs.h>
+
+#include "glutil.hpp"
+#include "sdl_lightmap.hpp"
+#include "sdl_texture.hpp"
+#include "sdl_surface_data.hpp"
+#include "drawing_context.hpp"
+#include "drawing_request.hpp"
+#include "renderer.hpp"
+#include "surface.hpp"
+#include "font.hpp"
+#include "main.hpp"
+#include "gameconfig.hpp"
+#include "texture.hpp"
+#include "texture_manager.hpp"
+#include "obstack/obstackpp.hpp"
+
+namespace SDL
+{
+ Lightmap::Lightmap()
+ {
+ screen = SDL_GetVideoSurface();
+
+ float xfactor = (float) config->screenwidth / SCREEN_WIDTH;
+ float yfactor = (float) config->screenheight / SCREEN_HEIGHT;
+ if(xfactor < yfactor)
+ {
+ numerator = config->screenwidth;
+ denominator = SCREEN_WIDTH;
+ }
+ else
+ {
+ numerator = config->screenheight;
+ denominator = SCREEN_HEIGHT;
+ }
+
+ LIGHTMAP_DIV = 8 * numerator / denominator;
+
+ width = screen->w / LIGHTMAP_DIV;
+ height = screen->h / LIGHTMAP_DIV;
+
+ red_channel = (Uint8 *)malloc(width * height * sizeof(Uint8));
+ green_channel = (Uint8 *)malloc(width * height * sizeof(Uint8));
+ blue_channel = (Uint8 *)malloc(width * height * sizeof(Uint8));
+ }
+
+ Lightmap::~Lightmap()
+ {
+ free(red_channel);
+ free(green_channel);
+ free(blue_channel);
+ }
+
+ void
+ Lightmap::start_draw(const Color &ambient_color)
+ {
+ memset(red_channel, (Uint8) (ambient_color.red * 255), width * height * sizeof(Uint8));
+ memset(green_channel, (Uint8) (ambient_color.green * 255), width * height * sizeof(Uint8));
+ memset(blue_channel, (Uint8) (ambient_color.blue * 255), width * height * sizeof(Uint8));
+ }
+
+ void
+ Lightmap::end_draw()
+ {
+ }
+
+//#define BILINEAR
+
+#ifdef BILINEAR
+ namespace
+ {
+ void merge(Uint8 color[3], Uint8 color0[3], Uint8 color1[3], int rem, int total)
+ {
+ color[0] = (color0[0] * (total - rem) + color1[0] * rem) / total;
+ color[1] = (color0[1] * (total - rem) + color1[1] * rem) / total;
+ color[2] = (color0[2] * (total - rem) + color1[2] * rem) / total;
+ }
+ }
+#endif
+
+ void
+ Lightmap::do_draw()
+ {
+ // FIXME: This is really slow
+ if(LIGHTMAP_DIV == 1)
+ {
+ int bpp = screen->format->BytesPerPixel;
+ if(SDL_MUSTLOCK(screen))
+ {
+ SDL_LockSurface(screen);
+ }
+ Uint8 *pixel = (Uint8 *) screen->pixels;
+ int loc = 0;
+ for(int y = 0;y < height;y++) {
+ for(int x = 0;x < width;x++, pixel += bpp, loc++) {
+ if(red_channel[loc] == 0xff && green_channel[loc] == 0xff && blue_channel[loc] == 0xff)
+ {
+ continue;
+ }
+ Uint32 mapped = 0;
+ switch(bpp) {
+ case 1:
+ mapped = *pixel;
+ break;
+ case 2:
+ mapped = *(Uint16 *)pixel;
+ break;
+ case 3:
+#if SDL_BYTEORDER == SDL_BIG_ENDIAN
+ mapped |= pixel[0] << 16;
+ mapped |= pixel[1] << 8;
+ mapped |= pixel[2] << 0;
+#else
+ mapped |= pixel[0] << 0;
+ mapped |= pixel[1] << 8;
+ mapped |= pixel[2] << 16;
+#endif
+ break;
+ case 4:
+ mapped = *(Uint32 *)pixel;
+ break;
+ }
+ Uint8 red, green, blue, alpha;
+ SDL_GetRGBA(mapped, screen->format, &red, &green, &blue, &alpha);
+ red = (red * red_channel[loc]) >> 8;
+ green = (green * green_channel[loc]) >> 8;
+ blue = (blue * blue_channel[loc]) >> 8;
+ mapped = SDL_MapRGBA(screen->format, red, green, blue, alpha);
+ switch(bpp) {
+ case 1:
+ *pixel = mapped;
+ break;
+ case 2:
+ *(Uint16 *)pixel = mapped;
+ break;
+ case 3:
+#if SDL_BYTEORDER == SDL_BIG_ENDIAN
+ pixel[0] = (mapped >> 16) & 0xff;
+ pixel[1] = (mapped >> 8) & 0xff;
+ pixel[2] = (mapped >> 0) & 0xff;
+#else
+ pixel[0] = (mapped >> 0) & 0xff;
+ pixel[1] = (mapped >> 8) & 0xff;
+ pixel[2] = (mapped >> 16) & 0xff;
+#endif
+ break;
+ case 4:
+ *(Uint32 *)pixel = mapped;
+ break;
+ }
+ }
+ pixel += screen->pitch - width * bpp;
+ }
+ if(SDL_MUSTLOCK(screen))
+ {
+ SDL_UnlockSurface(screen);
+ }
+ }
+ else
+ {
+ int bpp = screen->format->BytesPerPixel;
+ if(SDL_MUSTLOCK(screen))
+ {
+ SDL_LockSurface(screen);
+ }
+ Uint8 *div_pixel = (Uint8 *) screen->pixels;
+ int loc = 0;
+ for(int y = 0;y < height;y++) {
+ for(int x = 0;x < width;x++, div_pixel += bpp * LIGHTMAP_DIV, loc++) {
+ if(red_channel[loc] == 0xff && green_channel[loc] == 0xff && blue_channel[loc] == 0xff)
+ {
+ continue;
+ }
+ Uint8 *pixel = div_pixel;
+ for(int div_y = 0;div_y < LIGHTMAP_DIV;div_y++) {
+ for(int div_x = 0;div_x < LIGHTMAP_DIV;pixel += bpp, div_x++) {
+ Uint32 mapped = 0;
+ switch(bpp) {
+ case 1:
+ mapped = *pixel;
+ break;
+ case 2:
+ mapped = *(Uint16 *)pixel;
+ break;
+ case 3:
+#if SDL_BYTEORDER == SDL_BIG_ENDIAN
+ mapped |= pixel[0] << 16;
+ mapped |= pixel[1] << 8;
+ mapped |= pixel[2] << 0;
+#else
+ mapped |= pixel[0] << 0;
+ mapped |= pixel[1] << 8;
+ mapped |= pixel[2] << 16;
+#endif
+ break;
+ case 4:
+ mapped = *(Uint32 *)pixel;
+ break;
+ }
+ Uint8 red, green, blue, alpha;
+ SDL_GetRGBA(mapped, screen->format, &red, &green, &blue, &alpha);
+
+#ifdef BILINEAR
+ int xinc = (x + 1 != width ? 1 : 0);
+ int yinc = (y + 1 != height ? width : 0);
+ Uint8 color00[3], color01[3], color10[3], color11[3];
+ {
+ color00[0] = red_channel[loc];
+ color00[1] = green_channel[loc];
+ color00[2] = blue_channel[loc];
+ }
+ {
+ color01[0] = red_channel[loc + xinc];
+ color01[1] = green_channel[loc + xinc];
+ color01[2] = blue_channel[loc + xinc];
+ }
+ {
+ color10[0] = red_channel[loc + yinc];
+ color10[1] = green_channel[loc + yinc];
+ color10[2] = blue_channel[loc + yinc];
+ }
+ {
+ color11[0] = red_channel[loc + yinc + xinc];
+ color11[1] = green_channel[loc + yinc + xinc];
+ color11[2] = blue_channel[loc + yinc + xinc];
+ }
+ Uint8 color0[3], color1[3], color[3];
+ merge(color0, color00, color01, div_x, LIGHTMAP_DIV);
+ merge(color1, color10, color11, div_x, LIGHTMAP_DIV);
+ merge(color, color0, color1, div_y, LIGHTMAP_DIV);
+ red = (red * color[0]) >> 8;
+ green = (green * color[1]) >> 8;
+ blue = (blue * color[2]) >> 8;
+#else
+ red = (red * red_channel[loc]) >> 8;
+ green = (green * green_channel[loc]) >> 8;
+ blue = (blue * blue_channel[loc]) >> 8;
+#endif
+
+ mapped = SDL_MapRGBA(screen->format, red, green, blue, alpha);
+ switch(bpp) {
+ case 1:
+ *pixel = mapped;
+ break;
+ case 2:
+ *(Uint16 *)pixel = mapped;
+ break;
+ case 3:
+#if SDL_BYTEORDER == SDL_BIG_ENDIAN
+ pixel[0] = (mapped >> 16) & 0xff;
+ pixel[1] = (mapped >> 8) & 0xff;
+ pixel[2] = (mapped >> 0) & 0xff;
+#else
+ pixel[0] = (mapped >> 0) & 0xff;
+ pixel[1] = (mapped >> 8) & 0xff;
+ pixel[2] = (mapped >> 16) & 0xff;
+#endif
+ break;
+ case 4:
+ *(Uint32 *)pixel = mapped;
+ break;
+ }
+ }
+ pixel += screen->pitch - LIGHTMAP_DIV * bpp;
+ }
+ }
+ div_pixel += (screen->pitch - width * bpp) * LIGHTMAP_DIV;
+ }
+ if(SDL_MUSTLOCK(screen))
+ {
+ SDL_UnlockSurface(screen);
+ }
+ }
+ }
+
+ void Lightmap::light_blit(SDL_Surface *src, SDL_Rect *src_rect, int dstx, int dsty)
+ {
+ dstx /= LIGHTMAP_DIV;
+ dsty /= LIGHTMAP_DIV;
+ int srcx = src_rect->x / LIGHTMAP_DIV;
+ int srcy = src_rect->y / LIGHTMAP_DIV;
+ int blit_width = src_rect->w / LIGHTMAP_DIV;
+ int blit_height = src_rect->h / LIGHTMAP_DIV;
+ int bpp = src->format->BytesPerPixel;
+ if(SDL_MUSTLOCK(src))
+ {
+ SDL_LockSurface(src);
+ }
+ Uint8 *pixel = (Uint8 *) src->pixels + srcy * src->pitch + srcx * bpp;
+ int loc = dsty * width + dstx;
+ for(int y = 0;y < blit_height;y++) {
+ for(int x = 0;x < blit_width;x++, pixel += bpp * LIGHTMAP_DIV, loc++) {
+ if(x + dstx < 0 || y + dsty < 0 || x + dstx >= width || y + dsty >= height)
+ {
+ continue;
+ }
+ if(red_channel[loc] == 0xff && green_channel[loc] == 0xff && blue_channel[loc] == 0xff)
+ {
+ continue;
+ }
+
+ Uint32 mapped = 0;
+ switch(bpp) {
+ case 1:
+ mapped = *pixel;
+ break;
+ case 2:
+ mapped = *(Uint16 *)pixel;
+ break;
+ case 3:
+#if SDL_BYTEORDER == SDL_BIG_ENDIAN
+ mapped |= pixel[0] << 16;
+ mapped |= pixel[1] << 8;
+ mapped |= pixel[2] << 0;
+#else
+ mapped |= pixel[0] << 0;
+ mapped |= pixel[1] << 8;
+ mapped |= pixel[2] << 16;
+#endif
+ break;
+ case 4:
+ mapped = *(Uint32 *)pixel;
+ break;
+ }
+ Uint8 red, green, blue, alpha;
+ SDL_GetRGBA(mapped, src->format, &red, &green, &blue, &alpha);
+
+ if(red != 0)
+ {
+ int redsum = red_channel[loc] + (red * alpha >> 8);
+ red_channel[loc] = redsum & ~0xff ? 0xff : redsum;
+ }
+ if(green != 0)
+ {
+ int greensum = green_channel[loc] + (green * alpha >> 8);
+ green_channel[loc] = greensum & ~0xff ? 0xff : greensum;
+ }
+ if(blue != 0)
+ {
+ int bluesum = blue_channel[loc] + (blue * alpha >> 8);
+ blue_channel[loc] = bluesum & ~0xff ? 0xff : bluesum;
+ }
+ }
+ pixel += (src->pitch - blit_width * bpp) * LIGHTMAP_DIV;
+ loc += width - blit_width;
+ }
+ if(SDL_MUSTLOCK(src))
+ {
+ SDL_UnlockSurface(src);
+ }
+ }
+
+ /*void Lightmap::light_blit(SDL_Surface *src, SDL_Rect *src_rect, int dstx, int dsty)
+ {
+ int bpp = src->format->BytesPerPixel;
+ if(SDL_MUSTLOCK(src))
+ {
+ SDL_LockSurface(src);
+ }
+ Uint8 *pixel = (Uint8 *) src->pixels + src_rect->y * src->pitch + src_rect->x * bpp;
+ int loc = dsty * width + dstx;
+ for(int y = 0;y < src_rect->h;y++) {
+ for(int x = 0;x < src_rect->w;x++, pixel += bpp, loc++) {
+ if(x + dstx < 0 || y + dsty < 0 || x + dstx >= width || y + dsty >= height)
+ {
+ continue;
+ }
+ if(red_channel[loc] == 0xff && green_channel[loc] == 0xff && blue_channel[loc] == 0xff)
+ {
+ continue;
+ }
+
+ Uint32 mapped = 0;
+ switch(bpp) {
+ case 1:
+ mapped = *pixel;
+ break;
+ case 2:
+ mapped = *(Uint16 *)pixel;
+ break;
+ case 3:
+#if SDL_BYTEORDER == SDL_BIG_ENDIAN
+ mapped |= pixel[0] << 16;
+ mapped |= pixel[1] << 8;
+ mapped |= pixel[2] << 0;
+#else
+ mapped |= pixel[0] << 0;
+ mapped |= pixel[1] << 8;
+ mapped |= pixel[2] << 16;
+#endif
+ break;
+ case 4:
+ mapped = *(Uint32 *)pixel;
+ break;
+ }
+ Uint8 red, green, blue, alpha;
+ SDL_GetRGBA(mapped, src->format, &red, &green, &blue, &alpha);
+
+ if(red != 0)
+ {
+ int redsum = red_channel[loc] + (red * alpha >> 8);
+ red_channel[loc] = redsum & ~0xff ? 0xff : redsum;
+ }
+ if(green != 0)
+ {
+ int greensum = green_channel[loc] + (green * alpha >> 8);
+ green_channel[loc] = greensum & ~0xff ? 0xff : greensum;
+ }
+ if(blue != 0)
+ {
+ int bluesum = blue_channel[loc] + (blue * alpha >> 8);
+ blue_channel[loc] = bluesum & ~0xff ? 0xff : bluesum;
+ }
+ }
+ pixel += src->pitch - src_rect->w * bpp;
+ loc += width - src_rect->w;
+ }
+ if(SDL_MUSTLOCK(src))
+ {
+ SDL_UnlockSurface(src);
+ }
+ }*/
+
+ void
+ Lightmap::draw_surface(const DrawingRequest& request)
+ {
+ if((request.color.red == 0.0 && request.color.green == 0.0 && request.color.blue == 0.0) || request.color.alpha == 0.0 || request.alpha == 0.0)
+ {
+ return;
+ }
+ //FIXME: support parameters request.alpha, request.angle, request.blend
+
+ const Surface* surface = (const Surface*) request.request_data;
+ SDL::Texture *sdltexture = dynamic_cast<SDL::Texture *>(surface->get_texture());
+ SDL::SurfaceData *surface_data = reinterpret_cast<SDL::SurfaceData *>(surface->get_surface_data());
+
+ DrawingEffect effect = request.drawing_effect;
+ if (surface->get_flipx()) effect = HORIZONTAL_FLIP;
+
+ SDL_Surface *transform = sdltexture->get_transform(request.color, effect);
+
+ // get and check SDL_Surface
+ if (transform == 0) {
+ std::cerr << "Warning: Tried to draw NULL surface, skipped draw" << std::endl;
+ return;
+ }
+
+ SDL_Rect *src_rect = surface_data->get_src_rect(effect);
+ int dstx = (int) request.pos.x * numerator / denominator;
+ int dsty = (int) request.pos.y * numerator / denominator;
+ light_blit(transform, src_rect, dstx, dsty);
+ }
+
+ void
+ Lightmap::draw_surface_part(const DrawingRequest& request)
+ {
+ const SurfacePartRequest* surfacepartrequest
+ = (SurfacePartRequest*) request.request_data;
+
+ const Surface* surface = surfacepartrequest->surface;
+ SDL::Texture *sdltexture = dynamic_cast<SDL::Texture *>(surface->get_texture());
+
+ DrawingEffect effect = request.drawing_effect;
+ if (surface->get_flipx()) effect = HORIZONTAL_FLIP;
+
+ SDL_Surface *transform = sdltexture->get_transform(Color(1.0, 1.0, 1.0), effect);
+
+ // get and check SDL_Surface
+ if (transform == 0) {
+ std::cerr << "Warning: Tried to draw NULL surface, skipped draw" << std::endl;
+ return;
+ }
+
+ int ox, oy;
+ if (effect == HORIZONTAL_FLIP)
+ {
+ ox = sdltexture->get_texture_width() - surface->get_x() - (int) surfacepartrequest->size.x;
+ }
+ else
+ {
+ ox = surface->get_x();
+ }
+ if (effect == VERTICAL_FLIP)
+ {
+ oy = sdltexture->get_texture_height() - surface->get_y() - (int) surfacepartrequest->size.y;
+ }
+ else
+ {
+ oy = surface->get_y();
+ }
+
+ SDL_Rect src_rect;
+ src_rect.x = (ox + (int) surfacepartrequest->source.x) * numerator / denominator;
+ src_rect.y = (oy + (int) surfacepartrequest->source.y) * numerator / denominator;
+ src_rect.w = (int) surfacepartrequest->size.x * numerator / denominator;
+ src_rect.h = (int) surfacepartrequest->size.y * numerator / denominator;
+ int dstx = (int) request.pos.x * numerator / denominator;
+ int dsty = (int) request.pos.y * numerator / denominator;
+ light_blit(transform, &src_rect, dstx, dsty);
+ }
+
+ void
+ Lightmap::draw_gradient(const DrawingRequest& request)
+ {
+ const GradientRequest* gradientrequest
+ = (GradientRequest*) request.request_data;
+ const Color& top = gradientrequest->top;
+ const Color& bottom = gradientrequest->bottom;
+
+ int loc = 0;
+ for(int y = 0;y < height;++y)
+ {
+ Uint8 red = (Uint8)((((float)(top.red-bottom.red)/(0-height)) * y + top.red) * 255);
+ Uint8 green = (Uint8)((((float)(top.green-bottom.green)/(0-height)) * y + top.green) * 255);
+ Uint8 blue = (Uint8)((((float)(top.blue-bottom.blue)/(0-height)) * y + top.blue) * 255);
+ Uint8 alpha = (Uint8)((((float)(top.alpha-bottom.alpha)/(0-height)) * y + top.alpha) * 255);
+ for(int x = 0;x < width;x++, loc++) {
+ if(red != 0)
+ {
+ int redsum = red_channel[loc] + (red * alpha >> 8);
+ red_channel[loc] = redsum & ~0xff ? 0xff : redsum;
+ }
+ if(green != 0)
+ {
+ int greensum = green_channel[loc] + (green * alpha >> 8);
+ green_channel[loc] = greensum & ~0xff ? 0xff : greensum;
+ }
+ if(blue != 0)
+ {
+ int bluesum = blue_channel[loc] + (blue * alpha >> 8);
+ blue_channel[loc] = bluesum & ~0xff ? 0xff : bluesum;
+ }
+ }
+ }
+ }
+
+ void
+ Lightmap::draw_filled_rect(const DrawingRequest& request)
+ {
+ const FillRectRequest* fillrectrequest
+ = (FillRectRequest*) request.request_data;
+
+ int rect_x = (int) (request.pos.x * width / SCREEN_WIDTH);
+ int rect_y = (int) (request.pos.y * height / SCREEN_HEIGHT);
+ int rect_w = (int) (fillrectrequest->size.x * width / SCREEN_WIDTH);
+ int rect_h = (int) (fillrectrequest->size.y * height / SCREEN_HEIGHT);
+ Uint8 red = (Uint8) (fillrectrequest->color.red * fillrectrequest->color.alpha * 255);
+ Uint8 green = (Uint8) (fillrectrequest->color.green * fillrectrequest->color.alpha * 255);
+ Uint8 blue = (Uint8) (fillrectrequest->color.blue * fillrectrequest->color.alpha * 255);
+ if(red == 0 && green == 0 && blue == 0)
+ {
+ return;
+ }
+ for(int y = rect_y;y < rect_y + rect_h;y++) {
+ for(int x = rect_x;x < rect_x + rect_w;x++) {
+ int loc = y * width + x;
+ if(red != 0)
+ {
+ int redsum = red_channel[loc] + red;
+ red_channel[loc] = redsum & ~0xff ? 0xff : redsum;
+ }
+ if(green != 0)
+ {
+ int greensum = green_channel[loc] + green;
+ green_channel[loc] = greensum & ~0xff ? 0xff : greensum;
+ }
+ if(blue != 0)
+ {
+ int bluesum = blue_channel[loc] + blue;
+ blue_channel[loc] = bluesum & ~0xff ? 0xff : bluesum;
+ }
+ }
+ }
+ }
+
+ void
+ Lightmap::get_light(const DrawingRequest& request) const
+ {
+ const GetLightRequest* getlightrequest
+ = (GetLightRequest*) request.request_data;
+
+ int x = (int) (request.pos.x * width / SCREEN_WIDTH);
+ int y = (int) (request.pos.y * height / SCREEN_HEIGHT);
+ int loc = y * width + x;
+ *(getlightrequest->color_ptr) = Color(((float)red_channel[loc])/255, ((float)green_channel[loc])/255, ((float)blue_channel[loc])/255);
+ }
+}
--- /dev/null
+// $Id: sdl_lightmap.hpp 4986 2007-04-16 17:48:28Z matzeb $
+//
+// SuperTux
+// Copyright (C) 2006 Matthias Braun <matze@braunis.de>
+//
+// 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_SDL_LIGHTMAP_H
+#define SUPERTUX_SDL_LIGHTMAP_H
+
+#include <SDL_video.h>
+
+#include "lightmap.hpp"
+
+class Color;
+struct DrawingRequest;
+
+namespace SDL
+{
+ class Lightmap : public ::Lightmap
+ {
+ public:
+ Lightmap();
+ ~Lightmap();
+
+ void start_draw(const Color &ambient_color);
+ void end_draw();
+ void do_draw();
+ void draw_surface(const DrawingRequest& request);
+ void draw_surface_part(const DrawingRequest& request);
+ void draw_text(const DrawingRequest& request);
+ void draw_gradient(const DrawingRequest& request);
+ void draw_filled_rect(const DrawingRequest& request);
+ void get_light(const DrawingRequest& request) const;
+
+ private:
+ SDL_Surface* screen;
+ Uint8 *red_channel;
+ Uint8 *blue_channel;
+ Uint8 *green_channel;
+ int width, height;
+ int numerator, denominator;
+ int LIGHTMAP_DIV;
+
+ void light_blit(SDL_Surface *src, SDL_Rect *src_rect, int dstx, int dsty);
+ };
+}
+
+#endif
+
--- /dev/null
+// $Id: sdl_renderer.cpp 5063 2007-05-27 11:32:00Z matzeb $
+//
+// SuperTux
+// Copyright (C) 2006 Matthias Braun <matze@braunis.de>
+//
+// 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 <config.h>
+
+#include <functional>
+#include <algorithm>
+#include <stdexcept>
+#include <cassert>
+#include <iostream>
+#include <SDL_image.h>
+#include <sstream>
+#include <iomanip>
+#include <physfs.h>
+
+#include "glutil.hpp"
+#include "sdl_renderer.hpp"
+#include "sdl_texture.hpp"
+#include "sdl_surface_data.hpp"
+#include "drawing_context.hpp"
+#include "drawing_request.hpp"
+#include "surface.hpp"
+#include "font.hpp"
+#include "main.hpp"
+#include "gameconfig.hpp"
+#include "log.hpp"
+#include "texture.hpp"
+#include "texture_manager.hpp"
+#include "obstack/obstackpp.hpp"
+
+namespace
+{
+ SDL_Surface *apply_alpha(SDL_Surface *src, float alpha_factor)
+ {
+ // FIXME: This is really slow
+ assert(src->format->Amask);
+ int alpha = (int) (alpha_factor * 256);
+ SDL_Surface *dst = SDL_CreateRGBSurface(src->flags, src->w, src->h, src->format->BitsPerPixel, src->format->Rmask, src->format->Gmask, src->format->Bmask, src->format->Amask);
+ int bpp = dst->format->BytesPerPixel;
+ if(SDL_MUSTLOCK(src))
+ {
+ SDL_LockSurface(src);
+ }
+ if(SDL_MUSTLOCK(dst))
+ {
+ SDL_LockSurface(dst);
+ }
+ for(int y = 0;y < dst->h;y++) {
+ for(int x = 0;x < dst->w;x++) {
+ Uint8 *srcpixel = (Uint8 *) src->pixels + y * src->pitch + x * bpp;
+ Uint8 *dstpixel = (Uint8 *) dst->pixels + y * dst->pitch + x * bpp;
+ Uint32 mapped = 0;
+ switch(bpp) {
+ case 1:
+ mapped = *srcpixel;
+ break;
+ case 2:
+ mapped = *(Uint16 *)srcpixel;
+ break;
+ case 3:
+#if SDL_BYTEORDER == SDL_BIG_ENDIAN
+ mapped |= srcpixel[0] << 16;
+ mapped |= srcpixel[1] << 8;
+ mapped |= srcpixel[2] << 0;
+#else
+ mapped |= srcpixel[0] << 0;
+ mapped |= srcpixel[1] << 8;
+ mapped |= srcpixel[2] << 16;
+#endif
+ break;
+ case 4:
+ mapped = *(Uint32 *)srcpixel;
+ break;
+ }
+ Uint8 r, g, b, a;
+ SDL_GetRGBA(mapped, src->format, &r, &g, &b, &a);
+ mapped = SDL_MapRGBA(dst->format, r, g, b, (a * alpha) >> 8);
+ switch(bpp) {
+ case 1:
+ *dstpixel = mapped;
+ break;
+ case 2:
+ *(Uint16 *)dstpixel = mapped;
+ break;
+ case 3:
+#if SDL_BYTEORDER == SDL_BIG_ENDIAN
+ dstpixel[0] = (mapped >> 16) & 0xff;
+ dstpixel[1] = (mapped >> 8) & 0xff;
+ dstpixel[2] = (mapped >> 0) & 0xff;
+#else
+ dstpixel[0] = (mapped >> 0) & 0xff;
+ dstpixel[1] = (mapped >> 8) & 0xff;
+ dstpixel[2] = (mapped >> 16) & 0xff;
+#endif
+ break;
+ case 4:
+ *(Uint32 *)dstpixel = mapped;
+ break;
+ }
+ }
+ }
+ if(SDL_MUSTLOCK(dst))
+ {
+ SDL_UnlockSurface(dst);
+ }
+ if(SDL_MUSTLOCK(src))
+ {
+ SDL_UnlockSurface(src);
+ }
+ return dst;
+ }
+}
+
+namespace SDL
+{
+ Renderer::Renderer()
+ {
+ const SDL_VideoInfo *info = SDL_GetVideoInfo();
+ log_info << "Hardware surfaces are " << (info->hw_available ? "" : "not ") << "available." << std::endl;
+ log_info << "Hardware to hardware blits are " << (info->blit_hw ? "" : "not ") << "accelerated." << std::endl;
+ log_info << "Hardware to hardware blits with colorkey are " << (info->blit_hw_CC ? "" : "not ") << "accelerated." << std::endl;
+ log_info << "Hardware to hardware blits with alpha are " << (info->blit_hw_A ? "" : "not ") << "accelerated." << std::endl;
+ log_info << "Software to hardware blits are " << (info->blit_sw ? "" : "not ") << "accelerated." << std::endl;
+ log_info << "Software to hardware blits with colorkey are " << (info->blit_sw_CC ? "" : "not ") << "accelerated." << std::endl;
+ log_info << "Software to hardware blits with alpha are " << (info->blit_sw_A ? "" : "not ") << "accelerated." << std::endl;
+ log_info << "Color fills are " << (info->blit_fill ? "" : "not ") << "accelerated." << std::endl;
+
+ int flags = SDL_SWSURFACE | SDL_ANYFORMAT;
+ if(config->use_fullscreen)
+ flags |= SDL_FULLSCREEN;
+ int width = config->screenwidth;
+ int height = config->screenheight;
+
+ screen = SDL_SetVideoMode(width, height, 0, flags);
+ if(screen == 0) {
+ std::stringstream msg;
+ msg << "Couldn't set video mode (" << width << "x" << height
+ << "): " << SDL_GetError();
+ throw std::runtime_error(msg.str());
+ }
+
+ float xfactor = (float) config->screenwidth / SCREEN_WIDTH;
+ float yfactor = (float) config->screenheight / SCREEN_HEIGHT;
+ if(xfactor < yfactor)
+ {
+ numerator = config->screenwidth;
+ denominator = SCREEN_WIDTH;
+ }
+ else
+ {
+ numerator = config->screenheight;
+ denominator = SCREEN_HEIGHT;
+ }
+
+ if(texture_manager == 0)
+ texture_manager = new TextureManager();
+ }
+
+ Renderer::~Renderer()
+ {
+ }
+
+ void
+ Renderer::draw_surface(const DrawingRequest& request)
+ {
+ //FIXME: support parameters request.alpha, request.angle, request.blend
+ const Surface* surface = (const Surface*) request.request_data;
+ SDL::Texture *sdltexture = dynamic_cast<SDL::Texture *>(surface->get_texture());
+ SDL::SurfaceData *surface_data = reinterpret_cast<SDL::SurfaceData *>(surface->get_surface_data());
+
+ DrawingEffect effect = request.drawing_effect;
+ if (surface->get_flipx()) effect = HORIZONTAL_FLIP;
+
+ SDL_Surface *transform = sdltexture->get_transform(request.color, effect);
+
+ // get and check SDL_Surface
+ if (transform == 0) {
+ std::cerr << "Warning: Tried to draw NULL surface, skipped draw" << std::endl;
+ return;
+ }
+
+ SDL_Rect *src_rect = surface_data->get_src_rect(effect);
+ SDL_Rect dst_rect;
+ dst_rect.x = (int) request.pos.x * numerator / denominator;
+ dst_rect.y = (int) request.pos.y * numerator / denominator;
+
+ Uint8 alpha = 0;
+ if(request.alpha != 1.0)
+ {
+ if(!transform->format->Amask)
+ {
+ if(transform->flags & SDL_SRCALPHA)
+ {
+ alpha = transform->format->alpha;
+ }
+ else
+ {
+ alpha = 255;
+ }
+ SDL_SetAlpha(transform, SDL_SRCALPHA, (Uint8) (request.alpha * alpha));
+ }
+ /*else
+ {
+ transform = apply_alpha(transform, request.alpha);
+ }*/
+ }
+
+ SDL_BlitSurface(transform, src_rect, screen, &dst_rect);
+
+ if(request.alpha != 1.0)
+ {
+ if(!transform->format->Amask)
+ {
+ if(alpha == 255)
+ {
+ SDL_SetAlpha(transform, SDL_RLEACCEL, 0);
+ }
+ else
+ {
+ SDL_SetAlpha(transform, SDL_SRCALPHA | SDL_RLEACCEL, alpha);
+ }
+ }
+ /*else
+ {
+ SDL_FreeSurface(transform);
+ }*/
+ }
+ }
+
+ void
+ Renderer::draw_surface_part(const DrawingRequest& request)
+ {
+ const SurfacePartRequest* surfacepartrequest
+ = (SurfacePartRequest*) request.request_data;
+
+ const Surface* surface = surfacepartrequest->surface;
+ SDL::Texture *sdltexture = dynamic_cast<SDL::Texture *>(surface->get_texture());
+
+ DrawingEffect effect = request.drawing_effect;
+ if (surface->get_flipx()) effect = HORIZONTAL_FLIP;
+
+ SDL_Surface *transform = sdltexture->get_transform(Color(1.0, 1.0, 1.0), effect);
+
+ // get and check SDL_Surface
+ if (transform == 0) {
+ std::cerr << "Warning: Tried to draw NULL surface, skipped draw" << std::endl;
+ return;
+ }
+
+ int ox, oy;
+ if (effect == HORIZONTAL_FLIP)
+ {
+ ox = sdltexture->get_texture_width() - surface->get_x() - (int) surfacepartrequest->size.x;
+ }
+ else
+ {
+ ox = surface->get_x();
+ }
+ if (effect == VERTICAL_FLIP)
+ {
+ oy = sdltexture->get_texture_height() - surface->get_y() - (int) surfacepartrequest->size.y;
+ }
+ else
+ {
+ oy = surface->get_y();
+ }
+
+ SDL_Rect src_rect;
+ src_rect.x = (ox + (int) surfacepartrequest->source.x) * numerator / denominator;
+ src_rect.y = (oy + (int) surfacepartrequest->source.y) * numerator / denominator;
+ src_rect.w = (int) surfacepartrequest->size.x * numerator / denominator;
+ src_rect.h = (int) surfacepartrequest->size.y * numerator / denominator;
+
+ SDL_Rect dst_rect;
+ dst_rect.x = (int) request.pos.x * numerator / denominator;
+ dst_rect.y = (int) request.pos.y * numerator / denominator;
+
+ Uint8 alpha = 0;
+ if(request.alpha != 1.0)
+ {
+ if(!transform->format->Amask)
+ {
+ if(transform->flags & SDL_SRCALPHA)
+ {
+ alpha = transform->format->alpha;
+ }
+ else
+ {
+ alpha = 255;
+ }
+ SDL_SetAlpha(transform, SDL_SRCALPHA, (Uint8) (request.alpha * alpha));
+ }
+ /*else
+ {
+ transform = apply_alpha(transform, request.alpha);
+ }*/
+ }
+
+ SDL_BlitSurface(transform, &src_rect, screen, &dst_rect);
+
+ if(request.alpha != 1.0)
+ {
+ if(!transform->format->Amask)
+ {
+ if(alpha == 255)
+ {
+ SDL_SetAlpha(transform, SDL_RLEACCEL, 0);
+ }
+ else
+ {
+ SDL_SetAlpha(transform, SDL_SRCALPHA | SDL_RLEACCEL, alpha);
+ }
+ }
+ /*else
+ {
+ SDL_FreeSurface(transform);
+ }*/
+ }
+ }
+
+ void
+ Renderer::draw_gradient(const DrawingRequest& request)
+ {
+ const GradientRequest* gradientrequest
+ = (GradientRequest*) request.request_data;
+ const Color& top = gradientrequest->top;
+ const Color& bottom = gradientrequest->bottom;
+
+ for(int y = 0;y < screen->h;++y)
+ {
+ Uint8 r = (Uint8)((((float)(top.red-bottom.red)/(0-screen->h)) * y + top.red) * 255);
+ Uint8 g = (Uint8)((((float)(top.green-bottom.green)/(0-screen->h)) * y + top.green) * 255);
+ Uint8 b = (Uint8)((((float)(top.blue-bottom.blue)/(0-screen->h)) * y + top.blue) * 255);
+ Uint8 a = (Uint8)((((float)(top.alpha-bottom.alpha)/(0-screen->h)) * y + top.alpha) * 255);
+ Uint32 color = SDL_MapRGB(screen->format, r, g, b);
+
+ SDL_Rect rect;
+ rect.x = 0;
+ rect.y = y;
+ rect.w = screen->w;
+ rect.h = 1;
+
+ if(a == SDL_ALPHA_OPAQUE) {
+ SDL_FillRect(screen, &rect, color);
+ } else if(a != SDL_ALPHA_TRANSPARENT) {
+ SDL_Surface *temp = SDL_CreateRGBSurface(screen->flags, rect.w, rect.h, screen->format->BitsPerPixel, screen->format->Rmask, screen->format->Gmask, screen->format->Bmask, screen->format->Amask);
+
+ SDL_FillRect(temp, 0, color);
+ SDL_SetAlpha(temp, SDL_SRCALPHA | SDL_RLEACCEL, a);
+ SDL_BlitSurface(temp, 0, screen, &rect);
+ SDL_FreeSurface(temp);
+ }
+ }
+ }
+
+ void
+ Renderer::draw_filled_rect(const DrawingRequest& request)
+ {
+ const FillRectRequest* fillrectrequest
+ = (FillRectRequest*) request.request_data;
+
+ SDL_Rect rect;
+ rect.x = (Sint16)request.pos.x * screen->w / SCREEN_WIDTH;
+ rect.y = (Sint16)request.pos.y * screen->h / SCREEN_HEIGHT;
+ rect.w = (Uint16)fillrectrequest->size.x * screen->w / SCREEN_WIDTH;
+ rect.h = (Uint16)fillrectrequest->size.y * screen->h / SCREEN_HEIGHT;
+ Uint8 r = static_cast<Uint8>(fillrectrequest->color.red * 255);
+ Uint8 g = static_cast<Uint8>(fillrectrequest->color.green * 255);
+ Uint8 b = static_cast<Uint8>(fillrectrequest->color.blue * 255);
+ Uint8 a = static_cast<Uint8>(fillrectrequest->color.alpha * 255);
+ Uint32 color = SDL_MapRGB(screen->format, r, g, b);
+ if(a == SDL_ALPHA_OPAQUE) {
+ SDL_FillRect(screen, &rect, color);
+ } else if(a != SDL_ALPHA_TRANSPARENT) {
+ SDL_Surface *temp = SDL_CreateRGBSurface(screen->flags, rect.w, rect.h, screen->format->BitsPerPixel, screen->format->Rmask, screen->format->Gmask, screen->format->Bmask, screen->format->Amask);
+
+ SDL_FillRect(temp, 0, color);
+ SDL_SetAlpha(temp, SDL_SRCALPHA | SDL_RLEACCEL, a);
+ SDL_BlitSurface(temp, 0, screen, &rect);
+ SDL_FreeSurface(temp);
+ }
+ }
+
+ void
+ Renderer::do_take_screenshot()
+ {
+ // [Christoph] TODO: Yes, this method also takes care of the actual disk I/O. Split it?
+
+ SDL_Surface *screen = SDL_GetVideoSurface();
+
+ // save screenshot
+ static const std::string writeDir = PHYSFS_getWriteDir();
+ static const std::string dirSep = PHYSFS_getDirSeparator();
+ static const std::string baseName = "screenshot";
+ static const std::string fileExt = ".bmp";
+ std::string fullFilename;
+ for (int num = 0; num < 1000; num++) {
+ std::ostringstream oss;
+ oss << baseName;
+ oss << std::setw(3) << std::setfill('0') << num;
+ oss << fileExt;
+ std::string fileName = oss.str();
+ fullFilename = writeDir + dirSep + fileName;
+ if (!PHYSFS_exists(fileName.c_str())) {
+ SDL_SaveBMP(screen, fullFilename.c_str());
+ log_debug << "Wrote screenshot to \"" << fullFilename << "\"" << std::endl;
+ return;
+ }
+ }
+ log_warning << "Did not save screenshot, because all files up to \"" << fullFilename << "\" already existed" << std::endl;
+ }
+
+ void
+ Renderer::flip()
+ {
+ SDL_Flip(screen);
+ }
+}
--- /dev/null
+// $Id: sdl_renderer.hpp 4986 2007-04-16 17:48:28Z matzeb $
+//
+// SuperTux
+// Copyright (C) 2006 Matthias Braun <matze@braunis.de>
+//
+// 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_SDL_RENDERER_H
+#define SUPERTUX_SDL_RENDERER_H
+
+#include <SDL_video.h>
+
+#include "renderer.hpp"
+
+namespace SDL
+{
+ class Renderer : public ::Renderer
+ {
+ public:
+ Renderer();
+ ~Renderer();
+
+ void draw_surface(const DrawingRequest& request);
+ void draw_surface_part(const DrawingRequest& request);
+ void draw_text(const DrawingRequest& request);
+ void draw_gradient(const DrawingRequest& request);
+ void draw_filled_rect(const DrawingRequest& request);
+ void do_take_screenshot();
+ void flip();
+ private:
+ SDL_Surface *screen;
+ int numerator, denominator;
+ };
+}
+
+#endif
+
--- /dev/null
+// $Id: gl_surface_data.hpp 4063 2006-07-21 21:05:23Z anmaster $
+//
+// SuperTux
+// Copyright (C) 2006 Matthias Braun <matze@braunis.de>
+//
+// 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 __SDL_SURFACE_DATA_HPP__
+#define __SDL_SURFACE_DATA_HPP__
+
+#include <config.h>
+
+#include "surface.hpp"
+#include "texture.hpp"
+#include "main.hpp"
+#include "gameconfig.hpp"
+
+namespace SDL
+{
+ class SurfaceData
+ {
+ private:
+ const Surface &surface;
+ SDL_Rect src_rects[NUM_EFFECTS];
+
+ public:
+ SurfaceData(const Surface &surface) :
+ surface(surface)
+ {
+ int numerator, denominator;
+ float xfactor = (float) config->screenwidth / SCREEN_WIDTH;
+ float yfactor = (float) config->screenheight / SCREEN_HEIGHT;
+ if(xfactor < yfactor)
+ {
+ numerator = config->screenwidth;
+ denominator = SCREEN_WIDTH;
+ }
+ else
+ {
+ numerator = config->screenheight;
+ denominator = SCREEN_HEIGHT;
+ }
+
+ src_rects[NO_EFFECT].x = surface.get_x() * numerator / denominator;
+ src_rects[NO_EFFECT].y = surface.get_y() * numerator / denominator;
+ src_rects[NO_EFFECT].w = surface.get_width() * numerator / denominator;
+ src_rects[NO_EFFECT].h = surface.get_height() * numerator / denominator;
+
+ int flipped_x = surface.get_texture()->get_texture_width() - surface.get_x() - surface.get_width();
+ src_rects[HORIZONTAL_FLIP].x = flipped_x * numerator / denominator;
+ src_rects[HORIZONTAL_FLIP].y = surface.get_y() * numerator / denominator;
+ src_rects[HORIZONTAL_FLIP].w = surface.get_width() * numerator / denominator;
+ src_rects[HORIZONTAL_FLIP].h = surface.get_height() * numerator / denominator;
+
+ int flipped_y = surface.get_texture()->get_texture_height() - surface.get_y() - surface.get_height();
+ src_rects[VERTICAL_FLIP].x = flipped_y * numerator / denominator;
+ src_rects[VERTICAL_FLIP].y = surface.get_y() * numerator / denominator;
+ src_rects[VERTICAL_FLIP].w = surface.get_width() * numerator / denominator;
+ src_rects[VERTICAL_FLIP].h = surface.get_height() * numerator / denominator;
+ }
+
+ SDL_Rect *get_src_rect(DrawingEffect effect)
+ {
+ return src_rects + effect;
+ }
+ };
+}
+
+#endif
--- /dev/null
+// $Id: sdl_texture.cpp 4063 2006-07-21 21:05:23Z anmaster $
+//
+// SuperTux
+// Copyright (C) 2006 Matthias Braun <matze@braunis.de>
+//
+// 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 <config.h>
+
+#include "sdl_texture.hpp"
+#include "color.hpp"
+#include "gameconfig.hpp"
+#include "main.hpp"
+
+#include <assert.h>
+
+#include <SDL.h>
+
+namespace
+{
+#define BILINEAR
+
+#ifdef NAIVE
+ SDL_Surface *scale(SDL_Surface *src, int numerator, int denominator)
+ {
+ if(numerator == denominator)
+ {
+ src->refcount++;
+ return src;
+ }
+ else
+ {
+ SDL_Surface *dst = SDL_CreateRGBSurface(src->flags, src->w * numerator / denominator, src->h * numerator / denominator, src->format->BitsPerPixel, src->format->Rmask, src->format->Gmask, src->format->Bmask, src->format->Amask);
+ int bpp = dst->format->BytesPerPixel;
+ if(SDL_MUSTLOCK(src))
+ {
+ SDL_LockSurface(src);
+ }
+ if(SDL_MUSTLOCK(dst))
+ {
+ SDL_LockSurface(dst);
+ }
+ for(int y = 0;y < dst->h;y++) {
+ for(int x = 0;x < dst->w;x++) {
+ Uint8 *srcpixel = (Uint8 *) src->pixels + (y * denominator / numerator) * src->pitch + (x * denominator / numerator) * bpp;
+ Uint8 *dstpixel = (Uint8 *) dst->pixels + y * dst->pitch + x * bpp;
+ switch(bpp) {
+ case 4:
+ dstpixel[3] = srcpixel[3];
+ case 3:
+ dstpixel[2] = srcpixel[2];
+ case 2:
+ dstpixel[1] = srcpixel[1];
+ case 1:
+ dstpixel[0] = srcpixel[0];
+ }
+ }
+ }
+ if(SDL_MUSTLOCK(dst))
+ {
+ SDL_UnlockSurface(dst);
+ }
+ if(SDL_MUSTLOCK(src))
+ {
+ SDL_UnlockSurface(src);
+ }
+ if(!src->format->Amask)
+ {
+ if(src->flags & SDL_SRCALPHA)
+ {
+ SDL_SetAlpha(dst, SDL_SRCALPHA | SDL_RLEACCEL, src->format->alpha);
+ }
+ if(src->flags & SDL_SRCCOLORKEY)
+ {
+ SDL_SetColorKey(dst, SDL_SRCCOLORKEY | SDL_RLEACCEL, src->format->colorkey);
+ }
+ }
+ return dst;
+ }
+ }
+#endif
+
+#ifdef BILINEAR
+ void getpixel(SDL_Surface *src, int srcx, int srcy, Uint8 color[4])
+ {
+ int bpp = src->format->BytesPerPixel;
+ if(srcx == src->w)
+ {
+ srcx--;
+ }
+ if(srcy == src->h)
+ {
+ srcy--;
+ }
+ Uint8 *srcpixel = (Uint8 *) src->pixels + srcy * src->pitch + srcx * bpp;
+ Uint32 mapped = 0;
+ switch(bpp) {
+ case 1:
+ mapped = *srcpixel;
+ break;
+ case 2:
+ mapped = *(Uint16 *)srcpixel;
+ break;
+ case 3:
+#if SDL_BYTEORDER == SDL_BIG_ENDIAN
+ mapped |= srcpixel[0] << 16;
+ mapped |= srcpixel[1] << 8;
+ mapped |= srcpixel[2] << 0;
+#else
+ mapped |= srcpixel[0] << 0;
+ mapped |= srcpixel[1] << 8;
+ mapped |= srcpixel[2] << 16;
+#endif
+ break;
+ case 4:
+ mapped = *(Uint32 *)srcpixel;
+ break;
+ }
+ SDL_GetRGBA(mapped, src->format, &color[0], &color[1], &color[2], &color[3]);
+ }
+
+ void merge(Uint8 color[4], Uint8 color0[4], Uint8 color1[4], int rem, int total)
+ {
+ color[0] = (color0[0] * (total - rem) + color1[0] * rem) / total;
+ color[1] = (color0[1] * (total - rem) + color1[1] * rem) / total;
+ color[2] = (color0[2] * (total - rem) + color1[2] * rem) / total;
+ color[3] = (color0[3] * (total - rem) + color1[3] * rem) / total;
+ }
+
+ SDL_Surface *scale(SDL_Surface *src, int numerator, int denominator)
+ {
+ if(numerator == denominator)
+ {
+ src->refcount++;
+ return src;
+ }
+ else
+ {
+ SDL_Surface *dst = SDL_CreateRGBSurface(src->flags, src->w * numerator / denominator, src->h * numerator / denominator, src->format->BitsPerPixel, src->format->Rmask, src->format->Gmask, src->format->Bmask, src->format->Amask);
+ int bpp = dst->format->BytesPerPixel;
+ if(SDL_MUSTLOCK(src))
+ {
+ SDL_LockSurface(src);
+ }
+ if(SDL_MUSTLOCK(dst))
+ {
+ SDL_LockSurface(dst);
+ }
+ for(int y = 0;y < dst->h;y++) {
+ for(int x = 0;x < dst->w;x++) {
+ int srcx = x * denominator / numerator;
+ int srcy = y * denominator / numerator;
+ Uint8 color00[4], color01[4], color10[4], color11[4];
+ getpixel(src, srcx, srcy, color00);
+ getpixel(src, srcx + 1, srcy, color01);
+ getpixel(src, srcx, srcy + 1, color10);
+ getpixel(src, srcx + 1, srcy + 1, color11);
+ Uint8 color0[4], color1[4], color[4];
+ int remx = x * denominator % numerator;
+ merge(color0, color00, color01, remx, numerator);
+ merge(color1, color10, color11, remx, numerator);
+ int remy = y * denominator % numerator;
+ merge(color, color0, color1, remy, numerator);
+ Uint8 *dstpixel = (Uint8 *) dst->pixels + y * dst->pitch + x * bpp;
+ Uint32 mapped = SDL_MapRGBA(dst->format, color[0], color[1], color[2], color[3]);
+ switch(bpp) {
+ case 1:
+ *dstpixel = mapped;
+ break;
+ case 2:
+ *(Uint16 *)dstpixel = mapped;
+ break;
+ case 3:
+#if SDL_BYTEORDER == SDL_BIG_ENDIAN
+ dstpixel[0] = (mapped >> 16) & 0xff;
+ dstpixel[1] = (mapped >> 8) & 0xff;
+ dstpixel[2] = (mapped >> 0) & 0xff;
+#else
+ dstpixel[0] = (mapped >> 0) & 0xff;
+ dstpixel[1] = (mapped >> 8) & 0xff;
+ dstpixel[2] = (mapped >> 16) & 0xff;
+#endif
+ break;
+ case 4:
+ *(Uint32 *)dstpixel = mapped;
+ break;
+ }
+ }
+ }
+ if(SDL_MUSTLOCK(dst))
+ {
+ SDL_UnlockSurface(dst);
+ }
+ if(SDL_MUSTLOCK(src))
+ {
+ SDL_UnlockSurface(src);
+ }
+ if(!src->format->Amask)
+ {
+ if(src->flags & SDL_SRCALPHA)
+ {
+ SDL_SetAlpha(dst, SDL_SRCALPHA | SDL_RLEACCEL, src->format->alpha);
+ }
+ if(src->flags & SDL_SRCCOLORKEY)
+ {
+ SDL_SetColorKey(dst, SDL_SRCCOLORKEY | SDL_RLEACCEL, src->format->colorkey);
+ }
+ }
+ return dst;
+ }
+ }
+#endif
+
+ SDL_Surface *horz_flip(SDL_Surface *src)
+ {
+ SDL_Surface *dst = SDL_CreateRGBSurface(src->flags, src->w, src->h, src->format->BitsPerPixel, src->format->Rmask, src->format->Gmask, src->format->Bmask, src->format->Amask);
+ int bpp = dst->format->BytesPerPixel;
+ if(SDL_MUSTLOCK(src))
+ {
+ SDL_LockSurface(src);
+ }
+ if(SDL_MUSTLOCK(dst))
+ {
+ SDL_LockSurface(dst);
+ }
+ for(int y = 0;y < dst->h;y++) {
+ for(int x = 0;x < dst->w;x++) {
+ Uint8 *srcpixel = (Uint8 *) src->pixels + y * src->pitch + x * bpp;
+ Uint8 *dstpixel = (Uint8 *) dst->pixels + y * dst->pitch + (dst->w - x - 1) * bpp;
+ switch(bpp) {
+ case 4:
+ dstpixel[3] = srcpixel[3];
+ case 3:
+ dstpixel[2] = srcpixel[2];
+ case 2:
+ dstpixel[1] = srcpixel[1];
+ case 1:
+ dstpixel[0] = srcpixel[0];
+ }
+ }
+ }
+ if(SDL_MUSTLOCK(dst))
+ {
+ SDL_UnlockSurface(dst);
+ }
+ if(SDL_MUSTLOCK(src))
+ {
+ SDL_UnlockSurface(src);
+ }
+ if(!src->format->Amask)
+ {
+ if(src->flags & SDL_SRCALPHA)
+ {
+ SDL_SetAlpha(dst, SDL_SRCALPHA | SDL_RLEACCEL, src->format->alpha);
+ }
+ if(src->flags & SDL_SRCCOLORKEY)
+ {
+ SDL_SetColorKey(dst, SDL_SRCCOLORKEY | SDL_RLEACCEL, src->format->colorkey);
+ }
+ }
+ return dst;
+ }
+
+ SDL_Surface *vert_flip(SDL_Surface *src)
+ {
+ SDL_Surface *dst = SDL_CreateRGBSurface(src->flags, src->w, src->h, src->format->BitsPerPixel, src->format->Rmask, src->format->Gmask, src->format->Bmask, src->format->Amask);
+ int bpp = dst->format->BytesPerPixel;
+ if(SDL_MUSTLOCK(src))
+ {
+ SDL_LockSurface(src);
+ }
+ if(SDL_MUSTLOCK(dst))
+ {
+ SDL_LockSurface(dst);
+ }
+ for(int y = 0;y < dst->h;y++) {
+ for(int x = 0;x < dst->w;x++) {
+ Uint8 *srcpixel = (Uint8 *) src->pixels + y * src->pitch + x * bpp;
+ Uint8 *dstpixel = (Uint8 *) dst->pixels + (dst->h - y - 1) * dst->pitch + x * bpp;
+ switch(bpp) {
+ case 4:
+ dstpixel[3] = srcpixel[3];
+ case 3:
+ dstpixel[2] = srcpixel[2];
+ case 2:
+ dstpixel[1] = srcpixel[1];
+ case 1:
+ dstpixel[0] = srcpixel[0];
+ }
+ }
+ }
+ if(SDL_MUSTLOCK(dst))
+ {
+ SDL_UnlockSurface(dst);
+ }
+ if(SDL_MUSTLOCK(src))
+ {
+ SDL_UnlockSurface(src);
+ }
+ if(!src->format->Amask)
+ {
+ if(src->flags & SDL_SRCALPHA)
+ {
+ SDL_SetAlpha(dst, SDL_SRCALPHA | SDL_RLEACCEL, src->format->alpha);
+ }
+ if(src->flags & SDL_SRCCOLORKEY)
+ {
+ SDL_SetColorKey(dst, SDL_SRCCOLORKEY | SDL_RLEACCEL, src->format->colorkey);
+ }
+ }
+ return dst;
+ }
+
+ SDL_Surface *colorize(SDL_Surface *src, const Color &color)
+ {
+ // FIXME: This is really slow
+ assert(color.red != 1.0 || color.green != 1.0 || color.blue != 1.0);
+ int red = (int) (color.red * 256);
+ int green = (int) (color.green * 256);
+ int blue = (int) (color.blue * 256);
+ SDL_Surface *dst = SDL_CreateRGBSurface(src->flags, src->w, src->h, src->format->BitsPerPixel, src->format->Rmask, src->format->Gmask, src->format->Bmask, src->format->Amask);
+ int bpp = dst->format->BytesPerPixel;
+ if(SDL_MUSTLOCK(src))
+ {
+ SDL_LockSurface(src);
+ }
+ if(SDL_MUSTLOCK(dst))
+ {
+ SDL_LockSurface(dst);
+ }
+ for(int y = 0;y < dst->h;y++) {
+ for(int x = 0;x < dst->w;x++) {
+ Uint8 *srcpixel = (Uint8 *) src->pixels + y * src->pitch + x * bpp;
+ Uint8 *dstpixel = (Uint8 *) dst->pixels + y * dst->pitch + x * bpp;
+ Uint32 mapped = 0;
+ switch(bpp) {
+ case 1:
+ mapped = *srcpixel;
+ break;
+ case 2:
+ mapped = *(Uint16 *)srcpixel;
+ break;
+ case 3:
+#if SDL_BYTEORDER == SDL_BIG_ENDIAN
+ mapped |= srcpixel[0] << 16;
+ mapped |= srcpixel[1] << 8;
+ mapped |= srcpixel[2] << 0;
+#else
+ mapped |= srcpixel[0] << 0;
+ mapped |= srcpixel[1] << 8;
+ mapped |= srcpixel[2] << 16;
+#endif
+ break;
+ case 4:
+ mapped = *(Uint32 *)srcpixel;
+ break;
+ }
+ if(src->format->Amask || !(src->flags & SDL_SRCCOLORKEY) || mapped != src->format->colorkey)
+ {
+ Uint8 r, g, b, a;
+ SDL_GetRGBA(mapped, src->format, &r, &g, &b, &a);
+ mapped = SDL_MapRGBA(dst->format, (r * red) >> 8, (g * green) >> 8, (b * blue) >> 8, a);
+ }
+ switch(bpp) {
+ case 1:
+ *dstpixel = mapped;
+ break;
+ case 2:
+ *(Uint16 *)dstpixel = mapped;
+ break;
+ case 3:
+#if SDL_BYTEORDER == SDL_BIG_ENDIAN
+ dstpixel[0] = (mapped >> 16) & 0xff;
+ dstpixel[1] = (mapped >> 8) & 0xff;
+ dstpixel[2] = (mapped >> 0) & 0xff;
+#else
+ dstpixel[0] = (mapped >> 0) & 0xff;
+ dstpixel[1] = (mapped >> 8) & 0xff;
+ dstpixel[2] = (mapped >> 16) & 0xff;
+#endif
+ break;
+ case 4:
+ *(Uint32 *)dstpixel = mapped;
+ break;
+ }
+ }
+ }
+ if(SDL_MUSTLOCK(dst))
+ {
+ SDL_UnlockSurface(dst);
+ }
+ if(SDL_MUSTLOCK(src))
+ {
+ SDL_UnlockSurface(src);
+ }
+ if(!src->format->Amask)
+ {
+ if(src->flags & SDL_SRCALPHA)
+ {
+ SDL_SetAlpha(dst, SDL_SRCALPHA | SDL_RLEACCEL, src->format->alpha);
+ }
+ if(src->flags & SDL_SRCCOLORKEY)
+ {
+ SDL_SetColorKey(dst, SDL_SRCCOLORKEY | SDL_RLEACCEL, src->format->colorkey);
+ }
+ }
+ return dst;
+ }
+
+ SDL_Surface *optimize(SDL_Surface *src)
+ {
+ if(!src->format->Amask)
+ {
+ return SDL_DisplayFormat(src);
+ }
+ else
+ {
+ int transparent = 0;
+ int opaque = 0;
+ int semitransparent = 0;
+ int alphasum = 0;
+ int squaredalphasum = 0;
+ bool colors[(1 << 12)];
+ memset(colors, 0, (1 << 12) * sizeof(bool));
+
+ int bpp = src->format->BytesPerPixel;
+ if(SDL_MUSTLOCK(src))
+ {
+ SDL_LockSurface(src);
+ }
+ for(int y = 0;y < src->h;y++) {
+ for(int x = 0;x < src->w;x++) {
+ Uint8 *pixel = (Uint8 *) src->pixels + y * src->pitch + x * bpp;
+ Uint32 mapped = 0;
+ switch(bpp) {
+ case 1:
+ mapped = *pixel;
+ break;
+ case 2:
+ mapped = *(Uint16 *)pixel;
+ break;
+ case 3:
+#if SDL_BYTEORDER == SDL_BIG_ENDIAN
+ mapped |= pixel[0] << 16;
+ mapped |= pixel[1] << 8;
+ mapped |= pixel[2] << 0;
+#else
+ mapped |= pixel[0] << 0;
+ mapped |= pixel[1] << 8;
+ mapped |= pixel[2] << 16;
+#endif
+ break;
+ case 4:
+ mapped = *(Uint32 *)pixel;
+ break;
+ }
+ Uint8 red, green, blue, alpha;
+ SDL_GetRGBA(mapped, src->format, &red, &green, &blue, &alpha);
+ if(alpha < 16)
+ {
+ transparent++;
+ }
+ else if (alpha > 240)
+ {
+ opaque++;
+ alphasum += alpha;
+ squaredalphasum += alpha * alpha;
+ }
+ else
+ {
+ semitransparent++;
+ squaredalphasum += alpha * alpha;
+ }
+ if(alpha != 0)
+ {
+ colors[((red & 0xf0) << 4) | (green & 0xf0) | ((blue & 0xf0) >> 4)] = true;
+ }
+ }
+ }
+ if(SDL_MUSTLOCK(src))
+ {
+ SDL_UnlockSurface(src);
+ }
+ int avgalpha = (opaque + semitransparent) ? alphasum / (opaque + semitransparent) : 0;
+ int avgsquaredalpha = (opaque + semitransparent) ? squaredalphasum / (opaque + semitransparent) : 0;
+ int alphavariance = avgsquaredalpha - avgalpha * avgalpha;
+ if(semitransparent > ((transparent + opaque + semitransparent) / 8) && alphavariance > 16)
+ {
+ return SDL_DisplayFormatAlpha(src);
+ }
+ int keycolor = -1;
+ for(int i = 0;i < (1 << 12);i++)
+ {
+ if(!colors[i])
+ {
+ keycolor = i;
+ }
+ }
+ if(keycolor == -1)
+ {
+ return SDL_DisplayFormatAlpha(src);
+ }
+ SDL_Surface *dst = SDL_CreateRGBSurface(src->flags & ~(SDL_SRCALPHA), src->w, src->h, src->format->BitsPerPixel, src->format->Rmask, src->format->Gmask, src->format->Bmask, 0);
+ bpp = dst->format->BytesPerPixel;
+ Uint32 key = SDL_MapRGB(dst->format, (((keycolor & 0xf00) >> 4) | 0xf), ((keycolor & 0xf0) | 0xf), (((keycolor & 0xf) << 4) | 0xf));
+ if(SDL_MUSTLOCK(src))
+ {
+ SDL_LockSurface(src);
+ }
+ if(SDL_MUSTLOCK(dst))
+ {
+ SDL_LockSurface(dst);
+ }
+ for(int y = 0;y < dst->h;y++) {
+ for(int x = 0;x < dst->w;x++) {
+ Uint8 *srcpixel = (Uint8 *) src->pixels + y * src->pitch + x * bpp;
+ Uint8 *dstpixel = (Uint8 *) dst->pixels + y * dst->pitch + x * bpp;
+ Uint32 mapped = 0;
+ switch(bpp) {
+ case 1:
+ mapped = *srcpixel;
+ break;
+ case 2:
+ mapped = *(Uint16 *)srcpixel;
+ break;
+ case 3:
+#if SDL_BYTEORDER == SDL_BIG_ENDIAN
+ mapped |= srcpixel[0] << 16;
+ mapped |= srcpixel[1] << 8;
+ mapped |= srcpixel[2] << 0;
+#else
+ mapped |= srcpixel[0] << 0;
+ mapped |= srcpixel[1] << 8;
+ mapped |= srcpixel[2] << 16;
+#endif
+ break;
+ case 4:
+ mapped = *(Uint32 *)srcpixel;
+ break;
+ }
+ Uint8 red, green, blue, alpha;
+ SDL_GetRGBA(mapped, src->format, &red, &green, &blue, &alpha);
+ if(alpha < (avgalpha / 4))
+ {
+ mapped = key;
+ }
+ else
+ {
+ mapped = SDL_MapRGB(dst->format, red, green, blue);
+ }
+ switch(bpp) {
+ case 1:
+ *dstpixel = mapped;
+ break;
+ case 2:
+ *(Uint16 *)dstpixel = mapped;
+ break;
+ case 3:
+#if SDL_BYTEORDER == SDL_BIG_ENDIAN
+ dstpixel[0] = (mapped >> 16) & 0xff;
+ dstpixel[1] = (mapped >> 8) & 0xff;
+ dstpixel[2] = (mapped >> 0) & 0xff;
+#else
+ dstpixel[0] = (mapped >> 0) & 0xff;
+ dstpixel[1] = (mapped >> 8) & 0xff;
+ dstpixel[2] = (mapped >> 16) & 0xff;
+#endif
+ break;
+ case 4:
+ *(Uint32 *)dstpixel = mapped;
+ break;
+ }
+ }
+ }
+ if(SDL_MUSTLOCK(dst))
+ {
+ SDL_UnlockSurface(dst);
+ }
+ if(SDL_MUSTLOCK(src))
+ {
+ SDL_UnlockSurface(src);
+ }
+ if(avgalpha < 240)
+ {
+ SDL_SetAlpha(dst, SDL_SRCALPHA | SDL_RLEACCEL, avgalpha);
+ }
+ SDL_SetColorKey(dst, SDL_SRCCOLORKEY | SDL_RLEACCEL, key);
+ SDL_Surface *convert = SDL_DisplayFormat(dst);
+ SDL_FreeSurface(dst);
+ return convert;
+ }
+ }
+}
+
+namespace SDL
+{
+ Texture::Texture(SDL_Surface* image)
+ {
+ texture = optimize(image);
+ //width = texture->w;
+ //height = texture->h;
+ int numerator, denominator;
+ float xfactor = (float) config->screenwidth / SCREEN_WIDTH;
+ float yfactor = (float) config->screenheight / SCREEN_HEIGHT;
+ if(xfactor < yfactor)
+ {
+ numerator = config->screenwidth;
+ denominator = SCREEN_WIDTH;
+ }
+ else
+ {
+ numerator = config->screenheight;
+ denominator = SCREEN_HEIGHT;
+ }
+ cache[NO_EFFECT][Color::WHITE] = scale(texture, numerator, denominator);
+ }
+
+ Texture::~Texture()
+ {
+ SDL_FreeSurface(texture);
+ }
+
+ SDL_Surface *Texture::get_transform(const Color &color, DrawingEffect effect)
+ {
+ if(cache[NO_EFFECT][color] == 0) {
+ assert(cache[NO_EFFECT][Color::WHITE]);
+ cache[NO_EFFECT][color] = colorize(cache[NO_EFFECT][Color::WHITE], color);
+ }
+ if(cache[effect][color] == 0) {
+ assert(cache[NO_EFFECT][color]);
+ switch(effect) {
+ case NO_EFFECT:
+ break;
+ case HORIZONTAL_FLIP:
+ cache[HORIZONTAL_FLIP][color] = horz_flip(cache[NO_EFFECT][color]);
+ break;
+ case VERTICAL_FLIP:
+ cache[VERTICAL_FLIP][color] = vert_flip(cache[NO_EFFECT][color]);
+ break;
+ default:
+ return 0;
+ }
+ }
+ return cache[effect][color];
+ }
+}
--- /dev/null
+// $Id: sdl_texture.hpp 4063 2006-07-21 21:05:23Z anmaster $
+//
+// SuperTux
+// Copyright (C) 2006 Matthias Braun <matze@braunis.de>
+//
+// 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 __SDL_TEXTURE_HPP__
+#define __SDL_TEXTURE_HPP__
+
+#include <config.h>
+
+#include <SDL.h>
+
+#include "texture.hpp"
+#include "color.hpp"
+
+namespace SDL
+{
+ class Texture : public ::Texture
+ {
+ protected:
+ SDL_Surface *texture;
+ //unsigned int width;
+ //unsigned int height;
+
+ struct ColorCache
+ {
+ static const int HASHED_BITS = 3;
+ static const int CACHE_SIZE = 1 << (HASHED_BITS * 3);
+
+ static void ref(SDL_Surface *surface)
+ {
+ if(surface)
+ {
+ surface->refcount++;
+ }
+ }
+
+ static int hash(const Color &color)
+ {
+ return
+ ((int) (color.red * ((1 << HASHED_BITS) - 1)) << (HASHED_BITS - 1) * 2) |
+ ((int) (color.green * ((1 << HASHED_BITS) - 1)) << (HASHED_BITS - 1)) |
+ ((int) (color.blue * ((1 << HASHED_BITS) - 1)) << 0);
+ }
+
+ SDL_Surface *data[CACHE_SIZE];
+
+ ColorCache()
+ {
+ memset(data, 0, CACHE_SIZE * sizeof(SDL_Surface *));
+ }
+
+ ~ColorCache()
+ {
+ std::for_each(data, data + CACHE_SIZE, SDL_FreeSurface);
+ }
+
+ void operator = (const ColorCache &other)
+ {
+ std::for_each(other.data, other.data + CACHE_SIZE, ref);
+ std::for_each(data, data + CACHE_SIZE, SDL_FreeSurface);
+ memcpy(data, other.data, CACHE_SIZE * sizeof(SDL_Surface *));
+ }
+
+ SDL_Surface *&operator [] (const Color &color)
+ {
+ return data[hash(color)];
+ }
+ };
+ //typedef std::map<Color, SDL_Surface *> ColorCache;
+ ColorCache cache[NUM_EFFECTS];
+
+ public:
+ Texture(SDL_Surface* sdlsurface);
+ virtual ~Texture();
+
+ SDL_Surface *get_transform(const Color &color, DrawingEffect effect);
+
+ SDL_Surface *get_texture() const
+ {
+ return texture;
+ }
+
+ unsigned int get_texture_width() const
+ {
+ return texture->w;
+ }
+
+ unsigned int get_texture_height() const
+ {
+ return texture->h;
+ }
+
+ unsigned int get_image_width() const
+ {
+ return texture->w;
+ }
+
+ unsigned int get_image_height() const
+ {
+ return texture->h;
+ }
+
+ /*unsigned int get_texture_width() const
+ {
+ return width;
+ }
+
+ unsigned int get_texture_height() const
+ {
+ return height;
+ }
+
+ unsigned int get_image_width() const
+ {
+ return width;
+ }
+
+ unsigned int get_image_height() const
+ {
+ return height;
+ }*/
+ };
+}
+
+#endif
--- /dev/null
+// $Id$
+//
+// SuperTux
+// Copyright (C) 2006 Matthias Braun <matze@braunis.de>
+//
+// 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 __SURFACE_HPP__
+#define __SURFACE_HPP__
+
+#include <config.h>
+
+#include <string>
+#include <SDL.h>
+#include "math/vector.hpp"
+#include "texture.hpp"
+#include "video_systems.hpp"
+
+/**
+ * A rectangular image.
+ * The class basically holds a reference to a texture with additional UV
+ * coordinates that specify a rectangular area on this texture
+ */
+class Surface
+{
+private:
+ Texture* texture;
+ void *surface_data;
+ int x;
+ int y;
+ int w;
+ int h;
+ bool flipx;
+
+public:
+ Surface(const std::string& file) :
+ texture(texture_manager->get(file)),
+ x(0), y(0), w(0), h(0),
+ flipx(false)
+ {
+ texture->ref();
+ w = texture->get_image_width();
+ h = texture->get_image_height();
+ surface_data = new_surface_data(*this);
+ }
+
+ Surface(const std::string& file, int x, int y, int w, int h) :
+ texture(texture_manager->get(file)),
+ x(x), y(y), w(w), h(h),
+ flipx(false)
+ {
+ texture->ref();
+ surface_data = new_surface_data(*this);
+ }
+
+ Surface(const Surface& other) :
+ texture(other.texture),
+ x(other.x), y(other.y),
+ w(other.w), h(other.h),
+ flipx(false)
+ {
+ texture->ref();
+ surface_data = new_surface_data(*this);
+ }
+
+ ~Surface()
+ {
+ free_surface_data(surface_data);
+ texture->unref();
+ }
+
+ /** flip the surface horizontally */
+ void hflip()
+ {
+ flipx = !flipx;
+ }
+
+ bool get_flipx() const
+ {
+ return flipx;
+ }
+
+ const Surface& operator= (const Surface& other)
+ {
+ other.texture->ref();
+ texture->unref();
+ texture = other.texture;
+ x = other.x;
+ y = other.y;
+ w = other.w;
+ h = other.h;
+ return *this;
+ }
+
+ Texture *get_texture() const
+ {
+ return texture;
+ }
+
+ void *get_surface_data() const
+ {
+ return surface_data;
+ }
+
+ int get_x() const
+ {
+ return x;
+ }
+
+ int get_y() const
+ {
+ return y;
+ }
+
+ int get_width() const
+ {
+ return w;
+ }
+
+ int get_height() const
+ {
+ return h;
+ }
+
+ Vector get_position() const
+ { return Vector(get_x(), get_y()); }
+
+ /**
+ * returns a vector containing width and height
+ */
+ Vector get_size() const
+ { return Vector(get_width(), get_height()); }
+};
+
+#endif
--- /dev/null
+// $Id$
+//
+// SuperTux
+// Copyright (C) 2006 Matthias Braun <matze@braunis.de>
+//
+// 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 __TEXTURE_HPP__
+#define __TEXTURE_HPP__
+
+#include <config.h>
+
+#include <assert.h>
+#include <string>
+
+#include "texture_manager.hpp"
+
+/// bitset for drawing effects
+enum DrawingEffect {
+ /** Don't apply anything */
+ NO_EFFECT,
+ /** Draw the Surface upside down */
+ VERTICAL_FLIP,
+ /** Draw the Surface from left to down */
+ HORIZONTAL_FLIP,
+ NUM_EFFECTS
+};
+
+/**
+ * This class is a wrapper around a texture handle. It stores the texture width
+ * and height and provides convenience functions for uploading SDL_Surfaces
+ * into the texture
+ */
+class Texture
+{
+protected:
+ int refcount;
+ std::string filename;
+
+public:
+ Texture() : refcount(0), filename() {}
+ virtual ~Texture() {}
+
+ virtual unsigned int get_texture_width() const = 0;
+ virtual unsigned int get_texture_height() const = 0;
+ virtual unsigned int get_image_width() const = 0;
+ virtual unsigned int get_image_height() const = 0;
+
+ std::string get_filename() const
+ {
+ return filename;
+ }
+
+ void set_filename(std::string filename)
+ {
+ this->filename = filename;
+ }
+
+ void ref()
+ {
+ refcount++;
+ }
+
+ void unref()
+ {
+ assert(refcount > 0);
+ refcount--;
+ if(refcount == 0)
+ release();
+ }
+
+private:
+ void release()
+ {
+ texture_manager->release(this);
+ }
+};
+
+#endif
--- /dev/null
+// $Id$
+//
+// SuperTux
+// Copyright (C) 2006 Matthias Braun <matze@braunis.de>
+//
+// 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 <config.h>
+
+#include "texture_manager.hpp"
+
+#include <assert.h>
+#include <SDL.h>
+#include <SDL_image.h>
+#include <iostream>
+#include <sstream>
+#include <stdexcept>
+#include "physfs/physfs_sdl.hpp"
+#include "video_systems.hpp"
+#include "gl_texture.hpp"
+#include "glutil.hpp"
+#include "gameconfig.hpp"
+#include "file_system.hpp"
+#include "log.hpp"
+#include "texture.hpp"
+
+TextureManager* texture_manager = NULL;
+
+TextureManager::TextureManager()
+{
+}
+
+TextureManager::~TextureManager()
+{
+ for(ImageTextures::iterator i = image_textures.begin();
+ i != image_textures.end(); ++i) {
+ if(i->second == NULL)
+ continue;
+ log_warning << "Texture '" << i->first << "' not freed" << std::endl;
+ delete i->second;
+ }
+}
+
+Texture*
+TextureManager::get(const std::string& _filename)
+{
+ std::string filename = FileSystem::normalize(_filename);
+ ImageTextures::iterator i = image_textures.find(filename);
+
+ Texture* texture = NULL;
+ if(i != image_textures.end())
+ texture = i->second;
+
+ if(texture == NULL) {
+ texture = create_image_texture(filename);
+ image_textures[filename] = texture;
+ }
+
+ return texture;
+}
+
+void
+TextureManager::release(Texture* texture)
+{
+ image_textures.erase(texture->get_filename());
+ delete texture;
+}
+
+#ifdef HAVE_OPENGL
+void
+TextureManager::register_texture(GL::Texture* texture)
+{
+ textures.insert(texture);
+}
+
+void
+TextureManager::remove_texture(GL::Texture* texture)
+{
+ textures.erase(texture);
+}
+#endif
+
+Texture*
+TextureManager::create_image_texture(const std::string& filename)
+{
+ SDL_Surface* image = IMG_Load_RW(get_physfs_SDLRWops(filename), 1);
+ if(image == 0) {
+ std::ostringstream msg;
+ msg << "Couldn't load image '" << filename << "' :" << SDL_GetError();
+ throw std::runtime_error(msg.str());
+ }
+
+ Texture* result = 0;
+ try {
+ result = new_texture(image);
+ result->set_filename(filename);
+ } catch(...) {
+ delete result;
+ SDL_FreeSurface(image);
+ throw;
+ }
+
+ SDL_FreeSurface(image);
+ return result;
+}
+
+#ifdef HAVE_OPENGL
+void
+TextureManager::save_textures()
+{
+ glPixelStorei(GL_PACK_ROW_LENGTH, 0);
+ glPixelStorei(GL_PACK_IMAGE_HEIGHT, 0);
+ glPixelStorei(GL_PACK_SKIP_PIXELS, 0);
+ glPixelStorei(GL_PACK_SKIP_ROWS, 0);
+ glPixelStorei(GL_PACK_SKIP_IMAGES, 0);
+ glPixelStorei(GL_PACK_ALIGNMENT, 1);
+ for(Textures::iterator i = textures.begin(); i != textures.end(); ++i) {
+ save_texture(*i);
+ }
+ for(ImageTextures::iterator i = image_textures.begin();
+ i != image_textures.end(); ++i) {
+ save_texture(dynamic_cast<GL::Texture *>(i->second));
+ }
+}
+
+void
+TextureManager::save_texture(GL::Texture* texture)
+{
+ SavedTexture saved_texture;
+ saved_texture.texture = texture;
+ glBindTexture(GL_TEXTURE_2D, texture->get_handle());
+ glGetTexLevelParameteriv(GL_TEXTURE_2D, 0, GL_TEXTURE_WIDTH,
+ &saved_texture.width);
+ glGetTexLevelParameteriv(GL_TEXTURE_2D, 0, GL_TEXTURE_HEIGHT,
+ &saved_texture.height);
+ glGetTexLevelParameteriv(GL_TEXTURE_2D, 0, GL_TEXTURE_BORDER,
+ &saved_texture.border);
+ glGetTexParameteriv(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER,
+ &saved_texture.min_filter);
+ glGetTexParameteriv(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER,
+ &saved_texture.mag_filter);
+ glGetTexParameteriv(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S,
+ &saved_texture.wrap_s);
+ glGetTexParameteriv(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T,
+ &saved_texture.wrap_t);
+
+ size_t pixelssize = saved_texture.width * saved_texture.height * 4;
+ saved_texture.pixels = new char[pixelssize];
+
+ glGetTexImage(GL_TEXTURE_2D, 0, GL_RGBA, GL_UNSIGNED_BYTE,
+ saved_texture.pixels);
+
+ saved_textures.push_back(saved_texture);
+
+ glDeleteTextures(1, &(texture->get_handle()));
+ texture->set_handle(0);
+
+ assert_gl("retrieving texture for save");
+}
+
+void
+TextureManager::reload_textures()
+{
+ glPixelStorei(GL_UNPACK_ROW_LENGTH, 0);
+ glPixelStorei(GL_UNPACK_IMAGE_HEIGHT, 0);
+ glPixelStorei(GL_UNPACK_SKIP_PIXELS, 0);
+ glPixelStorei(GL_UNPACK_SKIP_ROWS, 0);
+ glPixelStorei(GL_UNPACK_SKIP_IMAGES, 0);
+ glPixelStorei(GL_UNPACK_ALIGNMENT, 1);
+
+ for(std::vector<SavedTexture>::iterator i = saved_textures.begin();
+ i != saved_textures.end(); ++i) {
+ SavedTexture& saved_texture = *i;
+
+ GLuint handle;
+ glGenTextures(1, &handle);
+ assert_gl("creating texture handle");
+
+ glBindTexture(GL_TEXTURE_2D, handle);
+ glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA,
+ saved_texture.width, saved_texture.height,
+ saved_texture.border, GL_RGBA,
+ GL_UNSIGNED_BYTE, saved_texture.pixels);
+ delete[] saved_texture.pixels;
+ assert_gl("uploading texture pixel data");
+
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER,
+ saved_texture.min_filter);
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER,
+ saved_texture.mag_filter);
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S,
+ saved_texture.wrap_s);
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T,
+ saved_texture.wrap_t);
+
+ assert_gl("setting texture_params");
+ saved_texture.texture->set_handle(handle);
+ }
+
+ saved_textures.clear();
+}
+#endif
--- /dev/null
+// $Id$
+//
+// SuperTux
+// Copyright (C) 2006 Matthias Braun <matze@braunis.de>
+//
+// 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 __IMAGE_TEXTURE_MANAGER_HPP__
+#define __IMAGE_TEXTURE_MANAGER_HPP__
+
+#include <config.h>
+
+#include "glutil.hpp"
+#include <string>
+#include <vector>
+#include <map>
+#include <set>
+
+class Texture;
+namespace GL { class Texture; }
+
+class TextureManager
+{
+public:
+ TextureManager();
+ ~TextureManager();
+
+ Texture* get(const std::string& filename);
+
+#ifdef HAVE_OPENGL
+ void register_texture(GL::Texture* texture);
+ void remove_texture(GL::Texture* texture);
+
+ void save_textures();
+ void reload_textures();
+#endif
+
+private:
+ friend class Texture;
+ void release(Texture* texture);
+
+ typedef std::map<std::string, Texture*> ImageTextures;
+ ImageTextures image_textures;
+
+ Texture* create_image_texture(const std::string& filename);
+
+#ifdef HAVE_OPENGL
+ typedef std::set<GL::Texture*> Textures;
+ Textures textures;
+
+ struct SavedTexture
+ {
+ GL::Texture* texture;
+ GLint width;
+ GLint height;
+ char* pixels;
+ GLint border;
+
+ GLint min_filter;
+ GLint mag_filter;
+ GLint wrap_s;
+ GLint wrap_t;
+ };
+ std::vector<SavedTexture> saved_textures;
+
+ void save_texture(GL::Texture* texture);
+#endif
+};
+
+extern TextureManager* texture_manager;
+
+#endif
--- /dev/null
+// $Id: video_systems.cpp 5063 2007-05-27 11:32:00Z matzeb $
+//
+// SuperTux
+// Copyright (C) 2006 Matthias Braun <matze@braunis.de>
+//
+// 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 <config.h>
+
+#include "video_systems.hpp"
+#include "gameconfig.hpp"
+#include "renderer.hpp"
+#include "gl_renderer.hpp"
+#include "sdl_renderer.hpp"
+#include "lightmap.hpp"
+#include "gl_lightmap.hpp"
+#include "sdl_lightmap.hpp"
+#include "texture.hpp"
+#include "gl_texture.hpp"
+#include "sdl_texture.hpp"
+#include "gl_surface_data.hpp"
+#include "sdl_surface_data.hpp"
+
+Renderer *new_renderer()
+{
+ switch(config->video)
+ {
+ case AUTO_VIDEO:
+#ifdef HAVE_OPENGL
+ return new GL::Renderer();
+#else
+ return new SDL::Renderer();
+#endif
+#ifdef HAVE_OPENGL
+ case OPENGL:
+ return new GL::Renderer();
+#endif
+ case PURE_SDL:
+ return new SDL::Renderer();
+ default:
+ assert(0 && "invalid video system in config");
+#ifdef HAVE_OPENGL
+ return new GL::Renderer();
+#else
+ return new SDL::Renderer();
+#endif
+ }
+}
+
+Lightmap *new_lightmap()
+{
+ switch(config->video)
+ {
+ case AUTO_VIDEO:
+#ifdef HAVE_OPENGL
+ return new GL::Lightmap();
+#else
+ return new SDL::Lightmap();
+#endif
+#ifdef HAVE_OPENGL
+ case OPENGL:
+ return new GL::Lightmap();
+#endif
+ case PURE_SDL:
+ return new SDL::Lightmap();
+ default:
+ assert(0 && "invalid video system in config");
+#ifdef HAVE_OPENGL
+ return new GL::Lightmap();
+#else
+ return new SDL::Lightmap();
+#endif
+ }
+}
+
+Texture *new_texture(SDL_Surface *image)
+{
+ switch(config->video)
+ {
+ case AUTO_VIDEO:
+#ifdef HAVE_OPENGL
+ return new GL::Texture(image);
+#else
+ return new SDL::Texture(image);
+#endif
+#ifdef HAVE_OPENGL
+ case OPENGL:
+ return new GL::Texture(image);
+#endif
+ case PURE_SDL:
+ return new SDL::Texture(image);
+ default:
+ assert(0 && "invalid video system in config");
+#ifdef HAVE_OPENGL
+ return new GL::Texture(image);
+#else
+ return new SDL::Texture(image);
+#endif
+ }
+}
+
+void *new_surface_data(const Surface &surface)
+{
+ switch(config->video)
+ {
+ case AUTO_VIDEO:
+#ifdef HAVE_OPENGL
+ return new GL::SurfaceData(surface);
+#else
+ return new SDL::SurfaceData(surface);
+#endif
+#ifdef HAVE_OPENGL
+ case OPENGL:
+ return new GL::SurfaceData(surface);
+#endif
+ case PURE_SDL:
+ return new SDL::SurfaceData(surface);
+ default:
+ assert(0 && "invalid video system in config");
+#ifdef HAVE_OPENGL
+ return new GL::SurfaceData(surface);
+#else
+ return new SDL::SurfaceData(surface);
+#endif
+ }
+}
+
+void free_surface_data(void *surface_data)
+{
+ delete reinterpret_cast<char *>(surface_data);
+}
+
+VideoSystem get_video_system(const std::string &video)
+{
+ if(video == "auto")
+ {
+ return AUTO_VIDEO;
+ }
+#ifdef HAVE_OPENGL
+ else if(video == "opengl")
+ {
+ return OPENGL;
+ }
+#endif
+ else if(video == "sdl")
+ {
+ return PURE_SDL;
+ }
+ else
+ {
+ return AUTO_VIDEO;
+ }
+}
+
+std::string get_video_string(VideoSystem video)
+{
+ switch(video)
+ {
+ case AUTO_VIDEO:
+ return "auto";
+ case OPENGL:
+ return "opengl";
+ case PURE_SDL:
+ return "sdl";
+ default:
+ assert(0 && "invalid video system in config");
+ return "auto";
+ }
+}
--- /dev/null
+// $Id: video_systems.hpp 5138 2007-08-15 01:02:22Z tuxdev $
+//
+// SuperTux
+// Copyright (C) 2006 Matthias Braun <matze@braunis.de>
+//
+// 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 __RENDER_SYTSTEMS_HPP__
+#define __RENDER_SYTSTEMS_HPP__
+
+#include <config.h>
+
+#include <string>
+#include <SDL.h>
+
+class Renderer;
+class Lightmap;
+class Texture;
+class Surface;
+
+enum VideoSystem {
+ AUTO_VIDEO,
+ OPENGL,
+ PURE_SDL,
+ NUM_SYSTEMS
+};
+
+Renderer *new_renderer();
+Lightmap *new_lightmap();
+Texture *new_texture(SDL_Surface *image);
+void *new_surface_data(const Surface &surface);
+void free_surface_data(void *surface_data);
+VideoSystem get_video_system(const std::string &video);
+std::string get_video_string(VideoSystem video);
+
+#endif
--- /dev/null
+// $Id$
+//
+// SuperTux
+// Copyright (C) 2006 Matthias Braun <matze@braunis.de>
+//
+// 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 <config.h>
+
+#include <stddef.h>
+#include <physfs.h>
+#include <stdexcept>
+
+#include "world.hpp"
+#include "file_system.hpp"
+#include "lisp/parser.hpp"
+#include "lisp/lisp.hpp"
+#include "physfs/physfs_stream.hpp"
+#include "scripting/squirrel_util.hpp"
+#include "scripting/serialize.hpp"
+#include "log.hpp"
+#include "worldmap/worldmap.hpp"
+#include "mainloop.hpp"
+
+static bool has_suffix(const std::string& data, const std::string& suffix)
+{
+ if (data.length() >= suffix.length())
+ return data.compare(data.length() - suffix.length(), suffix.length(), suffix) == 0;
+ else
+ return false;
+}
+
+World* World::current_ = NULL;
+
+World::World()
+{
+ is_levelset = true;
+ hide_from_contribs = false;
+ sq_resetobject(&world_thread);
+}
+
+World::~World()
+{
+ sq_release(Scripting::global_vm, &world_thread);
+ if(current_ == this)
+ current_ = NULL;
+}
+
+void
+World::set_savegame_filename(const std::string& filename)
+{
+ this->savegame_filename = filename;
+ // make sure the savegame directory exists
+ std::string dirname = FileSystem::dirname(filename);
+ if(!PHYSFS_exists(dirname.c_str())) {
+ if(PHYSFS_mkdir(dirname.c_str())) {
+ std::ostringstream msg;
+ msg << "Couldn't create directory for savegames '"
+ << dirname << "': " <<PHYSFS_getLastError();
+ throw std::runtime_error(msg.str());
+ }
+ }
+
+ if(!PHYSFS_isDirectory(dirname.c_str())) {
+ std::ostringstream msg;
+ msg << "Savegame path '" << dirname << "' is not a directory";
+ throw std::runtime_error(msg.str());
+ }
+}
+
+void
+World::load(const std::string& filename)
+{
+ basedir = FileSystem::dirname(filename);
+
+ lisp::Parser parser;
+ const lisp::Lisp* root = parser.parse(filename);
+
+ const lisp::Lisp* info = root->get_lisp("supertux-world");
+ if(info == NULL)
+ info = root->get_lisp("supertux-level-subset");
+ if(info == NULL)
+ throw std::runtime_error("File is not a world or levelsubset file");
+
+ hide_from_contribs = false;
+ is_levelset = true;
+
+ info->get("title", title);
+ info->get("description", description);
+ info->get("levelset", is_levelset);
+ info->get_vector("levels", levels);
+ info->get("hide-from-contribs", hide_from_contribs);
+
+ // Level info file doesn't define any levels, so read the
+ // directory to see what we can find
+
+ std::string path = basedir;
+ char** files = PHYSFS_enumerateFiles(path.c_str());
+ if(!files) {
+ log_warning << "Couldn't read subset dir '" << path << "'" << std::endl;
+ return;
+ }
+
+ for(const char* const* filename = files; *filename != 0; ++filename) {
+ if(has_suffix(*filename, ".stl")) {
+ levels.push_back(path + *filename);
+ }
+ }
+ PHYSFS_freeList(files);
+}
+
+void
+World::run()
+{
+ using namespace Scripting;
+
+ current_ = this;
+
+ // create new squirrel table for persisten game state
+ HSQUIRRELVM vm = Scripting::global_vm;
+
+ sq_pushroottable(vm);
+ sq_pushstring(vm, "state", -1);
+ sq_newtable(vm);
+ if(SQ_FAILED(sq_createslot(vm, -3)))
+ throw Scripting::SquirrelError(vm, "Couldn't create state table");
+ sq_pop(vm, 1);
+
+ load_state();
+
+ std::string filename = basedir + "/world.nut";
+ try {
+ IFileStream in(filename);
+
+ sq_release(global_vm, &world_thread);
+ world_thread = create_thread(global_vm);
+ compile_and_run(object_to_vm(world_thread), in, filename);
+ } catch(std::exception& ) {
+ // fallback: try to load worldmap worldmap.stwm
+ using namespace WorldMapNS;
+ main_loop->push_screen(new WorldMap(basedir + "worldmap.stwm"));
+ }
+}
+
+void
+World::save_state()
+{
+ using namespace Scripting;
+
+ lisp::Writer writer(savegame_filename);
+
+ writer.start_list("supertux-savegame");
+ writer.write_int("version", 1);
+
+ using namespace WorldMapNS;
+ if(WorldMap::current() != NULL) {
+ std::ostringstream title;
+ title << WorldMap::current()->get_title();
+ title << " (" << WorldMap::current()->solved_level_count()
+ << "/" << WorldMap::current()->level_count() << ")";
+ writer.write_string("title", title.str());
+ }
+
+ writer.start_list("tux");
+ player_status->write(writer);
+ writer.end_list("tux");
+
+ writer.start_list("state");
+
+ sq_pushroottable(global_vm);
+ sq_pushstring(global_vm, "state", -1);
+ if(SQ_SUCCEEDED(sq_get(global_vm, -2))) {
+ Scripting::save_squirrel_table(global_vm, -1, writer);
+ sq_pop(global_vm, 1);
+ }
+ sq_pop(global_vm, 1);
+ writer.end_list("state");
+
+ writer.end_list("supertux-savegame");
+}
+
+void
+World::load_state()
+{
+ using namespace Scripting;
+
+ try {
+ lisp::Parser parser;
+ const lisp::Lisp* root = parser.parse(savegame_filename);
+
+ const lisp::Lisp* lisp = root->get_lisp("supertux-savegame");
+ if(lisp == NULL)
+ throw std::runtime_error("file is not a supertux-savegame file");
+
+ int version = 1;
+ lisp->get("version", version);
+ if(version != 1)
+ throw std::runtime_error("incompatible savegame version");
+
+ const lisp::Lisp* tux = lisp->get_lisp("tux");
+ if(tux == NULL)
+ throw std::runtime_error("No tux section in savegame");
+ player_status->read(*tux);
+
+ const lisp::Lisp* state = lisp->get_lisp("state");
+ if(state == NULL)
+ throw std::runtime_error("No state section in savegame");
+
+ sq_pushroottable(global_vm);
+ sq_pushstring(global_vm, "state", -1);
+ if(SQ_FAILED(sq_deleteslot(global_vm, -2, SQFalse)))
+ sq_pop(global_vm, 1);
+
+ sq_pushstring(global_vm, "state", -1);
+ sq_newtable(global_vm);
+ load_squirrel_table(global_vm, -1, state);
+ if(SQ_FAILED(sq_createslot(global_vm, -3)))
+ throw std::runtime_error("Couldn't create state table");
+ sq_pop(global_vm, 1);
+ } catch(std::exception& e) {
+ log_debug << "Couldn't load savegame: " << e.what() << std::endl;
+ }
+}
+
+const std::string&
+World::get_level_filename(unsigned int i) const
+{
+ return levels[i];
+}
+
+unsigned int
+World::get_num_levels() const
+{
+ return levels.size();
+}
+
+const std::string&
+World::get_basedir() const
+{
+ return basedir;
+}
--- /dev/null
+// $Id$
+//
+// SuperTux
+// Copyright (C) 2006 Matthias Braun <matze@braunis.de>
+//
+// 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_WORLD_H
+#define SUPERTUX_WORLD_H
+
+#include <vector>
+#include <string>
+#include <squirrel.h>
+
+class World
+{
+private:
+ std::vector<std::string> levels;
+ std::string basedir;
+ std::string savegame_filename;
+ /// squirrel table that saves persistent state (about the world)
+ HSQOBJECT state_table;
+ HSQOBJECT world_thread;
+ static World* current_;
+
+public:
+ World();
+ ~World();
+
+ void set_savegame_filename(const std::string& filename);
+ void load(const std::string& filename);
+
+ void save_state();
+ void load_state();
+
+ const std::string& get_level_filename(unsigned int i) const;
+ unsigned int get_num_levels() const;
+
+ const std::string& get_basedir() const;
+
+ static World* current()
+ {
+ return current_;
+ }
+
+ void run();
+
+ std::string title;
+ std::string description;
+ bool hide_from_contribs;
+ bool is_levelset;
+};
+
+#endif
--- /dev/null
+// $Id$
+//
+// SuperTux - Worldmap Direction
+// Copyright (C) 2006 Christoph Sommer <christoph.sommer@2006.expires.deltadevelopment.de>
+//
+// 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 __WORLDMAP_DIRECTION_HPP__
+#define __WORLDMAP_DIRECTION_HPP__
+
+namespace WorldMapNS {
+
+enum Direction { D_NONE, D_WEST, D_EAST, D_NORTH, D_SOUTH };
+
+}
+
+#endif
--- /dev/null
+// $Id$
+//
+// SuperTux
+// Copyright (C) 2004 Ingo Ruhnke <grumbel@gmx.de>
+// Copyright (C) 2006 Christoph Sommer <christoph.sommer@2006.expires.deltadevelopment.de>
+//
+// 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 <config.h>
+
+#include <stddef.h>
+#include <physfs.h>
+#include "worldmap/level.hpp"
+#include "sprite/sprite_manager.hpp"
+#include "sprite/sprite.hpp"
+#include "video/drawing_context.hpp"
+#include "log.hpp"
+#include "file_system.hpp"
+
+namespace WorldMapNS
+{
+
+LevelTile::LevelTile(const std::string& basedir, const lisp::Lisp* lisp)
+ : solved(false), auto_play(false), basedir(basedir), picture_cached(false),
+ picture(0)
+{
+ lisp->get("name", name);
+ lisp->get("x", pos.x);
+ lisp->get("y", pos.y);
+ lisp->get("auto-play", auto_play);
+
+ std::string spritefile = "images/worldmap/common/leveldot.sprite";
+ lisp->get("sprite", spritefile);
+ sprite.reset(sprite_manager->create(spritefile));
+
+ lisp->get("extro-script", extro_script);
+
+ if (!PHYSFS_exists((basedir + name).c_str()))
+ {
+ log_warning << "level file '" << name
+ << "' does not exist and will not be added to the worldmap" << std::endl;
+ return;
+ }
+}
+
+LevelTile::~LevelTile()
+{
+ delete picture;
+}
+
+void
+LevelTile::draw(DrawingContext& context)
+{
+ sprite->draw(context, pos*32 + Vector(16, 16), LAYER_OBJECTS - 1);
+}
+
+void
+LevelTile::update(float )
+{
+}
+
+const Surface*
+LevelTile::get_picture()
+{
+ if (picture_cached) return picture;
+ picture_cached = true;
+ std::string fname = FileSystem::strip_extension(basedir + name)+".jpg";
+ if (!PHYSFS_exists(fname.c_str())) {
+ return 0;
+ }
+ picture = new Surface(fname);
+ return picture;
+}
+
+}
--- /dev/null
+// $Id$
+//
+// SuperTux
+// Copyright (C) 2004 Ingo Ruhnke <grumbel@gmx.de>
+// Copyright (C) 2006 Christoph Sommer <christoph.sommer@2006.expires.deltadevelopment.de>
+//
+// 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 __LEVEL_TILE_HPP__
+#define __LEVEL_TILE_HPP__
+
+#include <memory>
+#include <string>
+#include "math/vector.hpp"
+#include "game_object.hpp"
+#include "statistics.hpp"
+#include "video/surface.hpp"
+
+class Sprite;
+
+namespace WorldMapNS
+{
+
+class LevelTile : public GameObject
+{
+public:
+ LevelTile(const std::string& basedir, const lisp::Lisp* lisp);
+ virtual ~LevelTile();
+
+ virtual void draw(DrawingContext& context);
+ virtual void update(float elapsed_time);
+
+ Vector pos;
+ std::string title;
+ bool solved;
+ bool auto_play; /**< true if Tux should automatically enter this level if it's unfinished */
+
+ std::auto_ptr<Sprite> sprite;
+
+ /** Statistics for level tiles */
+ Statistics statistics;
+
+ /** Script that is run when the level is successfully finished */
+ std::string extro_script;
+
+ /** return Surface of level picture or 0 if no picture is available */
+ const Surface* get_picture();
+
+private:
+ std::string basedir;
+ bool picture_cached;
+ Surface* picture;
+
+};
+
+}
+
+#endif
--- /dev/null
+// $Id$
+//
+// SuperTux - Worldmap Spawnpoint
+// Copyright (C) 2006 Matthias Braun <matze@braunis.de>
+//
+// 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 <config.h>
+
+#include <stdexcept>
+#include <iostream>
+#include "spawn_point.hpp"
+#include "lisp/lisp.hpp"
+#include "lisp/list_iterator.hpp"
+#include "log.hpp"
+
+namespace WorldMapNS
+{
+
+// from worldmap.cpp
+Direction string_to_direction(const std::string& directory);
+
+SpawnPoint::SpawnPoint(const lisp::Lisp* slisp) : auto_dir(D_NONE)
+{
+ pos.x = -1;
+ pos.y = -1;
+ lisp::ListIterator iter(slisp);
+ while(iter.next()) {
+ const std::string& token = iter.item();
+ if(token == "name") {
+ iter.value()->get(name);
+ } else if(token == "x") {
+ iter.value()->get(pos.x);
+ } else if(token == "y") {
+ iter.value()->get(pos.y);
+ } else if(token == "auto-dir") {
+ std::string s = "";
+ iter.value()->get(s);
+ auto_dir = string_to_direction(s);
+ } else {
+ log_warning << "unknown token '" << token << "' in SpawnPoint" << std::endl;
+ }
+ }
+
+ if(name == "")
+ throw std::runtime_error("No name specified for spawnpoint");
+ if(pos.x < 0 || pos.y < 0)
+ throw std::runtime_error("Invalid coordinates for spawnpoint");
+}
+
+}
--- /dev/null
+// $Id$
+//
+// SuperTux - Worldmap Spawnpoint
+// Copyright (C) 2006 Matthias Braun <matze@braunis.de>
+//
+// 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 __WORLDMAP_SPAWN_POINT_H__
+#define __WORLDMAP_SPAWN_POINT_H__
+
+#include <string>
+#include "math/vector.hpp"
+#include "lisp/lisp.hpp"
+#include "game_object.hpp"
+#include "worldmap/direction.hpp"
+
+namespace WorldMapNS
+{
+
+class SpawnPoint
+{
+public:
+ SpawnPoint(const lisp::Lisp* lisp);
+
+ std::string name;
+ Vector pos;
+ Direction auto_dir; /**< automatically start walking in this direction */
+};
+
+}
+
+#endif
--- /dev/null
+// $Id$
+//
+// SuperTux
+// Copyright (C) 2004 Ingo Ruhnke <grumbel@gmx.de>
+// Copyright (C) 2006 Christoph Sommer <christoph.sommer@2006.expires.deltadevelopment.de>
+//
+// 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 <config.h>
+
+#include "worldmap/special_tile.hpp"
+#include "sprite/sprite_manager.hpp"
+#include "sprite/sprite.hpp"
+#include "video/drawing_context.hpp"
+
+namespace WorldMapNS
+{
+
+SpecialTile::SpecialTile(const lisp::Lisp* lisp)
+ : passive_message(false), invisible(false),
+ apply_action_north(true), apply_action_east(true),
+ apply_action_south(true), apply_action_west(true)
+{
+ lisp->get("x", pos.x);
+ lisp->get("y", pos.y);
+ lisp->get("invisible-tile", invisible);
+
+ if(!invisible) {
+ std::string spritefile = "";
+ lisp->get("sprite", spritefile);
+ sprite.reset(sprite_manager->create(spritefile));
+ }
+
+ lisp->get("map-message", map_message);
+ lisp->get("passive-message", passive_message);
+ lisp->get("script", script);
+
+ std::string apply_direction;
+ lisp->get("apply-to-direction", apply_direction);
+ if(!apply_direction.empty()) {
+ apply_action_north = false;
+ apply_action_south = false;
+ apply_action_east = false;
+ apply_action_west = false;
+ if(apply_direction.find("north") != std::string::npos)
+ apply_action_north = true;
+ if(apply_direction.find("south") != std::string::npos)
+ apply_action_south = true;
+ if(apply_direction.find("east") != std::string::npos)
+ apply_action_east = true;
+ if(apply_direction.find("west") != std::string::npos)
+ apply_action_west = true;
+ }
+}
+
+SpecialTile::~SpecialTile()
+{
+}
+
+void
+SpecialTile::draw(DrawingContext& context)
+{
+ if(invisible)
+ return;
+
+ sprite->draw(context, pos*32 + Vector(16, 16), LAYER_OBJECTS - 1);
+}
+
+void
+SpecialTile::update(float )
+{
+}
+
+}
--- /dev/null
+// $Id$
+//
+// SuperTux
+// Copyright (C) 2004 Ingo Ruhnke <grumbel@gmx.de>
+// Copyright (C) 2006 Christoph Sommer <christoph.sommer@2006.expires.deltadevelopment.de>
+//
+// 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 __WORLDMAP_SPECIAL_TILE_HPP__
+#define __WORLDMAP_SPECIAL_TILE_HPP__
+
+#include <memory>
+#include <string>
+#include "game_object.hpp"
+#include "math/vector.hpp"
+#include "lisp/lisp.hpp"
+
+class Sprite;
+
+namespace WorldMapNS
+{
+
+class SpecialTile : public GameObject
+{
+public:
+ SpecialTile(const lisp::Lisp* lisp);
+ virtual ~SpecialTile();
+
+ virtual void draw(DrawingContext& context);
+ virtual void update(float elapsed_time);
+
+ Vector pos;
+
+ /** Sprite to render instead of guessing what image to draw */
+ std::auto_ptr<Sprite> sprite;
+
+ /** Message to show in the Map */
+ std::string map_message;
+ bool passive_message;
+
+ /** Script to execute when tile is touched */
+ std::string script;
+
+ /** Hide special tile */
+ bool invisible;
+
+ /** Only applies actions (ie. passive messages) when going to that direction */
+ bool apply_action_north;
+ bool apply_action_east;
+ bool apply_action_south;
+ bool apply_action_west;
+};
+
+}
+
+#endif
--- /dev/null
+// $Id$
+//
+// SuperTux
+// Copyright (C) 2006 Matthias Braun <matze@braunis.de>
+//
+// 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 <config.h>
+
+#include "sprite_change.hpp"
+#include "sprite/sprite_manager.hpp"
+#include "sprite/sprite.hpp"
+#include "video/drawing_context.hpp"
+
+namespace WorldMapNS
+{
+
+SpriteChange::SpriteChange(const lisp::Lisp* lisp)
+ : change_on_touch(false), in_stay_action(false)
+{
+ lisp->get("x", pos.x);
+ lisp->get("y", pos.y);
+ lisp->get("change-on-touch", change_on_touch);
+
+ std::string spritefile = "";
+ lisp->get("sprite", spritefile);
+ sprite.reset(sprite_manager->create(spritefile));
+
+ lisp->get("stay-action", stay_action);
+ lisp->get("initial-stay-action", in_stay_action);
+
+ lisp->get("stay-group", stay_group);
+
+ all_sprite_changes.push_back(this);
+}
+
+SpriteChange::~SpriteChange()
+{
+ all_sprite_changes.remove(this);
+}
+
+void
+SpriteChange::draw(DrawingContext& context)
+{
+ if(in_stay_action && stay_action != "") {
+ sprite->set_action(stay_action);
+ sprite->draw(context, pos * 32, LAYER_OBJECTS-1);
+ }
+}
+
+void
+SpriteChange::update(float )
+{
+}
+
+void
+SpriteChange::set_stay_action()
+{
+ in_stay_action = true;
+}
+
+void
+SpriteChange::clear_stay_action()
+{
+ in_stay_action = false;
+
+ // if we are in a stay_group, also clear all stay actions in this group
+ if (stay_group != "") {
+ for (std::list<SpriteChange*>::iterator i = all_sprite_changes.begin(); i != all_sprite_changes.end(); i++) {
+ SpriteChange* sc = *i;
+ if (sc->stay_group != stay_group) continue;
+ sc->in_stay_action = false;
+ }
+ }
+}
+
+std::list<SpriteChange*> SpriteChange::all_sprite_changes;
+
+}
--- /dev/null
+// $Id$
+//
+// SuperTux
+// Copyright (C) 2006 Matthias Braun <matze@braunis.de>
+//
+// 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 __WORLDMAP_SPRITE_CHANGE_HPP__
+#define __WORLDMAP_SPRITE_CHANGE_HPP__
+
+#include <string>
+#include <memory>
+#include <list>
+#include "game_object.hpp"
+#include "lisp/lisp.hpp"
+#include "math/vector.hpp"
+
+class Sprite;
+
+namespace WorldMapNS
+{
+
+class SpriteChange : public GameObject
+{
+public:
+ SpriteChange(const lisp::Lisp* lisp);
+ virtual ~SpriteChange();
+
+ Vector pos;
+ /**
+ * should tuxs sprite change when the tile has been completely entered,
+ * or already when the tile was just touched
+ */
+ bool change_on_touch;
+ /// sprite to change tux image to
+ std::auto_ptr<Sprite> sprite;
+ /**
+ * stay action can be used for objects like boats or cars, if it is
+ * != "" then this sprite will be displayed when tux left the tile towards
+ * another SpriteChange object.
+ */
+ std::string stay_action;
+
+ /**
+ * name of a group in which only one SpriteChange will ever have its stay_action displayed.
+ * Leave empty if you don't care.
+ */
+ std::string stay_group;
+
+ virtual void draw(DrawingContext& context);
+ virtual void update(float elapsed_time);
+
+ /**
+ * Activates the SpriteChange's stay action, if applicable
+ */
+ void set_stay_action();
+
+ /**
+ * Deactivates the SpriteChange's stay action, if applicable
+ */
+ void clear_stay_action();
+
+private:
+ /**
+ * should the stayaction be displayed
+ */
+ bool in_stay_action;
+
+ static std::list<SpriteChange*> all_sprite_changes;
+
+};
+
+}
+
+#endif
--- /dev/null
+// $Id$
+//
+// SuperTux - Teleporter Worldmap Tile
+// Copyright (C) 2006 Christoph Sommer <christoph.sommer@2006.expires.deltadevelopment.de>
+//
+// 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 <config.h>
+
+#include "worldmap/teleporter.hpp"
+#include "sprite/sprite_manager.hpp"
+#include "sprite/sprite.hpp"
+#include "video/drawing_context.hpp"
+
+namespace WorldMapNS
+{
+
+Teleporter::Teleporter(const lisp::Lisp* lisp)
+ : automatic(false)
+{
+ lisp->get("x", pos.x);
+ lisp->get("y", pos.y);
+
+ std::string spritefile = "";
+ if (lisp->get("sprite", spritefile)) {
+ sprite.reset(sprite_manager->create(spritefile));
+ }
+
+ lisp->get("worldmap", worldmap);
+ lisp->get("spawnpoint", spawnpoint);
+ lisp->get("automatic", automatic);
+ lisp->get("message", message);
+}
+
+void
+Teleporter::draw(DrawingContext& context)
+{
+ if (sprite.get() != 0) sprite->draw(context, pos*32 + Vector(16, 16), LAYER_OBJECTS - 1);
+}
+
+void
+Teleporter::update(float )
+{
+}
+
+}
--- /dev/null
+// $Id$
+//
+// SuperTux - Teleporter Worldmap Tile
+// Copyright (C) 2006 Christoph Sommer <christoph.sommer@2006.expires.deltadevelopment.de>
+//
+// 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 __WORLDMAP_TELEPORTER_HPP__
+#define __WORLDMAP_TELEPORTER_HPP__
+
+#include <memory>
+#include <string>
+#include "game_object.hpp"
+#include "math/vector.hpp"
+#include "lisp/lisp.hpp"
+
+class Sprite;
+
+namespace WorldMapNS
+{
+
+class Teleporter : public GameObject
+{
+public:
+ Teleporter(const lisp::Lisp* lisp);
+
+ virtual void draw(DrawingContext& context);
+ virtual void update(float elapsed_time);
+
+ /** Position (in tiles, not pixels) */
+ Vector pos;
+
+ /** Sprite to render, or 0 for no sprite */
+ std::auto_ptr<Sprite> sprite;
+
+ /** Worldmap filename (relative to data root) to teleport to. Leave empty to use current word */
+ std::string worldmap;
+
+ /** Spawnpoint to teleport to. Leave empty to use "main" or last one */
+ std::string spawnpoint;
+
+ /** true if this teleporter does not need to be activated, but teleports Tux as soon as it's touched */
+ bool automatic;
+
+ /** optional map message to display */
+ std::string message;
+
+};
+
+}
+
+#endif
--- /dev/null
+// $Id$
+//
+// SuperTux - A Jump'n Run
+// Copyright (C) 2004 Ingo Ruhnke <grumbel@gmx.de>
+// Copyright (C) 2006 Christoph Sommer <christoph.sommer@2006.expires.deltadevelopment.de>
+//
+// 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 <config.h>
+
+#include "tux.hpp"
+#include "sprite/sprite.hpp"
+#include "sprite/sprite_manager.hpp"
+#include "video/drawing_context.hpp"
+#include "player_status.hpp"
+#include "worldmap.hpp"
+#include "worldmap/level.hpp"
+#include "special_tile.hpp"
+#include "sprite_change.hpp"
+#include "control/joystickkeyboardcontroller.hpp"
+#include "scripting/squirrel_util.hpp"
+#include "main.hpp"
+
+namespace WorldMapNS
+{
+
+static const float TUXSPEED = 200;
+static const float map_message_TIME = 2.8f;
+
+Tux::Tux(WorldMap* worldmap_)
+ : worldmap(worldmap_)
+{
+ sprite.reset(sprite_manager->create("images/worldmap/common/tux.sprite"));
+
+ offset = 0;
+ moving = false;
+ direction = D_NONE;
+ input_direction = D_NONE;
+}
+
+Tux::~Tux()
+{
+}
+
+void
+Tux::draw(DrawingContext& context)
+{
+ switch (player_status->bonus) {
+ case GROWUP_BONUS:
+ sprite->set_action(moving ? "large-walking" : "large-stop");
+ break;
+ case FIRE_BONUS:
+ sprite->set_action(moving ? "fire-walking" : "fire-stop");
+ break;
+ case NO_BONUS:
+ sprite->set_action(moving ? "small-walking" : "small-stop");
+ break;
+ default:
+ log_debug << "Bonus type not handled in worldmap." << std::endl;
+ sprite->set_action("large-stop");
+ break;
+ }
+
+ sprite->draw(context, get_pos(), LAYER_OBJECTS);
+}
+
+
+Vector
+Tux::get_pos()
+{
+ float x = tile_pos.x * 32;
+ float y = tile_pos.y * 32;
+
+ switch(direction)
+ {
+ case D_WEST:
+ x -= offset - 32;
+ break;
+ case D_EAST:
+ x += offset - 32;
+ break;
+ case D_NORTH:
+ y -= offset - 32;
+ break;
+ case D_SOUTH:
+ y += offset - 32;
+ break;
+ case D_NONE:
+ break;
+ }
+
+ return Vector(x, y);
+}
+
+void
+Tux::stop()
+{
+ offset = 0;
+ direction = D_NONE;
+ input_direction = D_NONE;
+ moving = false;
+}
+
+void
+Tux::set_direction(Direction dir)
+{
+ input_direction = dir;
+}
+
+void
+Tux::tryStartWalking()
+{
+ if (moving)
+ return;
+ if (input_direction == D_NONE)
+ return;
+
+ LevelTile* level = worldmap->at_level();
+
+ // We got a new direction, so lets start walking when possible
+ Vector next_tile;
+ if ((!level || level->solved)
+ && worldmap->path_ok(input_direction, tile_pos, &next_tile)) {
+ tile_pos = next_tile;
+ moving = true;
+ direction = input_direction;
+ back_direction = reverse_dir(direction);
+ } else if (input_direction == back_direction) {
+ moving = true;
+ direction = input_direction;
+ tile_pos = worldmap->get_next_tile(tile_pos, direction);
+ back_direction = reverse_dir(direction);
+ }
+}
+
+bool
+Tux::canWalk(int tile_data, Direction dir)
+{
+ return ((tile_data & Tile::WORLDMAP_NORTH && dir == D_NORTH) ||
+ (tile_data & Tile::WORLDMAP_SOUTH && dir == D_SOUTH) ||
+ (tile_data & Tile::WORLDMAP_EAST && dir == D_EAST) ||
+ (tile_data & Tile::WORLDMAP_WEST && dir == D_WEST));
+}
+
+void
+Tux::tryContinueWalking(float elapsed_time)
+{
+ if (!moving)
+ return;
+
+ // Let tux walk
+ offset += TUXSPEED * elapsed_time;
+
+ // Do nothing if we have not yet reached the next tile
+ if (offset <= 32)
+ return;
+
+ offset -= 32;
+
+ SpriteChange* sprite_change = worldmap->at_sprite_change(tile_pos);
+ if(sprite_change != NULL) {
+ sprite.reset(new Sprite( *(sprite_change->sprite.get()) ));
+ sprite_change->clear_stay_action();
+ }
+
+ // if this is a special_tile with passive_message, display it
+ SpecialTile* special_tile = worldmap->at_special_tile();
+ if(special_tile)
+ {
+ // direction and the apply_action_ are opposites, since they "see"
+ // directions in a different way
+ if((direction == D_NORTH && special_tile->apply_action_south) ||
+ (direction == D_SOUTH && special_tile->apply_action_north) ||
+ (direction == D_WEST && special_tile->apply_action_east) ||
+ (direction == D_EAST && special_tile->apply_action_west))
+ {
+ if(special_tile->passive_message) {
+ worldmap->passive_message = special_tile->map_message;
+ worldmap->passive_message_timer.start(map_message_TIME);
+ } else if(special_tile->script != "") {
+ try {
+ std::istringstream in(special_tile->script);
+ worldmap->run_script(in, "specialtile");
+ } catch(std::exception& e) {
+ log_warning << "Couldn't execute special tile script: " << e.what()
+ << std::endl;
+ }
+ }
+ }
+ }
+
+ // check if we are at a Teleporter
+ Teleporter* teleporter = worldmap->at_teleporter(tile_pos);
+
+ // stop if we reached a level, a WORLDMAP_STOP tile, a teleporter or a special tile without a passive_message
+ if ((worldmap->at_level())
+ || (worldmap->tile_data_at(tile_pos) & Tile::WORLDMAP_STOP)
+ || (special_tile && !special_tile->passive_message
+ && special_tile->script == "")
+ || (teleporter)) {
+ if(special_tile && !special_tile->map_message.empty()
+ && !special_tile->passive_message)
+ worldmap->passive_message_timer.start(0);
+ stop();
+ return;
+ }
+
+ // if user wants to change direction, try changing, else guess the direction in which to walk next
+ const int tile_data = worldmap->tile_data_at(tile_pos);
+ if ((direction != input_direction) && canWalk(tile_data, input_direction)) {
+ direction = input_direction;
+ back_direction = reverse_dir(direction);
+ } else {
+ Direction dir = D_NONE;
+ if (tile_data & Tile::WORLDMAP_NORTH && back_direction != D_NORTH)
+ dir = D_NORTH;
+ else if (tile_data & Tile::WORLDMAP_SOUTH && back_direction != D_SOUTH)
+ dir = D_SOUTH;
+ else if (tile_data & Tile::WORLDMAP_EAST && back_direction != D_EAST)
+ dir = D_EAST;
+ else if (tile_data & Tile::WORLDMAP_WEST && back_direction != D_WEST)
+ dir = D_WEST;
+
+ if (dir == D_NONE) {
+ // Should never be reached if tiledata is good
+ log_warning << "Could not determine where to walk next" << std::endl;
+ stop();
+ return;
+ }
+
+ direction = dir;
+ input_direction = direction;
+ back_direction = reverse_dir(direction);
+ }
+
+ // Walk automatically to the next tile
+ if(direction == D_NONE)
+ return;
+
+ Vector next_tile;
+ if (!worldmap->path_ok(direction, tile_pos, &next_tile)) {
+ log_debug << "Tilemap data is buggy" << std::endl;
+ stop();
+ return;
+ }
+
+ SpriteChange* next_sprite = worldmap->at_sprite_change(next_tile);
+ if(next_sprite != NULL && next_sprite->change_on_touch) {
+ sprite.reset(new Sprite( *(next_sprite->sprite.get()) ));
+ next_sprite->clear_stay_action();
+ }
+ SpriteChange* last_sprite = worldmap->at_sprite_change(tile_pos);
+ if(last_sprite != NULL && next_sprite != NULL) {
+ log_debug << "Old: " << tile_pos << " New: " << next_tile << std::endl;
+ last_sprite->set_stay_action();
+ }
+
+ tile_pos = next_tile;
+}
+
+void
+Tux::updateInputDirection()
+{
+ if(main_controller->hold(Controller::UP))
+ input_direction = D_NORTH;
+ else if(main_controller->hold(Controller::DOWN))
+ input_direction = D_SOUTH;
+ else if(main_controller->hold(Controller::LEFT))
+ input_direction = D_WEST;
+ else if(main_controller->hold(Controller::RIGHT))
+ input_direction = D_EAST;
+}
+
+void
+Tux::update(float elapsed_time)
+{
+ updateInputDirection();
+ if (moving)
+ tryContinueWalking(elapsed_time);
+ else
+ tryStartWalking();
+}
+
+void
+Tux::setup()
+{
+ // check if we already touch a SpriteChange object
+ SpriteChange* sprite_change = worldmap->at_sprite_change(tile_pos);
+ if(sprite_change != NULL) {
+ sprite.reset(new Sprite( *(sprite_change->sprite.get()) ));
+ sprite_change->clear_stay_action();
+ }
+}
+
+}
--- /dev/null
+// $Id$
+//
+// SuperTux - A Jump'n Run
+// Copyright (C) 2004 Ingo Ruhnke <grumbel@gmx.de>
+// Copyright (C) 2006 Christoph Sommer <christoph.sommer@2006.expires.deltadevelopment.de>
+//
+// 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 __WORLDMAP_TUX_HPP__
+#define __WORLDMAP_TUX_HPP__
+
+#include <memory>
+#include "game_object.hpp"
+#include "worldmap.hpp"
+
+class Sprite;
+
+namespace WorldMapNS
+{
+
+class WorldMap;
+
+class Tux : public GameObject
+{
+public:
+ Direction back_direction;
+private:
+ WorldMap* worldmap;
+ std::auto_ptr<Sprite> sprite;
+ Controller* controller;
+
+ Direction input_direction;
+ Direction direction;
+ Vector tile_pos;
+ /** Length by which tux is away from its current tile, length is in
+ input_direction direction */
+ float offset;
+ bool moving;
+
+ void stop();
+
+ bool canWalk(int tile_data, Direction dir); /**< check if we can leave a tile (with given "tile_data") in direction "dir" */
+ void updateInputDirection(); /**< if controller was pressed, update input_direction */
+ void tryStartWalking(); /**< try starting to walk in input_direction */
+ void tryContinueWalking(float elapsed_time); /**< try to continue walking in current direction */
+
+public:
+ Tux(WorldMap* worldmap_);
+ ~Tux();
+
+ void setup(); /**< called prior to first update */
+ void draw(DrawingContext& context);
+ void update(float elapsed_time);
+
+ void set_direction(Direction dir);
+
+ bool is_moving() const { return moving; }
+ Vector get_pos();
+ Vector get_tile_pos() const { return tile_pos; }
+ void set_tile_pos(Vector p) { tile_pos = p; }
+};
+
+}
+
+#endif
--- /dev/null
+// $Id$
+//
+// SuperTux - A Jump'n Run
+// Copyright (C) 2004 Ingo Ruhnke <grumbel@gmx.de>
+// Copyright (C) 2006 Christoph Sommer <christoph.sommer@2006.expires.deltadevelopment.de>
+//
+// 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 <config.h>
+
+#include <iostream>
+#include <fstream>
+#include <vector>
+#include <cassert>
+#include <stdexcept>
+#include <sstream>
+#include <unistd.h>
+#include <physfs.h>
+
+#include "worldmap.hpp"
+
+#include "gettext.hpp"
+#include "log.hpp"
+#include "mainloop.hpp"
+#include "shrinkfade.hpp"
+#include "video/surface.hpp"
+#include "video/drawing_context.hpp"
+#include "sprite/sprite.hpp"
+#include "sprite/sprite_manager.hpp"
+#include "audio/sound_manager.hpp"
+#include "lisp/parser.hpp"
+#include "lisp/lisp.hpp"
+#include "lisp/list_iterator.hpp"
+#include "lisp/writer.hpp"
+#include "game_session.hpp"
+#include "sector.hpp"
+#include "worldmap.hpp"
+#include "resources.hpp"
+#include "log.hpp"
+#include "world.hpp"
+#include "player_status.hpp"
+#include "textscroller.hpp"
+#include "main.hpp"
+#include "spawn_point.hpp"
+#include "file_system.hpp"
+#include "gui/menu.hpp"
+#include "gui/mousecursor.hpp"
+#include "control/joystickkeyboardcontroller.hpp"
+#include "object/background.hpp"
+#include "object/tilemap.hpp"
+#include "options_menu.hpp"
+#include "scripting/squirrel_error.hpp"
+#include "scripting/squirrel_util.hpp"
+#include "worldmap/level.hpp"
+#include "worldmap/special_tile.hpp"
+#include "worldmap/tux.hpp"
+#include "worldmap/sprite_change.hpp"
+
+namespace WorldMapNS {
+
+enum WorldMapMenuIDs {
+ MNID_RETURNWORLDMAP,
+ MNID_QUITWORLDMAP
+};
+
+WorldMap* WorldMap::current_ = NULL;
+
+Direction reverse_dir(Direction direction)
+{
+ switch(direction)
+ {
+ case D_WEST:
+ return D_EAST;
+ case D_EAST:
+ return D_WEST;
+ case D_NORTH:
+ return D_SOUTH;
+ case D_SOUTH:
+ return D_NORTH;
+ case D_NONE:
+ return D_NONE;
+ }
+ return D_NONE;
+}
+
+std::string
+direction_to_string(Direction direction)
+{
+ switch(direction)
+ {
+ case D_WEST:
+ return "west";
+ case D_EAST:
+ return "east";
+ case D_NORTH:
+ return "north";
+ case D_SOUTH:
+ return "south";
+ default:
+ return "none";
+ }
+}
+
+Direction
+string_to_direction(const std::string& directory)
+{
+ if (directory == "west")
+ return D_WEST;
+ else if (directory == "east")
+ return D_EAST;
+ else if (directory == "north")
+ return D_NORTH;
+ else if (directory == "south")
+ return D_SOUTH;
+ else if (directory == "none")
+ return D_NONE;
+ else {
+ log_warning << "unknown direction: \"" << directory << "\"" << std::endl;
+ return D_NONE;
+ }
+}
+
+//---------------------------------------------------------------------------
+
+WorldMap::WorldMap(const std::string& filename, const std::string& force_spawnpoint)
+ : tux(0), ambient_light( 1.0f, 1.0f, 1.0f, 1.0f ), force_spawnpoint(force_spawnpoint), in_level(false)
+{
+ tile_manager.reset(new TileManager("images/worldmap.strf"));
+
+ tux = new Tux(this);
+ add_object(tux);
+
+ name = "<no title>";
+ music = "music/salcon.ogg";
+
+ total_stats.reset();
+
+ worldmap_menu.reset(new Menu());
+ worldmap_menu->add_label(_("Pause"));
+ worldmap_menu->add_hl();
+ worldmap_menu->add_entry(MNID_RETURNWORLDMAP, _("Continue"));
+ worldmap_menu->add_submenu(_("Options"), get_options_menu());
+ worldmap_menu->add_hl();
+ worldmap_menu->add_entry(MNID_QUITWORLDMAP, _("Quit World"));
+
+ // create a new squirrel table for the worldmap
+ using namespace Scripting;
+
+ sq_collectgarbage(global_vm);
+ sq_newtable(global_vm);
+ sq_pushroottable(global_vm);
+ if(SQ_FAILED(sq_setdelegate(global_vm, -2)))
+ throw Scripting::SquirrelError(global_vm, "Couldn't set worldmap_table delegate");
+
+ sq_resetobject(&worldmap_table);
+ if(SQ_FAILED(sq_getstackobj(global_vm, -1, &worldmap_table)))
+ throw Scripting::SquirrelError(global_vm, "Couldn't get table from stack");
+
+ sq_addref(global_vm, &worldmap_table);
+ sq_pop(global_vm, 1);
+
+ // load worldmap objects
+ load(filename);
+}
+
+WorldMap::~WorldMap()
+{
+ using namespace Scripting;
+
+ for(GameObjects::iterator i = game_objects.begin();
+ i != game_objects.end(); ++i) {
+ GameObject* object = *i;
+ try_unexpose(object);
+ object->unref();
+ }
+
+ for(SpawnPoints::iterator i = spawn_points.begin();
+ i != spawn_points.end(); ++i) {
+ delete *i;
+ }
+
+ for(ScriptList::iterator i = scripts.begin();
+ i != scripts.end(); ++i) {
+ HSQOBJECT& object = *i;
+ sq_release(global_vm, &object);
+ }
+ sq_release(global_vm, &worldmap_table);
+
+ sq_collectgarbage(global_vm);
+
+ if(current_ == this)
+ current_ = NULL;
+}
+
+void
+WorldMap::add_object(GameObject* object)
+{
+ TileMap* tilemap = dynamic_cast<TileMap*> (object);
+ if(tilemap != 0 && tilemap->is_solid()) {
+ solid_tilemaps.push_back(tilemap);
+ }
+
+ object->ref();
+ try_expose(object);
+ game_objects.push_back(object);
+}
+
+void
+WorldMap::try_expose(GameObject* object)
+{
+ ScriptInterface* interface = dynamic_cast<ScriptInterface*> (object);
+ if(interface != NULL) {
+ HSQUIRRELVM vm = Scripting::global_vm;
+ sq_pushobject(vm, worldmap_table);
+ interface->expose(vm, -1);
+ sq_pop(vm, 1);
+ }
+}
+
+void
+WorldMap::try_unexpose(GameObject* object)
+{
+ ScriptInterface* interface = dynamic_cast<ScriptInterface*> (object);
+ if(interface != NULL) {
+ HSQUIRRELVM vm = Scripting::global_vm;
+ SQInteger oldtop = sq_gettop(vm);
+ sq_pushobject(vm, worldmap_table);
+ try {
+ interface->unexpose(vm, -1);
+ } catch(std::exception& e) {
+ log_warning << "Couldn't unregister object: " << e.what() << std::endl;
+ }
+ sq_settop(vm, oldtop);
+ }
+}
+
+void
+WorldMap::move_to_spawnpoint(const std::string& spawnpoint)
+{
+ for(SpawnPoints::iterator i = spawn_points.begin(); i != spawn_points.end(); ++i) {
+ SpawnPoint* sp = *i;
+ if(sp->name == spawnpoint) {
+ Vector p = sp->pos;
+ tux->set_tile_pos(p);
+ tux->set_direction(sp->auto_dir);
+ return;
+ }
+ }
+ log_warning << "Spawnpoint '" << spawnpoint << "' not found." << std::endl;
+ if (spawnpoint != "main") {
+ move_to_spawnpoint("main");
+ }
+}
+
+void
+WorldMap::change(const std::string& filename, const std::string& force_spawnpoint)
+{
+ main_loop->exit_screen();
+ main_loop->push_screen(new WorldMap(filename, force_spawnpoint));
+}
+
+void
+WorldMap::load(const std::string& filename)
+{
+ map_filename = filename;
+ levels_path = FileSystem::dirname(map_filename);
+
+ try {
+ lisp::Parser parser;
+ const lisp::Lisp* root = parser.parse(map_filename);
+
+ const lisp::Lisp* lisp = root->get_lisp("supertux-level");
+ if(!lisp)
+ throw std::runtime_error("file isn't a supertux-level file.");
+
+ lisp->get("name", name);
+
+ const lisp::Lisp* sector = lisp->get_lisp("sector");
+ if(!sector)
+ throw std::runtime_error("No sector sepcified in worldmap file.");
+
+ lisp::ListIterator iter(sector);
+ while(iter.next()) {
+ if(iter.item() == "tilemap") {
+ add_object(new TileMap(*(iter.lisp()), tile_manager.get()));
+ } else if(iter.item() == "background") {
+ add_object(new Background(*(iter.lisp())));
+ } else if(iter.item() == "music") {
+ iter.value()->get(music);
+ } else if(iter.item() == "init-script") {
+ iter.value()->get(init_script);
+ } else if(iter.item() == "worldmap-spawnpoint") {
+ SpawnPoint* sp = new SpawnPoint(iter.lisp());
+ spawn_points.push_back(sp);
+ } else if(iter.item() == "level") {
+ LevelTile* level = new LevelTile(levels_path, iter.lisp());
+ levels.push_back(level);
+ add_object(level);
+ } else if(iter.item() == "special-tile") {
+ SpecialTile* special_tile = new SpecialTile(iter.lisp());
+ special_tiles.push_back(special_tile);
+ add_object(special_tile);
+ } else if(iter.item() == "sprite-change") {
+ SpriteChange* sprite_change = new SpriteChange(iter.lisp());
+ sprite_changes.push_back(sprite_change);
+ add_object(sprite_change);
+ } else if(iter.item() == "teleporter") {
+ Teleporter* teleporter = new Teleporter(iter.lisp());
+ teleporters.push_back(teleporter);
+ add_object(teleporter);
+ } else if(iter.item() == "ambient-light") {
+ std::vector<float> vColor;
+ sector->get_vector( "ambient-light", vColor );
+ if(vColor.size() < 3) {
+ log_warning << "(ambient-light) requires a color as argument" << std::endl;
+ } else {
+ ambient_light = Color( vColor );
+ }
+ } else if(iter.item() == "name") {
+ // skip
+ } else {
+ log_warning << "Unknown token '" << iter.item() << "' in worldmap" << std::endl;
+ }
+ }
+ if(solid_tilemaps.size() == 0)
+ throw std::runtime_error("No solid tilemap specified");
+
+ move_to_spawnpoint("main");
+
+ } catch(std::exception& e) {
+ std::stringstream msg;
+ msg << "Problem when parsing worldmap '" << map_filename << "': " <<
+ e.what();
+ throw std::runtime_error(msg.str());
+ }
+}
+
+void
+WorldMap::get_level_title(LevelTile& level)
+{
+ /** get special_tile's title */
+ level.title = "<no title>";
+
+ try {
+ lisp::Parser parser;
+ const lisp::Lisp* root = parser.parse(levels_path + level.get_name());
+
+ const lisp::Lisp* level_lisp = root->get_lisp("supertux-level");
+ if(!level_lisp)
+ return;
+
+ level_lisp->get("name", level.title);
+ } catch(std::exception& e) {
+ log_warning << "Problem when reading leveltitle: " << e.what() << std::endl;
+ return;
+ }
+}
+
+void WorldMap::calculate_total_stats()
+{
+ total_stats.zero();
+ for(LevelTiles::iterator i = levels.begin(); i != levels.end(); ++i) {
+ LevelTile* level = *i;
+ if (level->solved) {
+ total_stats += level->statistics;
+ }
+ }
+}
+
+void
+WorldMap::on_escape_press()
+{
+ // Show or hide the menu
+ if(!Menu::current()) {
+ Menu::set_current(worldmap_menu.get());
+ tux->set_direction(D_NONE); // stop tux movement when menu is called
+ } else {
+ Menu::set_current(NULL);
+ }
+}
+
+Vector
+WorldMap::get_next_tile(Vector pos, Direction direction)
+{
+ switch(direction) {
+ case D_WEST:
+ pos.x -= 1;
+ break;
+ case D_EAST:
+ pos.x += 1;
+ break;
+ case D_NORTH:
+ pos.y -= 1;
+ break;
+ case D_SOUTH:
+ pos.y += 1;
+ break;
+ case D_NONE:
+ break;
+ }
+ return pos;
+}
+
+bool
+WorldMap::path_ok(Direction direction, const Vector& old_pos, Vector* new_pos)
+{
+ *new_pos = get_next_tile(old_pos, direction);
+
+ if (!(new_pos->x >= 0 && new_pos->x < get_width()
+ && new_pos->y >= 0 && new_pos->y < get_height()))
+ { // New position is outsite the tilemap
+ return false;
+ }
+ else
+ { // Check if the tile allows us to go to new_pos
+ int old_tile_data = tile_data_at(old_pos);
+ int new_tile_data = tile_data_at(*new_pos);
+ switch(direction)
+ {
+ case D_WEST:
+ return (old_tile_data & Tile::WORLDMAP_WEST
+ && new_tile_data & Tile::WORLDMAP_EAST);
+
+ case D_EAST:
+ return (old_tile_data & Tile::WORLDMAP_EAST
+ && new_tile_data & Tile::WORLDMAP_WEST);
+
+ case D_NORTH:
+ return (old_tile_data & Tile::WORLDMAP_NORTH
+ && new_tile_data & Tile::WORLDMAP_SOUTH);
+
+ case D_SOUTH:
+ return (old_tile_data & Tile::WORLDMAP_SOUTH
+ && new_tile_data & Tile::WORLDMAP_NORTH);
+
+ case D_NONE:
+ assert(!"path_ok() can't walk if direction is NONE");
+ }
+ return false;
+ }
+}
+
+void
+WorldMap::finished_level(Level* gamelevel)
+{
+ // TODO use Level* parameter here?
+ LevelTile* level = at_level();
+
+ bool old_level_state = level->solved;
+ level->solved = true;
+ level->sprite->set_action("solved");
+
+ // deal with statistics
+ level->statistics.merge(gamelevel->stats);
+ calculate_total_stats();
+
+ save_state();
+
+ if (old_level_state != level->solved) {
+ // Try to detect the next direction to which we should walk
+ // FIXME: Mostly a hack
+ Direction dir = D_NONE;
+
+ int dirdata = available_directions_at(tux->get_tile_pos());
+ // first, test for crossroads
+ if (dirdata == Tile::WORLDMAP_CNSE ||
+ dirdata == Tile::WORLDMAP_CNSW ||
+ dirdata == Tile::WORLDMAP_CNEW ||
+ dirdata == Tile::WORLDMAP_CSEW ||
+ dirdata == Tile::WORLDMAP_CNSEW)
+ dir = D_NONE;
+ else if (dirdata & Tile::WORLDMAP_NORTH
+ && tux->back_direction != D_NORTH)
+ dir = D_NORTH;
+ else if (dirdata & Tile::WORLDMAP_SOUTH
+ && tux->back_direction != D_SOUTH)
+ dir = D_SOUTH;
+ else if (dirdata & Tile::WORLDMAP_EAST
+ && tux->back_direction != D_EAST)
+ dir = D_EAST;
+ else if (dirdata & Tile::WORLDMAP_WEST
+ && tux->back_direction != D_WEST)
+ dir = D_WEST;
+
+ if (dir != D_NONE) {
+ tux->set_direction(dir);
+ }
+ }
+
+ if (level->extro_script != "") {
+ try {
+ std::istringstream in(level->extro_script);
+ run_script(in, "worldmap:extro_script");
+ } catch(std::exception& e) {
+ log_fatal << "Couldn't run level-extro-script: " << e.what() << std::endl;
+ }
+ }
+}
+
+void
+WorldMap::update(float delta)
+{
+ if(!in_level) {
+ Menu* menu = Menu::current();
+ if(menu != NULL) {
+ menu->update();
+
+ if(menu == worldmap_menu.get()) {
+ switch (worldmap_menu->check())
+ {
+ case MNID_RETURNWORLDMAP: // Return to game
+ Menu::set_current(0);
+ break;
+ case MNID_QUITWORLDMAP: // Quit Worldmap
+ main_loop->exit_screen();
+ break;
+ }
+ }
+
+ return;
+ }
+
+ // update GameObjects
+ for(size_t i = 0; i < game_objects.size(); ++i) {
+ GameObject* object = game_objects[i];
+ object->update(delta);
+ }
+
+ // remove old GameObjects
+ for(GameObjects::iterator i = game_objects.begin();
+ i != game_objects.end(); ) {
+ GameObject* object = *i;
+ if(!object->is_valid()) {
+ try_unexpose(object);
+ object->unref();
+ i = game_objects.erase(i);
+ } else {
+ ++i;
+ }
+ }
+
+ /* update solid_tilemaps list */
+ //FIXME: this could be more efficient
+ solid_tilemaps.clear();
+ for(std::vector<GameObject*>::iterator i = game_objects.begin();
+ i != game_objects.end(); ++i)
+ {
+ TileMap* tm = dynamic_cast<TileMap*>(*i);
+ if (!tm) continue;
+ if (tm->is_solid()) solid_tilemaps.push_back(tm);
+ }
+
+ // position "camera"
+ Vector tux_pos = tux->get_pos();
+ camera_offset.x = tux_pos.x - SCREEN_WIDTH/2;
+ camera_offset.y = tux_pos.y - SCREEN_HEIGHT/2;
+
+ if (camera_offset.x < 0)
+ camera_offset.x = 0;
+ if (camera_offset.y < 0)
+ camera_offset.y = 0;
+
+ if (camera_offset.x > (int)get_width()*32 - SCREEN_WIDTH)
+ camera_offset.x = (int)get_width()*32 - SCREEN_WIDTH;
+ if (camera_offset.y > (int)get_height()*32 - SCREEN_HEIGHT)
+ camera_offset.y = (int)get_height()*32 - SCREEN_HEIGHT;
+
+ if (int(get_width()*32) < SCREEN_WIDTH)
+ camera_offset.x = get_width()*16.0 - SCREEN_WIDTH/2.0;
+ if (int(get_height()*32) < SCREEN_HEIGHT)
+ camera_offset.y = get_height()*16.0 - SCREEN_HEIGHT/2.0;
+
+ // handle input
+ bool enter_level = false;
+ if(main_controller->pressed(Controller::ACTION)
+ || main_controller->pressed(Controller::JUMP)
+ || main_controller->pressed(Controller::MENU_SELECT)) {
+ /* some people define UP and JUMP on the same key... */
+ if(!main_controller->pressed(Controller::UP))
+ enter_level = true;
+ }
+ if(main_controller->pressed(Controller::PAUSE_MENU))
+ on_escape_press();
+
+ // check for teleporters
+ Teleporter* teleporter = at_teleporter(tux->get_tile_pos());
+ if (teleporter && (teleporter->automatic || (enter_level && (!tux->is_moving())))) {
+ enter_level = false;
+ if (teleporter->worldmap != "") {
+ change(teleporter->worldmap, teleporter->spawnpoint);
+ } else {
+ // TODO: an animation, camera scrolling or a fading would be a nice touch
+ sound_manager->play("sounds/warp.wav");
+ tux->back_direction = D_NONE;
+ move_to_spawnpoint(teleporter->spawnpoint);
+ }
+ }
+
+ // check for auto-play levels
+ LevelTile* level = at_level();
+ if (level && (level->auto_play) && (!level->solved) && (!tux->is_moving())) {
+ enter_level = true;
+ }
+
+ if (enter_level && !tux->is_moving())
+ {
+ /* Check level action */
+ LevelTile* level = at_level();
+ if (!level) {
+ //Respawn if player on a tile with no level and nowhere to go.
+ int tile_data = tile_data_at(tux->get_tile_pos());
+ if(!( tile_data & ( Tile::WORLDMAP_NORTH | Tile::WORLDMAP_SOUTH | Tile::WORLDMAP_WEST | Tile::WORLDMAP_EAST ))){
+ log_warning << "Player at illegal position " << tux->get_tile_pos().x << ", " << tux->get_tile_pos().y << " respawning." << std::endl;
+ move_to_spawnpoint("main");
+ return;
+ }
+ log_warning << "No level to enter at: " << tux->get_tile_pos().x << ", " << tux->get_tile_pos().y << std::endl;
+ return;
+ }
+
+ if (level->pos == tux->get_tile_pos()) {
+ try {
+ Vector shrinkpos = Vector(level->pos.x*32 + 16 - camera_offset.x,
+ level->pos.y*32 + 16 - camera_offset.y);
+ std::string levelfile = levels_path + level->get_name();
+
+ // update state and savegame
+ save_state();
+
+ main_loop->push_screen(new GameSession(levelfile, &level->statistics),
+ new ShrinkFade(shrinkpos, 0.5));
+ in_level = true;
+ } catch(std::exception& e) {
+ log_fatal << "Couldn't load level: " << e.what() << std::endl;
+ }
+ }
+ }
+ else
+ {
+ // tux->set_direction(input_direction);
+ }
+ }
+}
+
+int
+WorldMap::tile_data_at(Vector p)
+{
+ int dirs = 0;
+
+ for(std::list<TileMap*>::const_iterator i = solid_tilemaps.begin(); i != solid_tilemaps.end(); i++) {
+ TileMap* tilemap = *i;
+ const Tile* tile = tilemap->get_tile((int)p.x, (int)p.y);
+ int dirdata = tile->getData();
+ dirs |= dirdata;
+ }
+
+ return dirs;
+}
+
+int
+WorldMap::available_directions_at(Vector p)
+{
+ return tile_data_at(p) & Tile::WORLDMAP_DIR_MASK;
+}
+
+LevelTile*
+WorldMap::at_level()
+{
+ for(LevelTiles::iterator i = levels.begin(); i != levels.end(); ++i) {
+ LevelTile* level = *i;
+ if (level->pos == tux->get_tile_pos())
+ return level;
+ }
+
+ return NULL;
+}
+
+SpecialTile*
+WorldMap::at_special_tile()
+{
+ for(SpecialTiles::iterator i = special_tiles.begin();
+ i != special_tiles.end(); ++i) {
+ SpecialTile* special_tile = *i;
+ if (special_tile->pos == tux->get_tile_pos())
+ return special_tile;
+ }
+
+ return NULL;
+}
+
+SpriteChange*
+WorldMap::at_sprite_change(const Vector& pos)
+{
+ for(SpriteChanges::iterator i = sprite_changes.begin();
+ i != sprite_changes.end(); ++i) {
+ SpriteChange* sprite_change = *i;
+ if(sprite_change->pos == pos)
+ return sprite_change;
+ }
+
+ return NULL;
+}
+
+Teleporter*
+WorldMap::at_teleporter(const Vector& pos)
+{
+ for(std::vector<Teleporter*>::iterator i = teleporters.begin(); i != teleporters.end(); ++i) {
+ Teleporter* teleporter = *i;
+ if(teleporter->pos == pos) return teleporter;
+ }
+
+ return NULL;
+}
+
+void
+WorldMap::draw(DrawingContext& context)
+{
+ if (int(get_width()*32) < SCREEN_WIDTH || int(get_height()*32) < SCREEN_HEIGHT)
+ context.draw_filled_rect(Vector(0, 0), Vector(SCREEN_WIDTH, SCREEN_HEIGHT),
+ Color(0.0f, 0.0f, 0.0f, 1.0f), LAYER_BACKGROUND0);
+
+ context.set_ambient_color( ambient_light );
+ context.push_transform();
+ context.set_translation(camera_offset);
+
+ for(GameObjects::iterator i = game_objects.begin();
+ i != game_objects.end(); ++i) {
+ GameObject* object = *i;
+ object->draw(context);
+ }
+
+/*
+ // FIXME: make this a runtime switch similar to draw_collrects/show_collrects?
+ // draw visual indication of possible walk directions
+ static int flipme = 0;
+ if (flipme++ & 0x04)
+ for (int x = 0; x < get_width(); x++) {
+ for (int y = 0; y < get_height(); y++) {
+ int data = tile_data_at(Vector(x,y));
+ int px = x * 32;
+ int py = y * 32;
+ const int W = 4;
+ if (data & Tile::WORLDMAP_NORTH) context.draw_filled_rect(Rect(px + 16-W, py , px + 16+W, py + 16-W), Color(0.2f, 0.2f, 0.2f, 0.7f), LAYER_FOREGROUND1 + 1000);
+ if (data & Tile::WORLDMAP_SOUTH) context.draw_filled_rect(Rect(px + 16-W, py + 16+W, px + 16+W, py + 32 ), Color(0.2f, 0.2f, 0.2f, 0.7f), LAYER_FOREGROUND1 + 1000);
+ if (data & Tile::WORLDMAP_EAST) context.draw_filled_rect(Rect(px + 16+W, py + 16-W, px + 32 , py + 16+W), Color(0.2f, 0.2f, 0.2f, 0.7f), LAYER_FOREGROUND1 + 1000);
+ if (data & Tile::WORLDMAP_WEST) context.draw_filled_rect(Rect(px , py + 16-W, px + 16-W, py + 16+W), Color(0.2f, 0.2f, 0.2f, 0.7f), LAYER_FOREGROUND1 + 1000);
+ if (data & Tile::WORLDMAP_DIR_MASK) context.draw_filled_rect(Rect(px + 16-W, py + 16-W, px + 16+W, py + 16+W), Color(0.2f, 0.2f, 0.2f, 0.7f), LAYER_FOREGROUND1 + 1000);
+ if (data & Tile::WORLDMAP_STOP) context.draw_filled_rect(Rect(px + 4 , py + 4 , px + 28 , py + 28 ), Color(0.2f, 0.2f, 0.2f, 0.7f), LAYER_FOREGROUND1 + 1000);
+ }
+ }
+*/
+
+ draw_status(context);
+ context.pop_transform();
+}
+
+void
+WorldMap::draw_status(DrawingContext& context)
+{
+ context.push_transform();
+ context.set_translation(Vector(0, 0));
+
+ player_status->draw(context);
+
+ if (!tux->is_moving()) {
+ for(LevelTiles::iterator i = levels.begin(); i != levels.end(); ++i) {
+ LevelTile* level = *i;
+
+ if (level->pos == tux->get_tile_pos()) {
+ if(level->title == "")
+ get_level_title(*level);
+
+ context.draw_text(white_text, level->title,
+ Vector(SCREEN_WIDTH/2,
+ SCREEN_HEIGHT - white_text->get_height() - 30),
+ ALIGN_CENTER, LAYER_FOREGROUND1);
+
+ // if level is solved, draw level picture behind stats
+ /*
+ if (level->solved) {
+ if (const Surface* picture = level->get_picture()) {
+ Vector pos = Vector(SCREEN_WIDTH - picture->get_width(), SCREEN_HEIGHT - picture->get_height());
+ context.push_transform();
+ context.set_alpha(0.5);
+ context.draw_surface(picture, pos, LAYER_FOREGROUND1-1);
+ context.pop_transform();
+ }
+ }
+ */
+
+ level->statistics.draw_worldmap_info(context);
+ break;
+ }
+ }
+
+ for(SpecialTiles::iterator i = special_tiles.begin();
+ i != special_tiles.end(); ++i) {
+ SpecialTile* special_tile = *i;
+
+ if (special_tile->pos == tux->get_tile_pos()) {
+ /* Display an in-map message in the map, if any as been selected */
+ if(!special_tile->map_message.empty() && !special_tile->passive_message)
+ context.draw_text(gold_text, special_tile->map_message,
+ Vector(SCREEN_WIDTH/2,
+ SCREEN_HEIGHT - white_text->get_height() - 60),
+ ALIGN_CENTER, LAYER_FOREGROUND1);
+ break;
+ }
+ }
+
+ // display teleporter messages
+ Teleporter* teleporter = at_teleporter(tux->get_tile_pos());
+ if (teleporter && (teleporter->message != "")) {
+ Vector pos = Vector(SCREEN_WIDTH/2, SCREEN_HEIGHT - white_text->get_height() - 30);
+ context.draw_text(white_text, teleporter->message, pos, ALIGN_CENTER, LAYER_FOREGROUND1);
+ }
+
+ }
+
+ /* Display a passive message in the map, if needed */
+ if(passive_message_timer.started())
+ context.draw_text(gold_text, passive_message,
+ Vector(SCREEN_WIDTH/2, SCREEN_HEIGHT - white_text->get_height() - 60),
+ ALIGN_CENTER, LAYER_FOREGROUND1);
+
+ context.pop_transform();
+}
+
+void
+WorldMap::setup()
+{
+ sound_manager->play_music(music);
+ Menu::set_current(NULL);
+
+ current_ = this;
+ load_state();
+
+ // if force_spawnpoint was set, move Tux there, then clear force_spawnpoint
+ if (force_spawnpoint != "") {
+ move_to_spawnpoint(force_spawnpoint);
+ force_spawnpoint = "";
+ }
+
+ tux->setup();
+
+ // register worldmap_table as worldmap in scripting
+ using namespace Scripting;
+
+ sq_pushroottable(global_vm);
+ sq_pushstring(global_vm, "worldmap", -1);
+ sq_pushobject(global_vm, worldmap_table);
+ if(SQ_FAILED(sq_createslot(global_vm, -3)))
+ throw SquirrelError(global_vm, "Couldn't set worldmap in roottable");
+ sq_pop(global_vm, 1);
+
+ if(init_script != "") {
+ std::istringstream in(init_script);
+ run_script(in, "WorldMap::init");
+ }
+}
+
+void
+WorldMap::leave()
+{
+ using namespace Scripting;
+
+ // save state of world and player
+ save_state();
+
+ // remove worldmap_table from roottable
+ sq_pushroottable(global_vm);
+ sq_pushstring(global_vm, "worldmap", -1);
+ if(SQ_FAILED(sq_deleteslot(global_vm, -2, SQFalse)))
+ throw SquirrelError(global_vm, "Couldn't unset worldmap in roottable");
+ sq_pop(global_vm, 1);
+}
+
+void
+WorldMap::save_state()
+{
+ using namespace Scripting;
+
+ HSQUIRRELVM vm = global_vm;
+ int oldtop = sq_gettop(vm);
+
+ try {
+ // get state table
+ sq_pushroottable(vm);
+ sq_pushstring(vm, "state", -1);
+ if(SQ_FAILED(sq_get(vm, -2)))
+ throw Scripting::SquirrelError(vm, "Couldn't get state table");
+
+ // get or create worlds table
+ sq_pushstring(vm, "worlds", -1);
+ if(SQ_FAILED(sq_get(vm, -2))) {
+ sq_pushstring(vm, "worlds", -1);
+ sq_newtable(vm);
+ if(SQ_FAILED(sq_createslot(vm, -3)))
+ throw Scripting::SquirrelError(vm, "Couldn't create state.worlds");
+
+ sq_pushstring(vm, "worlds", -1);
+ if(SQ_FAILED(sq_get(vm, -2)))
+ throw Scripting::SquirrelError(vm, "Couldn't create.get state.worlds");
+ }
+
+ sq_pushstring(vm, map_filename.c_str(), map_filename.length());
+ if(SQ_FAILED(sq_deleteslot(vm, -2, SQFalse)))
+ sq_pop(vm, 1);
+
+ // construct new table for this worldmap
+ sq_pushstring(vm, map_filename.c_str(), map_filename.length());
+ sq_newtable(vm);
+
+ // store tux
+ sq_pushstring(vm, "tux", -1);
+ sq_newtable(vm);
+
+ store_float(vm, "x", tux->get_tile_pos().x);
+ store_float(vm, "y", tux->get_tile_pos().y);
+ store_string(vm, "back", direction_to_string(tux->back_direction));
+
+ sq_createslot(vm, -3);
+
+ // levels...
+ sq_pushstring(vm, "levels", -1);
+ sq_newtable(vm);
+
+ for(LevelTiles::iterator i = levels.begin(); i != levels.end(); ++i) {
+ LevelTile* level = *i;
+
+ sq_pushstring(vm, level->get_name().c_str(), -1);
+ sq_newtable(vm);
+
+ store_bool(vm, "solved", level->solved);
+ level->statistics.serialize_to_squirrel(vm);
+
+ sq_createslot(vm, -3);
+ }
+
+ sq_createslot(vm, -3);
+
+ // overall statistics...
+ total_stats.serialize_to_squirrel(vm);
+
+ // push world into worlds table
+ sq_createslot(vm, -3);
+ } catch(std::exception& ) {
+ sq_settop(vm, oldtop);
+ }
+
+ sq_settop(vm, oldtop);
+
+ if(World::current() != NULL)
+ World::current()->save_state();
+}
+
+void
+WorldMap::load_state()
+{
+ using namespace Scripting;
+
+ HSQUIRRELVM vm = global_vm;
+ int oldtop = sq_gettop(vm);
+
+ try {
+ // get state table
+ sq_pushroottable(vm);
+ sq_pushstring(vm, "state", -1);
+ if(SQ_FAILED(sq_get(vm, -2)))
+ throw Scripting::SquirrelError(vm, "Couldn't get state table");
+
+ // get worlds table
+ sq_pushstring(vm, "worlds", -1);
+ if(SQ_FAILED(sq_get(vm, -2)))
+ throw Scripting::SquirrelError(vm, "Couldn't get state.worlds");
+
+ // get table for our world
+ sq_pushstring(vm, map_filename.c_str(), map_filename.length());
+ if(SQ_FAILED(sq_get(vm, -2)))
+ throw Scripting::SquirrelError(vm, "Couldn't get state.worlds.mapfilename");
+
+ // load tux
+ sq_pushstring(vm, "tux", -1);
+ if(SQ_FAILED(sq_get(vm, -2)))
+ throw Scripting::SquirrelError(vm, "Couldn't get tux");
+
+ Vector p;
+ p.x = read_float(vm, "x");
+ p.y = read_float(vm, "y");
+ std::string back_str = read_string(vm, "back");
+ tux->back_direction = string_to_direction(back_str);
+ tux->set_tile_pos(p);
+
+ sq_pop(vm, 1);
+
+ // load levels
+ sq_pushstring(vm, "levels", -1);
+ if(SQ_FAILED(sq_get(vm, -2)))
+ throw Scripting::SquirrelError(vm, "Couldn't get levels");
+
+ for(LevelTiles::iterator i = levels.begin(); i != levels.end(); ++i) {
+ LevelTile* level = *i;
+ sq_pushstring(vm, level->get_name().c_str(), -1);
+ if(SQ_SUCCEEDED(sq_get(vm, -2))) {
+ level->solved = read_bool(vm, "solved");
+ level->sprite->set_action(level->solved ? "solved" : "default");
+ level->statistics.unserialize_from_squirrel(vm);
+ sq_pop(vm, 1);
+ }
+ }
+
+ // leave state table
+ sq_pop(vm, 1);
+
+ // load overall statistics
+ total_stats.unserialize_from_squirrel(vm);
+
+ } catch(std::exception& e) {
+ log_debug << "Not loading worldmap state: " << e.what() << std::endl;
+ }
+ sq_settop(vm, oldtop);
+
+ in_level = false;
+}
+
+size_t
+WorldMap::level_count()
+{
+ return levels.size();
+}
+
+size_t
+WorldMap::solved_level_count()
+{
+ size_t count = 0;
+ for(LevelTiles::iterator i = levels.begin(); i != levels.end(); ++i) {
+ LevelTile* level = *i;
+
+ if(level->solved)
+ count++;
+ }
+
+ return count;
+}
+
+HSQUIRRELVM
+WorldMap::run_script(std::istream& in, const std::string& sourcename)
+{
+ using namespace Scripting;
+
+ // garbage collect thread list
+ for(ScriptList::iterator i = scripts.begin();
+ i != scripts.end(); ) {
+ HSQOBJECT& object = *i;
+ HSQUIRRELVM vm = object_to_vm(object);
+
+ if(sq_getvmstate(vm) != SQ_VMSTATE_SUSPENDED) {
+ sq_release(global_vm, &object);
+ i = scripts.erase(i);
+ continue;
+ }
+
+ ++i;
+ }
+
+ HSQOBJECT object = create_thread(global_vm);
+ scripts.push_back(object);
+
+ HSQUIRRELVM vm = object_to_vm(object);
+
+ // set worldmap_table as roottable for the thread
+ sq_pushobject(vm, worldmap_table);
+ sq_setroottable(vm);
+
+ compile_and_run(vm, in, sourcename);
+
+ return vm;
+}
+
+float
+WorldMap::get_width() const
+{
+ float width = 0;
+ for(std::list<TileMap*>::const_iterator i = solid_tilemaps.begin(); i != solid_tilemaps.end(); i++) {
+ TileMap* solids = *i;
+ if (solids->get_width() > width) width = solids->get_width();
+ }
+ return width;
+}
+
+float
+WorldMap::get_height() const
+{
+ float height = 0;
+ for(std::list<TileMap*>::const_iterator i = solid_tilemaps.begin(); i != solid_tilemaps.end(); i++) {
+ TileMap* solids = *i;
+ if (solids->get_height() > height) height = solids->get_height();
+ }
+ return height;
+}
+
+} // namespace WorldMapNS
--- /dev/null
+// $Id$
+//
+// SuperTux
+// Copyright (C) 2004 Ingo Ruhnke <grumbel@gmx.de>
+// Copyright (C) 2006 Christoph Sommer <christoph.sommer@2006.expires.deltadevelopment.de>
+//
+// 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_WORLDMAP_H
+#define SUPERTUX_WORLDMAP_H
+
+#include <vector>
+#include <string>
+
+#include "math/vector.hpp"
+#include "lisp/lisp.hpp"
+#include "control/controller.hpp"
+#include "statistics.hpp"
+#include "timer.hpp"
+#include "screen.hpp"
+#include "tile_manager.hpp"
+#include "game_object.hpp"
+#include "console.hpp"
+#include "../level.hpp"
+#include "worldmap/special_tile.hpp"
+#include "worldmap/sprite_change.hpp"
+#include "worldmap/teleporter.hpp"
+#include "worldmap/spawn_point.hpp"
+#include "worldmap/direction.hpp"
+
+class Sprite;
+class Menu;
+class GameObject;
+class TileMap;
+
+namespace WorldMapNS {
+
+class Tux;
+class LevelTile;
+class SpecialTile;
+class SpriteChange;
+
+// For one way tiles
+enum {
+ BOTH_WAYS,
+ NORTH_SOUTH_WAY,
+ SOUTH_NORTH_WAY,
+ EAST_WEST_WAY,
+ WEST_EAST_WAY
+};
+
+std::string direction_to_string(Direction d);
+Direction string_to_direction(const std::string& d);
+Direction reverse_dir(Direction d);
+
+/**
+ * Screen that displays a worldmap
+ */
+class WorldMap : public Screen
+{
+private:
+ Tux* tux;
+
+ static WorldMap* current_;
+
+ std::auto_ptr<Menu> worldmap_menu;
+
+ Vector camera_offset;
+
+ std::string name;
+ std::string music;
+ std::string init_script;
+
+ typedef std::vector<GameObject*> GameObjects;
+ GameObjects game_objects;
+ std::list<TileMap*> solid_tilemaps;
+
+ std::auto_ptr<TileManager> tile_manager;
+
+public:
+ /** Variables to deal with the passive map messages */
+ Timer passive_message_timer;
+ std::string passive_message;
+
+private:
+ std::string map_filename;
+ std::string levels_path;
+
+ typedef std::vector<SpecialTile*> SpecialTiles;
+ SpecialTiles special_tiles;
+ typedef std::vector<LevelTile*> LevelTiles;
+ LevelTiles levels;
+ typedef std::vector<SpriteChange*> SpriteChanges;
+ SpriteChanges sprite_changes;
+ typedef std::vector<SpawnPoint*> SpawnPoints;
+ SpawnPoints spawn_points;
+ std::vector<Teleporter*> teleporters;
+
+ Statistics total_stats;
+
+ HSQOBJECT worldmap_table;
+ typedef std::vector<HSQOBJECT> ScriptList;
+ ScriptList scripts;
+
+ Color ambient_light;
+ std::string force_spawnpoint; /**< if set, spawnpoint will be forced to this value */
+
+ bool in_level;
+
+public:
+ WorldMap(const std::string& filename, const std::string& force_spawnpoint = "");
+ ~WorldMap();
+
+ void add_object(GameObject* object);
+
+ void try_expose(GameObject* object);
+ void try_unexpose(GameObject* object);
+
+ static WorldMap* current()
+ { return current_; }
+
+ virtual void setup();
+ virtual void leave();
+
+ /** Update worldmap state */
+ virtual void update(float delta);
+ /** Draw worldmap */
+ virtual void draw(DrawingContext& context);
+
+ Vector get_next_tile(Vector pos, Direction direction);
+
+ /**
+ * gets a bitfield of Tile::WORLDMAP_NORTH | Tile::WORLDMAP_WEST | ... values,
+ * which indicates the directions Tux can move to when at the given position.
+ */
+ int available_directions_at(Vector pos);
+
+ /**
+ * returns a bitfield representing the union of all Tile::WORLDMAP_XXX values
+ * of all solid tiles at the given position
+ */
+ int tile_data_at(Vector pos);
+
+ size_t level_count();
+ size_t solved_level_count();
+
+ /**
+ * gets called from the GameSession when a level has been successfully
+ * finished
+ */
+ void finished_level(Level* level);
+
+ LevelTile* at_level();
+ SpecialTile* at_special_tile();
+ SpriteChange* at_sprite_change(const Vector& pos);
+ Teleporter* at_teleporter(const Vector& pos);
+
+ /** Check if it is possible to walk from \a pos into \a direction,
+ if possible, write the new position to \a new_pos */
+ bool path_ok(Direction direction, const Vector& pos, Vector* new_pos);
+
+ /**
+ * Save worldmap state to squirrel state table
+ */
+ void save_state();
+
+ /**
+ * Load worldmap state from squirrel state table
+ */
+ void load_state();
+
+ const std::string& get_title() const
+ { return name; }
+
+ /**
+ * runs a script in the context of the worldmap (and keeps a reference to
+ * the script (so the script gets destroyed when the worldmap is destroyed)
+ */
+ HSQUIRRELVM run_script(std::istream& in, const std::string& sourcename);
+
+ /**
+ * switch to another worldmap.
+ * filename is relative to data root path
+ */
+ void change(const std::string& filename, const std::string& force_spawnpoint="");
+
+ /**
+ * moves Tux to the given spawnpoint
+ */
+ void move_to_spawnpoint(const std::string& spawnpoint);
+
+ /**
+ * returns the width (in tiles) of a worldmap
+ */
+ float get_width() const;
+
+ /**
+ * returns the height (in tiles) of a worldmap
+ */
+ float get_height() const;
+
+private:
+ void get_level_title(LevelTile& level);
+ void draw_status(DrawingContext& context);
+ void calculate_total_stats();
+
+ void load(const std::string& filename);
+ void on_escape_press();
+};
+
+} // namespace WorldMapNS
+
+#endif