From e495074443d9fd7bc16137084cf9de3d031b75c4 Mon Sep 17 00:00:00 2001 From: Charles McGarvey Date: Thu, 3 Dec 2009 21:35:25 -0700 Subject: [PATCH] foundational changes; tying up some loose ends --- README | 3 +- doc/yoink.6.in | 90 +++++------- src/Animation.cc | 4 +- src/ErrorHandler.cc | 87 ++++++++++++ src/ErrorHandler.hh | 41 ++++++ src/GameLayer.cc | 108 ++++++++------- src/GameLayer.hh | 43 +++--- src/Hud.cc | 30 +++- src/Hud.hh | 14 +- src/MainLayer.cc | 82 ++++++----- src/MainLayer.hh | 13 +- src/Makefile.am | 6 +- src/Moof/{Dispatcher.cc => Dispatch.cc} | 84 +++++++----- src/Moof/{Dispatcher.hh => Dispatch.hh} | 111 ++++++++------- src/Moof/Engine.cc | 173 ++++++++++++++---------- src/Moof/Engine.hh | 56 +++++--- src/Moof/Exception.hh | 9 +- src/Moof/Layer.hh | 12 +- src/Moof/Log.cc | 2 +- src/Moof/Log.hh | 2 +- src/Moof/ModalDialog.hh | 27 +++- src/Moof/Script.hh | 77 ++++++++++- src/Moof/Settings.cc | 6 + src/Moof/Settings.hh | 5 +- src/Moof/Sound.cc | 10 +- src/Moof/Texture.cc | 60 ++++---- src/Moof/Timer.cc | 10 +- src/Moof/Timer.hh | 4 +- src/Moof/Transition.hh | 32 ++--- src/Moof/Video.cc | 20 +-- src/Moof/Video.hh | 3 +- src/Tilemap.cc | 2 +- src/TitleLayer.cc | 27 ++-- src/TitleLayer.hh | 8 +- 34 files changed, 791 insertions(+), 470 deletions(-) create mode 100644 src/ErrorHandler.cc create mode 100644 src/ErrorHandler.hh rename src/Moof/{Dispatcher.cc => Dispatch.cc} (60%) rename src/Moof/{Dispatcher.hh => Dispatch.hh} (54%) diff --git a/README b/README index eaea5ad..a583d75 100644 --- a/README +++ b/README @@ -30,8 +30,7 @@ this simple, fast-moving action game to a wider audience. b) Requirements -boost -headers +boost headers freealut libvorbis Lua diff --git a/doc/yoink.6.in b/doc/yoink.6.in index ece0fa5..ec41dee 100644 --- a/doc/yoink.6.in +++ b/doc/yoink.6.in @@ -70,61 +70,28 @@ around, especially in the sky, if you can't find the last few. .br .SH OPTIONS .PP -There are a plethora of options available for tweaking various aspects of the -game. All options can be set either from a configuration file or by passing -them as arguments. -.PP -A -.B yoink -configuration file ("yoinkrc") consists of key-value pairs. The format is not -unlike that of other configuration files you are already familiar with. The -syntax used is lua. +Options are set from config files and command-line arguments. A .B yoink -looks for configuration files and loads them in this order, the options from -each subsequent configuration files taking precedence over the same options if -they exist in previous files. +configfile ("yoinkrc") consists of key-value pairs. They are loaded in this +order: .TP 1. @DATADIR@/yoinkrc -This is the base configuration file which should be considered read-only. Look -to this file as an example of the format used for configuration files. +This is the base config file which should be considered read-only. Look to this +file as an example of the format used for config files. .TP 2. /etc/yoinkrc -This is the system-wide configuration file. Not available on Windows. +This is the system-wide config file. Not available on Windows. .TP 3. $HOME/.yoinkrc -This is your own personal configuration file. +This is your own personal config file. .TP 4. $YOINKRC -This is an optional environment variable you can set to point to a configuration -file. -.PP -Options that are passed as arguments take precedence over options loaded from -the configuration file(s). This mechanism can be used to play the game with -temporary settings which you do not intend to retain. Here are some examples of -passing options on the command-line: -.PP -.TP -yoink fullscreen=true -Run Yoink with the option -.I fullscreen -as true. This will run the game in full-screen mode. -.TP -yoink maxfps=60 -Run Yoink with the option -.I maxfps -as 60. This will cap the display rate at 60Hz. +This is an optional environment variable you can set to the path of a config +file at a non-standard location. .PP -You can also set options with array values. Arrays can be passed on the command -line by surrounding all the parts with curly brackets and separating each part -by a comma. You may also have to quote the brackets so your shell doesn't parse -them. For example: -.TP -yoink videomode=\\{1024,768\\} -Run Yoink with the top -.I videomode -as the numbers 1024 and 768. The video size will be 1024x768. -.PP -Here is a list of some of the options available: +As usual, options that are passed as arguments take precedence over options +loaded from the config file(s). Here is a list of some of the options available +at your disposal: .TP .B detail The level of detail. Possible values are 1, 2, or 3 where 1 means the least @@ -180,11 +147,26 @@ is 80. .B videomode The resolution or size of the window. The value is an array with three number elements representing the width, height, and bits per pixel that make up the -video mode. The third number is optional. The default value is {800, 600}. +video mode. The third number is optional. The default value is {800,600}. You +may need to escape the curly braces so the shell doesn't parse them. .PP This is only a list of the more useful options. You'll have to use the source to find out about the more esoteric options, but you probably won't need to. .br +.SH EXAMPLES +.PP +Here are some examples of typical usage: +.PP +.TP +yoink fullscreen=true +Run Yoink in full-screen mode. +.TP +yoink maxfps=60 +Cap the allowable frame-rate to 60Hz. +.TP +yoink videomode=\\{1024,768\\} +Run yoink with a resultion of 1024x768. Notice the escapes for the curly +braces so the shell doesn't parse them. .SH ENVIRONMENT .PP .B yoink @@ -195,7 +177,7 @@ If set to a path of a valid directory (presumably a user's home directory), .B yoink will look for a file at .I $HOME/.yoinkrc -and load it as a configuration file. +and load it as a config file. .TP USER .B yoink @@ -210,10 +192,10 @@ variable if you move the game's assets to another directory or perhaps want to load your own custom assets rather than the defaults. .TP YOINKRC -If set to a path of a valid configuration file, +If set to a path of a valid config file, .B yoink will load the options from that file, and those options will take precedence -over options loaded from other configuration files. +over options loaded from other config files. .br .SH NOTES .PP @@ -239,10 +221,10 @@ Use the option. You can set the timestep to be as low as the your .I maxfps option, but it is not recommended to set this lower than the target frame rate. -Remember the trade-off here is decreased simulation accuracy. Try this to set -your frame rate to 30Hz and your timestep to 60Hz: +Remember the trade-off here is decreased simulation accuracy. Try to set your +maxfps to 30Hz and your timestep to 60Hz: .PP -yoink maxfps=30 timestep=maxfps\\*2 +yoink maxfps=30 timestep=60 .TP 3. Decrease the level of rendering detail. Use the @@ -253,7 +235,9 @@ than choppy animation. .SH BUGS .PP Although the pixelated graphics are intentional, there are some unintended -artifacts which are more obvious on certain OpenGL implementations. +artifacts which are more obvious with certain video drivers. +.PP +The robots are not very bright. .PP Send bug reports, patches, and love notes to: .br diff --git a/src/Animation.cc b/src/Animation.cc index c694aec..ab178df 100644 --- a/src/Animation.cc +++ b/src/Animation.cc @@ -166,8 +166,8 @@ class Animation::Impl Mf::Script script; std::string filePath = Animation::getPath(getName()); - script.importStandardLibraries(); - importLogScript(script); + script.importBaseLibrary(); + importLogPrintFunction(script); importAnimationBindings(script); if (script.doFile(filePath) != Mf::Script::SUCCESS) diff --git a/src/ErrorHandler.cc b/src/ErrorHandler.cc new file mode 100644 index 0000000..66a488e --- /dev/null +++ b/src/ErrorHandler.cc @@ -0,0 +1,87 @@ + +/******************************************************************************* + + Copyright (c) 2009, Charles McGarvey + All rights reserved. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions are met: + + * Redistributions of source code must retain the above copyright notice, + this list of conditions and the following disclaimer. + * 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. + + 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 HOLDER 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 + +#include "ErrorHandler.hh" + + +std::string getErrorString(const Mf::Exception& e) +{ + std::string str; + + switch(e.code()) + { + case Mf::ErrorCode::FASTEVENTS_INIT: + case Mf::ErrorCode::SDL_INIT: + str += "An error occurred during SDL initialization: "; + str += e.what(); + return str; + + case Mf::ErrorCode::FILE_NOT_FOUND: + str += "A required file ("; + str += e.what(); + str += ") could not be found."; + return str; + + case Mf::ErrorCode::RESOURCE_NOT_FOUND: + str += "A required resource ("; + str += e.what(); + str += ") could not be found."; + return str; + + case Mf::ErrorCode::SCRIPT_ERROR: + str += "An error occurred in a script: "; + str == e.what(); + return str; + + case Mf::ErrorCode::SDL_VIDEOMODE: + str += "An error occurred while trying to set up the video mode."; + return str; + + case Mf::ErrorCode::UNKNOWN_AUDIO_FORMAT: + str += "An error occurred while trying to load an audio file, "; + str += e.what(); + str += "."; + return str; + + case Mf::ErrorCode::UNKNOWN_IMAGE_FORMAT: + str += "An error occurred while trying to load an image file, "; + str += e.what(); + str += "."; + return str; + } + + std::ostringstream stream; + stream << "An unknown error (code " << e.code() << ") occurred."; + return stream.str(); +} + + +/** vim: set ts=4 sw=4 tw=80: *************************************************/ + diff --git a/src/ErrorHandler.hh b/src/ErrorHandler.hh new file mode 100644 index 0000000..2b13b1d --- /dev/null +++ b/src/ErrorHandler.hh @@ -0,0 +1,41 @@ + +/******************************************************************************* + + Copyright (c) 2009, Charles McGarvey + All rights reserved. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions are met: + + * Redistributions of source code must retain the above copyright notice, + this list of conditions and the following disclaimer. + * 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. + + 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 HOLDER 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 _ERRORHANDLER_HH_ +#define _ERRORHANDLER_HH_ + +#include + + +std::string getErrorString(const Mf::Exception& e); + + +#endif // _ERRORHANDLER_HH_ + +/** vim: set ts=4 sw=4 tw=80: *************************************************/ + diff --git a/src/GameLayer.cc b/src/GameLayer.cc index a37f175..f61ec3d 100644 --- a/src/GameLayer.cc +++ b/src/GameLayer.cc @@ -34,6 +34,7 @@ #include #include "GameLayer.hh" +#include "Hud.hh" #if HAVE_CONFIG_H #include "config.h" @@ -45,37 +46,40 @@ Mf::Scalar GameLayer::getZCoord(const Mf::Vector2& position) const { Mf::Scalar z; - mScript.getGlobalTable().pushField("GetZCoord"); - mScript.push(position[0]); - mScript.push(position[1]); - mScript.call(2, 1); - mScript.getTop().get(z); - mScript.pop(); + mState.script.getGlobalTable().pushField("GetZCoord"); + mState.script.push(position[0]); + mState.script.push(position[1]); + mState.script.call(2, 1); + mState.script.getTop().get(z); + mState.script.pop(); return z; } void GameLayer::loadSceneLoader() { + mState.script.importStandardLibraries(); + importLogPrintFunction(mState.script); + std::string loaderPath = Scene::getPath("loader"); if (loaderPath == "") { throw Mf::Exception(Mf::ErrorCode::RESOURCE_NOT_FOUND, "loader"); } - Mf::Script::Status status = mScript.doFile(loaderPath); + Mf::Script::Status status = mState.script.doFile(loaderPath); if (status != Mf::Script::SUCCESS) { std::string str; - mScript[-1].get(str); + mState.script[-1].get(str); Mf::logScript("%s", str.c_str()); - throw Mf::Exception(Mf::ErrorCode::SCRIPT_ERROR, str.c_str()); + throw Mf::Exception(Mf::ErrorCode::SCRIPT_ERROR, str); } - mScript.getGlobalTable().pushField("scenes"); - mScript.getTop().get(mSceneList); - if (mSceneList.size() == 0) + mState.script.getGlobalTable().pushField("scenes"); + mState.script.getTop().get(mState.sceneList); + if (mState.sceneList.size() == 0) { Mf::logScript("no variable `scenes' within loader"); throw Mf::Exception(Mf::ErrorCode::SCRIPT_ERROR, "no scenes to load"); @@ -84,11 +88,11 @@ void GameLayer::loadSceneLoader() void GameLayer::advanceScene() { - if (mSceneList.size() != 0) + if (mState.sceneList.size() != 0) { - mScene = Scene::alloc(mSceneList[0]); - mSceneList.erase(mSceneList.begin()); - mScene->load(mScript); + mState.scene = Scene::alloc(mState.sceneList[0]); + mState.sceneList.erase(mState.sceneList.begin()); + mState.scene->load(mState.script); } } @@ -104,47 +108,41 @@ GameLayer::GameLayer() : loadSceneLoader(); advanceScene(); // load the first scene - mHeroine = Heroine::alloc(); - mHeroine->animation.startSequence("FlyDiagonallyUp"); + mState.heroine = Heroine::alloc(); + mState.heroine->animation.startSequence("FlyDiagonallyUp"); Mf::Scalar a[6] = {0.0, 1.5, -0.5, 3.0, -2.0, 1.0}; - mInterp.init(a, 2.0, Mf::Interpolator::OSCILLATE); + mState.interp.init(a, 2.0, Mf::Interpolator::OSCILLATE); setProjection(); - - mHud = Hud::alloc(); } void GameLayer::pushed(Mf::Engine& engine) { - engine.push(mHud); + engine.push(Hud::alloc(mState)); } -void GameLayer::update(Mf::Scalar t, Mf::Scalar dt) +void GameLayer::update(Mf::Engine& engine, Mf::Scalar t, Mf::Scalar dt) { - mCamera.update(t, dt); - mHeroine->update(t, dt); + mState.camera.update(t, dt); + mState.heroine->update(t, dt); - mScene->checkForCollision(*mHeroine); + mState.scene->checkForCollision(*mState.heroine); - mCamera.setPosition(Mf::Vector3(-mHeroine->getState().position[0], - -mHeroine->getState().position[1], -10)); - //mCamera.lookAt(Mf::promote(mHeroine->getState().position)); + mState.camera.setPosition(Mf::Vector3(-mState.heroine->getState().position[0], + -mState.heroine->getState().position[1], -10)); + //mState.camera.lookAt(Mf::promote(mState.heroine->getState().position)); - //Mf::Vector3 heroinePosition = Mf::promote(mHeroine->getState().position); + //Mf::Vector3 heroinePosition = Mf::promote(mState.heroine->getState().position); //Mf::Sound::setListenerPosition(heroinePosition); - - mInterp.update(t, dt); - mHud->setBar1Progress(mInterp.getState(dt)); - mHud->setBar2Progress(1.0 - mInterp.getState(dt)); } -void GameLayer::draw(Mf::Scalar alpha) const +void GameLayer::draw(Mf::Engine& engine, Mf::Scalar alpha) const { - mCamera.uploadToGL(alpha); + mState.camera.uploadToGL(alpha); // DRAW THE SCENE Mf::Texture::resetBind(); @@ -152,45 +150,51 @@ void GameLayer::draw(Mf::Scalar alpha) const glEnableClientState(GL_VERTEX_ARRAY); glEnableClientState(GL_TEXTURE_COORD_ARRAY); - mScene->drawIfVisible(alpha, mCamera.getFrustum()); + mState.scene->drawIfVisible(alpha, mState.camera.getFrustum()); - mHeroine->setZCoord(getZCoord(mHeroine->getState().position)); - mHeroine->draw(alpha); + mState.heroine->setZCoord(getZCoord(mState.heroine->getState().position)); + mState.heroine->draw(alpha); } -bool GameLayer::handleEvent(const Mf::Event& event) +bool GameLayer::handleEvent(Mf::Engine& engine, const Mf::Event& event) { switch (event.type) { case SDL_KEYDOWN: if (event.key.keysym.sym == SDLK_SPACE) { - mHeroine->animation.startSequence("Flattened"); + mState.heroine->animation.startSequence("Flattened"); Mf::logInfo("thump!"); mPunchSound.play(); return true; } - else if (event.key.keysym.sym == SDLK_p) + else if (event.key.keysym.sym == SDLK_m) { mMusic.toggle(); return true; } - else if (event.key.keysym.sym == SDLK_y) + return mState.heroine->handleEvent(event); + + case SDL_KEYUP: + if (event.key.keysym.sym == SDLK_ESCAPE) { - Mf::Engine::getInstance().pop(); + engine.pop(this); return true; } - - case SDL_KEYUP: - return mHeroine->handleEvent(event); + else if (event.key.keysym.sym == SDLK_h) + { + engine.push(Hud::alloc(mState)); + return true; + } + return mState.heroine->handleEvent(event); case SDL_MOUSEMOTION: case SDL_MOUSEBUTTONDOWN: - mCamera.handleEvent(event); + mState.camera.handleEvent(event); return true; case SDL_VIDEORESIZE: - setProjection(Mf::Scalar(event.resize.w), Mf::Scalar(event.resize.h)); + setProjection(event.resize.w, event.resize.h); break; } @@ -200,13 +204,13 @@ bool GameLayer::handleEvent(const Mf::Event& event) void GameLayer::setProjection() { - Mf::Video& video = Mf::Engine::getInstance().getVideo(); - setProjection(video.getWidth(), video.getHeight()); + Mf::VideoP video = Mf::Engine::getInstance().getVideo(); + setProjection(video->getWidth(), video->getHeight()); } void GameLayer::setProjection(Mf::Scalar width, Mf::Scalar height) { - mCamera.setProjection(cml::rad(60.0), width / height, 1.0, 200.0); + mState.camera.setProjection(cml::rad(60.0), width / height, 1.0, 200.0); } diff --git a/src/GameLayer.hh b/src/GameLayer.hh index 4e26e68..d75d9dc 100644 --- a/src/GameLayer.hh +++ b/src/GameLayer.hh @@ -41,7 +41,6 @@ #include #include -#include #include #include #include @@ -50,7 +49,6 @@ #include "Character.hh" #include "Heroine.hh" -#include "Hud.hh" #include "Scene.hh" @@ -69,9 +67,24 @@ public: void pushed(Mf::Engine& engine); - void update(Mf::Scalar t, Mf::Scalar dt); - void draw(Mf::Scalar alpha) const; - bool handleEvent(const Mf::Event& event); + void update(Mf::Engine& engine, Mf::Scalar t, Mf::Scalar dt); + void draw(Mf::Engine& engine, Mf::Scalar alpha) const; + bool handleEvent(Mf::Engine& engine, const Mf::Event& event); + + struct State + { + // the script object must be mutable because some script functions must be + // called during draw + mutable Mf::Script script; + std::vector sceneList; + + HeroineP heroine; + SceneP scene; + + Mf::PolynomialInterpolator<5> interp; + + Mf::Camera camera; + }; private: @@ -83,23 +96,9 @@ private: void setProjection(); void setProjection(Mf::Scalar width, Mf::Scalar height); - - // the script object must be mutable because some script functions must be - // called during draw - mutable Mf::Script mScript; - std::vector mSceneList; - - Mf::Sound mMusic; - Mf::Sound mPunchSound; - - HeroineP mHeroine; - SceneP mScene; - - Mf::PolynomialInterpolator<5> mInterp; - - Mf::Camera mCamera; - - HudP mHud; + State mState; + Mf::Sound mMusic; + Mf::Sound mPunchSound; }; diff --git a/src/Hud.cc b/src/Hud.cc index a7e141e..90f5dd9 100644 --- a/src/Hud.cc +++ b/src/Hud.cc @@ -26,12 +26,12 @@ *******************************************************************************/ +#include #include +#include #include "Hud.hh" -#include - ProgressBar::ProgressBar(const Tilemap& tilemap, Tilemap::Index index) : mProgress(0.0), @@ -118,12 +118,14 @@ void ProgressBar::draw(Mf::Scalar alpha) const } -Hud::Hud() : +Hud::Hud(GameLayer::State& state) : + mState(state), mBar1(Tilemap("StatusBars"), 0), mBar2(Tilemap("StatusBars"), 2), mFont("Font") { - resize(800, 600); + Mf::VideoP video = Mf::Engine::getInstance().getVideo(); + resize(video->getWidth(), video->getHeight()); } @@ -145,7 +147,14 @@ void Hud::resize(int width, int height) } -void Hud::draw(Mf::Scalar alpha) const +void Hud::update(Mf::Engine& engine, Mf::Scalar t, Mf::Scalar dt) +{ + mState.interp.update(t, dt); + setBar1Progress(mState.interp.getState(dt)); + setBar2Progress(1.0 - mState.interp.getState(dt)); +} + +void Hud::draw(Mf::Engine& engine, Mf::Scalar alpha) const { glMatrixMode(GL_PROJECTION); glPushMatrix(); @@ -171,10 +180,19 @@ void Hud::draw(Mf::Scalar alpha) const glPopMatrix(); } -bool Hud::handleEvent(Mf::Event& event) +bool Hud::handleEvent(Mf::Engine& engine, const Mf::Event& event) { switch (event.type) { + case SDL_KEYUP: + if (event.key.keysym.sym == SDLK_h) + { + // don't want the hud anymore + engine.pop(this); + return true; + } + break; + case SDL_VIDEORESIZE: resize(event.resize.w, event.resize.h); break; diff --git a/src/Hud.hh b/src/Hud.hh index 59f0631..d3675a4 100644 --- a/src/Hud.hh +++ b/src/Hud.hh @@ -39,6 +39,7 @@ #include #include +#include "GameLayer.hh" #include "Tilemap.hh" @@ -76,11 +77,11 @@ class Hud : public Mf::Layer { public: - Hud(); + Hud(GameLayer::State& state); - static HudP alloc() + static HudP alloc(GameLayer::State& state) { - return HudP(new Hud); + return HudP(new Hud(state)); } void setBar1Progress(Mf::Scalar progress) @@ -99,11 +100,14 @@ public: void resize(int width, int height); - void draw(Mf::Scalar alpha = 0.0) const; - bool handleEvent(Mf::Event& event); + void update(Mf::Engine& engine, Mf::Scalar t, Mf::Scalar dt); + void draw(Mf::Engine& engine, Mf::Scalar alpha = 0.0) const; + bool handleEvent(Mf::Engine& engine, const Mf::Event& event); private: + GameLayer::State& mState; + ProgressBar mBar1; ProgressBar mBar2; diff --git a/src/MainLayer.cc b/src/MainLayer.cc index 6a426fb..38c849e 100644 --- a/src/MainLayer.cc +++ b/src/MainLayer.cc @@ -31,15 +31,15 @@ #include #include // access -#include -#include #include #include #include #include +#include #include #include +#include "ErrorHandler.hh" #include "GameLayer.hh" #include "MainLayer.hh" #include "TitleLayer.hh" @@ -52,21 +52,13 @@ MainLayer::MainLayer() { - Mf::dispatcher::addHandler("video.context_recreated", - boost::bind(&MainLayer::contextRecreated, this, _1), this); + mDispatchHandler = Mf::Engine::getInstance().addHandler("video.newcontext", + boost::bind(&MainLayer::contextRecreated, this)); setupGL(); } -MainLayer::~MainLayer() -{ - Mf::dispatcher::removeHandler(this); -} - - void MainLayer::pushed(Mf::Engine& engine) { - mEngine = &engine; - //Mf::Scalar coeff[] = {0.0, 1.0}; //Mf::Lerp interp(coeff, 0.25); @@ -75,11 +67,20 @@ void MainLayer::pushed(Mf::Engine& engine) //Mf::Transition::alloc(gameLayer, Mf::LayerP(), interp); //engine->push(transition); //engine->push(GameLayer::alloc()); - mEngine->push(TitleLayer::alloc()); + engine.push(TitleLayer::alloc()); } -void MainLayer::draw(Mf::Scalar alpha) const +void MainLayer::update(Mf::Engine& engine, Mf::Scalar t, Mf::Scalar dt) +{ + if (engine.getSize() == 1) + { + // this is the only layer left on the stack + engine.push(TitleLayer::alloc()); + } +} + +void MainLayer::draw(Mf::Engine& engine, Mf::Scalar alpha) const { glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); @@ -90,28 +91,24 @@ void MainLayer::draw(Mf::Scalar alpha) const glLoadIdentity(); } -bool MainLayer::handleEvent(const Mf::Event& event) +bool MainLayer::handleEvent(Mf::Engine& engine, const Mf::Event& event) { switch (event.type) { - case SDL_KEYDOWN: + case SDL_KEYUP: if (event.key.keysym.sym == SDLK_ESCAPE) { - quit(); + engine.clear(); } else if (event.key.keysym.sym == SDLK_f) { - mEngine->getVideo().toggleFull(); + engine.getVideo()->toggleFull(); } else if (event.key.keysym.sym == SDLK_l) { - Mf::Video& video = mEngine->getVideo(); - video.toggleCursorGrab(); - video.toggleCursorVisible(); - } - else if (event.key.keysym.sym == SDLK_y) - { - mEngine->push(GameLayer::alloc()); + Mf::VideoP video = engine.getVideo(); + video->toggleCursorGrab(); + video->toggleCursorVisible(); } break; @@ -120,19 +117,13 @@ bool MainLayer::handleEvent(const Mf::Event& event) break; case SDL_QUIT: - quit(); + engine.clear(); break; } return false; } -void MainLayer::quit() -{ - // remove all the layers - mEngine->clear(); -} - void MainLayer::setupGL() { @@ -157,7 +148,7 @@ void MainLayer::setupGL() glMatrixMode(GL_MODELVIEW); } -void MainLayer::contextRecreated(const Mf::Notification* note) +void MainLayer::contextRecreated() { // whenever the context is destroyed and a new one created, it probably // won't contain our state so we need to set that up again @@ -272,6 +263,7 @@ int main(int argc, char* argv[]) } } + std::cout << std::endl << PACKAGE_STRING << std::endl << "Compiled " << __TIME__ " " __DATE__ << std::endl << "Send patches and bug reports to <" @@ -305,19 +297,20 @@ int main(int argc, char* argv[]) Mf::Resource::addSearchPath(YOINK_DATADIR); - std::string iconFile = Mf::Resource::getPath(PACKAGE".png"); - // Build the list of config files to search for, in this order: // 1. YOINK_DATADIR/yoinkrc - // 2. /etc/yoinkrc + // 2. /etc/yoinkrc (not for Windows) // 3. $HOME/.yoinkrc // 4. YOINKRC (environment) std::string configFiles; configFiles += Mf::Resource::getPath("yoinkrc"); - configFiles += ":/etc/yoinkrc:$HOME/.yoinkrc"; +#if !defined(_WIN32) && !defined(__WIN32__) + configFiles += ":/etc/yoinkrc"; +#endif + configFiles += ":$HOME/.yoinkrc"; char* configFile = getenv("YOINKRC"); if (configFile) @@ -326,22 +319,27 @@ int main(int argc, char* argv[]) configFiles += configFile; } + Mf::Settings& settings = Mf::Settings::getInstance(); + settings.loadFromFile(configFiles); + settings.parseArgs(argc, argv); + + std::string iconFile = Mf::Resource::getPath(PACKAGE".png"); + + try { - Mf::Engine app(argc, argv, PACKAGE_STRING, iconFile, configFiles); + Mf::Engine& app = Mf::Engine::getInstance(); + app.setVideo(Mf::Video::alloc(PACKAGE_STRING, iconFile)); app.push(MainLayer::alloc()); app.run(); } catch (const Mf::Exception& e) { - Mf::logError("unhandled exception (code %d): \"%s\"", - e.code(), e.what()); - Mf::ModalDialog dialog; dialog.title = PACKAGE_STRING; dialog.text1 = "Unhandled Exception"; - dialog.text2 = e.what(); + dialog.text2 = getErrorString(e); dialog.type = Mf::ModalDialog::CRITICAL; dialog.run(); diff --git a/src/MainLayer.hh b/src/MainLayer.hh index 9738583..731adef 100644 --- a/src/MainLayer.hh +++ b/src/MainLayer.hh @@ -39,6 +39,7 @@ #include +#include #include #include #include @@ -52,7 +53,6 @@ class MainLayer : public Mf::Layer public: MainLayer(); - ~MainLayer(); static MainLayerP alloc() { @@ -61,10 +61,9 @@ public: void pushed(Mf::Engine& engine); - void draw(Mf::Scalar alpha) const; - bool handleEvent(const Mf::Event& event); - - void quit(); + void update(Mf::Engine& engine, Mf::Scalar t, Mf::Scalar dt); + void draw(Mf::Engine& engine, Mf::Scalar alpha) const; + bool handleEvent(Mf::Engine& engine, const Mf::Event& event); private: @@ -72,9 +71,9 @@ private: * Set OpenGL to a state we can know and depend on. */ void setupGL(); - void contextRecreated(const Mf::Notification* note); + void contextRecreated(); - Mf::Engine* mEngine; + Mf::Dispatch::Handler mDispatchHandler; }; diff --git a/src/Makefile.am b/src/Makefile.am index c5cddeb..c0600cd 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -19,8 +19,8 @@ libmoof_a_SOURCES = \ Moof/ConvertUTF.c \ Moof/ConvertUTF.h \ Moof/Cullable.hh \ - Moof/Dispatcher.cc \ - Moof/Dispatcher.hh \ + Moof/Dispatch.cc \ + Moof/Dispatch.hh \ Moof/Drawable.hh \ Moof/Engine.cc \ Moof/Engine.hh \ @@ -86,6 +86,8 @@ yoink_SOURCES = \ Animation.hh \ Character.cc \ Character.hh \ + ErrorHandler.cc \ + ErrorHandler.hh \ GameLayer.cc \ GameLayer.hh \ Heroine.cc \ diff --git a/src/Moof/Dispatcher.cc b/src/Moof/Dispatch.cc similarity index 60% rename from src/Moof/Dispatcher.cc rename to src/Moof/Dispatch.cc index 3207464..17f8f17 100644 --- a/src/Moof/Dispatcher.cc +++ b/src/Moof/Dispatch.cc @@ -28,43 +28,46 @@ #include -#include "Dispatcher.hh" +#include "Dispatch.hh" namespace Mf { -class Dispatcher::Impl +class Dispatch::Impl { - friend class Dispatcher; +public: Impl() : - mId(1) {} + mId(0) {} - Dispatcher::Handler getNewHandler() + Dispatch::Handler getNewHandler() { - mId += 2; - return (Dispatcher::Handler)mId; + ++mId; + //return Dispatch::Handler(this, mId); + Dispatch::Handler handler(this, mId); + return handler; } - typedef std::pair Callback; - typedef std::multimap CallbackLookup; - typedef CallbackLookup::iterator CallbackIter; + typedef std::pair Callback; + typedef std::multimap CallbackLookup; + typedef CallbackLookup::iterator CallbackIter; - typedef std::multimap HandlerLookup; - typedef HandlerLookup::iterator HandlerIter; + typedef std::multimap HandlerLookup; + typedef HandlerLookup::iterator HandlerIter; - inline Handler addHandler(const std::string& message, - const Function& callback, Handler id) + inline Handler addHandler(const std::string& event, + const Function& callback, Handler handler) { - mCallbacks.insert(std::make_pair(message, std::make_pair(id, callback))); - mHandlers.insert(std::make_pair(id, message)); + mCallbacks.insert(std::make_pair(event, + std::make_pair(handler.getId(), callback))); + mHandlers.insert(std::make_pair(handler.getId(), event)); - return id; + return handler; } - inline void removeHandler(Handler id) + inline void removeHandler(unsigned id) { std::pair matching(mHandlers.equal_range(id)); @@ -86,64 +89,73 @@ class Dispatcher::Impl mHandlers.erase(id); } - void dispatch(const std::string& message, const Notification* param) + void dispatch(const std::string& event, const Message* message) { std::pair - callbacks(mCallbacks.equal_range(message)); + callbacks(mCallbacks.equal_range(event)); for (CallbackIter it = callbacks.first; it != callbacks.second; ++it) { Function callback = (*it).second.second; - callback(param); + callback(message); } } - unsigned long mId; + unsigned mId; CallbackLookup mCallbacks; HandlerLookup mHandlers; }; -Dispatcher::Dispatcher() : - mImpl(new Dispatcher::Impl) {} +Dispatch::Handler::~Handler() +{ + if (mId) + { + mDispatch->removeHandler(mId); + } +} + + +Dispatch::Dispatch() : + mImpl(new Dispatch::Impl) {} -Dispatcher::~Dispatcher() {} +Dispatch::~Dispatch() {} -Dispatcher& Dispatcher::getInstance() +Dispatch& Dispatch::getInstance() { - static Dispatcher dispatcher; - return dispatcher; + static Dispatch dispatch; + return dispatch; } -Dispatcher::Handler Dispatcher::addHandler(const std::string& message, +Dispatch::Handler Dispatch::addHandler(const std::string& event, const Function& callback) { - return addHandler(message, callback, mImpl->getNewHandler()); + return addHandler(event, callback, mImpl->getNewHandler()); } -Dispatcher::Handler Dispatcher::addHandler(const std::string& message, - const Function& callback, Handler id) +Dispatch::Handler Dispatch::addHandler(const std::string& event, + const Function& callback, Handler handler) { // pass through - return mImpl->addHandler(message, callback, id); + return mImpl->addHandler(event, callback, handler); } -void Dispatcher::removeHandler(Handler id) +void Dispatch::removeHandler(unsigned id) { // pass through return mImpl->removeHandler(id); } -void Dispatcher::dispatch(const std::string& message, const Notification* param) +void Dispatch::dispatch(const std::string& event, const Message* message) { // pass through - mImpl->dispatch(message, param); + mImpl->dispatch(event, message); } diff --git a/src/Moof/Dispatcher.hh b/src/Moof/Dispatch.hh similarity index 54% rename from src/Moof/Dispatcher.hh rename to src/Moof/Dispatch.hh index 7945752..bbf37f3 100644 --- a/src/Moof/Dispatcher.hh +++ b/src/Moof/Dispatch.hh @@ -26,8 +26,8 @@ *******************************************************************************/ -#ifndef _MOOF_DISPATCHER_HH_ -#define _MOOF_DISPATCHER_HH_ +#ifndef _MOOF_DISPATCH_HH_ +#define _MOOF_DISPATCH_HH_ #include @@ -40,79 +40,90 @@ namespace Mf { /** - * Interface for a notification class. + * Dispatcher of messages to interested parties. */ -class Notification +class Dispatch { + class Impl; + boost::shared_ptr mImpl; + + void removeHandler(unsigned id); + public: - virtual ~Notification() {}; -}; + /** + * Interface for a notification class. + */ -/** - * Dispatcher of notifications to interested parties. - */ + class Message + { + public: + virtual ~Message() {}; + }; -class Dispatcher -{ - class Impl; - boost::shared_ptr mImpl; -public: + class Handler + { + public: - // TODO - the Handler would be even better as an object which automagically - // removes itself from the dispatcher on destruction, so users don't have to - // worry about forgetting - typedef void* Handler; - typedef boost::function Function; + Handler() : + mDispatch(0), + mId(0) {} - Dispatcher(); - ~Dispatcher(); + Handler(Impl* dispatch, unsigned id) : + mDispatch(dispatch), + mId(id) {} - // get global instance - static Dispatcher& getInstance(); + Handler(const Handler& handler) : + mDispatch(handler.mDispatch), + mId(handler.mId) + { + handler.mId = 0; + } - Handler addHandler(const std::string& message, const Function& callback); - Handler addHandler(const std::string& message, const Function& callback, - Handler id); + ~Handler(); - void removeHandler(Handler id); + Handler& operator = (const Handler& handler) + { + mDispatch = handler.mDispatch; + mId = handler.mId; + handler.mId = 0; + return *this; + } - void dispatch(const std::string& message, const Notification* param = 0); -}; + unsigned getId() const + { + return mId; + } + + private: + Impl* mDispatch; + mutable unsigned mId; -namespace dispatcher { + }; -inline Dispatcher::Handler addHandler(const std::string& message, - const Dispatcher::Function& callback) -{ - return Dispatcher::getInstance().addHandler(message, callback); -} + typedef boost::function Function; -inline Dispatcher::Handler addHandler(const std::string& message, - const Dispatcher::Function& callback, Dispatcher::Handler id) -{ - return Dispatcher::getInstance().addHandler(message, callback, id); -} -inline void removeHandler(Dispatcher::Handler id) -{ - Dispatcher::getInstance().removeHandler(id); -} + Dispatch(); + ~Dispatch(); -inline void dispatch(const std::string& message, const Notification* param = 0) -{ - Dispatcher::getInstance().dispatch(message, param); -} + // create and/or get a global instance + static Dispatch& getInstance(); -} // namespace dispatcher + Handler addHandler(const std::string& event, const Function& callback); + Handler addHandler(const std::string& event, const Function& callback, + Handler handler); + + void dispatch(const std::string& event, const Message* message = 0); +}; } // namespace Mf -#endif // _MOOF_DISPATCHER_HH_ +#endif // _MOOF_DISPATCH_HH_ /** vim: set ts=4 sw=4 tw=80: *************************************************/ diff --git a/src/Moof/Engine.cc b/src/Moof/Engine.cc index 9ff5e1a..f4bc712 100644 --- a/src/Moof/Engine.cc +++ b/src/Moof/Engine.cc @@ -35,15 +35,14 @@ #include "fastevents.h" #include -#include "Dispatcher.hh" #include "Engine.hh" #include "Event.hh" #include "Exception.hh" #include "Log.hh" +#include "Math.hh" #include "Random.hh" #include "Settings.hh" #include "Timer.hh" -#include "Video.hh" namespace Mf { @@ -53,36 +52,36 @@ class Engine::Impl { public: - Impl(int argc, char* argv[], const std::string& name, - const std::string& iconFile, const std::string& configFile, - Engine& engine) : + Impl(Engine& engine) : mInterface(engine), mTimestep(0.01), mPrintFps(false) { -#if defined(_WIN32) || defined(__WIN32__) || defined(__TOS_WIN__) || defined(__WINDOWS__) + // first, initialize the libraries + +#if defined(_WIN32) || defined(__WIN32__) if (SDL_Init(SDL_INIT_VIDEO | SDL_INIT_TIMER) != 0) #else if (SDL_Init(SDL_INIT_VIDEO | SDL_INIT_EVENTTHREAD) != 0) #endif { const char* error = SDL_GetError(); - logError("sdl is complaining: %s", error); throw Exception(ErrorCode::SDL_INIT, error); } if (FE_Init() != 0) { const char* error = FE_GetError(); - logError("fast events error: %s", error); throw Exception(ErrorCode::FASTEVENTS_INIT, error); } - alutInit(&argc, argv); + int argc = 1; + char name[] = "hello"; + alutInit(&argc, (char**)&name); + + // now load the settings the engine needs Settings& settings = Settings::getInstance(); - settings.loadFromFile(configFile); - settings.parseArgs(argc, argv); - long randomSeed; + unsigned randomSeed; if (settings.get("rngseed", randomSeed)) setSeed(randomSeed); else setSeed(); @@ -92,12 +91,10 @@ public: Scalar maxFps = 40.0; settings.get("maxfps", maxFps); - mDrawRate = 1.0 / maxFps; + mMaxFps = 1.0 / maxFps; + capFps(); settings.get("printfps", mPrintFps); - - mVideo = Video::alloc(name, iconFile); - mVideo->makeActive(); } ~Impl() @@ -139,14 +136,15 @@ public: deltaTime = newTicks - ticksNow; ticksNow = newTicks; + // don't slow the animation until 4Hz, which is unplayable anyway if (deltaTime >= 0.25) deltaTime = 0.25; accumulator += deltaTime; Timer::fireIfExpired(ticksNow); + dispatchEvents(); while (accumulator >= mTimestep) { - dispatchEvents(); update(totalTime, mTimestep); totalTime += mTimestep; @@ -183,17 +181,17 @@ public: draw(accumulator / mTimestep); mVideo->swap(); - nextDraw += mDrawRate; + nextDraw += mMaxFps; if (ticksNow >= nextDraw) { // we missed some scheduled draws, so reset the schedule - nextDraw = ticksNow + mDrawRate; + nextDraw = ticksNow + mMaxFps; } } // be a good citizen and give back what you don't need Timer::sleep(std::min(std::min(nextStep, nextDraw), - Timer::getNextFire()), true); + Timer::getNextFire()), Timer::ACTUAL); } while (!mStack.empty()); } @@ -211,7 +209,8 @@ public: (SDL_GetModState() & KMOD_CTRL) ) { // emergency escape - exit(0); + logWarning("escape forced"); + exit(1); } break; @@ -229,7 +228,7 @@ public: { for (mStackIt = mStack.begin(); mStackIt != mStack.end(); ++mStackIt) { - (*mStackIt)->update(t, dt); + (*mStackIt)->update(mInterface, t, dt); } } @@ -239,7 +238,7 @@ public: std::list::reverse_iterator it; for (it = mStack.rbegin(); it != mStack.rend(); ++it) { - (*it)->draw(alpha); + (*it)->draw(mInterface, alpha); } } @@ -247,7 +246,7 @@ public: { for (mStackIt = mStack.begin(); mStackIt != mStack.end(); ++mStackIt) { - if ((*mStackIt)->handleEvent(event)) break; + if ((*mStackIt)->handleEvent(mInterface, event)) break; } } @@ -256,7 +255,7 @@ public: { ASSERT(layer && "cannot push null layer"); mStack.push_front(layer); - logInfo(" push: %d", mStack.size()); + logDebug("stack: %d [pushed %X]", mStack.size(), layer.get()); layer->pushed(mInterface); } @@ -265,26 +264,26 @@ public: bool fixIt = false; if (mStack.begin() == mStackIt) fixIt = true; - LayerP popped = mStack.front(); + LayerP layer = mStack.front(); mStack.pop_front(); - logInfo(" pop: %d", mStack.size()); - popped->popped(mInterface); + logDebug("stack: %d [popped %X]", mStack.size(), layer.get()); + layer->popped(mInterface); if (fixIt) mStackIt = --mStack.begin(); - return popped; + return layer; } LayerP pop(Layer* layer) { bool fixIt = false; - std::list popped; + std::list layers; std::list::iterator it; for (it = mStack.begin(); it != mStack.end(); ++it) { - popped.push_back(*it); + layers.push_back(*it); if (it == mStackIt) fixIt = true; @@ -293,14 +292,15 @@ public: ++it; mStack.erase(mStack.begin(), it); - for (it = popped.begin(); it != popped.end(); ++it) + for (it = layers.begin(); it != layers.end(); ++it) { (*it)->popped(mInterface); + logDebug("stack: %d [popped %X]", mStack.size(), (*it).get()); } if (fixIt) mStackIt = --mStack.begin(); - return popped.back(); + return layers.back(); } } @@ -311,77 +311,83 @@ public: { mStack.clear(); mStackIt = mStack.begin(); - logInfo("clear: %d", mStack.size()); + logDebug("stack: 0 [cleared]"); } - Engine& mInterface; + void capFps() + { + if (mMaxFps < mTimestep) + { + logWarning("capping maximum fps to timestep (%f)", mTimestep); + mMaxFps = mTimestep; + } + } + - VideoP mVideo; + Engine& mInterface; + VideoP mVideo; + Dispatch mDispatch; std::list mStack; std::list::iterator mStackIt; - Scalar mTimestep; - Scalar mDrawRate; + Scalar mTimestep; + Scalar mMaxFps; - long mFps; - bool mPrintFps; + int mFps; + bool mPrintFps; }; -static Engine* instance = 0; - -Engine::Engine(int argc, char* argv[], const std::string& name, - const std::string& iconFile, const std::string& configFile) : - mImpl(new Engine::Impl(argc, argv, name, iconFile, configFile, *this)) -{ - instance = this; -} - +Engine::Engine() : + // pass through + mImpl(new Engine::Impl(*this)) {} Engine& Engine::getInstance() { - ASSERT(instance && "dereferencing null pointer"); - return *instance; - // TODO this has not been completely thought out - //static Engine engine; - //return engine; + static Engine engine; + return engine; } -void Engine::run() +void Engine::setVideo(VideoP video) { - return mImpl->run(); + // pass through + mImpl->mVideo = video; } -void Engine::setTimestep(Scalar ts) +VideoP Engine::getVideo() const { - mImpl->mTimestep = ts; + return mImpl->mVideo; } -Scalar Engine::getTimestep() const + +void Engine::setTimestep(int ts) { - return mImpl->mTimestep; + mImpl->mTimestep = 1.0 / Scalar(ts); + mImpl->capFps(); } -void Engine::setMaxFrameRate(long maxFps) +int Engine::getTimestep() const { - mImpl->mDrawRate = 1.0 / Scalar(maxFps); + return int(1.0 / mImpl->mTimestep); } -long Engine::getMaxFrameRate() const + +void Engine::setMaxFps(int maxFps) { - return long(1.0 / mImpl->mDrawRate); + mImpl->mMaxFps = 1.0 / Scalar(maxFps); + mImpl->capFps(); } - -Video& Engine::getVideo() const +int Engine::getMaxFps() const { - return *mImpl->mVideo; + return int(1.0 / mImpl->mMaxFps); } -long Engine::getFrameRate() const + +int Engine::getFps() const { return mImpl->mFps; } @@ -411,6 +417,37 @@ void Engine::clear() mImpl->clear(); } +int Engine::getSize() const +{ + return mImpl->mStack.size(); +} + + +void Engine::run() +{ + // pass through + return mImpl->run(); +} + + +Dispatch::Handler Engine::addHandler(const std::string& event, + const Dispatch::Function& callback) +{ + return mImpl->mDispatch.addHandler(event, callback); +} + +Dispatch::Handler Engine::addHandler(const std::string& event, + const Dispatch::Function& callback, Dispatch::Handler handler) +{ + return mImpl->mDispatch.addHandler(event, callback, handler); +} + +void Engine::dispatch(const std::string& event, + const Dispatch::Message* message) +{ + mImpl->mDispatch.dispatch(event, message); +} + } // namespace Mf diff --git a/src/Moof/Engine.hh b/src/Moof/Engine.hh index 84b1f58..c5e9986 100644 --- a/src/Moof/Engine.hh +++ b/src/Moof/Engine.hh @@ -31,45 +31,67 @@ #include +#include +#include #include -#include namespace Mf { -// forward declarations -class Video; +/** + * The engine is essentially a stack of layers. While running, it updates each + * layer from the bottom up every timestep. It also draws each layer from the + * bottom up, adhering to the maximum frame-rate. Events are send to each layer + * from the top down until a layer signals the event was handled. The engine is + * also responsible for firing timers on time. The engine will continue running + * as long as there are layers on the stack. + */ class Engine { public: - Engine(int argc, char* argv[], const std::string& name, - const std::string& iconFile, const std::string& configFile); ~Engine() {} // get global instance static Engine& getInstance(); - void run(); + // setting the video is required before you can run the engine and should + // probably be done before adding any layers + void setVideo(VideoP video); + VideoP getVideo() const; + + void setTimestep(int ts); + int getTimestep() const; + + void setMaxFps(int maxFps); // draw rate is always capped at the timestep + int getMaxFps() const; + + int getFps() const; - void setTimestep(Scalar ts); - Scalar getTimestep() const; - void setMaxFrameRate(long maxFps); - long getMaxFrameRate() const; + void push(LayerP layer); // push a layer onto the top + LayerP pop(); // pop the top layer + LayerP pop(Layer* layer); // pops a specific layer and all layers above it + void clear(); // remove all layers (the engine will stop) - Video& getVideo() const; - long getFrameRate() const; + int getSize() const; // get the size of the stack - void push(LayerP layer); - LayerP pop(); - // pops a specific layer and all layers above it - LayerP pop(Layer* layer); - void clear(); + // set this machine in motion + void run(); + + Dispatch::Handler addHandler(const std::string& event, + const Dispatch::Function& callback); + Dispatch::Handler addHandler(const std::string& event, + const Dispatch::Function& callback, Dispatch::Handler handler); + + void dispatch(const std::string& event, + const Dispatch::Message* message = 0); private: + Engine(); // use getInstance() to access the engine + class Impl; boost::shared_ptr mImpl; }; diff --git a/src/Moof/Exception.hh b/src/Moof/Exception.hh index a63f761..f8921bd 100644 --- a/src/Moof/Exception.hh +++ b/src/Moof/Exception.hh @@ -31,6 +31,7 @@ #include #include +#include #include @@ -42,10 +43,10 @@ class Exception : public std::exception { public: - explicit Exception(unsigned code, const char* what = "") + explicit Exception(unsigned code, const std::string& what = "") { mWhat[sizeof(mWhat)-1] = '\0'; - strncpy(mWhat, what, sizeof(mWhat)-1); + strncpy(mWhat, what.c_str(), sizeof(mWhat)-1); mCode = code; } virtual ~Exception() throw() {} @@ -81,8 +82,8 @@ enum Code SCRIPT_ERROR, // description SDL_INIT, // description SDL_VIDEOMODE, // - - UNKNOWN_AUDIO_FORMAT, // - - UNKNOWN_IMAGE_FORMAT, // - + UNKNOWN_AUDIO_FORMAT, // name of resource + UNKNOWN_IMAGE_FORMAT, // name of resource }; } // namespace ErrorCode diff --git a/src/Moof/Layer.hh b/src/Moof/Layer.hh index 55e233e..9ba4288 100644 --- a/src/Moof/Layer.hh +++ b/src/Moof/Layer.hh @@ -31,7 +31,6 @@ #include -#include #include #include @@ -43,7 +42,7 @@ namespace Mf { class Engine; -class Layer : public Drawable +class Layer { public: @@ -52,9 +51,12 @@ public: virtual void pushed(Engine& engine) {} virtual void popped(Engine& engine) {} - virtual void update(Scalar t, Scalar dt) {} - virtual void draw(Scalar alpha) const {} - virtual bool handleEvent(const Event& event) { return false; } + virtual void update(Engine& engine, Scalar t, Scalar dt) {} + virtual void draw(Engine& engine, Scalar alpha) const {} + virtual bool handleEvent(Engine& engine, const Event& event) + { + return false; + } }; typedef boost::shared_ptr LayerP; diff --git a/src/Moof/Log.cc b/src/Moof/Log.cc index 96827e1..a7cdae4 100644 --- a/src/Moof/Log.cc +++ b/src/Moof/Log.cc @@ -161,7 +161,7 @@ int logScript(Script& script) } -void importLogScript(Script& script) +void importLogPrintFunction(Script& script) { script.importFunction("print", (int (*)(Script&))logScript); } diff --git a/src/Moof/Log.hh b/src/Moof/Log.hh index 221827f..d3aa555 100644 --- a/src/Moof/Log.hh +++ b/src/Moof/Log.hh @@ -119,7 +119,7 @@ void logDebug(const char* fmt, ...); void logScript(const char* fmt, ...); class Script; int logScript(Script& script); -void importLogScript(Script& script); +void importLogPrintFunction(Script& script); } // namespace Mf diff --git a/src/Moof/ModalDialog.hh b/src/Moof/ModalDialog.hh index 11d9d79..68a6d28 100644 --- a/src/Moof/ModalDialog.hh +++ b/src/Moof/ModalDialog.hh @@ -31,6 +31,10 @@ #include +#if HAVE_CONFIG_H +#include "../config.h" +#endif + #if defined(_WIN32) || defined(__WIN32__) #include #elif defined(__APPLE__) && defined(__MACH__) @@ -43,12 +47,9 @@ #include #endif +#include #include -#if HAVE_CONFIG_H -#include "config.h" -#endif - namespace Mf { @@ -66,13 +67,31 @@ struct ModalDialog CRITICAL = 3 }; + std::string title; Type type; std::string text1; std::string text2; + void run() const { + switch (type) + { + case WARNING: + logWarning("%s", text1.c_str()); + logWarning("%s", text2.c_str()); + break; + case CRITICAL: + logError("%s", text1.c_str()); + logError("%s", text2.c_str()); + break; + default: + logInfo("%s", text1.c_str()); + logInfo("%s", text2.c_str()); + break; + } + #if USE_GTK int argc = 0; diff --git a/src/Moof/Script.hh b/src/Moof/Script.hh index df00619..2f285ce 100644 --- a/src/Moof/Script.hh +++ b/src/Moof/Script.hh @@ -471,15 +471,14 @@ public: Script() : - mState(luaL_newstate()) + mState(0) { - lua_pushlightuserdata(mState, this); - lua_setfield(mState, LUA_REGISTRYINDEX, "_script_obj"); + reset(); } ~Script() { - if (mIsMainThread) lua_close(mState); + destroy(); } @@ -488,12 +487,77 @@ public: return ScriptP(new Script); } + void reset() + { + if (mState) destroy(); + mState = luaL_newstate(); + lua_pushlightuserdata(mState, this); + lua_setfield(mState, LUA_REGISTRYINDEX, "_script_obj"); + } + void importStandardLibraries() { luaL_openlibs(mState); } + void importBaseLibrary() + { + lua_pushcfunction(mState, luaopen_base); + push(LUA_COLIBNAME); + call(1, 0); + } + + void importPackageLibrary() + { + lua_pushcfunction(mState, luaopen_package); + push(LUA_LOADLIBNAME); + call(1, 0); + } + + void importStringLibrary() + { + lua_pushcfunction(mState, luaopen_string); + push(LUA_STRLIBNAME); + call(1, 0); + } + + void importTableLibrary() + { + lua_pushcfunction(mState, luaopen_table); + push(LUA_TABLIBNAME); + call(1, 0); + } + + void importMathLibrary() + { + lua_pushcfunction(mState, luaopen_math); + push(LUA_MATHLIBNAME); + call(1, 0); + } + + void importIoLibrary() + { + lua_pushcfunction(mState, luaopen_io); + push(LUA_IOLIBNAME); + call(1, 0); + } + + void importOsLibrary() + { + lua_pushcfunction(mState, luaopen_os); + push(LUA_OSLIBNAME); + call(1, 0); + } + + void importDebugLibrary() + { + lua_pushcfunction(mState, luaopen_debug); + push(LUA_DBLIBNAME); + call(1, 0); + } + + void importFunction(const std::string& name, const Function& function) { push(function); @@ -815,6 +879,11 @@ private: return (*function)(*script); } + void destroy() + { + if (mIsMainThread) lua_close(mState); + } + lua_State* mState; bool mIsMainThread; std::list mFunctions; diff --git a/src/Moof/Settings.cc b/src/Moof/Settings.cc index 5a4f991..81b5c50 100644 --- a/src/Moof/Settings.cc +++ b/src/Moof/Settings.cc @@ -85,6 +85,12 @@ void Settings::loadFromFiles(const std::vector& filePaths) } +void Settings::clear() +{ + mScript.reset(); +} + + } // namepsace Mf /** vim: set ts=4 sw=4 tw=80: *************************************************/ diff --git a/src/Moof/Settings.hh b/src/Moof/Settings.hh index 42e7d3a..9b3e0cb 100644 --- a/src/Moof/Settings.hh +++ b/src/Moof/Settings.hh @@ -54,7 +54,8 @@ public: mGlobals(mScript.getGlobalTable()), mTop(mScript[-1]) { - importLogScript(mScript); + mScript.importBaseLibrary(); + importLogPrintFunction(mScript); } // get global instance @@ -65,6 +66,8 @@ public: void loadFromFile(const std::string& filePath); void loadFromFiles(const std::vector& filePaths); + void clear(); // remove all settings + template bool get(const std::string& key, T& value); diff --git a/src/Moof/Sound.cc b/src/Moof/Sound.cc index 4c60e32..4275ea4 100644 --- a/src/Moof/Sound.cc +++ b/src/Moof/Sound.cc @@ -35,6 +35,7 @@ #include #include +#include "Engine.hh" #include "Exception.hh" #include "Library.hh" #include "Log.hh" @@ -98,15 +99,12 @@ public: { logWarning("error while loading sound %s", getName().c_str()); - throw Exception(ErrorCode::UNKNOWN_AUDIO_FORMAT); + throw Exception(ErrorCode::UNKNOWN_AUDIO_FORMAT, getName()); } vorbis_info* vorbisInfo = ov_info(&mOggStream, -1); mFormat = getAudioFormat(vorbisInfo); mFreq = vorbisInfo->rate; - - logDebug(" channels: %d", vorbisInfo->channels); - logDebug(" frequency: %d", vorbisInfo->rate); } @@ -137,7 +135,6 @@ public: if (size == 0) { logWarning("decoded no bytes from %s", getName().c_str()); - //throw Exception("file_not_found"); return; } @@ -209,6 +206,9 @@ public: void init() { + // make sure the engine is initialized + Engine::getInstance(); + ALfloat zero[] = {0.0f, 0.0f, 0.0f}; alGenSources(1, &mSource); diff --git a/src/Moof/Texture.cc b/src/Moof/Texture.cc index 341b559..a701424 100644 --- a/src/Moof/Texture.cc +++ b/src/Moof/Texture.cc @@ -33,7 +33,8 @@ #include #include -#include "Dispatcher.hh" +#include "Dispatch.hh" +#include "Engine.hh" #include "Exception.hh" #include "Library.hh" #include "Log.hh" @@ -64,9 +65,9 @@ class Texture::Impl : public Library { if (mObject) { - if (mObject == globalObject_) + if (mObject == gObject) { - globalObject_ = 0; + gObject = 0; } glDeleteTextures(1, &mObject); @@ -80,9 +81,9 @@ class Texture::Impl : public Library * to cache it if the client has plenty of RAM. */ - void contextRecreated(const Notification* note) + void contextRecreated() { - mObject = globalObject_ = 0; + mObject = gObject = 0; uploadToGL(); } @@ -143,11 +144,16 @@ public: mWrapT(GL_CLAMP), mObject(0) { - loadFromFile(); + // make sure the engine is initialized + Engine& engine = Engine::getInstance(); + VideoP video = engine.getVideo(); + ASSERT(video && "cannot load textures without a current video context"); // we want to know when the GL context is recreated - Dispatcher::getInstance().addHandler("video.context_recreated", - boost::bind(&Impl::contextRecreated, this, _1), this); + mDispatchHandler = engine.addHandler("video.newcontext", + boost::bind(&Impl::contextRecreated, this)); + + loadFromFile(); } ~Impl() @@ -158,8 +164,6 @@ public: } unloadFromGL(); - - Dispatcher::getInstance().removeHandler(this); } @@ -246,7 +250,7 @@ public: if (!surface) { logWarning("texture not found: %s", getName().c_str()); - throw Exception(ErrorCode::FILE_NOT_FOUND, getName().c_str()); + throw Exception(ErrorCode::FILE_NOT_FOUND, getName()); } SDL_Surface* temp = prepareImageForGL(surface); @@ -254,7 +258,7 @@ public: if (!temp) { - throw Exception(ErrorCode::UNKNOWN_IMAGE_FORMAT); + throw Exception(ErrorCode::UNKNOWN_IMAGE_FORMAT, getName()); } if (temp->format->BytesPerPixel == 3) @@ -268,7 +272,7 @@ public: else { SDL_FreeSurface(temp); - throw Exception(ErrorCode::UNKNOWN_IMAGE_FORMAT); + throw Exception(ErrorCode::UNKNOWN_IMAGE_FORMAT, getName()); } mWidth = temp->w; @@ -366,29 +370,31 @@ public: { uploadToGL(); } - if (mObject != globalObject_) + if (mObject != gObject) { glBindTexture(GL_TEXTURE_2D, mObject); - globalObject_ = mObject; + gObject = mObject; } } - SDL_Surface* mContext; - unsigned mWidth; ///< Horizontal dimension of the image. - unsigned mHeight; ///< Vertical dimension. + SDL_Surface* mContext; + unsigned mWidth; ///< Horizontal dimension of the image. + unsigned mHeight; ///< Vertical dimension. + + GLuint mMode; ///< GL_RGB or GL_RGBA. + GLuint mMinFilter; ///< Minifcation filter. + GLuint mMagFilter; ///< Magnification filter. + GLuint mWrapS; ///< Wrapping behavior horizontally. + GLuint mWrapT; ///< Wrapping behavior vertically. - GLuint mMode; ///< Depth of the image, GL_RGB or GL_RGBA. - GLuint mMinFilter; ///< Minifcation filter. - GLuint mMagFilter; ///< Magnification filter. - GLuint mWrapS; ///< Wrapping behavior horizontally. - GLuint mWrapT; ///< Wrapping behavior vertically. + GLuint mObject; ///< GL texture handle. + static GLuint gObject; ///< Global GL texture handle. - GLuint mObject; ///< GL texture handle. - static GLuint globalObject_; ///< Global GL texture handle. + Dispatch::Handler mDispatchHandler; }; -GLuint Texture::Impl::globalObject_ = 0; +GLuint Texture::Impl::gObject = 0; Texture::Texture(const std::string& name) : @@ -421,7 +427,7 @@ GLuint Texture::getObject() const void Texture::resetBind() { glBindTexture(GL_TEXTURE_2D, 0); - Impl::globalObject_ = 0; + Impl::gObject = 0; } diff --git a/src/Moof/Timer.cc b/src/Moof/Timer.cc index 7f51510..b428f0a 100644 --- a/src/Moof/Timer.cc +++ b/src/Moof/Timer.cc @@ -64,7 +64,7 @@ void Timer::init(const Function& function, Scalar seconds, Mode mode) { mFunction = function; - if (mode == ABSOLUTEE) + if (mode == ACTUAL) { mAbsolute = seconds; } @@ -200,12 +200,12 @@ Scalar Timer::getTicks() return Scalar(ts.tv_sec - reference) + Scalar(ts.tv_nsec) / 1000000000.0; } -void Timer::sleep(Scalar seconds, bool absolute) +void Timer::sleep(Scalar seconds, Mode mode) { struct timespec ts; int ret; - if (absolute) seconds -= getTicks(); + if (mode == ACTUAL) seconds -= getTicks(); ts.tv_sec = time_t(seconds); ts.tv_nsec = long((seconds - Scalar(ts.tv_sec)) * 1000000000.0); @@ -230,9 +230,9 @@ Scalar Timer::getTicks() return Scalar(ms / 1000) + Scalar(ms % 1000) / 1000.0; } -void Timer::sleep(Scalar seconds, bool absolute) +void Timer::sleep(Scalar seconds, Mode mode) { - if (absolute) seconds -= getTicks(); + if (mode == ACTUAL) seconds -= getTicks(); SDL_Delay(Uint32(cml::clamp(int(seconds * 1000.0), 0, 1000))); } diff --git a/src/Moof/Timer.hh b/src/Moof/Timer.hh index a0bc676..b657a5f 100644 --- a/src/Moof/Timer.hh +++ b/src/Moof/Timer.hh @@ -53,7 +53,7 @@ public: { INVALID = -1, NORMAL = 0, - ABSOLUTEE = 1, // the ABSOLUTE keyword isn't available on win32... + ACTUAL = 1, REPEAT = 2 }; @@ -102,7 +102,7 @@ public: * sleep for the requested amount of time (and maybe longer). */ - static void sleep(Scalar seconds, bool absolute = false); + static void sleep(Scalar seconds, Mode mode = NORMAL); static Scalar getNextFire() diff --git a/src/Moof/Transition.hh b/src/Moof/Transition.hh index f1e1bcb..ab09c02 100644 --- a/src/Moof/Transition.hh +++ b/src/Moof/Transition.hh @@ -51,15 +51,12 @@ class Transition : public Layer T mInterp; - Engine* mEngine; - public: Transition(LayerP t, LayerP f, const T& interp) : mTo(t), mFrom(f), - mInterp(interp), - mEngine(0) {} + mInterp(interp) {} typedef boost::shared_ptr Ptr; @@ -69,27 +66,22 @@ public: } - void pushed(Engine& engine) - { - mEngine = &engine; - } - void popped(Engine& engine) { if (mTo) engine.push(mTo); } - void update(Scalar t, Scalar dt) + void update(Engine& engine, Scalar t, Scalar dt) { mInterp.update(t, dt); - if (mFrom) mFrom->update(t, dt); - if (mTo) mTo->update(t, dt); + if (mFrom) mFrom->update(engine, t, dt); + if (mTo) mTo->update(engine, t, dt); if (mInterp.isDone()) { // to should /replace/ this - mEngine->pop(this); + engine.pop(this); } } @@ -126,10 +118,10 @@ public: glPopMatrix(); } - void draw(Scalar alpha) const + void draw(Engine& engine, Scalar alpha) const { Scalar a = mInterp.getState(alpha); - logInfo("draw state: %f", a); + logDebug("transition state: %f", a); //glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); @@ -138,7 +130,7 @@ public: glPushMatrix(); glLoadIdentity(); glRotate(180.0 * a, 0.0, 1.0, 0.0); - mFrom->draw(alpha); + mFrom->draw(engine, alpha); glPopMatrix(); } //drawFade(a); @@ -148,21 +140,21 @@ public: glPushMatrix(); glLoadIdentity(); glRotate(180.0 * (1.0 - a), 0.0, 1.0, 0.0); - mTo->draw(alpha); + mTo->draw(engine, alpha); glPopMatrix(); } //drawFade(1.0 - a); } - bool handleEvent(const Event& event) + bool handleEvent(Engine& engine, const Event& event) { if (mTo) { - return mTo->handleEvent(event); + return mTo->handleEvent(engine, event); } else if (mFrom) { - return mFrom->handleEvent(event); + return mFrom->handleEvent(engine, event); } return false; } diff --git a/src/Moof/Video.cc b/src/Moof/Video.cc index 9fdd478..eeec47e 100644 --- a/src/Moof/Video.cc +++ b/src/Moof/Video.cc @@ -28,7 +28,8 @@ #include -#include "Dispatcher.hh" +#include "Dispatch.hh" +#include "Engine.hh" #include "Exception.hh" #include "Log.hh" #include "Settings.hh" @@ -64,6 +65,9 @@ Video::Video(const std::string& caption, const std::string& icon) void Video::init(const Attributes& attribs) { + // make sure the engine is initialized before setting up the video + Engine::getInstance(); + mContext = 0; mFlags = 0; mAttribs = attribs; @@ -132,7 +136,8 @@ void Video::setVideoMode(const long mode[3]) // on win32, creating a new context via SDL_SetVideoMode will wipe // out the GL state, so we gotta notify everyone to reload their // state after the change - Mf::dispatcher::dispatch("video.context_recreated"); + Engine::getInstance().dispatch("video.newcontext"); + logInfo("video context recreated"); #endif } @@ -182,6 +187,11 @@ std::string Video::getCaption() const return mAttribs.caption; } +const std::string& Video::getIcon() const +{ + return mAttribs.icon; +} + void Video::setFull(bool full) { @@ -275,12 +285,6 @@ void Video::setCursorGrab(bool cursorGrab) } -void Video::makeActive() -{ - // NOP until the day SDL supports more than only one window. - // Still waiting... -} - void Video::swap() { SDL_GL_SwapBuffers(); diff --git a/src/Moof/Video.hh b/src/Moof/Video.hh index 193274f..91eecb7 100644 --- a/src/Moof/Video.hh +++ b/src/Moof/Video.hh @@ -94,6 +94,8 @@ public: void setCaption(const std::string& caption); std::string getCaption() const; + const std::string& getIcon() const; + void setFull(bool full); void toggleFull(); bool isFull() const; @@ -110,7 +112,6 @@ public: void toggleCursorGrab(); bool isCursorGrab() const; - void makeActive(); void swap(); int getWidth() const; diff --git a/src/Tilemap.cc b/src/Tilemap.cc index b15d07f..02184bc 100644 --- a/src/Tilemap.cc +++ b/src/Tilemap.cc @@ -66,7 +66,7 @@ struct Tilemap::Impl : public Mf::Library std::string filePath = Tilemap::getPath(getName()); script.importStandardLibraries(); - importLogScript(script); + importLogPrintFunction(script); bindScriptConstants(script); if (script.doFile(filePath) != Mf::Script::SUCCESS) diff --git a/src/TitleLayer.cc b/src/TitleLayer.cc index 11c624d..f5e4ef0 100644 --- a/src/TitleLayer.cc +++ b/src/TitleLayer.cc @@ -36,40 +36,43 @@ void TitleLayer::pushed(Mf::Engine& engine) { - mEngine = &engine; - Mf::Scalar coeff[] = {0.0, 1.0}; mFadeIn.init(coeff, 0.1); mGameLayer = GameLayer::alloc(); } -void TitleLayer::update(Mf::Scalar t, Mf::Scalar dt) +void TitleLayer::update(Mf::Engine& engine, Mf::Scalar t, Mf::Scalar dt) { if (!mFadeIn.isDone()) mFadeIn.update(t, dt); } -void TitleLayer::draw(Mf::Scalar alpha) const +void TitleLayer::draw(Mf::Engine& engine, Mf::Scalar alpha) const { glClearColor(0.0, 0.0, mFadeIn.getState(alpha), 1.0); glClear(GL_COLOR_BUFFER_BIT); } -bool TitleLayer::handleEvent(const Mf::Event& event) +bool TitleLayer::handleEvent(Mf::Engine& engine, const Mf::Event& event) { switch (event.type) { case SDL_KEYUP: - Mf::LayerP titleLayer = mEngine->pop(this); - //mEngine->pushLayer(GameLayer::alloc()); + //if (event.key.keysym.sym == SDLK_ESCAPE) + //{ + //break; + //} + + Mf::LayerP titleLayer = engine.pop(this); + //engine.pushLayer(GameLayer::alloc()); - Mf::Scalar coeff[] = {0.0, 1.0}; - Mf::Lerp interp(coeff, 0.2); + Mf::Scalar coeff[] = {0.0, 0.75, 0.99, 1.0}; + Mf::PolynomialInterpolator<3> interp(coeff, 0.1); //Mf::LayerP mGameLayer = GameLayer::alloc(); - Mf::Transition::Ptr transition = - Mf::Transition::alloc(mGameLayer, titleLayer, interp); - mEngine->push(transition); + Mf::Transition >::Ptr transition = + Mf::Transition >::alloc(mGameLayer, titleLayer, interp); + engine.push(transition); return true; } diff --git a/src/TitleLayer.hh b/src/TitleLayer.hh index 8873988..a781c93 100644 --- a/src/TitleLayer.hh +++ b/src/TitleLayer.hh @@ -50,15 +50,13 @@ public: void pushed(Mf::Engine& engine); - void update(Mf::Scalar t, Mf::Scalar dt); - void draw(Mf::Scalar alpha) const; - bool handleEvent(const Mf::Event& event); + void update(Mf::Engine& engine, Mf::Scalar t, Mf::Scalar dt); + void draw(Mf::Engine& engine, Mf::Scalar alpha) const; + bool handleEvent(Mf::Engine& engine, const Mf::Event& event); private: Mf::Lerp mFadeIn; - Mf::Engine* mEngine; - Mf::LayerP mGameLayer; }; -- 2.43.0